Professional Documents
Culture Documents
The Getting Started section briefly introduced you to Clarion programming at its
highest level. Learning Clarion now teaches you how you can use all the rest of the
tools Clarion provides to create real-world applications. It contains two parts, on two
very different levels:
• A series of Application Generator lessons, which familiarizes you with all the tools
in the Clarion development environment.
• A Clarion Language lesson, which introduces the Clarion programming language
and familiarizes you with the type of code generated for you by the development
environment.
Lessons 1 through 12 introduce all the main Clarion development environment tools.
We start at the application planning stage, walk you through creating the data
dictionary with the Dictionary Editor, and then walk you through creating a complete
application with the Application Generator. By the end of these lessons you’ll have
created a complete order-entry application.
You’ll use the Application Generator and work with Procedure, Control, and Code
templates to produce an Order Entry application. You’ll work with the Window
Designer to design windows. You’ll work with the Report Designer to design reports.
You’ll use the Text Editor to embed Clarion language source code into the code
generated by the templates.
Lesson 13 introduces the Clarion programming language at the hand-coding level. It
starts at "Hello World", then walks you through creating simple forms of the most
typical types of procedures used in Clarion applications, all while explaining the
functionality of the hand-code you’re writing and relating it to the type of code you’ll
see generated for you by the Application Generator.
Please review the section entitled "Anatomy of a Database" in the help file. We
covered it in the Introduction to Clarion blog entry, where you read it in the
"Learning Clarion" PDF file. This time it's in the help file, so it should already be
familiar territory.
In summary:
• A database is a collection of information (data) in a system of columns (fields),
rows (records), and tables.
• Columns (fields) can store many different types of data, but each individual
column is specified to hold only one type.
• Each data item should be stored in only one place.
• One or more columns makes up a row. One or more rows make up a table. A
collection of related tables make up a database.
• Clarion programs can access many different table systems through the use of table
drivers.
• Keys and indexes declare sort orders other than the physical order of the rows
within the table, and can contain more than one column, allowing sorts within
sorts.
• Range Limits enable you to process a subset of rows, which reduces processing
time.
• Tables are related through common columns containing identical data, which
allows you to eliminate redundant data.
• Table names and column (field) names should be singular, not plural, in almost
all cases.
• A primary key is a column, or combination of columns, that uniquely identifies
each row in a data table.
• A foreign key is a column, or combination of columns, in one table whose value
must match a primary key’s value in another related table.
Referential Integrity
Referential Integrity refers to the process of checking all updates to the key column in
a given table, to ensure that the validity of parent-child relationships is correctly
maintained. It also refers to ensuring that all child table rows always have associated
parent rows so that there are no "orphan" rows in the database.
Because the data for a given transaction resides across several tables, this application
must enforce referential integrity. This is critical, yet many database application
development tools require you to hand code procedures to enforce this. The
Application Generator’s templates implement this automatically in your generated
source code when you select a few options in the Data Dictionary.
It is essential that the application does not allow an update to a row that leaves a
blank or duplicate value in a primary key column. For example, we need to restrict
the ability of the end user to update a row in a way that could cause a duplicate
Customer number. If two different companies shared a duplicate Customer number,
you could send a bill to the wrong company.
In my opinion, this diagram is a confusing mess. The boxes are too close together,
and the arrangement is jumbled, so it takes a while to figure out what is related to
what. Also, some table names are in ALL CAPS and many are plural words, which
makes things confusing.
If you are going to spend time designing the database correctly, spend a little more
organising the data diagram properly too. Try to put the "one" tables on the left, and
the "many" tables on the right. This immediately makes everything more easy to read.
Sloppy thinking leads to errors: always aim to be clear and precise.
This diagram shows more clearly what is going on. I have highlighted the Primary
Key (PK) columns in yellow. This immediately shows up two design flaws in the
database design.
Firstly, there is no PK in the Phone table. Secondly, the Detail table has problems. If
we make the PK OrderNumber + ProdNumber, then we can only sell the product
once in a given order. What about selling 10 items at the special price and 2 items at
the standard price? Also, the Products will not show up on the Order in the same
sequence as ordered by the Customer. This could cause problems in the real world. It
is best to resolve these issues at the design stage, and not wait until after you have
shipped the application to the customer.
The schematic above provides an overview of the entire database. If you look at it
from the perspective of the sales agent taking a phone order, the Order table stores
who’s ordering, the Detail stores what they’re ordering, and the Customer and
Product tables store constant information about the customers and products.
The item code looks up the description and price. The customer code looks up the
customer’s name and address. Other data, such as the transaction date, fill in
automatically (by looking up the system date, for example).
The lessons will create a brand new data dictionary, and you will copy and paste the
tables that Getting Started defined for you into the new dictionary.
As for the actual application you create, because the lessons are a teaching tool more
concerned with showing what Clarion can do for you, it won’t create a full-scale order
entry system. However, you will find that some parts of the application will be very
"showy," so that you can quickly learn how to do equivalent procedures in your
applications.
Application Interface
The next major task before coding is to plan the user interface. For a business
application like this, it’s crucial that a salesperson quickly locate the data they need,
so that they can record the sale and move on to the next phone call. Therefore, the
application should put all the important data "up front" by default, and no entry or
maintenance dialog should be more than a button or menu command away.
Additionally, the company uses many other Windows applications; therefore, it’s
especially important that the application have a standard Windows "look and feel."
End users learn a familiar interface more quickly.
To implement the tasks the application must execute consistently with our
guidelines, we can plan for the items listed below. Though the following is no
substitute for a real program spec, it should suit us for the upcoming lessons.
• Because the application will handle the maintenance for the customer, item, and
billings tables on different forms, the Multiple Document Interface (MDI) is
necessary.
• The application should have a toolbar with buttons to load forms and browse
windows, and to control the browsing behaviour.
• To maintain a consistent "look and feel," the main menu choices will be File, Edit,
View, Window, and Help. The File menu accesses the printing and exit procedures.
The Toolbar buttons call the form dialogs for editing a current row (if highlighted
in a browse) or adding/deleting rows, and for browsing through the tables. The
Browse menu calls the procedures for browsing tables. Window and Help perform
standard actions.
• When adding new orders, the sales people should be able to pick customers and
products from scrolling lists. Pertinent data in the order dialog (addresses, item
descriptions and prices) should automatically "fill in" as appropriate.
Make sure that "Select Server" says "Clarion Dictionary". Click on the ellipsis button
next to the "Select Dictionary" field, and navigate to the "Getting Started" folder to
find "GSLesson.dct". Click "Open".
Click on the "Add All" button, and then click "Finish". All the table and field
definitions are imported, including their relationships. By contrast, using
the Windows clipboard to copy and paste doesn't transfer the relationships, only the
table structures.
Note that in the Properties section of the Customer table, the "Full Path Name" is still
showing the old "Getting Started" path. Just remove this name entirely.
Last time we changed the description fields for each column to mixed case. This time
we are going to change the Column Names as well, as shown above.
When you get to the "State" field, go to the "Attributes" tab as well, and choose
"Uppercase" and set the initial value to 'FL', including the quote marks. This will
default the state code to Florida for new customers. Don't forget to change the case of
the "ZIPcode" field too. Then click on the "Save" button.
The initial value in this case is a string, hence the single quotes. You can also use
variable names (Preceed with an exclamation mark) or functions.
Repeat the process of removing the full Path Name and changing the case of the
column names in the "Order" table, as shown above. Then go to the "OrderDate" field
and set the "Initial Value" as the function TODAY() as shown.
Click on the green "Save and Close" button to return to the "Start Page".
Instead of clicking on the LCLesson file, click on the path to the right, as shown. This
will open the "LearningClarion" folder.
Click on the "Add Tables" button and fill in the details as shown for the Phone table. The
prefix is automatically derived from the first 3 letters of the table name. Since the other
prefixes are all in caps, change this one to caps too.
The prefix is one way to uniquely identify columns of the same name in different tables. For
example, PHO:CustNumber is the CustNumber column in the Phone table, while
CUS:CustNumber is the CustNumber column in the Customer table. You can also uniquely
identify columns by using Field Qualification syntax (discussed in the Language Reference).
Click on the "Comments" tab and add in some further information about this table:
"Customer Phone table". Comments are a helpful way of documenting your data structure. A
description of what the table is for can be very helpful for when you return to the table for
maintenance programming. Click "OK" to add the table.
Use the "Add Table" button to create the Detail table. Because it is a child table, it is
sometimes useful to include the parent table name in the description, as shown here: "Order
Details". We do not have to stick to the default prefixes, and they are not restricted to 3
letters. You could change it to "DTL" as shown, but we will leave it as "DET" for future
lessons. Click "OK" when done.
Use the "Add Table" button to create the Product table as shown. Leave the prefix as "PRO".
Click "OK" when done.
Save your work using the "Save" button.
Click on the "Pools" folder, and use the "Add Pools" button to create "ColumnPool" with a
prefix "POOL" as shown above. Click "OK". Pools do not generate any code.
We are going to copy the pool columns from their existing tables and add them to the pool by
pasting them.
Select the CustNumber field, right-click and choose "Copy".
Return to the "CustNumber" field in the "Customer" table, and fill in the "Derived From" field
as "POOL:CustNumber". Then click on the ellipsis button to the right.
The Set Freeze Checkbox dialog pops up. Press "Don’t Freeze" to allow changes in the
Derived Column to cascade changes in the ColumnPool column if they are made.
This means that the CUS:CustNumber column is now derived from the POOL:CustNumber
column. The term "derived from" means that the POOL:CustNumber column’s definition is
the "parent" and all "children" columns which are "derived from" that column automatically
share all the attributes of the parent.
Deriving column definitions from existing columns gives you the ability to make changes in
only one place, then cascade those changes to all derived columns. For example, if the
definition of the CustNumber column needs to change in all tables using it, simply make one
change to the POOL:CustNumber column definition, then cascade that change to all the
derived CustNumber columns in all tables.
Go to the Order table, click on "CustNumber", click on the ellipsis next to the empty "Derived
From" field. This will allow you to choose any field, so go to the "ColumnPool" and choose
"CustNumber". Click "Select", and "Don't Freeze".
Return to the "ColumnPool" and click the "Add" button. Fill in the details for OrderNumber
as shown. These are a bit different from the original "Order" table, so pay close attention.
(Alternatively, you could use the ellipsis button to "import" the details from the "Order"
table's "OrderNumber" column, and then remove the "Derived from" information and change
the Data Type to "LONG" and the Screen Picture to "@n_6".) Click "OK".
Fill in the details for "ProdNumber", which we will use as the PK in the "Product" table, as
well as part of the "Detail" table. Click "OK". You should get an empty "Column Properties"
dialog box. We don't need to add any more columns to the pool, so click "Cancel".
Go to the "Order" table and click on the "OrderNumber" column. Note how its current Data
Type is "SHORT" and its Screen Picture is "@n-7". Click in the "Derived From" field and type
in "POOL:OrderNumber" and press the Tab key. Choose "Don't Freeze". Notice how the Data
Type and Screen Picture have both changed to the properties of POOL:OrderNumber. Click
on the "Save Current File" button (Ctrl-S).
The Phone Table
Click on the "Phone" table, then on the "Columns" folder. Click the "Add" button and then the
ellipsis button to the right of the "Derived From" field. Choose the "CustNumber" field in
the column pool and click "Select", "Don't Freeze", "OK".
The next field is "PhoneType" and is a string field of 6 characters. It will contain words like
"Home", "Work", "Fax", "Cell", etc.
In the "Attributes" tab, set the "Initial Value" to 'Home' (including the single quotes) to create
a default value.
Go to the "Validity Checks" tab, click on "Must be in List" and type in the following in both
the "Values" and "Choices" fields: "Home|Work|Fax|Cell|Mobile|Other". Each choice is
separated by a vertical bar, and the user will select from a list.
When you specify a "Must be in List" option, the default window control for the column is an
OPTION structure with RADIO buttons. These appear by default in the Window Controls list.
In the "Controls" tab, change the "Control Type" to "LIST". Click "OK".
Choose "Must be True or False" in the "Validity Checks" tab. Set the True Value to 1 and the
False Value to 0. The default control is a checkbox. Click "OK".
Type "PhoneNumber" in the "Column Name" entry. The "Data Type" is "STRING" of 20
Characters (to allow for the occasional international number). Type in "@P(###)###-
####P" (without the quotes) in the "Screen Picture" field. This will set a "mask" for domestic
US phone numbers. We’ll use the OutsideUSA column in our application to change the
picture later to accommodate international phone numbers. Click "OK".
The last column for this table is "Extension", and it is 10 characters long. Click "OK", then
"Cancel". Remember to save your work.
The Detail Table
Go to the "Detail" table, click on "Columns", click the "Add" button, then the ellipsis on the
right of the "Derived From" field, and select "OrderNumber" from the column pool and
click on "Select", "Don't Freeze", "OK".
Click on the ellipsis button and choose "ProdNumber" this time. Click "Select", "Don't
Freeze", "OK".
Create the column "Quantity" as a "SHORT" data type (Two byte signed integer, with a
range from -32,768 to 32,767) and click "OK".
Create the column "ProdAmount" as a "DECIMAL" data type with 5 characters and 2
decimal places. Click "OK".
Create the column "TaxRate" as a "DECIMAL" data type with 2 characters and 2 decimal
places. Click "OK", then "Cancel". Save your work.
Go to the "Product" table, click on "Columns", click the "Add" button, then the ellipsis on the
right of the "Derived From" field, and select "ProdNumber" from the column pool and click
on "Select", "Don't Freeze", "OK".
Create the column "ProdDesc" as a "STRING" data type with 30 characters. Click "OK".
Create the column "ProdAmount" as a "DECIMAL" data type with 5 characters and 2
decimal places. Click "OK".
Create the column "TaxRate" as a "DECIMAL" data type with 2 characters and 2 decimal
places. Click "OK", then "Cancel". Save your work (Ctrl-S).
Make a backup of your work.
The columns in the "Order" table that relate to other tables in the database are the
"OrderNumber" and "CustNumber" columns. The "CustNumber" column relates to the
"Customer" table (purple line above). There will be duplicate values in the CustNumber
column of the "Order" table that relate to rows in the "Customer" table. The key we defined in
the "Order" table is a foreign key. The "Customer" table key does not allow duplicates and
nulls, and was defined as the primary key for that table.
Multiple "Order" rows can exist for each "Customer", making this a Many-to-One
relationship: the "Order" table is the child of the "Customer" table. The "Order" table was
imported from the Getting Started lessons, and there are two keys already defined that satisfy
the above requirements. They are both called "KEYCUSTNUMBER"
Now let's turn to the relationship shown in red in the first graphic. The "OrderNumber"
column is also the primary key (PK) of the "Order" table. Each Order row is uniquely
numbered by the "OrderNumber" field, irrespective of which customer placed the order.
Similarly, every row in the "Detail" table must belong to one and only one "Order". So we can
say that the "OrderNumber" column relates to the "Detail" table. There should be no
duplicate or null order numbers in the "Order" table; this is a primary key.
There may be multiple "Detail" rows for a single matching OrderNumber. Therefore, this is a
One-to-Many relationship: the "Order" table is the parent of the "Detail" table.
Also, there is a relationship between the "Product" table and the "Detail" table (blue line).
The "ProdNumber" column relates to the "Product" table. There will be duplicate values in
the "ProdNumber" column for the rows in the "Detail" table because a product can be sold
multiple times on different orders, assuming there is sufficient stock. There may be more
than one Detail row containing a single ProdNumber. Therefore, this is another Many-to-One
relationship, with the "Detail" table the child of the "Product" table.
Defining the Keys
The "Order" table already has the "KEYORDERNUMBER" (primary) key, but we need to
define keys for the "Product" and "Detail" tables. Let's start with the "Product" table.
Select the "Product" table, click on "Keys", and then the "Add" button. The key label is
"KEYPRODNUMBER", and it must be unique, and it is the primary key, as shown. Then click
on the "Add Field" button and choose "ProdNumber". Click on "Select", "Cancel", "OK",
and "Cancel". We now have a primary key for "Product".
Select the "Detail" table, click on "Keys", and then the "Add" button. The key label is
"KEYORDERNUMBER", and its description is "Order Number", as shown. Then click on the
"Add Field" button and choose "OrderNumber". Click on "Select", "Cancel", "OK".
The second key is "KEYPRODNUMBER" and its description is "Product Number". Click on
the "Add Field" button to choose "ProdNumber". Click "Select", "Cancel", "OK", and "Cancel".
Select the "Phone" table, click on "Keys", and then the "Add" button. The key label is
"KEYCUSTNUMBER", and its description is "Customer Number", as shown. Then click on
the "Add Field" button and choose "CustNumber". Click on "Select", "Cancel", "OK", and
"Cancel".
The "Detail" table is a child of both the "Product" table and the "Order" table. Select the
"Detail" table, click on "Relations" folder, then the "Add" button to add a new relationship.
Select "MANY:1" and choose "KEYPRODNUMBER" as the foreign key. Choose "Product" as
the parent table, and "KEYPRODNUMBER" as the primary key. Choose "Map by Name" and
"Restrict" for the "On Delete" constraint. You can't delete a product that has already been
ordered. Choose "Restrict" for the "On Update" constraint. You can't change a product
number for a product that is already ordered. Click "OK".
The "Detail" table is also the child of the "Order" table, so select "MANY:1" and
"KEYORDERNUMBER" as the foreign key, and "Order" as the parent table, and
"KEYORDERNUMBER" as the primary key. Choose "Map by Name" and "Cascade" for the
"On Delete" and "On Update" constraints. We must delete all the related Detail rows if the
Order is deleted. Click "OK", "Cancel".
Save your work using the "Save" button on the main toolbar.
Select the "CustNumber" column in the "Order" table. Click on the "Validity Checks" tab and
select the "Must be in Table" option. The IDE has selected the "Customer" table from the
drop-down box for "Table Label". Click "Save".
Select the "OrderNumber" column in the "Detail" table. Click on the "Validity Checks" tab
and select the "Must be in Table" option. The IDE has selected the "Order" table from the
drop-down box for "Table Label".
Click on the "ProdNumber" column and agree to save the changes. Click on the "Validity
Checks" tab and select the "Must be in Table" option. The IDE has selected the "Product"
table from the drop-down box for "Table Label". Click "Save".
Now do the same for the StateName field in the State table, as shown.
Save all your work by using the green "Save and Close" button on the DCT Explorer toolbar.
Make a backup of your work.
This follows on from Learning Clarion (Part 2). Now that we have finished defining the data
dictionary, we will import some data into the tables.
In the "Select Server" entry, choose "Comma Delimited Files (Basic)" from the dropdown list.
Press the ellipsis button to the right of the "Select Dictionary" entry to open the "Select
Database" dialog. Alternatively, you can also press the "Next" button.
Select
"C:\Users\Public\Documents\SoftVelocity\Clarion11\Lessons\LearningClarion\IMPORT1.C
SV" then press the "Open" button.
In the "Driver Options" field, press the ellipsis button to open the "BASIC Driver String
Builder" dialog. Locate and check the "First Record is Header" check box. Press the "OK"
button to close the "BASIC Driver String Builder" dialog.
Once the wizard has finished, notice the "IMPORT1" table definition has been added to the
dictionary.
View the imported data
Right-Click on the IMPORT1 table, select "Browse Table" to call the Database Browser. Or
press the "Browse" button on the DCT Explorer toolbar (highlighted in blue above).
The Clarion Database Browser allows you to directly edit the data in your tables. This is a
programmer’s tool, designed to allow you to do whatever is necessary to change the actual
data contained in your tables. This means that there are no safeguards against violating your
database’s Referential Integrity or Data Integrity rules. Therefore, you must take care when
you use this tool.
In this lesson, we do not need to make any changes at this time, and simply wanted to
identify this IDE tool if needed. Close the Database Browser at this time and return to the
DCT Explorer in the already opened GSLesson dictionary.
The "Customer" data file doesn't exist yet.
Right-click on the "Customer" table name and select "Browse Table".
Select the "IMPORT1" table as the data source and click "Select". The table that you
select first in the conversion program process is always the target. In our example, the
"Customer" table is the target, and "IMPORT1" is the source.
The "Select New Project File" dialog appears. Accept the default name (convert.cwproj), and
the default LearningClarion folder name, by pressing the Save button. Project files determine
what source file to compile, and how to build (link) it. The default extension of project files is
*.cwproj.
Select the target table file (Customer.TPS) and click "Open".
Another dialog pops up that asks you if you would like to load the data conversion program.
Press the "Yes" button. The wizard completes, but the conversion project is not opened (No
idea why). We will return to the conversion program shortly.
The best reason to generate Clarion source code for the data conversion is to provide you the
opportunity to modify the code before you compile and execute it to handle any special data
conversion needs you may have. This makes the conversion process completely flexible to
handle any situation that can occur.
From the "View" menu, select "Solution Explorer". If it doesn't open, click on the tab on the
far right, shown by the red arrow. Expand the project to find the "Convert.clw" generated
code, and click on the "Open" button (or double-click on the file name).
Clarion’s Text Editor appears with the file loaded, ready to edit. The Dictionary Editor
created the conversion program code in this file. This contains all the Clarion language
source code necessary to read the data from the BASIC (.CSV) file and copy it to the
TopSpeed table.
Scroll down until you find the routine called "AssignRecord". Change the line that reads
CUS:State = Source:State
to read:
CUS:State = UPPER(Source:State)
This will make sure that all the 2-letter state codes are converted to upper case during the
transfer.
Also, comment out the two lines in the previous routine by using the exclamation point (see
above). For some reason, the generated code tries to copy the Customer file over itself, and
then deletes the original file, so the data is lost.
Click on the "Save" button on the main toolbar to save your work.
Check it out
Now you can check the data in the new file by opening it with the Database Browser and
browsing through the records.
In the "Open TopSpeed File" dialog, press the ellipsis button, and select the
"CUSTOMER.TPS" file, then press the "Open" button, then click "OK".
This demonstrates another way to open the Database Browser, other than from within the
Dictionary Editor.
There are two obvious problems with this data. The first record is not data at all, but the
names of the column headings. The other problem is that we can't see the ZIP codes.
Click on the "Format Columns" button, select "CUS:ZIPCODE" from the drop-down list and
apply the "@P#####P" picture that we used in the Getting Started exercises. Click on "Apply
Formatting" and "Close". Now we can read the ZIP code data.
Notice that the data is sorted by default according to the first column. The first row
(CustNumber = 0) is meaningless. It came from the "Header" record of the CSV file, so it's
time to delete it. Click on the "Delete record" button at the bottom of the window. Then click
on the "Save" button and close the file.
Make a backup of your work.
In the resulting dialog box, type in "LCLesson" for the application name, and remove the
check mark from "Auto create project subdir". Click on the "Create" button.
Type in the dictionary file name "LCLesson.dct" or use the ellipsis button to select it. Remove
the check mark from "Application Wizard" because we are going to do some steps manually
instead. Click the "OK" button.
Click on the "Main" procedure object, and then on the "Properties" button.
Click on the "Defaults" tab, and choose the "Default MDI Frame" template. Click "Select".
The Procedure Properties dialog appears. It defines the functionality and data structures for
the procedure. Press the "Actions" button.
In the "Main – Properties" dialog, type "SplashScreen" in the Splash Procedure field, then
click "OK". This names the procedure containing an opening screen that will appear for just a
brief period when the user first opens the application.
The Window Designer appears. Here are all the tools that allow you to visually edit the
window and its controls. On the far right, you should see the "Properties" pad. Click on it (red
arrow above) to display the application properties. Find the "Title" property and click in the
field to change it to read "LCLesson Application" as shown. (If the properties pad isn't
available, you can select it from the "View" menu.)
Click on the "Paste" entry because we want to insert another menu below it. Then click on the
"Add New Menu" button.
Use the "Menu Down" button to move it to the main menu level, and then change the text of
the menu to read "&Browse" as shown. This defines the text that appears on the menu to the
end user. The ampersand (&) indicates that the following character (B) is underlined and
provides keyboard access (the user can press ALT+B to drop down this menu).
Press the "New Item" button (or press INSERT). This adds a new menu item — a command
on the dropdown menu — under "&Browse".
Type "&Customers" in the "Text" field then press TAB three times.
Type "?BrowseCustomer" in the "Use" field. (This is an equate for the menu item, so code
statements can reference it. The leading question mark (?) indicates it is a field equate label.)
Next, click on the "Actions ..." button.
For the "When Pressed" setting, choose "Call a Procedure" from the drop-down list.
Type in "BrowseCustomers" in the "Procedure Name" field. This names the "ToDo"
procedure for the Application Tree. Enable the "Initiate Thread" checkbox. Click "OK". The
BrowseCustomers procedure will display an MDI "child" window, and you must always start
a new execution thread for any MDI window called directly from the application frame. The
Thread Stack field defaults to the minimum recommended value.
Press the "New Item" button. Type "&Orders" in the "Text" field and press TAB three times.
Type "?BrowseOrders" in the "Use" field. Click on the "Actions ..." button.
In the Actions dialog, choose "Call a Procedure" from the "When Pressed" drop-down list.
Type "BrowseOrders" in the "Procedure Name" field.
Check the "Initiate Thread" box.
Press "OK" to close the ?BrowseOrders Prompts dialog.
Before we leave the menus, click on the top line "MENUBAR" and change the font to "Segoe
UI"
Press the green "Save and Close" button to close the Menu Editor.
Press the green "Save and Close" button to close the Window Designer.
Press the green "Save and Close" button to close the Procedure Properties dialog.
This returns you to the Application Tree dialog. There are now three new procedures
marked as "(ToDo)": "BrowseCustomers", "BrowseOrders", and "SplashScreen". These were
the procedures you named in the Menu Editor. Choose "Save" from the "File" menu, or press
the "Save" button on the toolbar.
Select the "SplashScreen" procedure, click on the "Properties" button, go to the "Defaults"
tab, choose the "Default Splash Window" template. Click on "Select".
We will use all the defaults for now. Just type in "Startup Splash Screen" in the "Description"
field. If you are like me and want to fiddle with the fonts and layout, click on the "Window"
button. Use the green "Save and Exit" buttons to return to the Application Tree. Save your
work.
Click on the "Main" procedure, and then on the "Window" button to the right. This opens the
Window Designer directly.
For some inexplicable reason, every time I open the Window Designer, the "Control
Templates" pad blocks part of the screen. The quickest way to remedy this is to click on the
"Properties" pad on the right. See the red arrow above.
Open the "Toolbox" pad, and use the "pin" button to pin it in place on the left. This will move
the window over to the right, so you can see it properly.
Scroll down and drag the "TOOLBAR" control onto the frame, and let go just underneath the
menu bar. This will put the toolbar control in place. Next, we need buttons on the toolbar.
Scroll up the list of Clarion Window Controls to find the "BUTTON" control. Drag and drop a
button onto the toolbar control, as shown. Move the button to the left of the toolbar, just
below the "File" menu. Also, adjust the height of the toolbar, so the button fits nicely.
Press F4 to display the button properties. Use the delete or backspace buttons to remove
"Button1" in the "Text" property field. Do not use the spacebar to replace the text with a
space. There should be no text at all. Change the "Use" property to "?CustomerButton".
Find the "Icon" property and from the drop-down list click on "Choose File ...". The Select
Icon File dialog appears. Select "GIF Files" from the Files of type drop-down list. Select the
"CUSTOMER.GIF" file, and click on "Open".
Scroll down to find the "Tip" property and type in "Browse Customers". This adds a tool tip to
the button that will display whenever the mouse cursor hovers over the button. Find the
"Flat" property and toggle it to "True".
Scroll down to the "AT" property and expand it. Remove the check mark from the Height and
Width's "Default" property. Next, set the "Value" property of the height to 14, and for the
width set the "Value" property to 16.
At the bottom of the "Properties" pad, click on the "Actions ..." link.
For the "When pressed" action, select "Run a Procedure". Choose "BrowseCustomers" from
the drop-down list. This is the procedure name you typed for the "Browse" menu
"Customers" menu item. Pressing the button will call the same procedure. Often, a command
button on a toolbar serves as a quick way to execute a menu command. Remember to enable
"Initiate Thread" as before. Click "OK".
Press F4 to display the button properties. Use the delete or backspace buttons to remove
"Button2" in the "Text" property field. Do not use the spacebar to replace the text with a
space. Change the "Use" property to "?ProductButton".
Find the "Icon" property and from the drop-down list click on "Choose File ...". The Select
Icon File dialog appears. Open the "PRODUCTS.GIF" file. Find the "Tip" property and type in
"Browse Products". Toggle the "Flat" property to "True".
Normally, you attach an action to the button at this point. Skip this step for now, for this
button only. Later, we’ll copy a procedure, then call it at the point in the generated source
code which handles what to do when the end user presses the button to demonstrate
using embed points.
Press F4 to display the button properties. Use the delete or backspace buttons to remove
"Button3" in the "Text" property field.
Change the "Use" property to "?OrderButton".
Find the "Icon" property and from the drop-down list click on "Choose File ...". The Select
Icon File dialog appears. Open the "ORDERS.GIF" file. Find the "Tip" property and type in
"Browse Orders". Toggle the "Flat" property to "True". Click on the "Actions ..." link.
For the "When pressed" action, select "Run a Procedure". Choose "BrowseOrders" from the
drop-down list. Remember to enable "Initiate Thread" as before. Click "OK".
With the "Orders" button still selected, Ctrl-Click on the "Customers" button. This gives both
buttons "handles". With both buttons still selected, Ctrl-Click on the "Products" button.
Ctrl-Click is the "multi-select" action that allows you to perform actions on several controls
at once. Once multiple controls are selected, you can move them all by dragging on any one of
the selected controls, or you can use any of the Alignment menu’s tools on the entire group.
Now all three buttons have "handles", and the "Orders" button has the white handles that
indicate it has focus and is the "key control" for the alignment actions. Click on the "Align
Tops" button on the toolbar above, as shown. Notice how all the buttons have the same top
position as the key control. Drag the group up to the top of the toolbar control, so they fit
under the menu bar.
Click on the "Spread Horizontally" button to space the buttons evenly. Another way to do this
is to right-click on the group, select "Format", "Horizontal Spacing", "Make Equal".
If the buttons are too far apart, use "Format", "Horizontal Spacing", "Decrease" to move them
closer together. Make sure they don't overlap! We need more space to the right for what is
coming next.
At the bottom left of the screen, click on "Control Templates". Then click on the toolbar
control to make sure it is selected. Choose the "FrameBrowseControl" template, then drag
and drop just to the right of the 3rd toolbar button.
Move the buttons into position so they are neatly placed.
Select the toolbar control again, and change the AT Height value to 20 so all the controls are
neatly positioned on the toolbar. Click on the green "Save and Close" button to save your
work.
Testing an Application under Development
Before you test your application, always save your work. Click on the "Save" button. Then
click on the "Start Without Debugger" button to Generate Source, Build and Run your
application.
The Application Generator generates the source code, displaying its progress in a message
window, procedure by procedure. Next, the Output window appears, showing you the
progress of the build as the compiler and linker do their work. Then your Application window
appears.
Press one of the buttons on the toolbar, or choose one of the items on the Browse menu. A
message box appears that says, "The Procedure (procedure name) has not been defined".
This capability allows you to incrementally test your application, whether you have designed
all the procedures or not. You’ll fill in their functionality, starting in the next lesson (Creating
a Browse).
Press the OK button to close the message box. Choose "Exit" from the "File" menu to close
the LCLesson application.
Throughout the rest of this lesson, feel free to Make and Run the developing application
whenever the lesson instructs you to save the file.
Look at the Generated Source Code
Let’s take a quick look at what the Application Generator has done for you. The whole
purpose of the Application Generator (and its Templates) is to write Clarion language source
code for you. There is no "magic" to what the Clarion tool set does to create applications; it
all goes back to the Clarion programming language.
With the Application Tree dialog open, change the "Tree Mode" drop list to "Module". This
changes your view of the application from the logical procedure call tree to the actual source
code modules generated for the application.
Highlight the "LCLesson.CLW" module, right-click to display the popup menu, then select
"Module Source File".
This takes you right into the Text Editor, looking at the last source code you generated (the
last time you pressed the Run button). Any changes you made since the last time you
generated code will not be visible here.
The "LCLesson.CLW" file is the main program module for this application, containing the
Global data declarations and code. Don’t be intimidated looking at all this code, we will
discuss Clarion Language code in a subsequent lesson.
When you have finished looking at the code, go to the "File" menu, choose "Close", "File" to
exit the Text Editor and return to the Application Generator (You can also simply press the
Close button "x" in the source window).
With the Application Tree dialog open, change the "Tree Mode" drop list back to "Procedure".
Right-click on the "Main (Frame)" procedure and select "Module Source File".
This takes you into the Text Editor again, looking at the last source code you generated for
the Main procedure. Again, any changes you made since the last time you generated code
will not show up in this code.
You may have noticed that right below the "Module Source File" selection was another called
"Embeditor Source". Do not confuse these two, they do very different things. We will
demonstrate the Embeditor Source selection later in the lessons.
If you do make any changes to this code, you actually can compile and run the program to see
what effect the changes make, however, your changes will be lost the next time you generate
source code. Therefore, it is not a good idea to make any changes here. Close the source
window. Save your work.
Make a backup of your work.
This follows on from Learning Clarion (Part 3). First, we are going to create a browse
window, and then an update window.
These are the ones we created in the Getting Started exercise 8 and following.
Click on the "Insert" button, and choose the "WindowResize" extension. Click "Select".
This Extension template generates code to automatically handle resizing and re-positioning
all the controls in the window when the user resizes the window, either by resizing the
window frame, or by pressing the Maximize/Restore buttons. Press the "OK" button to close
the window resize template dialog.
Return to the "Properties" tab of the "BrowseCustomers" procedure properties, and click on
the "Window" button.
Click on the window’s title bar, and open the Properties Pad (F4). Type "Browse Customers"
in the Title property. Change the "FrameType" property to "Resizable". It's the top property
in the list. Scroll down and find the "MaximizeBox" property and change it to "True". These
steps allow the user to resize the frame at runtime.
Click on the "Add Field" button (circled in blue above). This opens the "Select Column"
dialog. Click on "<ToDo>" and then "Add". Choose the "Customer" table and click "Select".
This adds the "Customer" table to the Table Schematic in the Select Column dialog, which
now lists the table and its columns. With the "Customer" table selected, click on the "Change"
button.
Highlight "KeyCustNumber" and press the "Select" button. This is important, because it sets
the display order for the rows in the list. If you don’t specify a key, the rows appear in (sort
of) whatever order they were added to the table (also called "Row Order").
Click on the "Company" field and then on "Select". This adds the "Company" field to the list
box formatter.
The tabs on the right allow you to format the appearance of the column highlighted in the list
on the left. In the Property list in the lower right pane, locate the "Right Border" and
"Resizeable" properties and set them to "True". This adds a resizable right vertical border to
the column at runtime.
Set the Header "Indent" property to 2 to slightly indent the heading text and verify that the
"DataIndent" property is already set at 2.
As the list box begins to grow, you can resize the List Box Formatter as needed to view
additional elements.
Group some columns
With the "Address" field highlighted, click on the "Add Group" button. By creating a new
group, in which you’ll place the address information, you can add a group header. This
appears above the column headers, and visually links the data in the columns beneath. Notice
that, as you add columns and make changes, you can see the effects of your changes in the
sample list box at the top of the List Box Formatter dialog. Set the new Header Text to
"Address Info". This provides the text for the group header. Then click on the "+" icon to
expand the group, so you can see its fields.
Any columns appearing to the right of this one will be included in the group, until you define
another group. As you add columns, the List Box Formatter continually updates its sample
window (at the top) to show you how your list will appear.
Select the "Address" field and then click on the "Add Field" button to add the "City", "State"
and "ZIPcode" fields.
You can use the editor to resize any fields as required. I have widened the "State" column for
readability. Then click "OK" to close the List Box formatter.
Adding tabs
When the default Application Wizard created this procedure it had tab controls that changed
the list’s sort order depending on which tab was selected. Therefore, we’ll add this
functionality right now to show how easy it is to accomplish.
First, click on the window to get the "drag handles" and drag the bottom one down to make
the window bigger.
Lasso all the controls on the window and then drag them down as shown, to make space at
the top.
Display the toolbox, and drag the "SHEET" control to the top-left corner of the window.
Resize the sheet control by using the grab handles until the sheet covers the list box and all
the buttons below it. Then right-click to the right of the "Tab1" and choose "Send to Back".
Click just below "Tab1" and change the Text to "by Customer number". We will set the display
order of the list box to this order shortly. Then click on "Select SHEET" at the bottom of the
properties window.
Notice that the grab handles have changed back to the entire sheet. Click on the "Add Tab"
link, and then click below the text "Tab2" to select it.
Change the text to read "by Company Name".
Instead of clicking on "Select SHEET" at the bottom of the properties window, another way to
return to the entire sheet control is to select it by name from the drop-down list at the top of
the properties window. Try it now.
Select the third tab from the drop-down list as shown, and change the tab text to "by ZIP
code".
Click on the "Close" button. Then Shift-Click on the other buttons in the row to select them
too. Change their "Hide" property to "True".
Select the LIST control from the properties dropdown list, and use the grab handles to make
the listbox control use up more space on the window. It can cover the hidden buttons too, as
shown.
Right-click on the list box control and choose "Actions ..." or click on the "Actions ..." link at
the bottom of the properties window.
Click on "Browse Box Behaviour" and then choose the "Conditional Behaviour" tab. Click
"Insert".
Type "CHOICE(?Sheet1) = 2" in the Condition field. Then click on the ellipsis next to the "Key
to Use" field, and select "KeyCompany" from the list of keys. Click the "Select" button, then
the "OK" button.
The condition expression sets the condition under which the alternate sort order will be used.
This expression uses the Clarion language CHOICE function to detect when the user has
selected the second tab on the sheet. The generated code will use this expression in a
conditional statement that will change the sort order at runtime.
Click on the "Insert" key to create another condition. Set the "Condition" expression to
"CHOICE(?Sheet1) = 3" and select "KeyZIPcode" as the "Key to Use". Click "OK".
Now, when the user selects the second or third tab, the BrowseBox Control template will
generate code to switch to the key on the Company or ZIP column. It doesn’t need to know
what to do for the first tab because that always uses the Access Key we set in the Table
Schematic. (Customer Number). Note that you can move the conditions around as required
using the arrow buttons. Please click "OK" and "OK" to return to the Window Designer.
Click on the "Window Preview" button to see how the window will look. It still needs work,
but at least we have a starting point. It's not a full working window, just a preview. Close the
preview, and then use the green "Save and Close" button, followed by the green "Save and
Exit" button.
Finally, use the "Accept changes" (Save) button to save your work.
So we add in ",FONT('Segoe UI') at the end of the line, to specify the default font for the
entire window. Click on the green "Save and Close" button, followed by the green "Save and
Exit" button. Then click on the "Accept changes" (Save) button.
Make a backup of your work.
We have been here before. This time, click on the "Actions" button.
Type "UpdateCustomer" in the Update Procedure field at the bottom of the Procedure
Properties dialog. This names the procedure to update the rows displayed in the browse.
Click "OK". Then "Save and Exit". The new procedure appears in the Application Tree as a
"ToDo".
Notice that you didn’t have to start a new execution thread for the update procedure. You
want it to run on the same thread as the browse, so that the end user can’t open a form
window to change a row, then activate the browse window again, and open another form on
the same row. In other words, you don’t want a user trying to change the same row twice at
the same time.
Find the "Data / Tables" (F12) panel in the bottom-left corner of the screen, and double-click
on the "<ToDo>" section of "Update Record on Disk" (or use the "Add" button). Click on the
"Customer" table and click "Select".
Make the Window Resizable
In the Procedure Properties dialog, select the "Extensions" tab. Click "Insert" and choose the
"WindowResize" extensions template. Click "Select".
We don't need to change any of these defaults, so click "OK". Then click on the green "Save
and Exit" button to return to the main application tree. Now click on the "Window" button to
load the Window Designer.
Go to the "Properties" window (F4) and change the "FrameType" property to "Resizable".
Find the "MaximizeBox" property in the "Options" group and set it to "True". These two
properties ensure that the user can resize the window at runtime.
From the main menu, choose "Window Designer", "Populate", "Multiple Columns".
When the "Select Column" dialog appears, choose the "Customers" table, "CustNumber" row,
and "Select" button. The dialog will briefly disappear, as it waits for you to decide where to
put the controls on the form. Move the cursor to the top left and click. The program places a
caption and a data control on the form. Now do the same with each of the columns, until all
the columns have a pair of controls on the form. Then click "Cancel" to remove the "Select
Column" dialog.
You should end up with something like this. Note that you can also use the "Data/Tables" pad
and drag and drop fields directly on the window. This seems a more logical way of doing
things, and the cursor works better too. Try deleting the "ZIPcode" control and dragging it
again from the "Data/Tables" pad instead.
Repeat the same steps with the "ZIP Code" controls. Note the blue "guidelines" that appear
when you are dragging the controls, to help you line them up with the other controls. This is
very useful to keep things uncluttered and professional.
Shift-drag the "City" caption closer to the "City" input field, to remove the large gap between
the caption and the control. Shift-drag the "Address" caption to the right, until its right-hand
side lines up with the right-hand side of the "City" caption below. Shift-drag the "Address"
input control so that its left-hand edge is above the left-hand edge of the "City" input control.
Then resize the address control until its right-hand edge lines up with the right-hand edge of
the "Zip Code" control, as shown.
Click on the "City" caption. This becomes the anchor. Now shift-click on all the other captions
on the left of the form, and then use the "Align Rights" button to align all the captions to the
right edge of the anchor control.
Click on the "City" input field. This is the new anchor. Shift-click on all the input fields above
it, and then use the "Align Lefts" button so that all the left-hand edges line up with the left-
hand edge of the anchor field. You have created two (invisible) "sight lines", one to the right
of the captions, and the other to the left of the input fields, that remove the clutter and make
the form easier to read, like margins on a printed page.
Click on the "Last Name" input field, and then Shift-click on the "Last Name" caption. Move
it up and to the right of the "First Name" input field. Click on just the "Last Name" input field,
and widen it until its right-hand edge lines up with the right-hand edge of the "Zip Code" and
"Address" fields. This is another sight line. Widen the "company" field to match up, too.
Select ONE input control from each line, and then use the "Spread Vertically" button to space
them out evenly between the top and bottom controls. Don't panic when it doesn't move all
the controls.
Select the "Address" input field as an anchor. Then shift-click on the "Address" caption and
click on "Align Middles" to line them up. Select the "First Name" input field as an anchor.
Shift-click on the "First Name" caption, the "Last Name" caption and the "Last Name" input
field. Use "Align Middles" to line them up with the anchor. Try the same steps with
"Company", "CustNumber" and "City" input fields (one line at a time) to make sure all the
middles are lined up.
The end result should be something like this. The form window is almost done. Before you go
any further, save your work! Click on the "Save and Close" button to return to the
Application Tree. Click on the "Accept Changes" (Save) button.
Select the "Control Templates" pad in the bottom right of the screen. Click on the
"BrowseBox" control template and drag it to the screen, just below the "City" caption, and
release it. The "Select Column" dialog box will appear. Double-click on the "<ToDo>" entry in
the "File Browsing List Box" section and select the "Phone" table.
Click on the "Change" button and choose the "KeyCustNumber" key. Click "Select".
Notice how the Phone table is sorted by the "pho:KeyCustNumber" key. Select the
"CustNumber" column on the right, and click "Select"
Adjust the size of the list box control to fit the space on the form, as shown here. I have also
widened the "Action Message" to the right of the "Cancel" button to line up with the right-
hand sight line.
Right-click on the browse box, and select "List Box format ..." from the popup menu.
The List Box Formatter now appears, ready for you to choose the rest of the columns to
display. Change the "DataJustification" property to "Center". The default for numeric values
is "Right". Also check the "Right Border" and "Resizable" properties are set to "True". This
adds a right border to the column and allows the user to resize the column width at runtime.
Click on the "Insert Field" button, and choose the "PhoneNumber" field from the Phone table
as shown. Click "Select".
Right-click on the List Box and select "Actions ..." from the popup menu.
Click on "Browse Box Behaviour" and then find the ellipsis to the right of the "Range Limit
Field" and select "PHO:CustNumber" to specify which field we want to limit the range on.
Click "Select".
Change the "Range Limit Type" to "File Relationship" and then click on the ellipsis on the
right of the "Related File" field. Choose "Customer" in the "Select Table" dialog, and click
"Select". This identifies the Customer table as the related table. These steps limit the rows
displayed in the list box to only those rows related to the currently displayed Customer table
row. Click "OK" and "OK" to return to the Window Designer. Save your work: click on the
green "Save and Close" button, then on the "Accept changes" (Save) button, and then use the
"Window" button to return to the Window Designer.
Now we need to make space for a row of buttons for the list box. Reduce the height of the list
box and make the form slightly taller. Move the existing buttons down a bit, as shown above.
In the "Control Templates" pad, search for the "BrowseUpdateButtons" template, and drag it
to the empty area below the Browse Box, as shown. Try to centre them below the list box, in a
way that doesn't associate them with the "OK" and "Cancel" buttons below. We don't want to
confuse the user. Save your work: click on the green "Save and Close" button, then on the
"Accept changes" (Save) button, and then use the "Window" button to return to the Window
Designer.
The Insert, Change, and Delete buttons are the buttons that will allow the toolbar buttons
to function, so they must be present in the window design. They do not have to be visible to
the end-user, so you can hide them if you choose. However, since this Browse Box is placed
on an update form procedure, for this application we’ll leave this set of
BrowseUpdateButtons visible. This will allow the user to use either set of buttons. The
toolbar update buttons will only function for this list when the list box has focus (not when
the user is inputting data into any other control) so keeping these buttons visible will ensure
that the user can easily maintain the Phone table rows.
Right-click on the "Delete" button and select "Actions..." from the popup menu.
Check the "Use Edit in Place" box. Click "OK". Save your work: click on the green "Save
and Close" button, then the green "Save and Exit" button, and finally the "Accept changes"
(Save) button in the Application Tree window to save your work.
Setting the Actions for one button sets them for all three buttons in the set, because they all
belong to the same control template. Since the "Phone" table is a small table with just a
couple of columns, there’s no need for a separate update procedure.
Make a backup of your work.
This follows on from Learning Clarion (Part 4). In the lesson below, we’ll create the
procedures that will maintain the "Product" data table
Change the text in the "New Procedure" dialog box to read "BrowseProducts" and
click "OK".
Because the "UpdateCustomer" procedure is nested under the "BrowseCustomers"
procedure (the one you are copying), the "Procedure name clash dialog appears". This
offers you options on how to handle the clashing procedures. Press the "Prompt"
button. By doing this, you tell the Application Generator to let you have the
opportunity to rename all the clashing procedures, or not.
Notice how the Application Tree now has the copied "BrowseProducts" procedure,
and its associated (empty) "UpdateProduct" procedure. They look "disconnected"
from the other procedures because no other procedure calls them (yet). We’ll do that
next.
Right-click on the "Main" procedure in the Application Tree. There are several ways
to access the embedded source code points within a procedure. Two of them appear
on the popup menu that you now see.
The first is the Embeds selection, which calls the Embedded Source dialog to show a
list of all the embed points within the procedure.
The second is the Embeditor Source selection, which actually generates source
code for the procedure and calls the "Embeditor" (the Text Editor in embed point edit
mode) to allow you to directly edit all the embed points within the context of
generated source. The generated source code is "greyed out" to indicate that you
cannot edit it, and every possible embed point in the procedure is identified by
comments, following which you may type your code.
There are advantages to each method of working in embed points, so we’ll cover both
methods during this lesson. First, we’ll use the Embedded Source dialog. Choose
"Embeds" from the popup menu.
The Embeds Tree dialog appears, allowing access to all the embed points in the
procedure. (You can also get here from the "Embeds" button on the Procedure
Properties window, but the popup menu is quicker.) This list is either sorted
alphabetically or in the order in which they appear in the generated source,
depending on whether you have the Sort Embeds Alphabetically box checked in
"Setup", "Application Options". Press the "Contract All" button on the toolbar. This
will make it easier to locate the specific embed point you need.
Type in "BrowseProducts" in the search bar. This is a quick way to find a particular
procedure. Or you can click on the "+" sign next to "Control Events" and scroll down
to "?BrowseProducts".
Click on the "+" sign next to "?BrowseProducts" and select "Accepted". The
"Accepted" event for this menu selection marks the point in the generated code that
executes when the user chooses the menu command. Click on the "Insert" button.
The Select embed type dialog appears to list all your options for embedding code. You
may simply Call a Procedure, write your own Clarion language Source in the Text
Editor, or use a Code template to write the source code for you. This is one advantage
to editing embed points from within the Embedded Source dialog: you can use Code
templates to write the code for you instead of writing it yourself.
Scroll down to the "Initiate Thread" template. Click on it and click "Select".
A Code template usually provides just a few prompts and instructions on its use. It
gathers the information it needs from you to write its executable code, which it then
inserts into the standard generated code produced by the Procedure template directly
into this embed point. This Code template is designed to start a new execution thread
by calling the procedure you name using the START procedure (as shown in the
window).
Choose the "BrowseProducts" procedure from the drop-down list of available
procedure names, and click "OK". This names the procedure to START when the user
chooses the menu item. This is the name of the procedure you previously copied.
Notice that there are some up and down buttons and a spin box on the right side of
the window that allow you to select a Priority. These are important. The templates
generate much of the code they write for you into these same embed points.
Sometimes, the code you want to write should execute before any template-generated
code, and sometimes it should execute after, and sometimes it should execute
somewhere between various bits of generated code.
The exact placement of your code within the embed point is determined by the
Priority number. This provides you with as much flexibility in placing your embed
code as possible. The Priority numbers themselves do not matter, but the logical
position within the generated code does, and that’s why this dialog also shows
comments which identify the embed priorities. Don’t worry, there’s more coming on
this issue later that’ll help make it clearer.
At this point, you could do the same thing to call the BrowseProducts procedure from
the Product button. However, there’s an easier way to write this code again: just Copy
and Paste it from one embed point to another. So, with the
BrowseProducts menu embed point still highlighted, click on the "Copy" button as
shown.
Search for the "ProductButton" in the search bar, or just scroll down until you find it.
Click on the "Accepted" embed point and press the "Paste" button. When the
"Procedure name clash" dialog appears to warn you that the same procedure has
already been used, click on "Same" because we want to use the same procedure. Now
click on the green "Save and Close" button.
The BrowseProducts procedure now "connects" to the Main procedure. Now you can
customize the copied procedures for the Product table, which we copied from the
BrowseCustomer procedure.
Click on the "Properties" button and change the description to "Browse Products".
Click on the green "Save and Close" button. Notice that the description is fixed.
Now we need to change the fields that are displayed. Click on the "Window" button to
open the window designer.
Change the window Title to "Browse Products" as shown.
Click on the main List Box, and then right-click and choose "List Box format ..." to
open the list box formatter.
Notice that all the fields still refer to the Customer table. Use the "Remove" button to
remove all the fields shown.
Now click on the "Add Field" button. It will display the current table and fields, none
of which are the ones we want. Click on the "Customer" table and then use the
"Delete" button to remove it.
Click on the "<ToDo>" word that replaced the "Customer" table and then the "Add"
button. This will open a list of tables. Click on "Product" and click "Select".
Use the "Change" button to change the default sort key to "KEYPRODDESC" and
click "Select". The "Select Column" dialog now lists the correct table and columns.
Click on "ProdNumber" and then the "Select" button.
Check that the RightBorder and Resizeable properties are both "True", and change
the Header Text property to "Prod #". The width should be 24.
Use the Add Field button to select "ProdDesc". Check that the RightBorder and
Resizeable properties are both "True". The width should be 120.
Use the Add Field button to select "ProdAmount". Check that the RightBorder and
Resizeable properties are both "True". The width should be 32.
Use the Add Field button to select "TaxRate". Check that the RightBorder and
Resizeable properties are both "True". Click "OK" to close the list box formatter.
Click to the right of the "by Zip Code" tab to select the entire tab sheet. Confirm this
by looking for the phrase "SHEET (Use = '?SHEET1')" in the properties window on
the right. Then press the "Delete" key to remove the sheet.
Use the green "Save and Close" button to save your work. Then click on the "Accept
changes" button to save your work. Return to the "Browse Products" design
window.
Use the Toolbox, and select the "STRING" control. Drag it to the top left of the list
box control.
Use the properties window to set the "IsPicture" property to "True", and use the
ellipsis to the right of the "Use" property to choose the "ProdDesc" field from the
Product table. Use the green "Save and Close" button to save your work and return to
the main window. Click on the "Accept changes" button to save your work.
From the main screen, ensure that the "BrowseProducts" procedure is still
highlighted, then click on the "Extensions" button.
In the "Extensions" tab, click on "Browse on Product()" and then click the
"properties" button. Change to the "Conditional Behaviour" tab and use the "Delete"
button to remove the two SHEET choices that applied to the Customer browse but are
no longer relevant to the Product browse. After all, we have also deleted the SHEET
control.
Change to the "Default Behaviour" tab, and click on the "Locator Behaviour" button.
Change the "Locator" option to "Incremental" and click "OK". This completes the
requirements for the Incremental Locator. The key column of the sort order (in this
case PRO:ProdDesc) is the default locator control. Click "OK", and then on the green
"Save and Close" button.
In the "Data / Tables" pad, highlight the "<ToDo>" table, then press the "Add" button
(or double-click on "<ToDo>"). Choose the "Product" table and click on the "Select"
button.
Set the Description to "Update Product" and click on the ellipsis next to the
"Window" button.
Change the WINDOW text to 'Update Product' and add in ",FONT('Segoe UI')" at the
end of the first line. Click on the green "Save and Close" button, and then use the
"Window" button to enter the Window Designer.
Starting with the "ProdNumber" field, drag and drop each field from the "Data /
Tables" pad to the form, as shown.
Spend a few minutes tidying up the form layout, and adjust the form size as shown.
Click on the green "Save and Close" button, then the green "Save and Exit" button,
and finally the "Accept changes" button to save your work. The Products table
update form window is completed.
Make a backup of your work.
Click on the "BrowseOrders" procedure and the "Properties" button. When the
"Select Procedure Type" dialog box opens, choose the "Templates" tab, and select
"Window - Generic Window Handler".
Type in "Browse Orders" in the Description field, and then click on the ellipsis next to
the "Window" button. When the "Select default declaration" dialog appears, choose
"MIDI Child Window" and click on "Select".
Change the WINDOW caption to 'Orders' and check that the FONT declaration shows
'Segoe UI'. Then click on the green "Save and Close" button. Then click on the
"Window" button to go into the normal Window Designer.
Resize the window to double its height and width. Change the "MaximizeBox"
property to "True" and check that the Frame Type is "Resizable". Open the "Control
Templates" pad.
Drag the "BrowseBox" control template to the top left corner of the window, and
drop. The "Select Column" dialog opens. Click on the "<ToDo>" entry, click the "Add"
button, choose the "Order" table and click the "Select" button.
With the "Order" table still selected, click on the "Change" button and select the
"KeyOrderNumber" key to sort the data by order number.
Use the "Insert Field" button to click on and "Select" each of the remaining fields in
the Order table.
Adjust the column widths by moving the right border on applicable fields. Set the
Header Indent to 1 for the first field. Ctrl-click on each of the fields to select them all.
Then Check that the "Resizable" and "RightBorder" properties are "True". Examine
the Header text property of each column, and abbreviate them as follows: "Cust#",
"Ord#", "Inv Amt", "Date", and "Note". Click "OK" when done.
Set the "Vertical" and "Horizontal" scrollbar properties to "True" and widen the list
box to fill most of the width of the form, leaving an equal distance between left, right
and top parts of the window. Change the Font "Size" to "8" points so that more of of
the Note field can be displayed. Use the green "Save and Close" and "Save and Exit"
buttons to save your work and return to the main screen.
Try the "Start Without Debugger" button to see how your application is progressing.
Close the application when done, and return to the Window Designer by clicking on
the "Window" button.
Search in the "Control Templates" pad for "browseupdatebuttons" and locate the
"Browse on Order" template. Drag it to the bottom left of the list box as shown. The
three buttons will appear.
Select just the "Delete" button, and then right-click and choose "Actions ..." from the
pop-up menu.
Type in "UpdateOrder" in the Procedure Name field and click "OK". This names the
procedure, in the same way that you named the Update procedure for the Customer
browse in its Procedure Properties dialog. Naming the Update Procedure for one
button in the Control template names it for all three.
Select the new "Detail" table entry, and click on "Change" to choose the
"KeyOrderNumber" sort key, and click "Select".
Click on the "Detail" table, choose the first field and click "Select". This will return us
to the Window Designer.
Adjust the shape of the new list box to fit the window, leave some space to the right
for a "Close" button which we will add later, and then right-click on it to choose the
"List Box format..." command and open the List Box Formatter.
Click on the "Add Field" button, select the "Detail" table and add in the remaining
fields as shown, using the "Select" button.
Adjust the headings and widths of each field to make them neater. Then click "OK"
when done.
Change the font "Size" property to "8" points so that it matches the size of the first list
box, and make sure the "Horizontal" and "Vertical" scrollbar properties are set to
"True".
Go to the "Extensions" tab. Click on the "Insert" button. Choose the "WindowResize"
extension and click on "Select".
Instead of just accepting the default resizing strategy, we are going to change a few
things. First, enable the "Restrict Minimum Window Size" option, to prevent the
window being made smaller than the designed size. Next, click on the "Override
Control Strategies" button and click on "Insert". Choose the "?Insert" window control
and change the "Vertical Positioning Strategy" option to "Fix Bottom". Click "OK".
Repeat these steps for the "?Change", "?Delete" and "?List:2" buttons. This ensures
that the three buttons and the bottom list box remain a constant distance from the
bottom edge of the top list box.
Insert another control strategy for the "?List" control, and set the "Vertical Resize
Strategy" to "Constant Bottom Border" and click "OK". This will ensure that the top
list box changes its height as the window grows, but the bottom list box stays the
same height. Click "OK" and "OK" again to return to the "Extensions" tab. Click on
"Accept changes" to save your work.
Highlight the "UpdateOrder" procedure in the Application Tree dialog, then click on
the "Properties" button. In the "Defaults" tab, choose "FORM(Add/Change/Delete)",
then press the "Select" button.
Type in a suitable description ("Order Form"), and then click on the ellipsis next to
the Window button.
Set the WINDOW caption to 'Order Form' and add ",FONT('Segoe UI')" to the end of
line 1. Click the green "Save and Close" button, and then click on the "Window"
button to open the Window Designer.
Go to the "Data / Tables" pad, and double-click on the "<ToDo>" entry, highlight the
"Order" table and click on "Select".
Drag and drop the "OrderDate" and "OrderNote" fields from the "Data / Tables" pad
to the top of the form, as shown.
Change over to the "Toolbox" pad, select the "ENTRY" control and drag it to a spot
below the "OrderDate" input field. Then go to the "Properties" pad on the right, click
on the ellipsis to the right of the "Use" property, and choose "CustNumber" from the
"Order" table.
Return to the toolbox, and drag a "PROMPT" control to the left of the "CustNumber"
entry control, as shown. Click on the "Edit Value" smart tag. If you miss it, you can
find "Edit Value" in the right-click popup menu. Change the value to "&Cust
Number:" and click "Accept".
Click on the ellipsis to the right of the "Lookup Key" for "When the Control is
Selected". Click on the "Order" table and then the "Add" button to highlight the
related "Customer" table and "Select" it.
Click on the "Customer" table, choose the "KeyCustNumber" key and click "Select".
Now we have the Lookup Key.
Now we move to the "Lookup Field", click on the ellipsis on the right, and choose
the related "CUS:CustNumber" field from the "Customer" table. Click "Select". Now
we have the Lookup Field.
Next, we need the "Lookup Procedure". From the drop-down list choose
"BrowseCustomers". Make sure the checkbox for "Force Window Refresh when
Accepted" is checked. Click "OK".
Once again, right-click on the "CustNumber" control and choose "Embeds..." from
the popup menu. This displays a list of just the embed points associated with this one
control. This is the quickest way to get to a specific control’s embed points, and it’s
the second way you’ve seen so far to get to an embed point. There is a third method
that’s still to come.
Click on the "Selected" event and press the "Insert" button. Highlight "Source" and
click "Select". This opens the Embeditor.
Start typing "?" and the floating "Populate Column" toolbox appears. This is to assist
with speed and accuracy, so you don't introduce errors from typing mistakes. We
need to type "?ORD:CustNumber{PROP:Touched} = TRUE".
It is "standard Windows behaviour" that, if the user does not enter data into a control
and just presses tab (or CLICKs the mouse) to go on to another control, an Accepted
event does not happen. This allows users to easily tab through the window’s controls
without triggering data-entry validation code on each control. However, sometimes
you need to override this "Windows standard behaviour" to ensure the integrity of
your database. The
?ORD:CustNumber{PROP:Touched} = TRUE
statement uses the Clarion language Property Assignment syntax. By setting
"PROP:Touched" to TRUE in the Selected event for this control, an Accepted event is
always generated, whether the user has entered data or not. This forces the lookup
code generated for you into the Accepted event for this control (from the information
you entered on the Actions tab on the previous page) to execute. This ensures that the
user either enters a valid Customer number, or the Customer list pops up to allow the
user to select a Customer for the Order. Click the green "Save and Close" button when
done.
Notice how the SOURCE entry has been added in. Click on the green "Save and Exit"
button to return to the Window Designer.
Drag a "STRING" control from the Toolbox and place it to the right of the
"CustNumber" control. We are going to use this to display the Customer name as a
read-only field.
In the "Properties" pad, click on the ellipsis to the right of the "Use" property and
select the "Company" field from the "Customer" table. Then change the "IsPicture"
property to "True" and set the "Picture" value to "@s30". Spend a few minutes
adjusting the fields and aligning them, so they are neat and tidy.
Switch to the "Control Templates" pad and drag a "BrowseBox" template just below
the "CustNumber" controls. When the "Select Column" dialog box opens, double-click
on the "<ToDo>" entry and choose "Detail". Click on "Select".
Now select the "Detail" table and click on the "Change" button to choose the
"KeyOrderNumber" key. Click "Select". Finally, click on the "ProdNumber" field and
click "Select".
Adjust the shape of the list box and then right click to choose "List Box format ..."
Change the Header "Indent" property to "2" and the "DataJustification" setting to
"Center".
Use the "Add Field" button to select the "Quantity" field, and check that its
"DataJustification" property is "Center".
Use the "Add Field" button to select the "ProdAmount" field, and check that its
"DataJustification" property is "Center".
We want to create a new variable to display the total price for each line item (the
quantity multiplied by the unit price). Click on the "Insert Field" button, click on the
"Local Data UpdateOrder" item, and click "New".
The column name is "ItemTotal". Give it a suitable description. Change the Data Type
to "DECIMAL", with 7 characters and 2 places. Click "OK". Check that its
"DataJustification" property is "Center".
Click on the "Add Field" button, select the "Detail" table, click "Add", choose
"Product" and click "Select". This adds the Products table to the Control template’s
table schematic as a lookup table. The related row from the Products table is
automatically retrieved for you so you can display the product description in the list.
Choose the "Product" table, highlight the "ProdDesc" field, and click "Select".
Adjust the field widths, change the Header "Justification" settings to "Left", and the
Header "Indent" settings to "1", except for the first field, where it should be "2".
Adjust the Header Text as shown.
We want this list to only display the "Detail" table rows that are related to the "Order"
table row currently being edited. Therefore, we need to specify a Range Limit. Right-
click on the list box and select "Actions..." from the popup menu.
In the "Range Limit Field" use the ellipsis to specify the Detail table's
"OrderNumber". Change the "Range Limit Type" to "File Relationship" and the
"Related file" to "Order". Don't click "OK" yet because we haven't finished here yet.
Double-click on the "ItemTotal" field to add it to the "Expression" list at the bottom.
This is the column whose contents will be used in the total calculation. So far, we’ve
only declared this column and not done anything to put any value into it, but we’ll get
to that soon. Click on the green "Save and Exit" button. Then click "OK".
Go to the "Classes" tab because we want to give the object a more useful name.
Change it to read "BrowseDET" and click "OK".
With the list box still selected, go to the "Properties" pad and set the "Horizontal" and
"Vertical" scroll bar values to "True". Click on the "Save and Close" button, and the
"Accept changes" button to save your work. Then click on the "Window" button to
return to the window designer.
Select just the "Delete" button, right-click and choose "Actions..." from the popup
menu.
Check the "Use Edit in Place" box. Checking this box for one button in the Control
template checks it for all three. We will be using the Edit in Place technique to update
the "Detail" table rows instead of an update Form procedure. This will allow us to
demonstrate some fairly advanced programming techniques and show just how easy
they are to perform within the Application Generator. Click "OK".
From the "Toolbox" pad, drag the "STRING" control to the bottom-right corner of the
list box. Change the "IsPicture" property to "True". Click on the ellipsis to the right of
the "Use" property to select the "InvoiceAmount" field from the "Order" table. This
specifies the control will display data from a variable, not just a string constant.
Change the "Picture" property to "@n$10.2". Click on the green "Save and Close"
button to close the Window Designer and save your work.
Click on the "Data" button on the right under "Operands". Choose the "Detail" table
and the "Quantity" field. Click "Select".
This places "DET:Quantity" into the "Statement" field for you. It contains the
expression being built. You can type directly into the "Statement" field to build the
expression, if you wish. Click the "*" button in the Operators group. This is the
multiplication operator. Notice how it has added the "*" to the Statement field. Click
on the "Data" button again, and select the "ProdAmount" from the "Detail" table.
Click the "Select" button.
Use the "Check" button to check the syntax of the Statement line. A green checkmark
appears left of the button, indicating the syntax is correct. If a red X appears, the
expression’s syntax is incorrect and the highlighted portion of the statement is what
you must change. Click on the green "OK" button.
Change to the "Extensions" tab, select "Update a Record from Browse Box on Detail"
and click on the "Properties" button.
Repeat these steps for the "ItemTotal" and "PRO:ProdDesc" fields. Their "Edit-In-
Place" facility should be disabled, as shown. Click "OK", "OK", "Save and Exit", and
"Accept changes" to save your work.
This opens the Embeditor: the third method of accessing embed points in a
procedure. The Embeditor is the same Text Editor you’ve already used, but opened in
a special mode which allows you to not only to edit all the embed points in your
procedure, but to edit them within the context of template-generated code.
Notice that most of the code is on a gray background and the points where you can
write your code have a white background. There are also identifying comments for
each embed point. You can turn these comments on and off as you choose through
the "Tools" -> "Options" dialog. Once you become familiar with them, you’ll probably
want to turn them off, so you can see more of the actual code.
You’ll notice that a message briefly appeared that said, "Generating LCLesson." The
Embeditor displays all possible embed points for the procedure within the context of
all the possible code that may be generated for the procedure. Notice the distinction
here: Embeditor does not show you the code that will be generated, but all the code
which could be generated for you, if you chose every possible option and placed code
into every available embed point. You are not likely to ever do that. Therefore, a lot
more generated code shows up in the Embeditor than will actually be in the
generated code when you compile your application. After we finish here, we’ll go look
at the generated code to see the difference.
At the right end of the toolbar are four buttons which are essential to know when
working in the Embeditor. These are (from left to right) the "Previous Embed", "Next
Embed", "Previous Filled Embed", and "Next Filled Embed" buttons (hover your
mouse over them and the tooltips appear naming the buttons). They allow you to
quickly get from one embed point to another; particularly after you’ve written code
into some of them.
One of the things we want this procedure to do is to detect changes to existing orders
and make sure the changes do not result in a data mismatch between the "Order" and
"Detail" tables. This system is storing the total dollar amount of an order in the
"ORD:InvoiceAmount" column, so when the user changes a "Detail" item in an
existing Order, we want to make sure the "Orders" table row is updated, too. There’s a
fairly simple way to do that which will allow us to demonstrate the ABC Library’s
flexible error handling.
Click on the "Next Embed" button (about 4 times) until you get to the embed
point immediately preceding the line of code reading
ThisWindow CLASS(WindowManager)
Each embed point potentially has 10,000 priority levels within it. This Embed code
Priority level system is designed to allow you to embed your code before or after any
generated code: whether that code is generated for you by Clarion’s ABC Templates
or any third-party templates you choose to use. This makes the embed system
completely flexible, allowing you to add your own code at any logical point needed;
before or after almost any "chunk" of generated code.
In the white area just above this line, type in (or copy and paste) the following:
LocalErrGroup GROUP
USHORT(1)
USHORT(99)
BYTE(Level:Notify)
PSTRING('Save the Order!')
PSTRING('Some Item changed -- Press the OK button.')
END
SaveTotal LIKE(ORD:InvoiceAmount)
as shown in the image above. The red text indicates that the text begins in Column 1,
and identifies a data label.
Clarion’s ABC (Application Builder Class) Templates generate Object Oriented code
for you using the ABC Library. The ABC Library contains an error handling class
called ErrorClass. This bit of code declares a LocalErrGroup GROUP (in exactly the
form that the ErrorClass requires: see the ABC Library Reference Vol I) containing a
"custom" error number and message that we are defining for use by the ErrorClass
object in our application. The "SaveTotal" declaration is a local variable which is
defined LIKE (always has the same data type) the "ORD:InvoiceAmount" column.
We’ll use this variable to hold the starting order total when the user is updating an
existing order.
Choose "Search" -> "Find" to bring up the Find dialog, or press Ctrl-F.
The text we want to find is "ThisWindow.Init", so type it in to the "Find what" field
and click the "Find" button. Click on the red "x" to close the Find dialog. You should
see "ThisWindow.Init" highlighted on the screen.
Press the "Next Embed" button (about 6 times) until you get to the point just after
SELF.Errors &= GlobalErrors
as shown above. Paste in the following:
SELF.Errors.AddErrors(LocalErrGroup) !Add custom error
IF SELF.Request = ChangeRecord !If Changing a row
SaveTotal = ORD:InvoiceAmount !Save the original order total
END
This code calls the "AddErrors" method of the "GlobalErrors" object to add the
"LocalErrGroup" to the list of available errors that the object handles. The
"GlobalErrors" object is an instance of the"ErrorClass" which the ABC Templates
declare globally to handle all error conditions in the application. Adding our
LocalErrGroup enables the GlobalErrors object to handle our "custom" error
condition. This demonstrates the flexibility of Clarion’s ABC Library.
The IF statement detects when the user is editing an existing order and saves the
original order total.
This embed point is in the "ThisWindow.Init" procedure which performs some
necessary initialization tasks. This is a virtual method of the "ThisWindow"
object. ThisWindow is the object which handles all the window and control
handling code.
You may not have noticed, but the ABC Templates generate exactly one line of
executable source code within the "UpdateOrder" procedure itself (GlobalResponse =
ThisWindow.Run) so all the functionality of the "UpdateOrder" procedure actually
occurs in object methods: either virtual methods specific to the "UpdateOrder"
procedure itself or standard ABC Library methods. This is true of every ABC
Template generated procedure. Generating fully Object-Oriented code makes the
code generated for you very tight and efficient. Only the code that actually needs to be
different for an individual procedure is handled differently. Everything else is
standard code that exists in only one place and has been tested and debugged to
ensure consistent performance.
Object-Oriented Programming (OOP) in Clarion starts with the CLASS structure. See
CLASS in the Language Reference Help for a discussion of OOP syntax.
The Advanced Programming Resources PDF contains several articles which discuss
OOP in depth, and the ABC Library Reference fully documents Clarion’s Application
Builder Class (ABC) Library.
Press Ctrl-F to get back to the Find tool, and look for "ThisWindow.Kill". Click down
two embed points and paste:
SELF.Errors.RemoveErrors(LocalErrGroup) !Remove custom error
This calls the ABC Library method to remove our "custom" error. The
ThisWindow.Kill method is a "cleanup" procedure (performs necessary exit tasks)
which executes when the user is finished working in the UpdateOrder procedure, so
the error is no longer needed at that point.
Press Ctrl-F to get back to the Find tool, and look for "EVENT:CloseWindow". Click
"Find" twice to get to the desired one. Move down to the embed point and paste:
IF SELF.Request = ChangeRecord AND | ! If Changing a row
SELF.Response <> RequestCompleted AND | ! and OK button not pressed
SaveTotal <> ORD:InvoiceAmount ! and detail recs changed
GlobalErrors.Throw(99) ! Display custom error
SELECT(?OK) ! then select the OK button
CYCLE
END
This is the code that will detect any attempt by the user to exit the UpdateOrder
procedure without saving the Orders table row after they’ve changed an existing
order. Note the vertical bar characters (|) at the end of the first two lines of code.
These are absolutely necessary. Vertical bar (|) is the Clarion language line
continuation character. This means that the first three lines of this code are a
single logical statement which evaluates three separate conditions and will only
execute the GlobalErrors.Throw(99) statement and the next 2 lines if all three
conditions are true. Click the green "Save and Close" button to save your work.
With "UpdateOrder" still selected, click on the "Embeds" button to display the
Embeds Tree. Click on the "Contract All Nodes" button.
By pressing the "+" button on the tree, expand the "Local Objects", "Abc Objects",
"EIP Field Manager for Browse Using ?List for column DET:Quantity
(EditSpinClass)", "Init", "CODE", "Parent Call". Click on the "Source" button.
Notice how the "BrowseProducts" procedure has been added to the "UpdateOrder"
tree. Click on the "Accept Changes" button to save your work.
Generate Code
Click on the "Generate the currently selected Application" button in the toolbar. Then
right-click on "UpdateOrder" and choose "Module Source File" from the popup menu.
The Text Editor appears, containing the generated source code for your
UpdateOrder procedure. Notice that there is a lot less code here than there was in the
Embeditor. All that generated code in the Embeditor was there to provide you
with context, and to provide you with embed points with which to override methods,
should you need to. However, Clarion’s Application Generator and ABC Templates
are smart enough to only generate the code you actually need, when you actually need
it.
From the IDE Menu, choose "File" -> "Close" -> "File" to close the Text Editor. Now
might be a good time to try out your application. You’ve got all the data entry portions
completed and the only things lefts to do now are the reports, which we’ll get to in the
next lesson.
Make a backup of your work.
This follows on from Learning Clarion (Part 6). In the lesson below, we’ll create the
application’s reports. But first, we need to tidy up the data.
Find and rename the following files, changing the file extension from .tps to .tps.old:
STATE.tps
ORDER.tps
PRODUCT.tps
DETAIL.tps
The "Convert File" dialog should appear because our index names are slightly
different to the "Solution" version. Click on the "Convert the file and backup the
existing file ..." option and click "OK".
Once the data has been displayed, click on the "x" next to "State.TPS" to close the
data file. Right-click on "Product" and select "Browse Table" and follow the same
steps. "Order" opens without conversion, but "Detail" will also need some help. Open
all the tables just to make sure. The "Phone" table is empty. The "Customer" data was
imported in Lesson 5. Click on the green "Save and Close" button. Make
a backup of your work. Change to "Solutions (Projects and Applications)" in the
"Start Page", and open the "LCLesson" application once again.
Right-click on the menu bar, and choose "Edit Menu" from the popup menu. This will
open the Menu Editor.
In the Menu Editor, click on the "P&rint Setup" item, and then click on the "Add new
Item (Insert)" button on the toolbar. This will add a new menu item just below the
"P&rint Setup" item.
In the "Text" property, change it to read "Print &Customer List" and press the TAB
key. Click on the "Actions..." link at the bottom of the properties pad.
Change the "When Pressed" selection to "Call a Procedure". In the "Procedure Name"
field, type in "CustReport" and enable "Initiate Thread". Click "OK".
Click on the newly created "Print &Customer List" item, and then click on the "Add
new Item (Insert)" button once more.
Type "Print &All Invoices" in the Text property entry, and press the TAB key. Click on
the "Actions..." link at the bottom of the properties pad.
Change the "When Pressed" selection to "Call a Procedure". In the "Procedure Name"
field, type in "InvoiceReport" and enable "Initiate Thread". Click "OK".
For the third menu item, set the text to "Print &One Customer’s Invoices" and set the
procedure to "CustInvoiceReport" as shown. Click "OK", "Save and Close", "Save and
Close" and "Accept changes" to save your work and return to the main procedure
display.
Click on the "Defaults" tab and notice that you can choose 3 different page sizes for
reports. Because this program is intended for users in the USA, click on the "Report
(Paper size Letter - Portrait)" option and click on the "Select" button to continue.
Fill in "Print a list of Customers" in the Description field. Click on the "Data/Tables"
pad, and double-click on the "<ToDo>" entry. Choose the "Customer" table and click
"Select". Click on the green "Save and Exit" button and "Accept changes". Then click
on the "Report" button. We are going to use a Control Template, so it is always a good
idea to save your work before doing so.
The Report Designer appears. Here you can visually edit the report and its controls.
The Report Designer represents the four basic parts of the REPORT data structure by
showing the Page Header, Detail, Page Footer, and Form as four "bands." Each band
is a single entity that prints all together. See the "IDE User’s Guide" lesson 8 on the
Report Designer for more information on the parts of the report and how the print
engine generates them ("C:\Clarion11\docs\IDEUsersGuide.pdf").
For this report, you’ll place page number controls in the header, then place the
columns from the Customer table in the Detail band.
Click on the "Control Templates" pad, and drag the "ReportPageNumber" template to
the Page Header band as shown.
Select the STRING control in the "Toolbox" pad, and drag the control to the top left of
the Page Header band, and drop. In the Properties Pad, set the "Text" property to
"Page Number:". Tidy up the two items so they work together.
Select the "Data/Tables" pad, and double-click on the "Customer" table. Choose the
"KeyCustNumber" key and click on the "Select" button. This will cause the report to
sort the data by CustNumber.
Use the grab handles to adjust the report header size, and to make more space for the
fields in the detail band. Drag the "Company" field to the top left of the detail band, as
shown.
Drag the "FirstName" and "LastName" field to the line below "Company". Put the
"Address" field on the line below, and finish off with the "City", "State" and "ZIPcode"
fields on the last line. Drag the "CustNumber" field to the top right, and use the
familiar alignment tools to neaten everything up.
Click on the drop-down list at the top of the "Properties" pad, and select "Report".
This will allow us to set the properties of the report. Click on the ellipsis next to the
"TextFont" property, and choose a font and size. "Arial" 10pt is suggested. You could
also use a more modern font like "Cambria" or "Calibri" to match the fonts often used
in Word documents.
Before we close the report, click on the "Print Preview Details" button. Click on
"Details" and then use the "Add" button a few times to get several records to display.
Click on "OK" to see the preview, and then "Close". Click on the green "Save and
Close" button and "Accept Changes" to save your work.
Click on the "Start without Debugger (Builds Solution)" button to run the application.
Use the "File" menu and click on "Print Customer List" to preview the actual report,
and to do a printout if you want to. "Exit" the application when done. Don't leave it
running before you return to the Clarion IDE.
An Invoice Report
Next, we will create one of the most common types of reports. An invoice will make
use of most of the tables in the data dictionary, demonstrating how to create group
breaks and totals. It will also show you how to control pagination based on group
breaks.
Select the empty "InvoiceReport" procedure and then click on the "Properties"
button. Change to the "Defaults" tab, choose "Report (Paper size Letter - Portrait)"
and click on the "Select" button. Type in "Print All Invoices" in the "Description"
field.
Go to the "Data/Tables" pad and double-click on the "<ToDo>" entry. Choose the
"Customer" table and click "Select".
The report will process all the Customer table rows in CustNumber order. It will
process all the Orders for each Customer. Each Order will print all the related Detail
rows. Each Detail row will lookup the related Products table row. Click on the "Accept
changes" button and then on the "Report" button. Use the "Properties" pad drop-
down to select "Report" and change the font to something suitable for your company
reports. I chose "Cambria" again.
The "Report Form" band prints once for each page in the report. Its content is only
composed once, when the report is opened. This makes it useful for constant
information that will always be on every page of the report. Go to the "Toolbox" pad
and drag a "STRING" control to the top middle of the "Report Form" band. Change
its "Text" property to "Invoice", the "Justification" property to "CENTER", "Bold" to
"True" and "Size" to 24. Use the grab handles to make the control span the full width
of the band, as shown.
Drag another "STRING" control to just below the "Invoice" control. Change its "Text"
property to your company/client name, the "Justification" property to "CENTER",
"Italic" to "True" and "Size" to 14. Use the grab handles to make the control span the
full width of the band, as shown.
Then drag and drop the "ProdNumber" field from the "Detail" table to the right of the
"Quantity" control.
Drag and drop the "ProdDesc" field from the "Product" table to the right of the
"ProdNumber" control.
Return to the "Detail" table and drag and drop the "ProdAmount" field to the right of
the "ProdDesc" control.
Highlight "Local Data InvoiceReport" in the "Data/Tables" pad, then press the "Add"
button. This local variable will be used to display the total price for each line item.
Type "LOC:ItemTotal" in the "Column Name" field. Change the "Data Type" to
"DECIMAL". Set the "Characters" to "7" and the "Places" to "2" and click "OK".
Drag "LOC:ItemTotal" and drop it to the right of the last control placed. Move all the
controls to the top of the Detail band, aligned horizontally, then resize the band, so it
is just a little taller than the controls. Also, modify the properties of the
"DET:ProdAmount" and "LOC:ItemTotal" controls to change the "Justification" to
"RIGHT" and the "Offset" to "2".
Click on the "Detail" band, and then choose "Report Designer", then "Bands",
"Surrounding Break".
Use the ellipsis to the right of the "Variable" entry to choose "CUS:CustNumber", or
just type it in. Change the "Label" from "break1" to "CustNumberBreak". Then click
"OK". A "Break (CUS:CustNumber)" band appears above the Detail band. Repeat the
process to choose "ORD:OrderNumber" for the second break. Change the "Label"
from "break2" to "OrderNumberBreak".
Click on the "Break (ORD:OrderNumber)" band, and then select "Report Designer",
"Bands", "Group Header". The "Group Header(ORD:OrderNumber)" band appears
above the Detail band. This band will print every time the value in the
ORD:OrderNumber column changes, at the beginning of each new group of rows. We
will use this to print the company name, address, along with the invoice number and
date.
Click on the "Break (ORD:OrderNumber)" band, and then select "Report Designer",
"Bands", "Group Footer". The "Group Footer(ORD:OrderNumber)" band appears
below the Detail band. This band will print every time the value in the
ORD:OrderNumber column changes, at the end of each group of rows. We will use
this to print the invoice total.
Click on the "Customer" table in the "Data/Tables" pad, and drag the "Company"
control to the top-left corner of the "Group Header(ORD:OrderNumber)" band. Drag
the "FirstName" and "LastName" controls to the line below, and then the "Address"
control below them. Put the "City", "State" and "ZIPcode" controls on the fourth line.
Tidy up the controls as before.
Click on the "Order" table in the "Data/Tables" pad, and drag the "OrderNumber"
control to the top-right corner of the "Group Header(ORD:OrderNumber)" band. Set
its "Bold" property to "True". Drag the "OrderDate" control to the line below it.
Change its "Justification" property to "RIGHT".
Go to the "Toolbox" pad and drag a "STRING" control to the left of the
"OrderNumber" control. Click on the "Edit Value" entry in the pop-up menu, or at the
bottom left of the "Properties" pad. Change the text value to "Order Number:" and
click "Accept".
Drag another "STRING" control below the first one, and change its text to "Order
Date:". Tidy up the controls so they look neat. Drag the bottom of the
"Group Header(ORD:OrderNumber)" downwards to make some space for a line of
text labels that will correspond to the controls in the "Detail" band.
Set the "LineWidth" property to "5" and change the "LineColor" to "ControlDark", so
we don't have a thick, ugly black line. Check that the "Height" value is 0, so we have a
straight line.
Select the LINE control we created in the Invoice Header. Note the "X" value of the
line. Then use the "Edit" menu and choose "Copy". Click in the Invoice Footer band
and use the "Edit" menu to "Paste". Twice. Set the "X" values of each new line to the
same value as the original line. Make sure the "Height" values are both 0. Change the
"Y" value of the first copy to 0, and the second one to 250. The "Order Total" controls
should now have a line above and another below, equally spaced. Tidy up the controls
if necessary. Reduce the height of the Invoice footer band.
Go to the "Data/Tables" pad, and click on the "Local Data InvoiceReport" section.
Click on the "Add" button. Type in "LOC:InvoiceCount" in the "Column Name" entry.
Change the "Data Type" to "LONG" and the "Screen Picture" to "@N3" and click
"OK". Then click "Cancel" when the empty "Column Properties" dialog box comes up.
Drag the "LOC:InvoiceCount" control to the right of the second "STRING" control.
Check that its "VariableString" property is "True". Set the "TotalType" to "Count" and
select "CustNumberBreak" for the "Reset" property. This is the same type of total
column that we placed in the "ORD:OrderNumber" group footer, but it will only reset
when "CUS:CustNumber" changes. Select "OrderNumberBreak" for the "Tally"
property. This total column will count the number of invoices that print for each
customer. The Tally list allows you to select the point(s) at which the total
increments. By selecting "OrderNumberBreak" from the list, the count will only
increment when a new invoice begins.
From the "Toolbox" control pad, drag a "STRING" control right of the Invoice Count
string you just placed. Set the "VariableString" property to "True". Set the "Use"
property to "LOC:ItemTotal", the "TotalType" to "Sum", and the "Reset" property to
"CustNumberBreak". This is the same type of total column that we placed in the
"ORD:OrderNumber" group footer, but it will only reset when "CUS:CustNumber"
changes.
Go to the "Data/Tables" Pad. Select the "Customer" table and drag the "Company"
control just right of the "Invoice Summary for:" string you placed. Tidy up the
controls, and resize the bands to make them look neater. Click on the green "save and
Close" button and the "Save and Exit" button. Then "Accept changes" to save your
work.
Adding a Formula
Select the report once again and click on the "Properties" button.
Click on the "Formulas" tab in the Procedure Properties dialog, and the "Insert"
button to open the Formula Editor.
The Formula Editor design dialog appears. Type "Item Total Formula" in the "Name"
entry. In the "Class" entry, select "Before Print Detail" from the Template Classes list.
The "Before Print Detail" class tells the Report template to perform the calculation
each time it gets ready to print a Detail. Press the ellipsis button for the Result entry.
Highlight "Local Data InvoiceReport" in the Tables list, select "LOC:ItemTotal" from
the Columns list, then click the "Select" button.
Press the "Data" button in the Operands group. Highlight the "Detail" table in the
Tables list, select "Quantity" from the Columns list, then click on the "Select" button.
This places the "DET:Quantity" column in the "Statement" entry for you. The
Statement entry contains the expression being built, and you can also type directly
into it to build the expression, if you wish. Click on the "*" button in the "Operators"
group. Then click on the "Data" button in the "Operands" group.
Choose the "Detail" table in the Tables list, then the "ProdAmount" from the Columns
list, and click on the "Select" button.
Now you should see the complete calculation in the statement entry:
"DET:Quantity*DET:ProdAmount". Click on the "Check" button to check for syntax
errors. Click on the green "OK" button to close the Formula Editor design window.
Notice the new "Item Total Formula" has been added. Click on "Save and Exit" and
"Accept changes" to save your work.
Try running the application by clicking on the "Start without Debugger (Builds
Solution)" button. Select the "Print All Invoices" report and preview it. The first page
is displaying the invoice correctly, but on the third page there are "ghost" invoices,
with an invoice number of zero. We need to fix this.
Click on the "Window" button in the properties tab. This will open the report progress
window in the Window Designer. Click on the title bar on the window, and change the
"Text" property to "Invoice Progress...". Click on the green "Save and Close" button,
and then on the green "Save and Exit" button to return to the main Application Tree.
Click on "Accept changes" to save your work. Try running the "Print all Invoices"
report again.
The New Procedure dialog appears. Type "CustInvoiceReport" in the entry box, then
click "OK". Click on the "properties" button for the new (copied) procedure which has
replaced the "<ToDo>" placeholder. Change the "Description" entry to "Print
Customer Invoices".
Change to the "Embeds" tab and click on the "Collapse All" button. Then click on each
of these entries to expand them: "Local Objects", "Abc Objects", "Window Manager
(ReportManager)".
Find the first "Init" procedure, click on "CODE", then "Open Files". Click on the
"Insert" button.
Choose "Source" and click "Select". This embed point is at the beginning of the
procedure, before the report has begun to process. It’s important that the tables for
the report already be open because we will call another procedure for the user to
select a "Customer" row. If the tables for the report weren’t already open, the
procedure we call would open the "Customer" table for itself then close it again and
we would lose the data that we want to have for the report. This has to do with
multithreading and the Multiple Document Interface (MDI). See "THREAD" in the
Language Reference for more on this.
Type in "GlobalRequest = SelectRecord" and click on the green "Save and Close"
button. This code sets up a Browse procedure to select a row (it enables the Browse
procedure’s "Select" button).
Make sure the SOURCE you just added is still selected. Then click on the "Insert"
button.
Go to the "Properties" tab, and then press the "Actions" button. In the Properties
dialog, press the "Report Properties" button.
The Report Properties dialog appears. This dialog allows you to set either Row
Filters or Range Limits (along with Hot Columns and Detail Filters).
Row Filters and Range limits are very similar. A Row Filter is a conditional
expression to filter out unwanted rows from the report, while a Range Limit limits the
rows printed to only those matching specific key column values. They can both be
used to create reports on a subset of your tables, but a Range Limit requires a
key and a Row Filter doesn’t. This makes a Row Filter completely flexible, while
a Range Limit is very fast. You can use both capabilities if you want to limit the
range, then filter out unneeded rows from that range.
Select the "Range Limits" tab. Press the ellipsis button for the "Range Limit" entry.
The "CUS:CustNumber" control, the only logical choice, is automatically populated.
Leave "Current Value" as the "Range Limit Type". Current Value indicates that
whatever value is in the column at the time the report begins is the value on which to
limit the report. Since the user will choose a Customer row from the BrowseCustomer
procedure, the correct value will be in the CUS:CustNumber column when the report
begins. Now press the "OK" button. Press the next "OK" button, followed by the green
"Save and Exit" button.
Run the Clarion IDE and open "LCLesson" once again. Since we have been diligent
about saving our work, click on "Last Saved" to recover the application.
When we copy a procedure that calls other procedures, we have the option to call
other procedures or not. In this case, we want to continue to use the procedures that
"CustInvoiceReport" procedure is using, so click on the "Same" button.
Select the "CustInvoiceReport" once again and click on the "Formulas" button, and
the "Insert" button to open the Formula Editor.
The Formula Editor design dialog appears. Type "Item Total Formula" in the "Name"
entry. In the "Class" entry, select "Before Print Detail" from the Template Classes list.
The "Before Print Detail" class tells the Report template to perform the calculation
each time it gets ready to print a Detail. Press the ellipsis button for the Result entry.
Highlight "Local Data CustInvoiceReport" in the Tables list, select "LOC:ItemTotal"
from the Columns list, then click the "Select" button.
Press the "Data" button in the Operands group. Highlight the "Detail" table in the
Tables list, select "Quantity" from the Columns list, then click on the "Select" button.
This places the "DET:Quantity" column in the "Statement" entry for you. The
Statement entry contains the expression being built, and you can also type directly
into it to build the expression, if you wish. Click on the "*" button in the "Operators"
group. Then click on the "Data" button in the "Operands" group.
Choose the "Detail" table in the Tables list, then the "ProdAmount" from the Columns
list, and click on the "Select" button.
Now you should see the complete calculation in the statement entry:
"DET:Quantity*DET:ProdAmount". Click on the "Check" button to check for syntax
errors. Click on the green "OK" button to close the Formula Editor design window.
Notice the new "Item Total Formula" has been added. Click on "Save and Exit" and
"Accept changes" to save your work.
Now repeat the same process to add the missing formula to the new report,
"SingleInvoiceReport".
Notice how the copied procedure appears unattached at the bottom of the application
tree. We'll "connect the lines" after we finish with the report.
Click on the "Expand filled nodes" button to show the user's added code to the
generated code. The red highlights at each node will tell you that there is some
embedded code further down the tree. At the bottom, you can see two entries of
embedded code, which we want to remove.
Click on the "Show Filled Only" button, and un-click the adjacent "Show Priority
Labels" button. This will just show the stuff we want to remove. Click on the line that
reads "SOURCE (GlobalRequest = SelectRecord)" and then click on the "Delete"
button, followed by "Yes". Do the same for the next line that reads "PROCEDURE
BrowseCustomers".
Highlight the "SingleInvoiceReport" procedure, and select the "Data / Tables" Pad.
Click on the "Customer" table, then press the "Delete" button. The tables disappear,
and are replaced by "<ToDo>".
Click on the "<ToDo>" and then click on the "Add" button. Choose the "Order" table
from the list, and click "Select".
Next, click on the "Order" table and then the "Change" button. Select the key called
"KEYORDERNUMBER" and click on the "Select" button. So now we can use the key
to find the Order number we want to print out.
Click on the "Order" table again, and then the "Add" button. Select the "Detail" table
and click "Select".
Now repeat the same process to add the "Product" table as a related table to "Detail".
Go back to the "Order" table, click "Add" and choose the "Customer" table. Click the
"Select" button. Now we have all the related tables needed to display the data in an
invoice, and we have the correct key on the "Order" table to be able to specify exactly
which single invoice to print.
Return your attention to the "SingleInvoiceReport" procedure in the main window,
and then click on the "Properties" button.
From the "properties" tab click on the "Actions" button. Click on the "Report
Properties" button and change to the "Range Limits" tab. Use the ellipsis button to
select "ORD:OrderNumber" as the Range Limit Field, and leave the Range Limit Type
as "Current Value". This indicates that whatever value is in the column at the time the
report begins is the value on which to limit the report. Since the user will run this
report from the BrowseOrders procedure, the correct value will be in the
ORD:OrderNumber column when the report begins. Click "OK", and "OK" again.
Press the "Accept changes" button to save your work. Click on the "Report" button
because we need to make a few changes.
Select the "Break(CUS:CustNumber)" band and use the delete key to remove it. This
removes not only the Group Break, but also the Group Footer that was associated
with it. Click on the green "Save and Close" button to return to the Procedure
Properties dialog. Click on the green "Save and Exit" button in the Procedure
Properties dialog to close it. Click on the "Accept Changes" button to save your work.
Now that we have a report, we need to connect it to the rest of the application, so it
can be used. Click on the "BrowseOrders" procedure, and then on the "Window"
button.
From the "Control Templates" Pad, find the "BrowsePrintButton" collection and drag
the "Browse on Order" control to the right of the "Delete" button as shown. Right-
click on the new button and select "Properties" from the pop-up menu.
Change the Text property to "&Print Invoice". Change the "Use" property to
"?PrintInvoice". Click on the "Actions ..." link as shown.
Change the "When Pressed" property to "Call a Procedure" and then use the drop-
down list to select "SingleInvoiceReport". Click "OK".
This Control Template is specifically designed to run a range-limited report based on
the currently highlighted row in the list box we selected (Browse on Orders). The
Order table row buffer will contain the correct value to allow the Current Value Range
Limit on the "SingleInvoiceReport" to work. It also automatically adds this button's
action to the popup menu for the browse.
Click on the green "Save and Close" button, and then on the "Accept Changes" button
to save your work.
Notice how the "SingleInvoiceReport" procedure is now connected to the
"BrowseOrders" procedure. Try running the application by clicking on the "Start
without Debugger (Builds Solution)" button, or press Ctrl-F5. Then make
a backup of your work.
What's Next?
Here is the completed application, created from scratch and without the help of the
wizards:
While this lesson application is by no means a "shrink-wrap" program, it has
demonstrated the normal process of using the Application Generator and all its
associated tools to create an application that actually performs some reasonably
sophisticated tasks. Along the way, you have used most of Clarion’s high-level tool
set, and seen just how much work can be done for you without writing source code.
You have also seen how just a little embedded source can add extra functionality to
the template-generated code, and how you can easily override the default ABC
Library classes.