Browsing Tables in &GAP; using ncurses –The User Interface

As stated in Section , 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 main table of a browse 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 row labels of a browse table; analogously, a column labels table column labels of a browse 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 corner table of a browse table, with information about the labels or about the main table. Optionally, a header header of a browse table and a footer footer of a browse table 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 | ---------------------------------- | | | | corner | column labels | | | | ---------------------------------- | | | | | | | row | main | | labels | table | | | | | | | | | | ---------------------------------- | footer | ----------------------------------
\begin{center} \begin{tabular}{|c|c|} \hline \multicolumn{2}{|c|}{header} \\ \hline corner & column labels \\ \hline & \\ row & main \\ labels & table \\ & \\ \hline \multicolumn{2}{|c|}{footer} \\ \hline \end{tabular} \end{center}
</p> <table rules="all" border="3"> <thead> <tr> <td colspan="2" align="center">header</td> </tr> </thead> <tfoot> <tr> <td colspan="2" align="center">footer</td> </tr> </tfoot> <tbody> <tr> <td align="center">corner</td> <td align="center">column labels</td> </tr> <tr> <td align="center">row labels</td> <td align="center">main table</td> </tr> </tbody> </table><p>

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 gives an overview of the features available in standard browse table applications, and Section describes the data structures used in browse tables. Finally, Section introduces the function , 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 .

Examples of browse table applications are shown in Chapter .

Features Supported by the Function NCurses.BrowseGeneric

Standard applications of the function have the following functionality. Other applications may provide only a subset, or add further functionality, see Chapters and .

Scrolling:scrolling in a browse table
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 mouse events are enabled then cells can be selected also via mouse clicks.
Selecting:selecting entries of a browse table
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 ). 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:searching in a browse table
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:sorting a browse table
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:categorizing a browse table
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 .) The category rows can be collapsed collapsed category row (that is, the table rows that belong to this category row are not shown) or expanded expanded category row (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:filtering a browse table
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:help window for a browse table
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 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:log of a browse table session
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, BrowseData.log see Section .)
Replay:replay of a browse table session
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 , 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:click on an entry of a browse table
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 . 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 for details. If mouse events mouse events are enabled then also mouse clicks can be used as an alternative to hitting the Enter key.
Return Value:return value of a browse table session
The function 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.
Data Structures used by NCurses.BrowseGeneric
<#Include Label="IsBrowseTableCellData_man">

<#Include Label="BlockEntry_man">

<#Include Label="IsBrowseTable_man">

The Function NCurses.BrowseGeneric
<#Include Label="NCurses.BrowseGeneric_man">

Browsing Tables in &GAP; using ncurses –The Programming Interface

This chapter describes some aspects of the internals of the browse table handling. The relevant objects are action functions that implement the individual navigation steps (see Section ), modes that describe the sets of available navigation steps in given situations (see Section ), and browse applications that are given by the combination of several modes (see Section ). Most of the related data is stored in the global variable .

For more details, one should look directly at the code in the file lib/browse.gi of the &Browse; package.

Navigation Steps in Browse Tables

Navigating in a browse table means that after entering visual mode by calling , the user hits one or several keys, or uses a mouse button, and if this input is in a given set of admissible inputs then a corresponding function is executed with argument the browse table (plus additional information in the case of mouse events). The function call then may change components in this table (recommended: components in its dynamic component), such that the appearance in the window may be different afterwards, and also the admissible inputs and their effects may have changed.

The relation between the admissible inputs and the corresponding functions is application dependent. However, it is recommended to associate the same input to the same function in different situations; for example, the ? key and the F1 key should belong to a function that shows a help window (see Section ), the q key and the Esc key should belong to a function that exits the current mode (Note that the Esc key may be recognized as input only after a delay of about a second.), the Q key should belong to a function that exits the browse application (see Section ), the F2 key should belong to a function that saves the current window contents in a global variable (see Section ), and the E key should belong to a function that enters a break loop (see Section ). The Enter and Return keys should belong to a click on a selected table entry, and if a category row is selected then they should expand/collapse this category. The M key should toggle enabling and disabling mouse events. Mouse events on a cell or on a category row of a browse table should move the selected entry to this position; it is recommended that no functionality is lost if no mouse events are used, although the number of steps might be reduced when the mouse is used.

Each such function is wrapped into a record with the components action (the function itself) and helplines (a list of attribute lines that describes what the function does). action record of a browse table The help lines are used by the help feature of NCurses.BrowseGeneric, see Section .

The action functions need not return anything. Whenever the shown screen shall be recomputed after the function call, the component dynamic.changed of the browse table must be set to true by the action functions.

