Harbour All Functions – S

SaveToken

SayScreen

Seconds
Secs

Select

Set

SetAtLike

SetDate

SetKey

SetMode

SetPrec

SetTime

SetTypeahead

Sign

Sin

SinH

Space

Sqrt

Str

StrDiff

StrFormat

StrSwap
StrTran
StrZero
SubStr

C5 UI Global Settings

C5 User Interface Global Settings :

SET CENTURY :

Modify the date format to include or omit century digits

SET CENTURY on | OFF | <xlToggle>

SET COLOR* :

Define screen colors

SET COLOR | COLOUR TO [[<standard>]
    [,<enhanced>] [,<border>] [,<background>]
    [,<unselected>]] | (<cColorString>)

SET CONFIRM :

Toggle required exit key to terminate GETs

SET CONFIRM on | OFF | <xlToggle>

SET CONSOLE :

Toggle console display to the screen

SET CONSOLE ON | off | <xlToggle>

SET CURSOR :

Toggle the screen cursor on or off

SET CURSOR ON | off | <xlToggle>

SET DATE :

Set the date format for input and display

SET DATE FORMAT [TO] <cDateFormat>
SET DATE [TO] AMERICAN | ansi | british | french
    | german | italian | japan | usa

SET DECIMALS :

Set the number of decimal places displayed

SET DECIMALS TO [<nDecimals>]

SET DELIMITERS :

Toggle or define GET delimiters

SET DELIMITERS on | OFF | <xlToggle>
SET DELIMITERS TO [<cDelimiters> | DEFAULT]

SET DEVICE :

Direct @…SAYs to the screen or printer

SET DEVICE TO SCREEN | printer

SET EPOCH :

Control the interpretation of dates with no century digits

SET EPOCH TO <nYear>

SET FIXED :

Toggle fixing of the number of decimal digits displayed

SET FIXED on | OFF | <xlToggle>

SETBLINK() :

Toggle asterisk (*) interpretation in SET COLOR

SETBLINK([<lToggle>]) --> lCurrentSetting

SETCOLOR() :

Return the current colors and optionally set new colors

SETCOLOR([<cColorString>]) --> cColorString

SETCURSOR() :

Set the cursor shape

SETCURSOR([<nCursorShape>]) --> nCurrentSetting

SETMODE() :

Change display mode to specified number of rows and columns

SETMODE(<nRows>, <nCols>) --> lSuccess

SETPOS() :

Move the cursor to a new position

SETPOS(<nRow>, <nCol>) --> NIL

C5_SETMODE

 SETMODE()
 Change display mode to a specified number of rows and columns
------------------------------------------------------------------------------
 Syntax

     SETMODE(<nRows>, <nCols>) --> lSuccess

 Arguments

     <nRows> is the number of rows in the desired display mode.

     <nCols> is the number of columns in the desired display mode.

 Returns

     SETMODE() returns true (.T.) if the mode change was successful;
     otherwise, it returns false (.F.).

 Description

     SETMODE() is an environment function that attempts to change the mode of
     the display hardware to match the number of rows and columns specified.
     The change in screen size is reflected in the values returned by
     MAXROW() and MAXCOL().

     Note:  In LLG_VIDEO_TXT mode, and when a VESA driver is present, it
     is possible to use the following values :
     25,80 | 43,80 | 50,80 | 60,80 | 25,132 | 43,132 | 50,132 | 60,132

 Examples

     .  This example switches to a 43-line display mode:

        IF SETMODE(43, 80)
           ? "43-line mode successfully set"
        ELSE
           ? "43-line mode not available"
        ENDIF

     .  This example switches the video mode to regular text mode with
        60 rows and 132 columns:

        // Switch to text mode
        SET VIDEOMODE( LLG_VIDEO_TXT )
        // Set the video mode to the largest number of characters
        SETMODE( 60,132 )

 Files   Library is CLIPPER.LIB.

See Also: SET VIDEOMODE

 

Quick Start to Migration

Chapter I – Text to text conversion

In Clipper world, “migration” means “convert a DOS based Clipper program to Windows”. This is a dream of every Clipper – DOS programmer.

 Before all, we need clarify some terms:

May be found multiple ways for convert a DOS based Clipper program to Windows. In general, DOS programs are runs in “text” mode and Windows program runs in “Graphic” mode; and this is what meant by term “migration”.

Converting a text mode program to directly GUI (Graphical User Interface) is a painful job. First, we need to find a Compiler with GUI support, or a GUI library usable with a specific compiler. If we have more than one opportunity ( yes, it is so ) we need make a choice between them.

For make a right selection we need learn, understand specialties of each option and differences between them.

Believe me, this is an endless way 😦

Instead, let’s begin with simpler thing: convert a DOS text mode program to Windows text mode program.

Question: Without GUI, what meaning will be to migrate from DOS to Windows?

Answer: Good question and like all good question, answer isn’t easy.

First, modern OSs moves away day to day from DOS conditions; memory problems, screen problems, codepage problems, etc… By the time, building / running 16 bit executable becomes more difficult day to day.

Whereas Harbour already is a 32 / 64 bit compiler.

Second, all DOS Compilers for Clipper are commercial and registration required products; furthermore they are almost out of sold for this days; what compiler you could use?

And third, Harbour is best free compiler and the best way to use a free GUI tool for xBase language.

So, beginning with using Harbour in text mode is the best start point, I think.

First step is downloading and install HMG or Harbour. If you didn’t past this step yet please refer previous articles in this section or “Links” page of this blog.

The easiest way for using Harbour compiler is calling hbmk2, the wonderful project maker for Harbour compiler.

Depending your installation, hbmk2 may be in different locations; such as C:\Harbour\bin or c:\hmg\harbour\bin or anything else.

