REQUEST

REQUEST

Declare a module request list

Syntax

      REQUEST <name1> [,<nameN>]

Arguments

<idModule list> is the list of modules that will be linked into the current executable (.EXE) file.

Description

REQUEST is a declaration statement that defines a list of module identifiers to the linker. Like all other declaration statements, a REQUEST statement must be specified before any executable statements in either the program file, or a procedure or user-defined function definition.

During the compilation of Clipper source code, all explicit references to procedures and user-defined functions are made to the linker. In some instances, within a source file, there may be no references made to procedure or user-defined function names until runtime. REQUEST resolves this situation by forcing the named procedures or user-defined functions to be linked even if they are not explicitly referenced in the source file. This is important in several instances:

. Procedures, user-defined functions, or formats referenced with macro expressions or variables

. Procedures and user-defined functions used in REPORT and LABEL FORMs and not referenced in the source code

. User-defined functions used in index keys and not referenced in the source code

. ACHOICE(), DBEDIT(), or MEMOEDIT() user functions

. Initialization procedures declared with the INIT PROCEDURE statement

. Exit procedures declared with the EXIT PROCEDURE statement

To group common REQUESTs together, place them in a header file and then include (#include) the header file into each program file (.prg) that might indirectly use them.

Examples

      .  This example shows a typical header file consisting of common
         REQUESTs for REPORT FORMs:
      // Request.ch

      REQUEST HARDCR
      REQUEST TONE
      REQUEST MEMOTRAN
      REQUEST STRTRAN

      OR :

      REQUEST HARDCR, TONE, MEMOTRAN, STRTRAN

Seealso

ACHOICE(), ANNOUNCE, DBEDIT(), EXIT, PROCEDURE; EXTERNAL*

dbEdit()

Template

Function

Name

dbEdit()*

Category

API

Subcategory

User interface

Oneliner

Browse records in a table

Syntax

      dbEdit( [<nTop>],  [<nLeft>],  [<nBottom>],  [<nRight>],;  
              [<acColumns>], [<xUserFunc>], [<xColumnSayPictures>],;  
              [<xColumnHeaders>], [<xHeadingSeparators>], ; 
              [<xColumnSeparators>], [<xFootingSeparators>],;  
              [<xColumnFootings>] ) --> lOk

Arguments

<nTop> coordinate for top row display. <nTop> could range from 0 to MaxRow(), default is 0.

<nLeft> coordinate for left column display. <nLeft> could range from 0 to MaxCol(), default is 0.

<nBottom> coordinate for bottom row display. <nBottom> could range rom 0 to MaxRow(), default is MaxRow().

<nRight> coordinate for right column display. <nRight> could range from 0 to MaxCol(), default is MaxCol().

<acColumns> is an array of character expressions that contain database fields names or expressions to display in each column. If not specified, the default is to display all fields from the database in the current work area.

<xUserFunc> is a name of a user defined function or a code block
that would be called every time unrecognized key is been pressed or
when there are no keys waiting to be processed and dbEdit() goes
into idle mode. If <xUserFunc> is a character string, it must
contain root name of a valid user define function without
parentheses. Both the user define function or the code block should
accept two parameters: nMode, nCurrentColumn. Both should return
a numeric value that correspond to one of the expected return codes
(see table below for a list of nMode and return codes).

<xColumnSayPictures> is an optional picture. If <xColumnSayPictures>
is a character string, all columns would used this value as a
picture string. If <xColumnSayPictures> is an array, each element
should be a character string that correspond to a picture string
for the column with the same index. Look at the help for @…SAY
to get more information about picture values.

<xColumnHeaders> contain the header titles for each column, if this
is a character string, all columns would have that same header, if
this is an array, each element is a character string that contain
the header title for one column. Header may be split to more than
one line by placing semicolon (;) in places where you want to break
line. If omitted, the default value for each column header is taken
from <acColumns> or field name if <acColumns> was not specified.

<xHeadingSeparators> is an array that contain characters that draw
the lines separating the headers and the fields data. Instead of an
array you can use a character string that would be used to display
the same line for all fields. Default value is a double line.

<xColumnSeparators> is an array that contain characters that draw
the lines separating displayed columns. Instead of an array you can
use a character string that would be used to display the same line
for all fields. Default value is a single line.

<xFootingSeparators> is an array that contain characters that draw
the lines separating the fields data area and the footing area.
Instead of an array you can use a character string that would be
used to display the same line for all footers. Default is to have to
no footing separators.

<xColumnFootings> contain the footing to be displayed at the bottom
of each column, if this is a character string, all columns would
have that same footer, if this is an array, each element is a
character string that contain the footer for one column. Footer may
be split to more than one line by placing semicolon (;) in places
where you want to break line. If omitted, no footer are displayed.

Returns

dbEdit() return .F. if there is no database in use or if the number
of columns to display is zero, else dbEdit() return .T.

Description

dbEdit() display and edit records from one or more work areas in
a grid on screen. Each column is defined by element from <acColumns>
and is the equivalent of one field. Each row is equivalent of one
database record.

      Following are active keys that handled by dbEdit():       
      ---------------------------------------------------

      Key              Meaning

      Left             Move one column to the left (previous field)
      Right            Move one column to the right (next field)
      Up               Move up one row (previous record)
      Down             Move down one row (next record)
      Page-Up          Move to the previous screen
      Page-Down        Move to the next screen
      Ctrl Page-Up     Move to the top of the file
      Ctrl Page-Down   Move to the end of the file
      Home             Move to the leftmost visible column
      End              Move to the rightmost visible column
      Ctrl Left        Pan one column to the left
      Ctrl Right       Pan one column to the right
      Ctrl Home        Move to the leftmost column
      Ctrl End         Move to the rightmost column

When <xUserFunc> is omitted, two more keys are active:

      Key              Meaning

      Esc              Terminate Browse()
      Enter            Terminate Browse()

When dbEdit() execute <xUserFunc> it pass the following arguments:
nMode and the index of current record in <acColumns>. If <acColumns>
is omitted, the index number is the FIELD() number of the open
database structure.

      dbEdit() nMode could be one of the following:    
      ---------------------------------------------
      dbedit.ch      Meaning

      DE_IDLE        dbEdit() is idle,  all movement keys have been handled.
      DE_HITTOP      Attempt to cursor past top of file.
      DE_HITBOTTOM   Attempt to cursor past bottom of file.
      DE_EMPTY       No records in work area,  database is empty.
      DE_EXCEPT      Key exception.

The user define function or code block must return a value that tell
dbEdit() what to do next.

      
      User function return codes:    
      ---------------------------   

      dbedit.ch    Value   Meaning

      DE_ABORT     0       Abort dbEdit().
      DE_CONT      1       Continue dbEdit() as is.
      DE_REFRESH   2       Force reread/redisplay of all data rows.

The user function is called once in each of the following cases:
– The database is empty.
– The user try to move past top of file or past bottom file.
– Key exception, the uses had pressed a key that is not handled by dbEdit().
– The keyboard buffer is empty or a screen refresh had just occurred
dbEdit() is a compatibility function, it is superseded by the
TBrowse class and there for not recommended for new applications.

Examples

      // Browse a file using default values
      USE Test
      dbEdit()

Compliance

<xUserFunc> can take a code block value, this is a Harbour
extension.

CA-Cl*pper will throw an error if there’s no database open, Harbour
would return .F.

CA-Cl*pper is buggy and will throw an error if the number of columns
is zero, Harbour would return .F.

The CA-Cl*pper 5.2 NG state that the return value is NIL, this is
wrong and should be read logical.

There is an undocumented result code (3) from the user defined
function in CA-Cl*pper (both 87 and 5.x). This is an Append Mode which:
“split the screen to allow data to be appended in windowed area”.
This mode is not supported by Harbour.

Files

Header files are dbedit.ch, inkey.ch, Library is core

Seealso

@…SAY, Browse(), TBrowse class, Transform()

Browse()

BROWSE()

Browse a database file

Syntax

      BROWSE( [<nTop>, <nLeft>, <nBottom>, <nRight>] ) --> lOk

Arguments

<nTop> coordinate for top row display.

<nLeft> coordinate for left column display.

<nBottom> coordinate for bottom row display.

<nRight> coordinate for right column display.

Returns

BROWSE() return .F. if there is no database open in this work area, else it return .T.

Description

BROWSE() is a general purpose database browser, without any thinking you can browse a file using the following keys:

       Key              Meaning
       ---------------  --------------------------------------------
       Left             Move one column to the left (previous field)
       Right            Move one column to the right (next field)
       Up               Move up one row (previous record)
       Down             Move down one row (next record)
       Page-Up          Move to the previous screen
       Page-Down        Move to the next screen
       Ctrl Page-Up     Move to the top of the file
       Ctrl Page-Down   Move to the end of the file
       Home             Move to the leftmost visible column
       End              Move to the rightmost visible column
       Ctrl Left        Pan one column to the left
       Ctrl Right       Pan one column to the right
       Ctrl Home        Move to the leftmost column
       Ctrl End         Move to the rightmost column
       Esc              Terminate BROWSE()

On top of the screen you see a status line with the following indication:

       Record ###/###   Current record number / Total number of records.
       <none>           There are no records, the file is empty.
       <new>            You are in append mode at the bottom of file.
       <Deleted>        Current record is deleted.
       <bof>            You are at the top of file.

You should pass whole four valid coordinate, if less than four parameters are passed to BROWSE() the coordinate are default to: 1, 0, MAXROW(), MAXCOL().

Examples

      // this one shows you how to browse around
      USE Around
      Browse()

Compliance

Clipper

Files

Library is rtl

Seealso

DBEDIT()*, TBrowse class

SP_FULLDIR

FULLDIR()

  Short:
  ------
  FULLDIR() Interactively navigate directories

  Returns:
  --------
  <lSelected> => Directory was selected

  Syntax:
  -------
  FULLDIR([lChange],[@cDirName])

  Description:
  ------------
  Interactively navigates directories on the current
  drive.

  Allows reading of a file ( with FILEREAD() ) in a
  directory.

  If file is DBF, does a DBEDIT browse (watch your
  memory..)

  [lChange]    True - change to selected directory
               False - don't change to selected directory
               Default is True - change

  [@cDirName] Char string passed by reference, will
  contain name of selected directory on return.

  Examples:
  ---------
   cNewDir := ""

   if FULLDIR(.F.,@cNewDir)
       SET DEFAULT TO (cNewDir)
       ?"New directory is:"
       ?Curdir()
   endif

   if FULLDIR(.t.)
       ?"New directory is:"
       ?Curdir()
   endif

  Source:
  -------
  S_FULLD.PRG

 

C5 UI – Advanced

C5 UI – Advanced

ACHOICE() :

Execute a pop-up menu

ACHOICE(<nTop>, <nLeft>, <nBottom>, <nRight>,
    <acMenuItems>,
    [<alSelectableItems> | <lSelectableItems>],
    [<cUserFunction>],
    [<nInitialItem>],
    [<nWindowRow>]) --> nPosition

BROWSE()* :

Browse records within a window

BROWSE([<nTop>], [<nLeft>],
    [<nBottom>], [<nRight>]) --> lSuccess

DBEDIT() :

Browse records in a table format

DBEDIT( [<nTop>], [<nLeft>],
    [<nBottom>], <nRight>],
    [<acColumns>],
    [<cUserFunction>],
    [<acColumnSayPictures> | <cColumnSayPicture>],
    [<acColumnHeaders> | <cColumnHeader>],
    [<acHeadingSeparators> | <cHeadingSeparator>],
    [<acColumnSeparators> | <cColumnSeparator>],
    [<acFootingSeparators> | <cFootingSeparator>],
    [<acColumnFootings> | <cColumnFooting>]) --> NIL

