Although interactive Paradox is powerful, it still can't do some things interactively. You have to accomplish some tasks using ObjectPAL (Object Paradox Application Language). Why would you need to use ObjectPAL? For example, you use ObjectPAL to automate or customize objects on a form. An example of a task that requires ObjectPAL is creating a custom menu system for a form. If you plan to develop a complete custom Paradox application, you probably will need to use ObjectPAL.
ObjectPAL is for both programmers and nonprogrammers. If you have experience with another language, especially an object-oriented programming language such as C++ or Delphi's ObjectPascal, you will find ObjectPAL especially interesting. If you have never programmed, ObjectPAL is a good language to learn first because it easier to learn than C++ or ObjectPascal. With Paradox, you can paint objects onto a form and then attach bits of code to events on the object. You can learn as you go.
You use the user interface to design forms with objects on them, such as fields, tables, and buttons. When you're happy with the way the form works interactively, you attach ObjectPAL code to the objects that require it. The fact that you draw objects on a form and attach code to the objects allows even the nonprogrammer to create applications easily. This system of programming falls into the category of an event-driven language. It is event-driven because the objects you place on forms have predefined events attached to them. You attach code to the events and the user interacts with the events.
Features of ObjectPAL
ObjectPAL's features include an event handler, data types, strong program control, the run-time library, user-defined commands, support for dynamic link libraries, and delivery time binding.
The event handler is the part of Paradox that controls the events attached to objects. It decides when events are triggered. This type of programming cuts down the amount of time you spend programming.
Each ObjectPAL object is also known as an object type. ObjectPAL also has a long list of commands called methods and procedures that a programmer can use. These methods and procedures are organized by object types. For example, there is a set of methods and procedures associated with the TCursor object type (a TCursor allows you to access a table). A subcategory of object types is the data types which include String, Number, SmallInt, Date, Time, Array, DynArray, and others.
Each object type may also have properties associated with it. ObjectPAL also enables you to read or set all the properties that are associated with an object. You are already familiar with many of the properties such as Color, Tab Stop, and Next Tab Stop. For each object you work with in Paradox, you have a set of methods, procedures, and properties you can use to manipulate the object. Paradox's Object Explorer is your guide to each object type's properties and events. Paradox's ObjectPAL Referenece help is your guide to each object type's methods and procedures.
The methods and procedures associated with each object type can either stand-alone or work on the object they're associated with. Many of the commands are duplicated with the same name throughout the object types. Examples include open(), attach(), moveTo(), setTitle(), getPosition(), setPosition(), and searchRegistry().
To control all these object types, the language supports programming control--branching and looping--such as if, for, while, and scan.
As with C and Pascal, you can create your own commands with ObjectPAL. These user-created commands are called custom methods and custom procedures. Custom methods and procedures consist of methods and procedures from the run-time library and other user-defined routines. You also can make calls to dynamic link libraries (DLLs). DLLs contain functions and procedures that usually are written in C, C++, or Pascal. After you register the user-created function in ObjectPAL, you can use it as though it was included. Also, support for Delphi add-ins allows you to use menus and forms of the add-in within the Paradox desktop environment. These features offer you almost unlimited expandability.
A Powerful Programming Language
ObjectPAL is a complete programming language. Like Visual Basic's VBA, Delphi's ObjectPascal, and C++Builder's C++, ObjectPAL supports variables, control structures, loops, and more. The power of ObjectPAL pleases traditional programmers and has many features not found in other languages. You will appreciate ObjectPAL's flexible data typing (it isn't as strict as ObjectPascal, for example). It also has powerfull features not found in other languages. For example, ObjectPAL has a powerful switch statement that can match on strings, and ObjectPAL can manipulate databases with ease. The ObjectPAL language contains a rich array of functionality for managing data. You can find and manipulate values in a table. You can add records from one table to another. You can even scan a table, find every field that has a certain word, and change that word. No language can do it all, but ObjectPAL certainly comes close.
Sometimes, you might spend a few minutes in ObjectPAL, adding a few lines of code to an object on a form, and then you are done. Other times, you might spend hours coding many different objects that interact with one eachother. If you program in small steps and test as you go, programming in ObjectPAL can be fun and easy.
What Is Elegant Programming?
Before we continue our journey into ObjectPAL, this section diverges a little and discusses the ambiguous part of every programming language. Some programmers believe that what they write often approaches art because of its sheer elegance. Elegant programming is the best way to accomplish a particular task. It can mean many things, but most often, elegant programming means a routine that takes the least amount of code to complete a task fully. Try to resist overcoding a task. Recall from the last chapter: if you add some code that doesn't work, take it out!
The term elegant code has many meanings. Fast code, for example, is elegant. Code that uses a TCursor (a pointer to a table) can, in some situations, be more elegant than code that uses a UIObject (a user interface object). A TCursor is faster because it doesn't have the overhead of screen refreshes. (In many cases, however, you can accomplish the same objective by using a UIObject--especially if it's already there.)
Code that is free of bugs and takes into account all possibilities is elegant. The least amount of code is not always the most elegant. If code does not take care of all possibilities, then it is not adequate. This is the reason you need to let some of the users of your software test the software for functionality. This type of test is usually referred to as beta testing your software. For example, test the effect of putting blanks into date fields. Try putting negative numbers where you normally expect a positive number. If users can do it, they will!
Portable code is elegant. The capability to copy an object with code from one form to another with little modification is elegant. ObjectPAL is an object-based programming (OBP) language that enables you to create objects you can copy from one form to another. In most cases, this is not automatic, but with a little creative programming, you can create objects that are fully self-contained and that you can paste into other forms.
Even where you put code determines how elegant it is. The places--events--to which you attach ObjectPAL code is part of the event handler. The event handler handles all the events coming from Windows, such as key presses, mouse movements, and mouse button clicks. The event model that the event handler uses determines when your code will trigger. In ObjectPAL, where you code is as important as what you code.
Although no book can claim to have the correct answer for everything, this book does attempt to demonstrate elegant programming. This book won't always have the perfect solution for every problem; indeed, no book can make that claim. After typing in a routine, you might think of a more elegant way to code it. This is good: the challenging and fun part of programming is coming up with a better solution. The most elegant way isn't always obvious. As a programmer with limited time, you must balance your time between elegant and functional programming.
As you will see in later chapters, a problem often has several solutions that range from simple to complicated--and sometimes elegant. You learn the most by studying simple solutions first, and then more complicated solutions (rather than the other way around). You see the quickest and best way to complete a task. By seeing various levels of solutions to a problem, you learn more about elegant programming.
One form of elegant programming is structured programming--refer to Appendix A for more information on structured programming and how it applies to ObjectPAL.
Setting Your ObjectPAL Level
In this section, you are going to jump right in and write some ObjectPAL. First, make sure that your ObjectPAL level is set to Beginner. To do this, start Paradox. Select Tools | Settings | Developer Preferences to display the Developer Preferences dialog box (see Figure 4-1). This is where you set the developer preferences. From the ObjectPAL level panel of the General tab, select Beginner. Later, in the next chapter, you will switch to the Advanced level.
Note: Whether your ObjectPAL level is set to Beginner or Advanced, you can use all the ObjectPAL methods, procedures, properties, constants, keywords, and so on. The level panel is a help filter used just for learning purposes.
Figure 1: The Developer Preferences dialog box
Starting ObjectPAL
An object type's event is the location in a form, script, or library in which you place ObjectPAL code. This code is attached to the object and defines the object's response to that type of event. For example, pushing (clicking) a button triggers the pushButton event. If you wish to execute your code when a user clicks a button, then attach code to the button's pushButton event. Other examples include clicking on a box triggers the box's mouseDown event, and opening a form triggers the form's init event.
Code in ObjectPAL is attached to objects in the object's events. When the object you wish to work with is selected, select Tools | Object Explorer to display the Object Explorer. Figure 4-2 shows the Object Explorer in the Object Tree plus Tabbed Pane view displaying the events of a button. To add code to an event, simply double-click the event (this displays the ObjectPAL editor). The editor is closely tied to the compiler; you type commands--that is, text--into the editor that the compiler parses through and compiles into compiled PCode. This compiled Pcode is what Paradox or the Paradox run time interprets when you run your application.
Figure 2: The Object Explorer
After you've opened the Object Explorer, you can edit any of the methods or events. For example, you can add code to the pushButton event of a button. The pushButton event is triggered when the user left-mouse clicks a button object. These events are what trigger your code. Figure 4-3 shows the ObjectPAL editor. With the Object Explorer open, select various objects and notice that the events for that particular object are displayed.
Figure 3: The ObjectPAL editor
In the ObjectPAL Editor, you enter code much as you would in other Windows editors, such as Notepad. The editor has the normal features of any Windows editor: cut, copy, paste, and so on. In addition, the editor has many features specific to ObjectPAL that check your syntax, help you build a line of code, color code your syntax, and debug your code.
Programming a Button to Send a Message to the Status Bar
A wonderful way to jump into ObjectPAL is to see what it can do. One type of command in ObjectPAL is called a procedure. Procedures generally are the easiest ObjectPAL commands to use because they usually are straightforward. The System object type has a message() procedure. You use it to display text on the status bar. The following is the syntax for message():
To use this procedure, you type in the keyword message followed by an open parenthesis, and then type the string (in quotes) you want to show up on the status line followed by a close parenthesis, as in the following example (see Figure 4-4):
1: message("type String here")
Figure 4: Using message() with a string
You also can send a message composed of many different strings. Although the string concatenation symbol for ObjectPAL is the +, you can use a comma with the message() procedure. For example, the following lines of code display the same message on the status line:
The first of the following few examples in ObjectPAL lead you step by step through the exercise. In most of the later examples, you are given only a setup description, the code for all the objects, and minimal instructions. It's important in these first few examples to understand how to get to the ObjectPAL editor, and how to enter code into methods and events.
Example of Sending a Message to the Status Line
Let's put the information we've learned so far to use and go through a step-by-step example. This example acquaints you with the pushButton event of a button and the System type's message() procedure. When you click on the button, a message is displayed on the status bar. Suppose that you want to send a message to the status bar when a user clicks a button.
Step By Step
Create a new blank form by selecting File | New | Form.
Select the Blank button on the New Form dialog box.
Place a button on the form with the Toolbar button. Change the label of the button to Display Message. Your form should now look like that shown here:
Illustration 1
Make sure the button is selected and select Tools | Object Explorer to display the Object Explorer (see Figure 4-5).
Figure 5: Select the Object Explorer option
To open the ObjectPAL editor for the pushButton event, double-click the pushButton event from the Events tab of the Object Explorer. Between method and endMethod, there is a blank line with a blinking cursor ready for you to type code.
In the pushButton event, type lines 2 and 3. Line 2 is a comment. You can tell it is a remark because it starts with a ;. Lines 1 and 4 signify the beginning and the end of the event. Line 3 does the actual work you want it to do.
1: method pushButton(var eventInfo Event) 2: ; Example of using System::message(). 3: message("The Holy Cow is a wild creature.") 4: endMethod
All modifiable events and custom methods start with method and end with endMethod. Take a closer look at line 1. After method the events name, pushButton. This identifies the event to which you are attaching code--in this case, pushButton. Following pushButton is the parameter that pushButton takes, var eventInfo Event. var signifies that the event is passed by reference (more on this later). eventInfo Event signifies that the eventInfo variable is of type Event. For now, realize that ObjectPAL is an event-driven type of language that sends events from object to object and line 1 identifies the type of event the event handler is sending.
The Elements of ObjectPAL
Now that you have gotten your feet wet, take a closer look at ObjectPAL. ObjectPAL consists of many different elements. When you study a broad subject such as a programming language, it often is helpful to categorize what you are about to learn. The following paragraphs describe the elements of ObjectPAL.
An event handler is a type of programming language that has preset triggers to which the programmer can attach code. The idea of attaching code to triggers on objects puts Paradox in this category. Do not confuse the category of event handler with the term event model. An event model is the map that an event handler uses to process events. The events are the triggers that start your code, such as pushButton, open, and changeValue. Events are part of the event handler.
The actual words or building blocks of ObjectPAL are called keywords, such as doDefault, DisableDefault, method, endMethod, var, endVar, if, endIf, switch, and endSwitch. For example, an if statement is comprised of the following keywords: if, then, the optional else, and endIf. The ObjectPAL editor displays all keywords in bold. Consider keywords to be the skeleton of your code.
The methods--commands--act on and belong to objects, such as open(), setPosition(), and moveTo(). Examples of methods include formVariable.open("form"), theBox.setPosition(100,100), and fieldName.moveTo(). Methods differ from procedures, which are commands that don't have a specified object on which they work, such as close(), setTitle(), and setAliasPath(). Examples of procedures include close(), setTitle("My Application"), and setAliasPath("MyAlias", "C:\\WORK\\DATA").
In ObjectPAL, you can manipulate the properties--the characteristics--of objects, such as value, color, and tabStop. Examples of properties include fieldName.value = "Angie Herbal", theBox.frame.color = Blue, and buttonName.tabStop = True. Most of the properties that you set interactively by typing in values or inspecting an object can be set using ObjectPAL.
Object variables are built into the language, such as self, container, and active. Examples of using object variables include self.color = Red, container.color = Blue, and message(active.Name).
A constant is a word that represents an unchanging value you can use in ObjectPAL. ObjectPaL has both predifined constants and user defined constants. Red, DataInsertRecord, and True are all constants that ObjectPAL already understands. With constants, the number the constant represents does not matter (and can change in a future version). This humanizes ObjectPAL; that is, it makes it easier to relate to. You use a meaningful word rather than a number in your code. Examples of using constants include box1.color = Red, action(DataInsertRecord), and fld3.tabStop = True. You also can use user-created constants; more on constants you create later in the section titled "Basic Language Elements and ObjectPAL."
Introducing the Event Handler
Object
Location
Example of Typical Use
Form
init
Initializes variables and set up tables
Form
arrive
Maximizes a form and sets values in the form
Form
menuAction
Processes menu events
Form
action
Traps for key violations on a single table form
Form
mouseExit
Clears the status bar if you use mouseEnter
Page
arrive
Sets up a custom pull-down menu
Button
pushButton
Executes code when the user selects a button
Button
mouseEnter
Adds help by sending a message to the status bar
Field
changeValue
Checks a new value against an old value in a table
Table 1: Common Places to Put Code
Every object has a default behavior that you can modify with built-in events. Most objects share the same built-in events: action, menuAction, arrive, canDepart, mouseClick, and error. Some objects have unique built-in events, such as pushButton for a button.
The Six Beginner-Level Events
Method
Description
action
Used when a user calls, or triggers, an event. Typically, action is used for trapping events and processing them. For example, action is useful for trapping database actions such as inserting a record, and moving in and out of edit mode.
menuAction
Called whenever a user selects a menu option. Put code in the menuAction method if you wish to trap for when the user selects an option from the menu, toolbar, or form control box.
arrive
arrive occurs whenever focus moves to a field. At first, arrive seems identical to setFocus, which occurs whenever focus moves to an object (and with fields, this appearance is somewhat accurate). With some objects, however, it is not. Look at a form opening to illustrate. First, the init event is called, followed by open and arrive, and finally setFocus. arrive occurs only after open, whereas setFocus occurs whenever the form becomes active--for example, with a multiple form application. You can use arrive to instruct the user what to enter. By the way, arrive only occurs after canArrive.
canDepart
Think of canDepart as the opposite of arrive. Typically, canDepart is used for data integrity. For example, if the value is less than 100, canDepart can prevent the user from leaving the field until the correct value is entered.
mouseClick
This is triggered whenever the logical left mouse button is pressed and released when the mouse is on an object.
error
This event is triggered whenever an error occurs. The error event is used to add to the event action (response).
Table 2: The Six Beginner-Level Events
Types of Commands
ObjectPAL has several types of commands you can use in an event. This section addresses procedures, methods, keywords, and properties, which are some of the commands you can use in an event.
Identifying Procedures
You already have used a procedure in your first example. A procedure is a powerful type of command; it can stand alone. In other words, a procedure would be complete on a line by itself. For example, each of the following is a procedure.
1: message("Press OK to continue") 2: msgInfo("Warning", "You are about to delete a record") 3: isFile("C:\\DOS\\COMMAND.COM") ;Note the double \.
Identifying Methods
A method is a weaker type of command. You must use an object of the same type as its class when using a method. For example:
1: TaxableField.isBlank() ;TaxableField is a Field object. 2: f.open("FORM1") ;f is a Form object.
Another way to look at methods versus procedures is to say that a procedure always knows the object it works on or with, but a method does not. To further confuse the issue, some commands can serve as both procedures and methods, as in the following example:
1: close() ;Procedure that closes the current form. 2: f.close() ;A method that closes the form associated 3: ;with f (f is a form variable).
Line 1 uses the close() command as a procedure--it knows to close the current form. If, however, you use close() as a method, you need to specify an object. In line 2, f is a form variable you have opened previously.
The Alternative Syntax
Do not confuse a command that can be both a method and procedure with the alternate syntax. Whenever you code object.method(), the alternate syntax enables you to code method(object), as in the following example:
When a single command supports the alternate syntax, which syntax should you use? Although it doesn't really matter, the regular syntax object.doIt(), as seen in the preceding first line, is preferred. It's more consistent with the rest of the language. Don't worry too much about these variations in syntax at this point.
Identifying Keywords
Keywords are words reserved for use with certain commands in ObjectPAL. They are special language construct commands that are neither procedures nor methods. Keywords include Proc, endProc, method, endMethod, doDefault, iif, and Database. At this point, just be aware that you shouldn't use keywords in ObjectPAL for the names of objects or variables.
Following is an example of keywords that are used properly:
1: ;Button :: pushButton 2: method pushButton(var eventInfo Event) ;method is a keyword. 3: if taxable.value = "Yes" then ;if & then are keywords. 4: tax.value = subtotal * .06 5: else ;else is a keyword. 6: tax.value = 0 7: endIf ;endIf is a keyword. 8: endMethod ;endMethod is a keyword.
method, if, then, else, endIf, and endMethod are all keywords. You can't give objects or variables names that are the same as these keywords. In fact, it's a good idea not to give an object or a variable the same name as any element of the ObjectPAL language.
Altering Properties and Dot Notation
Objects that you place on a form have properties. You have already set many of the properties of objects--for example, when you change the color of a box on a form to blue. With ObjectPAL, you can alter an object's property with dot notation. Dot notation is the basic syntax structure used in ObjectPAL. It uses dots to separate elements in a complex statement. Following is the basic syntax structure:
ObjectName.property = Constant
The capability to alter properties while the form is running is powerful and sets Paradox above many other DBMS systems. Following are some examples of altering the properties of objects:
1: box1.color = Blue ;Change box1 to blue. 2: box1.visible = False ;Make box1 disappear. 3: Last_Name.color = Yellow ;Change field color. 4: City.tabStop = False ;Do not allow focus to City.
Dot notation also can represent a complete path to an object. The following examples set the properties of objects in other objects:
1: pge3.box1.Last_Name.color = DarkGray ;Change field to dark gray. 2: f.pge3.visible = False ;Make a page disappear. 3: box1.Last_Name.Frame.Style = Windows3DGroup ;Change the style.
The example in line 3 of the preceding code represents a compound property. The path of the object is box1.Last_Name. The compound property is Frame.Style. This is confusing, especially when learning ObjectPAL. For now, understand that both the object path and the property can be composed of multiple values. The following are three instances for which dot notation is used in ObjectPAL:
To separate an object and property, as in the following:
Last_Name.value = "Santwier"
To separate or indicate an object's path, as in the following:
pge2.boxSection3.Last_Name.value = "Santwier"
To separate an object and a method, as in the following:
Last_Name.moveTo()
Note: For now, think of the containership path of an object as analogous to a directory path. When you open a file, you specify its path--for example, C:\PDOXWIN\DATA\MYFORM.FSL. You can think of a subfolder as being owned (or contained) within its parent folder just as objects are owned (contained) by owner objects. When referring to objects, you need to specify the path of the object with dots rather than a slash, as in the following example:
pge2.boxSection3.Last_Name.
Properties are another type of code you can use in ObjectPAL to get or alter the attributes of an object. Think of objects as having a set of properties attached to them. Some of the properties of a field object are value, color, font.color, TabStop, Name, FieldName, TableName and Enabled. The next example demonstrates that you can alter the properties of objects with ObjectPAL.
Changing the Color of a Box
Suppose that you want to change the color of a box when the user clicks the box. This example uses the mouseClick method of a box to alter the property color. The object variable self is used to refer to the object to which the code is attached. This example also demonstrates using dot notation.
Step By Step
Create a new form and add a box to the form (see Figure 4-6).
Figure 6: Setup form for example
Right-click the box and select Object Explorer to display its Object Explorer.
Select Events.
Open the built-in mouseClick method.
In the mouseClick method, enter lines 3 and 4. Lines 2 and 5 begin and end the method attached to the event. These lines of code are always provided for you by ObjectPAL. In line 2, note that the eventInfo variable type in this case is MouseEvent. This is important to note and is discussed in more detail later. For now, understand an event of type MouseEvent was sent to the mouseClick event. Line 3 is a comment. Lines that start with a semicolon are stripped out when the form is compiled or run. Do yourself a favor and comment all your code heavily. You'll appreciate your efforts when you go back to the code a month later. The semicolon also is a good way to disable a line of code; it's useful when you debug code. Look at line 4, which uses a special built-in object variable, self, to refer to the object to which the code is attached. If you named the box box1, you could have used box1.color = Red rather than self.color = Red. color is a property of the object type Box; in this case, we set it to red.
1: ;COLOR :: theBox :: mouseClick 2: method mouseClick(var eventInfo MouseEvent) 3: ;The following changes the color of self. 4: self.color = Red 5: endMethod
Check your syntax and save the form as COLOR.FSL.
Close the Edit box and run the form by selecting View | View Data. Click the box and watch the color change. The completed form is shown here:
Illustration 2
One type of code that you write in ObjectPAL gets or alters the property of an object. Think of objects as having both attributes (properties) and code (methods and procedures) attached to them. Some of the properties of a field object are value, color, tabStop, name, fieldName, and tableName. A compound property involves another object in font.color. As demonstrated in the previous example, you can alter the properties of objects with ObjectPAL.
ObjectPAL Is Descriptive
In addition to altering an application's look by manipulating properties, there are many ObjectPAL commands that can have an effect on an application. For example, you can change the look of the desktop with the procedures hideToolbar() and showToolbar(). They hide and show the toolbar. For example:
Notice how descriptive ObjectPAL is. It uses real words that describe what you're doing. Sounds easy so far, right? Well, that's because it is easy. The reason ObjectPAL has such a steep learning curve is that it's so rich in commands. Every procedure and method is as easy to understand as the previous examples; however, because there are so many procedures, methods, and properties, it takes a while just to get a handle on ObjectPAL.
Basic Language Elements and ObjectPAL
Now that you have looked at ObjectPAL from the big picture, this next section introduces basic programming elements common to most programming languages and relates these common elements to ObjectPAL. If you have programmed in another language before, then you will particularly like this section.
Note: If you want to type and run the examples in this section, then use the pushButton method of a button and type the code, run the form, and select the button. (There is no need to type the comments.)
A variable is a place in memory used to store data temporarily. You first declare a variable and then you use it. For example, a string is an alphanumeric value or an expression consisting of alphanumeric characters. You can convert a number to a string with string(x) or you can declare a string variable and use it as the following code demonstrates. For example, line 2 that follows declares s as a string:
1: var ;Begin variable block. 2: s String ;Declare s as a String. 3: endVar ;End variable block. 4: 5: s = "Press OK to continue." ;Set s to String value. 6: message(s) ;Display value stored in s.
An operator is a symbol that represents an operation to be performed on a value or values. For example, the + operator represents addition, and the * operator represents multiplication. Line 8 in the code that follows multiplies x and y and then displays the result on the status line.
1: var ;Start variable block. 2: x Number ;Declare x as a number. 3: y Number ;Declare y as a number. 4: endVar ;End variable block. 5: 6: x = 10 ;Set x to 10. 7: y = 5 ;Set y to 5. 8: message(x * y) ;Displays 50 on the status line.
When you concatenate two values, you combine two or more alphanumeric values with the + operator. For example, line 9 that follows concatenates two strings and then assigns the value to s2:
1: var ;Begin variable block. 2: s1 String ;Declare s1 as a String. 3: s2 String ;Declare s2 as a String. 4: endVar ;End variable block. 5: 6: s1 = "Enter name here" ;Set s1 to String. 7: s1.view("What is your name?") ;View s1 in a view box. 8: 9: s2 = "Hello " + s1 ;Set s2 to String plus s1. 10: message(s2) ;Display s2 on status line.
Comparison operators are symbols used to compare two values in a query, in a calculated field, or in an ObjectPAL expression. The comparison operators are <, >, <=, >=, and =. For example, lines 11–13 compare x and y. Depending on which value is greater, a different message is displayed.
1: var ;Begin variable block. 2: x Number ;Declare x as a number. 3: y Number ;Declare y as a number. 4: endVar ;End variable block. 5: x = 0 ;Set x to 0. 6: x.view("Enter value for x") ;View x in a view box. 7: y = 0 ;Set y to 0. 8: y.view("Enter value for y") ;View y in a view box. 9: 10: switch ;Begin switch block. 11: case x > y : message("x is bigger") ;Is x > y? 12: case y > x : message("y is bigger") ;Is y > x? 13: case x = y : message("They are equal") ;Is x = Y? 14: endSwitch ;End switch block.
A constant is a specific, unchanging value. ObjectPAL uses two types of constants: those used in calculations and defined in the Const window of an object and constants predefined by ObjectPAL. For example, line 2 that follows uses a user-set constant that sets the value of pi to 8 decimal points:
1: const ;Begin constant block. 2: pi = 3.14159265 ;Set the permanent value of pi. 3: endConst ;End constant block. 4: 5: ;Display the square root of pi. 6: message("The square root of pi is ", sqrt(pi))
You already have used predefined constants; all property values are actually predefined constants. For example, in the previous example, you used self.color = Red. Red is a constant that represents a number ObjectPAL associates with the color red. You use constants as values that you pass to methods and procedures, such as DataNextRecord, DarkBlue, and FieldForward. Examples of passing constants to methods include fieldName.action(DataNextRecord), fieldName.font.color = DarkBlue, and active.action(FieldForward).
The following line of code shows the numeric equivalent to the constant Red:
message("The numeric value of Red is ", SmallInt(Red))
Although it is academically interesting to understand the numbers behind the built-in ObjectPAL constants, always use the constants in your own coding. Never use the numbers, for they get renumbered in a future version.
A branch transfers program control to an instruction other than the next sequential instruction. When you used the switch block in the example for comparison operators, you branched to one line of code or another depending on a comparison. Normally, a programming language executes line after line in sequential order. This is true whether the language is a line-oriented language, such as BASIC, or a statement-oriented language, such as ObjectPAL, Object Pascal, or C++.
In programming languages, a control structure is a set of keywords used to branch or loop. With control structures, you can alter the sequence of execution and add logic to your code. A loop is a set of instructions that is repeated a predetermined number of times or until a specified condition is met. For example, lines 6 and 7 in the code that follows are repeated 10 times as indicated in line 5.
1: var ;Begin variable block. 2: x Number ;Declare x as a number. 3: endVar ;End variable block. 4: 5: for x from 1 to 10 ;Begin loop. 6: message(x) ;Display x. 7: sleep(500) ;Wait 1/2 second. 8: endFor ;End Loop.
A logical value is a True or False value that is assigned to an expression when it is evaluated. Logical operators are operators used in queries, in calculated fields, and in ObjectPAL methods. The three logical operators are and, or, and not. For example, line 2 in the code that follows declares l as a logical variable, and line 6 uses the not logical operator to display the opposite of l in a view box:
1: var ;Begin variable block. 2: l Logical ;Declare l as a logical. 3: endVar ;End variable block. 4: 5: l = True ;Set l to True. 6: view(not l) ;Display False in view box.
A subroutine is a sequence of instructions that performs a specific task, usually more than once in a program. The sequence may be invoked many times by the current program or by multiple applications. Although ObjectPAL doesn't have actual subroutines, you can think of custom methods and custom procedures as subroutines.
Hiding the Desktop
At one point or another, most users want to create a form that hides the Paradox desktop. To do this, the application type has two usefull methods: hide() and show(). To use them, you need to define an Application variable and use that variable with the hide() and show() methods, as in the following example:
1: var 2: app Application ;App is now an application variable. 3: endVar 4: app.hide() ;Hide the desktop. 5: sleep(5000) ;Wait 5 seconds. 6: app.show() ;Show the desktop.
Unless you have defined your form as a dialog box and have reopened it, your form will disappear with the desktop. At first, hide the desktop only for a period of time; for example, five seconds. Note that even though hide() and show() don't have a parameter passed to them, you still use parentheses. Parentheses are part of the basic syntax for all methods and procedures. Contrast this with properties, which never use parentheses.
To cause a delay, you can use sleep() from the system type. In line 5, you use the sleep() procedure to sleep for 5,000 milliseconds (5 seconds). You can tell that sleep() is a procedure and not a method because it has no object on which to work. A method requires an object on which to work; a procedure does not.
After a variable is defined as an application, you can use any of the application-type methods on it. In addition to hide() and show(), you can use and manipulate an application variable with the following methods: bringToTop(), getPosition(), getTitle(), isMaximized(), isMinimized(), isVisible(), maximize(), minimize(), setPosition(), setTitle(), windowClienthandle(), and windowHandle().
First Database Routines
All this programming theory and manipulating properties and the desktop is necessary and useful, but it probably isn't the reason you purchased Paradox. This section discusses some of the basic commands used to edit data.
In ObjectPAL, you often have several ways to do something. For example, to go to the beginning of a table, you could use either a method or an action constant. The following two lines of code are equivalent:
These two methods represent two techniques for maneuvering through a table. Both of these methods move the pointer from the current record to the next record. The first line requires less typing and is my preferred usage. The second line represents more clearly what ObjectPAL is doing, however. The action() method sends a constant to the event action. When the constant DataNextRecord reaches the action event, action knows to move the table cursor to the next record. In addition to nextRecord(), there is a whole set of table-related methods, including home(), end(), and priorRecord().
In addition to using the object variable self, which refers to the object the code is attached to, you can use active, which represents the object with focus. You can combine these with the two distinctly different techniques for positioning the pointer, as in the following example:
1: self.nextRecord() ;Move to the next record. 2: active.nextRecord() ;Move to the next record. 3: self.action(DataNextRecord) ;Move to the next record. 4: active.action(DataNextRecord) ;Move to the next record.
These two groups of code use the self and active built-in object variables. self refers to the object that executes the code and active refers to the object that has focus.
Using edit() and endEdit()
When you go into edit mode (with a command such as edit()), it is important to note that you put the whole form into edit mode. In other words, you can go to any field of any table on any page and enter or edit values. When you issue an endEdit() command, the whole form is taken out of edit mode. Since the edit() method is a method of an object (self) that also has a TableName property, the edit() method can use that property to put the correct table into edit mode. active refers to the object that currently has the focus. In a multitable form, you could use active to move the record pointer to the table that is attached to the selected object.
Basic Database Buttons
Suppose that you want to put eight buttons on a form that do simple database tasks: Top, Bottom, Previous, Next, Edit, Store, New, and Delete. This example introduces you to the following ObjectPAL methods: home(), end(), priorRecord(), nextRecord(), edit(), postRecord(), endEdit(), insertRecord(), and deleteRecord(). It also introduces the if structure.
Step By Step
Make your working directory Paradox's Samples directory and create a new form with the CUSTOMER.DB table in its data model.
Place eight buttons on the form. Change their labels to Top, Bottom, Previous, Next, Edit, Store, New, and Delete, as shown here:
Illustration 3
Add line 3 to the button labeled Top. Line 3 issues the home() command, which takes you to the beginning of the active table.
1: ;OV-LIKE :: btnTop :: pushButton 2: method pushButton(var eventInfo Event) 3: active.home() ;Move to the first record. 4: endMethod
Add line 3 to the button labeled Bottom. Line 3 issues the end() command, which takes you to the last record in the table.
1: ;OV-LIKE :: btnBottom :: pushButton 2: method pushButton(var eventInfo Event) 3: active.end() ;Move to the last record. 4: endMethod
Add line 3 to the button labeled Previous. Line 3 uses priorRecord() to move the pointer back one record.
1: ;OV-LIKE :: btnPrevious :: pushButton 2: method pushButton(var eventInfo Event) 3: active.priorRecord() ;Move to the previous record. 4: endMethod
Add line 3 to the button labeled Next. Line 3 uses nextRecord() to move the pointer forward one record.
1: ;OV-LIKE :: btnNext :: pushButton 2: method pushButton(var eventInfo Event) 3: active.nextRecord() ;Move to the next record. 4: endMethod
Add line 3 to the button labeled Edit. Line 3 puts the form in edit mode with edit().
1: ;OV-LIKE :: btnEdit :: pushButton 2: method pushButton(var eventInfo Event) 3: edit() ;Put form into edit mode. 4: endMethod
Add lines 3 and 4 to the button labeled Store. Lines 3 and 4 commit the user's changes to the record and end the edit session. Both postRecord() and endEdit() were used in this example to explicitly post the change to the table and end the edit mode. Note, however, that this was done just to introduce the method postRecord(). In a real application, ending edit mode with endEdit() automatically sends DataPostRecord to the built-in action event (changes, if any, to a record are committed when you end an edit session).
1: ;OV-LIKE :: btnStore :: pushButton 2: method pushButton(var eventInfo Event) 3: active.postRecord() ;Write the record to the table. 4: endEdit() ;End edit mode. 5: endMethod
Add lines 3–5 to the button labeled New. With the New button, you want to get the form ready for the user edit. Move the focus to the upper-left field using the MoveTopLeft action constant with action(MoveTopLeft). Go into edit mode and insert a record with edit() and insertRecord().
1: ;OV-LIKE :: btnNew :: pushButton 2: method pushButton(var eventInfo Event) 3: action(MoveTopLeft) ;Move to first field on form. 4: edit() ;Put form into edit mode. 5: active.insertRecord() ;Insert a new blank record. 6: endMethod
Add lines 3–7 to the button labeled Delete. The syntax of the Delete button deserves special attention. There are times when deleteRecord() is not a valid command. Therefore, you need to do some error checking. deleteRecord() returns a logical True or False in an if structure. If deleteRecord() is successful, the message in line 4 is sent to the status bar. deleteRecord() sometimes fails--for example, when the user isn't in edit mode or when deleteRecord() interferes with referential integrity. Whenever deleteRecord() fails, a different message is sent to the message box in line 6.
1: ;OV-LIKE :: btnDelete :: pushButton 2: method pushButton(var eventInfo Event) 3: if deleteRecord() then 4: message("Record deleted") 5: else 6: message("Could not delete record. Perhaps detail records exist.") 7: endIf 8: endMethod
Check the syntax, save the form as OV-LIKE.FSL, and run it. Try out all the various buttons. The completed form is shown here:
Illustration 4
Referring to Objects with active and By Name
The techniques shown in the previous example used the object variable active and a method. They are useful for single-table forms. Most often, however, you'll create multitable forms. Therefore, you must be more precise. In the next example, you learn how to use a method on a single table in a multitable form.
Using insertRecord() and deleteRecord() with a Multi-Table Form
Until now, you've seen how to use database-type methods on a form with a single table in the data model. But what if you want to delete a record from only one table in a 1:M relationship? You can use the name of an object to refer to the underlying table, as in the following example:
1: Last_Name.insertRecord() ;Insert a new blank record. 2: LineItem.DeleteRecord() ;Delete current record.
Using the name of an object enables you to specify the table with which you want to work, which is crucial when you work with multiple tables. This example puts the commands together.
Example of Using a UIObject Name to Refer to the Underlying Table
Suppose that you wish to set up a button on a 1:M form that deletes only the currently selected detail record. This example shows you how to use dot notation in combination with a method. It also introduces the procedure msgQuestion(), which displays a message question dialog box.
Step By Step
Create a new form with a 1:M relationship between ORDERS.DB and LINEITEM.DB, and place a button on the form. Change the button's label to Delete Line Item, as shown here:
Illustration 5
Note: All of the examples in this book that use tables, use the tables that come with Paradox. You need to change your working directory to the Samples subdirectory in the Paradox home directory.
Alter the button's pushButton method to look like the following. In line 3, the msgQuestion() procedure is used to ask the user whether he or she really wants to delete the record. If the answer is yes, lines 4 and 5 delete the record. Otherwise, the if statement ends at line 6. To delete the record, you put the form into edit mode in line 4. In line 5, you use the name of an object to signify the table from which to delete a record. Just as easily, you could have used any other object that is connected to the table, or even the name of the table frame. Remember that when you use the name of an object with a method that uses a table, the method will use the table to which that object is connected.
1: ;DELETE :: btnDeleteLine :: pushButton 2: method pushButton(var eventInfo Event) 3: if msgQuestion("Warning", " Are you sure you wish to Delete the current item?") = "Yes" then 4: edit() 5: Stock_No.deleteRecord() 6: endIf 7: endMethod
Test your syntax, correct any errors if necessary, save the form as DELETE.FSL, run the form, and click the button to delete a record in the detail table:
Illustration 6
OOP and ObjectPAL
ObjectPAL isn't a true object-oriented programming language. It is, however, object-based programming (OBP). Take a look at objects, the three elements of object-oriented programming (OOP), and see how both relate to ObjectPAL. First up is a discussion of objects.
What Are Objects?
The Six Categories of Objects
When you study a subject as broad as the more than 1,800 ObjectPAL methods and procedures and their variations, it helps to study them by object type. The ObjectPAL methods and procedures act on objects. The objects on which the methods and procedures act fall into six categories:
Data types
Display managers
Data model objects
Design objects
System data objects
Event objects
Following is an overview of each of the object categories. They are discussed in detail throughout this book.
Data Types and ObjectPAL
You use data type objects to store data. For example, Date, DateTime, Logical, Graphic, LongInt, and SmallInt are examples of data types that store specific types of data in memory. After you store data in a particular type of variable, you can manipulate the data by using the methods associated with that type of variable.
Display Managers
The display manager objects manage how objects are displayed. ObjectPAL has the following five display managers:
Application object
Form object
TableView object
Report object
Script object
The Application display manager is the Paradox desktop. It manages the display of the other display managers: Form, TableView, Report, and Script. A Form display manager manages all the objects that you put on it. The form is the center of every ObjectPAL application. A TableView display manager manages and displays tables. A Report display manager manages the formatted display and how table information is printed. A Script is a special display manager that has no UIObjects on it but can display information on the status bar and in message boxes. In general, you deal with display managers in ObjectPAL as a complete unit. For example, you might maximize, minimize, or hide any of the display managers except for a Script.
Data Model Objects
You use data model objects to manipulate data in tables and databases. ObjectPAL has five types of data model objects:
Database object
Query object
SQL object
Table object
TCursor object
You use the Database object's methods and procedures on a database--a set of tables in a directory. For example, with Database methods and procedures, you can open a database or delete a table. You use the Query object's methods and procedures to execute and manipulate queries.
The Table object's methods and procedures represent the table itself and are distinct from a TCursor's methods and procedures. A TCursor object is a pointer to the data while a Table object points to the whole table. It also is distinct from a TableFrame and a TableView, which are objects that display the data. Use the Table methods and procedures to add, copy, create, and index tables, to do column calculations, and to get information about a table's structure. Don't use Table methods to edit records, however. Use a TCursor or a table frame--a UIObject--instead.
Design Objects
Design object methods and procedures are commands used to manipulate menus, pop-up menus, and UIObjects. The three types of design objects are as follows:
Menu
PopUpMenu
UIObject
You use Menu and PopUpMenu methods and procedures to build and manipulate menus. Similarly, you use UIObject--short for user interface object--methods and procedures to manipulate UIObjects. For example, when you change the color of a box to red or when you set the value of a field, you manipulate UIObject properties. UIObject methods and procedures add to this functionality. For example, they enable you to set the position of an object.
Scripts, libraries, forms, and UIObjects are the only objects that have events. The form, for example, is a display manager that is also a UIObject. A form has events to which you can attach code, and it responds to events. There also are methods and procedures that you can use to manipulate a form.
Many of the UIObject methods are duplicated among the TCursor methods. For example, insertRecord() works on both a UIObject and a TCursor. The UIObject methods that work with tables work on the underlying table by means of a visible object. Actions directed to the UIObject that affect a table are immediately visible in the object to which the table is bound. On the other hand, TCursor methods work with a table behind the scenes as if another user were making the changes.
System Data Objects
The types of system data objects are as follows:
DDE object
Library object
FileSystem object
Script object
Session object
System object
TextStream object
You use the Dynamic Data Exchange (DDE) object's methods and procedures with DDE. DDE enables you to send and receive data between Windows applications. The Library object's methods and procedures work with libraries. A library is a place to store code. TextStream object's methods and procedures manipulate streams of text.
The other three categories--FileSystem, Session, and System--are used for objects outside Paradox, such as DOS-level or Windows-level procedures. FileSystem methods and procedures enable you to manipulate a FileSystem variable to provide access to and information about disk files, drives, and directories. A FileSystem variable provides a handle; that is, a variable you use in an ObjectPAL statement in order to work with a directory or a file. Session methods and procedures give you a channel to the BDE. For example, you can add and delete aliases by using Session methods. You use System procedures to display messages, find out about the user's system, get a filename with the file browser, and work with the help system.
Event Objects
Events are packets of information sent from one object to another. You use the methods and procedures to manipulate that packet. The following ten types of methods and procedures manipulate the various eventInfo variables:
As an example, the following paragraphs discuss the Event, ActionEvent, ErrorEvent, and KeyEvent categories. The Event category is the base type from which the other event types are derived. Many of the methods listed in this section are used by the other event types.
You generate an ActionEvent primarily by editing and navigating in a table. ActionEvents and procedures enable you to get and set information in the action event.
An ErrorEvent eventInfo message is sent to the built-in error event. Use the error event to add to the built-in error-trapping behavior.
You use KeyEvents methods and procedures to manipulate and retrieve information about keystroke events in keyPhysical and keyChar.
MenuEvents methods and procedures enable you to retrieve and set data in the MenuEvent event packet that is related to menu selections in the application menu bar--in other words, menu constants. For example, when the user chooses an item from the toolbar, it triggers the menuAction event by sending it the appropriate menu constant. You use the methods in the MenuEvent class to manipulate the MenuEvent eventInfo packet.
Tip: To print your own reference sheet, create and run the following one-line script:
enumRTLMethods("RTL.DB")
This script creates a table that lists all the run-time library methods and procedures and their variations. That's more than 1,800 commands. By using the RTL.DB, you can create and print elaborate reports and use them as reference sheets. You also can query the table to view the methods in various ways.
Is ObjectPAL OOP?
Now that you have a firm grasp of the various objects used in ObjectPAL, look at the three elements of object-oriented programming (OOP) and see how they relate to ObjectPAL. The three elements are encapsulation, polymorphism, and inheritance.
Encapsulation and ObjectPAL
In OOP, encapsulation is the bundling of methods and variables within an object so that access to the variables is permitted only through the object's published interface. ObjectPAL supports encapsulation. It was used to create Paradox and is surfaced nicely in ObjectPAL. The TCursor has a lot of code associated with it that allows us programmers to manipulate data, but only the published interface is available to us. The details of how it does things is hidden within the TCursor object. The code you write is stored with the object that triggers it. The code is completely independent of other objects and other code. In other words, if the object is moved inside another object or to another form, the code goes with it. If you compile a form, the code is hidden and only the published interface is available to others. This is encapsulation.
The key benefit of encapsulation is that your code is self-contained and protected from other objects. If another object needs to access variables inside your object, you can grant access through a custom method. If a group of people develop an ObjectPAL application, each programmer needs to be concerned only with his or her particular section. A section in ObjectPAL means a group of objects (typically a form with objects on it). If an external object needs access to something in the current object, the current object must have a method to allow it to be accessed.
For example, a team of programmers could designate one programmer to be in charge of the library for an application. (The library is where generic code is put.) When another programmer wants a routine to be included in the library, that person gives the programmer in charge of the library the specifications for the custom method; that is, what the custom method should be passed and what it should do and return. When another programmer wants to add to or alter that custom method, the programmer in charge of the library decides whether the modification is possible given what the method already does.
Encapsulation makes it possible to bring foreign code together. With a traditional procedural language, a team of programmers must be sure not to use the same function and variable names. Because ObjectPAL supports encapsulation, you don't have to worry about the names of methods, procedures, objects, or variables. Each programmer can use his or her own variable-naming convention and not worry about the other programmers. When it comes time to bring all the sections of an application together, you simply have to worry about how the objects communicate. If you are an individual programmer, this means that you can reuse more of your code.
The capability to have duplicate names for variables and custom methods is a great relief to a programmer. For example, you can have a variable called Counter that belongs to two different objects or two objects could both have a custom method called cmRoutine().
Dot Notation and Encapsulation
The dot notation syntax of ObjectPAL supports encapsulation and enables you to grab values from other objects. For example, from an object of one form, you can open another form and (using the public or published interface) grab the value of one of its objects. For example:
1: var 2: f2 Form 3: endVar 4: 5: f2.open("form2") 6: field1.value = f2.field1.value
The rules for when you can use duplicate names and about which code can see other code refer to scope, which will be discussed in the next chapter.
Polymorphism and ObjectPAL
In OOP, the capability of the same command to be interpreted differently when used with or received by different objects is called polymorphism. In ObjectPAL, polymorphism is the capability of an object to act differently depending on the context in which it is being used. For example, methods require an object with which to work. Depending on the object's type, the method does different things. The following expression opens the object named Orders, depending on how varName is defined:
1: varName.open("Orders") ;varName is an object.
Orders can be a table, a form, a script, or many other objects. What ObjectPAL tries to open depends on what variable type you declare varName as. You can define varName to be a database, a DDE link, a form, a library, a report, a session, a TableView, a TCursor, a TextStream, or something else. As the programmer using this object-based language, you don't need to concern yourself with the details of opening these various objects. You write code that is based on this simple formula:
var object ObjectType ;First declare a variable. endVar object.open ( parameterList) ;Then use the variable.
ObjectPAL takes over and handles the details. For example, to open another form, you could use the following:
1: var 2: tempVar Form ;tempVar is a form variable. 3: endVar 4: 5: tempVar.open("Orders") ;Open form.
TCursors and Polymorphism
A TCursor (table cursor) is a tool used to manipulate the data in a table and is a pointer to a record in a table. If you change the variable type of tempVar, the same open() method can open something else, such as a TCursor:
1: var 2: tempVar TCursor ;tempVar is a TCursor. 3: endVar 4: 5: tempVar.open("ORDERS.DB") ;Open a TCursor to Orders.db.
In the future, when another type of object that can be opened is added, you won't need to learn the new syntax; you'll need to learn only the new characteristics of the object. This certainly beats learning syntax for 10 to 15 different open routines.
Inheritance; Well, Actually, Delegation
In OOP, a mechanism for automatically sharing methods and data types among objects is called inheritance. Typically, you would create a base class followed by creating descendant classes. Each descendant class would have all the properties, methods, procedures, and events of its parent class, plus added functionality. ObjectPAL does not support true inheritance. This is not a drawback, it just means ObjectPAL has its own mechanisms for storing and reusing code.
In ObjectPAL, an object inherits all the variables, types, and custom procedures of the objects by which it is contained. Also, the fact that methods and procedures from one object type are derived--inherited--from other types shows the existence of inheritance in ObjectPAL. In Paradox's online help for the Report Type, the help shows the methods and procedures for the report types that are derived (or inherited) from the Form object type.
Paradox UIObjects--objects you place on a form--inherit characteristics (default behavior) from the objects that the Paradox development team created. For example, when you place a TableFrame on a form, it has a lot of built-in properties, methods, procedures, and events that are inherited from the base TableFrame object. Since you can't inherit down another level, I call this delegation and not true inheritance. Every object in Paradox supports delegation. When you copy an object, the copy is delegated the properties and methods of the parent. Be careful when you use Design | Copy to Toolbar. If the object that you copy to the toolbar has ObjectPAL code on it, the code is copied, too.
Summary
This chapter introduced you to the basics of ObjectPAL. You learned a little about all the various aspects of programming in ObjectPAL. You learned that programming in an event-driven environment is about attaching code to objects. You learned the basics of ObjectPAL syntax and to refer to the properties and methods of objects using dot notation. In addition to containing properties and methods, you also learned that objects can contain procedures and events.
Well, if you read the online help manual "ObjectPAL Reference Topics" material on "setEnvironmentVariable()" and "getEnvironmentVariable()", you will discover ...that there isn't any.
That is because on planet Borland, "set" is "write", "get" is "read", and "EnvironmentVariable" is "EnvironmentString". So you have to read the material on "writeEnvironmentString()" and "readEnvironmentString()" instead.
Google has never crawled the inside of the Paradox online help, and the online help index is not a Google search. When you can't find the entry you want under one word, you need to substitute synonyms until something turns up.
Google is still useful, though. You can use it to find the synonyms.