Hand-coded lookup from unrelated table

I have a form (ABC) where I want to add a lookup button to pre-fill some fields with typically used values stored in unrelated table. It contains some commonly used remarks, so I want to allow user to choose one of a few remarks instead of retyping every time, however this is not mandatory, user can choose to type own custom remark not listed in remarks table. I do it with this code

OF ?ButtonCallLookup
    GlobalRequest = SelectRecord
    BrowseRemarks()
    IF GlobalResponse = RequestCompleted THEN
        ....
        ! Assigning field values
        ....
       DISPLAY()
   END

However, this approach setā€™s the GlobalRequest and GlobalResponse for the calling form causing some undesired behaviour when trying to cancel after calling lookup.

Template generated lookups do it this way, using run method

        IF SELF.Run(1,SelectRecord) = RequestCompleted
        ....
        ! Assigning field values
        ....
        ELSE
          CLEAR(PRE:Myfield)
          SELECT(?PRE:MyField)
          CYCLE
        END

And the lookup procedure is defined in the run method code

ThisWindow.Run PROCEDURE(USHORT Number,BYTE Request)
ReturnValue          BYTE,AUTO
  CODE
  ReturnValue = PARENT.Run(Number,Request)
  IF SELF.Request = ViewRecord
    ReturnValue = RequestCancelled                         ! Always return RequestCancelled if the form was opened in ViewRecord mode
  ELSE
    GlobalRequest = Request
    BrwMyTable1				! subordinate procedure number 1																				
    ReturnValue = GlobalResponse
  END
  RETURN ReturnValue	  

How do I add my BrowseRemarks procedure to be called as number 2 ?

One way is to use Omit(ā€˜=====Omittedā€™) to comment out the generated procedure call and add you own version.
eg
ThisWindow.Run Parent Call Embed Priority 6000 / / / /
!My Embed code starts here
IF SELF.Request = ViewRecord
ReturnValue = RequestCancelled ! Always return RequestCancelled if the form was opened in ViewRecord mode
ELSE
GlobalRequest = Request
UpdateTemplateFile
MyAdditionalForm !<------Add additional procedures at the end of the list.
ReturnValue = GlobalResponse
END

Omit('=====Omitted')
!My Embed code finishes here
ThisWindow.Run Parent Call Embed Priority 6000 /\ /\ /\ /\

IF SELF.Request = ViewRecord
ReturnValue = RequestCancelled ! Always return RequestCancelled if the form was opened in ViewRecord mode
ELSE
GlobalRequest = Request
UpdateTemplateFile
ReturnValue = GlobalResponse
END
ThisWindow.Run Parent Call Embed Priority 8500 / / / /
=====Omitted
ThisWindow.Run Parent Call Embed Priority 8500 /\ /\ /\ /\

Anything else will mean involving modifying the template. Any additional procedures need to be added at the end of the list of procedures in ThisWindow.Run

Apologies for the formatting I havent mastered the different formatting options.

Greg,
Two points.
First, the Run(Number,Request) method also sets GlobalRequest, GlobalResponse. So if setting those is causing a problem, Iā€™m not sure switching will solve anything.
You can always save and restore the values. If you have embed code that is looking at GlobalRequest in your procedure, you should change that to use SELF.Request or ThisWindow.Request. This property gets set to GlobalRequest when the procedure opens.

Second, to add a procedure to the ā€˜runā€™ you need a template that adds your procedure to the ProcsCalled symbol.
#ADD(%ProcsCalled,ā€˜YourProcedureā€™)
or
#ADD(%ProcsCalled,%YourProcedure)

1 Like

Was thinking about it, but itā€™s not very elegant solution and requires changes in code if I add another lookup call. Can be confusing if someone else is making changes or even if I do it after a couple of months and forget about this omit statement.

