DMM (Data entry and Menu screen Manager) - version 1.1 part 1/2

Rob Bernardo rob at mtdiablo.Concord.CA.US
Tue Jun 18 11:21:51 AEST 1991


>From the README file:

DMM is a set of C library functions that provide a fairly object-oriented
front end to curses for the management of data entry and menu screens.
It requires the System V version of curses.

The sources for these functions are in dmm.c and dmm.h.  A make file is
provided.  The make file and sources compile as is under SUN 0S 4.03, 4.1
and 4.1.1.  They may need minor modifications for compilation on other
flavors of UNIX.

A section 3 manual page is included; it details the use of the functions
as well as the look and feel of the screens they produce.

A demo program is included and can be compiled using the make file.
Using DMM effectively can be complex and the demo program demonstrates
various DMM facilities and points out highlights of the demo sources.

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
# This is part 1 of a multipart archive                                    
# do not concatenate these parts, unpack them in order with /bin/sh        
#
#	Run the following text with /bin/sh to create:
#	  Makefile
#	  README
#	  dmm.3
#	  dmm.c
#	  dmm.h
#	  dmmdemo.h
#	  dmmdemo1.c
#	  dmmdemo2.c
#	  dmmdemo3.c
#	  dmmdemo4.c
#	  dmmdemo5.c
#	  dmmdemo6.c
#
if test -r s2_seq_.tmp
then echo "Must unpack archives in sequence!"
     next=`cat s2_seq_.tmp`; echo "Please unpack part $next next"
     exit 1; fi
