Sie sind auf Seite 1von 17

Second life for Clipper Application by Aleksandar Stefanovic

Alaska
Hybrid Start
for Clipper programmers
Clipper migration can be easy if you know
how to do that and where to start.

1
Second life for Clipper Application by Aleksandar Stefanovic

Windows applications differ from DOS applications. Windows provides more then
thousand functions you can call. There is different program structure. Remember, from
the user’s point of view program seems to run in bordered rectangle (window). When we
start application a window appears, when we close the window, the programs stops.
When the user made a click in the window, application responds. You can have more
then one window active at the same time, window can be visible or hidden by other
windows, window need procedure to repaint themselves. Remember what Microsoft want
at start. Software that is easy to use. If someone learned one windows application, he or
she can use many others. WHY? Standard GUI elements, they are already familiar with
it.

Standard windows have borders, title bar, menu bar, drop down menu, control box,
system minimize and maximize buttons, scroll bars… Windows part inside windows
borders without title bar and menu bar we call client area. There we display information
to the user. In clipper DOS application we use screen for output and we have full control
of it. On windows, this is another story. We can use something that ALASKA call
HYBRID system or we can use full GUI system. Many programmers said that it is better
to move on to full GUI. For some applications I believe that they are right but sometimes
we DON’T NEED that. I love HYBRID system. Why they call it this way? Because you
have window who act like old DOS screen, you can control it by row and colons. All
clipper display functions work well with that kind of windows. Inside them you can use
all GUI elements but… This application can look nice but remember, if you want OLD
way of application control and if you thing that your users can and will accept it just do
it. On the other hand if you have time to separate display logic from your application you
can try to migrate to full GUI application. Some accounting applications (most clipper
ones are like that) don’t need that event driven logic and you can only try to make nicer
screens. You have to decide what to do and how you will make migration to windows
world. At start HYBRID model is perfect. There is some limitation and you must live
with that.

So, for clipper programmer first thing that is how to have window for input and output
which act like old DOS screen. In description of Alaska was said that it is 100% clipper
compatible. We can use our clipper display functions and commands. Question is not
how but where. In special window that is created using Alaska XbpCrt class.

This is window just like other but we have row and column in it and we can use it in
“old” DOS way. Nice thing is that we can create it before our let say clipper application
start. For this we will place it in special Alaska procedure APPSYS. This procedure is
executed before our MAIN function. We can write it once and we can forget about it.

2
Second life for Clipper Application by Aleksandar Stefanovic

Lets write this procedure.

//////////////////////////////////////////////////////////////////////
// APPSYS.PRG
//////////////////////////////////////////////////////////////////////
#include "appevent.ch"
#include "xbp.ch"

#define DEF_ROWS 40
#define DEF_COLS 120

PROCEDURE AppSys()
LOCAL oCrt
LOCAL aSizeDesktop, aPos
local DEF_FONTHEIGHT:=16
local DEF_FONTWIDTH:=8

public maincrtobj
public maincrtwin

aSizeDesktop := AppDesktop():currentSize()
aPos := { (aSizeDesktop[1]-(DEF_COLS * DEF_FONTWIDTH)) /2, ;
(aSizeDesktop[2]-(DEF_ROWS * DEF_FONTHEIGHT)) /2 }
Do Case
Case aSizeDeskTop[1] = 1024
Def_FontHeight := 18
Def_FontWidth := 10
Endcase

oCrt := XbpCrt():New ( NIL, NIL, aPos, DEF_ROWS, DEF_COLS )


oCrt:FontWidth := DEF_FONTWIDTH
oCrt:FontHeight := DEF_FONTHEIGHT
oCrt:title := "This is Title"
oCRT:icon := ID_AS_ICON
oCrt:FontName := "Alaska Crt"
oCrt:sysmenu:=.f.
oCrt:visible:=.t.
oCrt:automark:=.f.

oCrt:Create()
oCrt:PresSpace()
SetAppWindow ( oCrt )
maincrtobj:=oCrt
maincrtwin:=oCrt:gethwnd()
RETURN

