The following is a description of a Clipper(tm) browse server which 
incorporates a number of features not previously combined along with support
for use of a mouse for various functions. The system uses a number of
routines from the Nanforum Toolkit.

Some of the basic features include:

1. The ability to browse under the control of a while condition (a substitute
   for setting a filter). This requires an active index which will keep all
   of the records which you wish to see logically contiguous. One can also
   use this system to do a browse without such restrictions to view an entire
   database.

2. The ability to edit a field within the browse. The programmer can chose 
   to allow only certain columns to be editable or none at all. Editing of the
   key field is also allowed while under the influence of a while condition.
   The programmer may specify an alternate GET/READ routine for more control
   over input.

3. The ability to append records to the database is also optionally provided.
   A programmer supplied routine is called to accept input so data can be 
   verified.

4. Mouse control is included. The mouse can be used to select fields/records,
   Initiate an edit of a field, or scroll the database. Hot spots for clicking
   the mouse cursor is also provided which allow the programmer the option to
   execute routines based upon the mouse input. Scroll bars are used in mouse
   controlled scrolling. Clicking on the up/down arrows with the left button
   moves one record while the right button moves a page. Clicking with both
   buttons moves to the top/bottom. The left/right arrows provide similar
   support for columns with the left button moving one column and the right
   panning. 

   Clicking the mouse on the actual scroll bar will position the display at 
   the corresponding proportional position directly. Thus clicking in the
   middle of the scroll bar of a 2000 active record database will move you to
   the logical record 1000. Obviously, one is limited in the increment
   of such scrolling by the ratio of the size of the display and the number
   of records present.

5. Scroll bars to indicate the current position in the browse for both rows
   and columns.

Please direct any questions to:
			Leo Letendre
			CIS: 73607,233

Please report any bugs as there will probably be a few (at least). Also
I am sure there are some oversights in the capabilities. Please pass any
positive or negative comments along. Please note that I have only tried
these routines with a genuine Microsoft mouse so I don't know how well
they work with a three button mouse or other drivers. Anyone who tries
them with another mouse please pass on your comments.

If anyone wishes to use these routines permission is granted for use in 
any finished program, commercial or otherwise. However, permission is not 
granted to include these routines in third party commercial or shareware 
LIBRARIES.

This work is based upon the work of Joe Orlowski who submitted FT_TBWHILE
to the Nanforum Toolkit and the routines in TBDemo.prg supplied with
Clipper 5.0. I hope to submit this work to the Nanforum Toolkit before the
next release. Therefore, I continue to use the code provided in
FT_TBWHILE for testing which is based upon the Toolkit convention of
declaring FT_TEST for the conditional compilation of the test code.
I also plan on integrating these routines with the routines I have written
for using the mouse with the GET/READ system and have therefore begun to
separate out common functions for what would appear to be no reason for one
looking at this code separately.

The headers from the public routines in the system are listed below. Hopefully
they provide enough information for understanding their use. A brief 
description of the mouse hot spots is however in order.

The programmer can designate a set of coordinates and an associated 
code block which will cause the program to execute the code block when the 
mouse is clicked within the coordinates. The code block is executed with four 
arguments passed: button number, row coordinate of mouse cursor, column 
coordinate of mouse cursor and the system time when the mouse button press was 
detected (Value returned by SECONDS()). Thus to use all four arguments a 
code block of the form:

   {|nButton,nRow,nColumn,nTime| MyFunc(nButton,nRow,nColumn,nTime)}

should be passed along with the coordinates which define the activation spot
on the screen. (Note: you must worry about having the mouse event span midnight
when the clock goes back to 0.) You can also temporarily deactivate the hot 
spot by calling BRCoolSpot(id) with the id number of the hot spot to deactivate 
(cool off). One can then reactivate it by calling BRWarmSpot(id) with the id 
number of the hot spot to reactivate (warm back up).

Options are available for setting delays between the successive actions when
a mouse button is held down or to require the release of the button. If you are
worried about .exe size, only the following NANFOR.LIB routines are used:

		FT_MSHOWCRS()		FT_MGETPOS()		FT_MBUTPRS()
		FT_MHIDECRS()		FT_MBUTREL()		FT_MRESET()
		FT_MSETPOS()		FT_INT86()

One additional mouse function is included in MOUSFUNC.PRG. FT_MINIT is a
routine which I have submitted to the Nanforum Toolkit for the next
release (v 3.0). When it appears, you should remove it from MOUSFUNC.PRG
as you will receive a linking warning about duplicate symbols.


The files contained in this zip which are relevant to the browse are:

MOUSBROW.PRG      The main browse routine - Remember - use /dFT_TEST to
                  compile demo.
SCROLBR2.PRG      The scroll bar functions from Clipper 5.01 corrected and
                  expanded to handle horizontal bars.
