Writing Programs Using newt

Abstract
The newt windowing system is a
terminal-based window and widget
library designed for writing
applications with a simple, but
user-friendly, interface. While newt is
not intended to provide the rich
feature set advanced applications may
require, it has proven to be flexible
enough for a wide range of applications
(most notably, Red Hat's installation
process). This tutorial explains the
design philosophy behind newt and how
to use newt from your programs.

          -----------------------------------------------------------

Table of Contents
Introduction
Basic Newt Functions
Windows
Components

Introduction

Newt has a definite design philosophy behind it, and knowing that design makes
it significantly easier to craft robust newt applications. This tutorial
documents newt 0.30 --- older versions of newt had annoying inconsistencies in
it (which writing this tutorial pointed out), which were removed while this
tutorial was written. The latest version of newt is always available from Red
Hat.

-------------------------------------------------------------------------------

Background

Newt was originally designed for use in the install code for Red Hat Linux. As
this install code runs in an environment with limited resources (most
importantly limited filesystem space), newt's size was immediately an issue. To
help minimize its size, the following design decisions were made early in its
implementation:

  * newt does not use an event-driven architecture.

  * newt is written in C, not C++. While there has been interest in
    constructing C++ wrapper classes around the newt API, nothing has yet come
    of those ideas.

  * Windows must be created and destroyed as a stack (in other words, all newt
    windows behave as modal dialogs). This is probably the greatest
    functionality restriction of newt.

  * The tty keyboard is the only supported input device.

  * Many behaviours, such as widget traversal order, are difficult or
    impossible to change.

While newt provides a complete API, it does not handle the low-level screen
drawing itself. Instead, newt is layered on top of the screen management
capabilities of John E. Davis's S-Lang library.

-------------------------------------------------------------------------------

Designing newt applications

As newt is not event driven and forces modal windows (forcing window order to
behave like a stack), newt applications tend to look quite like other text-mode
programs. It is quite straightforward to convert a command line program which
uses simple user prompts into a newt application. Some of the programs run as
part of the Red Hat installation process (such as Xconfigurator and
mouseconfig) were originally written as simple terminal mode programs which
used line-oriented menus to get input from the user and were later converted
into newt applications (through a process affectionately known as newtering).
Such a conversion does not require changes to the control flow of most
applications. Programming newt is dramatically different from writing programs
for most other windowing systems as newt's API is not event driven. This means
that newt applications look dramatically different from programs written for
event-driven architectures such as Motif, gtk, or even Borland's old
TurboVision libraries. When you're designing your newt program, keep this
differentiation in mind. As long as you plan your application to call a
function to get input and then continue (rather then having your program called
when input is ready), programming with the newt libraries should be simple.

-------------------------------------------------------------------------------

Components

Displayable items in newt are known as components, which are analogous to the
widgets provided by most Unix widget sets. There are two main types of
components in newt, forms and everything else. Forms logically group components
into functional sets. When an application is ready to get input from a user, it
``runs a form'', which makes the form active and lets the user enter
information into the components the form contains. A form may contain any other
component, including other forms. Using subforms in this manner lets the
application change the details of how the user tabs between components on the
form, scroll regions of the screen, and control background colors for portions
of windows. Every component is of type newtComponent, which is an opaque type.
It's guaranteed to be a pointer though, which lets applications move it through
void pointers if the need arises. Variables of type newtComponent should never
be directly manipulated -- they should only be passed to newt functions. As
newtComponent variables are pointers, remember that they are always passed by
value -- if you pass a newtComponent to a function which manipulates it, that
component is manipulated everywhere, not just inside of that function (which is
nearly always the behaviour you want).

-------------------------------------------------------------------------------

Conventions

Newt uses a number of conventions to make it easier for programmers to use.

  * All functions which manipulate data structures take the data structure
    being modified as their first parameter. For example, all of the functions
    which manipulate forms expect the newtComponent for that form to be the
    first parameter.

  * As newt is loosely typed (forcing all of the components into a single
    variable makes coding easier, but nullifies the value of type checking),
    newt functions include the name of the type they are manipulating. An
    example of this is newtFormAddComponent(), which adds a component to a
    form. Note that the first parameter to this function is a form, as the name
    would suggest.

  * When screen coordinates are passed into a function, the x location precedes
    the y location. To help keep this clear, we'll use the words ``left'' and
    ``top'' to describe those indicators (with left corresponding to the x
    position).

  * When box sizes are passed, the horizontal width precedes the vertical
    width.

  * When both a screen location and a box size are being passed, the screen
    location precedes the box size.

  * When any component other then a form is created, the first two parameters
    are always the (left, right) location.

  * Many functions take a set of flags as the final parameter. These flags may
    be logically ORed together to pass more then one flag at a time.

  * Newt uses callback functions to convey certain events to the application.
    While callbacks differ slightly in their parameters, most of them allow the
    application to specify an arbitrary argument to be passed to the callback
    when the callback is invoked. This argument is always a void *, which
    allows the application great flexibility.