Hereafter I will assume that your hbmk2 is in C:\hmg\Harbour\bin. If your installation is different, please modify above examples.

Second step is assign an empty folder (directory) for work / test affairs; say C:\test.

And the third step is copying your Clipper program(s) to this folder.

But don’t rush; we have some precautions:

– Better way is starting with a single-program project; if you haven’t written a new one. Don’t uses for now projects have multiple program file.

 – Your program may have some “national” characters and these characters may be differently shown between DOS and Windows. If so, you may want fix manually these differences via a Windows based text editor. Or use a program if you have one. Harbour has a clever tool (HB_OEMTOANSI() function) is usable for this purpose.

 – In Clipper it’s possible a program file without module (procedure / function) definition. If you have such file(s), enclose your code with PROCEDURE — RETURN statement pair.

– Every Harbour project must have one and only one MAIN module (procedure / function). The first procedure / function in your single program file will be considered as MAIN module of your project. (In HMG, name of this module must be “main” also).

– Almost all Clipper commands, statement, functions, pseudo functions, manifest constants etc are usable almost in the same ways with Clipper. May be exist some very few and very rare differences, and of course solving methods for its.

For compile process we will use command box (DOS / console window) of Windows. You can open a console window, with the menu Start -> Run -> cmd or selecting it in the “Command Prompt” from the Start Menu \ All Programs.

 – “Command / console window” size may not appropriate for easy use. You may

      – use a MODE ( DOS ) command :

         MODE CON LINES=54 COLS=148

       or

   – adding a SetMode() statement at the beginning of MAIN module of your project. For example:

       SetMode( 25,  80 )  // 25 line 80 column same as standard 
                           // DOS screen ( but not full screen ! )
       SetMode( 48, 128 )  // 48 line 128 column, may be more readable

Now, we are ready to begin: Enter this command in console window :

 C:\hmg\harbour\bin hbmk2 <mainPrgName>

You don’t need any SET command (such as PATH etc) before this command; hbmk2 will find all necessary paths / files.

For running executable after compile, add a -run switch to the command line :

 C:\hmg\harbour\bin hbmk2 <mainPrgName> -run

Of course, you need supply name of your main .prg file in place of <mainPrgName>.

Note that you don’t need a separate “linking” step; hbmk2 will do everything for you.

You may use this

 C:\hmg\harbour\bin hbmk2 <mainPrgName>

command via a batch ( .bat ) command file (such as “build.bat”) too. In this way you can apply compiling process without console window; run .bat file by double click in the Windows Explorer. In this case you may need add a PAUSE command at end of .bat file.

That’s all.

You know, a program file may contains more than one module (procedure / function). So you may develop your project by adding new modules to your single program file.

In this step you don’t need trying extra features, extensions of Harbour. Before that adventure your primary need is to convert existing project Clipper to Harbour.

When you reach a level of multiple-program file project:

– Basic rules are the same: the first module in the your program file is MAIN module of your project.

If your .prg files contains:

  SET PROCEDURE TO <procedure_File_Name>

 and / or

   #include <procedure_File_Name>

 you may or may not continue using these statement.

 – The shortest way for compiling a multiple-file project is use a .hbp ( Harbour Projet ) file. This is a text file and its simplest form is a file contains list of your .prg files. For example:

myprog01.prg
myprog02.prg
myprog03.prg
myprog04.prg

and the compile command is the same :

  C:\hmg\harbour\bin hbmk2 <mainProjectFileName>

In this case you don’t need to use SET PROC… and #include … statement and this is the better way.

Because hbmk2 applies “incremental” compiling, that is compiles only modified files.

Under normal circumstances, any module in any program file is callable in anywhere in the project. If you have some modules that exclusive to this program file, you may use STATIC keyword at the beginning of PROCEDURE / FUNCTION statement. For example:

STATIC FUNCTION OpenTable()

With this syntax you will prevent calling this module outside of this .prg file and the possibility of using this module name into other .prg files.

Example :

Take “A typical Harbour Program” in the “Harbour Sample” page.

As seen at .pdf file by given link, this sample program borrowed from official reference guide of a Clipper compiler. That is, in fact this is a Clipper program and it will may compile with Harbour and run without any modification.

Let’s try.

– Copy and paste this sample and save in your PC with a name say “typical.prg”.

– Comment out the line for now.

 #include "Database.prg" // Contains generic database functions

– Call hbmk2:

 C:\hmg\harbour\bin hbmk2 typical -run

 Note: While working / playing on programs, you may encounter some error messages like:

  Error F0029  Can't open #include file xxx
  Error E0002  Redefinition of procedure or function xxx
  Error: Referenced, missing, but unknown function(s): xxx
  undefined reference to HB_FUN_xxx

 Please don’t panic !

    “Error” is salt and pepper of programming play ! 😉

 The worst situation isn’t getting error, but is unable to stay !

   The “HB_FUN_xxx” may be seen weird at first meet. The “HB_FUN_” is a prefix given by system ( compiler ) to your function; so you need search erroneous point into tour program files without this prefix.

Now, let’s continue to our “typical” program:

If you compile the program with commented out #include … line, possibly it will work, by opening main menu:

Typical_1

But what’s that?

When selected a menu item (except “Quit”) we can’t see other than an empty screen!

Again, don’t panic!

This situation too is not very rare !

If you use vertical scroll bar of command / console window, you will notice that your screen is considerably much longer than seen !

To avoid this conflict, ( as stated above ) we need use a SetMode() function call at top of our Main() procedure ( but AFTER LOCAL statement ! ) :

  SetMode( 24, 79 )

 And now everything is OK.

Typical_2

In fact, not really everything, we have a few “fine adjustment”.

Cut and paste the section after “// Database.prg” to a separate “Database.prg” file, un-comment the “#include …” line and then re-compile.

