[futurebasic] Re: [FB] Re: Skeletons

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : June 2002 : Group Archive : Group : All Groups

From: Alain Pastor <pixmix@...>
Date: Sun, 02 Jun 2002 19:04:23 +0200
"Chip G." wrote:
> 
> At 22:08 -0400 06/01/02, Ken Shmidheiser wrote:
> >Nevertheless, there is something elegant to letting the toolbox do
> >the math for you:
> >
> >dim as rect r
> >setrect( r , 20, 50, 400, 300 )
> >window 1, "test window 1", @r
> >offsetrect( r, 50, 50 )
> >window 2, "test window 2", @r
> >offsetrect( r, 50, 50 )
> >window 3, "test window 3", @r
> >insetrect( r, 50, 50 )
> >window 4, "test window 4", @r
> >offsetrect( r, 50, 50 )
> >window 5, "test window 3", @r
> >
> >do
> >handleevents
> >until fn button
> 
> Here is another example of why I'd like a shell. Without a shell all
> I can do is study this example. With a shell I could drop this code
> in an appropriate location and run it to see what it does. This is
> mainly an issue with my being a novice with this language (although
> I've studied BASIC before, it's been so long that it is effectively a
> new language to me, especially with all the GUI elements).
> 

I would recommend to use the project manager to build a reusable
shell, otherwise you would end up building various little shells
mainly for demo purpose, like those posted in the list.

