DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT 

DOCUMENT:  FOSQL forms package UCO/Lick

1st draft Jan 1994
2nd revis Aug 1994

BETA BETA BETA BETA use with caution
BETA BETA BETA BETA use with caution
BETA BETA BETA BETA use with caution
^^^^ read this as red and blinking, with sound effects :-)

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

Contents of this doc:

	1.  History, Philosophy
		an overview of how fosql happened and what it is
	2.  Features and limitations
		overview of specific features and limitations of fosql
	3.  Actual pieces of fosql:  the tables
		detailed description of underlying Sybase tables
	4.  Actual pieces of fosql:  the code
		detailed description of procs in the fosql library
	5.  Cookbook
		basic "how to" -- how to make a form, starting from zero
	6.  Common problems
		how to use fosql to address common forms design issues
	7.  Use the Source, Luke
		a guide to finding and reading the sources of fosql, and
		related URL's
	8.  Prometheus Bound
		listing of all the nifty bindings that hold fosql together
	9.  Fonts of Wisdom
		listing of the font aliases and their probable display fonts

This document may not exactly match the current contents of the fosql
distribution, because keeping the doc up to date is not as high a
priority as developing the application.  I apologize in advance for
any cognitive dissonance the reader may experience.  The doc should
be incorrect only in fine detail.  The overall design approach and
function has not changed since the project began.

When in doubt, read the source.  Look at the examples.  Look at the
contents of the tables.  Believe what the code is really doing, not
what this doc says.  This doc is useful, but not guaranteed authoritative.

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

1.
Introduction:  History, Philosophy

The "fosql" package is a forms design and deployment package for
use with Sybase under Unix/X11.  It requires a working Sybase server,
the Sybase OpenClient library on the deployment host, and the Tcl/Tk
suite with the additional TclX (Tcl Extensions) and sybtcl packages.

Fosql is free (see the COPYRIGHT information) and so is all of the 
Tcl/Tk suite and its extensions.  Sybase is a licensed product of
Sybase Inc, as is OpenClient.  MIT X11 is free.  Fosql will run under
MIT X11 or any other ICCC compliant X11R5 implementation.

The project which engendered this package was the port of a complete
and extended UC accounting system from a 10-year old VMS/Datatrieve
platform to the Unix environment.  The previous environment offered
only a dumb alpha (query/response) user interface, and the new one
would offer GUI and many functional extensions.  Sybase was 
selected as the database engine of choice, partly for its technical
features and partly because of the advantageous contract which was
negotiated between UC and Sybase.

The project would require all the standard components of an AIS
system:  underlying schema development, integrity constraints,
report writing, ad hoc query, and forms query/entry.  Schema
design did not require CASE becase (a) the designer had enough
expertise to be impeded rather than helped by point/click tools,
and (b) the schema was not terribly complex.  Integrity constraints
likewise.  For report writing, the SQR language which Sybase had
recently acquired from SQL Solutions Inc was quite adequate.

For ad hoc query (naive user interface) we decided to go with
Natural Language from NLI.  This left the forms question
undecided.  It was a thorny question because the campus as a
whole was about to commit to a central IS which would impose,
in 2 years or less, a consistent vendor-supplied (and hideous)
forms interface on all offices.  So the lifetime of any forms
interface for the local system was limited, and an enormous
investment was not justifiable.

There are a lot of forms development packages for Sybase in the
market, so why did I choose to roll my own?  Partly because of
cost.  While the University receives a generous discount from
Sybase, everyone admits that Sybase's "Data Workbench" application
environment is lousy.  And the third party forms app vendors do
not offer a generous discount -- their products are insanely
expensive.  Most of the forms packages we considered would have
cost us more than our total expenditure for the Sybase software
suite.

At the time of acquisition (of Sybase) we had recently discovered
the Tcl suite.  As soon as I found out about the sybtcl package
which makes Tcl able to interact with a Sybase server, I began
to think about the possibility of writing our own applications.
The cost of doing so had to be balanced against the very high
cost of acquisition of commercial forms software and the ongoing
high cost of maintenance of commercial software (as well as the
implicit cost of not getting bugs fixed in a timely fashion, not
getting new features that we needed, etc).

I did a tiny pilot application using the Tk X11 toolkit, to 
discover what user interface development was like using Tk
and sybtcl.  The answer was, "Amazingly rapid and easy."  
At the same time, we were checking out Tom Poindexter's
"wisql" tool (friendly X11 general interface to Sybase server).
Wisql was impressive, well-designed and easy to use.  I
then made up my mind to extend wisql with a point/click
super-simple report writer and data editor, and to go ahead
and write a forms interface for local use.

Extending wisql was somewhat timeconsuming, but the tools that
emerged were immediately useful to me as well as others.  The
experience gained in doing this, and some entire sections of
code, were successfully recycled into the forms development
effort.  The enhanced wisql (uco-wisql) is now distributed
with Tom Poindexter's sybtcl suite, and the original TP wisql
is now known as "wisql-lite".  The extraordinary speed of X11
tool development using Tcl/Tk encouraged me to believe that
a forms package could indeed be designed and deployed in time
to meet my project deadline (working system by July 1 94!).

The basic requirements for a forms user interface do not vary
much from one environment to another.  View, edit and entry
operations have to be implemented in some relatively friendly
way.  Screens should be easy to read and intuitive to use.  
Error messages from the underlying database should be presented
visibly, catastrophic exits should not happen, etc., etc.

I had an additional design requirement, which was that the entire
forms interface should be based in Sybase tables, with one
general-purpose body of code generating the forms according to
data read from these tables.  Table-driven layout and function
would result in a far more flexible, controllable, maintainable
package, and one for which (by means of SQR) complete reference
documents could be generated by software and not by humans.

At this moment, the "fosql" suite consists of about six thousand
lines of code (in addition to the basic "ucosyb.tlib" Tcl library
which it requires and shares with wisql) and several Sybase tables.
This code is capable of painting forms, providing user controls such
as buttons and entry boxes, updating/inserting/deleting data by
means of user interaction with forms, performing implicit simple
joins, and allowing the developer to design forms interactively
(though the design process is not WYSIWYG, it is "close").

[NB as of Aug 1994 the fosql suite is about 9.5K lines of code]

This document explains how fosql works and how a designer would use
it to build forms.  This document is written mostly for in-house
reference, but it is my intention to release fosql for public use
as soon as it is stable and proven by in-house use.

[NB Fosql OLTP interface to financial database went online Jun 13 94,
 3 weeks ahead of schedule.  There was no beta period, so the first
 month of production was rough.  Since then, users have settled down
 and seem to like the interface.  New apps are still being feverishly
 developed.]

2.
Basic mechanics (and limitations) of fosql

Fosql consists (like wisql) of a "launch script" which sets up some
global variables and then calls the startup procedure.  The startup
procedure presents the user with a "login widget" for Sybase.  If
login is successful, then a startup screen is built.  In the case
of wisql, the startup screen is the main wisql window;  in the case
of fosql, the startup screen is merely a launch pad for forms which
the user selects from a set of menus.  The form menus are dynamically
built using information from the "forms" table (the Sybase table which
defines all forms and all widgets within each form).

[The startup screen is bound to be somewhat customized from one site
 to another, and at our site there are "select a fiscal year" buttons
 on the startup screen.  Alas, these buttons are hard-wired into the
 startup screen code.  I didn't quite have it together to drive the
 appearance of the main screen from a table :-).]

When the user selects a form name from a menu, the procedure makeForm
is called with the arguments FormName and 0, where 0 is the "level"
of this form being invoked (forms are able to spawn child forms).
The procedure makeForm uses information from the forms table to
build and display any level of any form.

Only one copy of a "form" (at this design stage) can be displayed and 
used at a time.  NOTE that a "form" consists of the level 0 form and any
or all of its children.  So multiple windows of one "form" may
be seem simultaneously.  But the same "form" cannot be displayed
twice on the same user's screen.  Multiple different "forms" and
their children can be displayed at any time.

No child levels of a "form" may be launched before their immediate parent 
has been launched, and parent levels cannot be dismissed before their 
children have been dismissed.  Multiple coequal siblings which
are children of one form, can all be displayed simultaneously and
dismissed at will.

From now on I will be referring to individual screens or windows
as forms, without quotes.

Each form is a window within which are displayed some number of
"widgets" (I am using this all-purpose word to mean the functional
bits and pieces of which a form is composed).  These widgets are
Tk toolkit widgets, like buttons, radiobuttons, listboxes, entry
boxes, text boxes, labels, etc.  When fosql paints the form, it
collects all the widgets that belong to that level of that form
family (by selecting records from the "forms" table) and converts
the information it collects into Tk commands which actually create,
bind, and pack widgets on the screen.

Once the form is built, the user can start interacting with it
by means of keyboard and mouse.   If the user is running in Expert
(designer) Mode, then interactions are possible which alter the
appearance or function of the form.  Normal users should not,
repeat not, be permitted to run fosql in Expert Mode.  Many
obscure bindings are activated in Expert Mode which might be
invoked accidentally by a naive user and cause confusion to the
user if not damage to the form.  Expert Mode is controlled by a
global variable set in the launch script (more details later).

2a. included functionality:  procedures

Fosql can be extended for custom use in a number of ways.  One
of the characteristics of a button widget, in fosql, is a piece
of text which is the literal command (set of Tcl/Tk commands)
to execute when the button is pushed.  Any designer could add 
procedures to the fosql library, so the range of actions which
a button can execute is bounded only by the limits of Tcl/Tk
(and those are very large limits).  However, a small set of 
"standard" procedures is included which provide certain 
predictable and simple functions.  This spares the designer the
necessity of writing the obvious stuff from scratch.

Standard "button-executed" procedures include code to Clear a
form, to Delete a record, Insert a record, or Update a record,
to get Help, to Quit a form, etc.  The Help facility is driven
by a directory containing help files, one per topic keyword,
which are read in and displayed to the user upon request.  The
help facility is very similar to that in wisql (context sensitive)
except in being file-driven rather than hardwired.

These procedures obviously have to make some assumptions about the 
kind of tables that the forms represent, in order to perform data-altering
operations safely.

The primary assumption is that any table represented by a 
fosql form MUST have a primary-key, which has been set in
Sybase using sp_primarykey.  Fosql will complain bitterly
if the underlying table is not primarykeyed, and results 
will be unpredictable!  The next major assumption is:  if
data from more than one table are represented on a single
form, then one table MUST be the primary or master table,
and the secondary table MUST be properly foreign-keyed to
the master tables primarykey, AND there MUST be a one/one
relationship between the two tables.  In other words,
a clean one/one join must be possible using the primary
key of the master table.

To show a hierarchical table relationship in fosql, you
would use a child form for the "many" side of the relation.
You could also use embedded listboxes.

Anyone can extend fosql to handle any bizarre non-normalized
table relationships they care to invent, but to use the
standard builtin functionality, your relations must be
normalized and adhere to these simple rules.

2b. included functionality:  bindings

More builtin functionality is provided in fosql by means of
default bindings.  The same help mechanism that is provided
with uco-wisql has been implemented in fosql.  Control-Mouse-1
gets "geographical" help, that is, help about the nature and
function of the types of widgets and parts of the form that
the user sees.  Control-Mouse-2 gets data dictionary help,
if the implementer has built a data dictionary to the wisql/fosql
specification.  Many other bindings are provided by default,
some for the designer (shortcuts and fastkeys for repetitive
design operations) and some for the user.

Every widget has at least one characteristic which is a text
field containing secondary binding information (bindings which
supplement or override the default fosql bindings) -- hence
the developer, using only the default code, can easily bind
any widget to do any Tcl/Tk command upon any recognized X11
event taking place within that widget.

Fosql is not at this time capable of handling Sybase's more
arcane datatypes such as BLOBs, bitfields, and text.  It can
handle all other normal Sybase datatypes properly.  One of
its more arcane little features is a "fprecis" attribute
on data display widgets which permits you to truncate the
display of FP values to N places (one of Sybase's more
irritating features is its excessively honest representation
of decimal fractions).

2c.  included functionality:  look&feel

Fosql is set up with many defaults for look&feel of forms.  It 
supports only those fonts used in wisql and accessible through
the getFont command in ucosyb.tlib, for example.  The borderwidths,
shape, and general appearance of most widgets is hardcoded into
the procedures that build them, to ensure a consistent appearance
throughout the form.  This document will explain in greater detail
in a later section, which attributes of widgets are dynamic (in
the forms table) and which are static (hardcoded).

For example, since my application is strictly monochrome, there
is no provision for control of widget colour.  This is definitely
a fosql limitation.  Anyone who routinely uses colour displays
may want to submit an extension or patch set for colour. Also,
fosql makes assumptions about the resolution of displays which
mean that forms come up the wrong size on X terminal screens
of lower res than 19in sun mono monitors.  This is on the TODO
list.  Fosql also does not use any X resources.  This should
be on the TODO list also.

Because all these defaults, whether enhancing or limiting, are built
in to the fosql release, it is ready "out of the box" to build forms which
will meet the needs of the average dba using ordinary, well-designed 
schema on normal Sybase datatypes.  The accessibility of Tcl source
means that the more ambitious designer can "take it and run with it"
to do anything they wish, still taking advantage of the basic mechanisms
provided.

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

3.
The actual pieces of fosql:  the tables

There are several tables involved in fosql:

forms		(defines all widgets in all forms)
fwidgets 	(defines the meaning of each field in forms for each widget
		  type -- user should not alter contents of this table)
ftypes		(defines form types, for the purpose of dividing them into
		 menus of similar forms, such as Personnel, Fiscal, Worksheet,
 	 	 MetaData, etc)
fassists 	(defines "assist list" code to present users with pick-lists
   		  for the entry of predefined data elements such as type
		  codes, category codes, zip codes, social security numbers,
		  etc -- anything where a "lookup table" would be handy for
		  the user to refresh their memory or find the code they
		  need)
dictionary 	(the data dictionary)

The names of these tables are set in the launch script in a series
of global variables (forms, fassists, fwidgets, ftypes, dictionary).
They should be fully-qualified Sybase table names (base.owner.table).

The launch script is heavily commented and should be read carefully by
anyone trying to install their own version of fosql.

The definitions of these tables are as follows (a script which creates
them and correctly sets their Pkeys and protections is included with
the fosql release):

------------------------------------------------------------------------------
forms table

This is the heart of fosql.  Each form, and each widget in every form,
is described by one record in this table.  This table is somewhat evil
by relational database design rules, because several distinct entities
are encoded in one table AND the fields in this table may have different
meanings (as attributes of those different entities).  However, I
wished to prevent the uncontrolled multiplication of tables, and since
this is an underlying "meta table" which normal users never see, I
was not terribly concerned about making it easy to understand.

The table "fwidgets" is the key to the forms table.  It tells you, for
each widget type, which fields are attributes for that widget and what
they mean *for that widget*.

Every record in the forms table has a unique primary key ID "fid".  There
is a trigger for this table which ensures unique new fid for every record
inserted (this trigger is provided with the release).

fname is the name of the form or form family, a "nickname" used to identify 
the members of one form family (a master form and its children if any).

ftype is the form type, used to divide forms into groups so that they may
be split into several menus on the startup screen.  ftype has this 
meaning only for records where wtype = 'GN'.  For EN and DA widgets it
is the FP precision flag.  (sorry, that was a late feature and had to
be crammed in somewhere).

level is the level (generational level) of this form in its family.
The first digit of this value is the generation;  if there is a 2nd
digit it is a unique sibling number within the generation.

item is the "item number" or sequential number assigned to each widget
as it is added to a form.

area is the 1-char name of a frame in the form, if the form has subframes
(there is provision in fosql for dividing the displayed form up into 
subframes, for more creative packing).  this is usually "f" in normal forms.

wtype is the widget type code.  Widget types as of 1.0.2 are:

BT	button
CB 	checkbutton
DA	data display (non-entry)
EN	entry box
GN	(not really a widget) general form info
LA	label
LB	listbox
MB	menubutton
ME	message widget
PS	PseudoSQL button, a button which executes SQL
RB	radiobutton
TX	text entry box

Note that GN is not a real widget type, but a magic value that means "this
record is a general description record for this level of this form".  A
GN record contains different kinds of information.

For the rest of the field meanings, it will be best for you to skip ahead to
the discussion of the fwidgets table, where the data in that table will
be printed out.

create table forms (
  fid	int	NOT NULL,
  fname	char(10)  NULL,		
  ftype	char(2)  NULL,
  level	tinyint	NULL,
  item	smallint	NULL,
  area	char(1)  NULL,
  wtype	char(2)  NULL,
  tbln	varchar(40)  NULL,
  fldn	varchar(40)  NULL,
  name	char(10)  NULL,
  labx	float	NULL,
  laby	float	NULL,
  labs	smallint	NULL,
  labf	varchar(40)  NULL,
  labt	varchar(40)  NULL,
  laba	char(2)  NULL,
  entx	float	NULL,
  enty	float	NULL,
  ents	smallint	NULL,
  entf	varchar(40)  NULL,
  enta	char(2)  NULL,
  cmd	varchar(100)  NULL,
  next	smallint	NULL,
  prev	smallint	NULL,
  help	varchar(30)  NULL,
  dflt	varchar(40)  NULL,
  cmd2	varchar(255)  NULL,
  echo	int	NULL
)

------------------------------------------------------------------------------
fwidgets table

Because of the design obscurity of the forms table, it's necessary to have
a "translation table" that tells us the meaning of the fields in the forms
table for each widget type.  This translation table is not only used by
poor confused mortals who have forgotten which field to look at, but by
the fosql code itself as it builds dialog boxes and help messages for
the designer.  Tcl is particularly well-suited (because of its capacity
for recursive evaluation) to table-driven design.

The fwidgets table is itself simple.   Each record consists of

wtype (the widget type code, a list of which appeared above)

field (the name of the field in the forms table)

meaning (a brief string cluing the reader in to the meaning of this field)

srt (a sorting value used to force these fields to appear in certain
     arbitrary orders in designer dialog boxes, etc.)

help (a help topic used to present help messages to the designer)

create table fwidgets (
  wtype	char(2)  NOT NULL,
  field	char(10)  NOT NULL,
  meaning	char(15)  NOT NULL,
  srt	tinyint	NULL,
  help	char(10)  NULL
)

here is the 1.0.2 version of the contents of this table (I have introduced
widget type sub-headings to remind you of the wtype translations) :

 wtype field      meaning         srt  help       
 ----- ---------- --------------- ---- ---------- 

button:

 BT    labf       button font        0 ChooseFont 
 BT    labt       button text        1 LabelText  
 BT    cmd        button command     2 ButtonCmd  
 BT    labx       button rel-x       3 RelXYpos   
 BT    laby       button rel-y       4 RelXYpos   
 BT    laba       button anchor      5 Anchors    
 BT    fldn       privs required     6 ReqPrivs   
 BT    cmd2       other bindings     7 UserBind   

checkbutton:

 CB    labf       check-b font       0 ChooseFont 
 CB    labt       check-b label      1 LabelText  
 CB    fldn       check-b variabl    2 AssocVar   
 CB    labx       check-b rel-x      3 RelXYpos   
 CB    laby       check-b rel-y      4 RelXYpos   
 CB    laba       check-b anchor     5 Anchors    
 CB    cmd        Bindings A         6 UserBind   
 CB    cmd2       Bindings B         7 UserBind   

data display:

 DA    tbln       name of table      0 TableName  
 DA    fldn       name of field      1 FieldName  
 DA    labf       label font         2 ChooseFont 
 DA    labs       label size         3 CharSize   
 DA    labt       label text         4 LabelText  
 DA    labx       label rel-x        5 RelXYpos   
 DA    laby       label rel-y        6 RelXYpos   
 DA    laba       label anchor       7 Anchors    
 DA    entf       data font          8 ChooseFont 
 DA    ents       data size          9 CharSize   
 DA    entx       data rel-x        10 RelXYpos   
 DA    enty       data rel-y        11 RelXYpos   
 DA    enta       data anchor       12 Anchors    
 DA    echo       Echo widget #     13 EchoWidget 
 DA    cmd        Bindings A        14 UserBind   
 DA    cmd2       Bindings B        15 UserBind   
 DA    dflt       default value     16 InsDefault 
 DA    ftype	  FP precision	    17 FPprecis

data entry:

 EN    tbln       name of table      0 TableName  
 EN    fldn       name of field      1 FieldName  
 EN    labf       label font         2 ChooseFont 
 EN    labs       label size         3 CharSize   
 EN    labt       label text         4 LabelText  
 EN    labx       label rel-x        5 RelXYpos   
 EN    laby       label rel-y        6 RelXYpos   
 EN    laba       label anchor       7 Anchors    
 EN    entf       entry font         8 ChooseFont 
 EN    ents       entry size         9 CharSize   
 EN    entx       entry rel-x       10 RelXYpos   
 EN    enty       entry rel-y       11 RelXYpos   
 EN    enta       entry anchor      12 Anchors    
 EN    dflt       Default KeyVal    13 InsDefault 
 EN    echo       id# of echo wid   14 EchoWidget 
 EN    cmd        Bindings A        15 UserBind   
 EN    cmd2       Bindings B        16 UserBind   
 EN    ftype	  FP precision	    17 FPprecis

form general info:

 GN    fname      Name of App        0 AppName    
 GN    ftype      Type of Form       1 FormType   
 GN    level      Level              2 FWlevel    
 GN    dflt       Primary Table      3 TableName  
 GN    area       Keying T/U         4 NULL       
 GN    tbln       Window Name        5 FWwinname  
 GN    entx       Width              6 FWwidth    
 GN    enty       Height             7 FWheight   
 GN    labx       X Position         8 FWstartx   
 GN    laby       Y Position         9 FWstarty   
 GN    fldn       Icon Name         10 FWicon     
 GN    labf       Icon Bitmap       11 FWbitmap   
 GN    cmd        Required Fields   12 FWrequired 

label:

 LA    labf       label font         0 ChooseFont 
 LA    labs       label size         1 CharSize   
 LA    labt       label text         2 LabelText  
 LA    labx       label rel-x        3 RelXYpos   
 LA    laby       label rel-y        4 RelXYpos   
 LA    laba       label anchor       5 Anchors    
 LA    cmd        Bindings A         6 UserBind   
 LA    cmd2       Bindings B         7 UserBind   

listbox:

 LB    tbln       name of table      0 TableName  
 LB    fldn       name of field      1 FieldName  
 LB    labf       label font         2 ChooseFont 
 LB    labs       label size         3 CharSize   
 LB    labt       label text         4 LabelText  
 LB    labx       label rel-x        5 RelXYpos   
 LB    laby       label rel-y        6 RelXYpos   
 LB    laba       label anchor       7 Anchors    
 LB    entf       data font          8 ChooseFont 
 LB    dflt       box size WxH       9 BoxSize    
 LB    entx       box rel-x         10 RelXYpos   
 LB    enty       box rel-y         11 RelXYpos   
 LB    enta       box anchor        12 Anchors    
 LB    cmd2       pseudoSQL A       13 PseudoSQL  
 LB    cmd        pseudoSQL B       14 PseudoSQL  
 LB    ents       1/0: master?      15 NULL       