In this case we have a “multiple prg” project. As stated earlier, better way is using a .hbp file instead of “#include …” statements.

Now comment out ( or delete now ) the #include line.

Build a new text file with name “typical.hbp” and with this content :

Typical.prg
DataBase.prg

And recall hbmk2 without any modification :

C:\hmg\harbour\bin hbmk2 typical -run

That’s all !

Congratulations !

Now you have a multiple-prg project  !

Parsing Text – Tokens

/*
From Harbour changelog (at 2007-04-04 10:35 UTC+0200 By Przemyslaw Czerpak )
Added set of functions to manipulate string tokens:
HB_TOKENCOUNT( <cString>, [ <cDelim> ], [ <lSkipStrings> ],
 [ <lDoubleQuoteOnly> ] ) -> <nTokens>

 HB_TOKENGET( <cString>, <nToken>, [ <cDelim> ], [ <lSkipStrings> ],
 [ <lDoubleQuoteOnly> ] ) -> <cToken>

 HB_TOKENPTR( <cString>, @<nSkip>, [ <cDelim> ], [ <lSkipStrings> ],
 [ <lDoubleQuoteOnly> ] ) -> <cToken>

 HB_ATOKENS( <cString>, [ <cDelim> ], [ <lSkipStrings> ],
 [ <lDoubleQuoteOnly> ] ) -> <aTokens>

 All these functions use the same method of tokenization. They can
 accept as delimiters string longer then one character. By default
 they are using " " as delimiter. " " delimiter has special mening

 Unlike other delimiters repeted ' ' characters does not create empty
 tokens, f.e.: 

 HB_ATOKENS( " 1 2 3 " ) returns array:
 { "1", "2", "3" }

 Any other delimiters are restrictly counted, f.e.:

 HB_ATOKENS( ",,1,,2,") returns array:
 { "", "", "1", "", "2", "" }
And a strong suggession made at 2009-12-09 21:25 UTC+0100 ( By Przemyslaw Czerpak )
I strongly suggest to use hb_aTokens() and hb_token*() functions.
 They have more options and for really large data many times
 (even hundreds times) faster.

*/
#define CRLF HB_OsNewLine()
PROCEDURE Main()
LOCAL cTextFName := "Shakespeare.txt",;
 c1Line 

 SET COLO TO "W/B"
 SetMode( 40, 120 )

 CLS

 HB_MEMOWRIT( cTextFName,;
 "When in eternal lines to time thou grow'st," + CRLF + ;
 "So long as men can breathe, or eyes can see," + CRLF + ;
 "So long lives this, and this gives life to thee." )

 aLines := HB_ATOKENS( MEMOREAD( cTextFName ), CRLF )

 ?
 ? "Text file line by line :"
 ?
 AEVAL( aLines, { | c1Line | QOUT( c1Line ) } )
 ?
 WAIT "Press a key for parsing as words"
 CLS
 ?
 ? "Text file word by word :"
 ?
 FOR EACH c1Line IN aLines
 a1Line := HB_ATOKENS( c1Line ) 
 AEVAL( a1Line, { | c1Word | QOUT( c1Word ) } )
 NEXT 
 ?
 WAIT "Press a key for parsing directly as words"
 CLS
 ?
 ? "Text file directly word by word :"
 ?
 aWords := HB_ATOKENS( MEMOREAD( cTextFName ) )
 AEVAL( aWords, { | c1Word | QOUT( c1Word ) } ) 

 ?
 @ MAXROW(), 0
 WAIT "EOF TP_Token.prg" 

RETURN // TP_Token.Main()
 TP_Token

Parsing Text – FParse()

/*
FParse()
Parses a delimited text file and loads it into an array.
Syntax :
FParse( <cFileName>, <cDelimiter> ) --> aTextArray
Arguments :
<cFileName> : This is a character string holding the name of the text file to load 
 into an array. It must include path and file extension. 
 If the path is omitted from <cFileName>, 
 the file is searched in the current directory. 

 <cDelimiter> : This is a single character used to parse a single line of text. 
 It defaults to the comma.
Return :
The function returns a two dimensional array, or an empty array when the file 
cannot be opened. 

Description :

 Function FParse() reads a delimited text file and parses each line 
 of the file at <cDelimiter>. The result of line parsing is stored in an array.
This array, again, is collected in the returned array, 
 making it a two dimensional array
FParse() is mainly designed to read the comma-separated values (or CSV) file format, 
 were fields are separated with commas and records with new-line character(s). 

Library is : xHb 

*/
#define CRLF HB_OsNewLine()
PROCEDURE Main()
LOCAL cTextFName := "Shakespeare.txt",;
      a1Line 

 SET COLO TO "W/B"
 SetMode( 40, 120 )

 CLS

 HB_MEMOWRIT( cTextFName,;
              "When in eternal lines to time thou grow'st," + CRLF + ;
              "So long as men can breathe, or eyes can see," + CRLF + ;
              "So long lives this, and this gives life to thee." )

 aLines := FParse( cTextFName, " " )

 ?
 ? "Text file word by word :"
 ?
 FOR EACH a1Line IN aLines
    AEVAL( a1Line, { | c1Word | QOUT( c1Word ) } )
 NEXT 
 ?
 @ MAXROW(), 0
 WAIT "EOF TP_FParse.prg" 

RETURN // TP_FParse.Main()

TP_FParse

