Learning Clarion
Learning Clarion
This publication is protected by copyright and all rights are reserved by SoftVelocity Inc. It may
not, in whole or part, be copied, photocopied, reproduced, translated, or reduced to any electronic
medium or machine-readable form without prior consent, in writing, from SoftVelocity Inc.
This publication supports Clarion. It is possible that it may contain technical or typographical
errors. SoftVelocity Incorporated provides this publication ―as is,‖ without warranty of any kind,
either expressed or implied.
SoftVelocity Inc
www.softvelocity.com
Trademark Acknowledgements:
i
Table of Contents
ii
Table of Contents
iii
Table of Contents
iv
Learning Clarion
1
Learning Clarion
Anatomy of a Database
This section briefly describes the fundamentals of database design. It is meant only to provide an
overview of the subject for those who are not already thoroughly familiar with standard database
design concepts and issues. Experienced developers may want to move right on to the next
chapter and skip this section.
Definitions
A database is a collection of information (data) in a system of tables, rows, and columns. The
database is maintained by one or more computer programs or applications.
The basic unit of data storage is a column. A column is a storage place for information of a similar
type. For example, one column might store a name and another column might hold a telephone
number.
A group of different columns that are logically related make up a row. A row contains all the
information related to one subject. For example, all the columns containing information
concerning one student (name, address, telephone number, student number, etc.) makes up one
student‘s row. This would be similar to a file folder a school might keep for each student.
Collections of logically related rows make up a table. Using the same example, a collection of all
students‘ rows makes up the student body table. This would be similar to the file cabinets where
students‘ folders are kept.
Another way of looking at this is as a table or spreadsheet:
In this format, the entire table is a file cabinet, with rows (folders) and columns (data about one
row).
A database is a collection of related tables. This is similar to a bank of file cabinets where the
entire school records are kept. One file cabinet might hold the files with students‘ personal data,
another with class enrollment information, and another with faculty information.
A relational database is a collection of tables with defined relationships between them. Effective
database design breaks the data into related tables that are joined together through linking
columns. This will be covered in detail later in this section.
2
Learning Clarion
Summary:
One or more columns combine to form a row.
Data Types
Columns can store many different types of data, but each individual column may hold only one
type. When a column is defined, its data type is specified. This enables it to efficiently store that
type of data. For example, to store a number from 0 to 100, using a column defined as a single
BYTE takes less space than one defined as a decimal number column (a byte can hold an
unsigned whole number between 0 and 255).
Clarion supports a wide variety of data types. All are fully documented in Chapter 3 of the
Language Reference core help.
3
Learning Clarion
An index is not dynamically maintained, it is only built when needed. Indexes are useful to
create sort sequences that are infrequently used, such as month-end or year-end
processes.
Using the student table example discussed earlier, suppose you wanted to sort the students‘ rows
two ways: by name alphabetically, and by name within each major. This produces two alternate
sorts.
This example uses a one-component key on the student‘s name.
The next example has two components in the key: major and student name. A key can contain
one or more columns, allowing sorts within sorts.
4
Learning Clarion
5
Learning Clarion
Once data storage is "normalized," related information is linked by ensuring a column in one table
is identical to a column in the other. These common columns create the links between related
tables. A linking column could be a student number, a course code, or a classroom number. Any
column (or group of columns) that uniquely identifies a row in the primary table can be used as a
link.
Examples of these relations can be found in a school database:
One teacher teaches many classes (One-to-Many).
Many students would have one major (Many-to-One).
Database Summary
A database is a collection of information (data) in a system of columns, rows, and tables.
Columns can store many different types of data, but each individual column is specified to
hold only one type.
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.
6
Learning Clarion
Customer Customer name and address data that changes only when a customer
moves. Created in the Getting Started lessons, along with its related Orders
table.
Phones In the communication age that we live in, it is probable that a customer may
have more than one contact phone number.
7
Learning Clarion
Orders Basic information needed for assembling the data needed to print an
invoice. It "looks up" information from the other tables, such as the customer
name and address. When a sales person takes a new order, they add a row
to this table.
Detail Product, price, and quantity ordered for an item on a given invoice: the
variable information for each order. Though this duplicates price information
in the Products table, you must maintain the price at the time of the sale
here. Otherwise, when you increase the price in the Products table, it would
cause the balance in the Detail table to change.
Products Information on the products sold by the company, including product number,
description and price. This data changes only when a price changes or a
new product is added.
8
Learning Clarion
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 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.
9
Learning Clarion
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).
Finally, the following 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 in a consistent manner 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 behavior.
10
Learning Clarion
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.
You designed the database that will allow the application to accomplish those
tasks.
Now that the application description is fairly complete, we‘re ready to begin work. The first step
is to create the data dictionary.
11
Learning Clarion
12
Learning Clarion
Lesson Files
We recommend that you complete the entire series of lessons here, regardless of your
experience with Clarion. There are many new things you need to know about. As you‘ve already
seen from the Getting Started lessons, Clarion‘s template-driven Application Generator approach
to programming is very different from other development environments for 3GL and 4GL
languages. If you‘ll thoroughly immerse yourself in the following lessons, you will get the most out
of your new tool.
The completed lesson files reside in the Shared Documents section on your computer.
For example, in Windows XP, the Lessons folder is found in:
In Vista:
C:\Users\Public\Documents\SoftVelocity\Clarion7\Lessons\LearningClarion\Solution
We provide these so you can see the end result of the lessons and compare your application.
There should only be cosmetic differences.
Wherever there are multiple ways to accomplish a single task, the lessons will expose you to
several of them to demonstrate the flexibility of the Clarion development environment.
13
Learning Clarion
Starting Point:
You should have the Clarion development environment open and the Start Page opened.
14
Learning Clarion
2. Select the Comments tab, press the Add button in the toolbar, and then type Learning
Clarion Lesson Dictionary in the text field.
The Comments tab allows you to write free form text notes regarding the dictionary. It‘s optional,
but extremely useful for programmers who may have to return to a project for maintenance after
an interval of months.
Just to the right of the Dictionary Properties button on the DCT Explorer toolbar, there is a Users
button which opens a dialog where new users can be added if needed. The dialog also provides a
Password entry, which allows you to prevent others from using this dictionary. There‘s no need
to fill it in for this lesson, but it‘s a useful feature to keep in mind.
1. From the DCT Explorer, select the Import Tables option found on the DCT Explorer
toolbar. Press Yes if you are prompted to save changes to the Dictionary.
15
Learning Clarion
The Select Dictionary dialog opens, allowing you to select the other Clarion Dictionary to import
from.
16
Learning Clarion
4. Press the Add All button to move all tables in the GSLesson dictionary to the new
LCLesson dictionary, and then press Finish to complete the import process.
17
Learning Clarion
Before we leave this section, we need to add one other small change to our new LCLesson
dictionary.
18
Learning Clarion
4. Press the Save and Close button in the Entity Browser toolbar to close the Column
Properties dialog, and the Customer table.
5. Choose File Save, or press the Save button on the IDE tool bar.
You imported existing table definitions from one data dictionary to another (the easy way
to work—never re-invent the wheel).
You added an initial value for one of the columns in the database.
In the next chapter, you‘ll learn how to add a table to the data dictionary, starting totally "from
scratch". You‘ll see just how quick and easy it is to do even without using a wizard.
19
Learning Clarion
20
Learning Clarion
Starting Point:
The LCLesson.DCT should be open.
21
Learning Clarion
22
Learning Clarion
large database which is frequently updated, or which drivers are best when the quickest query
time is the foremost concern.
2. Press the OK button to close the Add Table dialog.
You can accept the defaults for all other options in the dialog. The dialog box closes, and the
Dictionary dialog lists the Phones table, with "Customer phones table" listed next to it (If that
Dictionary Option is enabled).
From the IDE menu, choose File Save, or press the Save button on the IDE tool bar.
23
Learning Clarion
Column Pools are treated just like a table in the Data Dictionary, even though they do not
generate any code into applications. You may have as many Column Pools in your data
dictionary as you choose, but there is usually no need for more than one.
2. Type ColumnPool in the Label field.
3. Type POOL in the Prefix field.
4. Press the OK button.
24
Learning Clarion
This window displays a list of all the columns defined in the Pool "table." Since this is a new table,
there is nothing to display. We could simply start adding columns, but instead, we‘ll start by
copying a column from the Customer table.
2. Highlight the Customer table in the DCT Explorer , and double-click to open the
Entity Browser for this table.
Select the column and copy it
1. In the Entity Browser, locate the CustNumber column in the Columns section.
2. Choose Edit Copy (or press CTRL+C), or you can right-click on the column and
select Copy from the popup menu:
25
Learning Clarion
5. Press the Save and Close button to close the Column Properties dialog, and the
ColumnsPool table..
This is the column that will be the linking column for the relationship between the Customer and
Orders tables. Linking columns in separate tables are always defined the same, so copying the
column definition is one way to get the existing column‘s definition into the Column Pool.
Derive the existing column
1. The Customer table should still be opened. If the CUSTNUMBER properties is not
displayed, double-click on the CUSTNUMBER column to open it.
2. Type POOL:CustNumber in the Derived From entry (or, you could press the ellipsis
button to the right of the entry, and select the CustNumber field from the ColumnPool
table). Press the TAB key.
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.
3. Press the Save and Close button to close the Column Properties and Customer
table Entity Browser.
2. In the Quick View, press the Add button to open the Column Properties dialog.
Once you begin the process of defining new columns, an empty Column Properties dialog
automatically appears after you add each successive column. This speeds up the process of
adding multiple columns. After adding your last column, you just have to press the Cancel button
on an empty Column Properties dialog to return to the Quick View.
3. Type OrderNumber in the Column Name entry.
26
Learning Clarion
This will be the linking column between the Orders and Detail tables.
4. Choose LONG from the Data Type dropdown list.
This specifies a four byte signed integer. See the Language Reference in the core help for more
details.
5. Type @n_6 in the Screen Picture entry.
6. Press the OK button.
A new Column Properties dialog opens again.
7. Type ProdNumber in the Column Name entry.
This will link the Detail table to the Products table.
8. Choose LONG from the Data Type dropdown list.
9. Type @n_6 in the Screen Picture entry.
10. Press the OK button.
The Column Properties dialog re-appears.
11. On the next Column Properties dialog, press the Cancel button.
12. Press the Save button in the IDE toolbar to save your work to this point.
2. In the Quick View Fields pane on the right, press the Add button to open the
Column Properties dialog.
3. Type CustNumber in the Column Name entry.
4. Press the ellipsis (...) button to the right of the Derived From entry.
A Select window appears containing tree lists of all the columns already defined for all the tables
in the dictionary. You can derive new columns from any existing column—whether that column is
in a table definition, global data, or a column pool.
27
Learning Clarion
5. Highlight the CustNumber column in the ColumnPool table then press the Select
button. You may need to expand the ColumnPool table to see the columns under it.
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.
The new column automatically becomes a perfect copy of the column from which it was derived—
right down to the prompts and window control type. The button with the right arrow icon right next
to the ellipsis (...) button allows you to refresh the derived column from its parent‘s definition.
6. Press the OK button to close the Column Properties dialog.
This is the column that will provide the link between the Phones and Customer tables.
A new Column Properties dialog now opens.
Define the PhoneType Column, and Add Special Characteristics
This defines the type of phone number (Home, Work, etc.). We will also add a special Validity
Check, Default Value, and Window Control to this column.
1. Type PhoneType in the Column Name entry.
2. Choose STRING from the Data Type dropdown list.
3. Type 6 in the Characters spin control.
4. Select the Validity Checks tab.
The Validity Checks tab allows you to set numeric ranges for number columns, specify that a
column value must match another column in a related value, must be true or false, and in this
case, that the column value must be in a list you specify in this dialog.
5. Select the Must be in List radio button.
6. Type the following in the Choices box:
Home|Work|Fax|Cell|Mobile|Other
28
Learning Clarion
29
Learning Clarion
2. In the Quick View Fields pane on the right, press the Add button to open the
Column Properties dialog.
3. Type OrderNumber in the Column Name entry.
4. Press the ellipsis (...) button to the right of the Derived From entry.
5. Highlight the OrderNumber column in the ColumnPool table then press the Select
button. 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.
6. Press the OK button to close the Column Properties dialog.
This is the column that will be the link between the Orders and Detail tables.
A new Column Properties dialog appears.
30
Learning Clarion
31
Learning Clarion
2. In the Quick View Fields pane on the right, press the Add button to open the
Column Properties dialog.
3. Type ProdNumber in the Column Name entry.
4. Press the ellipsis (...) button to the right of the Derived From entry.
5. Highlight the ProdNumber column in the ColumnPool table then press the Select
button. 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.
6. Press the OK button to close the Column Properties dialog.
This is the column that will be the linking column for the relationship between the Detail and
Products tables.
A new Column Properties dialog appears.
32
Learning Clarion
2. Choose File Save, or press the Save button on the tool bar to save your work
up to this point.
All of the tough work is done! Hang in there, we are almost there!
2. Press the ellipsis (...) button to the right of the Derived From entry.
3. Highlight the CustNumber column in the ColumnPool table then press the Select
button. 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.
4. Press the Save and Close button to close the Column Properties dialog.
33
Learning Clarion
You created a pool of column definitions from which new column definitions
can be easily derived.
34
Learning Clarion
Now that all the tables are defined, we can add keys then specify the table relationships. You
already have defined the keys for the two tables you created in the GSLesson application in the
Getting Started lesson. In this chapter, we‘ll define keys for the remaining tables.
Starting Point:
The LCLESSON.DCT should be open.
The Orders table was imported in the Getting Started lesson, and there are two keys already
defined that satisfy the above requirements.
35
Learning Clarion
36
Learning Clarion
8. Choose File Save, or press the Save button on the tool bar.
There should be no duplicate or null order numbers in the Products table; this is a primary key.
For each ProdNumber in the row there can be many Detail rows. This is a One to Many
relationship with the Products table a "Parent" to the Detail table.
37
Learning Clarion
8. Choose File Save, or press the Save button on the tool bar.
38
Learning Clarion
10. Choose File Save, or press the Save button on the tool bar.
39
Learning Clarion
The relationships for the Customer, Orders, and States tables were defined in the Getting Started
lessons. Refer back to that section in the Getting Started if you would like to review those
definitions.
2. In the lower right pane, press the Add button located just above the Relations
list.
The Relationship Properties dialog appears:
40
Learning Clarion
41
Learning Clarion
5. Choose File Save, or press the Save button on the tool bar.
2. In the lower right pane, press the Add button located just above the Relations
list. The Relationship Properties dialog appears:
3. Choose MANY:1 from the Type dropdown list.
4. Choose DTL:ProdNumberKey from the Foreign Key dropdown list.
5. Choose Products from the Related Table dropdown list.
6. Choose PRD:ProdNumberKey from the Primary Key dropdown list.
7. Press the Map by Name button.
5. Choose File Save, or press the Save button on the tool bar.
2. In the lower right pane, press the Add button located just above the Relations
list. The Relationship Properties dialog appears:
42
Learning Clarion
6. Choose File Save, or press the Save button on the tool bar.
2. In the Fields Quick View, highlight CustNumber and press the Change button.
3. Select the Validity Checks tab.
4. Select the Must Be In Table radio button.
5. Choose Customer from the Table Label dropdown list. Actually, the IDE detects that
it is the only valid table here and selects it for you.
This requires that the column can only contain values verified by getting a matching row from the
Customer table. This is validated using the table relationship information, which is why this
Validity Check cannot be set until the relationships have been defined.
6. Press the Save and Close button in the Entity Browser to close the Orders table.
2. In the Fields Quick View, highlight ProdNumber and press the Change button.
43
Learning Clarion
You created the keys for all the new table definitions.
The data dictionary is now complete to this point. In the next chapter, we will import some existing
data from another application, to show you just how simple it is to accomplish.
44
Learning Clarion
Data Conversion
You may have existing data from legacy applications that you want to save and use in your
Clarion applications. Therefore, this chapter shows you:
How to import a table definition from an existing table.
How to browse and edit a table using the Database Manager.
How to convert data from one table format to another.
Starting Point:
The LCLESSON.DCT should be open.
2. In the Select Server entry choose Comma Delimited Files (BASIC) from the
dropdown list.
45
Learning Clarion
3. 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.
4. In the Filename field, press the ellipsis button to select
…\Lessons\LearningClarion\import1.csv then press the Open button.
5. In the Driver Options field, press the ellipsis button to open the BASIC Driver String
Builder dialog.
Browser. Or press the Browse button on the DCT Explorer toolbar. Press the Apply button
when the Dataset Parameters dialog is opened. This will open the Database Browser.
46
Learning Clarion
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.
Converting a Table
At this point, you‘re looking at the .CSV file‘s data in Clarion‘s Database Browser utility. Next, you
need to move that data into a TopSpeed table so your Clarion programs can use it.
Close the Database Browser at this time and return to the DCT Explorer in the already
opened GSLesson dictionary.
You can specify a table to convert from three different sources. The target can be directly on disk
(using Data File), a table definition in the current dictionary (using Table Definition), or a table
definition in another dictionary (using Table Definition from another DCT). We have chosen a
table from our current dictionary.
If you have Clarion.NET installed, you also have the option of generating a conversion program in
standard Clarion language format or Clarion# to generate a conversion program that runs with the
.NET Framework.
47
Learning Clarion
The table that you select first in the conversion program process is always the target. In our
example, the CUSTOMER table is the target.
2. In the Select a Table dialog, highlight IMPORT1 and press the Select button.
3. 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.
When you press the Save button, this will generate all the Clarion source code necessary to take
the data in the Source Filename, and copy it into a new Target Filename, using the file format
specified by the Target Structure.
4. The Select Destination Data File dialog appears. Highlight the CUSTOMER.TPS file
located in the Learning Clarion folder, and press the Open button.
5. Another dialog pops up that asks you if you would like to load the data conversion
program. Press the Yes button. 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.
48
Learning Clarion
The only purpose this file definition served was to allow the Dictionary Editor to generate file
conversion source code for you. Therefore, we can delete it from the Data Dictionary right now.
1. Highlight the IMPORT1 table in the DCT Explorer, and press the Delete button.
2. Press the Yes button when asked to confirm the deletion.
3. Press the Save and Close button to exit the Data Dictionary Editor, and press the
Yes button to save your changes as you exit.
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.
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.
1. Choose Tools Browse Table from the IDE Menu.
49
Learning Clarion
2. In the Select Driver… dialog window, highlight TopSpeed (TPS) and press the Select
button.
3. In the Open TopSpeed File dialog, press the ellipsis button, and select the
CUSTOMER.TPS file, then press the OK button.
A Dataset Parameters dialog opens that allows you to specify how many rows you want to read.
Press the Apply button to open the Database Browser:
This demonstrates another way to open the Database Browser, other than from within the
Dictionary Editor.
4. Press the Save button in the Database Browser toolbar to save the changes to
the TopSpeed table.
5. Press the Exit button to close the Browser, or you can simply select File
Close File from the IDE Menu.
You used Clarion‘s Database Browser utility to examine the contents of the
.CSV table.
You compiled and executed a table conversion program to import data from a
.CSV table into a TopSpeed table.
Now you‘ve converted some valuable existing data to the TopSpeed table format so your Clarion
applications can use it. In the next chapter, we will begin building an application "from scratch"
using the Application Generator.
50
Learning Clarion
Starting Point:
The Clarion environment should be open, and all dialog windows closed.
51
Learning Clarion
2. Select the Clarion for Windows item in the left Categories pane, and click or select the
Application Quick Starts as shown above.
3. Verify that the Location is set to the …\Lessons\LearningClarion directory.
4. Type LCLesson in the Name field.
5. Uncheck the Auto create project subdir checkbox.
6. Press the Create button.
52
Learning Clarion
button.
53
Learning Clarion
2. Choose the Defaults tab and highlight Default MDI Frame in the Select Procedure
Type dialog, then press the Select button.
The Procedure Properties dialog appears. It defines the functionality and data structures for the
procedure.
54
Learning Clarion
Usually the first task when creating a procedure is to edit the main window. You can place
controls, or if the procedure template has controls already, you can customize them.
The application frame itself never has controls. Windows doesn‘t allow it. We will, however,
customize the window caption (the text that appears on its title bar). Then we will add items to the
predefined menu, which is also built into the Frame Procedure template, and create a toolbar for
the application (a toolbar can have controls).
55
Learning Clarion
2. Press the Designer button shown above. The Window Designer appears. Here are
all the tools that allow you to visually edit the window and its controls.
3. Choose View Properties to display the Property Pad (if it is not already present).
You‘ll notice under the View menu that there are several toolboxes, or pads, that are available—
all of these are fully dockable and resizable. This means you can configure your workspace, as
you want. Because of this configurability, throughout the rest of the lessons some screen shots
taken within the Window Designer may not appear the same as on your computer.
4. CLICK on the window‘s title bar to make sure it has focus (that is, the white "handles"
appear to the right and bottom edge of the window).
5. Type LCLesson Application in the Title entry of the Property Pad, then press TAB.
This updates the caption bar text in the sample window.
56
Learning Clarion
57
Learning Clarion
The Menu Editor appears. It displays the menu in hierarchical form in a list box at the left. The
fields at the right allow you to name and customize the dropdown menus and menu items.
This template already provides you with a "standard" menu. It contains basic window commands
such as an Exit command on a File menu, the standard editing Cut, Copy, and Paste
commands, and the standard window management commands commonly found in an MDI
application.
58
Learning Clarion
4. Press the Menu Down button to position the MENU inline with the other
MENUs.
5. In the Text field in the right hand pane, type &Browse in the field then press TAB.
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).
59
Learning Clarion
3. Type ?BrowseCustomers 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.
4. Right-click on the ITEM just added, and select the Actions item from the popup
menu.
The prompts allow you to name a procedure to execute when the end user selects the Browse
Customers menu item.
5. Choose Call a Procedure from the When Pressed drop list.
New prompts appear to allow you to name the procedure to call and choose options.
6. Type BrowseCustomers in the Procedure Name field.
This names the "ToDo" procedure for the Application Tree.
7. Check the Initiate Thread box.
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.
8. Press OK to close the ?BrowseCustomers Prompts dialog
60
Learning Clarion
Normally, the next step is to define the action for the menu item—what happens when the end
user executes it from the menu. We‘ll skip over this step for now, for this menu item only. Later,
you‘ll create a procedure by copying it, then attaching it to this menu, just to show you this
capability of the Clarion environment.
Close the Menu Editor and Window Designer and save your work
1. Press the Save and Close button to close the Menu Editor.
This returns you to the Window Designer.
2. Press the Save and Close button.
This returns you to the Window Designer Editor dialog.
3. Press the Save and Close button to close the Window Editor and 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.
1. Highlight the SplashScreen (ToDo) procedure and press the Properties button.
2. Choose the Defaults tab, highlight Default Splash Window in the Select Procedure
Type dialog, then press the Select button.
The Procedure Properties dialog appears. There‘s nothing we really have to do to this procedure
except accepting all of the defaults.
3. Press the Save and Close button.
61
Learning Clarion
4. In the Application Tree toolbar, press the Save button to save your work.
Notice that this popup menu contains a set of menu selections that match the set of tabs on the
top of every Procedure Properties window. It also aligns closely with the right hand pane.
This popup menu provides you with direct access to all the Clarion tools that you use to modify
existing procedures, so that you don‘t have to go through the Procedure Properties window every
time.
3. Choose the Window menu item. The Window Designer Editor opens. If you are not
automatically taken there, press the Designer button to access the Window
Designer.
You can bypass the Window Designer Editor and load Designer directly, by setting the following
Application Options (this dialog can be found in the IDE Tools menu):
62
Learning Clarion
4. With the Window Designer active, choose View Toolbox from the main IDE Menu
(or press CTRL + ALT + X).
5. Locate and highlight the TOOLBAR control in the Toolbox, and DRAG and DROP to
the window:
This adds the toolbar—always immediately below the menu—to the sample window. You can add
any control to your toolbar by CLICKing on a control in the Controls toolbox, then CLICKing in
your toolbar.
63
Learning Clarion
2. RIGHT-CLICK on the button you just placed, then choose Properties from the popup
menu. Or you can press the F4 key.
The Properties dialog appears.
3. Clear the Text property.
Do not use the spacebar to do this! Use the DELETE or BACKSPACE keys.
We‘ll place images on these buttons, instead of text, to give the application a more modern look.
4. Type ?CustomerButton in the Use property.
This is the field equate label for referencing the button in code. We included the word "Button" for
code readability.
5. Set the Flat property to TRUE.
You can simply click on the Flat property, and it will toggle the TRUE/FALSE values!
6. Drop down the Icon property, scroll to the bottom and CLICK on Select File.....
The Select Icon File dialog appears.
7. Select GIF Files from the Files of type drop list.
8. Select the CUSTOMER.GIF file, then press the Open button.
64
Learning Clarion
12. Near the bottom of the Property Pad, click on the Actions link:
65
Learning Clarion
13. Choose Call a Procedure from the When Pressed drop list.
14. Choose BrowseCustomers from the Procedure Name drop list.
This is the procedure name you typed for the Browse 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.
15. Check the Initiate Thread box.
16. Press the OK button.
66
Learning Clarion
If the control toolbox is not present, choose View Toolbox from the Window Designer menu.
1. CLICK on the BUTTON control, drag the mouse to the toolbar design area (not the
Designer‘s toolbar!), right next to the first button, and drop to place the button.
A button appears, labeled "button2."
2. RIGHT-CLICK the button you just placed, then choose Properties from the popup
menu.
3. Clear the Text property.
4. Type ?ProductsButton in the Use property.
5. Set the Flat property to TRUE.
6. Drop down the Icon property, scroll to the bottom and CLICK on Select File.....
7. Select GIF Files from the Files of type drop list.
8. Select the PRODUCTS.GIF file, then press the Open button.
9. Type Browse Products into the Tip property.
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.
67
Learning Clarion
68
Learning Clarion
When you have multiple controls selected, RIGHT-CLICK displays an alignment popup menu
instead of a single control‘s popup menu. This action aligns all three buttons with the top of the
Products button.
4. RIGHT-CLICK and choose Format > Horizontally Spacing > Make Equal from the
popup menu.
This spaces all three buttons apart equally. Make sure they‘re close together, so there‘s room for
what‘s coming next!
69
Learning Clarion
The thirteen Browse control buttons appear on the toolbar. You‘ve already seen these buttons on
the applications you created in the Getting Started topic. These buttons can control the scrolling
and update procedure call behavior of the Browse procedures you‘ll create (later in the lessons).
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.
70
Learning Clarion
2. 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 topic (Creating a Browse).
3. Press the OK button to close the message box.
4. Choose File Exit 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.
71
Learning Clarion
This changes your view of the application from the logical procedure call tree to the actual source
code modules generated for the application.
2. Highlight the LCLesson.CLW module, RIGHT-CLICK to display the popup menu then
CLICK on Module.
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. After you‘ve finished this
lesson, you can go on to the Introduction to the Clarion Language lesson at the end of the
Learning Clarion topics and become more familiar with the Clarion Language (it‘s actually very
straight-forward).
3. When you have finished looking at the code, choose File Close File to exit the
Text Editor and return to the Application Generator (You can also simply press the
Close button in the source window).
Be sure NOT to choose File Exit, otherwise you‘ll exit Clarion for Windows completely.
4. With the Application Tree dialog open, Change the Tree Mode drop list to Procedure.
This changes your view of the application back to the logical procedure call tree.
5. Highlight the Main (Frame) procedure, RIGHT-CLICK to display the popup menu,
then CLICK on Module.
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 in the Application Generator since the last time
you generated code will not show up in this code.
72
Learning Clarion
You may have noticed that right below the Module selection was another called Source. Do not
confuse these two, they do very different things. We will demonstrate the 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.
6. When you have finished looking at the code, choose File Close File to exit the
Text Editor and return to the Application Generator. You can also simply press the
Close button in the source window.
You created a toolbar under your application‘s main menu and placed
iconized, flat buttons on it.
You used Clarion‘s resize and alignment tools to adjust your screen design.
You used a Control Template to populate your toolbar with a set of standard
navigation buttons.
73
Learning Clarion
74
Learning Clarion
7 - Creating a Browse
Creating a Browse Window
In this chapter, you‘ll create a browse window similar to the one created for you by the Application
Wizard. The Application Generator uses the same templates, which generate the same basic
code—but doing it this way, you‘ll have a chance to "do it from scratch." This shows you just how
much the Wizards do for you, and how you can do it all yourself, too. You‘ll start with the
Customer browse window.
Starting Point:
The LCLESSON.APP should be open in the Application Editor (Tree).
75
Learning Clarion
Now you‘ll create a similar one using the Browse Procedure template:
76
Learning Clarion
77
Learning Clarion
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.
4. Press the OK button to close the Window Resize template dialog.
78
Learning Clarion
You can call this dialog at any time by pressing the Add Field button.
Select the table and columns to place in the browse list box control
1. Highlight the <ToDo> item below the File-Browsing List Box and press the Add
button.
2. Highlight the Customer table in the Select dialog, then press the Select button.
This adds the table to the Table Schematic in the Select Column dialog, which now lists the table
and its columns.
3. With the Customer table that you just highlighted selected, press the Change button.
79
Learning Clarion
4. Highlight KeyCustNumber in the Change Access Key dialog 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").
5. Highlight COMPANY in the Columns list, then press the Select button.
This brings you into the List Box Formatter with the selected column added to the list. The tabs on
the right allow you to format the appearance of the column highlighted in the list on the left.
80
Learning Clarion
3. In the Property list in the lower right pane, locate the Right Border and Resizeable
properties and set them to TRUE.
This once again adds the resizable column divider between this and the next column.
As the list box begins to grow in size, you can resize the List Box Formatter as needed to view
additional elements.
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.
3. Type Address Info in the Text property.
81
Learning Clarion
This provides the text for the group header. 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.
4. Highlight the ADDRESS column:
Populate the seventh column, and exit the List Box Formatter
At this time, feel free to use the WYSIWYG display at the top to resize any of the columns as
needed.
82
Learning Clarion
3. CLICK and drag the LIST and BUTTONS to select all of them, then use your mouse
or arrow keys to move all controls down as shown here:
83
Learning Clarion
4. If the Controls Toolbox is not already opened, open it now by selecting View
Toolbox from the IDE Menu (or simply press CTRL + ALT + X)
5. CLICK and DRAG the SHEET control in the Controls toolbox above and to the left of
the List box to place the property sheet and one Tab control.
84
Learning Clarion
6. DRAG the "white handle" at the bottom left-hand corner so that it appears just below
and to the left of the Insert button.
7. DRAG the "white handle" at the bottom right-hand corner so that it appears just
below and to the right of the Close button.
This resizes the property sheet so that it appears as though the list box and buttons are on the
tab. In fact, they are not, and we don‘t want them to be, since we want all these controls to be
visible no matter which tab the user selects.
8. The list and button controls appear to be hidden. RIGHT-CLICK on the SHEET
control JUST TO THE RIGHT OF THE TAB CONTROL, and select Send To Back
from the popup menu.
85
Learning Clarion
9. CLICK just below the "Tab1" text for the first tab in the sheet.
10. In the Properties Pad, type by Customer Number in the Text property, and then
press the TAB key.
This changes the tab‘s text. This tab text can be anything, but naming the key also names the
sort order it will display.
86
Learning Clarion
2. With the SHEET control selected and active in the Properties Pad, press the Add
Tab link.
3. Here‘s another way to select a control in your active window. In the Properties Pad,
select the ?TAB2 control in the drop list as shown here:
87
Learning Clarion
4. Type by Company Name in the Text entry of the Property toolbox, then press TAB.
5. With the Property Pad still opened for the ?TAB2 control, locate and click on the
Select Sheet link.
6. With the SHEET control selected and active in the Properties Pad, press the Add
Tab link.
7. In the Property Pad, select the ?TAB3 control in the drop list
8. Type by Zip Code in the Text entry of the Property toolbox, then press TAB.
88
Learning Clarion
Alternatively, you can select all of the buttons by pressing CTRL + CLICK, and then you can set
the Hide property to TRUE for all selected button controls.
89
Learning Clarion
3. Choose File Save, or press the Save button on the toolbar to save your work.
You used the List Box Formatter tool to design a scrolling list of rows.
You used the List Box Formatter tool to design a scrolling list of rows.
You added a Property Sheet and several Tabs to your screen design.
You used the Window Designer‘s Preview mode to see your window design in
action.
You set dynamic sort orders for the user based on which Tab control they
select.
Now that the first Browse procedure is complete, we‘ll go on and create its associated update
Form procedure.
90
Learning Clarion
Starting Point:
The LCLESSON.APP file should be open.
3. Type UpdateCustomer in the Update Procedure entry box at the bottom of the
Procedure Properties dialog.
91
Learning Clarion
This names the procedure to update the rows displayed in the browse. The new procedure
appears in the Application Tree as a "ToDo."
4. Press the OK button to close the Browse Customers – Properties dialog, and then
press the Save and Close button to close the Procedure Properties dialog.
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!
92
Learning Clarion
Notice that this dialog looks different than the Splash, Frame, or Browse Procedure Properties
dialogs, because the prompts vary for each type of Procedure template. The User‘s Guide and
on-line help describe the customization options available on each Procedure Properties dialog.
4. Press OK to close the Update Customer – Properties dialog.
5. Open the Data / Tables Pad to name the table the Form will update. If not already
opened, press the F12 key, or select View Data / Tables Pad from the IDE Menu.
6. Highlight the <ToDo> just under the Update Record on Disk entry and press the Add
button.
93
Learning Clarion
7. The Select a Table dialog appears. Highlight the Customer table in the Select dialog,
then press the Select button.
94
Learning Clarion
95
Learning Clarion
8. DOUBLE-CLICK on City in the Select Column toolbox, and then move the cursor to
where you want your control to appear, and CLICK.
9. DOUBLE-CLICK on State in the Select Column toolbox, and then move the cursor to
where you want your control to appear, and CLICK.
10. DOUBLE-CLICK on ZipCode in the Select Column toolbox, and then move the cursor
to where you want your control to appear, and CLICK.
11. Press Cancel in the Select Column toolbox to return to the Window Designer.
The sequence of steps above can also be accomplished directly via the Data / Tables Pad!
Simply DRAG a control from the Pad and DROP it on to the window. It‘s that easy, try it!
96
Learning Clarion
The Window Designer has an Alignment toolbar (shown above) that contains the same set of
alignment tools that are also available through the Window Designer > Format menu.
1. CLICK on the first prompt in the upper left corner.
This should be the CustNumber prompt. Its handles should appear when you CLICK it.
2. CTRL+CLICK on the four prompts immediately below the first.
As you CTRL+CLICK on each control in turn, the previously selected controls‘ handles turn blue
while the newest selected control‘s handles are red. The control with the red handles provides the
"base point" for the alignment operation. All the other selected controls are aligned in relation to
the control with the red handles.
3. Press the Align Left button (the top left button) in the Alignment toolbar.
The controls all line up along their left edges, based on the position of the last item selected (the
one with the red handles).
97
Learning Clarion
To identify the controls in the Alignment toolbar, simply place the mouse cursor over the control
and wait half a second for the tool tip to appear.
98
Learning Clarion
The form window is almost done. Now we will add a browse list box for the related Phones table
rows.
99
Learning Clarion
5. Highlight the Phones table in the Select a Table dialog, then press the Select button.
6. Highlight the Phones table in the Select Column dialog, then press the Change
button.
7. Highlight Pho:CustNumberKey in the Change Access Key dialog, then press the
Select button.
100
Learning Clarion
3. 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.
4. Select Center from the Data property list‘s DataJustification drop list:
This changes the data justification from the default value (which is Right justification for numeric
values).
5. Set the Right Border and Resizable properties to TRUE.
This adds a right border to the column and allows the user to resize the column width at runtime.
101
Learning Clarion
102
Learning Clarion
6. Press OK to close the Browse Box Behavior dialog. Press OK again to close the
Prompts dialog.
103
Learning Clarion
6. Choose File Save, or press the Save button on the tool bar.
You learned just how quickly you can populate data entry controls onto a
Form by using the Columnbox toolbox.
You learned to use the Window Designer‘s tools to move and align
controls.
You used the List Box Formatter again and created a range limited list box.
You learned how to implement edit-in-place for simple updates which don‘t
require a Form procedure.
Now you‘ve created all the procedures necessary to maintain both the Customer and Phones
tables. Next, we‘ll create the procedures that will maintain the Products data table.
104
Learning Clarion
105
Learning Clarion
106
Learning Clarion
9 - Copying Procedures
The Products Table Procedures
Now that we‘ve created the Customer Browse procedure, we can reuse much of that work for the
next procedure by copying the procedure, then changing its columns. In this chapter, you‘ll copy
the BrowseCustomers procedure to create the BrowseProducts procedure.
You will also use "Embed points" to write "embedded source code" to call the BrowseProducts
procedure from your application‘s menu and toolbar. This will introduce you to the numerous
points at which you can add a few (or many) lines of your own source code to add functionality to
any procedure.
Starting Point:
The LCLESSON.APP file should be open, and the Application Editor (tree).
107
Learning Clarion
108
Learning Clarion
The menu selection is a control, just as an entry box on the window is.
You‘ll notice that there are some up and down buttons and a spin box at 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.
5. Locate the ?BrowseProducts folder, then CLICK on it to expand it.
6. Highlight Accepted then press the Insert button.
The "Accepted" event for this menu selection marks the point in the generated code that executes
when the user chooses the menu command.
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.
7. Select the InitiateThread Code template, then press the Select button.
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.
8. Choose BrowseProducts from the Procedure Name dropdown list.
This names the procedure to call when the user chooses the menu item. This is the name of the
procedure you previously copied.
9. Press the OK button.
109
Learning Clarion
3. Highlight Accepted then press the Paste button located on the toolbar.
The Procedure name clash dialog appears again to warn you that you‘ve already called this
procedure once.
4. Press the Same button.
Now this embed point will generate the same code as the previous one.
110
Learning Clarion
2. RIGHT-CLICK on the list box control and choose List Box Format... from the popup
menu.
3. In the List Box Formatter press the Delete button repeatedly until all the
columns are removed.
111
Learning Clarion
9. Choose File Save, or press the Save button on the tool bar.
112
Learning Clarion
This dialog also allows you to add and maintain Extension templates to the procedure. Extension
templates are very similar to Control templates, in that they add specific functionality to the
procedure, but an Extension template‘s functionality is not directly associated with any control(s)
on the window. In other words, Extension templates add "behind the scenes" functionality to a
procedure that don‘t directly affect the user interface.
A very good example of Extension templates comes in Clarion‘s ASP (Active Server Pages)
product. Clarion ASP contains (among its other development tools) a set of Extension templates,
which automatically "translate" a Clarion procedure into dynamic ASP pages.
2. Highlight Browse on Products then press the Properties button.
113
Learning Clarion
4. In the Data / Tables Pad, highlight the <ToDo> table, then press the Add button.
5. Highlight the Products table then press the Select button.
6. Press the Window tab and press the Designer button to enter the Window Designer
to design your form.
114
Learning Clarion
3. Choose File Save, or press the Save button on the toolbar to save your work.
115
Learning Clarion
You used a Code Template in the Embedded Source dialog to call the new
procedure from the main menu.
You created an entire Form procedure very quickly by just using the Populate
Column toolbox.
Now that you‘re thoroughly familiar with Procedure Templates, we‘ll go on to use some Control
and Extension Templates.
116
Learning Clarion
Starting Point:
The LCLESSON.APP file and Application Editor (tree) should be open.
117
Learning Clarion
118
Learning Clarion
5. RIGHT-CLICK on the Browse box just populated, and select List Box format… from
the popup menu.
16. Finally, examine the Header text property of each column, and modify as follows:
Cust#
Ord#
Inv Amt
Date
Note
119
Learning Clarion
120
Learning Clarion
121
Learning Clarion
4. Highlight OrderNumberKey in the Change Access Key dialog, then press the Select
button.
5. RIGHT-CLICK on the Browse box just populated, and select List Box format… from
the popup menu.
6. Press the Add Field button.
7. Highlight OrderNumber in the Columns list, then press the Select button.
8. Highlight the "dummy" field just above the OrderNumber column just added, and
press the Remove button to remove it.
9. Press the Add Field button.
10. Highlight ProdNumber in the Columns list, then press the Select button.
11. Press the Add Field button.
12. Highlight Quantity in the Columns list, then press the Select button.
13. Press the Add Field button.
14. Highlight ProdAmount in the Columns list, then press the Select button.
15. As you did in the first Browse Box, adjust the column Width property as needed, set
the Resizable and RightBorder property to TRUE as before, and adjust the Header
Text as needed.
16. Press the OK button to close the List Box Formatter.
17. Resize the browse list box control by dragging the handles, making it an appropriate
size for display (but leave some space to its right for a button we‘re going to place in
the bottom right corner of the window).
1. RIGHT-CLICK on the list box you just placed and select Actions from the popup
menu.
2. Press the ellipsis ( ... ) button for the Range Limit Field.
3. Highlight the DTL:OrderNumber column in the Components list, then press the
Select button. (Note: this column may populate automatically)
4. Choose File Relationship from the Range Limit Type drop list.
5. Press the ellipsis ( ... ) button in the Related File entry.
6. Highlight the Orders table in the Select Table from Procedure Schema list, then press
the Select button.
These last four steps limit the range of rows displayed in the second list box to only those Detail
rows related to the currently highlighted row in the Orders table‘s list box.
This tells the second control template to use the table relationship defined in the data dictionary to
synchronize the bottom list to the top.
7. Press OK to close the Actions(Prompts) dialog.
122
Learning Clarion
1. Make sure the second Browse Box is selected, and refer to the Properties Pad.
2. Set the Vertical and Horizontal properties to TRUE.
This adds horizontal and vertical scroll bars to the list box.
3. Press the ellipsis button to the right of the TextFont property.
Although there are no "long" columns in this list box, it will look better if you match the font to the
same font used in the top list box.
4. Choose a font (your choice), and set the size to 8 points.
5. Press OK to close the Select Font dialog.
123
Learning Clarion
2. Press the Reset Fields button, then press the Insert button.
3. Type ORD:InvoiceAmount in the Reset Column entry (or press the ellipsis and
select it from the select colukn list) , and then press the OK button.
This specifies that the Detail table‘s list box should reset itself whenever the value in the
ORD:InvoiceAmount column changes. This ensures that any changes you make to an existing
order are reflected in this dialog when you return from changing the order.
4. Press the OK button to close the Reset Fields dialog, and then press the OK button
to return to the Procedure Properties dialog.
124
Learning Clarion
2. Choose File Save, or press the Save button on the tool bar to save your work.
You created a new browse procedure, but did it using the BrowseBox and
BrowseUpdateButtons Control Templates instead of the Browse Procedure
Template.
You created a second, range-limited list box to display related child rows.
You set a Reset Column on the Detail table‘s Browse list so its display is
always kept current.
Next we‘ll create the UpdateOrder Form procedure to create and maintain the Orders and Detail
table rows.
125
Learning Clarion
126
Learning Clarion
11 - Advanced Topics
Set Up the UpdateOrder Form
For the Order Update form, we‘ll place the columns from the Order table on an update form,
perform an automatic lookup to the Customer table, add a BrowseBox Control template to show
and edit the related detail items, calculate each line item detail, then calculate the order total.
Starting Point:
The LCLESSON.APP file and Application Editor (Tree) should be open.
1. In the Data / Tables Pad dialog, highlight the <ToDo> item under the Update Record
on Disk, then press the Add toolbar button.
2. Highlight the Orders table from the Select list, then press the Select button.
3. Highlight the Orders table, and in the bottom half of the Data / Tables Pad, highlight
OrderDate, then DRAG near the top left of the window, and DROP.
4. Back in the Data / Tables Pad, highlight OrderNote, then DRAG just to the right of the
entry box placed for the date.
5. Switch to the Toolbox Pad, and if not visible, open it by selecting View Toolbox
from the IDE Menu.
6. Choose (DRAG) the ENTRY control from the Toolbox Pad and drop it under the
OrderDate entry control.
7. With the Entry control still selected, press the F4 key to switch focus to the
Properties Pad.
8. Enter ORD:CustNumber in the Use property.
Since the ORD:CustNumber column is setup as a string in the dictionary, we need to add an
entry control.
9. Switch back to the Toolbox Pad, and choose (DRAG) the PROMPT control from the
Toolbox Pad and drop it to the left of the CustNumber entry control.
127
Learning Clarion
10. Note the Edit Value Smart Tag shown just after populating. Click on it, and enter
&Cust Number: in the Text control:
11. With the PROMPT control still selected, press the F4 key to switch focus to the
Properties Pad.
12. Change the Use property to ?CustomerPrompt.
128
Learning Clarion
3. Highlight the Orders table in the Select Key dialog, then press the Add button.
4. Highlight the Customer table in the Select list, and press the Select button.
These last two steps add the Customer table to the procedure‘s Table Schematic as an automatic
lookup from the Orders table. This will automatically lookup the related Customer table row for
you, and the lookup is based on the table relationship set up in the data dictionary.
5. Highlight CUS:KeyCustNumber in the Select Key dialog, then press the Select
button.
This makes CUS:KeyCustNumber the key that will be used to attempt to get a matching valid row
from the Customer table for the value the user enters into this control.
6. Press the ellipsis button ( ... ) for the Lookup Field entry box. This makes
CUS:CustNumber the column that must contain the matching value to the value the
user enters into this control.
7. Choose the BrowseCustomers procedure from the Lookup Procedure drop list.
This calls the BrowseCustomers procedure when the user enters an invalid customer number, so
the end user can select from a list of customers.
8. Check the Perform Lookup during Non-Stop Select and Force Window Refresh
when Accepted boxes to ensure that the data displayed on screen is always valid
and current.
129
Learning Clarion
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.
11. Highlight the Selected event under Control Events then press the Insert button.
The Selected event occurs just before the control gains input focus. This embed point allows you
to do any "setup" specific to this one control.
12. Highlight Source then press the Select button.
This calls the Text Editor to allow you to write any Clarion source code you want. Notice that the
floating Populate Column toolbox is present. Whenever you DOUBLE-CLICK on a column in this
toolbox, it places the name of the column (including any prefix) in your code at the insertion point.
This not only helps your productivity (less typing), but also ensures you spell them correctly
(eliminating misspelled column name compiler errors).
Type the following code:
13. Press the Save and Close button to return to the Embedded Source dialog.
130
Learning Clarion
It is "standard Windows behavior" 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 is very different from the way DOS programs work). 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 behavior" 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.
14. Press the Save and Close button to return to the Window Designer.
131
Learning Clarion
5. Highlight OrderNumberKey in the Select Key from Detail dialog, then press the
Select button.
6. Highlight ProdNumber in the Columns list, then press the Select button.
7. RIGHT-CLICK on the Browse box just populated, and select List Box format… from
the popup menu.
8. Select Center from the Data group‘s DataJustification property drop list.
132
Learning Clarion
133
Learning Clarion
Change the form window caption and exit the Window Designer
1. CLICK on the caption bar of the sample window.
2. Type Order Form in the Title field in the Properties Pad toolbox, then press the TAB
key.
3. Choose Save and Close to close the Window Designer.
134
Learning Clarion
2. Highlight Format Browse in the Template Classes list, and press the Insert button to
add a new formula.
135
Learning Clarion
136
Learning Clarion
13. Press the Save and Close button to close the Formula Editor.
The Formula Editor dialog with a formula list re-appears.
137
Learning Clarion
7. Highlight the Detail table, and then ProdNumber, and then press the Select button.
The ABC Library contains an object class called EditEntryClass that is the default Edit in Place
class. We‘re going to override some of the methods of the default class for this column so we can
modify the default behavior. Adding this column to the Column Specific list of columns is what
allows us to override the default Edit in Place methods for this one column. We will do this so that
we can perform data validation on this column when the user enters data—we want to make sure
that they can only enter a number that refers to a valid Products table row.
8. Press the OK button.
9. Press the Insert button.
10. Press the ellipsis ( ... ) button for the Field field.
11. Highlight DTL:Quantity, then press the Select button.
The ABC Library‘s EditEntryClass defaults to using an ENTRY control, and for this column we
want to use a SPIN control so the user can just spin to the quantity they want to order. Therefore,
we need to override some methods for this column too, to have a SPIN instead of an ENTRY
control.
12. Clear the Use Default ABC check box.
This allows you to specify the exact class from which to derive.
13. Choose EditSpinClass from the Base Class drop list.
The EditEntryClass does too much that‘s unnecessary for this, since we‘re going to override the
methods anyway. Therefore, we‘re going to derive and override from the Base Class all the Edit
in place classes were derived from: EditClass.
14. Press the OK button.
15. Press the Insert button.
16. Press the ellipsis ( ... ) button for the Field field.
17. Highlight DTL:ProdAmount, then press the Select button.
18. Clear the Allow Edit in Place check box.
This turns off Edit in Place for this one column of the list box.
Only columns in the Primary table for the BrowseBox can be edited in place, and the default is
that all Primary table columns are editable. In this case, this means all the Detail table columns
can be edited and the Products table columns cannot be edited. However, for this procedure we
do NOT want the user to be able to edit the DTL:ProdAmount column because we‘re going to get
its value directly from the Products table and we don‘t want the user to be able to change it.
That‘s why we turned off the Allow Edit in Place box.
19. Press the OK button twice to return back to the Extensions dialog, then press Save
and Close to return to the Application Tree. Save your work by pressing the Save
138
Learning Clarion
The Source popup menu selection 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 Setup Application 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 very important 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.
139
Learning Clarion
updated, too. There‘s fairly simple way to do that which will allow us to demonstrate the ABC
Library‘s flexible error handling.
1. CLICK on the Next embed button (about 5 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 most any "chunk" of generated code.
2. Type (or copy and paste) the following code:
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)
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.
3. Choose Search Find to bring up the Find dialog.
4. Type ThisWindow.Init into the Find what entry, then press the Find button.
This takes you directly to the ThisWindow.Init method. Press Close (the Red X) to close the
Search and Replace dialog.
5. In the embed point immediately following the line of code reading SELF.Errors &=
GlobalErrors (this should be at Priority 5300), type the following code:
140
Learning Clarion
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 of 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.
6. Press Search Find (or press CTRL + F) to open the search dialog again. Type
ThisWindow.Kill into the Find what entry, then press the Find button.
7. In the embed point immediately following the line of code reading CODE (this should
be [Priority 2300]), type the following code:
SELF.Errors.RemoveErrors(LocalErrGroup) !Remove custom error
141
Learning Clarion
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.
8. Type EVENT:CloseWindow into the Find what field, then press the Find next button.
You will need to press the Find button twice to find the embed point referenced in
Step 9. Close the Search dialog once you have found the desired embed point.
9. In the embed point immediately following the line of code reading OF
EVENT:CloseWindow (this should be Priority 2500), type the following code:
142
Learning Clarion
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 if all three conditions are true.
4. By pressing the + button on the tree, expand the Local Objects ABC Objects EIP
Field Manager for Browse Using ?List for column DTL:Quantity (EditSpinClass) Init
Code Parent Call.
143
Learning Clarion
5. Press the Source button. You should now be in the embed point immediately
following the line of code reading PARENT.Init(FieldNumber,Listbox,UseVar)
(this should be [Priority 5300]), type the following code:
SELF.Feq{PROP:Text} = ListBox{PROPLIST:Picture,FieldNumber}
!Set entry picture token
SELF.Feq{PROP:RangeLow} = 1 !Set RANGE values for the SPIN
SELF.Feq{PROP:RangeHigh} = 9999
This code sets the data entry picture token and range of valid data for the SPIN control.
6. Scroll to the EditInPlace::DTL:Quantity.SetAlerts method (this should
immediately follow the EditInPlace::DTL:Quantity.ResetControl method). Enter the
following code:
SELF.Feq{PROP:Alert,5} = ''
SELF.Feq{PROP:Alert,6} = ''
If you wish to see the base class code that we‘ve overridden, open the
\CLARION7\LIBSRC\ABEIP.CLW file and search for EditSpinClass.
7. Press Search Find to open the search dialog again. Type
EditInPlace::DTL:ProdNumber.TakeEvent into the Find what field, then press the
Find button.
8. In the embed point immediately following the line of code reading ReturnValue =
PARENT.TakeEvent(Event) (this should be [Priority 5300]), type (or copy and paste)
the following code:
UPDATE(SELF.Feq) !Update Q field
IF ReturnValue AND ReturnValue <> EditAction:Cancel OR |
EVENT() = EVENT:Accepted !Check for completion
PRD:ProdNumber = BrowseDTL.Q.DTL:ProdNumber !Set for lookup
IF Access:Products.Fetch(PRD:ProdNumberKey) !Lookup Products row
GlobalRequest = SelectRecord !If no row, set for select
BrowseProducts ! then call Lookup proc
IF GlobalResponse <> RequestCompleted !Row selected?
CLEAR(PRD:Record) ! if not, clear the buffer
ReturnValue = EditAction:None ! and set the action to
END ! stay on same entry field
END
BrowseDTL.Q.DTL:ProdNumber = PRD:ProdNumber !Assign Products table
BrowseDTL.Q.DTL:ProdAmount = PRD:ProdAmount ! values to Browse QUEUE
BrowseDTL.Q.PRD:ProdDesc = PRD:ProdDesc ! fields
DISPLAY ! and display them
END
144
Learning Clarion
This is the really interesting code. Notice that the first executable statement (generated for you) is
a call to the PARENT.TakeEvent method. This calls the EditSpinClass.TakeEvent method we‘re
overriding so it can do what it usually does.
All the rest of the code is there to give this derived class extra functionality not present in its
parent class. This is the real power of OOP—if you want everything the parent does, plus a little
bit more you don‘t have to duplicate all the code the parent executes, you just call it. In this case,
all the extra code is to perform some standard data entry validation tasks. This code will verify
whether the user typed in a valid Product Number and if they didn‘t, it will call the ViewProducts
procedure to allow them to choose from the list of products.
145
Learning Clarion
3. Choose File Save, or press the Save button to save your work.
Generate code
1. Press the Generate All button in the IDE toolbar to generate the source code
for your application.
2. RIGHT-CLICK on UpdateOrder and select Module 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.
3. 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.
You created a "Scrolling-Form" metaphor Edit-in-place browsebox to update the Detail table.
You used the Embeditor to write your embedded source code within the context of template-
generated code.
You used the power of OOP to extend the standard error handling functionality of the ABC
Library.
You used the power of OOP to derive and override the Edit-in-place classes to extend the
standard functionality of the ABC Library.
You generated source code to compare the difference between the code shown in the
Embeditor to that which is actually generated.
We‘re almost finished with this application. In the next lesson we‘ll create the application‘s
reports.
146
Learning Clarion
147
Learning Clarion
12 - Creating Reports
Overview
The last item to cover is adding reports. First, we‘ll create a simple customer list to introduce you
to the Report Designer. Then we‘ll create an Invoice Report to demonstrate how you can easily
create Relational reports with multi-level group breaks, group totals, and page formatting. Then
we‘ll copy the Invoice Report and limit the copy to print invoice for only one customer at a time.
Starting Point:
LCLESSON.APP should be open, and the Application Tree displayed.
148
Learning Clarion
1. RIGHT-CLICK on the Print &Customer List item in the Menu Editor list, and select the
Actions item from the popup menu.
2. Choose Call a Procedure from the When Pressed drop list.
3. Type CustReport in the Procedure Name entry.
4. Check the Initiate Thread box.
5. Press OK to close the Actions (?ITEM1 Prompts) dialog.
10. Save your work by pressing the Save button on the IDE toolbar.
149
Learning Clarion
3. RIGHT-CLICK on the control, then choose Properties from the popup menu.
The String Properties dialog appears.
4. In the Properties Pad, type Page Number: in the Text property.
150
Learning Clarion
2. In the Select Column dialog, highlight the <ToDo> folder, then press the Add button.
3. Select the Customer table from the Select a Table dialog, then press the Select
button.
4. Highlight the Customer table, and press the Change button.
5. Highlight KeyCustNumber in the Select Key from CUSTOMER dialog, then press the
Select button.
6. Highlight Company in the Select Columns list in the right pane,and press the Select
button.
7. CLICK inside the Detail band, near its top left corner.
8. Highlight FirstName in the Select Columns list and press the Select button.
9. CLICK inside the Detail band, just below the first control.
10. Highlight LastName in the Select Columns list and press the Select button.
11. CLICK inside the Detail band, to the right of the control you just placed.
12. Highlight CUS:Address in the Select Columns list and press the Select button.
13. CLICK inside the Detail band, below the second control you placed.
151
Learning Clarion
At this point, you probably have very little room left in the Detail band, and need to make it longer.
1. Press the Cancel button to exit multi-populate mode.
2. CLICK inside the Detail band, but not on one of the string controls.
The Detail area‘s handles appear.
3. Resize the Detail band by dragging the middle handle on the bottom down—allow for
enough room for about two more lines.
Notice that you have the same set of alignment tools in the Report Designer that you have
already used in the Window Designer. Feel free to align and adjust the position of your controls at
this time.
152
Learning Clarion
2. In the Property Pad, locate the TextFont property, and press the ellipsis button to the
right.
3. Select a font, style, and size to use as the base font for the report.
If you don‘t select a font, it uses the printer‘s default font.
4. Press the OK button to close the Select Font dialog.
1. In the Report Designer toolbar, press the Print Preview button to "visualize"
how the printed page will appear.
2. Highlight Detail in the Details list then press the Add button several times.
This populates the preview with some print bands to view. Because you can have many bands of
various types within a single report, you have to select which to see before going to print preview.
This way, the Report Designer knows what to compose on the screen.
153
Learning Clarion
7. Choose File Save, or press the Save button on the tool bar to save your work.
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.
4. Highlight the the Customer table, and press the Change button.
5. Highlight KeyCustNumber in the Select Key from CUSTOMER dialog, then press the
Select button.
The report will process all the Customer table rows in CustNumber order.
6. Highlight the Customer table, then press the Add button.
7. Select the Orders table from the Related Tables tab, then press the Select button.
It will process all the Orders for each Customer.
8. Highlight the Orders table, then press the Add button.
9. Select the Detail table from the Related Tables tab, then press the Select button.
Each Order will print all the related Detail rows.
10. Highlight the Detail table, then press the Add button.
11. Select the Products table from the Related Tables tab, then press the Select button.
154
Learning Clarion
Each Detail row will lookup the related Products table row.
155
Learning Clarion
3. Back in the Data / Tables Pad, highlight ProdNumber in the Columns list and DRAG
the column and DROP directly to the right of the first control.
4. Highlight Products in the Tables list then DRAG ProdDesc in the Columns list and
DROP it to the right of the control just placed.
5. Highlight Detail in the Tables list then DRAG ProdAmount in the Columns list and
DROP it to the right of the control just placed.
6. Highlight LOCAL DATA InvoiceReport in the Tables list, then press the Add button.
156
Learning Clarion
This local variable will be used to display the total price for each line item.
7. Type LOC:ItemTotal in the Column Name entry.
8. Select DECIMAL from the Data Type drop list.
9. Type 7 in the Characters entry.
10. Type 2 in the Places entry then press the OK button.
11. Highlight LOCAL DATA InvoiceReport, and DRAG LOC:ItemTotal and DROP it to the
right of the last control placed.
12. 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
DTL:ProdAmount and LOC:ItemTotal to change the Justification to Right Justified
and the Offset to 2.
157
Learning Clarion
158
Learning Clarion
The Group Footer (CUS:CustNumber) band appears below the Group Footer
(ORD:OrderNumber) band. This band will print every time the value in the CUS:CustNumber
column changes, at the end of each group of rows. We will use this to print invoice summary
information for each company.
6. RIGHT-CLICK on the Group Footer (CUS:CustNumber) band then choose
Properties from the popup menu.
7. Enter 1 in the PageAfter property.
159
Learning Clarion
1. Open the Control ToolBox if not opened already. DRAG the STRING from the
Controls toolbox and DROP inside the Group Header (ORD:OrderNumber) band, left
of the ORD:OrderNumber control you placed.
2. As the control is populated, select the Edit Value "smart link" and enter Order
Number: in the Edit Text Value dialog, and press the Accept button..
3. Again, from the Toolbox Control Pad, DRAG the STRING from the Controls toolbox
and DROP inside the Group Header (ORD:OrderNumber) band, left of the
ORD:OrderDate control you placed.
4. As the control is populated, select the Edit Value "smart link" and enter Order Date:
in the Edit Text Value dialog, and press the Accept button.
5. Again, from the Toolbox Control Pad, DRAG the STRING from the Controls toolbox
and DROP inside the Group Header (ORD:OrderNumber) band, at the left end below
the Customer table controls you placed.
6. As the control is populated, select the Edit Value "smart link" and enter Quantity: in
the Edit Text Value dialog, and press the Accept button.
7. From the Toolbox Control Pad, DRAG the STRING from the Controls toolbox and
DROP inside the Group Header (ORD:OrderNumber) band, inside the Group Header
(ORD:OrderNumber) band, to the right of the last string you placed.
8. As the control is populated, select the Edit Value "smart link" and enter Product: in
the Edit Text Value dialog, and press the Accept button.
9. From the Toolbox Control Pad, DRAG the STRING from the Controls toolbox and
DROP inside the Group Header (ORD:OrderNumber) band, inside the Group Header
(ORD:OrderNumber) band, to the right of the last string you placed, directly above
the DTL:ProdAmount control in the Detail band.
10. As the control is populated, select the Edit Value "smart link" and enter At: in the Edit
Text Value dialog, and press the Accept button.
11. From the Toolbox Control Pad, DRAG the STRING from the Controls toolbox and
DROP inside the Group Header (ORD:OrderNumber) band, inside the Group Header
(ORD:OrderNumber) band, to the right of the last string you placed, directly above
the LOC:ItemTotal control in the Detail band.
160
Learning Clarion
12. As the control is populated, select the Edit Value "smart link" and enter Item Total in
the Edit Text Value dialog, and press the Accept button.
161
Learning Clarion
162
Learning Clarion
163
Learning Clarion
10. RIGHT-CLICK and choose Properties from the popup menu to bring focus to the
Properties Pad.
11. Set the VariableString property to TRUE.
12. Type LOC:ItemTotal in the Use property entry.
13. Select Sum from the TotalType drop property list.
14. Select CustNumberBreak from the Reset property drop list.
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.
3. Press the Save and Close button to return to the Procedure Properties dialog.
164
Learning Clarion
Adding a Formula
To make the ItemTotal column contain the correct amount for each Detail row in the invoice, you
need to add a Formula to the procedure.
1. Press the Formulas tab in the Procedure Properties dialog.
2. Press the Insert button in the Formula Editor dialog.
The Formula Editor design dialog appears.
3. Type Item Total Formula in the Name entry.
4. In the Class entry drop list, select Before Print Detail in 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.
5. Press the ellipsis ( ... ) button for the Result entry.
6. Highlight LOCAL DATA InvoiceReport in the Tables list, select LOC:ItemTotal from
the Columns list, then press the Select button.
7. Press the Data button in the Operands group.
8. Highlight the Detail table in the Tables list, select DTL:Quantity from the Columns list,
then press the Select button.
This places the DTL: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.
9. Press the * button in the Operators group.
10. Press the Data button in the Operands group.
11. Highlight the Detail table in the Tables list, select DTL:ProdAmount from the Columns
list, then press the Select button.
12. Press the Check button to check the expression‘s syntax.
13. Press the Save and Close button to close the Formula Editor design window.
165
Learning Clarion
If it retrieves a Parent row without a Child, the columns in the Child table are all blank or zero,
while the Parent table‘s columns contain valid data. Therefore, this is the condition for which we
test.
ORD:OrderNumber <> 0 checks to see if the ORD:OrderNumber column has any value in it other
than zero. Since ORD:OrderNumber is the key column in the Orders table that creates the link to
the related Customers table row, it must contain a value if there are existing Orders table rows for
the current Customer.
If ORD:OrderNumber does not contain a value other than zero, the current Customers table row
is skipped ("filtered out"). This eliminates printing Parent rows without related Children (in this
case, any Customers without Orders).
This means that a filter would work. However, since the VIEW structure can do the filtering
required, there is a better way. This is the "inner join". What this means is that there is a
Customer row only if there is a related Order row. This makes the VIEW smaller, and thus more
effecient than a filter.
To use this feature, follow these steps:
1. Open the Data / Tables Pad
2. Select the Orders table and press the Change button.
3. Check the Inner box, then press the OK button.
4. Press the OK button to close this dialog.
166
Learning Clarion
167
Learning Clarion
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.
9. Highlight Source then press the Select button to call the Text Editor.
10. Type in the following code:
GlobalRequest = SelectRecord
This code sets up a Browse procedure to select a row (it enables the Browse procedure‘s Select
button).
11. Press the Save and Close button to return to the Embedded Source dialog.
12. Highlight the SOURCE you just added then press the Insert button.
13. Highlight Call a procedure then press the Select button.
14. Select BrowseCustomers in the list, then press the OK button.
168
Learning Clarion
This will generate a procedure call to the BrowseCustomers Browse procedure to allow the user
to select which Customer‘s Invoices to print.
Notice that there are now two entries displayed under the embed point. At each embed point you
can place as many items as you want, mixing Code Templates with your own SOURCE or
PROCEDURE Calls. You can also move the separate items around within the embed point using
the arrow buttons, changing their logical execution order (the first displayed is the first to
execute). Note well that moving them will change the assigned Priority option setting for the
moved item if you attempt to move a higher priority item in front of another with a lower priority
setting.
15. Press the Save and Close button to return to the Procedure Properties dialog.
169
Learning Clarion
170
Learning Clarion
We‘ve selected all the same tables, but now the Primary table is the Orders table and the related
Customer table row will be looked up. This is important, because we need to limit this report to a
single invoice and that would be much more difficult to do if the Customer table were the Primary.
171
Learning Clarion
1. Press the Save and Close button in the Procedure Properties dialog to close it.
2. Choose File Save, or press the Save button on the toolbar to save your work.
12. Choose File Save, or press the Save button on the toolbar to save your work.
You range-limited a report to print a single Invoice from the current row
highlighted in a Browse list.
What's Next?
Congratulations, you made it to the end of the Application Generator lesson!
172
Learning Clarion
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.
173
Learning Clarion
174
Learning Clarion
175
Learning Clarion
Event-driven Programming
Windows programs are event-driven. The user causes an event by CLICKing the mouse on a
screen control or pressing a key. Every such user action causes Windows to send a message (an
event) to the program which owns the window telling it what the user has done.
Once Windows has sent the message signaling an event to the program, the program has the
opportunity to handle the event in the appropriate manner. This basically means the Windows
programming paradigm is exactly opposite from the DOS programming paradigm—the operating
system (Windows) tells the program what the user has done, instead of the program telling the
operating system what to do.
This is the most important concept in Windows programming—that the user is in control (or
should be) at all times. Therefore, Windows programs are reactive rather than proactive; they
always deal with what the user has done instead of directing the user as to what to do next. The
most common example of this is the data entry dialog. In most DOS programs, the user must
follow one path from field to field to enter data. They must always enter data in one field before
they can go on to the next (and they usually can only go on to a specific "next" entry field). This
makes data validation code simple—it simply executes immediately after the user has left the
field.
In Windows programs, the user may use a mouse or an accelerator key to move from control to
control, at any given time, in no particular order, skipping some controls entirely. Therefore, data
validation code should be called twice to ensure that it executes at least once: once when the
user leaves the entry control after entering data, and again when the user presses OK to leave
the dialog. If it isn‘t executed on the OK button, required data could be omitted. This makes
Windows programs reactive rather than proactive.
176
Learning Clarion
Hello Windows
Traditionally, all programming language lessons begin by creating a "Hello World" type of
program—and so does this one.
Starting Point:
The Clarion environment should be open. The LCLESSON.APP should now be closed.
You should not need to type any code in this lesson. Simply COPY what you need directly from
this help file into the appropriate areas.
177
Learning Clarion
1. RIGHT-CLICK on the Hello project, and select the Properties item in the popup
menu.
The Project Properties dialog appears. Project properties dialog has 4 Tabs; Application,
Compiling, Debug, and Build Events.
2. The Output Path entry specifies where the output is created (use the ellipsis button
to select a directory from the Browse for Folder dialog).
3. Verify that the default Output type is set to Exe. This controls the type of file that the
Build action will create.
4. Verify that the default Link Mode is set to Dll.
5. Save your project changes by pressing the Save button on the IDE toolbar, and close
the Project Properties by pressing the Close ( X ) button.
The HELLO.CLW file in the Text Editor now has focus.
178
Learning Clarion
PROGRAM
MAP
END
MyWin WINDOW('Hello Windows'),SYSTEM
END
CODE
OPEN(MyWin)
ACCEPT
END
This code begins with the PROGRAM statement. This must be the first non-comment statement
in every Clarion program. Notice that the keyword PROGRAM is indented in relation to the word
MyWin. In Clarion, the only statements that begin in column one (1) of the source code file are
those with a statement label. A label must begin in column one (1), by definition. The PROGRAM
statement begins the Global data declaration section.
Next there is an empty MAP structure. The END statement is a required terminator of the MAP
data structure. A MAP structure contains prototypes which define parameter data types, return
data types, and various other options that tell the compiler how to deal with your procedure calls
(this is all covered later in this lesson). A MAP structure is required when you break up your
program‘s code into PROCEDUREs. We haven‘t done that yet, but we still need it because there
is an OPEN statement in the executable code.
When the compiler processes a MAP structure, it automatically includes the prototypes in the
\CLARION7\LIBSRC\BUILTINS.CLW file. This file contains prototypes for almost all of the Clarion
language built-in procedures (including the OPEN statement). If the empty MAP structure were
not in this code, the compiler would generate an error on the OPEN statement.
MyWin is the label of the WINDOW data structure (the "M" must be in column one). In Clarion,
windows are declared as data structures, and not dynamically built by executable code
statements as in some other languages. This is one of the aspects of Clarion that makes it a 4GL.
Although Clarion can dynamically build dialogs at runtime, it is unnecessary to do so. By using a
data structure, the compiler creates the Windows resource for each dialog, enabling better
performance at runtime.
179
Learning Clarion
The ('Hello Windows') parameter on the WINDOW statement defines the title bar text for the
window. The SYSTEM attribute adds a standard Windows system menu to the window. The END
statement is a required terminator of the WINDOW data structure. In Clarion, all complex
structures (both data and executable code) must terminate with an END or a period (.). This
means the following code is functionally equivalent to the previous code:
PROGRAM
MAP.
MyWin WINDOW('Hello Windows'),SYSTEM.
CODE
OPEN(MyWin)
ACCEPT.
Although functionally equivalent, this code would become much harder to read as soon as
anything is added into the MAP, WINDOW, or ACCEPT structures. By convention, we use the
END statement to terminate multi-line complex statements, placing the END in the same column
as the keyword it is terminating while indenting everything within the structure. We only use the
period to terminate single-line structures, such as IF statements with single THEN clauses. This
convention makes the code easier to read, and any missing structure terminators much easier to
find.
The CODE statement is required to identify the beginning of the executable code section. Data
(memory variables, data files, window structures, report structures, etc.) are declared in a data
section (preceding the CODE statement), and executable statements may only follow a CODE
statement.
Since this program does not contain any PROCEDUREs (we‘ll get to them in the next chapter), it
only has a Global Data section followed by three lines of executable code. Variables declared in
the Global Data section are visible and available for use anywhere in a program.
The OPEN(MyWin) statement opens the window, but does not display it. The window will only
appear on screen when a DISPLAY or ACCEPT statement executes. This feature allows you to
dynamically change the properties of the window, or any control on the window, before it appears
on screen.
ACCEPT is the event processor. Most of the messages (events) from Windows are automatically
handled internally for you by ACCEPT. These are the common events handled by the runtime
library (screen re-draws, etc.). Only those events that actually may require program action are
passed on by ACCEPT to your Clarion code. This makes your programming job easier by
allowing you to concentrate on the high-level aspects of your program.
The ACCEPT statement has a terminating END statement, which means it is a complex code
structure. ACCEPT is a looping structure, "passing through" all the events that the Clarion
programmer might want to handle (none, in this program—we‘ll get back to this shortly), then
looping back to handle the next event.
An ACCEPT loop is required for each window opened in a Clarion program. An open window
"attaches" itself to the next ACCEPT loop it encounters in the code to be its event processor.
For this program, ACCEPT internally handles everything the system menu (placed on the window
by the SYSTEM attribute) does. Therefore, when the user uses the system menu to close the
window, ACCEPT automatically passes control to any statement immediately following its
terminating END statement. Since there is no other explicit Clarion language statement to
execute, the program ends. When any Clarion program reaches the end of the executable code,
an implicit RETURN executes, which, in this case, returns the user to the operating system.
180
Learning Clarion
PROGRAM
MAP
END
MyWin WINDOW('Hello Windows'),AT(,,100,100),SYSTEM !Changed
STRING('Hello Windows'),AT(26,23),USE(?String1) !Added
BUTTON('OK'),AT(34,60),USE(?Ok),DEFAULT !Added
END
CODE
OPEN(MyWin)
ACCEPT
IF ACCEPTED() = ?Ok THEN BREAK. !Added
END
181
Learning Clarion
The Window Designer is available to you in the Text Editor, just as it is in the Application
Generator. To call the Window Designer, place the insertion point anywhere within the WINDOW
structure then press CTRL+F. The only restrictions are that the Control Template and Dictionary
Field tools are unavailable (they are specific to the Application Generator).
The change is the addition of the STRING and BUTTON controls to the WINDOW structure. The
STRING places constant text in the window, and the BUTTON adds a command buttton.
The only other addition is the IF ACCEPTED() = ?Ok THEN BREAK. statement. This statement
detects when the user has pressed the OK button and BREAKs out of the ACCEPT loop, ending
the program. The ACCEPTED statement returns the field number of the control for which
EVENT:Accepted was just generated (EVENT:Accepted is an EQUATE contained in the
\CLARION7\LIBSRC\EQUATES.CLW file, which the compiler automatically includes in every
program).
?Ok is the Field Equate Label of the BUTTON control, defined by the control‘s USE attribute (see
Field Equate Labels in the Language Reference). The compiler automatically equates ?Ok to the
field number it assigns the control (using Field Equate Labels helps make the code more
readable).
When the ACCEPTED procedure returns a value equal to the compiler-assigned field number for
the OK button, the BREAK statement executes and terminates the ACCEPT loop.
182
Learning Clarion
PROGRAM
MAP
END
MyWin WINDOW('Hello Windows'),AT(,,100,100),SYSTEM
STRING('Hello Windows'),AT(26,23),USE(?String1)
BUTTON('OK'),AT(34,60),USE(?Ok),DEFAULT
END
CODE
OPEN(MyWin)
ACCEPT
CASE FIELD() !Added
OF ?Ok !Added
CASE EVENT() !Added
OF EVENT:Accepted !Added
BREAK !Added
END !Added
END !Added
END
In this code you have one CASE structure nested within another. A CASE structure looks for an
exact match between the expression immediately following the keyword CASE and another
expression immediately following an OF clause (although these only show one OF clause each, a
CASE structure may have as many as necessary).
The CASE FIELD() structure determines to which control the current event applies. When the
FIELD procedure returns a value equal to the field number of the OK button (the ?Ok Field
Equate Label) it then executes the CASE EVENT() structure.
The CASE EVENT() structure determines which event was generated. When the EVENT
procedure returns a value equal to EVENT:Accepted (an EQUATE contained in the
\CLARION7\LIBSRC\EQUATES.CLW file) it then executes the BREAK statement.
Nesting CASE EVENT() within CASE FIELD() allows you to put all the code associated with a
single control in one place. You could just as easily nest a CASE FIELD() structure within a CASE
EVENT() structure, reversing the code, but this would scatter the code for a single control to
multiple places.
Again, you can close the window either with the system menu, or the OK buttton, just as with the
previous code, but now the code is structured in a common style.
183
Learning Clarion
PROGRAM
MAP
END
MyWin WINDOW('Hello Windows'),AT(,,100,100),SYSTEM
STRING('Hello Windows'),AT(26,23),USE(?String1)
BUTTON('OK'),AT(34,60),USE(?Ok),DEFAULT
END
CODE
OPEN(MyWin)
ACCEPT
CASE EVENT() !Added
OF EVENT:OpenWindow !Added
MESSAGE('Opened Window') !Added
OF EVENT:GainFocus !Added
MESSAGE('Gained Focus') !Added
END !Added
CASE FIELD()
OF ?Ok
CASE EVENT()
OF EVENT:Accepted
BREAK
END
END
END
184
Learning Clarion
The new CASE EVENT( ) structure handles two field-independent events: EVENT:OpenWindow
and EVENT:GainFocus. The MESSAGE procedure used in this code is just to visually display to
you at runtime that the event was triggered. Instead of the MESSAGE procedure, you would add
here any code that your program needs to execute when the user triggers these events.
This demonstrates the basic logic flow and code structure for procedural window procedures—an
ACCEPT loop containing a CASE EVENT() structure to handle all the field-independent events,
followed by a CASE FIELD() structure with nested CASE EVENT() structures to handle all field-
specific events.
Adding a PROCEDURE
In Hello Windows we have an example of a very simple program. Most modern business
programs are not that simple—they require the use of Structured Programming techniques. This
means you break up your program into functional sections, where each performs a single logical
task. In the Clarion language these functional sections are called PROCEDUREs.
First, we‘ll add a PROCEDURE to the Hello Windows program.
PROGRAM
MAP
Hello PROCEDURE !Added
END
CODE !Added
185
Learning Clarion
Hello !Added
The only changes are at the beginning of the program. Inside the MAP structure we now see the
Hello PROCEDURE statement which prototypes the Hello procedure. A prototype is the
declaration of the procedure for the compiler, telling the compiler what to expect when your code
calls the procedure. This prototype indicates that the procedure takes no parameters and does
not return a value. All PROCEDUREs in your program must be prototyped in a MAP structure.
See PROCEDURE prototypes in the Language Reference for more on prototypes.
The keyword CODE immediately following the MAP structure terminates the Global data section
and marks the beginning of the Global executable code section, which only contains the Hello
statement—a call to execute the Hello procedure. A PROCEDURE which does not return a value
is always called as a single executable statement in executable code.
The second Hello PROCEDURE statement terminates the Global executable code section and
marks the beginning of the code definition of the Hello procedure.
A PROCEDURE contains a data declaration section just as a PROGRAM does, and so, also
requires the keyword CODE to define the boundary between data declarations and executable
code. This is why the rest of the code did not change from the previous example. This is the Local
data declaration section for the PROCEDURE.
The biggest difference between Global and Local data declarations is the scope of the declared
data. Any data item declared in a Local data section is visible only within the PROCEDURE which
declares it, while any data item declared in the Global data section is visible everywhere in the
186
Learning Clarion
program. See Data Declarations and Memory Allocation in the Language Reference for a full
discussion of all the differences.
MAP
Hello PROCEDURE
EventString PROCEDURE(LONG PassedEvent),STRING !Added
END
This adds the prototype for the EventString PROCEDURE. EventString receives a LONG
parameter called PassedEvent that may not be omitted, and returns a STRING. The data types of
all parameters passed to a PROCEDURE are specified inside the parentheses following
PROCEDURE, each separated by a comma (if there are multiple parameters being passed). The
data type of the return value of a PROCEDURE is specified following the closing parenthesis of
the parameter list. Both types of PROCEDUREs may receive parameters, see Prototype
Parameter Lists in the Language Reference for a more complete discussion of parameter
passing.
187
Learning Clarion
OF EVENT:OpenWindow
ReturnString = 'Opened Window'
OF EVENT:GainFocus
ReturnString = 'Gained Focus'
ELSE
ReturnString = 'Unknown Event: ' & PassedEvent
END
RETURN(ReturnString)
The EventString label (remember, it must be in column one) on the PROCEDURE statement
names this procedure, while the parameter list attached to the PROCEDURE keyword names the
LONG parameter PassedEvent. There must always be an equal number of parameter names
listed on the PROCEDURE statement as there are parameter data types listed in the prototype
for that PROCEDURE.
ReturnString is a local variable declared as a 255 character STRING field, on the stack. The
CODE statement terminates the procedure‘s Local data section. The CASE PassedEvent
structure should look familiar, because it is the same as a CASE EVENT() structure, but its CASE
condition is the PassedEvent instead of the EVENT() procedure. This CASE structure simply
assigns the appropriate value to the ReturnString variable for each event that is passed to the
procedure.
The interesting code here is the RETURN(ReturnString) statement. A PROCEDURE without a
return value does not require an explicit RETURN, since it always executes an implicit RETURN
when there is no more code to execute. However, a PROCEDURE prototyped to return a value
always contains an explicit RETURN statement which specifies the value to return. In this case,
the RETURN statement returns whichever value was assigned to the ReturnString in the CASE
structure.
CASE EVENT()
OF EVENT:OpenWindow
MESSAGE(EventString(EVENT:OpenWindow)) !Changed
OF EVENT:GainFocus
MESSAGE(EventString(EVENT:GainFocus)) !Changed
END
This changes the MESSAGE procedures to display the returned value from the EventString
procedure. The event number is passed to EventString as the event EQUATE to make the code
more readable.
188
Learning Clarion
PROGRAM
MAP
Main PROCEDURE !Added
Hello PROCEDURE
EventString PROCEDURE(LONG PassedEvent),STRING
END
CODE
Main !Changed
This adds the Main PROCEDURE prototype to the MAP structure and replaces the call to Hello
with a call to the Main procedure.
Main PROCEDURE
AppFrame APPLICATION('Hello Windows'),AT(,,280,200),SYSTEM,RESIZE,MAX
MENUBAR
MENU('&File'),USE(?File)
ITEM('&Browse Phones'),USE(?FileBrowsePhones)
ITEM,SEPARATOR
ITEM('E&xit'),USE(?FileExit),STD(STD:Close)
END
ITEM('&About!'),USE(?About)
END
END
CODE
OPEN(AppFrame)
ACCEPT
CASE ACCEPTED()
OF ?About
189
Learning Clarion
Hello
END
END
The program executes and only the About! and File Exit items actually do anything. Notice,
though, that File Exit does terminate the program, despite the fact that we wrote no code to
perform that action.
190
Learning Clarion
PROGRAM
INCLUDE('Keycodes.CLW') !Added
INCLUDE('Errors.CLW') !Added
MAP
Main PROCEDURE
BrowsePhones PROCEDURE !Added
UpdatePhones PROCEDURE(LONG Action),LONG !Added
Hello PROCEDURE
EventString PROCEDURE(LONG PassedEvent),STRING
END
The two new INCLUDE statements add the standard EQUATEs for keycodes and error numbers.
Using the EQUATEs instead of the numbers makes your code more readable, and therefore,
more maintainable.
The MAP structure has acquired two more procedure prototypes: the BrowsePhones
PROCEDURE and UpdatePhones PROCEDURE(LONG Action),LONG. The BrowsePhones
procedure will display a list of the records in the file and UpdatePhones will update individual
records.
191
Learning Clarion
In the interest of coding simplicity (you‘ll see why shortly), the BrowsePhones procedure will
simply display all records in the file in a LIST control. This is not exactly the same as a Browse
procedure in template generated code (which is page-loaded in order to handle very large files),
but will serve a similar purpose in this program.
Also in the interest of coding simplicity, UpdatePhones is a PROCEDURE that takes a parameter.
The LONG parameter indicates what file action to take: ADD, PUT, or DELETE. The LONG return
value indicates to the calling procedure whether the user completed or aborted the action. Again,
this is not the same as a Form procedure in template generated code (which is a PROCEDURE
using Global variables to signal file action and completion status), but will serve the same
purpose in this simple program.
The Phones FILE declaration creates a simple data file using the TopSpeed file driver. There are
two data fields: Name and Number which are both declared as STRING(20). Declaring the
Number field as a STRING(20) allows it to contain phone numbers from any country in the world
(more about that to come).
The five EQUATE statements define constant values that make the code more readable.
InsertRecord, ChangeRecord, and DeleteRecord all define file actions to pass as the parameter
to the UpdatePhones procedure. The ActionComplete and ActionAborted EQUATEs define the
two possible return values from the UpdatePhones procedure.
2. Edit the Main procedure‘s CASE ACCEPTED() code to read:
CASE ACCEPTED()
OF ?FileBrowsePhones !Added
START(BrowsePhones,25000) !Added
OF ?About
Hello
END
The START(BrowsePhones,25000) statement executes when the user chooses the Browse
Phones menu selection. The START procedure creates a new execution thread for the
BrowsePhones procedure and the second parameter (25000) specifies the size (in bytes) of the
new execution thread‘s stack. You must use the START procedure when you are calling a
procedure containing an MDI child window from the APPLICATION frame, because each MDI
child window must be on a separate execution thread from the APPLICATION frame. If you do
not START the MDI child, you get the "Unable to open MDI window on APPLICATION‘s thread"
runtime error message when you try to call the BrowsePhones procedure and the program
immediately terminates.
Once you have started a new thread, an MDI child may simply call another MDI child on its same
thread without starting a new thread. This means that, although BrowsePhones and
UpdatePhones both contain MDI windows, only BrowsePhones must START a new thread,
because it is called from the application frame. BrowsePhones will simply call UpdatePhones
without starting a new thread.
3. Add the data section of the BrowsePhones PROCEDURE definition to the end of the
file:
192
Learning Clarion
BrowsePhones PROCEDURE
PhonesQue QUEUE
Name STRING(20)
Number STRING(20)
Position STRING(512)
END
window WINDOW('Browse Phones'),AT(,,185,156),SYSTEM,GRAY,RESIZE,MDI
LIST,AT(6,8,173,100),ALRT(MouseLeft2),USE(?List) |
,FORMAT('84L|M~Name~80L~Number~'),FROM(PhonesQue),VSCROLL
BUTTON('&Insert'),AT(20,117),KEY(InsertKey),USE(?Insert)
BUTTON('&Change'),AT(76,117,35,14),KEY(EnterKey),USE(?Change)
BUTTON('&Delete'),AT(131,117,35,14),KEY(DeleteKey),USE(?Delete)
BUTTON('Close'),AT(76,137,35,14),KEY(EscKey),USE(?Close)
END
The PhonesQue QUEUE structure defines the data structure that will contain all the records from
the Phones FILE to display in the LIST control. A Clarion QUEUE is similar to a data file because
it has a data buffer and allows an indeterminate number of entries, but it only exists in memory at
runtime. A QUEUE could also be likened to a dynamically allocated array in other programming
languages.
The PhonesQue QUEUE contains three fields. The Name and Number fields duplicate the fields
in the Phones FILE structure and will display in the two columns defined in the LIST control‘s
FORMAT attribute. The Position field will contain the return value from the POSITION procedure
for each record in the Phones FILE. Saving each record‘s Position will allow us to immediately re-
get the record from the data file before calling UpdatePhones to change or delete a record.
The window WINDOW structure contains one LIST control and four BUTTON controls. The LIST
control is the key to this procedure. The ALRT(MouseLeft2) on the LIST alerts DOUBLE-CLICK
so the ACCEPT statement will pass an EVENT:AlertKey to our Clarion code. This will let us write
code to bring up the UpdatePhones procedure to change the record the user DOUBLE-CLICKs
on.
The vertical bar (|) at the end of the LIST statement is the Clarion line continuation character,
meaning that the LIST control continues on the next line with the
FORMAT('84L|M~Name~80L~Number~') attribute (you can put it all on one line if you want). The
parameter to the FORMAT attribute defines the appearance of the LIST control at runtime.
It is best to let the List Box Formatter tool in the Window Designer write format strings for you,
since they can become very complex very quickly. This format string defines two columns. The
first column is 84 dialog units wide (84), left justified(L), has a right border (|) that is resizable (M)
and "Name" is its heading (~Name~). The second column is 80 dialog units wide (80), left
justified(L), and "Number" is its heading (~Number~).
193
Learning Clarion
FROM(PhonesQue) on the LIST specifies the source QUEUE the LIST control will display, and
VSCROLL adds the vertical scroll bar. The LIST will display the Name and Number fields of all
entries in the PhonesQue while ignoring the Position field because the FORMAT attribute only
specified two columns.
The LIST automatically handles users scrolling through the list without any coding on our part.
The LIST does this because there is no IMM (immediate) attribute present. If there were an IMM
attribute, we would have to write code to handle scrolling records (as the page-loaded Browse
procedure template does).
The BUTTON('&Insert') statement defines a command button the user will press to add a new
record. The KEY(InsertKey) attribute specifies that the button is automatically pressed for the
user when they press insert on the keyboard. Notice that the other three BUTTON controls all
have similar KEY atributes. This means you don‘t have to write any special code to handle
keyboard access to the program versus mouse access.
CODE
DO OpenFile
DO FillQue
OPEN(window)
ACCEPT
CASE EVENT()
OF EVENT:OpenWindow
DO QueRecordsCheck
OF EVENT:AlertKey
IF KEYCODE() = MouseLeft2
POST(EVENT:Accepted,?Change)
END
END
END
The beginning of the CODE section starts with the DO OpenFile statement. DO executes a
ROUTINE, in this case, the OpenFile ROUTINE, and when the code in the ROUTINE is done
executing, control returns to the next line of code following the DO statement that called the
ROUTINE.
The code defining a ROUTINE must be at the bottom of the procedure, following all the main
logic, because the ROUTINE statement itself terminates the executable code section of a
PROCEDURE.
There are two reasons to use a ROUTINE within a PROCEDURE: to write one set of code
statements that need to execute at multiple logical points within the procedure, or to make the
logic more readable by substituting a single DO statement for a set of code statements that
perform a single logical task.
194
Learning Clarion
In this case, the DO OpenFile statement serves the second purpose by moving the code that
opens or creates the data file out of the main procedure logic. Next, the DO FillQue statement
executes the code that fills the PhonesQue QUEUE structure with all the records from the data
file. These two DO statements make it very easy to follow the logic flow ofthe procedure.
The CASE EVENT() structure‘s OF EVENT:OpenWindow clause executes DO
QueRecordsCheck to call a ROUTINE that checks to see if there are any records in PhonesQue.
Next, the OF EVENT:AlertKey clause contains the IF KEYCODE() = MouseLeft2 structure to
check for DOUBLE-CLICK on the LIST. Since the ALRT(MouseLeft2) attribute only appears on
the LIST control, we know that the POST(EVENT:Accepted,?Change) statement will only execute
when the user DOUBLE-CLICKs on the LIST.
The POST statement tells ACCEPT to post the event in its first parameter (EVENT:Accepted) to
the control in its second parameter (?Change). The effect of the
POST(EVENT:Accepted,?Change) statement is to cause the EVENT:Accepted code for the
Change button to execute, just as if the user had pressed the button with the mouse or keyboard.
This illustrates a very good coding practice: write specific code once and call it from many places.
This is the structured programming concept that gave us PROCEDUREs and ROUTINEs. Even
though the EVENT:Accepted code for the change button is not sectioned off separately in a
PROCEDURE or ROUTINE, using the POST statement this way means that the one section of
code is all you need to maintain—if the desired logic changes, you‘ll only have to change it in one
place.
5. Add the rest of the executable code section of the BrowsePhones PROCEDURE
definition to the end of the file:
CASE FIELD()
OF ?Close
CASE EVENT()
OF EVENT:Accepted
POST(EVENT:CloseWindow)
END
OF ?Insert
CASE EVENT()
OF EVENT:Accepted
IF UpdatePhones(InsertRecord) = ActionComplete
DO AssignToQue
ADD(PhonesQue)
IF ERRORCODE() THEN STOP(ERROR()).
SORT(PhonesQue,PhonesQue.Name)
ENABLE(?Change,?Delete)
END
GET(PhonesQue,PhonesQue.Name)
SELECT(?List,POINTER(PhonesQue))
END
195
Learning Clarion
OF ?Change
CASE EVENT()
OF EVENT:Accepted
DO GetRecord
IF UpdatePhones(ChangeRecord) = ActionComplete
DO AssignToQue
PUT(PhonesQue)
IF ERRORCODE() THEN STOP(ERROR()).
SORT(PhonesQue,PhonesQue.Name)
END
GET(PhonesQue,PhonesQue.Name)
SELECT(?List,POINTER(PhonesQue))
END
OF ?Delete
CASE EVENT()
OF EVENT:Accepted
DO GetRecord
IF UpdatePhones(DeleteRecord) = ActionComplete
DELETE(PhonesQue)
IF ERRORCODE() THEN STOP(ERROR()).
DO QueRecordsCheck
SORT(PhonesQue,PhonesQue.Name)
END
SELECT(?List)
END
END
END
FREE(PhonesQue)
CLOSE(Phones)
Following the CASE EVENT() structure is the CASE FIELD() structure. Notice that each OF
clause contains its own CASE EVENT() structure, and each of these only contains an OF
EVENT:Accepted clause. Because of this, we could have replaced the CASE FIELD() structure
with a CASE ACCEPTED() structure and eliminated the nested CASEs. This would actually have
given us slightly better performance—too slight to actually notice on-screen, though. The reason
we didn‘t is consistency; you will more often have occasion to trap more field-specific events than
just EVENT:Accepted, and when you do, this nested CASE structure code is the logic to use, so
it‘s a good habit to make now. It also demonstrates the kind of code structure that is generated for
you by Clarion‘s templates in the Application Generator.
The OF ?Close clause executes the POST(EVENT:CloseWindow) when the user presses the
Close button. Since there‘s no second parameter to the POST statement naming a control, the
event posts to the WINDOW (and should be a field-independent event). EVENT:CloseWindow
causes the ACCEPT loop to terminate and execution control drops to the first statement following
196
Learning Clarion
the ACCEPT‘s terminating END statement. In this case, control drops to the FREE(PhonesQue)
statement, which frees all the memory used by the QUEUE entries (effectively closing the
QUEUE). The CLOSE(Phones) statement then closes the data file. Since there are no other
statements to execute following CLOSE(Phones) the procedure executes an implicit RETURN
and goes back to the Main procedure (where it was called from).
The OF ?Insert clause contains the IF UpdatePhones(InsertRecord) = ActionComplete structure.
This calls the UpdatePhones PROCEDURE, passing it the InsertRecord constant value that we
defined in the Global data section, and then checks for the ActionComplete return value.
The DO AssignToQue statement executes only when the user actually adds a record.
AssignToQue is a ROUTINE that assigns data from the Phones FILE‘s record buffer to the
PhonesQue QUEUE‘s data buffer. Then the ADD(PhonesQue) statement adds a new entry to
PhonesQue. The IF ERRORCODE() THEN STOP(ERROR()). statement is a standard error
check that you should execute after any FILE or QUEUE action that could possibly return an error
(another good habit to form).
The SORT(PhonesQue,PhonesQue.Name) statement sorts the PhonesQue QUEUE entries
alphabetically by the Name field. Since there is no PRE attribute on the PhonesQue QUEUE
structure, you must reference its fields using Clarion‘s Field Qualification syntax by prepending
the name of the structure containing the field (PhonesQue) to the name of the field (Name),
connecting them with a period (PhonesQue.Name). See Field Qualification in the Language
Reference for more information.
The ENABLE(?Change,?Delete) statement makes sure the Change and delete buttons are
active (if this was the first entry in the QUEUE, these buttons were dimmed out by the
QueRecordsCheck ROUTINE). The GET(PhonesQue,PhonesQue.Name) statement re-gets the
new record from the sorted QUEUE, then SELECT(?List,POINTER(PhonesQue)) puts the user
back on the LIST control with the new record highlighted.
The code in the OF ?Change clause is almost identical to the code in the OF ?Insert clause.
There is an added DO GetRecord statement that calls a ROUTINE to put the highlighted
PhonesQue entry‘s related file record into the Phones file record buffer. The only other difference
is the PUT(PhonesQue) statement that puts the user‘s changes back in the PhonesQue.
The code in the OF ?Delete clause is almost identical to the code in the OF ?Change clause. The
difference is the DELETE(PhonesQue) statement that removes the entry from the PhonesQue
and the call to DO QueRecordsCheck to see if the user just deleted the last record.
6. Add the ROUTINEs called in the BrowsePhones PROCEDURE definition to the end
of the file:
AssignToQue ROUTINE
PhonesQue.Name = Phones.Rec.Name
PhonesQue.Number = Phones.Rec.Number
PhonesQue.Position = POSITION(Phones)
QueRecordsCheck ROUTINE
IF NOT RECORDS(PhonesQue)
DISABLE(?Change,?Delete)
SELECT(?Insert)
ELSE
SELECT(?List,1)
END
197
Learning Clarion
GetRecord ROUTINE
GET(PhonesQue,CHOICE(?List))
IF ERRORCODE() THEN STOP(ERROR()).
REGET(Phones,PhonesQue.Position)
IF ERRORCODE() THEN STOP(ERROR()).
OpenFile ROUTINE
OPEN(Phones,42h)
CASE ERRORCODE()
OF NoError
OROF IsOpenErr
EXIT
OF NoFileErr
CREATE(Phones)
IF ERRORCODE() THEN STOP(ERROR()).
OPEN(Phones,42h)
IF ERRORCODE() THEN STOP(ERROR()).
ELSE
STOP(ERROR())
RETURN
END
FillQue ROUTINE
SET(Phones.NameKey)
LOOP
NEXT(Phones)
IF ERRORCODE() THEN BREAK.
DO AssignToQue
ADD(PhonesQue)
IF ERRORCODE() THEN STOP(ERROR()).
END
As you can see, there are five ROUTINEs for this procedure. Notice that, although these
ROUTINEs look similar to a PROCEDURE, they do not contain CODE statements. This is
because a ROUTINE shares the procedure‘s Local data and does not usually have a data
declaration section of its own (a ROUTINE can have its own data section—see ROUTINE in the
Language Reference for a full discussion of this issue).
The AssignToQue ROUTINE performs three assignment statements. The PhonesQue.Name =
Phones.Rec.Name statement copies the data in the Name field of the Phones FILE record buffer
and places it in the Name field of the PhonesQue QUEUE data buffer. Since there is no PRE
attribute on the Phones FILE structure, you must also reference its fields using Clarion‘s Field
198
Learning Clarion
Qualification Syntax by stringing together the FILE name (Phones), the RECORD name (Rec),
and the name of the field (Name), connecting them all with a period (Phones.Rec.Name).
The PhonesQue.Number = Phones.Rec.Number statement assigns the data in the Phones file‘s
Number field to the PhonesQue‘s Number field. The PhonesQue.Position = POSITION(Phones)
statement assigns the return value of the POSITION procedure to the PhonesQue.Position field.
This value lets us retrieve from disk the one specific record that is currently in the Phones file‘s
record buffer. The POSITION procedure does this for every Clarion file driver, and is therefore the
recommended method of specific record retrieval across multiple file systems.
The QueRecordsCheck ROUTINE checks to see if there are any records in the PhonesQue. The
IF NOT RECORDS(PhonesQue) structure uses the logical NOT operator against the return value
from the RECORDS procedure. If RECORDS(PhonesQue) returns zero, then the NOT makes the
condition true and the code following the IF executes (zero is always false and the NOT makes it
true). If RECORDS(PhonesQue) returns anything other than zero, the code following the ELSE
will execute (any non-zero number is always true and the NOT makes it false). Therefore, if there
are no records in the PhonesQue, DISABLE(?Change,?Delete) executes to dim out the Change
and Delete buttons, then the SELECT(?Insert) statement places the user on the Insert button (the
next logical action). If there are records in the PhonesQue, then the SELECT(?List) statement
places the user on the LIST.
The GetRecord ROUTINE synchronizes the Phones file‘s record buffer and PhonesQue‘s data
buffer with the currently highlighted entry in the LIST. The GET(PhonesQue,CHOICE(?List))
statement uses the CHOICE procedure to "point to" the currently highlighted entry in the LIST
and GET the related QUEUE entry into the PhonesQue‘s data buffer (of course, always checking
for unexpected errors). Then the REGET(Phones,PhonesQue.Position) statement uses the
saved record position information to retrieve the Phones FILE record into the record buffer.
The OpenFile ROUTINE either opens or creates the Phones FILE. The OPEN(Phones,42h)
statement attempts to open the Phones file for shared access. The second parameter (42h) is a
hexadecimal number (signaled by the trailing "h"). Clarion supports the Decimal, Hexadecimal,
Binary, and Octal number systems. This number represents Read/Write, Deny None access (fully
shared) to the file (see the OPEN statement in the Language Reference for more on file access
modes). We‘re requesting shared access because this is an MDI program and the user could call
multiple copies of this procedure in the same program. However, this program does not do all the
concurrency checking required for a real multi-user application. See Multi-User Considerations in
the Programmer‘s Guide for more on the concurrency checking issue.
The CASE ERRORCODE() structure checks for any error on the OPEN. The OF NoError OROF
IsOpenErr clause (now you can see why we included ERRORS.CLW file) executes the EXIT
statement to immediately return from the ROUTINE. It is very important not to confuse EXIT with
RETURN, since RETURN immediately terminates the PROCEDURE, while EXIT only terminates
the ROUTINE. RETURN is valid to use within a ROUTINE, just be sure you want to terminate the
PROCEDURE and not simply terminate the ROUTINE.
The OF NoFileErr clause detects that there is no data file to open. The CREATE(Phones)
statement will then create an new empty data file for us. You must be sure that, if you intend to
use the CREATE statement that the FILE declaration also contains the CREATE attribute,
otherwise the CREATE statement will not be able to create the file for you. The CREATE
statement does not open the file for processing, so that explains the second OPEN(Phones,42h)
statement. The code in the ELSE clause executes if any error other than these occur. The
STOP(ERROR()) statement displays the ERROR procedure‘s message to the user in a system
modal window allowing the user the opportunity to abort the program (returning to Windows) or
ignore the error. The RETURN statement then terminates the procedure if the user chooses to
ignore the error.
The FillQue ROUTINE fills the PhonesQue QUEUE with all the records in the Phones file. The
SET(Phones.NameKey) statement sets up the processing order and starting point for the Phones
file. The Phone.NameKey parameter makes the processing order alphabetic based on the Name
199
Learning Clarion
field. The absence of a second parameter to the SET statement makes the starting point the
beginning (or end) of the file. The LOOP structure has no condition, which means you must place
a BREAK statement somewhere in the LOOP or else get an infinite loop. The NEXT(Phones)
statement retrieves the next record from the Phones data file, then the IF ERRORCODE() THEN
BREAK. statement ensures that we will BREAK out of the LOOP when all the records have been
read. The DO AssignToQue statement calls the AssignToQue ROUTINE that we‘ve already
discussed, and ADD(PhonesQue) adds the new record to the QUEUE.
7. Add the data section of the UpdatePhones PROCEDURE definition to the end of the
file:
200
Learning Clarion
the USE attribute displays when the control gains input focus. When the user enters data in the
ENTRY control then moves on to another control, the runtime library ensures that the variable
named in the USE attribute receives the current value displayed in the control.
The REQ attribute on the first ENTRY control means that the user cannot leave it blank, while the
REQ attribute on the OK button checks to make sure that the user entered data into all the
ENTRY controls with the REQ attribute. This required fields check is only done when the button
with the REQ attribute is pressed, because the user may not have even gone to the the ENTRY
with the REQ attribute.
8. Add the main logic of the UpdatePhones PROCEDURE definition to the end of the
file:
CODE
OPEN(window)
DO SetupScreen
ACCEPT
CASE FIELD()
OF ?Phones:Rec:Number
CASE EVENT()
OF EVENT:Selected
DO SetupInsert
END
OF ?Ok
CASE EVENT()
OF EVENT:Accepted
EXECUTE Action
ADD(Phones)
PUT(Phones)
DELETE(Phones)
END
IF ERRORCODE() THEN STOP(ERROR()).
ReturnValue = ActionComplete
POST(EVENT:CloseWindow)
END
OF ?Cancel
CASE EVENT()
OF EVENT:Accepted
ReturnValue = ActionAborted
POST(EVENT:CloseWindow)
END
END
END
201
Learning Clarion
RETURN(ReturnValue)
The DO SetupScreen statement calls the SetupScreen ROUTINE to perform some window
initialization code. Notice that it follows the OPEN(Window) statement. When you are going to
dynamically alter the window in a procedure, it must be opened first.
The OF ?Phones:Rec:Number clause in the CASE FIELD() structure demonstrates two important
points. The first is the Field Equate Label, itself. The USE(Phones.Rec.Number) attribute contains
periods in the field name and periods are not valid in Clarion labels. Therefore, to construct a
Field Equate Label for Phones.Rec.Number, the compiler substitutes colons for the periods
(because colons are valid in a Clarion label).
The second important point is the OF EVENT:Selected clause in the CASE EVENT() structure.
EVENT:Selected generates when the control gains input focus but before the user gets to input
data. The DO SetupInsert statement executes to offer the user an option then setup the display
and data entry format of the ENTRY control.
The OF EVENT:Accepted code in OF ?Ok is the code that actually writes the record to disk. The
EXECUTE Action structure executes exactly one of the ADD(Phones), PUT(Phones), or
DELETE(Phones) statements.
An EXECUTE structure is similar to the IF and CASE structures in that it conditionally executes
code based on the evaluation of a condition. The EXECUTE condition must evaluate to an integer
in the range of 1 to n (where n is the number of code statements within the EXECUTE structure),
then it executes the single ordinal line of code within the structure that corresponds to the value of
the condition.
In this code, EXECUTE looks at the Action parameter then executes ADD(Phones) if the value of
Action is one (1), PUT(Phones) if Action is two (2), or DELETE(Phones) if Action is three (3).
Generally, when you evaluate which Clarion code structure to use for an instance of conditional
code execution (IF/ELSIF, CASE, or EXECUTE) the IF/ELSIF structure is the most flexible and
least efficient, CASE is less flexible but much more efficient than IF/ELSIF, and EXECUTE is not
flexible but highly efficient. Therefore, when the condition to evaluate can resolve to an integer in
the range of 1 to n, use EXECUTE, otherwise try to use CASE. If CASE it is not flexible enough,
then resort to IF/ELSIF.
The IF ERRORCODE() THEN STOP(ERROR()). statement will check for an error, no matter
which statement EXECUTE performed. The ReturnValue = ActionComplete statement sets up
the return to the calling procedure, signalling that the user completed the file action, then the
POST(EVENT:CloseWindow) terminates the ACCEPT loop, dropping control to the
RETURN(ReturnValue) statement.
The OF ?Cancel code does almost the same thing, without writing anything to disk. The
ReturnValue = ActionAborted assignment statement sets up the return to the calling procedure,
signaling that the user aborted the file action, then the POST(EVENT:CloseWindow) terminates
the ACCEPT loop, dropping control to the RETURN(ReturnValue) statement.
9. Add the ROUTINEs called in the UpdatePhones PROCEDURE definition to the end
of the file:
SetupScreen ROUTINE
CASE Action
OF InsertRecord
CLEAR(Phones.Rec)
202
Learning Clarion
SetupInsert ROUTINE
IF Action = InsertRecord
CASE MESSAGE('International?','Format',ICON:Question, |
BUTTON:Yes+BUTTON:No,BUTTON:No,1)
OF BUTTON:Yes
TARGET{PROP:Text} = 'Adding New International Number'
?Phones:Rec:Number{PROP:Text} = '@S20'
Phones:Rec:Number[1] = '+'
DISPLAY
SELECT(?,2)
OF BUTTON:No
TARGET{PROP:Text} = 'Adding New Domestic Number'
?Phones:Rec:Number{PROP:Text} = '@P###-###-####P'
END
END
The SetupScreen ROUTINE starts by evaluating CASE Action. When the user is adding a record
(OF InsertRecord) the CLEAR(Phones.Rec) statement clears out the record buffer by setting all
the fields to blank or zero. The TARGET{PROP:Text} = 'Adding New Number' statement uses
Clarion‘s runtime property syntax to dynamically change the title bar text for the window to
"Adding New Number." Clarion‘s property syntax allows you to dynamically change any property
(attribute) of an APPLICATION, WINDOW, or REPORT structure in executable code. See
Appendix C - Property Assignments in the Language Reference for more on properties.
TARGET is a built-in variable that always "points to" the currently open WINDOW structure. The
curly braces ({}) delimit the property itself, and PROP:Text is an EQUATE (contained in
PROPERTY.CLW, automatically included by the compiler like EQUATES.CLW) that identifies the
parameter to the data element (in this case, the WINDOW statement).
203
Learning Clarion
204
Learning Clarion
10. The Solution Explorer should still be opened. If not, open it again by pressing CTRL
+ ALT + L.
11. RIGHT-CLICK on the File Drivers project node, and select Add File Driver from the
popup menu.
12. Highlight TOPSPEED(TPS) in the Select File Drivers window, then press the Select
button.
205
Learning Clarion
206
Learning Clarion
PROGRAM
INCLUDE('ABERROR.INC'),ONCE
INCLUDE('ABFILE.INC'),ONCE
INCLUDE('ABFUZZY.INC'),ONCE
INCLUDE('ABUTIL.INC'),ONCE
INCLUDE('ERRORS.CLW'),ONCE
INCLUDE('KEYCODES.CLW'),ONCE
The first four INCLUDE files (all starting with "AB" and ending with ".INC") contain CLASS
definitions for some of the ABC Library classes. The next two INCLUDE files (all ending with
".CLW") contain a number of standard EQUATE statements used by the ABC Template
generated code and ABC Library classes. The ONCE attribute on each means that if they have
already been included, they won‘t be included again.
MAP
MODULE('PHONEBC.CLW')
DctInit PROCEDURE
DctKill PROCEDURE
END
!--- Application Global and Exported Procedure Definitions ------------
MODULE('PHONE001.CLW')
Main PROCEDURE !Clarion 5.6 Quick Application
END
END
The MAP structure contains two MODULE structures. The first declares two procedures DctInit
and DctKill that are defined in the PHONEBC.CLW file. These two procedures are generated for
you to properly initialize (and un-initialize) your data files for use by the ABC Library. The second
MODULE structure simply names the application‘s first procedure to call (in this case, Main).
207
Learning Clarion
Phones FILE,DRIVER('TOPSPEED'),PRE(PHO),CREATE,BINDABLE,THREAD
Name_Key KEY(PHO:Name),DUP,NOCASE
Record RECORD,PRE()
Name STRING(20)
Number STRING(20)
END
END
Access:Phones &FileManager,THREAD
Relate:Phones &RelationManager,THREAD
FuzzyMatcher FuzzyClass
GlobalErrorStatus ErrorStatusClass,THREAD
GlobalErrors ErrorClass,THREAD
INIMgr INIClass
These objects handle all search criteria, errors, and your program‘s .INI file (if any), respectively.
These objects are used extensively by the other ABC Library classes, so must be present (as you
will shortly see).
GlobalRequest BYTE(0),THREAD
GlobalResponse BYTE(0),THREAD
VCRRequest LONG(0),THREAD
Following that are three Global variable declarations that the ABC Templates use to communicate
between procedures. Notice that the global variables all have the THREAD attribute. THREAD is
required since the ABC Templates generate an MDI application by default, which makes it
necessary to have separate copies of global variables for each active thread (which is what the
THREAD attribute does).
208
Learning Clarion
Dictionary CLASS,THREAD
Construct PROCEDURE
Destruct PROCEDURE
END
The Dictionary Class initializes both File and Relation Managers (contained in the DctInit
generated procedure, called from Construct) whenever a new thread is started. Likewise, the
Managers‘ kill code (contained in DctKill, called from Destruct) must be called whenever a thread
is terminated. The Construct is implicitly called when the global Dictionary object is instantiated.
CODE
GlobalErrors.Init(GlobalErrorStatus)
FuzzyMatcher.Init ! Initilaize the browse 'f
uzzy matcher'
FuzzyMatcher.SetOption(MatchOption:NoCase, 1) ! Configure case matching
FuzzyMatcher.SetOption(MatchOption:WordOnly, 0) ! Configure 'word only' ma
tching
INIMgr.Init('phones.INI', NVD_INI) ! Configure INIManager to
use INI file
Main
INIMgr.Update
INIMgr.Kill ! Destroy INI manager
FuzzyMatcher.Kill ! Destroy fuzzy matcher
The first, second, and the fourth statements call Init methods (in OOP parlance, a procedure in a
class is called a "method"). These are the constructor methods for the FuzzyMatcher and INIMgr
objects. You‘ll notice that the INIMgr.Init method takes two parameters. In the ABC Library, all
object constructor methods are explicitly called and are named Init. There are several reasons for
this. The Clarion language does support automatic object constructors (and destructors) and you
are perfectly welcome to use them in any classes you write. However, automatic constructors
cannot receive parameters, and many of the ABC Library Init methods must receive parameters.
Therefore, for consistency, all ABC object constructor methods are explicitly called and named
Init. This has the added benefit of enhanced code readability, since you can explicitly see that a
constructor is executing, whereas with autiomatic constructors you‘d have to look at the CLASS
declaration to see if there is one to execute or not.
The call to the Main procedure begins execution of the rest of your program for your user. Once
the user returns from the Main procedure, the INIMgr and FuzzyMatcher perform some necessary
update and cleanup operations before the return to the operating system.
Dictionary.Construct PROCEDURE
209
Learning Clarion
CODE
IF THREAD()<>1
DctInit()
END
Dictionary.Destruct PROCEDURE
CODE
DctKill()
The DctInit procedure call initializes the Access:Phones and Relate:Phones reference variables
so the template generated code (and any embed code that you write) can refer to the data file
methods using Access:Phones.Methodname or Relate:Phones.Methodname syntax. This gives
you a consistent way to reference any file in an ABC Template generated program—each FILE
will have corresponding Access: andRelate: objects.
The DctKill procedure call performs some necessary update and cleanup operations before the
return to the operating system.
The three INCLUDE files contain CLASS definitions for some of the ABC Library classes. Notice
that the list of INCLUDE files here is different than the list at the global level. You only need to
210
Learning Clarion
INCLUDE the class definitions that the compiler needs to know about to compile this single
source code module. That‘s why the list of INCLUDE files will likely be a bit different from module
to module.
Notice the MAP structure. By default, the ABC Templates generate "local MAPs" for you
containing INCLUDE statements to bring in the prototypes of the procedures defined in the
module and any procedures called from the module. This allows for more efficient compilation,
because you‘ll only get a global re-compile of your code when you actually change some global
data item, and not just by adding a new procedure to your application. In this case, there are no
other procedures called form this module.
The PROCEDURE statement begins the UpdatePhones procedure (which also terminates the
Module Data Section).
CurrentTab STRING(80)
ActionMessage CSTRING(40)
History::PHO:Record LIKE(PHO:RECORD),THREAD
Following the PROCEDURE statement are three declaration statements. The first two are
common to most ABC Template generated procedures. They provide local flags used internally
by the template-generated code. The ActionMessage and History::PHO:Record declarations are
specific to a Form procedure. They declare a user message and a "save area" for use by the
Field History Key ("ditto" key) functionality provided on the toolbar.
After the WINDOW structure comes the following object declarations:
ThisWindow CLASS(WindowManager)
Ask PROCEDURE(),DERIVED
Init PROCEDURE(),BYTE,PROC,DERIVED
Kill PROCEDURE(),BYTE,PROC,DERIVED
Run PROCEDURE(),BYTE,PROC,DERIVED
TakeAccepted PROCEDURE(),BYTE,PROC,DERIVED
END
Toolbar ToolbarClass
ToolBarForm ToolbarUpdateClass
Resizer CLASS(WindowResizeClass)
Init PROCEDURE(BYTE AppStrategy=AppStrategy:Resize,|
BYTE SetWindowMinSize=False,|
BYTE SetWindowMaxSize=False)
END
211
Learning Clarion
The two after the ThisWindow class are simple object declarations which create the local objects
which will enable the user to use the toolbar. The next class enables resizing the window at
runtime. The interesting code here is the ThisWindow CLASS declaration. This CLASS structure
declares an object derived from the WindowManager class in which the Ask, Init, and Kill, Run,
and TakeAccepted methods of the parent class (WindowManager) are overridden locally. These
are all VIRTUAL methods, which means that all the methods inherited from the WindowManager
class will be able to call the overridden methods. This is a very important concept in OOP.
Skipping the rest of the data declarations is all of the executable code in your procedure:
CODE
GlobalResponse = ThisWindow.Run()
That‘s right—one single, solitary statement! The call to ThisWindow.Run is the only executable
code in your entire procedure! So, you ask, "Where‘s all the code that provides all the funtionality
I can obviously see happening when I run the program?" The answer is, "In the ABC Library!" or,
at least most of it is! The good news is that all the standard code to operate any procedure is built
in to the ABC Library, which makes your application‘s "footprint" very small, since all your
procedures share the same set of common code which has been extensively debugged (and so,
is not likely to introduce any bugs into your programs).
All the functionality that must be explicit to this one single procedure is generated for you in the
overridden methods. In this procedure‘s case, there are only three methods that needed to be
overridden. Depending on the functionality you request in the procedure, the ABC Templates will
override different methods, as needed. You also have embed points available in every method it
is possible to override, so you can easily "force" the templates to override any method for which
you need slightly different functionality by simply adding your own code into those embed points
(using the Embeditor in the Application Generator).
OK, so let‘s look at the overridden methods for this procedure.
ThisWindow.Ask PROCEDURE
CODE
CASE SELF.Request
OF ViewRecord
ActionMessage = 'View Row'
OF InsertRecord
ActionMessage = 'Adding a phones Row'
OF ChangeRecord
ActionMessage = 'Changing a phones Row'
END
QuickWindow{Prop:Text} = ActionMessage
PARENT.Ask
The really interesting line of code in the ThisWindow.Ask PROCEDURE is last. The last
statement, PARENT.Ask, calls the parent method that this method has overridden to execute its
standard functionality. The PARENT keyword is very powerful, because it allows an overridden
method in a derived class to call upon the method it replaces to "do its thing" allowing the
overridden method to incrementally extend the parent method‘s functionality.
212
Learning Clarion
ThisWindow.Init PROCEDURE
ReturnValue BYTE,AUTO
CODE
GlobalErrors.SetProcedureName('Updatephones')
SELF.Request = GlobalRequest
ReturnValue = PARENT.Init()
IF ReturnValue THEN RETURN ReturnValue.
SELF.FirstField = ?PHO:Name:Prompt
SELF.VCRRequest &= VCRRequest
SELF.Errors &= GlobalErrors
CLEAR(GlobalRequest)
CLEAR(GlobalResponse)
SELF.AddItem(Toolbar)
SELF.HistoryKey = CtrlH
SELF.AddHistoryFile(PHO:Record,History::PHO:Record)
SELF.AddHistoryField(?PHO:Name,1)
SELF.AddHistoryField(?PHO:Number,2)
SELF.AddUpdateFile(Access:phones)
SELF.AddItem(?Cancel,RequestCancelled)
Relate:phones.Open
SELF.FilesOpened = True
SELF.Primary &= Relate:phones
IF SELF.Request = ViewRecord AND NOT SELF.BatchProcessing
SELF.InsertAction = Insert:None
SELF.DeleteAction = Delete:None
SELF.ChangeAction = Change:None
SELF.CancelAction = Cancel:Cancel
SELF.OkControl = 0
ELSE
SELF.ChangeAction = Change:Caller ! Changes allowed
SELF.CancelAction = Cancel:Cancel+Cancel:Query ! Confirm cancel
SELF.OkControl = ?OK
IF SELF.PrimeUpdate() THEN RETURN Level:Notify.
END
OPEN(QuickWindow)
SELF.Opened=True
Do DefineListboxStyle
IF SELF.Request = ViewRecord ! Configure controls for View Only mode
?PHO:Name{PROP:ReadOnly} = True
?PHO:Number{PROP:ReadOnly} = True
213
Learning Clarion
END
Resizer.Init(AppStrategy:Surface,Resize:SetMinSize)
INIMgr.Fetch('UpdatePhones',QuickWindow) ! Restore window settings from non
-volatile store
Resizer.Resize !Reset required after window size altere
d by INI manager
Resizer.Reset
SELF.SetAlerts()
RETURN ReturnValue
There are several interesting lines of code in the ThisWindow.Init PROCEDURE. This is the
ThisWindow object‘s constructor method, so all the code in it performs the initialization tasks
specifically required by the UpdatePhones procedure.
The second statement, SELF.Request = GlobalRequest, retrieves the global variable‘s value and
places it in the SELF.Request property. SELF is another powerful Clarion OOP keyword, which
always means "the current object" or "me." SELF is the object prefix which allows class methods
to be written generically to refer to whichever object instance of a class is currently executing.
The next statement calls the PARENT.Init() method (the parent method‘s code to perform all its
standard functions) before the rest of the procedure-specific initialization code executes.
Following that are a number of statements which initialize various necessary properties.
The Relate:Phones.Open statement opens the Phones data file for processing, and if there were
any related child files needed for Referential Integrity processing in this procedure, iyt would also
open them (there aren‘t, in this case).
ThisWindow.Kill PROCEDURE
ReturnValue BYTE,AUTO
CODE
ReturnValue = PARENT.Kill()
IF ReturnValue THEN RETURN ReturnValue.
IF SELF.FilesOpened
Relate:phones.Close
END
IF SELF.Opened
INIMgr.Update('UpdatePhones',QuickWindow) ! Save window data to non-
volatile store
END
GlobalErrors.SetProcedureName
RETURN ReturnValue
In addition to calling the PARENT.Kill() method to perform all the standard closedown
functionality (like closing the window), ThisWindow.Kill closes all the files opened in the
procedure, then sets the GlobalResponse variable.
214
Learning Clarion
215
Learning Clarion
There are two other lessons contained in this folder. The files used for the Online User‘s Guide‘s
Debugger lesson can be found in the
…\Lessons\Debugger folder.
A lesson targeting the use of the Dictionary Synchronizer The files used for the Online User‘s
Guide‘s Synchronizer lesson can be found in the
…\Lessons\SQL folder.
Good luck and keep going—the programming power that Clarion puts at your fingertips just keeps
on growing as you learn more!
216
Index
217
Learning Clarion
Index
1 - Planning the Application ......................... 7 4 - Adding Keys and Relations ................. 35
10 - Control and Extension Templates .... 117 5 - Importing Existing Data ....................... 45
11 - Advanced Topics .............................. 127 6 - Starting the Application ........................ 51
12 - Creating Reports .............................. 149 7 - Creating a Browse ............................... 75
13 - Clarion Language Lesson ................ 177 8 - Creating an Update Form .................... 91
2 - Creating a Data Dictionary ................... 13 9 - Copying Procedures .......................... 107
3 - Adding Tables and Columns ................ 21 Intro to Learning Clarion ............................. 1
218