After entering the first characters of an admissible input that consists of more characters, the last line of the window with the browse table shows these characters behind the prefix partial input:. partial input in a browse table One can delete the last entered character of a partial input via the Delete and Backspace keys. It is not possible to make these keys part of an admissible input. When a partial input is given, only those user inputs have an effect that extend the partial input to (a prefix of) an admissible input. For example, asking for help by hitting the ? key will in general not work if a partial input had been entered before.

Modes in Browse Tables
mode of a browse table

In different situations, different inputs may be admissible for the same browse table, and different functions may belong to the same input. For example, the meaning of moving down can be different depending on whether a cell is selected or not.

The set of admissible user inputs and corresponding functions for a particular situation is collected in a mode of the browse table. (There should be no danger to mix up this notion of mode with the visual mode introduced in Section .) A mode is represented by a record with the components name (a string used to associate the mode with the components of header, headerLength, footer, footerLength, Click, and for the help screen), flag (a string that describes properties of the mode but that can be equal for different modes), actions (a list of records describing the navigation steps that are admissible in the mode, see Section ), and ShowTables (the function used to eventually print the current window contents, the default is BrowseData.ShowTables). BrowseData.ShowTables Due to the requirement that each admissible user input uniquely determines a corresponding function, no admissible user input can be a prefix of another admissible input, for the same mode.

Navigation steps (see Section ) can change the current mode or keep the mode. It is recommended that each mode has an action to leave this mode; also an action to leave the browse table application is advisable.

In a browse table, all available modes are stored in the component work.availableModes, whose value is a list of mode records. The value of the component dynamic.activeModes is a list of mode records that is used as a stack: The current mode is the last entry in this list, changing the current mode is achieved by unbinding the last entry (so one returns to the mode from which the current mode had been entered by adding it to the list), by adding a new mode record (so one can later return to the current mode), or by replacing the last entry by another mode record. As soon as the dynamic.activeModes list becomes empty, the browse table application is left. (In this situation, if the browse table had been entered from the &GAP; prompt then visual mode is left, and one returns to the &GAP; prompt.)

The following modes are predefined by the &Browse; package. Each of these modes admits the user inputs ?, F1, q, Esc, Q, F2, E, and M that have been mentioned in Section .

browse
This mode admits scrolling of the browse table by a cell or by a screen, searching for a string, selecting a row, a column, or an entry, and expanding or collapsing all category rows.
help
This mode is entered by calling BrowseData.ShowHelpTable; it shows a help window concerning the actions available in the mode from which the help mode was entered. The help mode admits scrolling in the help table by a cell or by a screen. See Section for details.
select_entry
In this mode, one table cell is regarded as selected; this cell is highlighted using the attribute in the component work.startSelect as a prefix of each attribute line, see the remark in Section . The mode admits moving the selection by one cell in the four directions, searching for a string and for further occurrences of this string, expanding or collapsing the current category row or all category rows, and executing the click function of this mode, provided that the component work.Click.( "select_entry" ) of the browse table is bound.
select_row
This is like the select_entry mode, except that a whole row of the browse table is highlighted. Searching is restricted to the selected row, and click refers to the function work.Click.( "select_row" ).
select_row_and_entry
This is a combination of the select_entry mode and the select_row mode.
select_column
This is like the select_row mode, just a column is selected not a row.
select_column_and_entry
This is like the select_row_and_entry mode, just a column is selected not a row.
Browse Applications

The data in a browse table together with the set of its available modes and the stack of active modes forms a browse application. So the part of or all functionality of the &Browse; package can be available (standard application), or additional functionality can be provided by extending available modes or adding new modes.

When has been called with the browse table t, say, the following loop is executed.

1.
If the list t .dynamic.activeModes is empty then exit the browse table, and if the component t .dynamic.Return is bound then return its value. Otherwise proceed with step 2.
2.
If t .dynamic.changed is true then call the ShowTables function of the current mode; this causes a redraw of the window that shows the browse table. Then go to step 3.
3.
Get one character of user input. If then the current user input string is the name of an action of the current mode then call the corresponding action function and go to step 1; if the current user input string is just a prefix of the name of some actions of the current mode then go to step 3; if the current user input string is not a prefix of any name of an action of the current mode then discard the last read character and go to step 3.

When one designs a new application, it may be not obvious whether some functionality shall be implemented via one mode or via several modes. As a rule of thumb, introducing a new mode is recommended when one needs a new set of admissible actions in a given situation, and also if one wants to allow the user to perform some actions and then to return to the previous status.

Predefined Browse Functionalities
<#Include Label="BrowseData_man">

<#Include Label="SetReplay_man">

<#Include Label="AlertWithReplay_man">

<#Include Label="ShowHelp_man">

<#Include Label="SaveWindow_man">

<#Include Label="QuitMode_man">

<#Include Label="Error_man">