Nested Hashes

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.
/*
 Since a <Value> of a hash's pair may be in any scalar or complex type,
 a hash may be nested by assigning an another hash to a hash <Value>. 

*/
PROCEDURE Main()
   SET COLO TO "W/B"
   SetMode( 50, 120 )

   CLS

   hSouth := { 'Argentina' => "Buenos Aires",;
               'Brasil'    => "Brasilia",;
               'Chile'     => "Santiago" }

   hNorth:= { 'USA'    => "Washington DC",;
              'Canada' => "Ottawa",; 
              'Mexico' => "Mexico City" } 

   * a hash contains two hashes :

   hAmerica := { "America" => { "North" => hNorth,;
                                "South" => hSouth } } 

   * Standart array indexing syntax :

   ? hAmerica[ "America", "North", "USA" ] // Washington DC

   * Alternate syntax to indexing :

   ? hAmerica[ "America"][ "South" ][ "Chile" ] // Santiago

   ?
   @ MAXROW(), 0
   WAIT "EOF HashNest.prg"

RETURN // HashNest.Main()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

Hash vs Table

Consider a table for customers records with two character fields : Customer ID and customer name:

Cust_ID Cust_Name
CC001 Pierce Firth
CC002 Stellan Taylor
CC003 Chris Cherry
CC004 Amanda Baranski

 It’s known all possible and necessary operations on a table: APPEND, DELETE, SEEK and so on; by the way, for SEEK we need an index file also.

Listing this table is quite simple:

USE CUSTOMER
WHILE .NOT. EOF()
   ? CUST_ID, CUST_NAME
   DBSKIP()
ENDDO

 If our table is sufficiently small, we can find a customer record without index and SEEK :

LOCATE FOR CUST_ID = “CC003”
? CUST_ID, CUST_NAME

If we want all our data will stand in memory and we could manage it more simple and quick way, we would use an array ( with some considerations about size of table; if it is too big, this method will be problematic ) :

aCustomer := {}    // Declare / define an empty array
USE CUSTOMER
WHILE .NOT. EOF()
   AADD(aCustomer, { CUST_ID, CUST_NAME } )
   DBSKIP()
ENDDO
Traversing this array is quite simple :

FOR nRecord := 1 TO LEN( aCustomer )

    ? aCustomer[ nRecord, 1 ], aCustomer[ nRecord, 2 ]
NEXT
or :

a1Record := {}

FOR EACH a1Record IN aCustomer
   ? a1Record[ 1 ], a1Record[ 2 ]
NEXT

And locating a specific record too:

nRecord := ASCAN( aCustomer, { | a1Record | a1Record[ 1 ] == “CC003” } )

? aCustomer[ nRecord, 1 ], aCustomer[ nRecord, 2 ]

A lot of array functions are ready to use for maintain this array : ADEL(), AADD(), AINS() etc …

Now, let’s see how we could use a hash for achieve this job :

hCustomer := { => } // Declare / define an empty hash

USE CUSTOMER
WHILE .NOT. EOF()
   hCustomer[ CUST_ID ] := CUST_NAME
   DBSKIP()
ENDDO
Let’s traversing :

h1Record := NIL

FOR EACH h1Record IN hCustomer
   ? h1Record: __ENUMKEY(),h1Record:__ENUMVALUE()
NEXT

Now, we have a bit complicate our job; a few field addition to the table :

No: Field Name Type Width  Dec Decription

1

 CUST_ID

C

 5

0

Id ( Code )

2

 CUST_NAME

C

10

0

Name

3

 CUST_SNAM

C

10

0

Surname

4

 CUST_FDAT

D

 8

0

First date

5

 CUST_ACTV

L

 1

0

Is active ?

6

 CUST_BLNCE

N

11

2

Balance

 While <key> part of an element of a hash may be C / D / N / L type; <xValue> part of hash too may be ANY type of data, exactly same as arrays.

So, we can make fields values other than first ( ID) elements of an array:

hCustomer := { => } // Declare / define an empty hash
USE CUSTOMER
WHILE .NOT. EOF()
   a1Data:= { CUST_NAME, CUST_SNAM, CUST_FDAT, CUST_ACTV, CUST_BLNCE }
   hCustomer[ CUST_ID ] := a1Data
   DBSKIP()
ENDDO
Let’s traversing :

h1Record := NIL

FOR EACH h1Record IN hCustomer
   a1Key  := h1Record:__ENUMKEY()
   a1Data := h1Record:__ENUMVALUE()
   ? a1Key
   AEVAL( a1Data, { | x1 | QQOUT( x1 ) } )
NEXT
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
/*
Hash vs Tables
 
*/
#define NTrim( n ) LTRIM( STR( n ) )
#define cLMarj SPACE( 3 )
PROCEDURE Main()

  SET DATE GERM
  SET CENT ON
  SET COLO TO "W/B"
  SetMode( 40, 120 )
 
  CLS
 
  hCustomers := { => } // Declare / define an empty PRIVATE hash
 
  IF MakUseTable() 
 
     Table2Hash()
 
     * Here the hash hCustomers may be altered in any way
 
     ZAP
 
     Hash2Table()
 
  ELSE
      ? "Couldn't make / USE table"
  ENDIF
 
  ?
  @ MAXROW(), 0
  WAIT "EOF HashVsTable.prg"
 
RETURN // HashVsTable.Main()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.
PROCEDURE Table2Hash()
   hCustomers := { => } 
   WHILE .NOT. EOF()
     hCustomers[ CUST_ID ] := CUST_SNAM
     DBSKIP()
   ENDDO
 
   ListHash( hCustomers, "A hash transferred from a table (single value)" )
 
   hCustomers := { => } // Declare / define an empty hash
   DBGOTOP()
   WHILE .NOT. EOF()
      hCustomers[ CUST_ID ] := { CUST_NAME, CUST_SNAM, CUST_FDAT, CUST_ACTV, CUST_BLNCE }
      DBSKIP()
   ENDDO
 
   ListHash( hCustomers, "A hash transferred from a table (multiple values)" )
 
RETURN // Table2Hash()

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

