Sign up on the Revelation Software website to have access to the most current content, and to be able to ask questions and get answers from the Revelation community

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     ;*...etc

end

return RetFlag

CREATE:

return

CLICK:

begin case
  CP=PRINT"
    gosub ClickPrint
end case

return

GOTFOCUS:

begin case
  case CP=E1"
    gosub GotFocusE1
end case

return


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 0

End

Win_Name=CtrlEntId1,'.'

handler=Get_PRoperty(Win_Name,'@EVENTHANDLER')

if len(handler) else

 test=win_name:'_WINDOW_EVENTS'
 if rowexists('SYSREPOS',@appid:"*STPROCEXE**":test) then
    handler=test
    was=Set_Property(Win_Name,'@EVENTHANDLER',handler)
 end

end

if len(handler) then

return Function(@handler(CtrlEntId,parms))

end else

  • Pass thru
return 1

end

* 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 Error

end

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=parameters

end

Win_Name= CtrlEntId1,'.'

mdiframe=Get_Property(Win_Name,'MDIFRAME')

if len(mdiframe) then

parent=mdiframe

end else

parent=Win_Name

end

Controlname=CtrlentIdCol2()+1,65532

if len(ControlName) then

 Control_event=ControlName:'_':Event

end else

 Control_Event=WINDOW_':Event 

end

Begin Case

  Case Event=CREATE"
    Call CenterWindow(CtrlEntId)
  • Set IO Options.
    iooptions=Get_Property(CtrlEntId,'IOOPTIONS')      
    iooptions=1 ; * Only check required on save
    was=Set_Property(Win_Name,'IOOPTIONS',iooptions)
  • Product table to sort by product, ascending, left
    was=Set_Property(Win_Name:'.PRODUCT_TABLE','SORTEDCOL',edt$product,1)
  • Product col should be upper case
    Call 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_date      
  Case Control_Event=EFFECTIVE_DATE_OPTIONS'
  Case Control_Event=PRODUCT_TABLE_CALCULATE'
    '.....
  Case Control_Event=PRODUCT_TABLE_DBLCLK'
    '.....
  case Event=DBLCLK'
  • Generic handler
    Call 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 handler
    retval=1

End Case

Return Retval

Error:

 err=CtrlEntId : err
 x=Msg('','STANDARD_ERROR','',err
 return 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.

The Sprezzatura Group

World Leaders in all things RevSoft

View this thread on the forum...

  • third_party_content/community/commentary/forums_nonworks/198ea81a713cbe9c8525699300386aa0.txt
  • Last modified: 2023/12/28 07:40
  • by 127.0.0.1