![]() |
|
Published 1999-12-07 Printer-friendly version
TopSpeed's Clarion is arguably the most productive software development system available today, and the language, the development environment and the Application Builder Classes contribute to this productivity. Yet many software development systems have productive languages, development environments and class libraries. In the area of productivity, Clarion's templates are the single feature that sets Clarion above those other systems.
With the advent of Wizatrons, which are essentially a template integration system, templates are poised to present developers with another tremendous increase in productivity. However, if templates and Wizatrons are to provide the maximum advantage, it's imperative that developers be able to create their own templates and integrate them into the Wizatron system.
Many Clarion programmers seem to believe that template writing is an advanced skill, reserved for those who know the Clarion language inside and out, eat Windows API calls for breakfast, and leap tall buildings in a single bound. Actually, any Clarion programmer who has successfully added a few lines of code to an embed point is ready for the challenge of creating a simple template. Template writing is one of the most effective ways to increase your abilities and your value as a Clarion programmer.
Template development should become a standard part of your development cycle, rather than a separate activity. Add template writing to the development process by taking a few minutes any time you write any hand code to decide if the code would be useful as a template. If the answer is yes, write the template immediately, while the purpose and function of the code is still fresh in your mind. Do this even if you're on a tight deadline. If you really don't have the time, or the task is beyond your current template-writing abilities, make a few notes in your simple version of the template so that you can go back and add that functionality later.
The process of writing, implementing and testing a template takes a bit more time than writing the equivalent hand code, so the best approach is to develop the functionality in procedure embed points. Then convert those embeds to a template after the code is proven. In a recent discussion on the TopSpeed.Products.C5EE newsgroup, someone wondered how to color the background of the currently selected field on a window. One of the responses advised declaring the local variable LastField as a LONG, then placing the following three lines of code in the TakeSelected event of ThisWindow, at priority 4000.
LastField{PROP:Background} = COLOR:White
SELECTED(){PROP:Background} = COLOR:Blue
LastField = SELECTED()
This code simply changes the color of the field represented by LastField to white, changes the color of the currently selected field to blue, then saves the field equate value of the currently selected field as LastField. I thought this could be a useful addition to my toolkit, so I decided to test the code in a current project and see if I liked the results. For my initial testing, I chose a simple procedure in a small application, to keep the overhead to a minimum. I also have a testbed application that I use for developing more complex templates.
Embedding the code as instructed worked, but the result wasn't quite as expected. Three-D windows use colors for buttons and checkbox text, but this code turned those controls white as you tabbed past them. Even worse, during the first execution of the code, LastField was equal to 0, which forced the background of the window itself to white!
The code needed to address these issues to be usable with 3D windows. First, it needed to leave the background color of the window alone. Second, it needed to store the color of the control it was about to change, and restore that color to the control when another control was selected. Finally, I decided to change the variable name LastField, so that it wouldn't be confused with the LastField() function. I chose OldField as the replacement name.
The background color of the window could be protected by an IF test. Storing the color of the control about to be changed required a new local variable, OldColor, to store the color code. OldColor must be a LONG to store a color value. As in the initial example, this code should be placed in the TakeSelected event of ThisWindow, at priority 4000.
IF OldField
OldField{Prop:Background} = OldColor
END
OldColor = SELECTED(){Prop:Background}
OldField = SELECTED()
SELECTED(){Prop:Background} = COLOR:Blue
The IF test wrapped around the second line prevents the window color from being changed by skipping the background color assignment if OldField = 0, as it does when the first field on the window is selected. Zero is the control equate for the window itself.
Line four saves the color and line five saves the control equate of the currently selected field for later restoration. The sixth line colors the background of the currently selected field blue. These six lines of code will assure that all controls have their normal colors, except the currently selected control, which will have a background color of blue.
Problem solved. Move on to the next programming task, and just remember these six lines of code in case there's ever a need to color the background of the currently selected field on a window, right? WRONG! Take a little extra time, turn these six lines of code (and the two local variables... you'd forgotten them already, right?) into a template, and the next time you need the capability, you're only three mouse clicks from thought to implementation.
There are a number of template-writing tools available, including the Template Wizatron, that can be used to shorten the template writing process. Learn to use one, and your efforts will be even more productive. For this example, writing the template by hand will be more instructive.
Templates are simply text files with a TPL or TPW extension. When you register a template the registry always asks for a TPL file. But having all your templates in one big TPL can be unwieldy, so you have the option of creating TPW files and linking them to the TPL with the #INCLUDE statement. Because the Clarion IDE is still 16-bit, you should follow the DOS 8.3 file naming convention. For this exercise, use your favorite text editor to create a file called MYTPL.TPL in your CLARION5\TEMPLATE directory. This file will become the glue that holds your collection of templates together, and you'll develop and store each template you create in a separate TPW file.
#!This file contains my collection of custom templates
#TEMPLATE(MyTpl,'My Template Classes'),FAMILY('ABC')
#!SpotLite - Accent the currently selected field
#INCLUDE('SPOTLITE.TPW')
The first line of MYTPL.TPL is a template comment line (denoted by #!) that describes the purpose of the file. The second line uses the #TEMPLATE command to declare the template set. The third line specifies that the SPOTLITE.TPW file should be included in the processing of this template set. I decided to name the feature Spotlighting, because it provides a focus for the user, much as a spotlight helps the audience focus on the most important action on a stage.
After completing the MYTPL.TPL file, create the SPOTLITE.TPW file to contain the template itself.
Before writing the first line of any template you have to make a decision. What kind of template should this be? The simplest form of template, the #CODE template, embeds code in any embed point where it is used. This template needs to place code in specific locations whenever it's used, so a code template probably isn't the best choice.
#EXTENSION templates place code in embed points specified by the template writer, making an extension template the perfect fit for this task. #CONTROL templates are just extension templates that include one or more controls. Since there's no control associated with this task, a control template isn't needed. The fourth type of template, the #PROCEDURE template, is for creating complete procedures, rather than additions to existing procedures, so the initial choice of an extension template is correct. An extension template requires a template name and description. Once you've decided on those you can write the #EXTENSION statement.
#EXTENSION(SpotLite,'Accent the currently selected control')
A template's primary purpose is to generate source code during the code generation phase of development. To do this successfully, the template also needs to tell the developer what it can do, and ask the developer any questions it needs answered to perform the task. To determine the questions that should be asked of the developer during the design phase, review the code developed in the test procedure for options that could be left to the developer. The spotlight color is an option that should certainly be left to the developer. You can get this information using the #DISPLAY and #PROMPT statements.
#DISPLAY('This extension will set the color of the')
#DISPLAY('currently selected control to a developer-')
#DISPLAY('specified value and return the control to ')
#DISPLAY('the original color when a different control ')
#DISPLAY('is selected.')
#PROMPT('Selected Field Color',COLOR),%SelectedColor
The series of #DISPLAY statements tells the developer what the template is designed to accomplish. The #PROMPT command displays a prompt, accepts an input and stores the value selected for later use. In this version, the template displays 'Selected Field Color' as the prompt. The COLOR keyword used as an input parameter displays the standard Windows color dialog and allows the developer to select a color. The color selected by the developer is placed in the %SelectedColor variable.
Now that the developer is up to speed about the purpose of this template, and the template has collected the information it needs to do the job, it's time for the code generation part of the template. Code needs to be generated in two places. First, there are two local variables that need to be declared. After the variables are declared, the actual working code needs to be placed in the TakeSelected method of the WindowManager.
In the template language, you place code in a certain embed by wrapping the code in an #AT...#ENDAT phrase. The two local variables need to be placed in the Data Section of the procedure.
#AT(%DataSection) OldField LONG !for use by SpotLite template OldColor LONG !for use by SpotLite template #ENDAT
The OldField and OldColor lines are the first lines of code in the template that don't start with a # character. Any line in a template that starts with a # is considered template code. If there is no leading #, the line is considered source code to be generated based on the surrounding template code. These four lines of code create the OldField and OldColor variables, both LONG data types, in the data section of the procedure.
Finally, the template needs to generate the actual field color control code in the TakeSelected method of the WindowManager. You can determine the #AT location for this block of code by examining the ABC templates, or by using the Template Wizatron to generate a skeleton template from the test procedure. In a future article I'll use the template wizatron to demonstrate ways to speed up template development.
#AT(%WindowManagerMethodCodeSection,'TakeSelected','(),BYTE'),PRIORITY(4000) IF OldField OldField{Prop:Background} = OldColor END OldColor = SELECTED(){Prop:Background} OldField = SELECTED() SELECTED(){Prop:Background} = %SelectedColor #ENDAT
The actual embedded code is essentially identical to the code in the test procedure. The only difference is in the line SELECTED(){Prop:Background} = %SelectedColor. This assigns the color selected by the developer as the background color of the currently selected field. The test procedure code assigned COLOR:Blue to the control.
When the template development process is complete, save and register the template, then comment out the hand code in the test procedure and add the template to the procedure. Generate, compile and test for proper final results, and you have a tool that will be in your toolkit many projects from now, instead of some code in an embed point somewhere that you don't know you could find if you really needed it again.
One handy feature of the template approach to software development manifests itself nicely in this example. Instead of placing this template on one procedure and having it apply to that procedure, you can also place it as an extension template in the Global section of an application, and it will be applied to every procedure in the application. You don't need to make any changes to the template! If you do this, spotlighting the current field in every procedure in your application is only three mouse clicks from thought to implementation.
Using the techniques in this article, plus some advanced study of the #PROMPT and #AT template commands, you can begin turning that 'write once and forget it' hand code into useful tools for future development projects.
Create and register the template, add it as a global extension to your favorite project, then take a look at a few of the browses and forms to see what an impact this simple tool can have on the appearance of any application.
Click here for the complete source code.
Don Childers has been programming in Clarion since 1988. He was a co-author of Tips, Tricks and Templates for CDD and published a set of add-on templates for early versions of Clarion for Windows.
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