CXXCURSORWINDOWS
[See also: CXXTOOLS.txt, CXXLIBRARY.txt, CXXDIRVIEWER, CXXEDITOR.txt, and README]
==================
CXX CURSOR WINDOWS
==================
INTRODUCTION
------------
The library, tools.a (or .lib on Windows), contains a variety of general purpose
routines that can be used to write applications portable to linux, unix, and
windows. One set of routines provides a cursor based windowing system. That is,
windowing applications can be written that draw in "windows" within an Xterm
window, a putty terminal, or on MS Windows using a console window.
There are several layers of routines that make up this subsystem. However, it is
generally advisable to only think in terms of the highest layer. The lower layers
are driven carefully by the upper layers -- if you try to mingle calls to the
wrong layers you will create an application that doesn't always respond correctly
to user events.
The cursor windows that you can create are tiled. That is, due to the small size
of terminal windows, a design decision was made not to waste space with window
borders. To avoid this, tiled windows are used -- and instead of explicitly
drawn window boxes, highlighting and underlining are used to separate tiled
window regions. Thus you have a some slight responsibility with respect to the
content of your windows. Your responsibities include setting the text attributes
of the top and bottom lines of your windows to help mark the borders. Note
that the screen can be split both horizontally and vertically.
To write an application that uses the CXX cursor windows, you derive from class
Viewer and superseed to virtual two functions: a repaint handler and a keyboard
event handler. Note that I thought that I would try to support mice one day but
I never could get it to work reliably. Don't let the data structures confuse you,
there is no mouse support at this time.
In addition to the tiled windows, it is possible to create popup dialog windows
that are centered in the middle of the terminal and are drawn in reversed video
attributes to eliminate the need for space wasting boxes -- although by the time
you read this, I may have added a box border anyway.
To see an example of how create a cursor windowing application, look to file
editor/editor.cxx. This file combines all the existing cursor windowing
applications into a single executable. You can add new ones to the editor or
make separate executables if you don't want to burden the editor.exe program
with your new creations.
MAIN PROGRAMS FOR TEXT WINDOWING APPLICATIONS
---------------------------------------------
The basic logic of a cursor windowing executable works like this:
ViewerManager vm; // the cursor windowing system main interface
// set up some information used by the help subsystem
vm.executable_version_ = editversion;
vm.executable_name_ = options.argv[0]; // guaranteed to exist
// open the main cursor window
vm.open(); // need to open the window so that sizes of viewports
// can be calculated. This must be done AFTER the program
// options are parsed because of the potential of the
// ProgramOptions constructor to call exit(). If the window
// is open when that happens the terminal characteristics will
// get messed up.
// now add application TYPES to the viewer manager so that it can know how to
// handle various types of files
// directories
vm.add_app(DirectoryViewer::app, DirectoryViewer::app_name);
vm.set_directory_app(DirectoryViewer::app);
// generalized text editing (edit or 'e' command from dir viewer)
vm.add_app(TextEditor::app, TextEditor::app_name);
vm.add_editor_app(TextEditor::app, ""); // handles all files extensions
// generalized text viewing (show or 's'
// command from dir viewer (enter key too))
vm.add_app(TextViewer::app, TextViewer::app_name);
vm.add_viewer_app(read_only_edit, ""); // handles all files extensions
// comma separated values file editing.
vm.add_app(CsvViewer::app, "CsvViewer");
vm.add_editor_app(CsvViewer::app, ".csv");
// other windowing applications that have to be manually invoked.
vm.add_app(KeyViewer::app, KeyViewer::app_name);
// ... other types of your own construction
// now, add an actual Viewer that the user can interact with:
Viewer * editor = vm.open("Some Filename");
vm.add(editor, "Some Filename"); // viewer's are associated with a file
// finally, animate the windows until the user quits.
vm.run();
WINDOWING APPLICATIONS
----------------------
As mentioned above, a windowing application consists of two fundamental functions:
* screen repaint handler
* keyboard event handler
There are a few other things you have to do as well:
* declare a variable holding the application name and brief description
* create a function that will "new-up" your application and when invoked by
the viewer manager.
* properly handle some system specific events that switch windows, kill
the application, etc.
* implement help for your application
The editor of course does all these things and can serve as a programming example
when needed -- if you can find how things work. It's pretty complicated. The
Key Viewer application is much simpler -- it ignores some of the interfaces
because it is designed to always work with the screen space given to it. See
lib/keyviewer.cxx.
REPAINT HANDLER
---------------
An application must have a repaint handler function. This function repaints the
window from scratch. It must obey the text attribute guidelines or your app
will look messed up when the screen is resized. Here are the basic rules:
* each window can either be active or not
* the top line of a window should be the title line and should be in the
active title attribute or the inactive title attribute at all times
* the bottom line of the window should be displayed in the bottom line
attribute -- which will make it underlined on xterms and putty and
a unique color in windows.
* when the screen is split horizontallay and your window is not the
leftmost window, you should draw verticle bars in column 0 of every
line.
* you draw most of your window's text in the normal text attribute
* use the highted attribute for cursor lines
* use the active_mark attribute or the inactive_mark attribute for
lines the user has marked.
The ViewerManager provides variables that hold most of these text attributes.
The member names and default values are:
int ViewerManager::normal_att = CursorWindow::normal;
int ViewerManager::active_title_att = CursorWindow::reverse_bold;
int ViewerManager::inactive_title_att= CursorWindow::underlined;
int ViewerManager::active_mark_att = CursorWindow::reverse_ul;
int ViewerManager::inactive_mark_att = CursorWindow::bold;
int ViewerManager::bottom_att = CursorWindow::underlined;
int ViewerManager::highlighted_att = CursorWindow::reversed;
Theoretically, you might implement functionality that changes these
attributes on the fly but it is proably not a good idea -- there are just
barely enough separate attributes in the curses implementation to do the
above.
A trivial repaint handler works something like this:
Get the width and height of the viewport passed in
fill the whole viewport with normal attribute
Paint the top line of the viewport with a title using the
active_title attribute -- and fill to end of line to make
it a title bar.
Paint any other text of interest in either the normal attribute
or the bottom_att if you are painting the bottom line.
Position the text cursor on the first line of interest.
Note that the repaint handler is passed a command to tell you what is going
on. For example, you might receive a normal paint request. Or, your window
might be losing focus and in that case you would only want to repaint the
very top line of your window in inactive_title_att so that the user sees
that your window is not longer active. There should only be 1 active viewport
at a time.
WINDOWS AND VIEWPORTS AND MANAGERS -- OH MY!
----------------------------------------------
There is actually only 1 cursor window object for the whole application. You
should not make any direct drawing calls to the cursor pointer that you
might have occaision to receive. You can use it to call the beep() function
and to get the total screen width and height. That's about the only safe
things you can do with it.
There is also only 1 viewer manager object.
There are an infinite number of viewports. A viewport is a constantly
changing handle to sub-set of the screen space. Basically, your application
gets a new (and different) viewport everytime the repaint handler or keyboard
handler is invoked. Do not attempt to save the pointers passed to these
functions. You should architecture your function calls to take a viewport
pointer as a parameter.
You can store the viewer manager pointer, however, and you should because
you will need it to make various inquiries.
POPUP DIALOGS
-------------
Popup dialogs fall into various categories and popups know how to respond
to screen resizes, so they are a good way to ask the user for input data.
They do not make sense as an entire application implementation strategy.
They pop up in the middle of the screen and you can only have one at a time.
There are several kinds of dialogs:
simple message -- Just pops up text and waits for the user to press
enter.
string -- asks the user for a string and also interprets the
tab key as a way of expand the existing string
with any files in the current directory that match
that string as a prefix. Similar to the way
bash and the Windows command line interpret tab.
Esc aborts input.
Multifield -- a collection of fields of various types that you
can construct. The user can enter them all or
none by pressing Esc to abort input.
FileSelector -- prompts the user for a filename by providing a list
alternatives to choose from. The user can scroll
in the list or add a new name or change the text of
an existing name.
All dialogs have a title and are painted in reversed video to delimit
them from the background text.
Dialog's are typically class object that have an operator += that lets you add
fields or strings. To construct most dialogs you do something roughly like
this:
CursorWindow::Dialog::DialogType d(some parameters);
d += "some strings";
d += new FieldType(name, parms);
if(d.popup(viewerManager_->window()))
{
return false; // the user aborted
}
Dialog fields typically have names. To get the data out of the dialog
you inquire the field value given a field name. If it is not there,
you assume the user didn't set the value.
ListViewers
-----------
The directory viewer shows you the files in a directory and lets you
issue commands on them. It is implemented as a derived class of the
ListViewer base class. Any simple list of items can be handled with
a ListViewer extension class. On the other hand, it only lets you
perform fairly simple commands on the list. See lib/directoryviewer.cxx.
The ListViewer base class handles most screen painting and keyboard
interactions. The derived class focuses on handling keyboard events
but is limited in how it can draw the lines. It does support
marking lines and special lines that are displayed in bold.
Table Viewers and Editors
-------------------------
The TableViewer and TableEditor classes present the user with rectangular
grids of strings. They are meant to be base classes from which real
editors and viewers are derived. There two examples in the editor.exe
program:
* The comma separated values editor (also handles tab separated values)
* The ScriptTableEditor that lets you supply a bash script that
that drives the scriptTableEditor viewer to display information
and let the user issue commands on it. The ScriptTableEditor is
meant primarily for displaying of data from the file system or
CM system (such as svn or git). It is not meant as a primary
editing too -- mainly it should be used to display script outputs
and let the user make simple choices. See the STV subdirectory
for scripts. From the DirectoryViewer, you can press the 'V' key
and it will prompt you for one of the table editor scripts to
execute. At this time the following table editor scripts exist:
* example.sh (heavily commented trivial example)
* svn.sh (pretty complete)
* git.sh (pretty useful)
* grep.sh (poor quality)
* ps.sh (toy wrapper around the system ps command)
* zip.sh (toy wrapper around unzip)
Tree Viewer
-----------
An indented list viewer that is currently used only to display the
program symbol database (TAGPP.tagpp) created by cpptagdb.exe.
You can derive from the base class and create your own viewers.