PROCEDURE Hash2Table()
   LOCAL h1Record,;
         c1Key,;
         a1Record,;
         n1Field
 
   FOR EACH h1Record IN hCustomers
      c1Key := h1Record:__ENUMKEY()
      a1Record := h1Record:__ENUMVALUE()
      DBAPPEND()
      FIELDPUT( 1, c1Key )
      AEVAL( a1Record, { | x1, n1 | FIELDPUT( n1 + 1 , x1 ) } )
   NEXT h1Record
   DBGOTOP()
 
   ?
   ? "Data trasferred from hash to table :"
   ?
   WHILE ! EOF()
      ? STR( RECN(), 5), ''
      FOR n1Field := 1 TO FCOUNT()
         ?? FIELDGET( n1Field ), ''
      NEXT n1Field
      DBSKIP()
   ENDDO 
 
RETURN // Hash2Table()

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

PROCEDURE ListHash( hHash, cComment )
 
  LOCAL x1Pair
 
  cComment := IF( HB_ISNIL( cComment ), '', cComment )
 
  ? 
  ? cComment // , "-- Type :", VALTYPE( hHash ), "size:", LEN( hHash )
  ?
  IF HB_ISHASH( hHash ) 
     FOR EACH x1Pair IN hHash
        nIndex := x1Pair:__ENUMINDEX()
        x1Key := x1Pair:__ENUMKEY()
        x1Value := x1Pair:__ENUMVALUE()
        ? cLMarj, NTrim( nIndex ) 
*       ?? '', VALTYPE( x1Pair )
        ?? '', x1Key, "=>"
*       ?? '', VALTYPE( x1Key ) 
*       ?? VALTYPE( x1Value ) 
        IF HB_ISARRAY( x1Value ) 
           AEVAL( x1Value, { | x1 | QQOUT( '', x1 ) } )
        ELSE 
           ?? '', x1Value
        ENDIF 
     NEXT
  ELSE
    ? "Data type error; Expected hash, came", VALTYPE( hHash ) 
  ENDIF HB_ISHASH( hHash )
RETURN // ListHash()
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.

FUNCTION MakUseTable() // Make / USE table
 
 LOCAL cTablName := "CUSTOMER.DBF"
 LOCAL lRetval, aStru, aData, a1Record 
 
 IF FILE( cTablName ) 
    USE (cTablName)
 ELSE
    aStru := { { "CUST_ID", "C", 5, 0 },;
               { "CUST_NAME", "C", 10, 0 },;
               { "CUST_SNAM", "C", 10, 0 },;
               { "CUST_FDAT", "D", 8, 0 },;
               { "CUST_ACTV", "L", 1, 0 },;
               { "CUST_BLNCE", "N", 11, 2 } }
    * 
    * 5-th parameter of DBCREATE() is alias - 
    * if not given then WA is open without alias 
    *                              ^^^^^^^^^^^^^ 
    DBCREATE( cTablName, aStru, , .F., "CUSTOMER" ) 
 
    aData := { { "CC001", "Pierce", "Firth", 0d20120131, .T., 150.00 },; 
               { "CC002", "Stellan", "Taylor", 0d20050505, .T., 0.15 },;
               { "CC003", "Chris", "Cherry", 0d19950302, .F., 0 },;
               { "CC004", "Amanda", "Baranski", 0d20011112, .T., 12345.00 } }
 
    FOR EACH a1Record IN aData
        CUSTOMER->(DBAPPEND())
        AEVAL( a1Record, { | x1, nI1 | FIELDPUT( nI1, X1 ) } )
    NEXT a1Record 
    DBGOTOP()
 
 ENDIF 
 
 lRetval := ( ALIAS() == "CUSTOMER" )
 
RETURN lRetval // MakUseTable()

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._
 
HashVsTable

C5 UI Commands & Functions

C5 User Interface Commands and Functions

Global Settings :

SET CENTURY :

Modify the date format to include or omit century digits

SET CENTURY on | OFF | <xlToggle>

SET COLOR* :

Define screen colors

SET COLOR | COLOUR TO [[<standard>]
    [,<enhanced>] [,<border>] [,<background>]
    [,<unselected>]] | (<cColorString>)

SET CONFIRM :

Toggle required exit key to terminate GETs

SET CONFIRM on | OFF | <xlToggle>

SET CONSOLE :

Toggle console display to the screen

SET CONSOLE ON | off | <xlToggle>

SET CURSOR :

Toggle the screen cursor on or off

SET CURSOR ON | off | <xlToggle>

SET DATE :

Set the date format for input and display

SET DATE FORMAT [TO] <cDateFormat>
SET DATE [TO] AMERICAN | ansi | british | french
    | german | italian | japan | usa

SET DECIMALS :

Set the number of decimal places displayed

SET DECIMALS TO [<nDecimals>]

SET DELIMITERS :

Toggle or define GET delimiters

SET DELIMITERS on | OFF | <xlToggle>
SET DELIMITERS TO [<cDelimiters> | DEFAULT]

SET DEVICE :

Direct @…SAYs to the screen or printer

SET DEVICE TO SCREEN | printer

SET EPOCH :

Control the interpretation of dates with no century digits

SET EPOCH TO <nYear>

SET FIXED :

Toggle fixing of the number of decimal digits displayed

SET FIXED on | OFF | <xlToggle>

SETBLINK() :

Toggle asterisk (*) interpretation in SET COLOR

SETBLINK([<lToggle>]) --> lCurrentSetting

SETCOLOR() :

Return the current colors and optionally set new colors

SETCOLOR([<cColorString>]) --> cColorString

SETCURSOR() :

Set the cursor shape

SETCURSOR([<nCursorShape>]) --> nCurrentSetting

SETMODE() :

Change display mode to specified number of rows and columns

SETMODE(<nRows>, <nCols>) --> lSuccess

SETPOS() :

Move the cursor to a new position