As for code formatting options you can put Triple Backtick (```) on the line above and below your code - thatā€™s what I am now doing. The default way is to use indentation but I find that confusing

Not really sure why, but I never had issues with forms using template genenerated field lookups and my hand coded one caused the form to keep on asking if I want to cancel regardless of how many times I pressed ā€œYesā€. The solution was to press ā€œNoā€, which would then return to the first tab of the form and then allow to cancel.

Thanks for the hint on ProcsCalled symbol, but writing a template for this one-off requirement would be an overkill. Also not so strong on template language, so would probably take much more time.

I decided to create 2 local variables called: OriginalRequest and OriginalResponse ( I recall these were used in CFD ) and save the values before calling lookup. Iā€™m not refering to GlobalRequest and GlobalResponse in my hand code anywhere else there but that 4 lines of code did the trick, so I think I will stick to that unless someone suggests a better way.

OF ?ButtonCallLookup
	! Save original values
	OriginalRequest = GlobalRequest
	OriginalResponse = GlobalResponse
    GlobalRequest = SelectRecord
    BrowseRemarks()
    IF GlobalResponse = RequestCompleted THEN
        ....
        ! Assigning field values
        ....
       DISPLAY()
    END
	! restore original values
	GlobalRequest = OriginalRequest 
	GlobalResponse = OriginalResponse  

Thanks

Greg

It helps that you specified what was the ā€œIssueā€.

This ā€œBad Codeā€ should Not be required, but there is no denying it fixed your Issue. GlobalRequest / Response should only be used for a short time between called procedures.

When Bad Code is required to fix an issue it sometimes means there is Bad Code some place else which somehow 2 wrongs make a right **. By Bad Code that could mean its in the wrong Embed point, like it should be in Accepted but its in Selected or All Events embed.

If you want to post the Module CLW code for just this Form as an Attachment one of us will take a look at it. ABC tends to have very compact code.


** I work on some old code that has had many authors. I often run into code that IMO was obviously in the wrong place (event or routine), or should not have been needed. I would wonder how it ever worked at all. I would take out that code, then find various new problems appearing. Often I would find bad code elsewhere that was making 2 bad things work.

So this would need to be a template that becomes part of the ABC templates, as its scope is restricted to the ABWindow.tpw and #Procedure templates that have the ,PARENT(Window(ABC)) attribute, ie ABBrowse.tpw

Agreed its not elegant, but I found a page in the help docs called ā€œCustomizing Default Templatesā€, so it seems SV dont mind and perhaps encourages the customisation of the shipping templates.

With this in mind, you could open ABwindow.tpw and search for
#AT(%WindowManagerMethodCodeSection,'Run','(USHORT Number,BYTE Request),BYTE')

then in this section, look for the line
#?ReturnValue = GlobalResponse

Immediately before it add this line
#EMBED(%ThisWindowRunBeforeGlobalResponse,'ThisWindow.Run(Number,Request) AfterProcedureList/Additional Procedures/Before Global Response')

You have now created a new embed point where you can add code immediately after the list of procedures called. Its less code changes, but its still code changes, however at least any additional procedures added by the templates wont affect any procedure calls you add to this embed point as it sits after everything else.

When looking for this new embed, make sure the template registry is not readonly and reload the app, then you should see this embed point on its own probably below the embed ListBoxStyle After Define.

If this doesnt quite hit the spot, when in the ABWindow.tpw at this section comment out the ,Hide for the #EMBED(%BeforeProcedureCall,'Before calling procedure'),%ProcsCalled #!,HIDE

eg
#?OF %(INSTANCE(%ProcsCalled))
#EMBED(%BeforeProcedureCall,ā€˜Before calling procedureā€™),%ProcsCalled #!,HIDE
#RESUME

That also breaks into list of procedures called before they get Executed but I suspect this is more for allowing overrides, but again it requires a bit of coding which will be tied to the instance number/order the procedures are called in. The embed code should move if a procedure is removed from %ProcsCalled which might or might not be acceptable.

Dont know if the above would be any good? Youā€™ll notice in the shipping templates there are quite a few things hidden with the Hide attribute and its fairly painless adding additional embeds to the default templates.

I found a way for a separate stand alone procedure extension to access the %ProcsCalled, never tried this approach before but it works.

#AT(%WindowManagerMethodCodeSection,'Run','(USHORT Number,BYTE Request),BYTE'),First

#For(%ProcsCalled)
#! Cycling to the end of the list just to add some more procs.
#EndFor
#Add(%ProcsCalled,'SomeProcedure')
#Add(%ProcsCalled,'yetAnotherProcedure')
#! The above #Add could be populated from a #Procedure #Extension #prompt
#EndAt

So @greg
It means there is no need to edit the ABWindow.tpw now and add an additional embed like:
#EMBED(%ThisWindowRunBeforeGlobalResponse,'ThisWindow.Run(Number,Request) AfterProcedureList/Additional Procedures/Before Global Response')

Less work and gained more control over other templates. :grinning:

So its a bit of a hack but it is possible to build a Procedure extension template which can update the %ProcsCalled multi var used in the ABWindow.tpw ThisWindow.Run as the screen shots show. Tested and works in C6, not tested in C11 yet, but it should work. :wink:
Step 1.

Step 2.

Step 3.

Step 4.

Step 5. Source code generated and compiles fine.

Bit of overkill, but possible and it also shows the proof of concept that any template symbol can be modified. :grinning: