Macros and Compiled Programs

Macro substitution takes place only when the software is parsing a source (uncompiled) program.

During compilation, the software parses the source code and performs macro substitution based on current macro values. The resulting commands are then compile and stored in compiled form.

When the compiled program is executed, its source code is not parsed again. Therefore, changes to macro values in the interim are not reflected when the compiled program is executed.

For this reason, programs that use macros should not normally be compiled.

Problems that can arise

Consider a program called ExtremeCase that contains a single line of code:

#< CommandLine>

The program ExtremeCase could contain any command, depending on the character string value assigned to the macro < CommandLine>.

If ExtremeCase is not compiled, executing the commands

let = ‘list all Employees’
ExtremeCase
let = ‘form open fTotals’
ExtremeCase

is equivalent to executing

list all Employees
form open fTotals

If ExtremeCase were to be compiled at this point, the command that would be compiled is

form open fTotals

From this point forward, executing the compiled program ExtremeCase would always result in the execution of the FORM OPEN command, regardless of the current value of the macro .

If you choose to compile

IF you choose to compile programs that use macros, keep the following principles in mind:

  • Values for local macros cannot be specified in a call to a compiled macro program

  • After compilation, LET and INPUT assignments to a macro have no effect on the value of that macro in the compiled program.

  • Global macros should be declared and assigned a value before the programs that call them are completed.

  • The software issues a warning when a program that is being compiled contains global macros.

Macros vs Variables

Although macros appear to behave as variables do, they are, in fact, quite different:

  • Variables are defined in the Object Dictionary, with a given data type, length, and so on.

  • Variables are objects in the application database.

  • Variables can be unassigned ($Null).

  • The value of a variable can vary at execution time, even in fully compiled applications.

On the other hand

  • macros are not defined in the Object Dictionary.

  • macros are not objects in the application database.

  • macros always have a value; they cannot be $Null.

  • macro values are substituted for macro calls when commands are parsed. Data, object names, and entire commands can all be used as macro values.

  • macro substitutions are not re-evaluated when a compiled program is executed. Once a program is compiled, changes to the values of macros have not effect on the compiled code.  

How Macros Acquire Values

A macro acquires a value in one of two ways:

  1. implicitly when the software encounters a call to a previously unknown global macro, or when a local macro is unassigned in a macro call program
  2. explicitly in a LET or INPUT command (global or local macros), or in a call to a procedure (local macros only)

Implicit Value

If the software encounters a call to a previously unknown macro or an unassigned local macro, it substitutes the null string for the macro.

Example

Suppose the global macro #<NumToDo> has not been previously declared. The command

list #<NumToDo> WaterSamples

processes after replacing #<NumToDo> with a null string. The command executes as

list WaterSamples

Explicit Value

You can use the LET and INPUT commands to explicitly assign values to either global or local macros.

You can also explicitly assign values to local macros in a call to a procedure.

LET Command Examples

You can use the LET command to assign the character string value of an expression to a macro.

In the following example, the LET command assigns values to the global macros <mac1>, <mac2>, and <mac3>, and to the local macro <8>:

let <mac1> = 'Employees where LastName = Smith'
let <mac2> = 'abc'
let <mac3> = 'def'
let <8> = $concat('f', EntName)

You can make any number of assignments in a single LET command. The value assigned to a macro can be the result of a complex value expression.