-------------------------------------------------------------------------------

Basic Newt Functions

While most newt functions are concerned with widgets or groups of widgets
(called grids and forms), some parts of the newt API deal with more global
issues, such as initializing newt or writing to the root window.

-------------------------------------------------------------------------------

Starting and Ending newt Services

There are three functions which nearly every newt application use. The first
two are used to initialize the system.

int newtInit(void);
void newtCls(void);

newtInit() should be the first function called by every newt program. It
initializes internal data structures and places the terminal in raw mode. Most
applications invoke newtCls() immediately after newtInit(), which causes the
screen to be cleared. It's not necessary to call newtCls() to use any of newt's
features, but doing so will normally give a much neater appearance. When a newt
program is ready to exit, it should call newtFinished().

int newtFinished(void);

newtFinished() restores the terminal to its appearance when newtInit() was
called (if possible -- on some terminals the cursor will be moved to the
bottom, but it won't be possible to remember the original terminal contents)
and places the terminal in its original input state. If this function isn't
called, the terminal will probably need to be reset with the reset command
before it can be used easily.

-------------------------------------------------------------------------------

Handling Keyboard Input

Normally, newt programs don't read input directly from the user. Instead, they
let newt read the input and hand it to the program in a semi-digested form.
Newt does provide a couple of simple functions which give programs (a bit of)
control over the terminal.

void newtWaitForKey(void);
void newtClearKeyBuffer(void);

The first of these, newtWaitForKey(), doesn't return until a key has been
pressed. The keystroke is then ignored. If a key is already in the terminal's
buffer, newtWaitForKey() discards a keystroke and returns immediately.
newtClearKeyBuffer() discards the contents of the terminal's input buffer
without waiting for additional input.

-------------------------------------------------------------------------------

Drawing on the Root Window

The background of the terminal's display (the part without any windows covering
it) is known as the root window (it's the parent of all windows, just like the
system's root directory is the parent of all subdirectories). Normally,
applications don't use the root window, instead drawing all of their text
inside of windows (newt doesn't require this though -- widgets may be placed
directly on the root window without difficulty). It is often desirable to
display some text, such as a program's name or copyright information, on the
root window, however. Newt provides two ways of displaying text on the root
window. These functions may be called at any time. They are the only newt
functions which are meant to write outside of the current window.

void newtDrawRootText(int left, int top, const char * text);

This function is straightforward. It displays the string text at the position
indicated. If either the left or top is negative, the position is measured from
the opposite side of the screen. The final measurement will seem to be off by
one though. For example, a top of -1 indicates the last line on the screen, and
one of -2 is the line above that. As it's common to use the last line on the
screen to display help information, newt includes special support for doing
exactly that. The last line on the display is known as the help line, and is
treated as a stack. As the value of the help line normally relates to the
window currently displayed, using the same structure for window order and the
help line is very natural. Two functions are provided to manipulate the help
line.

void newtPushHelpLine(const char * text);
void newtPopHelpLine(void);

The first function, newtPushHelpLine(), saves the current help line on a stack
(which is independent of the window stack) and displays the new line. If text
is NULL, newt's default help line is displayed (which provides basic
instructions on using newt). If text is a string of length 0, the help line is
cleared. For all other values of text, the passed string is displayed at the
bottom, left-hand corner of the display. The space between the end of the
displayed string the the right-hand edge of the terminal is cleared.
newtPopHelpLine() replaces the current help line with the one it replaced. It's
important not to call tt/newtPopHelpLine()/ more then newtPushHelpLine()!
Suspending Newt Applications By default, newt programs cannot be suspended by
the user (compare this to most Unix programs which can be suspended by pressing
the suspend key (normally ^Z). Instead, programs can specify a callback
function which gets invoked when the user presses the suspend key.

typedef void (*newtSuspendCallback)(void);

void newtSetSuspendCallback(newtSuspendCallback cb);

The suspend function neither expects nor returns any value, and can do whatever
it likes to when it is invoked. If no suspend callback is registered, the
suspend keystroke is ignored. If the application should suspend and continue
like most user applications, the suspend callback needs two other newt
functions.

void newtSuspend(void);
void newtResume(void);

newtSuspend() tells newt to return the terminal to its initial state. Once this
is done, the application can suspend itself (by sending itself a SIGTSTP, fork
a child program, or do whatever else it likes. When it wants to resume using
the newt interface, it must call newtResume before doing so. Note that suspend
callbacks are not signal handlers. When newtInit() takes over the terminal, it
disables the part of the terminal interface which sends the suspend signal.
Instead, if newt sees the suspend keystroke during normal input processing, it
immediately calls the suspend callback if one has been set. This means that
suspending newt applications is not asynchronous.

-------------------------------------------------------------------------------

Refreshing the Screen

To increase performance, S-Lang only updates the display when it needs to, not
when the program tells S-Lang to write to the terminal. ``When it needs to'' is
implemented as ``right before the we wait for the user to press a key''. While
this allows for optimized screen displays most of the time, this optimization
makes things difficult for programs which want to display progress messages
without forcing the user to input characters. Applications can force S-Lang to
immediately update modified portions of the screen by calling newtRefresh.

 1. The program wants to display a progress message, without forcing for the
    user to enter any characters.

 2. A misfeature of the program causes part of the screen to be corrupted.
    Ideally, the program would be fixed, but that may not always be practical.

-------------------------------------------------------------------------------

Other Miscellaneous Functions

As always, some function defy characterization. Two of newt's general function
fit this oddball category.

void newtBell(void);
void newtGetScreenSize(int * cols, int * rows);

The first sends a beep to the terminal. Depending on the terminal's settings,
this been may or may not be audible. The second function, newtGetScreenSize(),
fills in the passed pointers with the current size of the terminal.

-------------------------------------------------------------------------------

Basic newt Example

To help illustrate the functions presented in this section here is a short
sample newt program which uses many of them. While it doesn't do anything
interesting, it does show the basic structure of newt programs.

#include <newt.h>
#include <stdlib.h>

int main(void) {
    newtInit();
    newtCls();

    newtDrawRootText(0, 0, "Some root text");
    newtDrawRootText(-25, -2, "Root text in the other corner");

    newtPushHelpLine(NULL);
    newtRefresh();
    sleep(1);

    newtPushHelpLine("A help line");
    newtRefresh();
    sleep(1);

    newtPopHelpLine();
    newtRefresh();
    sleep(1);

    newtFinished();
}

-------------------------------------------------------------------------------

Windows

While most newt applications do use windows, newt's window support is actually
extremely limited. Windows must be destroyed in the opposite of the order they
were created, and only the topmost window may be active. Corollaries to this
are:

  * The user may not switch between windows.

  * Only the top window may be destroyed.

While this is quite a severe limitation, adopting it greatly simplifies both
writing newt applications and developing newt itself, as it separates newt from
the world of event-driven programming. However, this tradeoff between function
and simplicity may make newt unsuitable for some tasks.

-------------------------------------------------------------------------------

Creating Windows

There are two main ways of opening newt windows: with or without explicit
sizings. When grids (which will be introduced later in this tutorial) are used,
a window may be made to just fit the grid. When grids are not used, explicit
sizing must be given.

int newtCenteredWindow(int width, int height, const char * title);
int newtOpenWindow(int left, int top, int width, int height,
                   const char * title);

The first of these functions open a centered window of the specified size. The
title is optional -- if it is NULL, then no title is used. newtOpenWindow*( is
similar, but it requires a specific location for the upper left-hand corner of
the window.

-------------------------------------------------------------------------------

Destroying Windows

All windows are destroyed in the same manner, no matter how the windows were
originally created.

void newtPopWindow(void);

This function removes the top window from the display, and redraws the display
areas which the window overwrote.

-------------------------------------------------------------------------------

Components

Components are the basic user interface element newt provides. A single
component may be (for example) a listbox, push button checkbox, a collection of
other components. Most components are used to display information in a window,
provide a place for the user to enter data, or a combination of these two
functions. Forms, however, are a component whose primary purpose is not noticed
by the user at all. Forms are collections of components (a form may contain
another form) which logically relate the components to one another. Once a form
is created and had all of its constituent components added to it, applications
normally then run the form. This gives control of the application to the form,
which then lets the user enter data onto the form. When the user is done (a
number of different events qualify as ``done''), the form returns control to
the part of the application which invoked it. The application may then read the
information the user provided and continue appropriately. All newt components
are stored in a common data type, a newtComponent (some of the particulars of
newtComponents have already been mentioned. While this makes it easy for
programmers to pass components around, it does force them to make sure they
don't pass entry boxes to routines expecting push buttons, as the compiler
can't ensure that for them. We start off with a brief introduction to forms.
While not terribly complete, this introduction is enough to let us illustrate
the rest of the components with some sample code. We'll then discuss the
remainder of the components, and end this section with a more exhaustive
description of forms.

-------------------------------------------------------------------------------

Introduction to Forms

As we've mentioned, forms are simply collections of components. As only one
form can be active (or running) at a time, every component which the user
should be able to access must be on the running form (or on a subform of the
running form). A form is itself a component, which means forms are stored in
newtComponent data structures.

newtComponent newtForm(newtComponent vertBar, const char * help, int flags);

To create a form, call newtForm(). The first parameter is a vertical scrollbar
which should be associated with the form. For now, that should always be NULL
(we'll discuss how to create scrolling forms later in this section). The second
parameter, help, is currently unused and should always be NULL. The flags is
normally 0, and other values it can take will be discussed later. Now that
we've waved away the complexity of this function, creating a form boils down to
simply:

newtComponent myForm;

myForm = newtForm(NULL, NULL, 0);

After a form is created, components need to be added to it --- after all, an
empty form isn't terribly useful. There are two functions which add components
to a form.

void newtFormAddComponent(newtComponent form, newtComponent co);
void newtFormAddComponents(newtComponent form, ...);

The first function, newtFormAddComponent(), adds a single component to the form
which is passed as the first parameter. The second function is simply a
convenience function. After passing the form to newtFormAddComponents(), an
arbitrary number of components is then passed, followed by NULL. Every
component passed is added to the form. Once a form has been created and
components have been added to it, it's time to run the form.

newtComponent newtRunForm(newtComponent form);

This function runs the form passed to it, and returns the component which
caused the form to stop running. For now, we'll ignore the return value
completely. Notice that this function doesn't fit in with newt's normal naming
convention. It is an older interface which will not work for all forms. It was
left in newt only for legacy applications. It is a simpler interface than the
new newtFormRun() though, and is still used quite often as a result. When an
application is done with a form, it destroys the form and all of the components
the form contains.

void newtFormDestroy(newtComponent form);

This function frees the memory resources used by the form and all of the
components which have been added to the form (including those components which
are on subforms). Once a form has been destroyed, none of the form's components
can be used.

-------------------------------------------------------------------------------

Components

Non-form components are the most important user-interface component for users.
They determine how users interact with newt and how information is presented to
them.

-------------------------------------------------------------------------------

General Component Manipulation

There are a couple of functions which work on more then one type of components.
The description of each component indicates which (if any) of these functions
are valid for that particular component.

typedef void (*newtCallback)(newtComponent, void *);

void newtComponentAddCallback(newtComponent co, newtCallback f, void * data);
void newtComponentTakesFocus(newtComponent co, int val);

The first registers a callback function for that component. A callback function
is a function the application provides which newt calls for a particular
component. Exactly when (if ever) the callback is invoked depends on the type
of component the callback is attached to, and will be discussed for the
components which support callbacks. newtComponentTakesFocus() works on all
components. It allows the application to change which components the user is
allowed to select as the current component, and hence provide input to.
Components which do not take focus are skipped over during form traversal, but
they are displayed on the terminal. Some components should never be set to take
focus, such as those which display static text.

-------------------------------------------------------------------------------

Buttons

Nearly all forms contain at least one button. Newt buttons come in two flavors,
full buttons and compact buttons. Full buttons take up quit a bit of screen
space, but look much better then the single-row compact buttons. Other then
their size, both button styles behave identically. Different functions are used
to create the two types of buttons.

newtComponent newtButton(int left, int top, const char * text);
newtComponent newtCompactButton(int left, int top, const char * text);

Both functions take identical parameters. The first two parameters are the
location of the upper left corner of the button, and the final parameter is the
text which should be displayed in the button (such as ``Ok'' or ``Cancel'').

-------------------------------------------------------------------------------

Button Example

Here is a simple example of both full and compact buttons. It also illustrates
opening and closing windows, as well a simple form.

#include <newt.h>
#include <stdlib.h>

void main(void) {
    newtComponent form, b1, b2;
    newtInit();
    newtCls();

    newtOpenWindow(10, 5, 40, 6, "Button Sample");

    b1 = newtButton(10, 1, "Ok");
    b2 = newtCompactButton(22, 2, "Cancel");
    form = newtForm(NULL, NULL, 0);
    newtFormAddComponents(form, b1, b2, NULL);

    newtRunForm(form);

    newtFormDestroy(form);
    newtFinished();
}

-------------------------------------------------------------------------------

Labels

Labels are newt's simplest component. They display some given text and don't
allow any user input.

newtComponent newtLabel(int left, int top, const char * text);
void newtLabelSetText(newtComponent co, const char * text);

Creating a label is just like creating a button; just pass the location of the
label and the text it should display. Unlike buttons, labels do let the
application change the text in the label with newtLabelSetText. When the
label's text is changed, the label automatically redraws itself. It does not
clear out any old text which may be leftover from the previous time is was
displayed, however, so be sure that the new text is at least as long as the old
text.

-------------------------------------------------------------------------------

Entry Boxes

Entry boxes allow the user to enter a text string into the form which the
application can later retrieve.

typedef int (*newtEntryFilter)(newtComponent entry, void * data, int ch,
                               int cursor);

newtComponent newtEntry(int left, int top, const char * initialValue, int width,
                        char ** resultPtr, int flags);
void newtEntrySet(newtComponent co, const char * value, int cursorAtEnd);
char * newtEntryGetValue(newtComponent co);
void newtEntrySetFilter(newtComponent co, newtEntryFilter filter, void * data);

newtEntry() creates a new entry box. After the location of the entry box, the
initial value for the entry box is passed, which may be NULL if the box should
start off empty. Next, the width of the physical box is given. This width may
or may not limit the length of the string the user is allowed to enter; that
depends on the flags. The resultPtr must be the address of a char *. Until the
entry box is destroyed by newtFormDestroy(), that char * will point to the
current value of the entry box. It's important that applications make a copy of
that value before destroying the form if they need to use it later. The
resultPtr may be NULL, in which case the user must use the newtEntryGetValue()
function to get the value of the entry box. Entry boxes support a number of
flags:

NEWT_ENTRY_SCROLL

    If this flag is not specified, the user cannot enter text into the entry
    box which is wider then the entry box itself. This flag removes this
    limitation, and lets the user enter data of an arbitrary length.

NEWT_FLAG_HIDDEN

    If this flag is specified, the value of the entry box is not displayed.
    This is useful when the application needs to read a password, for example.

NEWT_FLAG_RETURNEXIT

    When this flag is given, the entry box will cause the form to stop running
    if the user pressed return inside of the entry box. This can provide a nice
    shortcut for users.

After an entry box has been created, its contents can be set by newtEntrySet().
After the entry box itself, the new string to place in the entry box is passed.
The final parameter, cursorAtEnd, controls where the cursor will appear in the
entry box. If it is zero, the cursor remains at its present location; a nonzero
value moves the cursor to the end of the entry box's new value. While the
simplest way to find the value of an entry box is by using a resultPtr, doing
so complicates some applications. newtEntryGetValue() returns a pointer to the
string which the entry box currently contains. The returned pointer may not be
valid once the user further modifies the entry box, and will not be valid after
the entry box has been destroyed, so be sure to save its value in a more
permanent location if necessary. Entry boxes allow applications to filter
characters as they are entered. This allows programs to ignore characters which
are invalid (such as entering a ^ in the middle of a phone number) and provide
intelligent aids to the user (such as automatically adding a '.' after the user
has typed in the first three numbers in an IP address). When a filter is
registered through newtEntrySetFilter(), both the filter itself and an
arbitrary void *, which passed to the filter whenever it is invoked, are
recorded. This data pointer isn't used for any other purpose, and may be NULL.
Entry filters take four arguments.

 1. The entry box which had data entered into it

 2. The data pointer which was registered along with the filter

 3. The new character which newt is considering inserting into the entry box

 4. The current cursor position (0 is the leftmost position)

The filter returns 0 if the character should be ignored, or the value of the
character which should be inserted into the entry box. Filter functions which
want to do complex manipulations of the string should use newtEntrySet() to
update the entry box and then return 0 to prevent the new character from being
inserted. When a callback is attached to a entry box, the callback is invoked
whenever the user moves off of the callback and on to another component. Here
is a sample program which illustrates the use of both labels and entry boxes.

#include <newt.h>
#include <stdlib.h>
#include <stdio.h>

void main(void) {
    newtComponent form, label, entry, button;
    char * entryValue;

    newtInit();
    newtCls();

    newtOpenWindow(10, 5, 40, 8, "Entry and Label Sample");

    label = newtLabel(1, 1, "Enter a string");
    entry = newtEntry(16, 1, "sample", 20, &entryValue,
                      NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
    button = newtButton(17, 3, "Ok");
    form = newtForm(NULL, NULL, 0);
    newtFormAddComponents(form, label, entry, button, NULL);

    newtRunForm(form);

    newtFinished();

    printf("Final string was: %s\n", entryValue);

    /* We cannot destroy the form until after we've used the value
       from the entry widget. */
    newtFormDestroy(form);
}

-------------------------------------------------------------------------------

Checkboxes

Most widget sets include checkboxes which toggle between two value (checked or
not checked). Newt checkboxes are more flexible. When the user presses the
space bar on a checkbox, the checkbox's value changes to the next value in an
arbitrary sequence (which wraps). Most checkboxes have two items in that
sequence, checked or not, but newt allows an arbitrary number of value. This is
useful when the user must pick from a limited number of choices. Each item in
the sequence is a single character, and the sequence itself is represented as a
string. The checkbox components displays the character which currently
represents its value the left of a text label, and returns the same character
as its current value. The default sequence for checkboxes is " *", with ' '
indicating false and '*' true.

newtComponent newtCheckbox(int left, int top, const char * text, char defValue,
                           const char * seq, char * result);
char newtCheckboxGetValue(newtComponent co);

Like most components, the position of the checkbox is the first thing passed to
the function that creates one. The next parameter, text, is the text which is
displayed to the right of the area which is checked. The defValue is the
initial value for the checkbox, and seq is the sequence which the checkbox
should go through (defValue must be in seq. seq may be NULL, in which case " *"
is used. The final parameter, result, should point to a character which the
checkbox should always record its current value in. If result is NULL,
newtCheckboxGetValue() must be used to get the current value of the checkbox.
newtCheckboxGetValue() is straightforward, returning the character in the
sequence which indicates the current value of the checkbox If a callback is
attached to a checkbox, the callback is invoked whenever the checkbox responds
to a user's keystroke. The entry box may respond by taking focus or giving up
focus, as well as by changing its current value.

-------------------------------------------------------------------------------

Radio Buttons

Radio buttons look very similar to checkboxes. The key difference between the
two is that radio buttons are grouped into sets, and exactly one radio button
in that set may be turned on. If another radio button is selected, the button
which was selected is automatically deselected.

newtComponent newtRadiobutton(int left, int top, const char * text,
                              int isDefault, newtComponent prevButton);
newtComponent newtRadioGetCurrent(newtComponent setMember);

Each radio button is created by calling newtRadiobutton(). After the position
of the radio button, the text displayed with the button is passed. isDefault
should be nonzero if the radio button is to be turned on by default. The final
parameter, prevMember is used to group radio buttons into sets. If prevMember
is NULL, the radio button is assigned to a new set. If the radio button should
belong to a preexisting set, prevMember must be the previous radio button added
to that set. Discovering which radio button in a set is currently selected
necessitates newtRadioGetCurrent(). It may be passed any radio button in the
set you're interested in, and it returns the radio button component currently
selected. Here is an example of both checkboxes and radio buttons.

#include <newt.h>
#include <stdlib.h>
#include <stdio.h>

void main(void) {
    newtComponent form, checkbox, rb[3], button;
    char cbValue;
    int i;

    newtInit();
    newtCls();

    newtOpenWindow(10, 5, 40, 11, "Checkboxes and Radio buttons");

    checkbox = newtCheckbox(1, 1, "A checkbox", ' ', " *X", &cbValue);

    rb[0] = newtRadiobutton(1, 3, "Choice 1", 1, NULL);
    rb[1] = newtRadiobutton(1, 4, "Choice 2", 0, rb[0]);
    rb[2] = newtRadiobutton(1, 5, "Choice 3", 0, rb[1]);

    button = newtButton(1, 7, "Ok");

    form = newtForm(NULL, NULL, 0);
    newtFormAddComponent(form, checkbox);
    for (i = 0; i < 3; i++)
        newtFormAddComponent(form, rb[i]);
    newtFormAddComponent(form, button);

    newtRunForm(form);
    newtFinished();

    /* We cannot destroy the form until after we've found the current
       radio button */

    for (i = 0; i < 3; i++)
        if (newtRadioGetCurrent(rb[0]) == rb[i])
            printf("radio button picked: %d\n", i);
    newtFormDestroy(form);

    /* But the checkbox's value is stored locally */
    printf("checkbox value: '%c'\n", cbValue);
}

-------------------------------------------------------------------------------

Scales

It's common for programs to need to display a progress meter on the terminal
while it performs some length operation (it behaves like an anesthetic). The
scale component is a simple way of doing this. It displays a horizontal bar
graph which the application can update as the operation continues.

newtComponent newtScale(int left, int top, int width, long long fullValue);
void newtScaleSet(newtComponent co, unsigned long long amount);

When the scale is created with newtScale, it is given the width of the scale
itself as well as the value which means that the scale should be drawn as full.
When the position of the scale is set with newtScaleSet(), the scale is told
the amount of the scale which should be filled in relative to the fullAmount.
For example, if the application is copying a file, fullValue could be the
number of bytes in the file, and when the scale is updated newtScaleSet() would
be passed the number of bytes which have been copied so far.

-------------------------------------------------------------------------------

Textboxes

Textboxes display a block of text on the terminal, and is appropriate for
display large amounts of text.

newtComponent newtTextbox(int left, int top, int width, int height, int flags);
void newtTextboxSetText(newtComponent co, const char * text);

newtTextbox() creates a new textbox, but does not fill it with data. The
function is passed the location for the textbox on the screen, the width and
height of the textbox (in characters), and zero or more of the following flags:

NEWT_FLAG_WRAP

    All text in the textbox should be wrapped to fit the width of the textbox.
    If this flag is not specified, each newline delimited line in the text is
    truncated if it is too long to fit. When newt wraps text, it tries not to
    break lines on spaces or tabs. Literal newline characters are respected,
    and may be used to force line breaks.

    NEWT_FLAG_SCROLL

        The text box should be scrollable. When this option is used, the
        scrollbar which is added increases the width of the area used by the
        textbox by 2 characters; that is the textbox is 2 characters wider then
        the width passed to newtTextbox().

    After a textbox has been created, text may be added to it through
    newtTextboxSetText(), which takes only the textbox and the new text as
    parameters. If the textbox already contained text, that text is replaced by
    the new text. The textbox makes its own copy of the passed text, so these
    is no need to keep the original around unless it's convenient.

-------------------------------------------------------------------------------

Reflowing Text

When applications need to display large amounts of text, it's common not to
know exactly where the linebreaks should go. While textboxes are quite willing
to scroll the text, the programmer still must know what width the text will
look ``best'' at (where ``best'' means most exactly rectangular; no lines much
shorter or much longer then the rest). This common is especially prevalent in
internationalized programs, which need to make a wide variety of message string
look god on a screen. To help with this, newt provides routines to reformat
text to look good. It tries different widths to figure out which one will look
``best'' to the user. As these commons are almost always used to format text
for textbox components, newt makes it easy to construct a textbox with reflowed
text.

char * newtReflowText(char * text, int width, int flexDown, int flexUp,
                      int * actualWidth, int * actualHeight);
newtComponent newtTextboxReflowed(int left, int top, char * text, int width,
                                  int flexDown, int flexUp, int flags);
int newtTextboxGetNumLines(newtComponent co);

newtReflowText() reflows the text to a target width of width. The actual width
of the longest line in the returned string is between width - flexDown and
width + flexUp; the actual maximum line length is chosen to make the displayed
check look rectangular. The ints pointed to by actualWidth and actualHeight are
set to the width of the longest line and the number of lines in in the returned
text, respectively. Either one may be NULL. The return value points to the
reflowed text, and is allocated through malloc(). When the reflowed text is
being placed in a textbox it may be easier to use newtTextboxReflowed(), which
creates a textbox, reflows the text, and places the reflowed text in the
listbox. It's parameters consist of the position of the final textbox, the
width and flex values for the text (which are identical to the parameters
passed to newtReflowText(), and the flags for the textbox (which are the same
as the flags for newtTextbox(). This function does not let you limit the height
of the textbox, however, making limiting it's use to constructing textboxes
which don't need to scroll. To find out how tall the textbox created by
newtTextboxReflowed() is, use newtTextboxGetNumLines(), which returns the
number of lines in the textbox. For textboxes created by newtTextboxReflowed(),
this is always the same as the height of the textbox. Here's a simple program
which uses a textbox to display a message.

#include <newt.h>
#include <stdlib.h>

char message[] = "This is a pretty long message. It will be displayed "
                 "in a newt textbox, and illustrates how to construct "
                 "a textbox from arbitrary text which may not have "
                 "very good line breaks.\n\n"
                 "Notice how literal \\n characters are respected, and "
                 "may be used to force line breaks and blank lines.";

void main(void) {
    newtComponent form, text, button;

    newtInit();
    newtCls();

    text = newtTextboxReflowed(1, 1, message, 30, 5, 5, 0);
    button = newtButton(12, newtTextboxGetNumLines(text) + 2, "Ok");

    newtOpenWindow(10, 5, 37,
                   newtTextboxGetNumLines(text) + 7, "Textboxes");

    form = newtForm(NULL, NULL, 0);
    newtFormAddComponents(form, text, button, NULL);

    newtRunForm(form);
    newtFormDestroy(form);
    newtFinished();
}

-------------------------------------------------------------------------------

Scrollbars

Scrollbars (which, currently, are always vertical in newt), may be attached to
forms to let them contain more data then they have space for. While the actual
process of making scrolling forms is discussed at the end of this section,
we'll go ahead and introduce scrollbars now so you'll be ready.

newtComponent newtVerticalScrollbar(int left, int top, int height,
                                    int normalColorset, int thumbColorset);

When a scrollbar is created, it is given a position on the screen, a height,
and two colors. The first color is the color used for drawing the scrollbar,
and the second color is used for drawing the thumb. This is the only place in
newt where an application specifically sets colors for a component. It's done
here to let the colors a scrollbar use match the colors of the component the
scrollbar is mated too. When a scrollbar is being used with a form,
normalColorset is often NEWT_COLORSET_WINDOW and thumbColorset
NEWT_COLORSET_ACTCHECKBOX. Of course, feel free to peruse <newt.h> and pick
your own colors. As the scrollbar is normally updated by the component it is
mated with, there is no public interface for moving the thumb.

-------------------------------------------------------------------------------

Listboxes

Listboxes are the most complicated components newt provides. They can allow a
single selection or multiple selection, and are easy to update. Unfortunately,
their API is also the least consistent of newt's components. Each entry in a
listbox is a ordered pair of the text which should be displayed for that item
and a key, which is a void * that uniquely identifies that listbox item. Many
applications pass integers in as keys, but using arbitrary pointers makes many
applications significantly easier to code.

-------------------------------------------------------------------------------

Basic Listboxes

Let's start off by looking at the most important listbox functions.

newtComponent newtListbox(int left, int top, int height, int flags);
int newtListboxAppendEntry(newtComponent co, const char * text,
                           const void * data);
void * newtListboxGetCurrent(newtComponent co);
void newtListboxSetWidth(newtComponent co, int width);
void newtListboxSetCurrent(newtComponent co, int num);
void newtListboxSetCurrentByKey(newtComponent co, void * key);

A listbox is created at a certain position and a given height. The height is
used for two things. First of all, it is the minimum height the listbox will
use. If there are less items in the listbox then the height, suggests the
listbox will still take up that minimum amount of space. Secondly, if the
listbox is set to be scrollable (by setting the NEWT_FLAG_SCROLL flag, the
height is also the maximum height of the listbox. If the listbox may not
scroll, it increases its height to display all of its items. The following
flags may be used when creating a listbox:

NEWT_FLAG_SCROLL

    The listbox should scroll to display all of the items it contains.

NEWT_FLAG_RETURNEXIT

    When the user presses return on an item in the list, the form should
    return.

NEWT_FLAG_BORDER

    A frame is drawn around the listbox, which can make it easier to see which
    listbox has the focus when a form contains multiple listboxes.

NEWT_FLAG_MULTIPLE

    By default, a listbox only lets the user select one item in the list at a
    time. When this flag is specified, they may select multiple items from the
    list.

Once a listbox has been created, items are added to it by invoking
newtListboxAppendEntry(), which adds new items to the end of the list. In
addition to the listbox component, newtListboxAppendEntry() needs both elements
of the (text, key) ordered pair. For lists which only allow a single selection,
newtListboxGetCurrent() should be used to find out which listbox item is
currently selected. It returns the key of the currently selected item.
Normally, a listbox is as wide as its widest element, plus space for a
scrollbar if the listbox is supposed to have one. To make the listbox any
larger then that, use newtListboxSetWidth(), which overrides the natural list
of the listbox. Once the width has been set, it's fixed. The listbox will no
longer grow to accommodate new entries, so bad things may happen! An
application can change the current position of the listbox (where the selection
bar is displayed) by calling newtListboxSetCurrent() or
newtListboxSetCurrentByKey(). The first sets the current position to the entry
number which is passed as the second argument, with 0 indicating the first
entry. newtListboxSetCurrentByKey() sets the current position to the entry
whose key is passed into the function.

-------------------------------------------------------------------------------

Manipulating Listbox Contents

While the contents of many listboxes never need to change, some applications
need to change the contents of listboxes regularly. Newt includes complete
support for updating listboxes. These new functions are in addition to
newtListboxAppendEntry(), which was already discussed.

void newtListboxSetEntry(newtComponent co, void * key, const char * text);
int newtListboxInsertEntry(newtComponent co, const char * text,
                           const void * data, void * key);
int newtListboxDeleteEntry(newtComponent co, void * key);
void newtListboxClear(newtComponent co);

The first of these, newtListboxSetEntry(), updates the text for a key which is
already in the listbox. The key specifies which listbox entry should be
modified, and text becomes the new text for that entry in the listbox.
newtListboxInsertEntry() inserts a new listbox entry after an already existing
entry, which is specified by the key parameter. The text and data parameters
specify the new entry which should be added. Already-existing entries are
removed from a listbox with newtListboxDeleteEntry(). It removes the listbox
entry with the specified key. If you want to remove all of the entries from a
listbox, use newtListboxClear().

-------------------------------------------------------------------------------

Multiple Selections

When a listbox is created with NEWT_FLAG_MULTIPLE, the user can select multiple
items from the list. When this option is used, a different set of functions
must be used to manipulate the listbox selection.

void newtListboxClearSelection(newtComponent co);
void **newtListboxGetSelection(newtComponent co, int *numitems);
void newtListboxSelectItem(newtComponent co, const void * key,
                           enum newtFlagsSense sense);

The simplest of these is newtListboxClearSelection(), which deselects all of
the items in the list (listboxes which allow multiple selections also allow
zero selections). newtListboxGetSelection() returns a pointer to an array which
contains the keys for all of the items in the listbox currently selected. The
int pointed to by numitems is set to the number of items currently selected
(and hence the number of items in the returned array). The returned array is
dynamically allocated, and must be released through free().
newtListboxSelectItem() lets the program select and deselect specific listbox
entries. The key of the listbox entry is being affected is passed, and sense is
one of NEWT_FLAGS_RESET, which deselects the entry, NEWT_FLAGS_SET, which
selects the entry, or NEWT_FLAGS_TOGGLE, which reverses the current selection
status.

-------------------------------------------------------------------------------

Advanced Forms

Forms, which tie components together, are quite important in the world of newt.
While we've already discussed the basics of forms, we've omitted many of the
details.

-------------------------------------------------------------------------------

Exiting From Forms

Forms return control to the application for a number of reasons:

  * A component can force the form to exit. Buttons do this whenever they are
    pushed, and other components exit when NEWT_FLAG_RETURNEXIT has been
    specified.

  * Applications can setup hot keys which cause the form to exit when they are
    pressed.

  * Newt can exit when file descriptors are ready to be read or ready to be
    written to.

By default, newt forms exit when the F12 key is pressed (F12 is setup as a hot
key by default). Newt applications should treat F12 as an ``Ok'' button. If
applications don't want F12 to exit the form, they can specify NEWT_FLAG_NOF12
as flag when creating the form with newtForm.

void newtFormAddHotKey(newtComponent co, int key);
void newtFormWatchFd(newtComponent form, int fd, int fdFlags);

void newtDrawForm(newtComponent form);
newtComponent newtFormGetCurrent(newtComponent co);
void newtFormSetCurrent(newtComponent co, newtComponent subco);
void newtFormRun(newtComponent co, struct newtExitStruct * es);

newtComponent newtForm(newtComponent vertBar, const char * help, int flags);
void newtFormSetBackground(newtComponent co, int color);
void newtFormSetHeight(newtComponent co, int height);
void newtFormSetWidth(newtComponent co, int width);