echo "x - extracting Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X# @(#)Makefile	1.1 Copyright (c) 1991 Robert Bernardo, rob at mtdiablo.Concord.CA.US,
X# an employee of Pande, Inc.  Permission to use granted only if this
X# notice is not removed from this make file produced from it.
X
XCC = /usr/5bin/cc
X
X
XDEMOOBJS =  dmmdemo1.o dmmdemo2.o dmmdemo3.o dmmdemo4.o dmmdemo5.o dmmdemo6.o
Xdmmdemo: $(DEMOOBJS) dmm.o
X	$(CC) $(DEMOOBJS) dmm.o -lcurses -o dmmdemo
X
Xdmm:	dmm.o
X
Xclean:
X	rm -f *.o dmmdemo core
X
Xlint:
X	lint -I/usr/5include *.c
SHAR_EOF
chmod 0444 Makefile || echo "restore of Makefile fails"
set `wc -c Makefile`;Sum=$1
if test "$Sum" != "462"
then echo original size 462, current size $Sum;fi
echo "x - extracting README (Text)"
sed 's/^X//' << 'SHAR_EOF' > README &&
XThis is the 1.1 version of DMM.  It is a beta version.  Bugs, fixes,
Xcomments and suggestions should be sent to Robert Bernardo, at
Xrob at mtdiablo.Concord.CA.US.
X
XDMM is a set of C library functions that provide a fairly object-oriented
Xfront end to curses for the management of data entry and menu screens.
XIt requires the System V version of curses.
X
XThe sources for these functions are in dmm.c and dmm.h.  A make file is
Xprovided.  The make file and sources compile as is under SUN 0S 4.03, 4.1
Xand 4.1.1.  They may need minor modifications for compilation on other
Xflavors of UNIX.
X
XA section 3 manual page is included; it details the use of the functions
Xas well as the look and feel of the screens they produce.
X
XA demo program is included and can be compiled using the make file.
XUsing DMM effectively can be complex and the demo program demonstrates
Xvarious DMM facilities and points out highlights of the demo sources.
X
X@(#)README	1.1 Copyright (c) 1991 Robert Bernardo,
Xrob at mtdiablo.Concord.CA.US, an employee of Pande, Inc.  Permission to
Xuse DMM granted only if the copyright notice is not removed from any of
Xthe component files nor from any binaries produced from them.
SHAR_EOF
chmod 0444 README || echo "restore of README fails"
set `wc -c README`;Sum=$1
if test "$Sum" != "1183"
then echo original size 1183, current size $Sum;fi
echo "x - extracting dmm.3 (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmm.3 &&
X.\ @(#)dmm.3	1.1
X.TH DMM 3 6/13/91
X.SH NAME
XdmmRun, dmmReview, dmmInit, dmmClose, dmmPutMsgs, dmmPutMsgsTimer, dmmPutMsgsHit, dmmPutMsgsNext, dmmClear, dmmRmMsgs, dmmGetNumLines - manage data entry and menu screens
X.SH SYNOPSIS
X.nf
X.B #include <dmm.h>
X.PP
X.B extern DMMRETTYPE dmmRetType;
X.PP
X.B int dmmRun(startField, screen, menu, drawFlag, valFlag)
X.B int startField;
X.B DMMSCREEN screen;
X.B DMMMENU menu;
X.B DMMDRAWFLAG drawFlag;
X.B DMMVALFLAG valFlag;
X.PP
X.B int dmmReview(screen)
X.B DMMSCREEN screen;
X.PP
X.B void dmmInit()
X.PP
X.B void dmmClose()
X.PP
X.B void dmmPutMsgs(mesgblock)
X.B DMMMESGBLK mesgblock;
X.PP
X.B void dmmPutMsgsTimer(mesgblock, duration)
X.B DMMMESGBLK mesgblock;
X.B unsigned duration;
X.PP
X.B void dmmPutMsgsHit(mesgblock)
X.B DMMMESGBLK mesgblock;
X.PP
X.B void dmmPutMsgsNext(mesgblock)
X.B DMMMESGBLK mesgblock;
X.PP
X.B void dmmClear()
X.PP
X.B void dmmRmMsgs(removetype)
X.B DMMREMOVETYPE removetype;
X.PP
X.B int dmmGetNumLines()
X.fi
X.SH DESCRIPTION
X.IR "Data entry and Menu Manager" ", " DMM ,
Xis a set of functions that
Xprovide a fairly object-oriented interface to
X.I curses
Xand enforce a particular look and feel in managing data entry and 
Xmenu screens.
XThe next two subsections describe this look and feel; subsequent subsections
Xdescribe the use of the functions.
X.SS Screen appearance
X.PP
XThe screen consists of three
X.I curses
Xwindows.
X.PP
XThe top line is an instruction window that displays a general user instruction
Xthat varies with the state that the screen is in.
X.PP
XThe next two lines are used by the menu.
XThe menu is presented as a ring menu.
XA scrollable list of typically one-word menu items is 
Xdisplayed horizontally on the first of the two lines.
XWhen the menu is active, one menu item is considered current and is highlighted.
XThe second line of the menu window is used to display a longer description
Xof the current menu item.
XThrough input keystrokes, the user can make a particular menu item current,
Xand then with a particular keystroke can confirm the
Xcurrently selected menu item as the choice made.
X.PP
XThe rest of the screen is the data entry window. Visually, this window appears
Xto consist of two sorts of items: editable fields and static, non-editable 
Xphrases (used for headings and for labels of the editable fields).
XTypically the two are displayed with different attributes (e.g. editable
Xfields displayed highlighted and the static fields displayed plain).
XWhen the data entry window is active, one editable field is considered
Xcurrent, and the cursor is placed inside it.
XTypically the current editable field is displayed with a different
Xhighlighting attribute from the other editable fields.
XThe user can edit the current field, or move to another field,
Xmaking it current.
XWhen the user enters a keystroke to move to another field, the value of
Xthe then-current field may be audited and,
Xif found in error, an error message displayed and the cursor
Xis moved to its first character position;
Xotherwise the cursor is moved to first character position of that other field,
Xwhich is made current.
X.PP
XWhile the screen is interactive,
Xeither the menu or the data entry window will be active.
XThrough a particular keystroke, the user can switch between these two states.
XTypically, a screen consists of both a menu and a data entry window (though
Xonly one of the two are required) and starts with the data entry window
Xactive and a program-specified field current.
XThe user may enter data in the editable fields and then go to the menu
Xto select an action to happen, such as to go forward with the data as entered,
Xor perhaps to ignore the data and just pop back up to a higher menu.
X.PP
XIn either menu or data entry state, the user can enter a keystroke that
Xdisplays help information.
X.SS User input
X.PP
XThe following user input keystrokes have similar effects 
Xin both menu and data entry states:
X.PD 0
X.TP 20
Xcontrol-g
XToggles between the menu and data entry states.
X.TP 20
Xcontrol-h
XDisplays a screen giving this keystroke-effect information.
X.TP 20
Xcontrol-r
XRedraws the entire screen.
X.TP 20
Xtab, control-n
XMakes the next editable field or menu item current.
X.TP 20
Xcontrol-p
XMakes the preceding editable field or menu item current.
X.PD
X.PP
XThe following user input keystrokes are particular to the menu state:
X.PD 0
X.TP 20
Xcarriage return
XChoses the current menu item.
X.TP 20
Xalpha character
XMakes current the single menu item that begins with the alpha character.
XIf there is not exactly one such menu item, the screen is flashed.
X.PD
X.PP
XThe following user input keystrokes are particular to the data entry state:
X.PD 0
X.TP 20
Xcontrol-c
XClears current editable field from current position to end of field and makes
Xthe next editable field current.
X.TP 20
Xcarriage return
XMakes the the next editable field current.
X.TP 20
Xarrow key
XMoves the cursor in the indicated direction within the current editable field
Xif possible.
X.TP 20
Xdelete
XErases the character to the left of the cursor, i.e. 
Xreplaces it with a blank and moves the cursor left,
Xif not in the first character position in the current editable field.
XIf it is in the last character position, and the last character is not blank,
Xit erases the character under the cursor and leaves the cursor there.
X.TP 20
Xprinting character
XInserts the character in the current editable field at the cursor position
X(and advances the cursor within the field
Xif not at the last character position of the field)
X.PD
X.PP
XAny other keystroke will flash the screen.
X.SS Screen specification
X.PP
XAn invocation of the function
X.B dmmRun
Xmanages the display and operation of a screen
Xand engages the user in interaction with the screen.
X.PP
XThe layout of the data entry window is specified by
X.IR screen ,
Xa null terminated array of pointers to DMMFIELD structures, defined in
X.IR dmm.h .
X.PP
XEach DMMFIELD describes one data entry field. Its elements describe
Xtwo parts of the data entry field. The label elements describe the
Xconstant part of the field, which may be used for display of headings,
Xdata or a label for the editable part. The input elements describe the editable
Xpart, a field on the screen where the user can move the cursor and input data.
XThe editable part of a field is realized as a (nearly) rectangular area within
Xthe window.
X.TP 20
XlabelValue
XA non-editable string to be displayed.
XA NULL value indicates no string to display.
X.TP 20
X.PD 0
XlabelCol,
X.TP 20
X.PD
XlabelLine
XThe coordinates in the data entry window where the first character of
X.I labelValue
Xis displayed.
XA negative line number represents lines from the bottom
Xof the window, the bottom line represented with -1.
X.TP 20
XlabelAttrib
XThe attributes with which the label is displayed, given as the logical
X.IR or -ing
Xof the attribute constants given in <curses.h>, or  0L for no attributes.
X.TP 20
XinputLength
XThe number of data characters to accept from the user for editable part of
Xthe field.
XThis governs the total size of the rectangular input area.
XIf 0,
Xthe field is considered to have no editable part;
Xthis is useful for the presentation of display-only data on the screen,
Xe.g. headings.
X.TP 20
XinputEditValue
XA buffer of length 
X.I inputLength
X+ 1 (for the terminating null) used to store the data as entered or 
Xedited by the user.
X.TP
XinputInitValue
XA string with an initial value to be copied to inputEditValue before user
Xinteraction.
X(This is dependent on the value of 
X.I valFlag
X- see below.)
XThe string must no be longer than
X.I inputLength.
XA NULL value is treated the same as a zero-length string.
X.TP 20
X.PD 0
XinputCol,
X.TP 20
X.PD
XinputLine
XThe coordinates in the data entry window where the upper left corner
Xof the input area is displayed.
XA negative column value designates a column position that
Xis that many intervening columns to the right of the last character of
X.IR labelValue .
XA negative line number represents lines from the bottom
Xof the window, the bottom line represented with -1.
X.TP 20
XinputMaxCols
XThe width of the rectangular input area. If smaller than
X.I inputLength
Xthe rectangular area will be more than one line in height.
XIf 0,
Xthe width is taken from
X.IR inputLength ,
Xi.e. the rectangular area will be exactly one line.
X.TP
X.PD 0
XinputFullLines,
X.TP 20
X.PD
XinputShortCols
XTo be left unset by the calling function and calculated by
X.BR dmmRun .
XThey represent the number of lines of full width in the rectangular area
Xand the number of columns in the last ``remainder'' line of the rectangular
Xarea, respectively.
XActually, because of the ``remainder'' line, the rectangular area is not
Xalways an exact rectangle.
X.TP 20
XinputAttrib
XThe attributes with which the rectangular area is displayed when 
Xthe field is not current.
XSee
X.I labelAttrib
Xfor a description of possible values.
X.TP 20
XinputEditAttrib
XThe attributes with which the rectangular area is displayed when the field
Xis current.
XSee
X.I labelAttrib
Xfor a description of possible values.
XThe value A_INVIS can be used for such things as password fields.
XDespite the warning in <curses.h> that this value is not supported,
Xit is safe to use because its realization within
X.I DMM
Xis independent of that of
X.IR curses .
X.TP 20
XinputInstructVec
XA null terminated array of string pointers which should contain detailed
Xinstructions to the user on how to edit the field.
XThey are displayed, when the field is current,
Xat the bottom of the screen, one string per line.
XA NULL value indicates no instructions to display.
X.TP
XinputNumInstructs
XTo be left unset by the calling function and calculated by
X.B dmmRun
Xas the number of strings in
X.IR inputInstructVec .
X.TP 20
XinputAudit
XA pointer to a function that returns an
X.IR int .
XIt is invoked with 
X.I inputEditValue
Xas its single argument
Xwhen the user enters a keystroke that would make the field non-current.
XThis function is useful for auditing the value of
X.I inputEditValue
Xand displaying audit error messages to the user (using the message display
Xfunctions described below).
XIf NULL, it is not invoked.
XThe function must accept a single 
X.I char *
Xargument and must return one of the following DMMAUDITRETURN values, defined in 
X.IR dmm.h :
X.RS 20
X.TP 20
XdmmAuditOkay
XWhen returned the cursor is advanced to the appropriate editable field.
XUseful when the value of 
X.I inputEditValue
Xis okay.
X.TP 20
XdmmAuditBad
XWhen returned the cursor is moved to the first position of the current input
Xfield.
XUseful when the value of 
X.I inputEditValue
Xis needs correction by the user.
X.TP 20
X.PD 0
XdmmAuditReturnOkay,
X.TP 20
X.PD 
XdmmAuditReturnBad
XUseful when an inter-field audit needs to be performed.
XThese two values cause
X.B dmmRun
Xto return with
X.B dmmRetType
Xset to
X.I dmmOkayField
Xor
X.IR dmmBadField ,
Xrespectively, and the return value of
X.B dmmRun
Xset to the number of the current editable field.
XThe calling function can then do its inter-field audit, such as might be 
Xnecessary when the two fields are month-of-year and day-of-month, where
Xlegal values are interdependent and then reinvoke
X.BR dmmRun .
XSee the discussion of
X.I startField
Xbelow for further details on reinvocations of
X.B dmmRun
Xafter an inter-field audit.
X.RE
X.TP 20
X.PD 0
XnextInField,
X.TP 20
X.PD
XprevInField
XTo be left unset by the calling function and calculated by
X.BR dmmRun .
X.PP
XSome user keystrokes cause the next or preceding editable field to 
Xbecome current.
XThe order of editable fields is taken from the order of the DMMFIELD
Xstructures in
X.IR screen ;
Xthis list is considered circular,
Xe.g. the editable field that comes next after the
Xlast editable field is the first editable field in the
X.I screen
Xarray.
X.PP
XIf there are no fields to display, i.e. if this is a menu-only screen,
X.I screen
Xshould be NULL.
X.PP
XThe menu window is specified by
X.IR menu ,
Xa null terminated array of pointers to DMMENUITEM structures, defined in
X.IR dmm.h .
X.PP
XEach DMMMENUITEM describes one menu item, the elements of which are as follows:
X.TP 20
Xlabel
XA short string to be displayed in the menu item list.
X.TP 20
Xdescript
XA longer, more explanatory string that is displayed on the
Xsecond line of the menu window when
Xthe menu item is current.
X.TP 20
X.PD 0
Xcol,
X.TP 20
X.PD
Xpage
XTo be left unset by the calling function and calculated by
X.BR dmmRun .
X.PP
XIf there is to be no menu for the screen,
X.I menu
Xshould be NULL.
X.PP
XThe value of
X.I drawFlag
Xdetermines whether internal window drawing and calculations need to
Xtake place.
XPermissible values are:
X.TP 20
XdmmNew
XUsed when a screen is displayed for the first time
Xor when a screen is redisplayed after intervening call to
X.B dmmRun
Xhas displayed another screen.
X.TP 20
XdmmOld
XUsed when 
X.B dmmRun
Xis to display the same screen as when it was last called.
XNothing about the 
X.I screen
Xor
X.I menu
Xstructures nor the values that they point to may have been changed
Xsince the last call because the screen in this case is repainted
Xby a simple
X.I wrefresh
Xwith no internal window redrawing or recalculations.
X.PP
XThe value of
X.I valFlag
Xdetermines the initial values of the editable fields. 
XPermissible values are:
X.TP 20
XdmmInitVal
Xcauses the value of
X.I inputInitValue
Xto be copied to
X.I inputEditValue
Xbefore the screen is displayed and user interaction begun.
XThis is useful when a certain screen is to be displayed more than oncer
Xper execution of the program and some of those times it should
Xstart off with certain default values instead of with the edited values.
XThe default value of each field should be placed in the
X.I inputInitVal
Xparameter for the field.
X.TP 20
XdmmEdVal
Xcauses no such copying.
XThe values displayed are those that are already in the
X.I inputEditValue
Xparameters for each field.
X.PP
XThe value of
X.I startField
Xdetermines which field is made current when user interaction is begun;
Xit must be the index in
X.I screen
Xof a DMMFIELD that is editable, i.e. one that has a non-zero
X.IR inputLength .
XThe special value of -1 has a significance according to the value of
X.IR drawFlag .
XIf 
X.I drawFlag
Xis
X.IR dmmNew ,
Xthe starting editable field will be the 
Xfirst editable field (not necessarily the first field) in
X.IR screen .
XIf 
X.I drawFlag is
X.IR dmmOld ,
Xthe starting editable field depends upon the state of affairs when
Xlast invocation of
X.B dmmRun
Xreturned.
XIf it returned due to a menu selection
Xthe starting editable field will be the last current field.
XIf it returned because an
X.I inputAudit
Xfunction returned
X.I dmmAuditReturnOkay 
Xor
X.IR dmmAuditReturnBad ,
Xthe starting editable field will be either the next or preceding editable
Xfield (depending upon whether the user keystroke that invoked the audit
Xwas for moving to the next or preceding field).
XAs described above, this is useful after an inter-field edit has taken place
Xfor a seamless appearance to the separate invocations of
X.BR dmmRun .
XIf the inter-field audit showed valid editable field values, it is useful
Xto reinvoke
X.B dmmRun
Xwith an
X.I drawFlag
Xvalue of 
X.I dmmOld
Xand a
X.I startField
Xvalue of -1.
XIf the inter-field audit indicates that an editable field value needs
Xcorrection by the user,
Xan error message should be displayed and
X.B dmmRun
Xshould be reinvoked with an
X.I drawFlag
Xvalue of
X.I dmmOld
Xand
X.I startField
Xset to the index of the offending field.
X.PP
X.B dmmRun
Xreturns typically when a user choses a menu item (i.e. enters a carriage
Xreturn while in the menu state), but also when an
X.I inputAudit
Xfunction returns
X.I dmmAuditReturnOKay
Xor
X.IR dmmAuditReturnBad .
XIn the former case,
X.B dmmRun
Xreturns the index of the menu item chosen, and sets
X.B dmmRetType
Xto
X.IR dmmMenu .
XIn the latter cases, it returns the index of the associated editable field;
X.B dmmRetType
Xis set to
X.I dmmOkayField
Xor
X.I dmmBadField
Xdepending upon whether the audit function returned
X.I dmmAuditReturnOKay
Xor
X.IR dmmAuditReturnBad ,
Xrespectively.
XA menuless screen must therefore have an
X.I inputAudit
Xfunction that unconditionally returns either
X.I dmmAuditReturnOkay
Xor
X.IR dmmAuditReturnBad ;
Xotherwise there will be nothing the user can input that will cause
X.B dmmRun
Xto return!
X.SS Terminal initialization and cleanup
X.PP
XThe function
X.B dmmRun
Xprovides normal 
X.I curses
Xinitialization (e.g. puts the terminal in raw mode) and creates the various
Xwindows needed for its work.
XWhen all 
X.I curses
Xinteraction is finished in the program, whether through
X.I DMM
Xfunctions
Xor though other functions, the program should invoke
X.B dmmClose
Xto clean up and return the terminal to its previous state.
XThe function
X.B dmmInit
Xprovides the same initialization functionality as
X.B dmmRun
Xwithout the presentation of a screen.
XThis is useful when the program needs to do
X.I curses
Xwork (such as display of data on the screen using any of the message
Xdisplay function described below) prior to the first call of
X.BR dmmRun .
XIf initialization has already been done,
X.B dmmRun
Xwill not do it again (unless, of course, there has been an intervening
Xinvocation of
X.BR dmmClose ).
X.SS Non-interactive display of data
X.PP
XData, such as error messages, may be displayed in the data entry window
Xwithout the user-interaction of a screen.
XThis is useful not only between invocations of
X.B dmmRun 
X(or
X.B dmmInit
Xand
X.BR dmmRun ),
Xbut also within the
X.I inputAudit
Xfunctions to display messages to the user when the user needs to correct
Xan editable field.
X.B dmmPutMsgs
Xis the simplest of the message display functions; it adds a block of
Xmessages to the existing data entry window and leaves them there.
X.I mesgblock
Xis a null-terminated array of DMMMESSAGE structures, defined in
X.IR dmm.h .
XEach DMMMESSAGE describes one message string,
Xthe elements of which are as follows:
X.TP 20
Xvalue
XThe string to be displayed.
X.TP 20
X.PD 0
Xcol,
X.TP 20
X.PD
Xline
XThe coordinates in the data entry window where the first character of
X.I value
Xis displayed.
XA negative value for
X.I line
Xrepresents lines from the bottom
Xof the window, the bottom line represented with -1.
X.TP 20
Xattrib
XThe attributes with which 
X.I value
Xis displayed.
XSee
X.IR labelAttrib ,
Xabove, for a description of possible values.
X.PP
XMessages are cleared from the window with the next invocation of
X.B dmmRun
Xor with an invocation of
X.BR dmmRmMsgs .
XIf
X.I removetype
Xis
X.IR dmmRemoveAll ,
Xall message displayed since the last invocation of
X.B dmmRun
Xor 
X.B dmmInit
Xare removed from the window.
XIf
X.I removetype
Xis
X.IR dmmRemoveLast
Xonly the messages displayed with the last invocation of
X.B dmmPutMsgs
Xare removed.
X.PP
X.B dmmPutMsgsTimer
Xdisplays the messages described by
X.IR mesgblock ,
Xsleeps for
X.I duration
Xseconds, removes them and returns.
X.PP
X.B dmmPutMsgsHit
Xdisplays the messages described by
X.IR mesgblock ,
Xdisplays an instruction in the instruction window for the user to press
Xany key to continue, and when the user enters a keystroke, the messages
Xare removed and the function returns.
X.PP
X.B dmmPutMsgsNext
Xdisplays the messages described by
X.I mesgblock
Xand removes them upon the next user keystroke entered under an invocation of 
X.B dmmRun
X(unless removed first by a call to
X.IR dmmRmMsgs ).
XThe keystroke is interpreted by
X.B dmmRun
Xas it would normally rather than discard it as it is under an invocation of
X.BR dmmPutMsgsHit .
X.PP
X.B dmmClear
Xclears everything from the data entry window,
Xall messages and any display from the last call of
X.BR dmmRun .
XThis is useful prior to using
X.RI non- DMM
Xfunctions for screen manipulation and after a call to 
X.B dmmRun
Xwhen you don't want the data entry portion of the screen displayed any longer.
X.PP
X.B dmmGetNumLines
Xreturns the number of lines in the data entry window.
XThis is useful, for example, when displaying tabular data as a set of messages,
Xand one needs to know how many lines of the table can be displayed in a single
Xscreenful.
X.SS Non-interactive auditing of editable field values
XAs mentioned above, a field's input value is audited under
X.B dmmRun
Xonly when the user input a keystroke that would change the field from current
Xto non-current.
XConsequently, fields that the user doesn't traverse go unaudited under
X.BR dmmRun ,
Xa problem with fields whose initial input value (see discussion
Xof
X.I valFlag
Xabove) is not a valid value;
X(this is often the case,
Xe.g. when there is no default value to give for a field's
X.I initVal
Xbut a value for the field is required nontheless.)
XThe solution is to call
X.BR dmmReview ,
Xwhich invokes the
X.I inputAudit
Xfunction of each editable field and 
Xreturning the index of the first editable field whose
X.I inputAudit
Xreturns either
X.I dmmAuditBad
Xor
X.I dmmAuditReturnBad
Xand by setting 
X.B dmmRetType
Xto
X.IR dmmBadField .
XIf no editable fields have invalid values, it returns -1 and sets
X.B dmmRetType
Xto
X.IR dmmOkayField .
XTherefore, if a screen has initial editable field values that are not valid,
Xit is recommended that the call to 
X.B dmmRun
Xthat presents the screen be followed by a call to
X.B dmmReview
X(given an appropriate return value of
X.BR dmmRun ).
X.SS Data structures as function arguments
X.I dmm.h
X.I typedefs 
Xa number of data structures for use with these functions:
X.PD 0
X.PP
X.nf
X    typedef struct dmmField DMMFIELD, *DMMSCREEN[], **DMMSCREEN2;
X    typedef struct dmmMenuItem DMMMENUITEM, *DMMMENU[], **DMMMENU2;
X    typedef struct dmmMessage DMMMESSAGE, *DMMMESGBLK[], **DMMMESGBLK2;
X.fi
X.PP
XThe first of each triplet are the basic structures used, and the second
Xare the array-of-pointers data types used above as arguments to the 
X.I DMM
Xfunctions.
XHowever, sometimes it is useful (and syntactically equivalent in some contextss)
Xto use a pointer pointer instead of an array of pointers for the argument of
Xthe called function, as are the third of each triplet.
XAn example of a situation in which the pointer pointers are
Xuseful is as follows:
X.PD
X.PP
X.nf
X    int		startField = -1;
X    DMMDRAWFLAG	drawFlag = dmmNew;
X    DMMVALFLAG	valFlag = dmmInitVal;
X		/* initialization of the DMMSCREENs and DMMMENUs
X		 * omitted in this example for brevity
X		 */
X    DMMSCREEN	normal_screen, root_screen;
X    DMMSCREEN2	screen_to_use;
X    DMMMENU	normal_menu, root_menu;
X    DMMMENU2	menu_to_use;
X    ...
X    if(getuid() == 0) {
X	...
X	screen_to_use = root_screen;
X	menu_to_use = root_menu;
X	...
X    } else {
X	...
X	screen_to_use = normal_screen;
X	menu_to_use = normal_menu;
X	...
X    }
X    ...
X    dmmRun(startField, screen_to_use, menu_to_use, drawFlag, valFlag);
X.fi
X.SH EXAMPLE
X.PP
XThe sources for a demo program that illustrates good use of
X.I DMM
Xare included with the
X.I DMM
Xsources.
XNotice how the declaration of the various
X.I DMM
Xdata structures as static variables enables easy initialization.
X.SH DIAGNOSTICS
X.PP
X.B dmmRun
Xreturns -1 if the screen is faulty,
X(e.g. a label or input portion of a field or a menu item
Xdoes not fit on the screen,
Xeach menu item label does not begin with a unique alphabetic character,
X.I startField
Xis not the index of an editable field,
Xthere is no menu and the data entry portion has no editable fields,
Xetc.), and
Xa diagnostic message is displayed on the screen.
XNon-error return values for
X.B dmmRun
Xand
X.B dmmReview
Xare described above.
X.SH SEE ALSO
X.PP
X.IR curses (3V)
X.SH AUTHOR
X.PP
XCopyright \(co 1991, Robert Bernardo, rob at mtdiablo.Concord.CA.US,
Xan employee of Pande, Inc.
XPermission to use granted only if this 
Xnotice is not removed from this manual page.
SHAR_EOF
chmod 0444 dmm.3 || echo "restore of dmm.3 fails"
set `wc -c dmm.3`;Sum=$1
if test "$Sum" != "23153"
then echo original size 23153, current size $Sum;fi
echo "x - extracting dmm.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > dmm.c &&
X#ifndef LINT
Xstatic char     dmm_c_id[] = {"@(#)dmm.c	1.1 \
XCopyright (c) 1991 Robert Bernardo, rob at mtdiablo.Concord.CA.US, \
Xan employee of Pande, Inc. \
XPermission to use granted only if this notice is not removed from \
Xthis source file nor from any binaries produced from it."};
X#endif
X
X#include <ctype.h>
X#include <curses.h>
X#include <string.h>
X#include <signal.h>
X#include <term.h>
X#include <sys/ttychars.h>
X#include "dmm.h"
X
X/* see man page for description */
XDMMRETTYPE      dmmRetType;
X
X/* enum for type of cursor change */
Xenum change {
X    previous, stayput, movecursor, next
X};
X
X/* error message data structures */
Xstatic char     		errorMsgBuf[BUFSIZ];
Xstatic DMMMESSAGE 		errorMsg = {errorMsgBuf, 0, 0, A_REVERSE};
Xstatic DMMMESGBLK 		errorMsgBlk = {&errorMsg, 0};
X
X#define CNVRT_LN_NUM(lnnum)	(lnnum < 0 ? LFLINE+lnnum+1 : lnnum)
X#define DISP_ERROR_0(mesg)	{strcpy(errorMsg.value,mesg);	\
X				dmmPutMsgsHit(errorMsgBlk);}
X#define DISP_ERROR_1(fmt,arg)	{sprintf(errorMsg.value,fmt,arg);	\
X				dmmPutMsgsHit(errorMsgBlk);}
X#define DISP_ERROR_2(fmt,a,b)	{sprintf(errorMsg.value,fmt,a,b);	\
X				dmmPutMsgsHit(errorMsgBlk);}
X#define DISP_ERROR_3(fmt,a,b,c) {sprintf(errorMsg.value,fmt,a,b,c); \
X				  dmmPutMsgsHit(errorMsgBlk);}
X
X/* special command input character symbols */
X#define TAB	'\t'
X#define RETURN	'\r'
X#define DELETE	CERASE
X
X/* menu scroll symbols */
X#define LEFT_SCROLL_SYMBOL	"<<"
X#define RIGHT_SCROLL_SYMBOL	">>"
X
X/* parameters of the windows */
X/* 	- number of lines in each window */
X#define MLINES	2		/* menu window */
X#define ILINES	1		/* instruct window */
X#define FLINES	(LINES - MLINES - ILINES)	/* field window */
X
X/*	- menu line functions */
X#define MENU_LABEL_LINE		0
X#define MENU_DESCRIPT_LINE	1
X
X/* 	- line number of first line of windows */
X
X#define FILINE	0		/* instruct window */
X#define FMLINE	(FILINE + ILINES)	/* menu window */
X#define FFLINE	(FMLINE + MLINES)	/* field window */
X
X/* 	- line number of last line of windows in window (not in screen) */
X#define LFLINE	(FLINES - 1)	/* field window */
X
X#define MENUI_SPACING 2		/* number of spaces between */
X
X/* string constants */
X/* 	- general instruction messages */
X#define MENU_INSTRUCT		"Cursor to desired item and enter RETURN. Press control-h for help."
X#define GOTO_MENU_INSTRUCT	"Press control-g to enter menu. Press control-h for help."
X#define NULL_MENU_INSTRUCT	"Press control-h for help."
X#define ANY_KEY_INSTRUCT	"Press any key to continue."
X
X/* 	- diagnostic error messages */
X#define BAD_FIELDNUM	"Program error! Target field %d beyond max %d.\n"
X#define NO_INPUT_MSG	"Program error! No input fields nor menu items.\n"
X#define NOT_IFIELD	"Program error! Target field %d not an input field.\n"
X#define WIDE_LABEL	"Program error! Menu label %d too wide for screen.\n"
X#define NON_ALPHA	"Program error! Menu item %d doesn't begin with an alphabetic character."
X#define LABEL_NO_FIT	"Program error! Label for field %d won't fit on screen.\n"
X#define IP_NO_FIT	"Program error! Input part of field %d won't fit on screen.\n"
X
X#define DMMWATTRSET(win, attrib)	wattrset(win, attrib&~A_INVIS)
X#define DMMWATTRON(win, attrib)	wattron(win, attrib&~A_INVIS)
X#define DMMWATTROFF(win, attrib)	wattroff(win, attrib&~A_INVIS)
X
X
X/* global variables */
X/* general information discoverd about a screen */
Xstatic int      numFields,	/* number of fields */
X                numInputParts,	/* number of fields with input parts */
X                numMenuItems,	/* number of menu items */
X                numMenuPages,	/* number of menu pages */
X                curMenuItem,	/* current menu item */
X                firstInputFld;	/* first field with input part */
X/* flags needed to manage the removal of messages displayed with
X * dmmPutMsgsNext() when not called under a call to dmmRun(), e.g. not
X * under an inputAudit function.
X */
Xstatic int      interactive,	/* set when dmm is interactive */
X                wantNextClear,	/* set when next keystroke should clear
X				 * messages from screen */
X                needAddMsgs;	/* set when need to add messages from a
X				 * dmmPutMsgsNext() called when dmm was not
X				 * interactive */
XPMNmsgsWereLast;		/* set when the last messages added were
X				 * added by dmmPutMsgsNext(). This is needed
X				 * because if dmmRmMsgs() is called to remove
X				 * just the last messages, the need to clear
X				 * the dmmPutMsgsNext() is removed and need
X				 * not be done on the next user keystroke
X				 * under dmmRun(). */
X
X/* global variables for parameters so they don't have to be passed all over */
Xstatic DMMSCREEN2 dupeScreen;	/* field vector */
Xstatic DMMMENU2 dupeMenu;	/* menu vector */
X/* windows */
Xstatic WINDOW  *fieldWin,	/* field window */
X               *fieldWinCopy,	/* saved window between dmmRun() calls */
X               *fieldWinPreMsg,	/* saved window before messages */
X               *mesgWin,	/* overlay window of messages */
X               *instructWin,	/* general instruction window */
X               *instructWinCopy,/* copy of instruction window */
X               *menuWin,	/* menu window */
X               *menuWinCopy;	/* copy menu window */
X/* miscellaneous */
Xstatic int      isOpen;		/* 0 if dmmInit needs to be performed */
X
Xstatic int      menuIndex[(int) ('z' - 'a' + 1)];	/* index by letter into
X							 * menu by initial
X							 * letters of menu
X							 * labels. */
X/* structures for help screens */
X#define KEY_COLUMN	5
X#define	EFFECT_COLUMN	22
X#define	INTRO_LINE	0
X#define	HELP_LINE	(INTRO_LINE + 2)
X#define	REDRAW_LINE	(HELP_LINE + 1)
X#define NEXT_LINE	(REDRAW_LINE + 1)
X#define PREV_LINE	(NEXT_LINE + 1)
X#define RETURNS_LINE	(PREV_LINE + 1)
X#define RETURNM_LINE	(RETURNS_LINE + 1)
X#define DELETE_LINE	(RETURNM_LINE + 1)
X#define ARROW_LINE	(DELETE_LINE + 1)
X#define CLEAR_LINE	(ARROW_LINE + 1)
X#define GOTO_LINE	(CLEAR_LINE + 1)
X#define NORMALS_LINE	(GOTO_LINE + 1)
X#define NORMALM_LINE	(NORMALS_LINE + 1)
X
Xstatic DMMMESSAGE introHelpKey =
X{"Key", KEY_COLUMN, INTRO_LINE, A_REVERSE};
Xstatic DMMMESSAGE helpHelpKey =
X{"control-h", KEY_COLUMN, HELP_LINE};
Xstatic DMMMESSAGE redrawHelpKey =
X{"control-r", KEY_COLUMN, REDRAW_LINE};
Xstatic DMMMESSAGE nextHelpKey =
X{"control-n/tab", KEY_COLUMN, NEXT_LINE};
Xstatic DMMMESSAGE prevHelpKey = {"control-p", KEY_COLUMN, PREV_LINE};
Xstatic DMMMESSAGE returnHelpKey = {"return", KEY_COLUMN, RETURNS_LINE};
Xstatic DMMMESSAGE deleteHelpKey = {"delete", KEY_COLUMN, DELETE_LINE};
Xstatic DMMMESSAGE arrowHelpKey = {"arrow key", KEY_COLUMN, ARROW_LINE};
Xstatic DMMMESSAGE clearHelpKey = {"control-c", KEY_COLUMN, CLEAR_LINE};
Xstatic DMMMESSAGE gotoHelpKey = {"control-g", KEY_COLUMN, GOTO_LINE};
Xstatic DMMMESSAGE normalHelpKey =
X{"`normal' keys", KEY_COLUMN, NORMALS_LINE};
X
Xstatic DMMMESSAGE introEffectKey =
X{"Effect", EFFECT_COLUMN, INTRO_LINE, A_REVERSE};
Xstatic DMMMESSAGE helpEffectKey =
X{"display help (this screen)", EFFECT_COLUMN, HELP_LINE};
Xstatic DMMMESSAGE redrawEffectKey =
X{"redraw screen", EFFECT_COLUMN, REDRAW_LINE};
Xstatic DMMMESSAGE nextEffectKey =
X{"go to next data field or menu item", EFFECT_COLUMN, NEXT_LINE};
Xstatic DMMMESSAGE prevEffectKey =
X{"go to previous data field or menu item", EFFECT_COLUMN, PREV_LINE};
Xstatic DMMMESSAGE returnScreenEffectKey =
X{"if in data entry, go to next data field or,", EFFECT_COLUMN, RETURNS_LINE};
Xstatic DMMMESSAGE returnMenuEffectKey =
X{"if in menu, accept menu choice", EFFECT_COLUMN, RETURNM_LINE};
Xstatic DMMMESSAGE deleteEffectKey =
X{"erase preceding character in current data field", EFFECT_COLUMN, DELETE_LINE};
Xstatic DMMMESSAGE arrowEffectKey =
X{"move within current data field", EFFECT_COLUMN, ARROW_LINE};
Xstatic DMMMESSAGE clearEffectKey =
X{"clear to end of current data field and go to next",
XEFFECT_COLUMN, CLEAR_LINE};
Xstatic DMMMESSAGE gotoEffectKey =
X{"move from data entry to menu or vice versa", EFFECT_COLUMN, GOTO_LINE};
Xstatic DMMMESSAGE normalScreenEffectKey =
X{"if in data entry, input letter into data field or,",
XEFFECT_COLUMN, NORMALS_LINE};
Xstatic DMMMESSAGE normalMenuEffectKey =
X{"if in menu, highlight menu item with same first letter",
XEFFECT_COLUMN, NORMALM_LINE};
X
Xstatic DMMMESGBLK helpMsgVec =
X{&introHelpKey, &helpHelpKey, &redrawHelpKey, &nextHelpKey,
X    &prevHelpKey, &returnHelpKey, &deleteHelpKey, &arrowHelpKey, &clearHelpKey,
X    &gotoHelpKey, &normalHelpKey, &introEffectKey,
X    &helpEffectKey, &redrawEffectKey, &nextEffectKey, &prevEffectKey,
X    &returnScreenEffectKey, &deleteEffectKey, &returnMenuEffectKey,
X    &arrowEffectKey, &clearEffectKey, &gotoEffectKey, &normalScreenEffectKey,
X&normalMenuEffectKey, 0};
X/* dmmInit()	put the terminal in the right mode and set up curses with
X *		the right windows.
X */
Xvoid
XdmmInit ()
X{
X    initscr ();
X    raw ();
X    nonl ();
X    noecho ();
X
X    /* get terminal in right mode */
X    putp (init_1string);
X    putp (init_2string);
X    putp (init_3string);
X    putp (cursor_visible);	/* blinking cursor */
X
X    /* create windows */
X    fieldWin = newwin (FLINES, COLS, FFLINE, 0);
X    fieldWinCopy = newwin (FLINES, COLS, FFLINE, 0);
X    fieldWinPreMsg = newwin (FLINES, COLS, FFLINE, 0);
X    mesgWin = newwin (FLINES, COLS, FFLINE, 0);
X    instructWin = newwin (ILINES, COLS, FILINE, 0);
X    instructWinCopy = newwin (ILINES, COLS, FILINE, 0);
X    menuWin = newwin (MLINES, COLS, FMLINE, 0);
X    menuWinCopy = newwin (MLINES, COLS, FMLINE, 0);
X
X    /* leave cursor where code says to */
X    leaveok (instructWin, FALSE);
X    leaveok (fieldWin, FALSE);
X    leaveok (menuWin, FALSE);
X
X    /* treat escape sequences as single characters */
X    keypad (fieldWin, TRUE);
X    keypad (menuWin, TRUE);
X
X    /* set attributes of windows fixed at a single attribute */
X    DMMWATTRSET (instructWin, A_REVERSE);
X
X    isOpen = 1;
X
X    return;
X}
X
X
X/* redraw()	unconditionally redraw the entirety of each window.
X */
Xstatic void
Xredraw ()
X{
X    clearok (fieldWin, TRUE);
X    clearok (instructWin, TRUE);
X    clearok (menuWin, TRUE);
X    wnoutrefresh (fieldWin);
X    wnoutrefresh (instructWin);
X    wnoutrefresh (menuWin);
X    doupdate ();
X    return;
X}
X
X
X/* dmmClose()	cleanup dmm and close down curses */
Xvoid
XdmmClose ()
X{
X    delwin (fieldWin);
X    delwin (fieldWinCopy);
X    delwin (fieldWinPreMsg);
X    delwin (mesgWin);
X    delwin (instructWin);
X    delwin (instructWinCopy);
X    delwin (menuWin);
X    delwin (menuWinCopy);
X    isOpen = 0;
X    clear ();
X    refresh ();
X    endwin ();
X    putp (cursor_normal);
X    return;
X}
X
X
X/* dmmGetNumLines	return the number of lines in fieldWin */
Xint
XdmmGetNumLines ()
X{
X    return FLINES;
X}
X
X/* outOfTheWay		put the cursor in a place where it's not distracting */
Xvoid
XoutOfTheWay()
X{
X    wmove (instructWin, 0, COLS - 1);
X    wnoutrefresh (instructWin);
X    return;
X}
X
X
X/* dmmRmMsgs()	remove messages displayed */
Xvoid
XdmmRmMsgs (removeType)
XDMMREMOVETYPE   removeType;
X{
X    /*
X     * remove residue of dmmPutMsgsNext type of messages as appropriate to
X     * whether they were last or wether we are clearing all messages not just
X     * the last messages
X     */
X    if ((removeType == dmmRemoveAll) || (PMNmsgsWereLast)) {
X	wclear (mesgWin);
X	needAddMsgs = 0;
X	wantNextClear = 0;
X    }
X    /* remove proper "layer" of messages */
X    overwrite ((removeType == dmmRemoveAll ? fieldWinCopy : fieldWinPreMsg),
X	       fieldWin);
X    wnoutrefresh (fieldWin);
X    outOfTheWay();
X    doupdate();
X    return;
X}
X
X
X/* wGetCh()		like wgetch(), but clears any messages displayed by
X *			dmmPutMesgsNext()
X */
Xstatic int
XwGetCh (win)
XWINDOW         *win;
X{
X    int             keystroke;
X    /* get keystroke */
X    keystroke = wgetch (win);
X
X    /* if need to clear messages, do so */
X    if (wantNextClear) {
X	dmmRmMsgs (dmmRemoveLast);
X	wantNextClear = 0;
X    }
X    /* if mesgWin was used, clear it so stale messages don't get reused */
X    if (needAddMsgs) {
X	werase (mesgWin);
X	needAddMsgs = 0;
X    }
X    return keystroke;
X}
X
X
X/* addMsgs()	add messages to the indicated window */
Xstatic void
XaddMsgs (win, mesgBlock)
XWINDOW         *win;
XDMMMESGBLK      mesgBlock;
X{
X    /* display each item in the message array */
X    while (*mesgBlock) {
X	DMMWATTRON (win, (*mesgBlock)->attrib);
X	mvwaddstr (win, CNVRT_LN_NUM ((*mesgBlock)->line),
X		   (*mesgBlock)->col, (*mesgBlock)->value);
X	DMMWATTROFF (win, (*mesgBlock)->attrib);
X	mesgBlock++;
X    }
X    wnoutrefresh (win);
X    return;
X}
X
X
X/* doPutMsgs()	see comments for dmmPutMsgs, if save is set, write
X * 			messages as well to mesgWin
X */
Xstatic void
XdoPutMsgs (mesgBlock, save)
XDMMMESGBLK      mesgBlock;
Xint             save;
X{
X
X    /* save copy of window as it currently is */
X    overwrite (fieldWin, fieldWinPreMsg);
X
X    /* display messages on fieldWin, and on mesgWin as needed */
X    addMsgs (fieldWin, mesgBlock);
X    if (save)
X	addMsgs (mesgWin, mesgBlock);
X    PMNmsgsWereLast = 0;	/* may be set by outer call to
X				 * dmmPutMsgsNext() */
X    outOfTheWay();
X    doupdate ();
X}
X
X
X/* dmmPutMsgsNext()	remove messages displayed  until a key is input
X * 			under dmmRun or until explicitly removed
X */
Xvoid
XdmmPutMsgsNext (mesgBlock)
XDMMMESGBLK      mesgBlock;
X{
X    wantNextClear = mesgBlock ? 1 : 0;
X    if (!interactive)
X	needAddMsgs = 1;
X    doPutMsgs (mesgBlock, needAddMsgs);
X    PMNmsgsWereLast = 1;
X    return;
X}
X
X
X/* dmmPutMsgs()		display messages */
Xvoid
XdmmPutMsgs (mesgBlock)
XDMMMESGBLK      mesgBlock;
X{
X    doPutMsgs (mesgBlock, 0);
X    return;
X}
X
X/* dmmClear()		clear everything irretrievably from fieldWin */
Xvoid
XdmmClear()
X{
X    wclear(mesgWin);
X    wnoutrefresh(mesgWin);
X    wclear(fieldWin);
X    wnoutrefresh(fieldWin);
X    wclear(fieldWinCopy);
X    wnoutrefresh(fieldWinCopy);
X    outOfTheWay();
X    doupdate();
X    return;
X}
X
X
X/* dmmPutMsgsTimer()		display messages for a certain number of secs */
Xvoid
XdmmPutMsgsTimer (mesgBlock, duration)
XDMMMESGBLK      mesgBlock;
Xunsigned        duration;
X{
X    /* display messages */
X    dmmPutMsgs (mesgBlock);
X
X    /* sleep and then redisplay the original window without the messages */
X    (void) sleep (duration);
X    dmmRmMsgs (dmmRemoveLast);
X
X    return;
X}
X
X
X/* dispInstruct()	display a message in the general instruction window */
Xstatic void
XdispInstruct (msg)
Xchar           *msg;
X{
X    mvwaddstr (instructWin, 0, 0, msg);
X    wclrtoeol (instructWin);
X    wnoutrefresh (instructWin);
X    return;
X}
X
X
X/* doPutMsgsHit()	display the messages indicated by mesgBlock
X *			until the user enters a keystroke
X */
Xvoid static
XdoPutMsgsHit (mesgBlock, restoreThem)
XDMMMESGBLK      mesgBlock;
Xint             restoreThem;
X{
X    register int 	x;
X    overwrite (instructWin, instructWinCopy);
X    if (restoreThem) {
X	overwrite (fieldWin, fieldWinCopy);
X	overwrite (menuWin, menuWinCopy);
X	wclear (fieldWin);
X	wclear (menuWin);
X	wnoutrefresh (menuWin);
X    }
X    dispInstruct (ANY_KEY_INSTRUCT);
X    flushinp ();
X    dmmPutMsgs (mesgBlock);
X    wgetch (fieldWin);
X    flushinp ();
X    if (restoreThem) {
X	overwrite (fieldWinCopy, fieldWin);
X	overwrite (menuWinCopy, menuWin);
X	wnoutrefresh (fieldWin);
X	wnoutrefresh (menuWin);
X    } else
X	dmmRmMsgs (dmmRemoveLast);
X    overwrite (instructWinCopy, instructWin);
X
X    /* That last overwrite makes the whole window inverse video including
X     * trailing blanks, not just the message part, so we have to clear
X     * the trailing spaces while in normal attributes.
X     */
X    DMMWATTROFF (instructWin, A_REVERSE);
X    x = instructWin->_maxx;
X    while(x--) {
X	if((mvwinch(instructWin, 0, x) & A_CHARTEXT) != ' ') {
X	    x++;
X	    break;
X	}
X    }
X    wmove(instructWin, 0, x);
X    wclrtoeol(instructWin);
X    DMMWATTRON (instructWin, A_REVERSE);
X    wnoutrefresh (instructWin);
X    doupdate ();
X    return;
X}
X
X
X/* dmmPutMsgsHit()	display a message until the user presses any key */
Xvoid
XdmmPutMsgsHit (mesgBlock)
XDMMMESGBLK      mesgBlock;
X{
X    doPutMsgsHit (mesgBlock, 0);
X    return;
X}
X
X
X/* dispInField()	display the input part of the specified field
X *			with the specified attributes.
X */
Xstatic void
XdispInField (fldPtr, attrib)
Xregister DMMFIELD *fldPtr;
Xlong            attrib;
X{
X    register int    charNum;	/* counter of characters output */
X    int             doNulls,	/* set if characters run out */
X                    len;	/* length of field */
X    if (fldPtr->inputLength) {
X	DMMWATTRON (fieldWin, attrib);
X	charNum = 0;
X	doNulls = (attrib & A_INVIS) ? 1 : 0;
X	for (charNum = 0, len = fldPtr->inputLength; charNum < len; charNum++) {
X	    if (fldPtr->inputEditValue[charNum] == '\0')
X		doNulls = 1;	/* we got to end of string, from here to end
X				 * output blanks */
X	    mvwaddch (fieldWin,
X		      fldPtr->inputLine + (charNum / fldPtr->inputMaxCols),
X		      fldPtr->inputCol + (charNum % fldPtr->inputMaxCols),
X		      doNulls ? ' ' : fldPtr->inputEditValue[charNum]);
X	}
X	DMMWATTROFF (fieldWin, attrib);
X    }
X    return;
X}
X
X
X/* calcDflts()	calculate line and column numbers for the label
X * 		and input part of data fields; a negative line
X *		number represents lines from the window bottom,
X *		with -1 being the bottom; a negative column for
X *		the input part of the field indicates the number
X *		of columns separating it and the end of the label part;
X *		calculate the number of full length lines and
X *		the length of the last line (if not full length)
X *		of the input part.
X */
Xstatic void
XcalcDflts (fldPtr)
Xregister DMMFIELD *fldPtr;
X{
X
X    /* calculate line numbers */
X    fldPtr->labelLine = CNVRT_LN_NUM (fldPtr->labelLine);
X    fldPtr->inputLine = CNVRT_LN_NUM (fldPtr->inputLine);
X
X    /* calculate input part column number */
X    if (fldPtr->inputCol < 0)
X	fldPtr->inputCol = fldPtr->labelCol
X	    + strlen (fldPtr->labelValue) - fldPtr->inputCol - 1;
X
X    /* calculate dimensions of lines of input part from length; if
X     * inputMaxCols is 0, field will be one line of inputLength chars
X     */
X    if (fldPtr->inputMaxCols) {
X	fldPtr->inputFullLines = fldPtr->inputLength / fldPtr->inputMaxCols;
X	fldPtr->inputShortCols = fldPtr->inputLength % fldPtr->inputMaxCols;
X    } else {
X	fldPtr->inputMaxCols = fldPtr->inputLength;
X	fldPtr->inputFullLines = 1;
X	fldPtr->inputShortCols = 0;
X    }
X
X    return;
X}
X
X
X/* calcMenu()	calculate page and column for each menu item
X *		and create index for menu; return -1 if
X *		more than one item label begin with the same
X *		letter, else 0.
X */
Xstatic int
XcalcMenu ()
X{
X    int             letter,
X                    thisPage,
X                    nextCol,
X                    scrollSymSize = strlen (LEFT_SCROLL_SYMBOL),
X                    numScrollSymbols,
X                    itemLen,
X                    itemNum = 0;
X    /* initialize menu index */
X    for (letter = 0; letter < (int) ('z' - 'a' + 1); letter++)
X	menuIndex[letter] = -1;
X
X    /* get count of menu items */
X    while (dupeMenu[itemNum++]);
X    numMenuItems = itemNum - 1;
X
X    /* start on first column of first page */
X    thisPage = 0;
X    nextCol = 0;
X    itemNum = 0;
X    while (dupeMenu[itemNum]) {
X
X	/* register item in index */
X	letter = (int) *(dupeMenu[itemNum]->label);
X	if (!(isascii (letter) && isalpha (letter))) {
X	    DISP_ERROR_1 (NON_ALPHA, itemNum);
X	    return -1;
X	}
X	if (isupper (letter))
X	    letter = tolower (letter);
X	letter -= (int) 'a';	/* start from 0, not 'a' */
X	if (menuIndex[letter] == -1)
X	    menuIndex[letter] = itemNum;
X	else
X	    menuIndex[letter] = -2;;
X
X	itemLen = strlen (dupeMenu[itemNum]->label);
X
X	/* check to see that label isn't wider than the window itself if this
X	 * menu has only one item, then we don't need to worry about fitting
X	 * in any scroll symbols; if we are already beyond the first page and
X	 * there are no more items, we need to worry about only one scroll
X	 * symbol also fitting; if we are already beyond the first page and
X	 * there are more items, we need to worry about two scroll symbols
X	 * also fitting.
X	 */
X	if (numMenuItems == 1)
X	    numScrollSymbols = 0;
X	else if ((thisPage > 0) && ((itemNum + 1) == numMenuItems))
X	    numScrollSymbols = 1;
X	else
X	    numScrollSymbols = 2;
X	if ((itemLen +
X	     (numScrollSymbols * (scrollSymSize + MENUI_SPACING))) > COLS) {
X	    DISP_ERROR_1 (WIDE_LABEL, itemNum);
X	    return -1;
X	}
X
X	/* check to see if it will fit on this page; if not the last item we
X	 * need to make sure on scrolling symbol will fit as well.
X	 */
X	numScrollSymbols = ((itemNum + 1) == numMenuItems) ? 0 : 1;
X	if ((itemLen + nextCol +
X	     (numScrollSymbols * (scrollSymSize + MENUI_SPACING))) > COLS) {
X
X	    /* won't fit on this page, start new page */
X	    thisPage++;
X	    nextCol = scrollSymSize + MENUI_SPACING;
X	}
X
X	/* assign its col and page */
X	dupeMenu[itemNum]->col = nextCol;
X	dupeMenu[itemNum]->page = thisPage;
X	nextCol += (itemLen + MENUI_SPACING);
X
X	itemNum++;
X    }
X
X    /* Set global menu variables; hmm ... numMenuItems already set above */
X    numMenuPages = thisPage + 1;
X    return 0;
X}
X
X
X/* dispMenuItem()	display menu item highlighted or not
X *			and display description field if highlighted
X */
Xstatic void
XdispMenuItem (mitemPtr, highlight)
XDMMMENUITEM    *mitemPtr;
Xint             highlight;
X{
X    if (mitemPtr) {
X	if (highlight)
X	    DMMWATTRON (menuWin, A_REVERSE);
X	mvwaddstr (menuWin, MENU_LABEL_LINE, mitemPtr->col,
X		   mitemPtr->label);
X	if (highlight) {
X	    DMMWATTROFF (menuWin, A_REVERSE);
X	    mvwaddstr (menuWin, MENU_DESCRIPT_LINE, 0, mitemPtr->descript);
X	    wclrtoeol (menuWin);
X	}
X    }
X    return;
X}
X
X
X/* dispMenuPage()	display page of menu items containing the
X *			item indicated; if highlight is TRUE
X *			highlight the menu item; if force is TRUE,
X *			redisplay from scratch.
X */
Xstatic void
XdispMenuPage (itemNum, highlight, dmmRenew)
Xint             itemNum,
X                highlight,
X                dmmRenew;
X{
X    static int      lastPageDisplayed = -1,
X                    lastItemHighlighted = -1;
X    int             itemIndex,
X                    pageNum;
X
X    /* if page is current only redisplay old item unhighlighted and new item
X     * highlighted, else display whole page from scratch adding right and
X     * left scrolling symbols as needed
X     */
X
X    if (!dmmRenew && (dupeMenu[itemNum]->page == lastPageDisplayed)) {
X
X	if (itemNum != lastItemHighlighted)
X	    if (lastItemHighlighted != -1)
X		dispMenuItem (dupeMenu[lastItemHighlighted], 0);
X	dispMenuItem (dupeMenu[itemNum], highlight);
X
X    } else {
X
X	werase (menuWin);
X	pageNum = dupeMenu[itemNum]->page;
X	for (itemIndex = 0; itemIndex < numMenuItems; itemIndex++)
X	    if (dupeMenu[itemIndex]->page == pageNum)
X		dispMenuItem (dupeMenu[itemIndex],
X			      ((itemIndex == itemNum) && highlight));
X
X	if (pageNum)
X	    mvwaddstr (menuWin, MENU_LABEL_LINE, 0, LEFT_SCROLL_SYMBOL);
X	if (pageNum + 1 < numMenuPages)
X	    mvwaddstr (menuWin, MENU_LABEL_LINE,
X		  COLS - sizeof (RIGHT_SCROLL_SYMBOL), RIGHT_SCROLL_SYMBOL);
X
X	lastPageDisplayed = pageNum;
X    }
X    lastItemHighlighted = itemNum;
X    wnoutrefresh (menuWin);
X    return;
X}
X
X
X/* layout()		do layout and calculations for the first display */
Xstatic int
Xlayout ()
X{
X    register int    fldNum = 0;	/* number of current field */
X    register DMMFIELD *fieldPtr;
X    int             inputPartCnt = 0,	/* number of input fields so far */
X                    lastInFieldNum = -1,
X                    firstInFieldNum;
X
X    /* indicate first input field not yet discovered */
X    firstInputFld = -1;
X
X    /* erase previous contents of windows */
X    werase (fieldWin);
X    werase (instructWin);
X    werase (menuWin);
X
X    /* draw field window */
X    if (dupeScreen) {
X	while (dupeScreen[fldNum]) {
X	    fieldPtr = dupeScreen[fldNum];
X
X	    /* check that label fits on screen */
X	    if (fieldPtr->labelValue &&
X	    ((fieldPtr->labelCol + strlen (fieldPtr->labelValue) > COLS)) ||
X		((fieldPtr->labelLine + 1> FLINES))) {
X		DISP_ERROR_1 (LABEL_NO_FIT, fldNum);
X		return -1;
X	    }
X
X	    /* display label, if any */
X	    if (fieldPtr->labelValue && *fieldPtr->labelValue) {
X		DMMWATTRON (fieldWin, fieldPtr->labelAttrib);
X		mvwaddstr (fieldWin, fieldPtr->labelLine,
X			   fieldPtr->labelCol, fieldPtr->labelValue);
X		DMMWATTROFF (fieldWin, fieldPtr->labelAttrib);
X	    }
X
X	    /* if there is an input part, display it */
X	    if (fieldPtr->inputLength) {
X
X		/* calculate number of instruction lines */
X		fieldPtr->inputNumInstructs =
X		    cntInstructs (fieldPtr);
X
X		/* set the first input field if not yet found */
X		if (firstInputFld == -1)
X		    firstInputFld = fldNum;
X
X		/* calculate defaults */
X		calcDflts (fieldPtr);
X
X		/* check that input part of field fits on screen */
X		if ((fieldPtr->inputCol + fieldPtr->inputMaxCols > COLS) ||
X		    (fieldPtr->inputLine + fieldPtr->inputFullLines +
X		     (fieldPtr->inputShortCols ? 1 : 0) > FLINES)) {
X		    DISP_ERROR_1 (IP_NO_FIT, fldNum);
X		    return -1;
X		}
X
X		/* display edited value */
X		dispInField (fieldPtr, fieldPtr->inputAttrib);
X
X		/* set last input field's next pointer to this field and this
X		 * field's prev pointer to last input field
X		 */
X		if (lastInFieldNum != -1) {
X		    dupeScreen[lastInFieldNum]->nextInField = fldNum;
X		    fieldPtr->prevInField = lastInFieldNum;
X		} else
X		    firstInFieldNum = fldNum;
X
X		lastInFieldNum = fldNum;
X		inputPartCnt++;
X	    }
X	    fldNum++;
X	}
X	if ((firstInFieldNum != -1) && (lastInFieldNum != -1)) {
X	    dupeScreen[firstInFieldNum]->prevInField = lastInFieldNum;
X	    fieldPtr->nextInField = firstInFieldNum;
X	}
X    }
X
X    /* set globals to values of local register variables */
X    numFields = fldNum;
X    numInputParts = inputPartCnt;
X
X    /* display menu items and determine menu instruction */
X    if (dupeMenu) {
X
X	/* do menu calculations */
X	if (calcMenu ())
X	    return -1;
X
X	/* display menu page that contains first item; no item highlighted */
X	dispMenuPage (curMenuItem = 0, 0, 1);
X    }
X    return 0;
X}
X
X
X/* copyInFields()	copy all initial values to edited values of
X *			input parts of fields and display; a null
X *			initial value is equivalent to a zero-length
X *			string.
X */
Xstatic void
XcopyInFields ()
X{
X    int             fldNum;	/* counter of field */
X    DMMFIELD       *fldPtr;
X    for (fldNum = 0; fldNum < numFields; fldNum++) {
X	fldPtr = dupeScreen[fldNum];
X	if (fldPtr->inputLength) {
X	    strncpy (fldPtr->inputEditValue,
X		     fldPtr->inputInitValue ? fldPtr->inputInitValue : "",
X		     fldPtr->inputLength);
X	    fldPtr->inputEditValue[fldPtr->inputLength] = '\0';
X	    dispInField (fldPtr, fldPtr->inputAttrib);
X	}
X    }
X    return;
X}
X
X
X/* insane()	check the sanity of things, where fldNum is the number
X *		of the selected starting input field,
X *		returning 0 if okay, else writing a diagnostic message
X *		to stderr and returning non-zero.
X */
Xstatic int
Xinsane (fldNum)
Xint             fldNum;
X{
X    /* is the starting field within the proper range? */
X    if (fldNum > numFields - 1) {
X	DISP_ERROR_2 (BAD_FIELDNUM, fldNum + 1, numFields);
X	return 1;
X    }
X
X    /* does the starting field have an input part */
X    if (numInputParts) {
X	if (dupeScreen[fldNum]->inputLength < 1) {
X	    DISP_ERROR_1 (NOT_IFIELD, fldNum + 1);
X	    return 1;
X	}
X    }
X
X    /* is there at least one field with an input part or one menu item */
X    if (!(numInputParts || numMenuItems)) {
X	DISP_ERROR_0 (NO_INPUT_MSG);
X	return 1;
X    }
X
X    return 0;
X}
X
X
X/* dmmReview()		audit all input fields */
Xint
XdmmReview (screen)
XDMMSCREEN       screen;
X{
X    register int    fldNum;	/* field index */
X    register DMMFIELD *fldPtr;
X    DMMAUDITRETURN  auditReturnVal;	/* return value of the audit function */
X
X    for (fldNum = 0; screen[fldNum]; fldNum++) {
X	fldPtr = screen[fldNum];
X	if (fldPtr->inputLength) {
X	    auditReturnVal = fldPtr->inputAudit (fldPtr->inputEditValue);
X	    if ((auditReturnVal == dmmAuditBad)
X	        || (auditReturnVal == dmmAuditReturnBad)) {
X		dmmRetType = dmmBadField;
X		return fldNum;
X	    }
X	}
X    }
X    dmmRetType = dmmOkayField;
X    return -1;
X}
X
X
X/* cntInstructs(fldPtr)	return the number of instruction lines for
X *			the specified field number
X */
Xstatic int
XcntInstructs (fldPtr)
XDMMFIELD       *fldPtr;
X{
X    register int    numInstruct = 0,	/* number of instruction lines */
X                    instructNum = 0;	/* index in inputInstructVec */
X
X    if (fldPtr->inputInstructVec) {
X	while (fldPtr->inputInstructVec[instructNum++]) {
X	    numInstruct++;
X	}
X    }
X    return numInstruct;
X}
X
X
X/* arriveInField()		display the input field of the specified
X *				number with the edit attributes and display the
X *				instructions for that input field
X */
Xstatic void
XarriveInField (fldPtr)
XDMMFIELD       *fldPtr;
X{
X    register int    numInstructs,	/* number of instructions left to
X					 * display */
X                    instructLine;	/* line number for displaying next
X					 * instr */
X
X    /* display field in appropriate attributes */
X    dispInField (fldPtr, fldPtr->inputEditAttrib);
X
X    /* display instruction lines in reverse order, bottom up */
X    numInstructs = fldPtr->inputNumInstructs;
X    instructLine = LFLINE;
X    while (numInstructs--)
X	mvwaddstr (fieldWin,
X		 instructLine--, 0, fldPtr->inputInstructVec[numInstructs]);
X    return;
X}
X
X
X/* leaveInField()	display the input field of the specified
X *				number with the normal attributes and erase
X *				the instructions for that input field
X */
Xstatic void
XleaveInField (fldPtr)
XDMMFIELD       *fldPtr;
X{
X    register int    numInstructs,	/* number of instructions left to
X					 * display */
X                    instructLine;	/* line number for displaying next
X					 * instr */
X    /* display field in appropriate attributes */
X    dispInField (fldPtr, fldPtr->inputAttrib);
X
X    /* erase instruction lines starting with the bottom */
X    numInstructs = fldPtr->inputNumInstructs;
X    instructLine = LFLINE;
X    while (numInstructs--) {
X	wmove (fieldWin, instructLine--, 0);
X	wclrtoeol (fieldWin);
X    }
X    return;
X}
X
X
X/* chgInField()	change to the first position of a (perhaps
X *			input field; old is the last input field,
X *			and delta specifies what the change is to the
X *			new input field; return the number of the new
X *			input field.
X */
Xstatic int
XchgInField (old, delta)
Xint             old;
Xenum change     delta;
X{
X    int             new;	/* number of the new input field */
X
X
X    /* determine new field */
X    new = ((delta != stayput) ?
X	   (delta == next ?
X	    dupeScreen[old]->nextInField : dupeScreen[old]->prevInField) 
X	   : old);
X
X    /* if field actually changed, leave old field and arrive at new field */
X    if (new != old)
X	leaveInField (dupeScreen[old]);
X    arriveInField (dupeScreen[new]);
X
X    /* move cursor to first position in new field */
X    wmove (fieldWin, dupeScreen[new]->inputLine, dupeScreen[new]->inputCol);
X
X    return new;
X}
X
X
X/* adjustFld()	convert trailing blanks of an edited
X *		field value to nulls and intermediate nulls to blanks.
X */
Xstatic void
XadjustFld (fldPtr)
Xregister DMMFIELD *fldPtr;
X{
X    register int    charCnt = fldPtr->inputLength;	/* counter for
X							 * characters output */
X    enum location {
X	internal, external
X    };				/* whether before or after the terminating
X				 * null of inputEditValue */
X    register enum location state;
X
X    state = external;
X    while (charCnt--) {
X	switch (state) {
X	case external:
X
X	    switch (fldPtr->inputEditValue[charCnt]) {
X	    case '\0':
X		break;
X	    case ' ':
X		fldPtr->inputEditValue[charCnt] = '\0';
X		break;
X	    default:
X		state = internal;
X		break;
X	    }
X	    break;
X
X	case internal:
X	    if (fldPtr->inputEditValue[charCnt] == '\0')
X		fldPtr->inputEditValue[charCnt] = ' ';
X	    break;
X	}
X    }
SHAR_EOF
echo "End of part 1"
echo "File dmm.c is continued in part 2"
echo "2" > s2_seq_.tmp
exit 0
-- 
Rob Bernardo					Mt. Diablo Software Solutions
email: rob at mtdiablo.Concord.CA.US		phone: (415) 827-4301



More information about the Alt.sources mailing list