3
Second life for Clipper Application by Aleksandar Stefanovic

As you see we define numbers for rows end columns. In DOS time we have limited
number of screen sizes but in ALASKA we have bigger freedom.
We start from windows desktop. We can get information about screen size (in pixels) and
use it to place our window on it. We can use different font size but here is first limit. We
can’t use all windows fonts. Why? Simple, we need fonts where we have the same size
for all characters. We have rows and columns. We can try to use other fonts in this crt
window but we will not get error if Alaska can’t display it in crt window.

Under windows we don’t have access to hardware divices but we can use windows
functions to access them. We can get identification and sometimes we call it handlers.

AppDesktop() function will give us desktop handler. Now we can use function
currentSize() and we will get array with X and Y size of desktop. Alaska don’t have
support for calling windows functions to change screen resolution, we can only detect
current screen size and act on it. If this size is 1024 and 768 we can create “DOS”
window that has 40 rows and 120 columns with font size 18 by 10 pixels. This is nice if
we have in our old DOS clipper applications support for only 80 by 25 screen size, we
can use this extra space for something else (pictures for example) and our screen will not
be monotony black screen.

How to create this class? This way:

First we must define it.


oCrt := XbpCrt():New ( NIL, NIL, aPos, DEF_ROWS, DEF_COLS )

In variable oCrt we have handler for our new class instance.


Function that actually create our class is method function

NEW() with parameters

XbpCrt():new( [<oParent>] , [<oOwner>] , [<aPos>] , ;


[<nRowCount>], [<nColCount>], [<cTitle>], ;
[<lVisible>] ) --> oXbpCrt

We will create instance of our window. Parent and owner are something that every
window have, If we put NIL for these parameters we want DESKTOP to be parent and
no owner. When we create this class we can define rows and columns numbers, title for
this window and is it visible or not.

Now we can start with 3 files, project.xpj, appsys.prg and testcrt.prg

4
Second life for Clipper Application by Aleksandar Stefanovic

Project.xpj

[PROJECT]
COMPILE = xpp
COMPILE_FLAGS = /q /l
DEBUG = yes
GUI = yes
LINKER = alink
LINK_FLAGS =
RC_COMPILE = arc
RC_FLAGS = /v
VERSION = 2.0
TESTCRT.XPJ

[TESTCRT.XPJ]
testcrt.exe
[testcrt.exe]
testcrt.prg
appsys.prg

testcrt.obj
appsys.obj

appsys.prg

Appsys listing

//////////////////////////////////////////////////////////////////////
// APPSYS.PRG
//////////////////////////////////////////////////////////////////////
#include "xbp.ch"

PROCEDURE AppSys()

#define DEF_ROWS 40
#define DEF_COLS 120
LOCAL oCrt
LOCAL aSizeDesktop, aPos
local DEF_FONTHEIGHT:=16
local DEF_FONTWIDTH:=8
public maincrtobj
public maincrtwin
aSizeDesktop:=AppDesktop():currentSize()
Do Case
Case aSizeDeskTop[1] = 1024
Def_FontHeight := 18

5
Second life for Clipper Application by Aleksandar Stefanovic

Def_FontWidth := 10
Endcase
oCrt := XbpCrt():New ( NIL, NIL, aPos, DEF_ROWS, DEF_COLS )
oCrt:FontWidth := DEF_FONTWIDTH
oCrt:FontHeight := DEF_FONTHEIGHT
oCrt:title := "Title for our application"
oCrt:FontName := "Alaska Crt"
oCrt:Create()
oCrt:PresSpace()
SetAppWindow ( oCrt )
maincrtobj:=oCrt
maincrtwin:=oCrt:gethwnd()
RETURN

Testcrt listing:

//////////////////////////////////////////////////////////////////////
//
// TESTCRT.PRG
//
//////////////////////////////////////////////////////////////////////

#include "Appevent.ch"
#include "Xbp.ch"
PROCEDURE Main
LOCAL nEvent

DO WHILE nEvent <> xbeP_Close


nEvent := AppEvent( @mp1, @mp2, @oXbp )
oXbp:handleEvent( nEvent, mp1, mp2 )
ENDDO
RETURN

6
Second life for Clipper Application by Aleksandar Stefanovic

When we start PBUILD we will get testcrt.exe and now we have active window for our
application like old DOS one.

Let stop for a moment and learn how we can change some parameters of our window.

Properties for XbpCrt are

AlwaysOnTop, border, clipChildren, closeable, fontHeight, fontWidth, fontName,


gridMove, icon, minMax, SysMenu, takList, title, titleBar,visible, autoFocus. AutoMark,
DropFont. DropZone, helpLink, maxCol, maxRow, mouseMode, modalResult,
asyncFlush, toolTipText, useShortCuts, xSize, ySize.

Don’t be afraid of this, all of them have default values so you don’t need to change it. But
sometimes if you need or like you can set it to values you want.

Creating, reconfigurating or destroying our window with these methods.

:create( [<oParent>] , [<oOwner>] , [<aPos>] , ;


[<nRowCount>], [<nColCount>], [<cTitle>], ;
[<lVisible>] ) --> self

7
Second life for Clipper Application by Aleksandar Stefanovic

:configure( [<oParent>] , [<oOwner>] , [<aPos>] , ;


[<nRowCount>], [<nColCount>], [<cTitle>], ;
[<lVisible>] ) --> self

:destroy()

there is more methods that you can use to get some values or to change them
:currentPos()
:currentSize()
:showModal()
:getFrameState()
:getModalState()
:hide()
:isVisible()
;menuBar()
:presSpace()
:setFont()
:setCompoundname()
:setFrameState()
:setModalState()
:setPointer()
:setTrackPointer()
:setPos()
:setSize()
:setTitle()
:show()
:toBack()
:toFront()

If you want you can control and act on mouse events using code blocks. You can define
events for:
enter(), leave(), lbClick(), lbDblClick(), lbDown(), lbUp(), mbClick(), mbDblClick(),
mbDown(), mbUp(), motion(), rbClick(), rbDblClick(), rbDown(), rbUp(), wheel().

How you can use these events?

Declare function MyMouseEnter() and MyMouseLeave()

Function MyMouseEnter(aPos)
@ 1,1 say “Mouse enter”
return (NIL)

Function MyMouseLeave()
@ 1,1 say “Mouse lieve”
return (NIL)

8
Second life for Clipper Application by Aleksandar Stefanovic

We will define code blocks

Local block_me:={|s|MyMouseEnter(s)}
Local block_ml:={||MyMouseLeave()}

Now we must make connection with our window and these functions:

oCrt:enter:=block_me
oCrt:leave:=block_ml

All these function we can put in our TESTCRT.PRG.

This is only demonstration, but you have to use


Setmouse(.t.)

And

oCcrt:setTrackPointer(.t.)

In testcrt module we don’t have accees to oCrt but in APPSYS we define public variable
maincrtobj so you must use it.

New our testcrt looks like this

#include "Appevent.ch"
#include "Xbp.ch"
PROCEDURE Main
LOCAL nEvent
local block_me:={|s|MyMouseEnter(s)}
local block_ml:={||MyMouseLeave()}

maincrtobj:enter:=block_me
maincrtobj:leave:=block_ml

SetMouse(.t.)
maincrtobj:setTrackpointer(.t.)

DO WHILE nEvent <> xbeP_Close


nEvent := AppEvent( @mp1, @mp2, @oXbp )
oXbp:handleEvent( nEvent, mp1, mp2 )
ENDDO
RETURN

Function MyMouseEnter(aPos)
@ 1,1 say "Mouse Enter"

9
Second life for Clipper Application by Aleksandar Stefanovic

return (NIL)

Function MyMouseLeave()
@ 1,1 say "Mouse Leave"
return (NIL)

Sometimes these functions DON’T WORK WELL. If we start our application and mouse
is not inside our windows, when we move it Alaska trigger our functions for mouse
detection but if application start and mouse is inside, NOTHING is happened. Nobody is
perfect. Other support is much, much better.

So, let’s find out some “nice” thing that we can do with our xbpcrt window.

If we need to change window title we can use:


Maincrtobj:setTitle(“New title”)

Changing frame state with


maincrtobj:setFrameState(XBPDLG_FRAMESTAT_MINIMIZED)
maincrtobj:setFrameState(XBPDLG_FRAMESTAT_NORMALIZED)
maincrtobj:setFrameState(XBPDLG_FRAMESTAT_MAXMIZED)

Hide and show window with


maincrtobj:hide()
maincrtobj:show()

Border can be set using maincrtobj:border:= one of these constants

XBPDLG_NO_BORDER
XBPDLG_SIZEBORDER
XBPDLG_THINBORDER
XBPDLG_DLGBORDER
XBPDLG_RAISEDBORDERTHICK
XBPDLG_RAISEDBORDERTHIN
XBPDLG_RECESSEDBORDERTHICK
XBPDLG_RECESSEDBORDERTHIN
XBPDLG_RAISEDBORDERTHICK_FIXED
XBPDLG_RAISEDBORDERTHIN_FIXED
XBPDLG_RECESSEDBORDERTHICK_FIXED
XBPDLG_RECESSEDBORDERTHIN_FIXED

System menu (in left upper corner) can be disabled

maincrtobj:sysmenu:=.f.

Pushbuttons in right upper corner can be hidden with

10
Second life for Clipper Application by Aleksandar Stefanovic

Maincrtobj:closeable:=.f.
Maincrtobj:minMax:=.f.
I think that this is enough for start playing with CRT window.

On the other hand if you ever want to use other windows API functions that need window
handle you will get it using
Maincrtobj:gethwnd()

For now we have window where all text display functions work and we can add other
things that Alaska simple call xbase parts.

Now we can go on. What most window applications have? Menu. Now we will add menu
to our window.

In our application we will add:

AS_MENU( SetAppWindow():menuBar() )

We will define menu options in separate function as_menu.

function AS_MENU( oMenubar )


LOCAL oMenu

oMenu := XbpMenu():new( oMenuBar )


oMenu:title := "~System"
oMenu:create()
oMenu:itemSelected := {|nItem| MenuDispatcher( 100+nItem ) }
oMenu:addItem( {"~a) End" , NIL} )
oMenu:addItem( {NIL, NIL, XBPMENUBAR_MIS_SEPARATOR, 0} )

oMenu:addItem( {"~b) Indexing service", {||Indexserv()}} )


oMenubar:addItem( {oMenu, NIL} )

oMenu := XbpMenu():new( oMenuBar )


oMenu:title := "~AS Software"
oMenu:create()
oMenu:itemSelected := {|nItem| MenuDispatcher( 700+nItem ) }
oMenu:addItem( {"~a) About application" ,{||aboutas()}} )

oMenubar:addItem( {oMenu, NIL} )

RETURN (NIL)

Menu have to be build.

11
Second life for Clipper Application by Aleksandar Stefanovic

First we must get handler for our menu


oMenu := XbpMenu():new( oMenuBar )
Now we will add title
oMenu:title := "~System"
and we create it
oMenu:create()
and we will add items (one by one).

First we define numeric values for menu items:


oMenu:itemSelected := {|nItem| MenuDispatcher( 100+nItem ) }

Then we define text and action


oMenu:addItem( {"~a) End" , NIL} )
oMenu:addItem( {NIL, NIL, XBPMENUBAR_MIS_SEPARATOR, 0} )
oMenu:addItem( {"~b) Indexing service", {||Indexserv()}} )
oMenubar:addItem( {oMenu, NIL} )

We need this procedure to:

PROCEDURE MenuDispatcher( nSelection )


DO CASE
CASE nSelection == 101
QUIT
ENDCASE
RETURN