Do not use the macro call indicator (#) when assigning a value to the macro. If you use the call indicator in an assignment statement, the macro call is replaced by the current character string value of the macro.

For example, suppose the global macro <mac> currently represents the character string <abc>. If you attempt to change the assigned value of <mac> by entering

let #<mac> = 'xyz'

the statement is interpreted as

let <abc> = 'xyz'

Instead of <mac> acquiring a new value, a new macro <abc> has been declared.

INPUT Command Examples

The INPUT command enables the application user to enter one line of data from the terminal. The software resolves the data into a series of values and assigns the values to the expressions listed in the INPUT command. The expressions in the INPUT command can be macros, variables, parameters, and so on, as shown in the following example:

input <mac1> <mac2> <mac3> <8> <mac4> <7>

The macro call indicator (#) is not used in the statement, because the INPUT command, like the LET command, is performing value assignments.

The data entered at the terminal by the user is parsed and separated into individual character strings. The strings are assigned to the specified macros, in order. If there are more strings than values, the remaining macros are assigned the null string.

For example, in response to the INPUT command above, the application user could enter

abc 4+3 '3*5' xyz !

If the current delimiter is the space, the macros are assigned values as follows:

<mac1>abc
<mac2>4+3
<mac3>‘3*5’
<8>xyz
<mac4>!
<7>” (null string)

Macro Program Call Examples

You can also assign values to local macros in the call to a macro application.

Normally, you call a macro program by issuing a command consisting of the program’s name. However, the program name can be followed by a list of character strings to assign to the local macros belonging to the main procedure in the program.

Any character string is valid, including

  • reserved words
  • object names
  • special characters
  • number literals
  • character literals

The software reads any characters following the macro program name and separates them into individual character string values, based on the current field delimiter. The first value from the program call is assigned to macro <1>, the second to macro <2>, and so on.

If there are not enough values to be assigned to all local macros, the remaining local macros are assigned the null string.

If there are more than nine values, the first nine are assigned to the local macros <1> to <9>, then all remaining characters in the statement, including the spaces between them, are assigned to local macro <0>.

If a macro program call includes no character strings, all local macros <0> through <9> are assigned the null string.

Suppose ListFields is a macro program called by the command

ListFields Employees * 2 'Salary/Age'

If the field delimiter is space, values are assigned to the local macros as follows:

<1>Employees
<2>5
<3>*
<4>2
<5>Salary/Age
<6>” (null string)
<7>” (null string)
<8>” (null string)
<9>” (null string)
<0>” (null string)

Discarding Macro Values

Once defined, a global macro persists until the end of the application session; a local macro persists until the procedure to which it belongs stops executing.

To explicitly discard a macro value, you can assign the null string to the macro as shown in the following example:

let <NumToDo> = ''

This command discards the value of the global macro <NumToDo>.

Opening Existing Objects

Opening Objects through the Database Tab Page.

✓   In Zim IDE, the Database Tab Page can be accessed by clicking the Database Tab at the bottom-left of the screen or the Database Tab Page Icon on the View Menu bar.

✓   The Database Tab Page will become visible on the left side of the Zim IDE window.

✓   Browse the objects in the tree view and double-click on the object you wish to open and edit in the GUI Designer.

Designing Form Fields

Note: when painting displays you must add one or more forms to the display first before adding fields.  Forms are the only object that can be directly added to a display.

✓   From the toolbox tab, select the type of object that you wish to add to the current form:

✓   Using the mouse, draw the outline of the field on the form.  Start in the top left position where the field is to be placed and click on the left mouse button.  While holding the mouse button down, position the mouse cursor at the bottom right location of the new field and then release the mouse button.

✓   The field has now been added to the form with a default set of properties.  You may now navigate to the properties tab on the right side of the painter window and change any of the field’s properties.

Designing Menus

✓   From the Database tab, open an existing menu by double clicking on the menu name or

From the main menu (File -> New, or the “New” icon in the menu toolbar), create a new menu object or

Open a form/display with an associated menu.

✓    Add a menu item.

Click on the menu bar where it says “Type Here” and a new menu item will be added immediately at that location.

Note: As long as you see a menu item that still says “Type Here”, that menu item has not yet been added to your menu and will not be saved.  The IDE is showing the available locations where a menu item can currently be added.

✓   Edit menu item properties.

On the right hand side of the painter window, select the properties tab to view/change the available properties for the currently selected menu item, and to see the children of that menu item listed at the bottom of that tab.

To change the location of an item within the menu, drag and drop the menu item to the desired position.

Working with Tab Controls

Introduction to Tab Controls and Tab Pages

A tab control defines an area of the form in which several tab pages of the same size can coexist.

Tab pages within the same tab control are visually represented as a stacked group of frames, since at any time, only a single tab page can have be displayed and the input focus.

As such, the active tab page within the tab control appears on the top of the stack, above all other inactive tab pages.

Tab Controls and the Tab Pages are both container objects. Tab Control can only contain tab pages, but tab pages can contain all other form fields, including other tab controls.

Tab controls and tab pages represent an efficient and practical solution to reuse the same area of a form to present different form controls.

Advantages of Tab Controls and Tab Pages

✓   There are two main advantages of using tab controls and tab pages over previous solutions that combined other form fields:

✓   The operating system directly manages the process of selecting, displaying and hiding tab pages within a tab control. As such, there is no need for writing additional Zim code to implement the functionality of tab controls and tab pages;

✓   The logical hierarchical structure of a tab control object and its children tab pages is maintained visually in the graphical user interface (e.g., all children tab pages will be moved along when a tab control is moved within a form) and in the database structure (e.g,. the tab control appears as a parent node and its children tab pages as children nodes in the Tree View Tab Page).

Adding Tab Controls and Tab Pages to the User Interface

✓   In your current form, add a tab control object from the toolbox tab .

✓   From the toolbox tab, select the tab page object and add at least 1 tab page to the tab control.

✓    To add user interface objects to tab pages, in the painter workspace double click on the tab header for which tab you wish to add user interface objects to.  The tab that has been selected is now the “current” tab page and form fields from the tool box tab can now be added to this  tab page.

Creating New Objects

 

Creating User Interface Objects

User interface objects are created using the New Object dialog. There are two ways to open this dialogue:

✓ From the Main Menu

✓ From the Menu Toobar

✓ From the Database Tab Page

From the Main Menu

✓  Select File then click on New.

From the Menu Tool Bar

✓  Click on the New icon in the Menu Tool Bar:

From the Database Tab Page

✓ Right-click on the type of object to be created and click on New:

✓ The New Object dialog will pop up.

The New Object Dialog

✓ If you are connected to multiple databases, make sure you select the correct database to which you want to add this new Zim object.

✓ Select the type of object that you wish to create.

✓ On the right side panel name the object you are creating, and select/define the dimensions if applicable.

✓ Click on the “OK” button to create the object.

Creating Database Objects

✓ From the “Database” tab, navigate to the type  of object you wish to create, right click on the object, and select “New” from the right click menu.

✓ The default attributes of the newly-created database object will appear in the GUI Designer page tab ins, since such objects have no visual attributes.

✓ If necessary, Modify the default attributes of the Database object created

Backscreen Hide

When you are ready to run your application in production mode, you may want to hide the ZIM prompt which is known as the backscreen.  You can change the setting dynamically using the $setproperty method.  You can also find the current setting using the $getproperty method.

backscreen hide yes | no

Specifies if the application window (BACKSCREEN) is hidden when the client session starts up.

Valid Settings

yes indicates the application window is hidden; no indicates the application window is not hidden at start-up. The default is always no.

pt_BRPortuguese