![]() |
|
Published 1997-11-01 Printer-friendly version
In the last issue, Tom Moseley presented his "Better Ellipsis" template, which performed a cool trick: a global extension generated code into local procedure embeds. Although this is a great time saver, sometimes you dont want the global extension to do its thing everywhere, so you need to be able to override its actions here and there.
Ill be presenting a very handy template as a test bed for this topic. How many of you wish that the "Close" button in a browse would change to "Cancel" when the browse is being used to select a record? This would make it much more intuitive for the user. I know that Ive often added the following code to my "After Opening Window" embed:
IF LocalRequest = SelectRecord
?CloseRecord{PROP:Text} = 'Cancel'
END
It would be great if the system would handle this for me. I could modify Clarions BrowseSelectButton template to add this capability, but I generally prefer to leave Clarions templates alone (unless there is a "serious" bug that I need to fix right away). All extra functionality should be implemented with additional templates.
Whats the first challenge that we must surmount? Well, we need to know which procedures will need code. In our case its Browses that have a Select button. We know that the BrowseSelectButton template requires the BrowseBox template, so we can just look for that. The following template code will achieve this goal:
#SET(%ValueConstruct, %False) #FOR(%ActiveTemplate),WHERE(%ActiveTemplate = 'BrowseSelectButton(ABC)') #SET(%ValueConstruct, %True) #BREAK #ENDFOR
As you may have noticed in other template examples from me, I normally save the current %ActiveTemplate and %ActiveTemplateInstance before doing this type of operation. In the case of a global extension, though, its not necessary. (Actually, it may not be necessary at all anymore, but I keep doing it "just in case".)
Also, notice that Im looking specifically for "BrowseSelectButton(ABC)". If you also want to use the old Clarion template chain, just modify the "WHERE" condition. By the way, youll also have to modify the FAMILY attribute of the #TEMPLATE statement.
Once you know whether the Select button exists, you have to find the Close button control. This is a little tougher, because it might be there via the CloseButton control template, or it could be a regular button that calls the CloseCurrentWindow code template, or it may just be a button that calls POST(EVENT:CloseWindow) in hand-code. Consequently, our only option is to search for the text on the button. We can probably assume that it will be "Close", "Exit" or something like that (excluding an optional ampersand).
In fact, youve likely been pretty consistent throughout your APP, so we should be generally safe with making this assumption. Therefore, well specify this text as a global option called %mhCloseButtonText. The following code will find the button:
#FOR(%Control),WHERE(%ControlType = 'BUTTON')
#SET(%ValueConstruct, UPPER(EXTRACT(%ControlStatement, 'BUTTON', 1)))
#SET(%ValueConstruct, SUB(%ValueConstruct, 2, LEN(%ValueConstruct)-2))
#SET(%mhAmpersand, INSTRING('&', %ValueConstruct, 1, 1))
#IF(%mhAmpersand)
#SET(%ValueConstruct, SUB(%ValueConstruct, 1, %mhAmpersand-1) & SUB(%ValueConstruct, %mhAmpersand+1, LEN(%ValueConstruct)-%mhAmpersand))
#ENDIF
#IF(%ValueConstruct = UPPER(%mhCloseButtonText))
#SET(%mhCloseButtonControl, %Control)
#BREAK
#ENDIF
#ENDFOR
Of course, there are always situations where this wont work. For whatever reason, youve used different text on the Close button in one of your windows, and the global extension cant find it. Now what are we going to do? Well we could write a local template that does the same thing as the global one does. A better solution, however, is to have the local template tell the global template which button control to use.
To achieve this, we will utilize a frequently overlooked feature in CW templates: Local procedure templates can access prompts and declared tokens in global extensions. (Thats why Ive used unusual names with my global prompts and tokens, so that I dont conflict with other templates.)
To make this work, we must understand the order of generation. User hand-code is always inserted first, followed by global extensions code, then by any procedure template code. This means that the global extension can initialize variables at the start, then the procedure template gets a chance to override those before the global template uses them in a later embed. Ive found the best embed to use for the initializing and overriding is %GatherSymbols. Our global template does the following:
#AT(%GatherSymbols) #CLEAR(%mhCloseButtonControl) #ENDAT
The local template (if its populated into the procedure) does this:
#AT(%GatherSymbols) #SET(%mhCloseButtonControl, %mhLocalCloseButtonControl) #ENDAT
When it comes time for the global extension to generate the code into the %BrowsePrepSelectRecord embed, it can check to see if there is a value stored in %mhCloseButtonControl by the optional local extension. If not, then it goes looking for the control itself.
The only other thing of significance in our template is that the global extension can optionally change the buttons icon along with its text. There is also a global setting to prevent this icon assignment in the case where the local button didnt have an icon to begin with. Heres how its done:
#IF(%mhCancelButtonIcon AND (EXTRACT(%ControlStatement, 'ICON') OR NOT %mhCancelButtonSmartIcon))
%mhCloseButtonControl{PROP:Icon} = '~%mhCancelButtonIcon'
#ENDIF
Well, thats the extent of it. Now you have another example of a global extension template that generates code into multiple procedures. Youve also learned how to modify its operation with a local procedure template. This is a very useful technique for situations where you want some code to be placed "almost everywhere". It allows a great deal of flexibility, along with an ease of implementation.
Copyright © 1999-2008 by CoveComm Inc. All Rights Reserved. Reproduction in any form without the express written consent of CoveComm Inc., except as described in the subscription agreement, is prohibited.
Clarion Magazine ISSN 1718-9942
One year: $184
(includes all back issues since '99)
Renewals from $134
Two years: $274
Renewals from $224