menu button:

 MB    labf       Menu font          0 ChooseFont 
 MB    labt       Menu Label         1 LabelText  
 MB    entf       Item font          2 ChooseFont 
 MB    cmd        Menu Cmds A        3 MenuCmds   
 MB    cmd2       Menu Cmds B        4 MenuCmds   
 MB    labx       Menu Rel X         5 RelXYpos   
 MB    laby       Menu Rel Y         6 RelXYpos   
 MB    laba       Menu Anchor        7 Anchors    

message widget:

 ME    labf       message font       0 ChooseFont 
 ME    labs       message size       1 CharSize   
 ME    labt       message text       2 LabelText  
 ME    entf       aspect ratio       3 Aspect     
 ME    labx       message rel-x      4 RelXYpos   
 ME    laby       message rel-y      5 RelXYpos   
 ME    laba       message anchor     6 Anchors    

pseudo-SQL button:

 PS    labf       Button font        0 ChooseFont 
 PS    labt       Button text        1 LabelText  
 PS    cmd2       pseudoSQL A        2 PseudoSQL  
 PS    cmd        pseudoSQL B        3 PseudoSQL  
 PS    labx       Button rel x       4 RelXYpos   
 PS    laby       Button rel y       5 RelXYpos   
 PS    laba       Button anchor      6 Anchors    
 PS    fldn       Privs required     7 ReqPrivs   
 PS    entf       Return A           8 PSQLreturn 
 PS    dflt       Return B           9 PSQLreturn 

radiobutton:

 RB    labf       radio-b font       0 ChooseFont 
 RB    labt       radio-b label      1 LabelText  
 RB    fldn       radio-b variabl    2 AssocVar   
 RB    labx       radio-b rel-x      3 RelXYpos   
 RB    laby       radio-b rel-y      4 RelXYpos   
 RB    laba       radio-b anchor     5 Anchors    
 RB    cmd        Bindings A         6 UserBind   
 RB    cmd2       Bindings B         7 UserBind   

text entry widget:

 TX    tbln       table name         0 TableName  
 TX    fldn       field name         1 FieldName  
 TX    labf       label font         2 ChooseFont 
 TX    labs       label size         3 CharSize   
 TX    labt       label text         4 LabelText  
 TX    labx       label rel-x        5 RelXYpos   
 TX    laby       label rel-y        6 RelXYpos   
 TX    laba       label anchor       7 Anchors    
 TX    entf       text font          8 ChooseFont 
 TX    dflt       text size WxH      9 TextSize   
 TX    entx       text rel-x        10 RelXYpos   
 TX    enty       text rel-y        11 RelXYpos   
 TX    enta       text anchor       12 Anchors    
 TX    cmd        Bindings A        13 UserBind   
 TX    cmd2       Bindings B        14 UserBind   

Rather than try to comment or document each one of these entries, I will
reprint here the contents of the designer-help messages.  They have been
alphabetized by topic code, for ease of look up.

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

Designer Help: Anchors

Anchors: all widgets are anchored, meaning that you have to decide which
edge or corner of the widget is at the coordinate position you specify.
The anchor positions are n, e, w, s, and c (center) for all labels,
and n, e, w, s, nw, ne etc (and c) for TX and LB widgets (which being
large rectangles, are trickier to place).


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

Designer Help: AppName

Form Name: each form application has a unique name. This name should
be brief and meaningful. It is what the user will see in the Forms
menu when the app is deployed.


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

Designer Help: Aspect

Aspect Ratio: message widgets have an aspect ratio indicating the ration
of width to height, in arbitrary units (100 is square, 1000 is 10 times
as wide as high, etc.) -- this ratio controls the way text will be
displayed in the message.


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

Designer Help: AssocVar

Variables: some widget types can have associated variables (checkbuttons
and radiobuttons). You can determine the name of the associated TCL
global var by setting this value.


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

Designer Help: BoxSize

ListBox Size: the size of a listbox is expressed as a string WxH --
you need the 'x' between the width and height integer values. The width
is the width of each line in the box, in characters, and the height
is the count of displayed lines. Remember that since v7, Tk is stingy
in the way it computes widget widths, using character '0' (which is
rather narrow in some proportional fonts).


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

Designer Help: ButtonCmd

Button Command: this is the tcl code that you would have written after
the -command option when declaring a button using ordinary Tk. If you
want global variables to be interpreted at execution time, remember
to backslash (escape) your dollar signs.


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

Designer Help: CharSize

Character Sizing: the widths of entry boxes and a number of other widget
types are expressed in count of characters displayed. As of v7, Tk
is a bit stingy in its sizing, using character '0' as the standard
1-unit width. You may want to oversize by 1 character if you are using
proportional fonts.


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

Designer Help: ChooseFont

Font Names: the wisql/fosql/wisqr suite uses a common procedure called
setFont, which accepts 'font nicknames' or keywords which it then translates
into real x11 screen font names. See the source of getFont for the
full list of legitimate font names, but some commonly-used ones are:
banner (Helvetica bold 20) mediumcou (courier 14) mediumhel (Helvetica
14) mediumtim (Times 18) courier (Courier 12) helvetica (Helvetica
12) smallcou (Courier 9) smallhel (Helvetica 9) secure (obfuscated
font used for your password when loggin in) Adding 'b' to the end of
most of these names will embolden the font, and adding 'i' will italicize
it. 'bi' is not yet supported, sorry.


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

Designer Help: EchoWidget

Echoing Widgets: you can cause one widget in your form to echo or replicate
the contents of another widget. The most usual use for this is in a
child form, to replicate an essential key value from the parent and
prohibit the user from altering it. The DA (data) widget type is well
suited to this. But you could use an EN widget and make the user able
to alter data echoed from another widget, for a data editing form perhaps.


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

Designer Help: FieldName

Field Names: some widgets refer directly to a field (column) of a specific
table. If they do, you need to enter the field name exactly as it appears
in the table definition, so that the forms app will properly retrieve
and/or update data using that widget.


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

Designer Help: FormType

Form Type: originally I intended to provide a few standard form layouts,
each one distinguished by a 'form type'. For example, one might have
a vertical control panel at the left, a message area below, and then
an open area for widgets. I have not abandoned this idea, and a 'standard'
4-or-5 area form is built into makeForm. At the moment, however, most
forms are type 'XX' (generic).


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

Designer Help: FWbitmap

Bit Maps: when the user closes your form into an icon, this field lets
you specify the bitmap that will be displayed in the icon. It will
be found in the 'bitmapdir' directory (see the global variables in
the 'forms' launch script) so supply only a filename, not a full pathname.


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

Designer Help: FWheight

Form Height: this is the vertical size of your form window when it is
painted on the user's screen.


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

Designer Help: FWicon

Icon Name: when the user iconifies your form, the icon will have a name
emblazoned on it. You set this name here.


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

Designer Help: FWlevel

Levels of Forms: a forms application may have one or more levels of
child forms that 'pop up' from parent forms. at this point, only one
form at each level is supported. Levels are numebred starting at 0,
and the user is prohibited from quitting from earlier levels while
later levels are still on screen IF you use formControl properly in
your Quit button definition. The proper usage is del appname level
which instructs the application to pop that form off the current stack.


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

Designer Help: FWrequired

Required Fields:  This is the list of fields from the master table that
will be shown in the "Find Window" if the user tries to Find an incompletely
specified record.  The Find Window is not large enough to display all
fields of all tables, so this list lets you choose the ones that will
quickly identify a unique record.


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

Designer Help: FWstartx

Start Position: This is the X starting position of your forms window
on the user's screen.


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

Designer Help: FWstarty

Start Position: This is the Y starting position of your forms window
on the user's screen.


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

Designer Help: FWwidth

Form Height: this is the horizontal size of your form window when it
is painted on the user's screen.


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

Designer Help: FWwinname

Window Name: this is the name of the parent window for this level of
this form. Make sure that all winnames for one forms application are
unique. Avoid funny characters in these names. You do not include the
initial '.' -- the application will do that for you, and will be confused
if you try to provide it. Typical winnames: ppl_form, host_form, label_form
etc.  Window names cannot start with a capital letter (Tk v3).


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

Designer Help: InsDefault

Default Insert Values:
Some tables have triggers that replace or recalculate inserted values,
(for auto-numbering, for example, or random number generation). You
need to know what insert values the trigger finds acceptable (it may
want 0, NULL, or -1, or some other number) so that its own action is
not confused. This is the place to specify a safe insert value for
any field which will be recalculated or replaced by a trigger-supplied
value.


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

Designer Help: LabelText

Labels of Things: entry boxes have labels, buttons have text on them,
standalone labels have text in them, etc. This text can be not more
than 40 characters in length.


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

Designer Help: MenuCmds

Menu Definition: the items in menus are expressed in this field as follows:
a list of *-separated items, each item consisting of one word which
is the menu item label, and N more words which constitute the command
to be executed when the user chooses that item (sorry about the one
word limit, it's just lazy parsing). Example: 'General fHelp general
* FastKeys fHelp fastKeys'


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

Designer Help: PseudoSQL

Some widgets support a pseudo-SQL command (PS and LB widgets). An LB
widget can only do a 'select' to get data back into the listbox display.
A PS widget can do anything you want it to do. A pseudo-SQL command
is just like ordinary SQL except that any literal value can be replaced
by a special string consisting of a question mark and the name of an
EN or DA widget. In the resulting parsed SQL, the ?name string will
be replaced by the properly-quoted contents of that widget. This is
a powerful tool. Have fun.


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

Designer Help: PSQLreturn

pseudoSQL return values: A PS widget might execute some SQL that would
return values. This field permits you to tell the app what to do with
the return values. It consists of a list of *-separated triplets, each
in turn consisting of a pair of indices into the list of values returned
by the SQL command, and then the ID number of a widget to put the output
into (I used ID number to save characters in these limited-length fields).
The pair of indices will be used literally in an 'lrange' tcl command,
so 'end' is legitimate. You get two fields of 40 chars each, which
are catted together to make the Return instructions. If your pseudoSQL
will return multiple rows, use a listbox (LB) instead.


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

Designer Help: RelXYpos

Positioning Widgets: All widgets in a forms window are positioned in
relative X and Y coordinates: they are placed at X percent of the width
of the frame, and Y percent of the height of the frame. Enter these
values as precise floating point numbers between 0 and .9999.


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

Designer Help: ReqPrivs

User Privs: Some buttons may perform SQL actions that require specific
user privs. This field is 20 chars, but you only need 3 at most: I
(insert) D (delete) and U (update). You express them as a space-separated
list ('I D' for example).


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

Designer Help: TableName

Table Names: Many widgets refer explicitly to one particular table.
This is where you set the name of that table, in the canonical form
owner.object (dbo.headers, for example). At the moment the forms app
is not capable of spanning multiple databases in one form.


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

Designer Help: TextSize

Sizing TX widgets: a TX widget is a text widget, a rectangle containing
text wrapped to word boundaries. Its dimensions are specified, like
those of a listbox, in characters wide x lines high. Use integer values
in the format WxH.


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

Designer Help: UserBind

User Bindings: for designer convenience the application permits you
to set additional bindings on any widget, or to override the default
bindings of the app or of Tk. The bindings spec is a *-separated list
of items. Each item consists of exactly one word of bind event (<Leave>,
for example) and N more words of the command you would like to bind
to that event. A sample entry might be '<Control-Shift-Meta-1> userHelp
arg0 arg1 ' -- if you had written a proc called userHelp that would
do something with such args.


(end of Designer Help printout)

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

fassists table

Some entry boxes may be intended to receive one of of a known list
of codes or identifiers, such as one of a number of known building
ID numbers, one of a list of employee classifications, one of a list
of social security numbers of employees.  In these cases, it is 
easier if the user can see the list and see the "English" translations
of the codes to verify that they are picking the right one.

The "fassist" feature is designed to pop up a pickList (see ucosyb.tlib)
with selected fields from a table or tables which will permit the user
to pick the right value to enter in the associated entry widget.  When
the user double clicks on the correct choice, or chooses a list item and
then clicks OK, the code or ID value will be entered into the entry
box for them.

Assist-equipped entry boxes should be visually distinguished from all
others.  I do this by adding an asterisk to the end of the label for
that widget.  But it could be done any number of other ways, also.
There can be only one fassist for any given widget.

The fassists table is composed of

fid (the fid of the widget (record in forms table) for which this is the
	assist list)

sqlcmd (the SQL code which generates the lines to display in the pickList)

resindex (the index of the result value in each line in the pickList -- 0,
	usually, if you print the code value first and then N words
	of translation, but you could have a different index)

ident (a string to print as the title of the pickList data)

width (the width with which to format the code value when printing the
	pickList lines, so that all text after the code value will align
	properly)

inswid (a list of other index pairs and widget fids for inserting multiple
        values from the return record at one time.  this value consists
   	of triples (i1 i2 fid) separated by back-quotes.)

create table fassists (
  fid	int	NOT NULL,
  sqlcmd	varchar(255)  NOT NULL,
  resindex	smallint	NOT NULL,
  ident	varchar(20)  NULL,
  width	int	NULL,
  inswid varchar(40) NULL
)


fassist is a very powerful tool, which can really speed up data entry
by allowing users to look up just about any encode value on line, rather
than pawing through paper lists while at the keyboard.

------------------------------------------------------------------------------
ftypes table

this trivial little table just translates the "form type code" into
text equivalents:  for example, "FI" into "Fiscal Data".

From these types are built the menus in the main startup form.  The
DBA should establish as many ftypes as are needed to divide the forms
sensibly into categories.  The menu buttons on the startup form will
display the ftype string + ellipsis.

create table ftypes (
  ftcode	char(2)  NOT NULL,
  ftype	char(25)  NULL
)

------------------------------------------------------------------------------
data dictionary table

This table is optional.  If you set the global var "dictionary" to NONE,
then fosql will know that there is no data dictionary support.  If you
set this variable to the name of the dictionary table, and you populate 
this table with correct information, then data dictionary help will be 
available to both fosql and wisql users.

Each record in this table describes exactly one field in one table.
Each table is identified by dbase, owner, and name.

Each record has a unique "ddid" primary key value.  the rest of the fields
are:

base (name of database)

owner (name of owner)

tbln (name of table)

fldn (name of field)

ddesc (text description of field's meaning or function and possibly some
	data entry hints like "no dashes, please")

xref (if this is a foreignkey, a "copy" of a field found in another table,
	then the xref is the ddid of the "master" copy of that field -- this
	is a way of representing foreignkeying in the dictionary)

uid (uid of person who entered this record)

stamp (date/time this record was entered)


create table data_dict (
  ddid	int	NOT NULL,
  base  varchar(40) NOT NULL,
  owner varchar(40) NOT NULL,
  tbln	varchar(40)  NOT NULL,
  fldn	varchar(40)  NOT NULL,
  ddesc	varchar(255)  NULL,
  xref	int	NULL,
  uid	int	NULL,
  stamp	datetime	NULL
)

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

here endeth the description of the tables which support the fosql software.

The fosql distribution includes sample forms and data dictionary entries.
As shipped it consists of:

	3 forms:
		Data Dictionary
		Forms Form
		Test Form
	(which are all of ftype DB)

and all the relevant data in all necessary tables to support these 3
samples.  The data dictionary documents itself and the forms table,
for example.


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

4.
The actual pieces of fosql:  the code

Fosql, like wisql, relies on about 1700 lines of tcl code which are
collected in "ucosyb.tlib", the general-purpose UCO sybtcl library.
Excluding that code, the modules actually in fosql.tlib are:

aboutFosql
	executed when the user chooses "about Fosql" from the startup
	screen help menu.  displays copyright information and "This
	is free software" banner.

areYouSure
	pops up a dialog box saying "Really Delete?".  if the user selects
	"Yes I'm sure" then the global var areyousure is set to 1, else 0.
	This global will be used on return to main code, to continue with
	or abort a SQL delete command.

assistList
	builds the "assist list" pickList (see ucosyb.tlib) for a entry
	widget that has this feature.  invoked on dbl-click on an
	assist-equipped widget.
	
bindWidgets
	during form startup, performs all default (fosql) and designer-
	supplied bindings for all widgets.

chkEntry
	for widget types TX and EN, checks that the data the user has
	entered in the widget is correct for that widget type, and
	tries to make corrections if not.  for example, it will truncate
	strings to the length of a char field.  if the value entered in
	a numeric field is not a number, chkEntry will blank out the
	field.

chkMsg
	(lifted from TP's original wisql) checks the sybase return message
	buffer and if it finds messages, displays them in the main message
	area of whatever window we are currently in.  under construction.

confirmExit
	(lifted from TP wisql) for non-expert mode, confirms that the user
	really wants to exit fosql.

eFcommit
	in developer mode, when changes are made to the basic form, this
	proc is executed by pressing the Commit button from the form editor
	dialog box.  the changes are then committed to the forms table
        by means of a SQL update statement.

eWLcommit
	in developer mode, when changes are made to a list of widgets, this
	proc is executed by pressing the Commit button from the widget list
	editor dialog box.  the changes are then committed to the forms table
	by means of SQL update statements.

eWapply
	in developer mode, when changes are made to one widget, this proc is
	executed by pressing the Apply button in the widget editor dialog
	box.  the changes are then displayed on the screen.

eWcommit
	in developer mode, when changes are made to one widget, this proc is
        executed by pressing the Commit button in the widget editor dialog
        box.  the changes are then committed to the forms table by a SQL
	update statement or an insert statement.

eWpaint
	in developer mode, this routine paints the widget editor dialog box
	on the screen, displaying only those attributes which are relevant
	for the widget type (by using the fwidgets table).

editForm
	this proc is executed either from the "New" button (in developer mode)
	in the startup window, or from the binding Control-Shift-3 anywhere
	in a frame in the form window.  it invokes the form editor dialog
	box.

editWidget
	this proc is executed from the binding Control-Shift-1 (edit existing
	widget) in any widget on the form, or Control-Shift-2 (create new
	widget) in any empty area of the frame, in developer mode in a
	form window.  it invokes the widget editor dialog box.

fHelp
	this proc is called with one argument, the "topic code", and pops
	up a "help box" consisting of help text and a dismiss button.  It
	is called from a Control-1 binding in any part of a form, or from
	various Help Menus in various windows.  Help is independent of
	form app name or form level.

formControl
	this proc is used when forms are started up and dismissed, to check
	whether the launch or dismissal is valid.  launch of later levels
	(children) of a form before the parent is launched are invalid, as
	are dismissals of parent (earlier) levels of a form before the
	children have been dismissed.  All Quit buttons should use 
	formControl (examples follow in a later section)

gFindExe
	this proc is merely a wrapper for genericFind, which gets keyvalues
	from the form before calling genericFind.  doubtless I had some
	reason for doing this.

genericClear
	this proc is called from the pressing of a Clear button, usually,
	and its function is to clear all EN, TX, and DA widgets on the
	form.

genericComm
	this proc is called when the user presses an Update, Insert, or 
	Delete button.  It will do its best to perform the SQL operation
	correctly, including the return and insertion into the form of
	values generated by insert triggers, and the display of error
	messages if the SQL operation should fail.
	in the case where the user used Control-3 to press the button,
	rather than a normal Mouse-1, genericComm will run in "show" mode,
	where the generated SQL instead of being executed will just be
	displayed for the user's edification.

genericFind
	this proc is called when the user presses a Find button.  It will
	check the keyvalues for this form.  if there are adequate key
	values to identify a unique record, it will call genericGet.
	otherwise, it will display a pickList of the "required fields"
	for the master table of this form, permitting the user to
	choose a record of interest.

genericGet
	this proc is used when the user has entered sufficient keyvalues
	to find a unique record.  The SQL statement for finding a record
	based on a primarykey is so much faster and cheaper than any other
	select statement, that genericGet will be used whenever possible
	rather than the (potentially expensive) SQL generated when many
	fields have been filled out by the user ("where n = M and o = P and
	q = R and s = T...")

genericNext
	this proc is executed when the user presses a Next (in my implementation
	usually ">>>" or ">") button.  It will find the record with the next
	higher primarykey value for the master table.  by using Prev and
	Next buttons, the user can "walk through" records in the table in
	order of their primarykeys.

genericPrev
	this proc is executed when the user presses a Prev (in my implementation
        usually "<<<" or "<") button.  It will find the record with the next
        lower primarykey value for the master table.

genericTdel
	this proc is called from genericComm when the SQL operation is a
	deletion.  it generates N SQL statements, one for each table
	represented on the form, and deletes where pkfield = pkvalue for
	each of those tables.

genericTins
	this proc is called from genericComm when the SQL operation is an
	insertion.  it is smarter than Tdel, for those widgets which are
	associated with a field in a table, it generates correct SQL syntax
	(including quotes) for an insertion value list and a field name
	list, then inserts the values into the fields.   If the form is
	a multi-table form, then Tins is smart enough to do N inserts,
	inserting new values into each table and preserving pkey integrity.

genericTupd
	this proc is called from genericComm when the SQL operation is an 
	update.  for those widgets which are associated with a field in a 
	table, it determines which fields have changed their values since
	the values were displayed from the original data, and generates
	a value list of new values with which to update that list of
	fields.  in the case where the form is a multi-table form, Tupd
	is smart enough to know that a missing dependent table entry should
	be inserted, even if the operation on the master table is an
	update.

getJvalues
	this proc is called from genericGet and generates a select *
	from <join expression> SQL statement, where the join expression
	is the correct join for a multi-table form (of course, no join is
	done if the form is a single-table form).  after getting the
	value for each field, it sets an element in the table data
	global array (tabledata(col_name,load)) to the retrieved value.

getKeys
	getKeys checks that a widget is visible for every key field of
	each table represented in the form, and that only one widget is
	visible for each key field.  It then makes a list of the 
	"keywidgets" for that form, for future reference.

getValues
	getValues is no longer used.  It was replaced by getJvalues.
	it used to be executed in a loop (foreach table, getValues).
	But the SQL join is much more efficient.

initGlobals
	initGlobals is called once at form launch.  It sets up all required
	global variables (lists, arrays, and single variables) to hold all
	the state information required to manage all levels of the form
	family.

linkDBentry
	given a level value and the names of two widgets, this proc will
	link the two widgets in the forms table by setting "next" of the
	first widget to contain the fid of the second one, and "prev" of
	the 2nd one to contain the fid of the 1st.  This is how linked
	lists of widgets are created.

linkEntry
	this proc does the bindings that link two entry widgets (EN or TX).
	when entry widgets are linked into a list, Tab or Return will
	traverse the list forward, and Shift-Tab or Shift-Return will
	traverse the list backwards.  this enables the user to fill out
	this list of widgets without having to reach for the mouse and
	break the typing rhythm.  widgets can be linked by the designer
	in any order, regardless of their position on the form, and 
	multiple linked lists of widgets can exist on the same form.	
	linked lists of widgets are always circular;  Tab from the last
	widget traverses to the first one again.
	Additional bindings are provided for EN widgets which are linked.
	on traversal to the next widget in the list, a chkEntry is done
	on the widget being left behind.

linkLBends
	fosql uses "lists of listboxes" to display multiple records of
	data on one form.  a pseudoSQL statement is associated with
	the list of listboxes (discussed later).  listbox lists are
	not circular.  they have a beginning and an end, and the
	first box in the list is the "master" or "name" of the list.

listFmt
	(somewhat derived from TP wisql) this proc, given a list of column
	names, types, lengths and a maximum length constraint, will generate
	a Tcl format string sufficient to "typeset" output records composed
	of these columns in an aligned, tabular format.

mAcommit
	this proc is called in developer mode when the designer presses the
	Commit button in the AssistList editor dialog box.  this commits
	the assist-list to the fassists table.

mainMsg
	this proc uses global variables containing the names of the message
	widget (the main form message area) for each level of the current
	form, so that given the current level it can display a message
	in the appropriate message widget (the topmost one, we hope!)

makeForm
	this proc is the Big One, the one that launches a form family.
	it calls many other procs including formControl and initGlobals,
	as it sets up the form.  it retrieves all the widget information
	from the forms table and "builds" each widget as a set of attributes
	stored in global variables, as well as actually painting the widget
	on the screen.

mkAssist
	this proc launches the fassist editor dialog box.  it is invoked by
	the developer Shift-Double-Clicking on an EN widget.
	
mkButton
	this is the first of a series of procs which are called from 
	mkWidget.  each one, given a set of variables containing values
	collected from the forms table or from a dialog box, will try to
	"make" a widget of the appropriate type, with all its default
	fosql bindings.  this one makes a plain old X11 button or BT.

mkCheck
	this one makes a checkbutton or CB.

mkData
	this one makes a "DA" widget, which is an entry widget which is
	disabled for user data entry.  the user can look but not touch.
	DA widgets do not have the visible border that entry widgets have,
	because on a paper form they would not be "blanks to fill in."
	
mkEntry
	this one makes an entry-box or EN.

mkLabel
	this one makes a label or LA.

mkList
	this one makes a listbox or LB.

mkMenu
	this one makes a menubutton or MB.

mkMesg
	this one makes a message widget or ME.

mkRadio
	this one makes a radiobutton or RB.

mkText
	this one makes a text widget or TX.

mkWidget
	this proc is the "master widget maker" that takes care of universal
	attribute management (attributes that apply to all or most widgets)
	and executes the "mk" procedures listed above.

In order to keep the "widget makers" together, I had to list these next two
procedures out of alphabetical order.  I thought it would be less confusing,
even so, than lumping them in with the last series.

mkFormMenu
	this proc is executed at fosql startup time, and foreach formtype
	(from ftypes table) it paints a menu of all those forms belonging
	to that formtype.  these menus are therefore DYNAMIC, table driven,
	and the source need not be edited to keep the user menus up to
	date.

mkTellTale
	in developer mode, when a form is launched, a "TellTale" widget floats
	off to the right of it.  upon Shift-Enter (shifted mouse traverse into)
	a widget, certain selected widget characteristics will be displayed in
	the telltale.  this is handy for a quick look at "what was the Y
	position of that entry box, I want to line this label up with it" and
	so forth.  only one Widget Editor dialog box can be active at a time,
	so the TellTale is a big time saver when one wants to recall basic
	attributes of existing widgets while trying to create a new one.
	
popList
	this proc is invoked usually by a button called GET or something like
	that, and it executes pseudoSQL associated with a list of listboxes.
	the results of the pseudoSQL are then displayed in the listboxes.
	popList stands for "populate listboxes".

pseuSQL
	this proc turns pseudoSQL (SQL in which certain values are expressed
	as ?name_of_widget) into real SQL (SQL in which the entered values
	are retrieved from the named widgets and correctly substituted into
	the SQL command).  it then executes the real SQL.  then it displays
	the results of the SQL in the widgets associated with the PS button.
	(by interpreting the triples startind, endind, fid which are stored
	as an attribute of the widget, see above for fwidgets "help" text).

readKeys
	this proc reads the keyvalues from the displayed form and puts them
	in a global list keyvals.  if it cannot get non-null correct values
	for all keys, it returns NO.  it's up to the calling proc to watch
	the return value and act accordingly.

refreshFrame
	in developer mode, sometimes the designer has Applied too many changes
	and wants to return to "how it looks in the database."  this proc
	will kill all the widgets in the current frame and reload them all
	from the forms table (basically doing a portion of startForm over
	again, but without unnecessary overhead.)  It is invoked when the
	designer uses Control-Meta-3 on a blank area of the frame.

selInsert
	not used at this revision
	
selList
	this proc will populate entry or DA widgets on the form, when the
	user selects a line in a linked list of listboxes (of the data
	displayed in the linked list of listboxes, the user picks one
	record to modify or delete.  the values in that record therefore
	need to be copied into accessible EN widgets on the form.  that
	is what this proc does.)

selWclear
	in developer mode, the designer can select a list of widgets on the
	screen, in order to link them or in order to make some basic change to
	the entire list.  (obviously, the only attributes that can be changed
	under these circumstances are attributes that all the widgets in the
	list have in common, and thereby hangs a fair amount of code..).  the
	designer uses Control-Meta-1 to select widgets in the desired order,
	and those widgets become visually distinguished on the screen as
	members of the current list.  selWclear will clear the list, i.e.
	deselect all widgets from the current list.

selWedit
	in developer mode, this proc will be invoked when a list of widgets
	has been established and the designer presses the "E" button to
	edit attributes of the list of widgets.  this proc will launch the
	"edit widget list" dialog box.

selWlink
	in developer mode, this proc will be invoked when a list of widgets
        has been established and the designer presses the "L" button to
	link the list together.  the "next" and "prev" fields in forms will
	be altered appropriately to establish the list.  (see linkDBentry and
	linkEntry).

selectWidget
	in developer mode, when the designer uses Control-Meta-1 to select
	a widget, this proc is invoked.  if it is invoked on a widget already
	selected, the widget becomes deselected.

setMsg
	a very trivial little proc that stuffs text into a message widget.

showValues
	after getJvalues has preserved the represented table(s)' data in
	global variables, showValues will actually insert the retrieved
	data into the appropriate entry, text, or data display widgets on
	the screen.

startForm
	after the user has successfully logged in to Sybase server, startForm
	is executed to construct and pain the start window from which all
	forms are launched.

stuffTT
	in developer mode, this proc is invoked when Shift-MouseEnter happens
	for any widget on the form.  it retrieves attributes of the entered
	widget from global variables and displays them in the TellTale (see
	mkTellTale).

tblPrivs
	this proc, given a list of requested privileges (in the form U, I,
	D, U I, I D, etc) will check to see whether the current user has
	the requested privs for all tables represented in the current form.
	if so, it returns OK.  otherwise it returns an informative little
	message about tableprivs not being equal to requested privs.  this
	proc is used in fosql when painting SQL action buttons.  one possible
	attribute of a button is the priv(s) required for the SQL it 
	performs.  if fosql does not confirm that these privs are available
	on the current represented tables for the current user, then the
	button is disabled by fosql (appears grayed-out on the user's screen).

testCnt
	this proc checks how many records of a given table match the current
	keyvals from the form.  this is handy to check for:  dependent table
	record already exists on master record insert (badness);  more than
	one dependent table records exist for master record (badness) etc.

tryConnect
	this is the login widget, the first thing the user sees on starting
	fosql.

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

5.  Cookbook

What do you actually have to do to use fosql?

First you have to install it.  That means, read the INSTALL document in
the release.  This document does not deal with the installation of
fosql.  To install it, you have to execute various isql and csh scripts
(after careful thought and editing) in a specific order.  The INSTALL
doc gives step-by-step instructions.

Next, you have to start fosql by typing, probably

	forms <username>

where username is your Sybase username.

You get a Sybase login widget.  Once you have entered a correct Sybase
password, you get the fosql startup window.  We'll assume that you 
are a developer, in which case you started out with

	forms -x <username>

or your site has hacked fosql about a bit so that your username is one
of a list of magic usernames (besides sa) which always run fosql in
developer mode.

The window you see will therefore have a "New" and a "Menu" button on
it, as well as the menubuttons for each formtype in the ftypes table,
a Help button, and a Quit button.

If you were at my site, you would also see a series of radiobuttons that
select which fiscal year the fiscal data forms should look at.  But other
sites will probably not be using those!

Making a new Form, part 1:  create the basic form

So, you want to create a new form for a table.  Let's say you have a table
in a Sybase database called work, and the table is owned by dbo, and the
table's name is "people."

Let's say that the fields in "people" are:

empid	int
last	varchar(50)
first	varchar(50)
street	varchar(50)
city	varchar(30)
state	char(2)
zip	(int)
starts	smalldatetime
ends	smalldatetime
status	char(1)

where empid is the unique primary key (an employee id number) which is
generated by the insert trigger on this table.  NOTE that you must use
the sp_primarykey procedure in Sybase to establish the correct primary-
keying for the table.  Otherwise fosql will complain.  (Also it will
not work right!)

Let's also assume that that you have another table people_status that 
looks like:

status	char(1)
statext	varchar(50)

where status is a unique pkey for people_status.  (Fosql will not care 
whether you have used sp_primarykey for this one, as it is not part of
a join with people for the purpose of this form).

Now, we will also pretend that you have data in the people_status table,
like

R	Retiree
E	Emeritus, still present
S	Regular Staff
F	Fired, do not admit to building
M	Management
X	Executive Management
C	Outside Consultant, do not admit to Floor 3

(I'm making this up as I go along, so don't expect it to look much like
real-world data).

Now we have a very typical simple forms entry situation.  You have a basic
entity, a Person, which is described by the table people, and you have
categories into which people fall, or codes that you assign to people,
which are described by the table people_status.  This is a very simple
fosql form.

You're going to want a simple form with entry blanks for each of the
fields in 'people', and an assist list feature for the entry blank 
for field "status" in people.  Piece o' cake.

The first thing you do is press "New".  The Forms Editor dialog box
pops up and with a lot of blanks for you to fill in.

It wants to know the form name, or Name of App.   Here you would give it
a simple, short name like "People".  Then it wants the form type.  That
depends on how your site organizes the ftypes table and hence the 
startup window menus.  Assuming that at your site you have an ftype
PE, for Personnel, then you need a record in the ftypes table

	PE	Personnel

and you can enter PE as the formtype for this form.

For level, since you are entering the first form for this app, you enter
zero (0).

For Primary Table, you would enter work.dbo.people (the Fully Qualified Name
of the table).  Note that you *could* operate fosql in a more braindead mode
where it will work only on tables in one database, and that database is
hardwired into a global variable in the launch script.  However, you
may as well do it the right way in this example.

For Keying you will enter T (because your primarykey(s) are trigger-supplied,
not user-supplied).

For window name, enter any reasonably short lowercase string for X11 to
use as the name of this window.  You could call it "people".

For width and height, enter the dimensions (as understood by your window
manager) of the form as you want it painted.  Don't make it so large that
it won't fit on the smallest screen a potential user might sit at, or so
small that you'll have trouble fitting all the information into it.

For X and Y position, enter the screen coordinates (as understood by your
window manager) where you want the form to pop up when the user launches
it.  

For Icon Name, enter a string that you would like the user to see displayed
as the title of the icon when the form is iconified.  For Icon Bitmap,
enter the name of a .xbm file that you would like used as the icon for
this form.

For Required Fields, enter the list of fieldnames from the table people
that the user would need to see to select a unique record easily.  In this
case, you almost certainly want empid, last, and first.

That's all.  Now use Commit to commit the basic form info to the database.

You will now need to use the Menu button to recalculate the menus so that
your new form will appear in them.  You need to start up your new form in
order to edit it.

Making a new Form, part 2:  labels and buttons

So now, you check the Personnel menu to see if your new form is there.  If
not, one of several things might have gone wrong.  The ftypes table might
not have the type code you assigned to the form.  Something might be wrong
with your copy of fosql.  Time to read the source, Luke.

But we'll assume that you succeeded and that your new form is there.  You
select it, and it comes up with the dimensions and at the location that you
requested.  Of course, it is totally blank, and fosql complains (in the 
xterm from which you started it) that the primarykey values are not
displayed.  But that's all right.

The first thing you probably want is a label, something that tells the
user what form this is (sorry, we can't do logos, shading, boxes, or any
other form of graphics at this time).  So you move the mouse into the
blank form window and use Control-Shift-Mouse-2 to bring up the "create
new widget" editor dialog box.  You might want to put the mouse near 
where you would like the label to be (see below) before clicking.

This box is small at first, because fosql does not yet know what kind of 
widget you want.  It will ask you for the widget type and name.  ALWAYS
use lowercase strings with no embedded "funny characters" for widget
names, and NEVER use the same name for two widgets at the same level
of the same form.  It's not a bad idea to make the widget names at least
reasonably meaningful, also.

OK, so this is a label.  You might want to call it pplformlab, and its
widget type is LA.  When you hit return after entering the last piece of
information, the box suddenly grows larger and presents you with blanks to
fill in for all the attributes of a label widget.  There are not too many
of them, because a label is a simple sort of widget.  First you have
to pick a label font.  This is not an X11 font name, but a font alias
as understood by the getFont proc in the ucosyb.tlib library (see 
section 9, about fonts).  Fosql will try to provide reasonable defaults
for most of these fields, by the way.

Now you have to decide how long your label is.  Note that the sizing of
text widgets in fosql is a little counter-intuitive.  The Tk toolkit sizes
a text box or label according the the width of the character 0 (zero) in
the selected font.  So, because all fonts other than Courier are usually
proportionally spaced, your text could come out shorter than Tk would
predict for its length in characters, or it might end up wider.  You will
have to "get a feel for" how wide to make your labels, entry boxes, etc.
Anyway, let's say 15 characters for now.

Now for the label text.  Enter "Employee Form".

You inherited an x and y position from the position the mouse had when
you invoked the widget editor.  You could change this position, or
accept it.  Now you need to decide how to anchor your label.  What this
means is "Which edge of the label will be at the x and y position that
I selected?"  So if you enter e, for east, the right hand edge of the
label will be at that position and the label will be "right justified"
against that point.  If you enter w, it will be "left justified," which
is more intuitive for most people.  Try w for now.

For the Bindings, just erase anything in those blanks and leave them
empty.  You could, as a more advanced designer, bind this label to do
just about anything when the user clicked on it, passed over it, etc.
(in other words, even a lowly label can become a button of sorts).
For now, make them blank and leave them that way.  

This is a good moment to point out that Control-u (or Control-x or -d)
will erase all of the contents of any entrybox or text widget.  This
can save you a lot of keystrokes, but do make sure that the insertion
cursor (upright blinking bar) is actually in the target widget before
you try to erase.  Just moving the mouse over the widget is not enough,
you have to click on it to get the insertion bar in there.

OK, now if you press the Apply button, you should see your label appear
on your form!  An exciting moment...  but...

NOTE that you have only Applied at this point.  You have not Committed.
If you were to quit fosql now, or if your system crashed, your label 
would be lost.  If you like where it is and how it looks, you'd better
commit now.  To commit this widget to the forms table, press the New
button.  NOTE that when you commit, you get a new widget number 
(fid) and that the message area of your form prints a confirming
"last action" message for you to help you remember what you just
did and what you ought to do next.

Now your form has a label.  That's nice, but surely it needs at least
a Quit button as well.  Otherwise you cannot dismiss it.

Move the mouse to the lower right hand area of the form and start up the
'edit new widget' dialog box again.  This time tell it the type is BT,
and call the button "quit" (widget name).

You are now presented with a list of attributes for describing a button.
Choose a font and some text (I suggest QUIT).  Now you are confronted
with a new blank, the one for the button command.  However, fosql is
dedicated to making your life easier.  Type Meta-q while the insertion
bar is in the Command entry box.  A bunch of text should appear.  That
text is the quit command for this form.

Now you can check on the X and Y position of your button and make sure it
is anchored as you wish.  As far as privileges required, there are none
(it doesn't take any privileges to Quit), so you can just make that 
field blank and leave it that way.  Also, there are no other bindings.

Now you are ready to Apply.  You should see your button pop up.  If you
like it, you can press New.  Otherwise you could tweak it a bit and 
Apply again until you like it.  Do remember to use New when you are
happy with it.  If you have already committed it with New, and then
you decide to change it and re-Apply, just use Change to commit the
changes to the database.

What if you start to edit a widget and decide you really didn't mean to?
Just press "Ciao Baby" and the editor will go away quietly without making
any changes to anything.  [NB -- to those who object to facetious
button labels, sorry.  My assistant programmer begged for some small
shred of humour in the interface, and I gave in... you can always change
it at your site.]

What if you want to get rid of a widget that you already created?  Use
Control-Mouse-1 to click on the offending, pre-existing widget, and
the editor will pop up.  Press Kill and the offending widget will be
wiped out of the forms table and off the face of the earth.  But not 
off the form (known problem).  You have to dismiss and re-start the
form to get rid of that widget.  Be careful with that Kill button.

That Meta-q business was rather handy, no?  You will be delighted to know
that fosql provides quite a set of these designer shortcuts for creating
standard button widgets.  Here they are:

	Meta-u	creates an Update button	(genericComm upd)
	Meta-i	creates an Insert button	(genericComm ins)
	Meta-d	creates a Delete button		(genericComm del)
	Meta-n	creates a Next button		(genericNext)
	Meta-p	creates a Previous button	(genericPrev)
	Meta-f	creates a Find button		(genericFind)
	Meta-g	creates a Get button		(genericGet)
	Meta-c 	creates a Clear button		(genericClear)
	Meta-z  creates a Clone button		(cloneForm)
	Meta-s  creates a Print button		(snapShot)

and 
	Meta-q 	creates a Quit button		(you knew that)

and if you are creating a MB (menu button) then

	Meta-h	creates a Help menu		(General and FastKey help)
	
and to save your weary wrists from too much mousing, when in the widget
editor,

	Meta-a	invokes Apply
	Meta-c	invokes Change
	Meta-n	invokes New
	Meta-k	invokes Kill
	Meta-Q	invokes Ciao Baby  (note that is cap Q!)

The Change button is used when you are editing an existing widget and wish
to commit your changes to the forms table, remember...

Now you know the basics of creating labels and buttons.  You may as well create
an update, insert, and delete button for your form, as well as a find button.
Try to group the control buttons together in an intuitive way.

Now shut down your form and restart it.  All your widgets should show up
where you last left them.  

Now, try out the TellTale now that you have some widgets to examine.  Hold
down Shift and run the mouse into one of your new widgets.  The TellTale should
display information about that widget.  The TellTale will only work on
widgets which existed at the time the form was last started.

NOTE that you can put any text you like inside your buttons.  If your users
would find it easier to understand "Add New" than "Insert", then put "Add New"
in your Insert button.  If they would rather see "Change" than "Update", it's
up to you to give them what they want.   NOTE:  try to observe a consistent
convention, that is, if you use ellipsis (...) to indicate a menu button
(this is pretty standard practise) then do not make regular buttons that
contain text ending in an ellipsis.  Remember MacOS, and try to make your
user environment so consistent that a user who learns one of your forms
can then use any and all of your other forms.  Bear this rule in mind 
when selecting fonts, placing buttons, etc.  Pick a basic look&feel and
*Stick to It*. 

You can use your Quit button, if you like, to dismiss your form.  You should
be able to start it back up again and all your widgets should show up where
you last left them.    It will complain about no widget for the P-key
field, which leads you on to ...

Making a Form, Part 3:  Entry Blanks

This is all very well, you say, but there is still no information on this
form.  We want data.  We want blanks where the user can type in things.
Also the form keeps whining every time you start it, because it has
no entry blank for the Pkey.

OK, no problem.  Pick a spot, maybe near the upper right hand corner, and
start up the create-new-widget dialog box again.  Tell it the type is EN,
and name the widget "empid".

NOTE that the field name was preset to empid by fosql when the rest of
the dialog box unfolded.  Just another little feature.

Make sure you get the table name right.  Make sure it is work.dbo.people.
It must be the name of a real table and it must match exactly what you
entered for the Primary Table attribute for the form.  THIS IS IMPORTANT.

Now, an Entry Box has two parts, a label (that tells the user what this
blank means) and an actual entry box (where they can type in data).  Now
you see why there are fields called labf, labt, labx, and laby -- those
are the label control for an Entry Box (or a DA or TX widget).  I
suggest a smallish font for the label, as you want it not to overpower
the data.  Try "helvetica".  Now pick a fairly visible font for the
actual entry, like "mediumcou" (ugly, but readable and monospaced).
Choose your sizes (6 digits seems reasonable for an empid).  Try to
juggle the x and y coordinates of the two elements, and their anchors,
so that the label text is just below, above, or to the right of the
entry box itself.  

For Default KeyVal, enter the value you need the user to give to your
trigger for an auto-generated Pkey.  We use -1 a lot for
autogen integer keys.  Your trigger, btw, will need to return the
new keyvalue in an output string.  Take a look at the triggers provided
with the forms and data_dict tables.  The Default val will override
anything the user tries to enter in that field.

Now you can Apply and start tweaking your positions and sizes until you
like the look.  Then press New to commit your new entry widget.

You now know how to make an entry widget.  You can create one for each
additional field in the table "people", and they will be very much like
this first one EXCEPT that they will not have default keyvals (that 
attribute must be left blank because you want the user to enter real
values in all the other fields).

NOTE that the quickest way to make new entry widgets, after the first one,
is to keep the dialog box up and just edit the data in it, using New to
store each new widget as you go.

When you are done, you should have a number of entry widgets on your screen.

You should now be able to use the basic fosql features.  Try entering a
known empid in the empid blank and using Find to find that record.  Try
Clearing the Form.  Try Find when the form is cleared.  Try entering some
nonsense data and Inserting it with the insert button.  Try Deleting the
record that you just inserted.  Hey, it's a form!  It does things!

Making a Form, part 4:  Ergonomic improvements

Now it's time to make the form work better for the users.  One thing they
will want is the ability to enter data smoothly, typing it in as if it
were a list:  item, RETURN, item, RETURN, item, RETURN (some users will
prefer TAB to return).  They don't want to be lifting their hands from
the keyboard after each field is filled in, in order to get the insertion
cursor into the next field.

So, no problem, we can link the entryboxes together into a traversal list.

Decide which order you want the boxes to link in.  I advise you start at
the top and traverse the form L-to-R in the normal English text fashion,
unless you want unhappy users.  Now, use Shift-Mouse-1 to click
on the entry widgets in that order.  You should see each one "light up"
as you click on it, and you should see four new buttons appear down in
the message area of the form:   L, U, E and C.  NOTE that a confirming
message appears as you click on each widget, telling you that it has
been added to the list you are building.

When you have finished your list, press the new L button to Link all
your widgets together in a list.  You should see a chatter of confirming
messages as each widget gets linked.  When this chatter is over, and
the last widget has been linked back to the first, you are ready to
press the C button to clear the current list (release the widgets from
the temporary list you built).  You should see them "unlight" one by
one, with confirming messages that they have been released.  If you
were to shift-Mouse-1 on each of them, you could remove them one by
one.

The U button?  it Unlinks them, should you wish to restore them all
to a "vanilla" state.  The E button?  it lets you edit certain
shared characteristics of all of them at once.

NOTE:  the links you have built are in the forms table, but not in
this instantiation of your form.  This is a 1.0.2 limitation which will
one day be overcome when I have a lot of spare time :-) -- you need now
to Quit your form and start it up again.

When you start it up again (it's probably taking a bit longer to start
up now) you can try traversing your list of widgets.  Put the insertion
cursor in an entry widget and hit Return or Tab.  The cursor should leap
to the next widget on the the list, and so on.  Shift-Return and Shift-Tab
should traverse the list backwards.  When you get to the end or the 
beginning, depending on which way you are headed, you should traverse
smoothly round again.

Nice feature.  Now, to make your users' lives even more pleasant we should
give them a built-in lookup list for those pesky status codes.  Use
Ctrl-Meta-Click (Mouse 1) on the status code entry widget.  You should
see the "Assist List Editor" pop up, which will ask you for some information.

It wants an ID, basically a title to print over the list of values it will
present to the user.  Try "Employee Status Codes".  Then it wants a result
index, meaning the position at which the code value will be found in your
lines of output.  Stick with 0 for now.

It wants a width.  Well, the status codes are 1 character wide, but it might
be nice to have a little extra white space.  Tell it 2.

Then it wants some SQL.  So type in

	select * from work.dbo.people_status order by status

which would return the contents of the people_status table sorted by 
status code ascending.  As you can see, the status code is the first thing
that would be printed, so it appears at index 0 of the result.  Note that
as of v1.0.5, the SQL in an assistList definition is pseudoSQL (see below).

Now you can Commit this information, which is the definition of an fassist
and gets stored in the fassists table.

Now you'll have to Quit the form and start it up again (Sorry).  When you
do, if you double-click on the label for status code, or on the entry box,
an assist list should pop up.  If you double-click on a line in the 
assist list, the status code value from that line should be entered in
the entry box for you!

Fosql by default maps the Fkeys on the user's Sun or X terminal keyboard
to execute the basic button set.  See the FastKeys help (main window)
for details.

But how will the user tell that this handy dandy gizmo is hiding under
here?  It is up to you to tell them by

	adding a magic character (like *) to the label text
	setting the label text in a different font

or, if you want to hack on fosql a bit, you could come up with a number of
other creative ways to indicate visually than an fassist was hiding behind
an entry box.

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

Here endeth the basic tutorial on creating a form.  All other features of
fosql are just extensions, refinements, and agonizing contortions of these
basic features.  The more advanced features will be discussed in the
"Common Problems" section which follows, which will assume that you have
got at least this far and are comfortable with the software.

I should confess here that there are sometimes 2 or more ways to do the
same thing in fosql, because the code evolved in parallel with the
applications that it was being used for.  This makes for some confusing
decisions at times.  Send email if you run into one of these "which way
do I do this" problems.  Actually, you will probably find ways to
use this code that I never imagined!


6. Common Problems in Forms Design
   and how to use fosql to address them

We have already seen how fosql can address the three most basic forms
design issues, namely

	1.  the proper traversal of fields so as not to break up data entry
	    typing patterns

	2.  the presentation of reference lists of codes so as to avoid
	    data entry having to refer constantly to paper docs

	3.  the provision of online help explaining data types and meanings
	    and the function of various form parts

This next session is about more subtle forms design problems and how you would
use the fosql features to handle them.

Protected fields:

Often you want to retrieve some piece of information from the database and
display it to the user, but not permit the user to update it.  One way to
do this, of course, is to set Sybase protections on that field in the table
such that this user cannot update that field.  But you might want this user
to be able to update that field from one form, but not from another form.
So the forms themselves have to be able to impose data protection of sorts.
The DA widget type addresses this problem.

The DA widget type can be associated with a table and column, or not, as you
wish.  If it is associated with a table and column, it acts just like an EN
widget type EXCEPT that the user cannot get an insertion cursor into it and
cannot therefore change it.  The DA widget type will not have a box around
it like an entry box.  You may also want to choose a different font for its
text display, to differentiate it still further from normal entry widgets.

The DA widget type is identical in function to the EN widget type except
that it has been disabled by a tcl command.  It is briefly re-enabled
whenever fosql wants to update its contents, then immediately disabled
again so that the user cannot touch it.

Duplicates of fields:

Sometimes, especially in a multi-level form, you want one field to echo the
value found in another field.  For example, if you have a form for PO's, 
and a button on your PO form pops up a child form for viewing and editing
vendor information, you might want the vendor ID on the child form to be
an echo of the vendor ID on the parent form, so that you viewed and edited
the vendor from the current PO.

The "fosql way" to do this would be to make the vendor ID field in the
child form a DA widget (so that the user cannot alter it) and set its
'Echo widget id' attribute to the fid of the vendor ID field in the
parent form.  Then, whenever you pop up the vendor form, the vendor ID
will be a xerox copy of the vendor ID in the parent PO form.

Values returned by triggers:

Insert triggers often return the value of the new primarykey that the insert
trigger has generated and stored.  You would want to display this value in
the Pkey field of the form, partly as a confirmation that the insert has 
taken place and partly for the user's reference.

The Def Ins Val attribute of an EN widget consists of a pair of values 
or a single value.  If it is a pair, the second value is the index
into the trigger return string which fosql should use to recover the
generated value for an autogen keyfield.  The first or only value
is the default value to be used for this field in all inserts.  For
example, "-1 2" in this attribute would indicate that -1 is the default
value and that the trigger returns the new generated value as the 3rd
word of the return string.  "-1" would be more common and indicates that
the returned gen value is the first word of the return string.

NOTE that fosql cannot at this time deal with multifield Pkeys in which
more than one field is trigger-generated on insert and therefore more than
one value is returned.  This is a possible future feature.

To really understand how this works, read the insert triggers for Forms
and Data_Dict, and look at the EN widgets for fid and ddid.

Data Hierarchies:

The simplest way to model a data hierarchy in forms is to stick to the
relational model, so that the parent table is the level 0 form and the
child table is the level 1 form, etc.  But sometimes the nature of the
child table is that it contains *lists* rather than individual records.

For example, the lines of a PO you might want to see one by one.  But
the list of Principal Investigators on a grant or project is usually
shown (on paper) as a comma-sep horizontal list, which users are used
to seeing "as one item" rather than as a set of separate items.  They
would like to see all the PI's on one subform, not having to page
through the subform at one PI per page.

Therefore, fosql has the linked-listbox capacity.  The popList command
takes as its first two arguments the level and name of a listbox widget 
which is understood to be the first in a chain of linked listboxes.  
With this first widget is associated a pseudoSQL command.  The output 
from the command is understood to consist of N column values which 
correspond to the N listboxes in the chain.  The command is executed 
and for each line of output, each field in that line is distributed to 
the appropriate listbox, in order.

All this is a bit abstract, so let's examine a real one.

This is the pseudoSQL associated with the listbox which is head of the
chain of listboxes in the subform "Principal Investigators" for the
Contracts and Grants list:

select a.pid,b.last,b.nname,a.ord from info.dbo.prinvs a, 
	info.dbo.folks b where a.prid = ?prid and b.pid = a.pid

The pseudoSQL is turned into real SQL by substituting for "?prid" the
actual contents of the widget called "prid" at this form level.  Which,
by the way, is a DA echoing the Project ID number from the level 0 
(parent) C&G form.

That SQL is then executed.  Observe that this SQL can be fairly complicated.
It might even be a call to a stored procedure.  The only restriction
is that only one stream of output must come back, formatted in columns.

When building the form, fosql noted that the LB widgets in this subform
are linked together as follows:

	fid	tbln	fldn	name	next 	prev

	184	prinvs	pid	ppid	185	-1
	185	prinvs	last	plast	186	184
	186	prinvs	nick	pnick	187	185
	187	prinvs	ord	pord	-1	186

The list ends with a -1 in each direction.  fosql knows that the LB whose
"prev" field contains a -1 is the first or master listbox.  That is how
it got the pseudoSQL in the first place -- from the attributes of the
master listbox.  Got that?

Now, the order in which these are linked just happens to correspond with
the order in which the columns are retrieved from the data.  If these
two orders are not the same, you will end up with a confused display.
So make sure that your listboxes are linked in the correct order for
the output of your pseudoSQL command.

These listboxes are magic in yet one more way.  Each listbox has a binding
for a simple mouse click (Mouse-1).  That binding invokes selList with the
arguments: level, masterboxname, Y-position-of-click.  What selList does
is this.

It figures out for each box in the chain what its table and field affiliation
is, and looks to see if there is some kind of display widget on the form
for that particular table/field.  It builds a list of those widgets and
narrows the list of boxes to only those which correspond to these widgets.
Then it simply walks through to the selected line (same Y position in each
box as in the box that was clicked on) in each box, and grabs that text
and sticks it in the corresponding widget.

What all this means in practise is that you can display a list of records
in list boxes, and elsewhere on the form, have a set of entry boxes for
entering, deleting, or updating any particular one of those records, while
still seeing the whole list.  Picking a record out of the listboxes will
cause the values in that record to be placed in the entry/data widgets,
if there are any, for those fields.

One of the awkward things about this feature as of 1.0.2 is the invocation of
popList.  At the moment it gets invoked with a button, rather than 
automatically.  This means that things do not get updated as smoothly
as one would like, and the user has to press the GET button before feeling
sure that they are seeing the correct data in the listboxes.

This was overcome in practise by the "exec fid list" attribute on BT and
PS widgets, which permits one button, after completing its main function,
to invoke an arbitrary list of other buttons (by their fids).  It's  kind
of hokey but it works very well and has permitted me to do some "real
magic" in complex financial forms by having one button invoke 3 or more
invisible buttons on the same form, each executing some complex Tcl or
SQL code.

Also, the listboxes can be scrolled independently (bad news) because they
are not hacked about to the same degree as the listboxes in wisql's ezEdit
tool.  So the user should try to keep them all aligned.  Another future
feature.  

If the user sticks to the default bindings in the listboxes (u, d, U, D,
return, tab, Return, Tab, uparrow, downarrow, Uparrow, Downarrow) then
they will scroll in unison.  But if the user starts dragging around with
Mouse-3, individual boxes can be vertically scrolled.  It's on the TODO
list.

****	improvement:  make listboxes scroll in unison (sigh)
			this was a real pain in wisql and doubtless will
			be so again

The linked list of listboxes concept, though a little rough in implementation
at this point, is a powerful way to represent a small data hierarchy on
one form.  It can be used for some tricky purposes, as in the "Labels"
application where each line in the listboxes is a line of a (printable)
label.

Arbitrary SQL Statements:

Sometimes, you just cannot live within the constraints of fosql (perfectly
normalized tables always joining on the Pkey, with one/one relationships,
etc.)  Sometimes you want to perform arbitrary actions with the push of
a form button.  The PS (PseudoSQL) button type is designed to meet this
need.

You have already seen how the contents of fields on the screen can be
incorporated into pseudoSQL commands, by prefacing the name of the
screen widget with a question mark and using it wherever a literal
value could be used in SQL.  The sybtcl interface, take note, is
capable of submitting multi-statement batches in one command buffer.
So you could build a pseudoSQL statement that included newlines,
and execute multiple SQL statements with the push of one button.
You could also execute stored procedures.

The PS widget also offers you a little power as far as the disposition of
your return values.  You can store in the Return Value attribute fields
a series of *-separated triplets, similar in intent to the duples that
determine the disposition of a trigger return.  In this case, the
triplet is
	startindex	endindex	widget-fid
Only one line of output is expected, and for each triplet, the words
of the output line indexed by startind through endind will be put
into the widget whose fid is widget-fid.

If you expected multiple lines of output from your pseudoSQL, you would
be more likely to use a listbox list.

***** improvement:  ability to lump all cols in a listbox output into one
	formatted line, a la find help window.

***** improvement:  ability to ignore newlines in output from a PS command
	and distribute multiple lines of output among widgets

NOTE that if you get desperate, you can always use a normal BT button
and have it call a new Tcl proc that you yourself wrote.  That proc can
do anything you want it to.  You are not limited to the default features
of this software.  Add as many procs as you like.  I have occasionally had
to resort to new procs for site-specific features, when I just could not
fit that much function into one button definition.

Menus for Users:

Another thing you often want on a form is a menu of choices.  The MB widget
lets you set up your own menus.

The MB widget is pretty much like any other button except that its commands
are menu commands.  They are a list of `-sep phrases, each phrase consisting
of one word that is the item label for the menu (a limitation:  only 1-word
labels can appear on your menus) and then of a Tcl command.  You could
add in your own .tlib file and add a bunch of Tcl procs to fosql, invoking
them from your own menus.  Or you could use menus to invoke existing fosql
procedures.  Or single Tcl commands.  Or calls to pseudoTcl.  The world
is yours.

Multiple-Choice Tests:

Radiobuttons (RB) are handy for offering your users a multiple-choice
switch.  Only one of a set of radiobuttons can be selected at a time.
A set of radiobuttons is defined by their all sharing the same (global)
variable.

You will need to be a Tcl programmer to really exploit the power of
radiobuttons, because you will need to write code that uses the
global variables your radiobuttons control.

There are also Checkbuttons, which impose one of two values (on/off)
on a global variable.  Each checkbutton has its own variable, unlike
radiobuttons which share a variable.  Checkbuttons are handy on forms
to turn on and off some modal characteristics of your form, like
CAPITALIZE (you could write code that auto-uppercased all user input
if the global variable 'capitalize' was turned on, for example).

Remember that your global variable names will be altered a bit on their
way into fosql.  Take a look at "mkRadio" or "mkCheck" to see what it
is doing with your original names.  It will be adding more characters
to them to try to ensure their uniqueness.

Designer Bindings:

Section 8 of this manual discusses the standard fosql bindings.  Remember
that most widgets permit you to override or augment these bindings with
your own.  Lists of bindings come in `-separated phrases, where the first
element of the phrase is the X11 event descriptor in Tk format (see the
Tk manuals if you are mystified by this, or read the fosql source for lots
of examples), and the rest is a Tcl command to execute when that binding
is invoked.

Make sure you understand the default fosql bindings so that you do not
inadvertently destroy a useful one.  I've used a lot of the possible
X11 mouse events, but there are plenty of Meta and Shift-Meta alpha keys
left for you to do things with.

See the spec doc for most of the standard bindings (and a list will be
appended here also, one day).

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

7.  Use the Source, Luke!

Part 1: The fosql tcl sources and how they get built

THIS SECTION IS FOR UCO/LICK USE ONLY.  OTHER SITES WILL NOT RECEIVE
THE FULL RCS SOURCE TREE.

The fosql sources are located under one tree, which currently starts
at /u/de/tcl/f (f for forms).  There are a number of other branches in
the /u/de/tcl tree, notably

	w (wisql sources)
	r (sqrl sources)
	tools (misc tcl tools)
	s (ucosyb tlib sources)

The standard contents of one of these directories are:

	a Makefile (example to be discussed later)
	patchlevel (keeps track of revisions released to the public)
	an RCS directory (containing tcl sources)
	a file Modules (created by Makefile)
	a file tlibHeader (edited by hand, not very volatile)
	a file mumble.tlib (built by Makefile)
		which in the case of fosql is fosql.tlib
	a file README (from which README.mumble is made)
	files	mumble.body
		mumble.private.header
		mumble.public.header
	  which constitute the building blocks of the launch script for
	  the application.  for wisql 'mumble' would be 'wisql', for 
	  sqrl it would be 'reports' and for fosql it would be 'forms'.
	(optional) a file DEBUG.mumble.tlib
	(optional) directories 	bitmaps		(X11 icon bitmaps)
			       	sqlcode		(sql sources for underlying
						 sybase structures if needed)
				datafiles	(bcp data dumps or other 'get
						 started' datafiles if needed)
	and probably a lot of cruft with oddball names, relics of various
		evolutionary stages of the code --  among which, mumble.PREV 
		files are the N-1th revision of the released version
	there should also be a file BUG.mumble which is a bug report form.

The makefile for fosql, on Jan 27th 94, looked like this:

===============================================================================
#
LIBDIR = /usr/local/tcl/local
BINDIR = /usr/local/tcl/bin
#
RM = /bin/rm -f
#
install:
	/u/de/tcl/tools/touchrev forms forms.pub fosql.tlib 
	cp fosql.tlib $(LIBDIR)
	/u/de/tcl/tools/bpi $(LIBDIR)/fosql
	chmod a+x forms forms.pub
	cp forms.pub $(BINDIR)/forms.pub
	chown de forms* REA* patchlevel fosql.tlib
#
clean:
	for i in `cat Modules`; do \
		/bin/rm -f $$i; \
	done
#
tlib:
	/bin/rm -f Modules
	ls RCS | tail +2 | sed "s/,v//" > Modules
	$(RM) fosql.tlib
	cat tlibHeader > fosql.tlib
	co -l AAA_PackageHeader
	cat AAA_PackageHeader > fosql.tlib
	$(RM) AAA_PackageHeader
	echo "" >> fosql.tlib
	for i in `cat Modules`; do \
		echo $$i; \
		co -l $$i; \
		cat $$i >> fosql.tlib; \
		$(RM) $$i ; \
	done 
	undebug fosql.tlib
	bpi fosql
#
script:
	$(RM) forms forms.pub
	cat forms.private.header > forms
	cat forms.public.header > forms.pub
	cat forms.body >> forms
	cat forms.body >> forms.pub
	chmod 755 forms
	chmod 755 forms.pub
#
tar:	
	$(RM) fosql*.tar*
	cp $(LIBDIR)/ucosyb.tlib .
	tar cvf fosql10.tar fosql fosql.tlib \
		README.1.0 README.fwidgets COPYRIGHT INSTALL* BUG.fosql \
		patchlevel datafiles sqlcode bitmaps securefont ucosyb.tlib
	compress fosql10.tar
#	NOT YET
#	cp fosql10.tar.Z /usr/local/ftp/UCOSYB
#

================================================================================	
As you can see, there are still some silly hardwired things in the Makefile,
like the code name of the latest revision (10 == 1.0).

make tlib
	checks out all the modules and makes them into a tlib.
	note that AAA_Packageheader is hand-edited and must contain
	the names of all procs in the package

make script
	makes the scripts forms (uses local development copies of 
	everything) and forms.pub (which gets released to the world)

make tar
	makes a tar file for public distribution via anon ftp

make install 
	puts the release copies of the tlib and launch script in the
	right places with the right chmod bits set

The touchrev tool just gets the last patch level from the patchlevel file,
increments the teeny-rev component of the patch level, and seds the 
released files to get the current patchlevel into them.  There is, I
am sure, a better way to do this.  Possibly I should store the current
patchlevel in a Sybase table instead.  Anyway, at the moment this is
what it does.

The usual development cycle is

	co mumble
	vi mumble
	ci mumble
	make tlib
	./forms 	(test local version)
	co mumble
	...

	(success!)
	make script
	make install	(as root, usually)

	(for export? then)
	make tar


Part 2:  The rest of the sources

The source tree for tcl at this site is under

	/usr/local/src/net/TCL...

and the version we run here is built in the directory

	/usr/local/src/net/TCL/ucotcl

You can see in /usr/local/bin/tcl that there are two executables

	ucotcl
	ucowish

which get built by the Makefile in the ucotcl source directory.
Read that makefile if you really want to know.  The process of
building Tcl is nontrivial, but here's a very coarse overview:

	1. build Berkeley Tcl (/usr/local/src/net/TCL/tcl???)
	2. build Berkeley Tk  (/usr/local/src/net/TCL/tk???)
	3. build NeoSoft TclX (/usr/local/src/net/TCL/tclX???)
	4. go to ucotcl directory and 'make'
	   this adds the blt and dp toolkits as well as sybtcl
	   and Xess support.  latest patch was NLI support, May 94

Then you put the executables where they should go (make install
should do this from the ucotcl directory) and you're off.

Part 3:  Where did the Source Come From?  (teleology)

Most if not all of Tcl is posted on 

	harbor.ecn.purdue.edu

which is the 'home server' for the Tcl community.  This is where
you find the Berkeley tcl pieces, sybtcl, extended tcl, and a
plethora of working or nearly-working tools and toys.

Sometimes, when Tcl is in rapid flux, updates are acquired from

	sprite.berkeley.edu

where Ousterhout posts his latest mutations.

See the READMEs in the Tcl source tree for more information.

8.  Prometheus Bound

Here are the standard keybindings for User Fosql:


-------------------------------------------------------------------------------
	      Magic Keys and Short-Cuts for Users

(map of Fkeys is at the end of this doc)

			HELP Keys

Control-Mouse-1           -- gets help on screen objects 
Control-Mouse-2 	  -- gets data dictionary help (if available) on 
			     data entry boxes, or button help on buttons
Control-Mouse-3           -- for SQL actions only, shows the SQL the 
			     button would have executed

Shift-Mouse-1		  -- selects an entry box or adds it to a list of
			     selected entry boxes; deselects if selected

Double-Mouse-1 -- on certain marked entry fields, gets `Code List Help'  
		  to assist the user in choosing one of a list of codes 
Meta-a or F6   -- in the entry blank for these same marked fields, gets 
		  the same code list help 

Meta-/ or Meta-h or F8 gets screen-specific Help

			NAVIGATION Keys

Return or Tab 		  -- moves the insertion point to the next entry box 
Shift-Return or Shift-Tab -- moves the insertion point to the previous box

In list boxes, the data can be moved around (scrolled) in the box by:
	u, d and U, D (up/down one line, up/down 10 lines)  
	up-arrow, down-arrow and their shifted selves
	Return, Shift-Return (down, up) and 
	Tab, Shift-Tab (Down 10, Up 10)

			DATA BROWSING Keys

Find Data is 			Meta-f or F2
Previous Record (<<<) is 	Meta-, (that's Meta-comma) or F3
Next Record (>>>) is 		Meta-. (Meta-period) or F4
Quit is 			Meta-q (no Fkey)
Clone is Meta-z or F11, and Print is Meta-p or F10

			TEXT EDITING Keys

Control-d    -- deletes only the selected text in an Entry or Text widget
Control-u    -- deletes all the text in an Entry or Text widget 
Control-x    -- does the same as Control-u 
Mouse-3-Drag -- scrolls text sideways inside an entry box

			DATA ENTRY Keys

Insert is Meta-i, Meta-n (new), F1
Update is Meta-u, Meta-c (change), F7
Delete is Meta-d, Meta-k (kill), F12 
Clear  is Meta-e (erase) F5 

=============================================================== FKEY Mappings

F1    F2    F3    F4    F5     F6    F7    F8    F9    F10    F11    F12
INS   FIND  PREV  NEXT  CLEAR  LIST  UPD   HELP  XESS  PRINT  CLONE  DEL

SHIFTED:
F1  Insert transaction          F7  Update transaction
F2                              F8  FastKey help (this screen)
F3                              F9
F4                              F10
F5                              F11
F6                              F12 Delete transaction

=============================================================== END OF TEXT


9.  Fonts of Wisdom

(this section intentionally left blank -- not yet provided;  read the source
 of getFont in ucosyb.tlib)


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