MOUSFUNC.PRG      Common routines for browse and get/read systems
MBROWSE.CH        Include file for browse
SCROLBR2.CH       Include file for scroll bars
MOUSBROW.RMK      RMAKE file for this demo
MOUSBROW.LNK      RTLINK file for this demo
MBROWSE.TXT       This text file


The API:

*****************************************************************************
*****
* MBROWSE()
*
 *     Browse an indexed database limited to a while condition with mouse
 * 
 *     MBROWSE( <aFields>, <bWhileCond>, <cKey>,
 *                 [<nFreeze>], [<lSaveScrn>], [<cColorList>],
                   [<cColorShadow>], [<nTop>], [<nLeft>],
                   [<nBottom>], [<nRight>], [<bScreenInit>], [<nReadInit>],
                   [<bAltReader>], [<nExitKey>], [<lAppendMode>], 
                   [<AppendReader>] ) -> nRecno
 *  Calling Parameters:
 *
 *     <aFields> is multidimensional array of field blocks of fields you 
 *        want to display. Each row contains up to four members containing:
 *
 *        Index 1: Column heading
 *        Index 2: Code block for displaying information. Note: if you are
 *                 going to do an a read, this must code block must be written
 *                 so that if a parameter is passed, it will set the GET 
 *                 variable. For example: {|x| IIF(x=NIL,Last,Last:=x) }
 *                 can be used by both browse and the GET system.
 *        Index 3: Optional logical indicating if field can be edited if you
 *                 are allowing edits. By default, if you are allowing edits
 *                 (see below) it is .T., and, obviously, if you are not .F.
 *        Index 4: Optional Picture function for get on this column.
 *        Index 5: Optional Code block to be executed as a WHEN clause.
 *        Index 6: Optional Code block to be executed as a VALID clause.
 *
 *        Example to set up last name and first name in array:
 *        aFields := {}
 *        AADD(aFields, {"Last Name", {||Names->Last}, .T., "!XXXXXXXXXXXXXX" })
 *        AADD(aFields, {"First Name", {||Names->First} } )
 *
 *     <bWhileCond> is the limiting WHILE condition as a block.
 *        If cKey (below) is NIL. This is automatically set to {|| .T.}.
 *        If you will be allowing appends you will want to allow arguments to
 *        be passed so that you can use this while block on data entered by the
 *        user prior to adding it to the database. The third example below
 *        shows such a block (as does the example program above).
 *
 *        Example 1: { ||Names->Last == "JONES" }
 *        Example 2: { ||Names->Last == "JONES" .AND. Names->First == "A"  }
 *        Example 3: { |x| IIF(x=NIL, Names->Last = cKey, x = cKey)}
 *
 *     <cKey> is the key to find top condition of WHILE.  
 *        If NIL the entire database is browsed.
 *        cLast  := "JONES     "
 *        cFirst := "A"
 *        Example 1: cKey := cLast
 *        Example 2: cKey := cLast + cFirst
 *
 *     <nFreeze> is number of fields to freeze in TBrowse.  Defaults
 *     to 0 if not passed.
 *
 *     <lSaveScrn> is a logical indicating whether or not you want to
 *     save the screen from the calling program.  Defaults to .T. if
 *     not passed.
 *
 *     <cColorList> is a list of colors for the TBrowse columns.
 *     The 1st color is used as SAY/TBrowse Background and the
 *     3rd and 4th colors are used as part of column:defColor := {3, 4}

 *     Thus if you pass a cColorList, you MUST pass at least 4 colors.
 *     Defaults to "N/W, N/BG, B/W, B/BG, B/W, B/BG, R/W, B/R" if not passed.
 *
 *     <cColorShad> is the color of the TBrowse box shadow.  Defaults
 *     to "N/N" if not passed.
 *
 *     <nTop>, <nLeft>, <nBottom>, <nRight> are the coordinates of
 *     the area to display the TBrowse in.  Defaults to 2, 2,
 *     MaxRow() - 2, MaxCol() - 2 with shadowed box, i.e. full screen.
 *
 *     <bScreenInit> is a code block which calls a routine to initialize the
 *     screen after it has been cleared. You could place help or mouse hot
 *     spots on the screen with this routine. If NIL then nothing happens.
 *
 *     <nReadInit> indicates how a read is initiated. The constants 
 *     GET_ON_RETURN and DOUBLE_CLICK_GET are defined in MBROWSE.CH. 
 *     GET_ON_RETURN indicates that a return will initiate a GET on the current
 *     field. DOUBLE_CLICK_GET indicates that a mouse double click will initiate
 *     a GET on the current field. Either one or the sum of the two can be used
 *     to indicate how to initiate a GET. Passing NO_GET or NIL will prevent any 
 *     editing.
 *     Example:
 *            GET_ON_RETURN+DOUBLE_CLICK_GET   will allow both the mouse and
 *                                             return initiated gets
 *
 *     bAltReader is a code block which is called in place of the internal
 *     reader when the user requests an edit. If NIL then the internal one is
 *     called. This gives more flexibility in doing reads and actually can
 *     be used for functions other than reads (especially with the mouse).
 *     see the function DoGet for details of what must be passed.
 *
 *     nExitKey is a InKey code of a key which will exit the browse other than
 *     the escape key which is always active. By default is is set to K_RETURN
 *     if GETS are not allowed and nothing if they are allowed.
 *
 *     lAppendMode indicates if appends to the database are allowed.
 *
 *     bAppendReader is a code block which will perform a read and append
 *     when the user requests an append. The routine receives no arguments but
 *     must return the number of records meeting the current while condition
 *     that were appended to the database. NOTE: if you are appending records
 *     under a while condition, do not include records which you may have added
 *     but which do not meet the criteria.
 *
 *  RETURNS
 *     nRecno is the number of the record selected by the <Enter> key.
 *     0 is returned if there are either no records matching the WHILE
 *     condition or an <Esc> is pressed instead of an <Enter>
 *
 *  EXAMPLES
 *     * This example will only show those people with last name of "JONES"
 *     * in the TBNames.dbf which contains at least the fields:
 *     * Last, First, City AND is indexed on Last + First.
 *     LOCAL nRecSel    := 0
 *     LOCAL aFields    := {}
 *     LOCAL bWhile     := {||TBNames->Last = "JONES"}
 *     LOCAL cKey       := "JONES"
 *     LOCAL nFreeze    := 1
 *     LOCAL lSaveScrn  := .t.
 *     LOCAL cColorList := "N/W, N/BG, B/W, B/BG, B/W, B/BG, R/W, B/R"
 *     LOCAL cColorShad := "N/N"
 *     LOCAL bInitScreen:= {|| FancyScreen()}
 *     LOCAL nReadInit := GET_ON_RETURN+DOUBLE_CLICK_GET  // return and dbl clk
 *     LOCAL bAltReader := NIL  // no alternate reader
 *     LOCAL nExitKey := K_ALT_E // alt-E will get you out
 *     LOCAL lAppendMode := .T. // we will be daring and allow appends
 *     LOCAL bAppendReader := {|| MybrowseReader()} // The reader
 *
 *     USE TBNames INDEX TBNames NEW // indexed on Last + First
 *
 *     * Pass Heading as character and Field as Block including Alias
 *     * To eliminate the need to use FIELDWBLOCK() function in BROWSE()
 *     AADD(aFields, {"Last Name" , ;
 *          {|x| IIF(x=NIL,TBNames->Last, TBNames->Last:=x) },.T.  } )
 *     AADD(aFields, {"First Name", ;
 *          {|x| IIF(x=NIL, TBNames->First, TBNames->First:=x) },.T. } )
 *     AADD(aFields, {"City"      , ;
 *          {|x| IIF(x=NIL, TBNames->City, TBNames->City:=x},.T. } )
 *     AADD(aFields, {"Zip Code"  , ;
 *          {|x| IIF(x=NIL, TBNames->City, TBNames->City:=x},.T.,;
 *          "@R 99999-9999"})
 *
 *     IF MBROWSE( aFields, bWhile, cKey, nFreeze, lSaveScrn, ;
 *       cColorList, cColorShad, 3, 6, MaxRow() - 2, MaxCol() - 6, bInitScreen,;
 *       nReadInit, bAltReader, nExitKey, lAppendMode, bAppendReader) == 0
 *       ? "Sorry, NO Records Were Selected"
 *     ELSE
 *        ? "You Selected: " + TBNames->Last +" "+ ;
 *           TBNames->First +" "+ TBNames->City
 *     ENDIF
 *
 * Notes: This browse supplies scroll bars with active arrows for both columns
 *     and rows.
 *     Mouse functions: left button on arrow moves one item. 
 *                      Right button on arrow pages up/down or pans left/right.
 *                      Both buttons on arrow moves to top/bottom
 *                      Click on data area moves to that item.
 *                      Double click on item allows edit if programmer desires.
 *     This system was written with reentrancy in mind but it has not been
 *          test for such.
 *
 *     This browse system will handle calling functions set by SET KEY in the
 *     normal way. Two return values (defined in mbrowse.ch) are significant.
 *     REFRESH_CURRENT will cause browse:RefreshCurrent() to be called while
 *     REFRESH_ALL will cause browse:RefreshAll() to be called after your
 *     function is complete.
 *
 *     The information stored about the browse is stored in browse:cargo as an
 *     array. The file mbrowse.ch contains the defines which allow access to
 *     this information including the while code block.
 *
*****************************************************************************
******
*
* BRHotSpot()
*
* This function allows the caller to define a location on the screen which
* if clicked on with the mouse will cause an action to take place.
*
* Calling parameters:
*                     nTopRow - the top row of the area 
*                     nLeftCol - the left column of the area
*                     nBotRow - the bottom row of the area
*                     nRightCol - the right column of the area 
*                     bAction - Code block which will be executed when
*                              mouse is clicked in the area
*                     nButton - Optional button number for action to occur. IF
*                              equal to 0 or NIL, the action occurs on 
*                              clicking anybutton (the code block can decide 
*                              what to do with based upon the button). If equal
*                              to 1, code block executes only on left click,
*                              if equal to 2 only on right click and if equal
*                              to 4(?) then the middle button.
*                     nSleep - Optional value of a minimum time (in seconds) to
*                              wait between servicing multiple button presses. 
*                              Prevents routine from operating too quickly and 
*                              reading the press of a button multiple times 
*                              when not intended. If =NIL then the default value
*                              is used (see MDefaultSleep()).
*                      lRelease - Optional Logical Value. If set to .T. the
*                              servicing routine will pause after the completion
*                              of bAction for the release of the mouse button(s)
*                              Useful for guaranteeing no multiple hits on
*                              an area. If =NIL then the default is used (see
*                              MDefaultRelease())
*
* Returns: nId which is an ID to be used to remove the area with a call
*              to RDRemHotSpot(nId)
*
* Note: The code block bAction is called with four arguments:
*
*                 nButNum: the number of the button pressed with
*                          1=left, 2=right, 4=middle(?).
*                 nRow: The row that the mouse cursor was in when it
*                       was clicked
*                 nCol: The column that the mouse cursor was in when it
*                       was clicked
*                 nTime: The time returned by SECOND() shortly after the
*                       button was clicked.
*                 oBrowse: The current browse object
*
*        Thus the code block should have a form similar to the following
*        if one wishes to use the button/cursor information:
*
*        {|nButNum, nRow, nCol, nTime, oBrowse|;
*			 MyFunc(NButNum,nRow,nCol,nTime,oBrowse)}

*****************************************************************************
******
*
* BRRemHotSpot()
*
* This subroutine clears the specified Hotspot 
*
* Calling Parameters : nID - which is the ID number of the region to remove
*                            from active duty. It is given by AddHotSpot.
*
* Returns: NIL
*

*****************************************************************************
******
*
* BRCoolSpot()
*
* This subroutine deactivates the specified HotSpot without deleting it
*
*
* Calling Parameters : nID - which is the ID number of the region to remove
*                            from active duty. It is given by AddHotSpot.
*
* Returns: NIL
*

*****************************************************************************
******
*
* BRWarmSpot()
*
* This subroutine reactivates the specified HotSpot which was deactivated
* by RDCoolSpot
*
* Calling Parameters : nID - which is the ID number of the region to return 
*                            to active duty. It is given by AddHotSpot.
*
* Returns: NIL
*

*****************************************************************************
*****
*
*  BRDefSleep()
*
* This routine sets the default minimum time between servicing the mouse
* button when on a "hot spot". Useful for slowing things down.
*
* Calling Paramters: nSleepDef - Number of second for the default interval
*                                If absent no change made.
*
* Returns: current setting
*
* Note: the default time within the system is 0.2 seconds
*

*****************************************************************************
*****
*
*  BRDefRelease()
*
* This routine sets the default logical value controling if the program
* waits for the release of the mouse button before continuing when it is in
* a "Hot Spot"
*
*
* Calling Paramters: lReleaseDef - If present will set the default action
*                                  on releasing the mouse button before
*                                  continuing when servicing a "Hot Spot"
*                                  If absent no change made. .T. causes
*                                  a wait for release.
*
* Returns: current setting
*
*
* Note: The default release action is not to require a release (= .F.)
*
*

*****************************************************************************
*****
*
* BRIgnoreMouse()
*
* Purpose: force the routines to ignore the mouse and perform the overhead
*          necessary for mouse support
*
* Calling Parameters: lIgnore - logical for ignoring mouse .T. = act as if
*                               mouse is not present. If absent just returns
*                               current setting
*
* Returns: setting in effect prior to call
*

*****************************************************************************
*****
*
* ActiveBrowse()
*
* Purpose: returns the currently active browse
*
* Calling parameters: none
*
* Returns: the currently active browse object
*

*****************************************************************************
*****
*
* BrDblClk
*
* Purpose: set the mouse double click interval
*
* Calling parameters: nDblTime - the maximum time for a double click to take
*
* Returns: The current setting of the double click time
*