DISPLAY :

Display records to the console

DISPLAY <exp list>
    [TO PRINTER] [TO FILE <xcFile>]
    [<scope>] [WHILE <lCondition>]
    [FOR <lCondition>] [OFF]

LIST :

List records to the console

LIST <exp list>
    [TO PRINTER] [TO FILE <xcFile>]
    [<scope>] [WHILE <lCondition>]
    [FOR <lCondition>] [OFF]

LABEL FORM :

Display labels to the console

LABEL FORM <xcLabel>
    [TO PRINTER] [TO FILE <xcFile>] [NOCONSOLE]
    [<scope>] [WHILE <lCondition>] [FOR <lCondition>]
    [SAMPLE]

REPORT FORM :

Display a report to the console

REPORT FORM <xcReport>
    [TO PRINTER] [TO FILE <xcFile>] [NOCONSOLE]
    [<scope>] [WHILE <lCondition>] [FOR <lCondition>]
    [PLAIN | HEADING <cHeading>] [NOEJECT] [SUMMARY]

TEXT :

Display a literal block of text

TEXT [TO PRINTER] [TO FILE <xcFile>]
    <text>...
ENDTEXT

C5_DBEDIT

 DBEDIT()
 Browse records in a table layout
------------------------------------------------------------------------------
 Syntax

     DBEDIT([<nTop>], [<nLeft>],
        [<nBottom>], <nRight>],
        [<acColumns>],
        [<cUserFunction>],
        [<acColumnSayPictures> | <cColumnSayPicture>],
        [<acColumnHeaders> | <cColumnHeader>],
        [<acHeadingSeparators> | <cHeadingSeparator>],
        [<acColumnSeparators> | <cColumnSeparator>],
        [<acFootingSeparators> | <cFootingSeparator>],
        [<acColumnFootings> | <cColumnFooting>]) --> NIL

 Arguments

     <nTop>, <nLeft>, <nBottom>, and <nRight> define the
     upper-left and lower-right coordinates of the DBEDIT() window.  Row
     values can range from zero to MAXROW() and column positions can range
     from zero to MAXCOL().  If not specified, the default coordinates are 0,
     0, MAXROW(), and MAXCOL().

     <acColumns> is an array of character expressions containing database
     field names or expressions to use as column values for each row
     displayed.  If this argument is not specified, DBEDIT() displays all
     fields in the current work area as columns.

     <cUserFunction> is the name of a user-defined function that executes
     when an unrecognizable key is pressed or there are no keys pending in
     the keyboard buffer.  Specify the function name as a character
     expression without parentheses or arguments.  Note that the behavior of
     DBEDIT() is affected by the presence of this argument.  Refer to the
     discussion below for more information.

     <acColumnSayPictures> is a parallel array of picture clauses to
     format each column.  Specifying <cColumnSayPicture> instead of an array
     displays all columns with the same format.  Refer to TRANSFORM() or
     @...SAY for more information on pictures.

     <acColumnHeaders> is a parallel array of character expressions that
     define the headings for each column.  Specifying <cColumnHeader> gives
     the same heading for all columns.  To display a multi-line heading,
     embed a semicolon in the heading expression where you want the string to
     break.  If not specified, column headings are taken from the <acColumns>
     array or the field names in the current work area, if the <acColumns>
     argument is not specified.

     <acHeadingSeparators> is a parallel array of character expressions
     that define the characters used to draw horizontal lines separating
     column headings from the field display area. Specifying
     <cHeadingSeparator> instead of an array uses the same heading separator
     for all columns.  If this argument is not specified, the default
     separator is a double graphics line.

     <acColumnSeparators> is a parallel array of character expressions
     that define the characters used to draw vertical lines separating the
     columns.  Specifying <cColumnSeparator> instead of an array uses the
     same separator for all columns.  If this argument is not specified, the
     default separator is a single graphics line.

     <acFootingSeparators> is a parallel array of character expressions
     that define the characters used to draw horizontal lines separating
     column footings from the field display area.  Specifying
     <cFootingSeparator> instead of an array uses the same footing separator
     for all columns.  If this argument is not specified, there is no footing
     separator.

     <acColumnFootings> is a parallel array of character expressions that
     define footings for each column.  Specifying <cColumnFooting> instead of
     an array gives the same footing for all columns.  To display a multi-
     line footing, embed a semicolon in the footing expression where you want
     the string to break.  If this argument is not specified, there are no
     column footings.

 Returns

     DBEDIT() always returns NIL.

 Description

     DBEDIT() is a user interface and compatibility function that displays
     records from one or more work areas in a table form.  The DBEDIT()
     window display is a grid of cells divided into columns and rows.
     Columns correspond to database fields and rows correspond to database
     records.  Each column is defined by an element of the <acColumns> array.
     The display width of each column is determined by the evaluation of the
     column expression in <acColumns> array or the column picture specified
     in the <acColumnSayPictures> array.

     All cursor movement keys are handled within DBEDIT(), including Page up,
     Page down, Home, End, the four arrow keys, and all Ctrl key combinations
     that produce cursor movement.  The navigation keys that DBEDIT()
     responds to when a user function argument is not specified are listed in
     the Active Keys table below:

     DBEDIT() Active Keys
     ------------------------------------------------------------------------
     Key                 Action
     ------------------------------------------------------------------------
     Up arrow            Up one row
     Down arrow          Down one row
     Left arrow          Column left
     Right arrow         Column right
     Ctrl+Left arrow     Pan left one column
     Ctrl+Right arrow    Pan right one column
     Home                Leftmost current screen column
     End                 Rightmost current screen column
     Ctrl+Home           Leftmost column
     Ctrl+End            Rightmost column
     PgUp                Previous screen
     PgDn                Next screen
     Ctrl+PgUp           First row of current column
     Ctrl+PgDn           Last row of current column
     Return              Terminate DBEDIT()
     Esc                 Terminate DBEDIT()
     ------------------------------------------------------------------------

     When the user function argument (<cUserFunction>) is specified, all keys
     indicated in the Active Keys table are active with the exception of Esc
     and Return.  When DBEDIT() calls the user function, it automatically
     passes two arguments:

     .  The current mode passed as a numeric value

     .  The index of the current column in <acColumns> passed as a
        numeric value

     The mode parameter indicates the current state of DBEDIT() depending on
     the last key executed.  The possible mode values are listed in the
     DBEDIT() Modes table below:

     DBEDIT() Modes
     ------------------------------------------------------------------------
     Status  Dbedit.ch      Description
     ------------------------------------------------------------------------
     0       DE_IDLE        Idle, any cursor movement keystrokes have been
                            handled and no keystrokes are pending
     1       DE_HITTOP      Attempt to cursor past top of file
     2       DE_HITBOTTOM   Attempt to cursor past bottom of file
     3       DE_EMPTY       No records in work area
     4       DE_EXCEPT      Key exception
     ------------------------------------------------------------------------

     The index parameter points to the position of the current column
     definition in the <acColumns> array.  If <acColumns> is not specified,
     the index parameter points to the position of the field in the current
     database structure.  Access the field name using FIELD().

     A user-defined function must return a value that indicates to DBEDIT()
     the action to perform.  The User Function Return Values table below
     lists the possible return values and the corresponding actions:

     DBEDIT() User Function Return Values
     ------------------------------------------------------------------------
     Value   Dbedit.ch      Description
     ------------------------------------------------------------------------
     0       DE_ABORT       Abort DBEDIT()
     1       DE_CONT        Continue DBEDIT()
     2       DE_REFRESH     Force reread/repaint and continue; after repaint,
                            process keys and go to idle
     ------------------------------------------------------------------------

     A number of instances affect calls to the user function:

     .  A key exception occurs.  This happens when DBEDIT() fetches a
        keystroke that it does not recognize from the keyboard.  Any pending
        keys remain in the keyboard buffer until fetched within the user
        function or until DBEDIT() continues.

     .  DBEDIT() enters the idle mode (i.e., all pending keys have
        been processed).  This happens when the keyboard is empty or after a
        screen refresh.  In this instance, there is one call to the user
        function and then DBEDIT() waits for a key.

     .  Beginning or end of file is encountered.  This is the same as
        idle.  All executable keys are performed, and there is one call to
        the user function with the appropriate status message.

     Note that when DBEDIT() is first executed, all keys pending in the
     keyboard buffer are executed and then DBEDIT() enters the idle mode with
     a user function call.  If no keys are pending, the idle mode is
     immediate.

     The user function should handle all modes and status messages received
     from DBEDIT().

     A user-defined function must ensure that the DBEDIT() status is
     equivalent to DE_EXCEPT (4); otherwise, the value of LASTKEY() is
     meaningless and a Return value of DE_REFRESH (2) will place the
     application into an endless loop.  For example:

     FUNCTION DBEditFunc ( nMode, nColumnPos )
        LOCAL RetVal := DE_CONT

        IF ( nMode == DE_EXCEPT )
           IF ( LASTKEY() == K_F5 )
                    RetVal := DE_REFRESH
           ENDIF
        ENDIF
        RETURN(  RetVal )

     DBEDIT() is fully re-entrant, which means you can make nested calls to
     it.  Using this feature, you can have multiple browse windows on the
     screen at the same time.

     DBEDIT() is a compatibility function and, therefore, no longer
     recommended as a programmable browse facility.  As such, it is
     superseded by the TBrowse object class.  For more information, refer to
     TBrowse class in this chapter.

 Examples

     .  This example demonstrates a generic call to DBEDIT():

        USE Names NEW
        DBEDIT()

     .  This example demonstrates calling DBEDIT() with a user
        function:

        #include "dbedit.ch"
        #include "inkey.ch"

        // Array must be visible to other user-defined programs in
        // program

        STATIC acColumns := {}

        PROCEDURE Main()

           USE Names NEW
           INDEX ON Names->Lastname + Names->FirstName TO Names

           CLS

           acColumns := { "LastName", "FirstName" }

           DBEDIT( 5, 5, 20, 70, acColumns, "UserFunc" )

        RETURN

        FUNCTION UserFunc( nMode, nCol )
           LOCAL nKey := LASTKEY()
           LOCAL nRetVal := DE_CONT         // Default return value

           DO CASE
           CASE nMode == DE_IDLE
              nRetVal := IdleFunc()
           CASE nMode == DE_HITTOP
              TONE( 100, 3 )
           CASE nMode == DE_HITBOTTOM
              TONE( 100, 3 )
              nRetVal := AppendFunc( nKey )
           CASE nMode == DE_EMPTY
              nRetVal := EmptyFunc()
           CASE nMode == DE_EXCEPT
              nRetVal := ExceptFunc( nKey, nCol )
           OTHERWISE
              TONE( 100, 3 )
           ENDCASE

        RETURN nRetVal

        FUNCTION AppendFunc( nKey )
           LOCAL nRetVal := DE_CONT         // Default return value

           IF nKey == K_DOWN                  // If DOWN ARROW
              APPEND BLANK                  // Append blank record
        // Note: The appended record will appear at the top of the
        //       DBEDIT() screen when the database file is indexed.

              nRetVal := DE_REFRESH         // Refresh screen
           ENDIF

        RETURN nRetVal

        FUNCTION ExceptFunc( nKey, nCol )
           LOCAL nRetVal := DE_CONT         // Default return value

           DO CASE
           CASE nKey == K_ESC                  // If ESCAPE
              nRetVal := DE_ABORT               // Exit
           CASE nKey == K_RETURN               // If RETURN
              nRetVal := EditFunc( nCol )      // Function to edit
                                               // field

           // Toggle DELETED status
           CASE nKey == K_DEL .AND. LASTREC() != 0  // DELETE pressed
              IF DELETED()
                 RECALL
              ELSE
                 DELETE
              ENDIF
           OTHERWISE
                 TONE( 100, 1 )
           ENDCASE

        RETURN nRetVal

        FUNCTION EditFunc( nCol )
           LOCAL cIndexVal         // Value of current key expression
           LOCAL nRetVal            // Return value
           LOCAL nField            // Position of current field
           LOCAL cFieldVal         // Value of current field
           LOCAL nCursSave         // Preserve state of cursor

           // This will return an error if no index is open
           cIndexVal := &( INDEXKEY(0) )

           nField := FIELDPOS( acColumns[nCol] )

           IF nField != 0
              nCursSave := SETCURSOR()         // Save state of cursor
              SETCURSOR(1)                     // Change cursor shape
              cFieldVal := FIELDGET( nField )         // Save contents
                                                     // of field
              @ ROW(), COL() GET cFieldVal            // GET new value
              READ
              FIELDPUT( nField, cFieldVal )            // REPLACE with
                                                     // new value
              SETCURSOR( nCursSave )                  // Restore cursor
                                                     // shape
           ENDIF

           IF cIndexVal != &( INDEXKEY(0) )         // If key expression

                                                  // changed
              nRequest := DE_REFRESH               // Refresh screen
           ELSE                                    // Otherwise
              nRequest := DE_CONT                  // Continue
           ENDIF

        RETURN nRequest

        FUNCTION IdleFunc()
           // Idle routine
        RETURN DE_CONT

        FUNCTION EmptyFunc()
           // Empty Records routine
        RETURN DE_CONT

 Files   Library is EXTEND.LIB, header files are Dbedit.ch and Inkey.ch.

See Also: @…GET ACHOICE() BROWSE()* MEMOEDIT() READ

 

TBrowse & TBColumn basics

TBrowse and TBColumn basics (.pdf)

Working version of sample program in this article.

 

TBr_Bass

The Clipper conversion process

The Clipper conversion process (.pdf)

Array and GET tricks

Array and GET tricks