SETPOS(<nRow>, <nCol>) --> NIL

User Input :

CLEAR TYPEAHEAD :

Empty the keyboard buffer

CLEAR TYPEAHEAD

INKEY() :

Extract a character from the keyboard buffer

 
INKEY( [ <nSeconds> ] ) --> nInkeyCode

KEYBOARD :

Stuff a string into the keyboard buffer

KEYBOARD <cString>

LASTKEY() :

Return the INKEY() value of the last key in the buffer

LASTKEY() --> nInkeyCode

NEXTKEY() :

Read the pending key in the keyboard buffer

NEXTKEY() --> nInkeyCode

SET TYPEAHEAD :

Set the size of the keyboard buffer

SET TYPEAHEAD TO <nKeyboardSize>

Basic :

?/?? :

Display one or more values to the console

? | ?? [<exp list>]

@…BOX :

Draw a box on the screen

@ <nTop>, <nLeft>, <nBottom>, <nRight>
  BOX <cBoxString> [COLOR <cColorString>]

@…CLEAR :

Clear a rectangular region of the screen

@ <nTop>, <nLeft> [CLEAR
  [TO <nBottom>, <nRight>]]

@…SAY :

Display data at a specified screen or printer row and column

@ <nRow>, <nCol>
    [SAY <exp>
    [PICTURE <cSayPicture>]
    [COLOR <cColorString>]]
    GET <idVar>
    [PICTURE <cGetPicture>]
    [COLOR <cColorString>]
    [WHEN <lPreExpression>]
    [RANGE* <dnLower>, <dnUpper>] |
    [VALID <lPostExpression>]

@…TO :

Draw a single or double line box

@ <nTop>, <nLeft>
    TO <nBottom>, <nRight> [DOUBLE] [COLOR
    <cColorString>]

ACCEPT :

Place keyboard input into a memory variable

ACCEPT [<expPrompt>] TO <idVar>

INPUT :

Enter the result of an expression into a variable

INPUT [<expPrompt>] TO <idVar>

 

ALERT() :

Display a simple modal dialog box

ALERT( <cMessage>, [<aOptions>] ) --> nChoice

CLEAR SCREEN :

Clear the screen and home the cursor

CLEAR [SCREEN] | CLS

COL() :

Return the screen cursor column position

COL() --> nCol

COLORSELECT()

                Activate attribute in current color settings

   COLORSELECT(<nColorIndex>) --> NIL

DEVOUT() :

Write a value to the current device

DEVOUT(<exp>, [<cColorString>]) --> NIL

DEVOUTPICT() :

Write a value to the current device using a picture clause

DEVOUTPICT(<exp>, <cPicture>, [<cColorString>]) --> NIL

           DEVPOS() :

Move the cursor or printhead to a new position depending on the

current device

        DEVPOS(<nRow>, <nCol>) --> NIL

DISPBEGIN() :

Begin buffering screen output

DISPBEGIN() --> NIL

DISPBOX() :

Display a box on the screen

DISPBOX(<nTop>, <nLeft>, <nBottom>, <nRight>,
    [<cnBoxString>], [<cColorString>]) --> NIL

DISPCOUNT() :

Return the number of pending DISPEND() requests

DISPCOUNT() --> nDispCount

DISPEND() :

Display buffered screen updates

DISPEND() --> NIL

DISPOUT() :

Write a value to the display

DISPOUT(<exp>, [<cColorString>]) --> NIL

OUTERR() :

Write a list of values to the standard error device

OUTERR(<exp list>) --> NIL

OUTSTD() :

Write a list of values to the standard output device

OUTSTD(<exp list>) --> NIL

QOUT() :

Display a list of expressions to the console

QOUT([<exp list>]) --> NIL
QQOUT([<exp list>]) --> NIL

RESTORE SCREEN* :

Display a saved screen

RESTORE SCREEN [FROM <cScreen>]

RESTSCREEN() :

Display a saved screen region to a specified location

RESTSCREEN([<nTop>], [<nLeft>],
    [<nBottom>], [<nRight>], <cScreen>) --> NIL

ROW() :

Return the screen row position of the cursor

ROW() --> nRow

SAVE SCREEN* :

Save current screen to a buffer or variable

SAVE SCREEN [TO <idVar>]

SAVESCREEN() :

Save a screen region for later display

SAVESCREEN([<nTop>], [<nLeft>],
    [<nBottom>], [<nRight>]) --> cScreen

SCROLL() :

Scroll a screen region up or down

SCROLL([<nTop>], [<nLeft>],
     [<nBottom>], [<nRight>], [<nVert>] [<nHoriz>])
 --> NIL

TYPE :

Display or print the contents of a text file

TYPE <xcFile> [TO PRINTER] [TO FILE <xcOutFile>]

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

GET System :

Standard :

@…GET :

Create a new Get object and display it on the screen

@ <nRow>, <nCol>
    [SAY <exp>
    [PICTURE <cSayPicture>]
    [COLOR <cColorString>]]
    GET <idVar>
    [PICTURE <cGetPicture>]
    [COLOR <cColorString>]
    [WHEN <lPreExpression>]
    [RANGE* <dnLower>, <dnUpper>] |
    [VALID <lPostExpression>]

CLEAR GETS :

Release Get objects from the current GetList array

CLEAR GETS

READ :

Activate full-screen editing mode using Get objects

READ [SAVE]

READEXIT() :

Toggle Uparrow and Downarrow as READ exit keys

READEXIT([<lToggle>]) --> lCurrentState

READINSERT() :

Toggle the current insert mode for READ and MEMOEDIT()

READINSERT([<lToggle>]) --> lCurrentMode

READKEY()* :

Determine what key was used to terminate a READ

READKEY() --> nReadkeyCode

READMODAL() :

Activate a full-screen editing mode for a GetList