First option will quit our application, then we have separator, then we add code block to
start one our function. We then send this data to windows. This way we will define all
application options.
All applications have big number of parts, I believe that your clipper application is
structured this way, so it will not be a big problem to make menu options to call your
modules.

To avoid problems, fist menu start with 100+nItem, second with 200+nItem… Don’t
forget this.

12
Second life for Clipper Application by Aleksandar Stefanovic

We must have PRG file with function definition for our Indexserv() function and
aboutas()

Now we can add one more option for access our application modules. We will add
pushbutton but with pictures in it.

We need picture handle and we will load image from external file.

oBMP1:=XbpBitmap():new()
oBMP1:loadfile("pic1.jpg")
oBMP2:=XbpBitmap():new()
oBMP2:loadfile("pic2.jpg")

We will add 3 more pictures for decoration

oBMPdec0:=XbpBitmap():new()
oBMPdec0:loadfile("dec0.jpg")
oBMPdec1:=XbpBitmap():new()
oBMPdec1:loadfile("dec1.jpg")

oBMP1 and oBMP2 are handler for our pictures pushbutton (all pictures are 150x150
pixels.

First we need handler for it.

Xbpb := XbpPushButton():new()

We will create it
oXbpb:create( , , {10,450}, {150,150})
We create button 150x150 pixel at location 10,450.

We use cargo variable


oXbpb:cargo := .F
When button is activated we will start our test1 module.
oXbpb:activate := {|mp1, mp2, obj| obj:cargo := ! obj:cargo, ;
DrawBMPall( obj,oBMP2,0 ),test1() }
We have 2 pictures for our button and we want to act when mouse pointer is over it

oXbpb:paint := {|mp1, mp2, obj| DrawBMPall( obj,oBMP1,0 ) }


oXbpb:enter := {|mp1, mp2, obj| DrawBMPall( obj,oBMP1,0) }
oXbpb:leave := {|mp1, mp2, obj| DrawBMPall( obj,oBMP1,0) }
oXbpb:lbdown := {|mp1, mp2, obj| DrawBMPall( obj,oBMP2,0 ) }
oXbpb:motion := {|mp1, mp2, obj| DrawBMPall( obj,oBMP2,0 ) }
oXbpb:lbup := {|mp1, mp2, obj| DrawBMPall( obj,oBMP1,0 ) }
oXbpb:drawmode:=XBP_DRAW_OWNER

13
Second life for Clipper Application by Aleksandar Stefanovic

DrawBmpall(oXbpb,oBMP1,-1)

Function for displaying pictures inside pushbutton is

STATIC PROCEDURE DrawBMPall( oControl,oBitmap,nn)


LOCAL oPS,
oPS := oControl:lockPS()

oBitmap:draw( oPS, {0 , 0})


oControl:unlockPS( oPS )
do case
case nn==0
oBMPden0:draw(oPSg,{500,50})
case nn==1
oBMPden1:draw(oPSg,{500,50})
ENDCASE
RETURN

What we do here? First we draw inside button. For this we need to use functions lock()
and unlock() to get access to button surface. Other pictures we draw inside our main crt
window. Notice that we don’t need to lock and unlock our main window, but when we
want to draw inside xbase parts we must use it. Lock is must call to enable pushbutton
drawing but don’t forget to unlock it. It is important not only for our application,
windows OS need that.

When mouse pointer move over pushbutton, we have picture change in button and in our
main window. Nice and easy. When we click on picture we start application module.

Until now we have explain XbpCrt, XbpMenu, XbpBitmap, XbpPushButton.


Remember, when we draw inside CRT window we can erase it using standard CLS
function. All drawing will be erased but pushbutton don’t. We can hide it using hide()
method or destroy() when we don’t need it any more.

Now our testcrt.prg is

#include "Appevent.ch"
#include "Xbp.ch"
PROCEDURE Main
LOCAL nEvent
local oBMP1,oBMP2
local oXbpb,oPS
private oPSg
private oBMPdec0,oBMPdec1

oPS := SetAppWindow():presSpace()

14
Second life for Clipper Application by Aleksandar Stefanovic

oPSg:=oPS

SetMouse(.t.)

oBMP1:=XbpBitmap():new()
oBMP1:loadfile("pic1.jpg")
oBMP2:=XbpBitmap():new()
oBMP2:loadfile("pic2.jpg")

oBMPdec0:=XbpBitmap():new()
oBMPdec0:loadfile("dec0.jpg")
oBMPdec1:=XbpBitmap():new()
oBMPdec1:loadfile("dec1.jpg")

oXbpb := XbpPushButton():new()
oXbpb:create( , , {10,450}, {150,150})
oXbpb:cargo := .F.
oXbpb:activate := {|mp1, mp2, obj| obj:cargo := ! obj:cargo, ;
DrawBMPall( obj,oBMP2,1 ),test1() }

oXbpb:paint := {|mp1, mp2, obj| DrawBMPall( obj,oBMP1,1 ) }


oXbpb:enter := {|mp1, mp2, obj| DrawBMPall( obj,oBMP1,1) }
oXbpb:leave := {|mp1, mp2, obj| DrawBMPall( obj,oBMP1,0) }
oXbpb:lbdown := {|mp1, mp2, obj| DrawBMPall( obj,oBMP2,1 ) }
oXbpb:motion := {|mp1, mp2, obj| DrawBMPall( obj,oBMP2,1 ) }
oXbpb:lbup := {|mp1, mp2, obj| DrawBMPall( obj,oBMP1,1 ) }
oXbpb:drawmode:=XBP_DRAW_OWNER
DrawBmpall(oXbpb,oBMP1,0)

AS_MENU( SetAppWindow():menuBar() )
setcolor("W/N")
DO WHILE nEvent <> xbeP_Close
nEvent := AppEvent( @mp1, @mp2, @oXbp )
oXbp:handleEvent( nEvent, mp1, mp2 )
ENDDO
RETURN
PROCEDURE MenuDispatcher( nSelection )

DO CASE
CASE nSelection == 101
QUIT

15
Second life for Clipper Application by Aleksandar Stefanovic

ENDCASE
RETURN

PROCEDURE AS_MENU( oMenubar )


LOCAL oMenu

oMenu := XbpMenu():new( oMenuBar )


oMenu:title := "~Sistem"
oMenu:create()
oMenu:itemSelected := {|nItem| MenuDispatcher( 100+nItem ) }
oMenu:addItem( {"~a) End" , NIL} )
oMenu:addItem( {NIL, NIL, XBPMENUBAR_MIS_SEPARATOR, 0} )

oMenu:addItem( {"~b) Indexing", {||indexserv()}} )


oMenubar:addItem( {oMenu, NIL} )

oMenu := XbpMenu():new( oMenuBar )


oMenu:title := "~AS Software"
oMenu:create()
oMenu:itemSelected := {|nItem| MenuDispatcher( 700+nItem ) }
oMenu:addItem( {"~a) Informacije o programu" ,{||aboutas()}} )

oMenubar:addItem( {oMenu, NIL} )

RETURN

STATIC PROCEDURE DrawBMPall( oControl,oBitmap,nn)


LOCAL oPS
oPS := oControl:lockPS()

oBitmap:draw( oPS, {0 , 0})


oControl:unlockPS( oPS )
do case
case nn==0
oBMPdec0:draw(oPSg,{500,50})
case nn==1
oBMPdec1:draw(oPSg,{500,50})
ENDCASE
RETURN

function test1()
msgbox("WE ARE NOW IN TEST1 function")
return (NIL)

16
Second life for Clipper Application by Aleksandar Stefanovic

func indexserv()
return (NIL)
func aboutas()
return (NIL)

This is screen when we start our application. When we move mouse over picture button,
button will change and picture to.

Now our old Clipper application can look better.

17

Das könnte Ihnen auch gefallen