Commuter function tutorial (OpenInsight Specific)
At 10 NOV 2000 05:16:13AM Oystein Reigem wrote:
In case somebody's interested: Here's a merged and updated version of a couple of postings I once made.
- - - - -
Commuter function tutorial
Some programmers, when they make a form, have their programming all over the place. E.g, they have several user events. E.g, they have quickevents executing several different stored procedures.
It's generally better to try and collect the code in one place - in a so-called commuter function (commuter module). The commuter is called from the form's quickevents. The commuter must be called in such a way it can know which event called it. The commuter first checks which event called it, and then branches to the appropriate section of code.
It's generally a good idea to let the commuter function have the same name as the form.
(There are cases where one cannot use a quickevent but has to have user events. But that's not too common.)
Here's an example:
Let's say you have a window XXXX with a couple of pushbuttons B1 and B2 and a couple of edit controls E1 and E2. Let's say you need event handling for the following:
- window CREATE
- button CLICKs
- GOTFOCUS for E1
- some special character checking and handling while the user keys data into E1 (CHAR event).
Then make a function Xxxx with the following structure:
function Xxxx( Event, CtrlEntID, Param1, Param2, Param3, Param4, Param5, Param6, Param7 )
. $insert Logical
. begin case
. case Event=CREATE"
. . CreateParam=Param1
. . (code for CREATE event)
. case Event=CLICK"
. . begin case
. . case CtrlEntID=@Window : ".B1"
. . . (code for pushbutton B1)
. . case CtrlEntID=@Window : ".B2"
. . . (code for pushbutton B2)
. . case true$
. . . debug
. . end case
. case Event=GOTFOCUS"
. . begin case
. . case CtrlEntID=@Window : ".E1"
. . . (code for GOTFOCUS event of edit control E1)
. . case true$
. . . debug
. . end case
. case Event=CHAR"
. . begin case
. . case CtrlEntID=@Window : ".E1"
. . . VirtCode=Param1
. . . ScanCode=Param2
. . . Ctrl=Param3
. . . Shift=Param4
. . .*STPROCEXEXXXX (- Message: EXECUTE) - Parameters: see below. Note that if the commuter function has the same name as the form, the Send Message to Entity value can be *STPROCEXE@WINDOW
i.e, you can use the same value for all forms.
The Parameters value depend on the event. For the ones in my example use:
'CREATE','@SELF','@PARAM1'
'CLICK','@SELF'
'CHAR','@SELF','@PARAM1','@PARAM2','@PARAM3','@PARAM4','@PARAM5'
To find out how many @PARAM… parameters you need, and what they mean, you can check with the user event editor, or the help docs.
Note that using '@SELF' instead of the actual control name makes the value for a certain event the same for all instances of that event.
To save yourself a lot of time and energy, prepare a file with both the Send Message to Entity value(s) and the Parameters values for all possible events - and have your word processor and that file open while you do form development work. Now when you define your quickevents you can simply copy and paste values instead of keying them in.
Another advantage with using @WINDOW and '@SELF' instead of the actual form and control names, is that it's much easier to change the names of forms and controls (assuming you change the name of the commuter function along with the name of the form, of course). You don't have to change all your quickevents afterwards.
Here are the Parameters values, and a short explanation, for all the events I have used myself so far:
'BUTTONUP','@SELF','@PARAM1','@PARAM2','@PARAM3','@PARAM4','@PARAM5','@PARAM6','@PARAM7'
1=xDown
2=yDown
3=xUp
4=yUp
'CHANGED','@SELF','@PARAM1'
1=NewData
'CHAR','@SELF','@PARAM1','@PARAM2','@PARAM3','@PARAM4','@PARAM5'
1=virtcode
2=scancode
3=ctrl
4=shift
5=alt
'CLEAR','@SELF','@PARAM1','@PARAM2','@PARAM3'
1=bsavekey
2=bsuppresswarning
3=bmaintainfocus
'CLICK','@SELF'
'CLOSE','@SELF','@PARAM1'
1=CancelFlag, hva nå der er for noe!!!???
'CREATE','@SELF','@PARAM1'
1=CreateParam
'DBLCLK','@SELF','@PARAM1','@PARAM2','@PARAM3'
1=CtrlKey
2=ShiftKey
3=MouseButton
'DELETE','@SELF'
'GOTFOCUS','@SELF','@PARAM1'
1=PrevFocusID
'LOSTFOCUS','@SELF','@PARAM1'
1=Flag
2=FocusID
'OMNIEVENT','@SELF','@PARAM1','@PARAM2','@PARAM3','@PARAM4','@PARAM5'
1=Message
2=Param1
3=Param2
4=Param3
5=Param4
'OPTIONS','@SELF'
'PAGE','@SELF','@PARAM1'
1=PageAction
'READ','@SELF'
'SIZE','@SELF','@PARAM1','@PARAM2','@PARAM3','@PARAM4'
1=x
2=y
3=Width
4=Height
'VSCROLL','@SELF','@PARAM1'
1=Value
'WINMSG','@SELF','@PARAM1','@PARAM2','@PARAM3','@PARAM4'
1=hWnd
2=Message
3=wParam
4=lParam
'WRITE','@SELF'
- Oystein -
At 10 NOV 2000 02:23PM Don Miller - C3 Inc. wrote:
Oystein ..
Thanks - I mean it!. Over time, I have clipped pieces of this into my word-processor, but they always seem to get misplaced. It's nice to have it all in one place again.
Don Miller
C3 Inc.
At 11 NOV 2000 01:16AM Barry Stevens wrote:
I do use commuter code (Ex Arev). I Do:
function Xxxx( Event, CtrlEntID, Param1, Param2, Param3, Param4, Param5, Param6, Param7 )
CP= field(CtrlEntId,'.',2)
EventList=CREATE,CLICK,GOTFOCUS" ;*…etc
locate Event in EventList using ',' setting Pos then
on Pos gosub CREATE,CLICK,GOTFOCUS ;*...etcend
return RetFlag
CREATE:
return
CLICK:
begin caseCP=PRINT"gosub ClickPrintend casereturn
GOTFOCUS:
begin casecase CP=E1"gosub GotFocusE1end casereturn
At 12 NOV 2000 07:42AM Oystein Reigem wrote:
Barry,
From the code I've seen I believe more developers use locate/on gosub than case. It's probably more efficient. Me I simply have to use case. My shrink says it's OK.
![]()
Btw - one problem with commuters is they can grow too big.
- Oystein -
At 12 NOV 2000 08:14AM Oystein Reigem wrote:
Don,
Thanks - I mean it!. Over time, I have clipped pieces of this into my word-processor, but they always seem to get misplaced.
I'm glad to be of some help.
The following works for me: I have a couple of OI projects running, and for each project a word processor file where I jot down/paste in everything relevant about the project. In each of the files - at the very bottom - I keep a copy of this quickevent stuff.
But it's still a hassle with all this switching back and forth, and to select, copy and paste. What if we had this utility - a panel floating on top of all other windows (could be written in something else than OI), with a button for each of these useful strings. A click on a button would put its string into the Windows clipboard. So all one would have to do to get a string was to click the appropriate utility button, click back in the QuickEvents window, and do a paste (ctrl-V).
Or do you think the utility could be clever enough to paste the string too?
Or perhaps John Gunther's idea with a window where one can edit all events simultaneously would be better. The problem with that is of course it won't interface with Form Designer. Both of them need to have the window row open.
It's nice to have it all in one place again.
If you have events missing on my list feel free to post them back.
- Oystein -
At 12 NOV 2000 09:01PM Scott, LMS wrote:
Hi All
Thanks for that Oystein. It looks like a very good idea, and much easier to share across forms, athough I have been known to cut and paste controls from one form to another which helps too, it brings all the event code with it.
What I want to know is when I create a check out to make an update for the client app, When I have created a new form, I have to select all the events individually and it drives me nuts.
I have sped things up with the uses button but I am thinking of writing something that just grabs all the stuff with the form ID from all the relevant tables and shifts it.
double click mouse, all, mouse move ok, doubleclick mouse move ok blah. Talk about tedious, especially if I have a form with a lot of stuff on it. What's worse with my fingers, is that I don't know if I have everything, there is no way to print the list and verify what I have is what I want.
I get similar headaches when I copy program code but not as bad.
Is there another way that I missed? If I decide to write something can someone tell me what the files are that I need to check for form components, source and exe (and anything else).
Thanks
Scott
At 13 NOV 2000 08:03AM Bob Carten, WinWin Solutions Inc. wrote:
Oystein:
FWIW here is my approach to Commuter Modules (with much help from others):
- 2 arguments: CtrlEntId and Params, where params is &= delimited list
- Naming convention for Commuter Modules is WINDOWNAME_EVENTS
- Use INET_QUERYPARAM to make QUERYPARAM. Set queryparam=REQUEST, rather than REQUEST
- Write a function DO_EVENT to redirect to appropriate module
- Cost: Complexity
- Benefit: Flexible, and you will migrate toward functions that you can call from OICGI or from conventional form. Parameters pass through to other functions without having to keep count.
Function Do_Event(CtrlEntId,Parms)
*—-
* RJC 08-15-00
* Route Event to Window Handler
* Assumes Naming convention is WINDOWNAME_EVENTS
* To use unconventional name, set the property @EVENTHANDLER in the create event
* then call DO_EVENT
* Can get fancier with naming convention, allow for site specific modules, then application
*
* Calling Convention
* In each event put RETURN FUNCTION(DO_EVENT(CtrlEntID,'EVENT'))
* To pass event_parameters, call as
* params=EVENT=POSCHANGED&NextRow=:nextrow:'&NextColumn=:Nextcolumn
* RETURN FUNCTION(DO_EVENT(CtrlEntID,params))
*———
Declare Function Set_Property, Get_Property
If assigned( CtrlEntId ) else CtrlEntId='
If assigned(parms) else parms='
If len(ctrlEntid) else
Return 0End
Win_Name=CtrlEntId1,'.'
handler=Get_PRoperty(Win_Name,'@EVENTHANDLER')
if len(handler) else
test=win_name:'_WINDOW_EVENTS'if rowexists('SYSREPOS',@appid:"*STPROCEXE**":test) thenhandler=testwas=Set_Property(Win_Name,'@EVENTHANDLER',handler)endend
if len(handler) then
return Function(@handler(CtrlEntId,parms))end else
Pass thrureturn 1end
* Commuter Module **
Function Spa_Entry_Events(CtrlEntId,parameters)
* Events for Spa Entry Window
* RJC 06-12-00 Add Omnievent for Refresh of product table
* RJC 09-04-00 Add REFESH_NEEDED to activate, de-activate
* RJC 09-05-00 Added cascading delete logic
Declare Function Utility
Declare Function QueryParam,Msg,Dialog_Box,Collect.IXVals,popup
Declare Function Repository, Get_Property,Set_Property,Send_Message, Start_MDIChild
Declare Subroutine Set_Property, Send_Event, Forward_Event,Set_status,Set_EventStatus
Declare Function IndexLookup
$INSERT POPUP_EQUATES
$INSERT MSG_EQUATES
$INSERT PRODUCTS_EQUATES
$INSERT OIWIN_EQUATES
If assigned(CtrlEntId) else CtrlEntId='
If assigned(Parameters) else Parameters='
err='
retval=0 ; * default to Done
if len(CtrlEntId) and Len(Parameters) else
err=Missing Parameters'GoTo Errorend
was_Cursor=Utility('CURSOR','H')
if CtrlEntId=@SELF' then
CtrlEntId=Get_Property('SYSTEM','FOCUS')end
if Index(parameters,'=,1) then
event=QUERYPARAM(parameters,'EVENT')end else
event=parametersend
Win_Name= CtrlEntId1,'.'
mdiframe=Get_Property(Win_Name,'MDIFRAME')
if len(mdiframe) then
parent=mdiframeend else
parent=Win_Nameend
Controlname=CtrlentIdCol2()+1,65532
if len(ControlName) then
Control_event=ControlName:'_':Eventend else
Control_Event=WINDOW_':Eventend
Begin Case
Case Event=CREATE"Call CenterWindow(CtrlEntId)
Set IO Options.iooptions=Get_Property(CtrlEntId,'IOOPTIONS')iooptions=1 ; * Only check required on savewas=Set_Property(Win_Name,'IOOPTIONS',iooptions)
Product table to sort by product, ascending, leftwas=Set_Property(Win_Name:'.PRODUCT_TABLE','SORTEDCOL',edt$product,1)
Product col should be upper caseCall EtSetCase(Win_Name:'.PRODUCT_TABLE',edt$product,1)Case Event=WRITE'ID')was=Set_Property(Win_Name,'@PREVIOUS',id)Forward_Event()Case Event=CLEAR'ID')was=Set_Property(Win_Name,'@PREVIOUS',id)Forward_Event()Case Control_Event=BTN_OK_CLICK'call send_event(Win_Name,'WRITE')Case Control_Event=BTN_CANCEL_CLICK'call send_event(Win_Name,'CLEAR')call send_event(Win_Name,'CLOSE')Case Control_Event=EFFECTIVE_DATE_DEFAULT'quote_date=Get_Property(Win_Name: '.QUOTE_DATE','TEXT')quote_date=Iconv(quote_date,'D')Return quote_dateCase Control_Event=EFFECTIVE_DATE_OPTIONS'Case Control_Event=PRODUCT_TABLE_CALCULATE''.....Case Control_Event=PRODUCT_TABLE_DBLCLK''.....case Event=DBLCLK'
Generic handlerCall Send_Event(CtrlEntId,'OPTIONS')Case 1
Not Handled, pass thru to System Event Handler
Could call a PROMOTED_EVENTS(CtrlEntID,Parameters) if
you want to implement a Generic event handlerretval=1End Case
Return Retval
Error:
err=CtrlEntId : errx=Msg('','STANDARD_ERROR','',errreturn 0
At 13 NOV 2000 10:57AM Oystein Reigem wrote:
Bob,
Interesting.
One difference from my approach you don't mention explicitly is that you use user events and not quickevents. I can think of advantages and disadvantages with both. I'd like to hear the opinion of others on this.
Having one centralized Do_Event function seems like a good idea.
As you hint at - with other and more sofisticated naming conventions you can let it select between versions of handlers. E.g, "I - the mighty Do_Event - was just told to look for and execute an OPTIONS handler for ABCWINDOW*DEFCONTROL. But I am now running for the XYZ customer. So before I run the standard ALL_ABCWINDOW_EVENTS function first let me see if there's a special XYZ_ABCWINDOW_DEFCONTROL_OPTIONS or XYZ_ABCWINDOW_OPTIONS or XYZ_ABCWINDOW_EVENTS function I should run instead."
With a different twist you can have some automatic pre- and post-processing. E.g, "I - the glorious Do_Event - was just told to look for and execute an OPTIONS handler for ABCWINDOW*DEFCONTROL. But first let me see if there is some general pre-processing I should do - let me look for a PRE_OPTIONS function. In case there is one I'll run that first. Then I'll run the standard ABCWINDOW_EVENTS. Finally let me see if there's a post-processing POST_OPTIONS function that I should run last." It won't be as general and flexible as promoted events, but still useful.
Is there any sense in what I write here?
- Oystein -
At 13 NOV 2000 11:49AM Oystein Reigem wrote:
Scott,
I'm not the right person to help you with this.
I'll just mention another nutsdriving matter with Checkout: When you have laboriously selected all your components and clicked the OK button (or whatever the button's called) you (OI) may discover one of your components is locked - e.g a stored procedure open in System Editor. You get an error message and Checkout promptly forgets everything you selected. Back to square one. Balder than before.
![]()
- Oystein -
At 13 NOV 2000 05:08PM Bob Carten wrote:
You are correct.
1. You can call from quick_events using execute stored procedure, DO_EVENT parameters '@self','CLICK'. Is more difficult to get the added parameters NextRow, Nextcolumn etc. If you want to do pre and post processing then quick events won't work anyway.
2. Is not as general as promoted events. Specificity can be a benefit or a problem ( a feature or a Feature ? ). It is easier to control / understand the order of events.
3. You can arrange any 'intelligent' chaining scheme you want. We used one where we kept track of the site_name in a config file, then
used that to check for the existence of a site specific commuter module. (test=WINDOWNAME:"_":SITE:"_EVENTS" if rowexists … ).
Site specic code ends with a call to the core module, so you only override what you need to. Thus we can override core functionality without editing core modules; ship new revised modules without losing local customisation.
Hope this helps.
Bob
At 14 NOV 2000 02:43AM Barry Stevens wrote:
Have you tried "Deploy Application"
User system calls RDKInstall(installpath) to install
I use it all the time - works for me - But do find it misses something ever now and the, so I have a TestSytem that I install to.
It wont pick up dict changes ( if not new) - Use Full app deploy - after you have selected from "All changes since", then mark off the stuff you need.
Attach SYSUPGRADE if you deploy files and check that the path is right for you.
Check the help from App manager - Guide to app development - Runtime deployment kit.
At 14 NOV 2000 10:02AM Oystein Reigem wrote:
Bob,
Thanks for the explanation and exchange of ideas.
- Oystein -
At 15 NOV 2000 12:50AM Scott, LMS wrote:
Hi Barry
I think we have written our own version of the deploy. The whole system gets a bit cranky if we upgrade to the latest version of OI however. Something to do with creating tables I think.
We still have to make a checkout the tedious way before we can use our version of the deploy bit.
I will have a look at what you suggested although I suspect that trying to teach myself from the manuals will not be adequate (except as further hair depletion).
Scott
At 20 NOV 2000 04:32AM [url=http://www.sprezzatura.com]The Sprezzatura Group[/url] wrote:
We have a check-out utility I wrote serveral years back (early 1997) that runs through a entity and will check out all of it's children in one go. It still needs a bit of work, but I'd be willing to send it out to anyone interested.
Just send me akaplan@sprezzatura.com. If it's popular, we just might post the source on our download page.
World Leaders in all things RevSoft