READMODAL(<aGetList>) --> NIL

READVAR() :

Return the current GET/MENU variable name

READVAR() --> cVarName

SET ESCAPE :

Toggle Esc as a READ exit key

SET ESCAPE ON | off | <xlToggle>

SET FORMAT :

Activate a format when READ is executed

SET FORMAT TO [<idProcedure>[.<ext>]]

SET INTENSITY :

Toggle enhanced display of GETs and PROMPTs

SET INTENSITY ON | off | <xlToggle>

SET SCOREBOARD :

Toggle the message display from READ or MEMOEDIT()

SET SCOREBOARD ON | off | <xlToggle>

UPDATED() :

Determine if any GET changed during a READ

UPDATED() --> lChange

Getsys.prg Functions :

GETACTIVE() :

Return the currently active Get object

GETACTIVE() --> objGet

GETAPPLYKEY() :

Apply a key to a Get object from within a Get reader

GETAPPLYKEY(<oGet>, <nKey>) --> NIL

GETDOSETKEY() :

Process SET KEY during Get editing

GETDOSETKEY(<oGet>) --> NIL

GETPOSTVALIDATE() :

Postvalidate the current Get object

GETPOSTVALIDATE(<oGet>) --> lSuccess

GETPREVALIDATE() :

Prevalidate a Get object

GETPREVALIDATE(<oGet>) --> lSuccess

GETREADER() :

Execute standard READ behavior for a Get object

GETREADER(<oGet>) --> NIL

READFORMAT() :

Return, and optionally set, the format file code block

READFORMAT([<bFormat>]) --> bCurrentFormat

READKILL() :

Return, and optionally set, the READ terminate flag

READKILL([<lKillRead>]) --> lCurrentSetting

READUPDATED() :

Return, and optionally set, whether a Get changed

READUPDATED([<lChanged>]) --> lCurrentSetting

GET Class :

Class Function :

GetNew() : Create a new Get object

Exported Instance Variables :

badDate : Indicates if the editing buffer contains an invalid date
block : Code block to associate Get with a variable
buffer : Character value that defines the editing buffer
cargo : User-definable variable
changed : Indicates whether the Get:buffer has changed
clear : Indicates whether the editing buffer should be cleared
col : Get column number
colorSpec : Display attributes string
decPos : Decimal point position within the editing buffer
exitState : Means by which the user exited the Get
hasFocus : Indicates whether or not the Get object has input focus
minus : Indicates whether or not a minus sign has been entered
name : Get variable name
original : Character string containing the original value of the Get
picture : PICTURE string
pos : Current cursor position within the editing buffer
postBlock : Code block to validate a newly entered value
preBlock : Code block to decide if editing is permitted
reader : Contains a block to affect READ behavior on a Get object
rejected : Indicates if last insert/overStrike character was rejected
row : Get row number
subscript : Information about array Get objects .
type : Get variable data type
typeOut : Indicates attempt to move the cursor out of editing buffer

Exported Methods :

State Change Methods :

assign() : Assigns the editing buffer contents to the Get variable
colorDisp() : Changes a Get object’s color and then redisplay it
display() : Displays the Get on the screen .
killFocus() : Takes input focus away from the Get object
reset() : Resets the internal state information of the Get
setFocus() : Gives input focus to the Get object
undo() : Sets the Get variable back to Get:original
unTransform() : Converts character value to its original data type
updateBuffer() : Updates the editing buffer and redisplays the Get
varGet() : Returns the current value of the Get variable
varPut() : Sets the Get variable to the passed value

Cursor Movement Methods :

end() : Moves the cursor to the rightmost position
home() : Moves the cursor to the leftmost position
left() : Moves the cursor left one character
right() : Moves the cursor right one character
toDecPos() : Moves the cursor to the immediate right of Get:decPos
wordLeft() : Moves the cursor left one word
wordRight() : Moves the cursor right one word

Editing Methods :

backspace() : Moves the cursor to the left and deletes one character
delete() : Deletes the character under the cursor
delEnd() : Deletes from current cursor position to the end of the Get
delLeft() : Deletes the character to the left of the cursor
delRight() : Deletes the character to the right of the cursor
delWordLeft() : Deletes the word to the left of the cursor
delWordRight() : Deletes the word to the right of the cursor

Text Entry Methods :

insert() : Inserts characters into the editing buffer
overStrike() : Overwrites characters in the editing buffer

Menu System :

@…PROMPT :

Paint a menu item and define a message

@ <nRow>, <nCol> PROMPT <cMenuItem>
     [MESSAGE <cExpression>]

MENU TO :

Execute a lightbar menu for defined PROMPTs

     MENU TO <idVar>

          MENUMODAL :

Activate a top bar menu

         MENUMODAL(<oTopBar>, <nSelection>, <nMsgRow>,
             <nMsgLeft>, <nMsgRight>, <cMsgColor>) --> MenuID

SET MESSAGE :

Set the @…PROMPT message line row

SET MESSAGE TO [<nRow> [CENTER | CENTRE]]

SET INTENSITY :

Toggle enhanced display of GETs and PROMPTs

SET INTENSITY ON | off | <xlToggle>

SET WRAP :

Toggle wrapping of the highlights in MENUs

SET WRAP on | OFF | <xlToggle>

Browse Classes :

TBrowse :

Provides objects for browsing table-oriented data.

Description :

A TBrowse object is a general purpose browsing mechanism for table-oriented data. TBrowse objects provide a sophisticated architecture for acquiring, formatting, and displaying data. Data retrieval and file positioning are performed via user-supplied code blocks, allowing a high degree of flexibility and interaction between the browsing mechanism and the underlying data source. The format of individual data items can be precisely controlled via the TBColumn data retrieval code blocks; overall display formatting and attributes can be controlled by sending appropriate messages to the TBrowse object.