I personally have no shell to offer (I always build from scratch
because the samples here are short), however I would amend Ken's work
since I think that it is easier to remove things than to add them.
First, I would include all the event types that the dialog handler can
handle (it would be useful to add a few comments in white characters
so that BB can't see them ;-)). Here is a more complete list:

select case ( evnt )

'~window events

case _wndClick
case _wndClose
case _wndRefresh
case _wndActivate
case _wndZoomin
case _wndZoomOut
case _wndResized

'~System events

case _evDiskInsert

'~button events

case _btnClick

'~contextual menu events

case _cntxtMenuClick

'~edit fields events

case _efClick
case _efReturn
case _efTab
case _efShiftTab
case _efClear
case _efLeftArrow
case _efRightArrow
case _efUpArrow
case _efDownArrow
case _efSelected
case _efClick
case _pfClick

'~keystroke events

case _evKey

'~cursor events

case _cursOverBtn
case _cursOverEF
case _cursOverNothing, _cursEvent

'~process events

case _MFEvent
case _FBQuitEvent

'~preview events

case _preview
select id
case _preMenuClick
case _preWndGrow
case _wndMoved
case _wndSized
case _efChanged
case _preEfClick
case _preWndZoomIn
case _preWndZoomOut
case _wndDocWillMove 
end select

'~programmer events

case _userDialog

end select

I would also build a raw event handler in a similar manner. A raw
event handler lets you handle the event before the runtime deals with
it. If you don't want the runtime to do its usual work once you have
handled the event, just set the what field to 0.

clear local fn doEvent
dim evnt as ^EventRecord
dim handled as boolean

evnt = event

select evnt.what
case _keyDwnEvt
case _nullevt
case _mbutdwnevt
case _mbutupevt 
case _keydwnevt
select ( evnt.message and _charCodeMask )

end select
case _keyupevt
case _autokeyevt
case _updatevt
case _diskinsertevt
case _activateevt
case _osevt
case _khighlevelevent
case _highlevelevtbit
end select
if handled then evnt.what = 0
end fn

the above handler needs more work in order to decipher the event. You
will probably notice that the constants used by FB differ from the
Apple constants. Don't ask me, I don't know why.

If you are used to deciphering the events with other languages such as
C or Pascal, it is the place where you can use the same techniques, I think.
Frankly, it is better to let the runtime do the hard work for you,
since the deciphering routines are built-in and will be included in
your final application anyway. This handler is sometimes very useful
on special occasions.

I've been told that it is not a good idea to use the gFBQuit global
variable. Again, I don't know precisely the reason for this, let's say
it's a private global for the runtime. So, it is better to use your
own flag to end the application. You should replace all gFBQuit with
something like gAppMustQuit or anything you like (declared as a
boolean global variable).

I would also create a function for the initialization where I would
set all the possible vectors

clear local fn initialize
dim err as OSErr
/*
 set here the initial values for the global variables
 for example:
gIsRunningOnX = ( system( _sysVers ) => 1000 )
 if something may fail return an error
*/
on dialog     fn doDialog
on menu       fn doMenu
on event      fn doEvent
on mouse      fn doMouse
on edit       fn doEdit
on break      fn doBreak
on finderinfo fn doFinderInfo
on overflows  fn doOverFlow
on stop       fn doStop
on timer(1)   fn doTimer
kill appleevent _kRequiredEventClass, _kAEOpenDocuments
on appleevent ( _kRequiredEventClass, _kAEOpenDocuments ) fn doAEReadFile
end fn = err

It is likely that you won't need all of them, but there are all there
in case and besides you can rem out or delete the unneeded statements.

Of course you would have to create the empty functions. Don't forget
to set the gAppMustQuit variable to _zTrue during development in your
doBreak and doStop functions, so that you can exit gracefully in
certain situations.

You might wish to plug in draft functions that handle the opening of
files specifically with the finderInfo and Appleevent vectors because
they both use global runtime variables not always easy to remember:

clear local fn doFinderInfo
dim info as FInfo

long if gFBFndrInfoCount
gFBFndrInfoCount--
long if fn FSpGetFInfo( gFBInfoSpec( gFBFndrInfoCount ), info ) = _noErr
select gFBInfoAction%( gFBFndrInfoCount )
case _finderInfoOpen  : fn OpenFile( gFBInfoSpec( gFBFndrInfoCount ),
info.fdType )
case _finderInfoPrint : fn PrintFile( gFBInfoSpec( gFBFndrInfoCount ),
info.fdType )
end select
end if
end if
end fn


clear local fn doAEReadFile
dim as int     i
dim as long @  count, actualSize, keyWord, type
dim as AEDesc  AE
dim as FSSpec  f
dim as FInfo   info

long if fn AEGetParamDesc( gFBAEEvent, _keyDirectObject, _typeAEList,
AE ) = _noErr
long if fn AECountItems( AE, count ) = _noErr
for i = 1 to count
long if fn AEGetNthPtr( AE, i, _typeFSS, keyWord, type, @f,
sizeof(FSSpec), actualSize ) = _noErr
long if fn FSpGetFInfo( f, info ) = _noErr
fn OpenFile( f, info.fdType )
end if
end if
next
end if
i = fn AEDisposeDesc( AE )
end if

end fn

I would also add a kind of template to remind me how I can deal with
contextual menus (this is not what I would call straight forward).
Here is a little example that can be added to Ken's shell. In that
function I remap the user selection to an FB menu event so that the
handling is done in the regular menu handler.
To see this working, control click onto the text that reads "Click
around to see things work", but before:

1 - Add the following line in your dialog handler:

case _cntxtMenuClick : fn HandleContextualMenu

2 - Append the following line to your doMenu function (1000 is an
arbitrary menuID)

case 1000 : edit$(1) = "Contextual menu selected, item " + str$(
itemID )

3 - Plug the HandleContextualMenu function, near the top of the program:

local fn HandleContextualMenu
dim evnt   as .EventRecord
dim teH    as ..TERec
dim menuH  as handle
dim where  as point
dim @ selectionType as unsigned long
dim choice as long;0,menuID as int, itemID as Int

evnt = event
where = evnt.where
GlobalToLocal( where )
teH = tehandle( 3 )

long if teH
long if fn PtInRect( where, teH..viewRect )
menuH = fn NewMenu( 1000, "" )
long if menuH
InsertMenu( menuH, -1 )
AppendMenu( menuH, "item 1" )
AppendMenu( menuH, "item 2" )
AppendMenu( menuH, "item 3" )
LocalToGlobal( where )
long if fn ContextualMenuSelect( menuH, where, _nil,
_kCMHelpItemNoHelp, "", #_nil, selectionType, menuID, itemID ) = _noErr
long if selectionType
evnt.where = choice
evnt.what  = _FBMenuEvent
fn FBDoMacEvent( evnt )
end if
end if
DisposeMenu( menuH )
end if
end if
end if
end fn

I hope it won't confuse the matter.
-- 
Cheers,

Alain