Converting Clipper Applications
CONVERTING @..SAYS...Converting @SAYS from text to GUI
CONVERTING ACHOICE().Converting ACHOICE() from text to GUI
CONVERTING DBEDIT()..Converting DBEDIT() from text to GUI
CONVERTING GETS......Converting @SAY..GETS from text to GUI
CONVERTING MEMOEDIT()Converting MEMOEDIT() from text to GUI
CONVERTING MENUS.....Converting @PROMPT menus from text to GUI
CONVERTING PRINT.....Converting Printer @..SAYS
CONVERTING @..SAYS
Converting @SAYS from text to GUI
Description:
Working with @..SAYS is not as straight forward as working
with AChoice() or Dbedit() because @..SAYS usually used in
Clipper applications for a variety of screen functions. For
example, it is not possible to create a GUI conversion
system that can convert any text-based screen to a GUI-based
dialogue because the conversion program simply cannot
decipher the "intent" of the programmer by looking at either
the code or the screen.
It is possible, however, to break down some @SAY scenarios
into pieces that can be managed with Dual-Mode functions.
Let's look at the following Clipper @SAY scenario:
We want to display a series of messages on the screen as
we are progressing through the updating of a database
structure. The original Clipper source code might look
something like this:
cScreen := SaveScreen( 3,5,16,50 )
@ 3,5 TO 16,50
@ 4,6 CLEAR TO 15,49
@ 6,10 SAY 'Creating Backup..'
Backup()
@ 8,10 SAY 'Copying Structure ..'
CopyStru()
@ 10,10 SAY 'Modifying Structure..'
ModiStru()
@ 12,10 SAY 'Appending from Backup..'
AppendBack()
@ 14,10 SAY 'Done! Press any key to continue'
Inkey(0)
RestScreen( 3,5,14,75,cScreen)
The above code can be easily modified to Dual-Mode because
the @..SAYS are organized in a "window" area of the screen
and because they are simply status messages which are
displayed during the progress of the program. To accomplish
this task we use the Dual-Mode functions: DC_EXPL(), DC_IMPL()
DC_SAY(), and DC_CLS() or the Dual-Mode commands: DCEXPLODE,
DCIMPLODE, @..SAYWIN, and CLS (contained in EXPRESS.CH).
The command DCEXPLODE paints a box on the screen and returns an
array of information about this new "object" which includes saved
screen contents (in TEXT mode) or a reference to a Dialog object
(in GUI mode). @..SAYWIN is translated in EXPRESS.CH to paint the
says on the parent object. If no parent object is included in
the @..SAYWIN command, then the last object created by DCEXPLODE
is assumed to be the parent, otherwise the full screen is used as
the parent. If the GUI flag is on, then the screen object will
be a GUI dialog box. The command DCIMPLODE is used to restore
the screen area (in Text mode) or destroy the parent dialogue
object (in GUI mode).
/* ----------------------- */
#include "express.ch"
FUNCTION XTest()
LOCAL aScreen
GUI ON // turn on GUI mode
@ 3, 5, 16, 50 DCEXPLODE aScreen FONT "10.courier"
@ 2,5 SAYWIN 'Creating Backup..'
DC_Pause(1)
Backup()
@ 4,5 SAYWIN 'Copying Structure ..'
DC_Pause(1)
CopyStru()
@ 6,5 SAYWIN 'Modifying Structure..'
DC_Pause(1)
ModiStru()
@ 8,5 SAYWIN 'Appending from Backup..'
DC_Pause(1)
AppendBack()
DCIMPLODE aScreen
RETURN nil
/* ----------------------- */
NOTE: These examples are here for general guidelines on how
to approach the conversion of existing code to GUI, not
because it is a recommended method of application conversion.
After studying the examples in \express\sample by running
XDEMO.EXE, it will become more clear on how to approach the
conversion of your application to GUI. EXPRESS.CH is a very
simplistic approach to understanding how the preprocessor can
be used in a variety of ways to create a command interface to
Xbase++ parts which resembles or closely emulates Clipper text-
based syntax. The recommended approach to converting Clipper
applications is to study DCDIALOG.CH and the DC* commands.
This is a much more robust command set specifically designed to
produce a full GUI application that is easy to write and
maintain.
Source/Library:
EXPRESS.CH, DCDIALOG.CH
See Also:
dc_expl()
dc_say()
CONVERTING ACHOICE()
Converting ACHOICE() from text to GUI
Description:
Let's look at the following Clipper ACHOICE() scenario:
We have a field list which we want to browse on the screen
by first saving a screen area, painting a box on the screen,
creating an ACHOICE() pick-list in the the boxed area,
choosing a field, then restoring the original screen. The
original code might look something like this:
FUNCTION Xtest()
LOCAL cScreen, aFields, nField
cScreen := SaveScreen( 3,5,20,25 )
@ 3,5 TO 20,25
@ 4,6 CLEAR TO 19,24
USE COLLECT
aFields := Array(Fcount())
AFields(aFields)
nField := AChoice( 4, 6, 19, 24, aFields )
RestScreen( 3,5,14,25,cScreen)
RETURN nField
/* --------------------- */
Now we want to change the code to display the same Browse in
a Windows GUI dialogue box:
FUNCTION Xtest()
#include "express.ch"
LOCAL aFields, nField
* cScreen := SaveScreen( 3,5,20,25 ) // this isn't needed
* @ 3,5 TO 20,25 // this isn't needed
* @ 4,6 CLEAR TO 13,74 // this isn't needed
GUI ON // turn on GUI mode
USE COLLECT
aFields := Array(Fcount())
AFields(aFields)
nField := AChoice( 4, 6, 19, 24, aFields )
* RestScreen( 3,5,14,25,cScreen) // this isn't needed
RETURN nField
/* -------------- */
That's all there is to it. "express.ch" translates the
ACHOICE() function to call DC_ACHOICE() which is a Dual-Mode
function.
Source/Library:
EXPRESS.CH
See Also:
dc_achoice()
CONVERTING DBEDIT()
Converting DBEDIT() from text to GUI
Description:
Let's look at the following Clipper DBEDIT() scenario:
We have a database which we want to browse on the screen by
first saving a screen area, painting a box on the screen,
creating an DBEDIT() browse in the the boxed area, choosing
a field, then restoring the original screen. The original
code might look something like this:
FUNCTION Xtest1()
LOCAL cScreen
cScreen := SaveScreen( 3,5,20,75 )
@ 3,5 TO 20,75
@ 4,6 CLEAR TO 19,74
USE COLLECT VIA DBFNTX
DbEdit( 4, 6, 19, 74 )
RestScreen( 3,5,14,75,cScreen)
RETURN nil
/* ---------------------- */
Now we want to change the code to display the same Browse in
a Windows GUI dialogue box:
FUNCTION XTest2()
#include "express.ch"
* cScreen := SaveScreen( 3,5,20,75 ) // this isn't needed
* @ 3,5 TO 20,75 // this isn't needed
* @ 4,6 CLEAR TO 13,74 // this isn't needed
GUI ON // turn on GUI mode
USE COLLECT VIA DBFNTX
DbEdit( 4, 6, 19, 74 )
* RestScreen( 3,5,14,75,cScreen) // this isn't needed
RETURN nil
/* ------------------ */
That's all there is to it. "express.ch" translates the
DBEDIT() function to call DC_DBEDIT() which is a Dual-Mode
function.
Source/Library:
EXPRESS.CH
See Also:
dc_dbedit()
CONVERTING GETS
Converting @SAY..GETS from text to GUI
Description:
Let's look at the following Clipper @SAY..GET scenario:
We have a table of GETS which we want to display on the screen
by first saving a screen area, painting a box on the screen,
writing all the GETS in the boxed area, editing the gets,
then restoring the original screen. The code might look
something like this:
FUNCTION Xtest1()
LOCAL GetList, cLastName := Space(15), cFirstName := Space(15), ;
cCompany := Space(30), cStreet := Space(30), cCity := Space(25), ;
cState := Space(10), cCountry := Space(20), cPhone := Space(12), ;
cScreen
cScreen := SaveScreen( 3,5,14,75 )
@ 4,6 CLEAR TO 13,74
GetList := {}
@ 5,10 SAY ' Last Name' GET cLastName VALID !Empty(cLastName)
@ 5,40 SAY 'First Name' GET cFirstName VALID !Empty(cFirstName)
@ 7,10 SAY ' Company' GET cCompany
@ 8,10 SAY ' Street' GET cStreet VALID !Empty(cStreet)
@ 9,10 SAY ' City' GET cCity VALID !Empty(cCity)
@10,10 SAY ' State' GET cState PICT '@!' ;
VALID StateValid(cState)
@10,40 SAY ' Country' GET cCountry PICT '@!' ;
VALID CountryValid(cCountry)
@12,40 SAY ' Phone' GET cPhone PICT '(999)-999-9999'
READ
RestScreen( 3,5,14,75,cScreen)
RETURN nil
/* ------------------------- */
STATIC FUNCTION StateValid(cState)
IF !( ' ' + Alltrim(cState) $ ' AL AK AZ CA MI MN NY NSW ' )
DC_MsgBox('State must be AK, AK, AZ, CA, MI, MN, NY or NSW')
RETURN .f.
ENDIF
RETURN .t.
/* ------------------------- */
STATIC FUNCTION CountryValid(cCountry)
IF !( ' ' + Alltrim(cCountry) $ ' USA CANADA GERMANY ' )
DC_MsgBox('Country must be USA, CANADA or GERMANY')
RETURN .f.
ENDIF
RETURN .t.
/* -------------------------- */
Now we want to change the code to display the same GETS in a
Windows GUI dialogue box:
FUNCTION Xtest2()
#include "express.ch"
LOCAL GetList, cLastName := Space(15), cFirstName := Space(15), ;
cCompany := Space(30), cStreet := Space(30), cCity := Space(25), ;
cState := Space(10), cCountry := Space(20), cPhone := Space(12)
* cScreen := SaveScreen( 3,5,14,75 ) // this isn't needed
* @ 4,6 CLEAR TO 13,74 // this isn't needed
GUI ON // turn on GUI mode
GetList := {}
@ 5,10 SAY ' Last Name' GET cLastName VALID !Empty(cLastName)
@ 5,40 SAY 'First Name' GET cFirstName VALID !Empty(cFirstName)
@ 7,10 SAY ' Company' GET cCompany
@ 8,10 SAY ' Street' GET cStreet VALID !Empty(cStreet)
@ 9,10 SAY ' City' GET cCity VALID !Empty(cCity)
@10,10 SAY ' State' GET cState PICT '@!' ;
VALID StateValid(cState)
@10,40 SAY ' Country' GET cCountry PICT '@!' ;
VALID CountryValid(cCountry)
@12,40 SAY ' Phone' GET cPhone PICT '(999)-999-9999'
READ
* RestScreen( 3,5,14,75,cScreen) // this isn't needed
RETURN nil
That's all there is to it. "express.ch" translates the
@SAY..GETS so that they add a new kind of object to the Getlist
array and it translates the READ command so it passes the
Getlist array to DC_ReadGui() instead of ReadModal().
NOTE: These examples are here for general guidelines on how
to approach the conversion of existing code to GUI, not
because it is a recommended method of application conversion.
After studying the examples in \express\sample by running
XDEMO.EXE, it will become more clear on how to approach the
conversion of your application to GUI. EXPRESS.CH is a very
simplistic approach to understanding how the preprocessor can
be used in a variety of ways to create a command interface to
Xbase++ parts which resembles or closely emulates Clipper text-
based syntax. The recommended approach to converting Clipper
applications is to study DCDIALOG.CH and the DC* commands.
This is a much more robust command set specifically designed to
produce a full GUI application that is easy to write and
maintain.
Source/Library:
DCDIALOG.CH, EXPRESS.CH
See Also:
DCREAD GUI
dc_readgui()
CONVERTING MEMOEDIT()
Converting MEMOEDIT() from text to GUI
Description:
Let's look at the following Clipper MemoEdit() scenario:
We have a memo which we want to edit on the screen
by first saving a screen area, painting a box on the screen,
running the Memo Editor, then restoring the original screen.
The code might look something like this:
FUNCTION Xtest1()
LOCAL cScreen, cMemo
cScreen := SaveScreen( 5,5,20,75 )
@ 5,5 CLEAR TO 20,75
@ 5,5 TO 20,75 DOUBLE
cMemo := MemoRead('README.TXT')
cMemo := MemoEdit(cMemo,6,6,19,74)
MemoWrit( 'README.TXT', cMemo )
RestScreen( 8,5,20,75,cScreen)
RETURN cMemo
/* ----------------------- */
Now we want to change the code to display the memo editor
in a Windows GUI dialogue box:
FUNCTION Xtest2()
#include "express.ch"
LOCAL cScreen, cMemo
GUI ON // turn on GUI mode
* cScreen := SaveScreen( 5,5,20,75 ) this is not needed
* @ 5,5 CLEAR TO 20,75 this is not needed
* @ 5,5 TO 20,75 DOUBLE this is not needed
cMemo := MemoRead('README.TXT')
cMemo := MemoEdit(cMemo,6,6,19,74)
MemoWrit( 'README.TXT', cMemo )
* RestScreen( 8,5,20,75,cScreen) this is not needed
RETURN cMemo
/* ------------------ */
That's all there is to it. "express.ch" translates the
MEMOEDIT() function to call DC_MEMOEDIT() which is a Dual-Mode
function.
Source/Library:
EXPRESS.CH
See Also:
dc_memoedit()
CONVERTING MENUS
Converting @PROMPT menus from text to GUI
Description:
Let's look at the following Clipper Menu scenario:
We have a menu which we want to display on the screen
by first saving a screen area, painting a box on the screen,
writing all the menu PROMPTS in the boxed area, running the
menu, then restoring the original screen. The code might look
something like this:
FUNCTION Xtest1()
LOCAL cScreen, PromptList, nChoice
cScreen := SaveScreen( 8,5,16,75 )
@ 8,5 CLEAR TO 16,75
@ 8,5 TO 15,75 DOUBLE
PromptList := {}
SET WRAP ON
SET MESSAGE TO 16 CENTER
@ 10,10 PROMPT ' Enter New Names ' ;
MESSAGE 'Add new names and addresses'
@ 11,10 PROMPT ' Print List ' ;
MESSAGE 'Print the Customer List'
@ 12,10 PROMPT ' Dial ' ;
MESSAGE 'Dial customers starting at top of list'
@ 13,10 PROMPT ' Import ' ;
MESSAGE 'Import a new Customer List'
@ 10,40 PROMPT ' Manager ' ;
MESSAGE 'Run the Database Manager'
@ 11,40 PROMPT ' Calendar ' ;
MESSAGE 'Check the Appointment Schedule'
@ 12,40 PROMPT ' Purge ' ;
MESSAGE 'Remove all the dialed numbers from the list'
@ 13,40 PROMPT ' Exit ' ;
MESSAGE 'Quit the program'
MENU TO nChoice
RestScreen( 8,5,16,75,cScreen)
RETURN nChoice
/* ----------------------- */
Now we want to change the code to display the same PROMPTS
in a Windows GUI dialogue box:
FUNCTION Xtest2()
LOCAL PromptList, nChoice
#include "express.ch"
* cScreen := SaveScreen( 3,5,15,75 ) // don't need this
* @ 3,5 TO 15,75 DOUBLE // don't need this
* @ 4,6 CLEAR TO 14,74 // don't need this
PromptList := {}
SET WRAP ON
SET MESSAGE TO 16 CENTER
GUI ON // turn on GUI
@ 10,10 PROMPT ' Enter New Names ' ;
MESSAGE 'Add new names and addresses'
@ 11,10 PROMPT ' Print List ' ;
MESSAGE 'Print the Customer List'
@ 12,10 PROMPT ' Dial ' ;
MESSAGE 'Dial customers starting at top of list'
@ 13,10 PROMPT ' Import ' ;
MESSAGE 'Import a new Customer List'
@ 10,40 PROMPT ' Manager ' ;
MESSAGE 'Run the Database Manager'
@ 11,40 PROMPT ' Calendar ' ;
MESSAGE 'Check the Appointment Schedule'
@ 12,40 PROMPT ' Purge ' ;
MESSAGE 'Remove all the dialed numbers from the list'
@ 13,40 PROMPT ' Exit ' ;
MESSAGE 'Quit the program'
MENU TO nChoice
* RestScreen( 3,5,15,75,cScreen) // don't need this
RETURN nChoice
/* --------------------- */
That's all there is to it. "express.ch" translates the
@PROMPTS so that they add a new kind of object to the
PromptList array and it translates the MENU TO command so it
passes the PromptList array to DC_MenuTo() instead of _MenuTo().
NOTE: These examples are here for general guidelines on how
to approach the conversion of existing code to GUI, not
because it is a recommended method of application conversion.
After studying the examples in \express\sample by running
XDEMO.EXE, it will become more clear on how to approach the
conversion of your application to GUI. EXPRESS.CH is a very
simplistic approach to understanding how the preprocessor can
be used in a variety of ways to create a command interface to
Xbase++ parts which resembles or closely emulates Clipper text-
based syntax. The recommended approach to converting Clipper
applications is to study DCDIALOG.CH and the DC* commands.
This is a much more robust command set specifically designed to
produce a full GUI application that is easy to write and
maintain.
Source/Library:
EXPRESS.CH
See Also:
dc_menu_to()
CONVERTING PRINT
Converting Printer @..SAYS
Description:
Let's look at the following Clipper Print @..SAY scenario:
We have a columnar report in which we want to give the user
the option of choosing a printer, such as a network printer
or a fax, a starting and ending page, a font, and the number
of rows and columns on the paper.
The original Clipper report looks like this:
FUNCTION MyReport ( nCopies )
LOCAL nRow, nSaveRec, cScrn, aFor_Sale, i, nPage
SELE collect
nSaveRec := RecNo()
GO TOP
aFor_Sale := { 'No','Yes','Not Sure' }
SET DEVICE TO PRINT
cScrn := DC_WaitOn('Printing..')
FOR i := 1 TO nCopies
GO TOP
nRow := 1
nPage := 1
DO WHILE !Eof()
IF nRow = 1
@ nRow,10 SAY 'My Personal Collection Inventory'
@ nRow,50 SAY 'Page ' + Alltrim(Str(nPage))
@ nRow,70 SAY Date()
nRow += 2
@ nRow,0 SAY 'Description'
@ nRow,35 SAY 'Type'
@ nRow,50 SAY 'Sub-Type'
@ nRow,65 SAY 'Cond'
@ nRow,71 SAY 'For Sale?'
@ nRow,82 SAY 'Value'
nRow += 2
ELSE
@ nRow,0 SAY COLLECT-þ>descrip
@ nRow,35 SAY COLLECT-þ>type
@ nRow,50 SAY COLLECT-þ>sub_type
@ nRow,65 SAY COLLECT-þ>condition
@ nRow,71 SAY aFor_Sale[COLLECT-þ>for_sale+1]
@ nRow,82 SAY Str(COLLECT-þ>appr_value,7,2)
nRow++
SKIP
IF nRow þ> 60
EJECT
nRow := 1
ENDIF
ENDIF
ENDDO
NEXT
SET DEVICE TO SCREEN
DC_Impl(cScrn)
GO nSaveRec
RETURN nil
-----------------------------------------------------------
The modified report looks like this:
#include "DCPRINT.CH"
FUNCTION MyReport ( )
LOCAL nRow, nSaveRec, cScrn, aFor_Sale, oPrinter, i, nPage
SELE collect
nSaveRec := RecNo()
GO TOP
aFor_Sale := { 'No','Yes','Not Sure' }
BEGIN SEQUENCE
DCPRINT ON TO oPrinter // Pop-Up printer dialog
cScrn := DC_WaitOn('Printing..')
FOR i := 1 TO oPrinter:nCopies
GO TOP
nRow := 1
oPrinter:nPage := 1
DO WHILE !Eof()
IF nRow = 1
@ nRow,10 DCPRINT SAY 'My Personal Collection Inventory'
@ nRow,50 DCPRINT SAY 'Page ' + Alltrim(Str(oPrinter:nPage))
@ nRow,70 DCPRINT SAY Date()
nRow += 2
@ nRow,0 DCPRINT SAY 'Description'
@ nRow,35 DCPRINT SAY 'Type'
@ nRow,50 DCPRINT SAY 'Sub-Type'
@ nRow,65 DCPRINT SAY 'Cond'
@ nRow,71 DCPRINT SAY 'For Sale?'
@ nRow,82 DCPRINT SAY 'Value'
nRow += 2
ELSE
@ nRow,0 DCPRINT SAY COLLECT-þ>descrip
@ nRow,35 DCPRINT SAY COLLECT-þ>type
@ nRow,50 DCPRINT SAY COLLECT-þ>sub_type
@ nRow,65 DCPRINT SAY COLLECT-þ>condition
@ nRow,71 DCPRINT SAY aFor_Sale[COLLECT-þ>for_sale+1]
@ nRow,82 DCPRINT SAY Str(COLLECT-þ>appr_value,7,2)
nRow++
SKIP
IF nRow þ> ( oPrinter:nRows - 5 )
DCPRINT EJECT
nRow := 1
ENDIF
ENDIF
ENDDO
NEXT
END SEQUENCE
DCPRINT OFF
DC_Impl(cScrn)
GO nSaveRec
RETURN nil
The structure of the report does not change. Only the syntax
of a few commands are required to change like so:
From To
--------------------------- ------------------------------------
SET DEVICE TO PRINT DCPRINT ON [ TO <þoPrinterþ> ]
SET PRINT ON DCPRINT ON [ TO <þoPrinterþ> ]
@ <þnRowþ>,<þnColþ> SAY <þcTextþ> @ <þnRowþ>,<þnColþ> DCPRINT SAY
<þcTextþ>
EJECT DCPRINT EJECT
SET DEVICE TO SCREEN DCPRINT OFF
?/?? <þcTextþ> DCPRINT ?/?? <þcTextþ>
Additional commands can then be added to the report to
improve the output like so:
DCPRINT FONT <þcFontNameþ>
@ <þnSrowþ>,<þnSColþ>,<þnERowþ>,<þnEColþ> DCPRINT BOX <þOPTIONSþ>
@ <þnSrowþ>,<þnSColþ>,<þnERowþ>,<þnEColþ> DCPRINT BITMAP <þcBitMapþ>
Source/Library:
DCPRINT.CH