A TBrowse object relies on one or more TBColumn objects. A TBColumn object contains the information necessary to define a single column of the browse table (see TBColumn class in this chapter).

During operation, a TBrowse object retrieves data by evaluating code blocks. The data is organized into rows and columns and displayed within a specified rectangular region of the screen. The TBrowse object maintains an internal browse cursor. The data item on which the browse cursor rests is displayed in a highlighted color. (The actual screen cursor is also positioned to the first character of this data item.)

Initially, the browse cursor is placed on the data item at the top left of the browse display. Messages can then be sent to the TBrowse object to navigate the displayed data, causing the browse cursor to move. These messages are normally sent in response to user keystrokes.

New data is automatically retrieved as required by navigation requests. When navigation proceeds past the edge of the visible rectangle, rows or columns beyond that edge are automatically brought into view. When new rows are brought into view, the underlying data source is repositioned by evaluating a code block.

Note: TBrowse objects do not clear the entire window before output during redisplay operations. Part of the window may still be cleared when data from the existing display is scrolled.

Class Functions :

TBrowseNew() :

Create a new TBrowse object

 TBrowseNew(<nTop>, <nLeft>, <nBottom>, <nRight>)
 --> objTBrowse

Returns a new TBrowse object with the specified coordinate settings. The TBrowse object is created with no columns and no code blocks for data positioning. These must be provided before the TBrowse object can be used.

TBrowseDB() :

Create a new TBrowse object for browsing a database file

 TBrowseDB(<nTop>, <nLeft>, <nBottom>, <nRight>)
 --> objTBrowse

Returns a new TBrowse object with the specified coordinate settings and default code blocks for data source positioning within database files. The default code blocks execute the GO TOP, GO BOTTOM, and SKIP operations.

Note that TBrowseDB() creates an object with no column objects. To make the TBrowse object usable, you must add a column for each field to be displayed

Exported Instance Variables:

autoLite : Logical value to control highlighting
cargo : User-definable variable
colCount : Number of browse columns
colorSpec : Color table for the TBrowse display
colPos : Current cursor column position
colSep : Column separator character
footSep : Footing separator character
freeze : Number of columns to freeze
goBottomBlock : Code block executed by TBrowse:goBottom()
goTopBlock : Code block executed by TBrowse:goTop()
headSep : Heading separator character
hitBottom : Indicates the end of available data
hitTop : Indicates the beginning of available data
leftVisible : Indicates position of leftmost unfrozen column in display
nBottom : Bottom row number for the TBrowse display
nLeft : Leftmost column for the TBrowse display
nRight : Rightmost column for the TBrowse display
nTop : Top row number for the TBrowse display
rightVisible : Indicates position of rightmost unfrozen column in display
rowCount : Number of visible data rows in the TBrowse display
rowPos : Current cursor row position
skipBlock : Code block used to reposition data source
stable : Indicates if the TBrowse object is stable

Exported Methods:

Cursor Movement Methods :

down() : Moves the cursor down one row
end() : Moves the cursor to the rightmost visible data column
goBottom() : Repositions the data source to the bottom of file
goTop() : Repositions the data source to the top of file
home() : Moves the cursor to the leftmost visible data column
left() : Moves the cursor left one column
pageDown() : Repositions the data source downward
pageUp() : Repositions the data source upward
panEnd() : Moves the cursor to the rightmost data column
panHome() : Moves the cursor to the leftmost visible data column
panLeft() : Pans left without changing the cursor position
panRight() : Pans right without changing the cursor position
right() : Moves the cursor right one column
up() : Moves the cursor up one row

Miscellaneous Methods :

addColumn() : Adds a TBColumn object to the TBrowse object
colorRect() : Alters the color of a rectangular group of cells
colWidth() : Returns the display width of a particular column
configure() : Reconfigures the internal settings of the TBrowse object
deHilite() : Dehighlights the current cell
delColumn() : Delete a column object from a browse
forceStable() : Performs a full stabilization .
getColumn() : Gets a specific TBColumn object
hilite() : Highlights the current cell
insColumn() : Insert a column object in a browse
invalidate() : Forces redraw during next stabilization
refreshAll() : Causes all data to be refreshed during the next stabilize
refreshCurrent() : Causes the current row to be refreshed on next stabilize
setColumn() : Replaces one TBColumn object with another
stabilize() : Performs incremental stabilization

TBColumn :

Provides the column objects TBrowse objects.

Description :

A TBColumn object is a simple object containing the information needed to fully define one data column of a TBrowse object (see the TBrowse reference in this chapter). TBColumn objects have no methods, only exported instance variables.

Class Function :

TBColumnNew() :

Create a new TBColumn object.

TBColumnNew(<cHeading>, <bBlock>) --> objTBColumn

Exported Instance Variables :

block : Code block to retrieve data for the column
cargo : User-definable variable
colorBlock : Code block that determines color of data items
colSep : Column separator character
defColor : Array of numeric indexes into the color table
footing : Column footing
footSep : Footing separator character
heading : Column heading
headSep : Heading separator character
width : Column display width

Example :

This example is a code fragment that creates a TBrowse object and adds some TBColumn objects to it:

USE Customer NEW
//
// Create a new TBrowse object
objBrowse := TBrowseDB(1, 1, 23, 79)
//
// Create some new TBColumn objects and
// add them to the TBrowse object
objBrowse:addColumn(TBColumnNew( "Customer", ;
                    {|| Customer->Name} ))
objBrowse:addColumn(TBColumnNew( "Address", ;
                    {|| Customer->Address} ))
objBrowse:addColumn(TBColumnNew( "City", ;
                    {|| Customer->City} ))
.
. <statements to actually browse the data>
.
CLOSE Customer

For a simple and working sample look at here.