Software Customisation Guide
Software Customisation Guide
Customisation Guide
Disclaimer
Information of a technical nature, and particulars of the product and its use, is given by AVEVA
Solutions Ltd and its subsidiaries without warranty. AVEVA Solutions Ltd and its subsidiaries disclaim
any and all warranties and conditions, expressed or implied, to the fullest extent permitted by law.
Neither the author nor AVEVA Solutions Ltd, or any of its subsidiaries, shall be liable to any person or
entity for any actions, claims, loss or damage arising from the use or possession of any information,
particulars, or errors in this publication, or any incorrect use of the product, whatsoever.
Copyright
Copyright and all other intellectual property rights in this manual and the associated software, and every
part of it (including source code, object code, any data contained in it, the manual and any other
documentation supplied with it) belongs to AVEVA Solutions Ltd or its subsidiaries.
All other rights are reserved to AVEVA Solutions Ltd and its subsidiaries. The information contained in
this document is commercially sensitive, and shall not be copied, reproduced, stored in a retrieval
system, or transmitted without the prior written permission of AVEVA Solutions Ltd. Where such
permission is granted, it expressly requires that this Disclaimer and Copyright notice is prominently
displayed at the beginning of every copy that is made.
The manual and associated documentation may not be adapted, reproduced, or copied, in any material
or electronic form, without the prior written permission of AVEVA Solutions Ltd. The user may also not
reverse engineer, decompile, copy, or adapt the associated software. Neither the whole, nor part of the
product described in this publication may be incorporated into any third-party software, product,
machine, or system without the prior written permission of AVEVA Solutions Ltd, save as permitted by
law. Any such unauthorised action is strictly prohibited, and may give rise to civil liabilities and criminal
prosecution.
The AVEVA products described in this guide are to be installed and operated strictly in accordance with
the terms and conditions of the respective license agreements, and in accordance with the relevant
User Documentation. Unauthorised or unlicensed use of the product is strictly prohibited.
First published September 2007
AVEVA Solutions Ltd, and its subsidiaries
AVEVA Solutions Ltd, High Cross, Madingley Road, Cambridge, CB3 0HB, United Kingdom
Trademarks
AVEVA and Tribon are registered trademarks of AVEVA Solutions Ltd or its subsidiaries. Unauthorised
use of the AVEVA or Tribon trademarks is strictly forbidden.
AVEVA product names are trademarks or registered trademarks of AVEVA Solutions Ltd or its
subsidiaries, registered in the UK, Europe and other countries (worldwide).
The copyright, trade mark rights, or other intellectual property rights in any other product, its name or
logo belongs to its respective owner.
Contents
Page
Customisation Guide
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:1
Customising a Graphical User Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:1
Serious Warning About Software Customisation . . . . . . . . . . . . . . . . . . . . . . . . 1:1
How to Use this Manual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:2
Hints on the Trying the Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1:2
2:3
2:3
2:4
2:5
2:5
12.0
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2:9
2:11
2:11
2:11
2:12
2:12
ii
12.0
DO Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5:4
Stopping a DO loop: break and breakif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5:5
Skipping Commands in a DO Loop using Skip or Skip if . . . . . . . . . . . . . . . . . . . . . . . . . . . 5:6
Nested DO Loops. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5:6
Arrays
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:1
6:3
6:3
6:3
6:3
6:4
6:10
6:10
6:11
6:11
Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:1
PML Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:1
Naming and Running Macros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:1
Macros with Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7:1
iii
12.0
8:2
8:3
8:4
8:4
8:5
8:5
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11:1
iv
12.0
14:3
14:3
14:5
14:6
Forms
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15:1
12.0
15:5
15:5
15:6
15:7
15:8
15:8
15:9
15:9
15:10
15:10
15:11
15:12
15:12
15:12
Menus
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16:1
16:3
16:4
16:4
16:5
16:6
16:6
vi
12.0
17:3
17:3
17:4
17:4
17:6
Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:1
Types of Frame. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18:1
Normal Frames. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Tabset Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Toolbar Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
PANEL Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Fold Up Panel Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18:1
18:1
18:2
18:2
18:2
vii
19:3
19:3
19:4
19:4
19:6
12.0
20:10
20:11
20:14
20:14
20:20
20:21
20:21
20:22
viii
12.0
20:36
20:38
20:38
20:40
20:40
20:43
20:45
20:45
20:46
20:46
20:48
20:50
20:50
20:52
ix
12.0
Menus
23:2
23:2
23:2
23:4
23:4
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23:5
APPMENU Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Addition of Menu Items. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Removing Menu Items . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Modifying the Bar Menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23:5
23:5
23:6
23:6
Toolbars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23:7
Toolbar Control. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23:7
Removing Gadgets from a Toolbar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23:8
Deactivating Gadgets on a Toolbar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23:8
Forms
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23:8
12.0
xi
12.0
xii
12.0
Introduction
This manual describes how to use PML, the AVEVA Programmable Macro Language. You
should use it together with the Software Customisation Reference Manual.
You do not have to be a professional programmer to start to learn PML, although you may
find this manual difficult to follow unless you have some understanding of programming
concepts such as if statements and do loops. If you have no programming experience, you
should consider attending a PML Training Course. For details, contact your local support
office, whose address is given on the copyright page of this manual.
The current version of PML, sometimes referred to as PML2, may simply be seen as an
extension to the original PML1 facilities. However, the more powerful techniques that are
available mean that many programming tasks are carried out in different ways.
There are some tasks which are carried out more efficiently using PML 1 facilities. These
are described in this manual where necessary, you should also refer to the Database
Management Reference Manual.
1.1
1.2
1:1
12.0
To minimise this risk, it is most important that your in-house customisation policies constrain
any changes which you make to the Applications so that they retain maximum compatibility
with the standard product at all times.
Remember that AVEVA Ltd can give you full technical support only for products over which
it has control. We cannot guarantee to solve problems caused by software which you have
written yourself.
1.3
1.3.1
1.4
Run your existing macros, thereby ignoring any enhanced features which may have
been incorporated into the standard versions.
Update your customised macros to take advantage of any enhanced features now
available.
In order to minimise any problems resulting from updating your customised macros, you
should adopt the following key principles as part of your customisation policy:
The directory which holds the macros associated with your Applications is divided into
two main areas: a standard product area and a user area. You should make
changes only in the User area using only copies of the standard Application macros.
1:2
12.0
Warning: You should never modify the original files, so that you can always revert to
using these if things go wrong.
The environment variable PMLLIB is a search path which must be set to the directory
or list of directories under which user-defined PML Functions, objects and forms are
stored. The directory specified can contain sub-directories which will automatically be
scanned by PML.
When writing your own PML Application macros or functions, follow the styles and
conventions used in the AVEVA standard AppWare.
Ensure that the names of any new forms which you create cannot conflict with the
names of any standard forms which may be used in the AVEVA Applications. You
should prefix the names of your forms and global variables with a suitable code to
distinguish them from AVEVA names. For example, a company XYZ could choose to
start all form and global variables names with the characters XYZ.
Prefix
Accommodation
ACC
Administration
ADM
Comments
ABA
Add-in application
Assembly
ASSY
Associations
ASSOC
None apparent
Common
COMM
Mixed
Design
DES
Mixed
Draft
DRA
Final Designer
DRA
Global
GLB
Hull Design
HULL
Hull Drafting
HDRA
Integrator
INT
Isodraft
ISO
Isometric ADP
ISO
Add-in application
Diagrams
DIAG
Monitor
MON
Paragon
CAT
Review Interface
REVI
None
1:3
12.0
Application
Prefix
Router
RTR
Spooler
SPL
Training
TRA
Comments
Icon naming
Icons are named without regard to any rules, except on odd occasions.
Folder
Prefix
Actions
Various
Applications
Various
ASL
Assembly ADP
AD
Constructs
Various
Design
DES
Draft
DRAFT
Profile
PRFL
Various
SMV
Various
Various
Shapes
No existing icons
Splash
Various
Styles
1.5
Comments
STY
Plus FSTY
1:4
12.0
Warning: Changing the value of an argument inside a PML Functions or method will
change the value in the calling function or method.
1:5
12.0
1:6
12.0
2.1
PML Variables
Variables are used to store values. Variables have names. The value that is stored can be
changed but the name is fixed. You choose the names and decide what is stored by each
variable.
The variables used in PML 2 are objects:
2.2
An object has a set of functions associated with it, which can be called to manipulate
this data. These functions are called methods.
Some methods change the data held by the object, some return a result derived from
the data, and some methods do both.
The data that an object can hold and the functions that are available are fixed by the
object type (sometimes called the objects class).
Before you can create an object, the object type must have been defined. The object
type definition specifies what the members and methods are for that type of object.
Object Types
Every PML2 variable has an object type which is set when the variable is created and
remains fixed as long as the variable exists.
Attempting to store a value of the wrong type in an existing variable will cause an error. The
object type must exist before you can create a variable of that type.
PML 2 is supplied with built-in object types, system-defined object types, and you can also
define your own user-defined object types.
The built-in object types include the following:
Object
Description
STRING
REAL
2:1
12.0
Object
Description
BOOLEAN
ARRAY
There are also system-defined variable types, such as POSITION and ORIENTATION in
PDMS.
User-defined variable types are explained in User-defined Object Types.
2.3
2.4
2:2
12.0
It is a good idea to use upper case for object-type names, and mixed upper and lower case
for variable names, the objects themselves. For example, a type might be WORKERS, and
a variable name might be NumberOfWorkers.
2.4.1
2.5
2.5.1
!SurfaceArea
!!Area
PML Variable names may be any combination of letters and digits, starting with a letter, up
to a maximum of 16 characters (plus the !! or !). Names are allowed to contain a dot (.)
but this is now strongly discouraged as a dot has a special meaning in PML2 as a separator
between an object and its methods (see Using the Methods of an Object), and between the
components of form and gadget names.
Rather than using a dot as part of a name we recommend that you use a mixture of upper
and lower case to make variable names more meaningful, for example:
!!StartInUpperCase
2.5.2
Use each variable for one purpose only and give it a meaningful name.
Where possible use PML Functions rather than macros (macros are described in
Macros) so as to make use of function return values and output arguments to return
results.
2:3
12.0
Remember that within the methods of PML Objects and Forms you generally need only
local variables. Any values which are to be exported should be made available as
members or as method values or returned arguments.
Only use PML Functions (necessarily global) for tasks which are genuinely globally
accessible, i.e. independent of a specific object or form, or which need to be shared by
several objects or forms.
Prefix
Comments
!!ABA
Add-in application
Administration
!!ADM
Plus !!CDA
Application Switching
!!APP
Batch Handling
!!BAT
CADCentre
!!CADC
Draft
!!CDR
!!CD2D
Defaults
!!DFLTS
General
!!CDC
Plus !!CDD, E, F, G, H, I, L, M, N, P, S, U, V, W
Specialised
!!CE
Icon pathnames
Various Others
2.5.3
!MyNumber = 99
Because the variable is set to a real, PML knows that its type is real. Similarly, you can
create a STRING variable and set it like this:
\!Name = 'Fred'
!Grid = TRUE
2:4
12.0
A variable may also be given a type without giving it an initial value, in which case it will
have the value UNSET:
!!Answer = REAL()
!Name = STRING()
!Grid = BOOLEAN()
!Lengths = ARRAY()
For more about unset variables, see UNSET Values and UNDEFINED Variables.
2.5.4
2.5.5
!People = !NewPlant.Workers
sets the variable !People to 451.
2.6
2:5
12.0
FRED
')
Here ' FRED ' is a string constant passed as an argument. We could rewrite this function
so that it returns its results by changing its arguments. The output argument, !Length, will
be set to the value required when the function is called:
!!LengthAndTrim('
FRED
' ,4 )
$* WRONG
A PML Function returning a value may be used wherever an expression or PML Variable
can be used, for example, this call to the !Area function defined above:
!PartLength = 7
!PartWidth = 6
2:6
12.0
2.6.1
2.6.2
PML Procedures
A PML Procedure is a PML Function that does not return a result.
A function is defined as a procedure by omitting the data type at the end of the define
function statement:
!SurfaceArea = REAL()
!Partlength = 7
!PartWidth = 6
call !!Area(!PartLength, !PartWidth, !SurfaceArea)
There will be an error if you attempt to assign the result of a PML Procedure because there
is no return value to assign. So, for example you can say:
2:7
$* WRONG
12.0
The ( ) parentheses after the name of a procedure or function must always be present
even for procedures that do not need arguments:
2.7
2:8
12.0
2.8
Name
Result or Type
Status
Purpose
2.8.1
Example
This section explains how to use methods, using a STRING variable as an example.
Although the STRING is a simple object type, it has a large number of methods which can
be called.
For example, if you are interested in the length of the string value, look under the list in the
Software Customisation Reference Manual for STRING objects, and you will find a method
named Length.
2:9
12.0
This method returns a REAL value (the number of characters in the string), but has no
effect on the variable itself.
You can extract the number of characters in the string and store it in a new variable,
!Nchars, by calling the method as follows:
$* A method call
!Nchars = !MyString.length()
Notice the dot separator between the name of the variable and the name of the method.
Also note the ( ) brackets following the name of the method. The brackets are used to
enclose the arguments of the method, but they must be present even if there are no
arguments.
2.9
What it Does
2:10
12.0
2.9.1
Method Overloading
Two or more methods on an object may share the same name providing they have different
arguments. This is called method overloading. PML will invoke the method with the
arguments which match the method call. It is common practice:
To use a method with the same name as a member and one argument of the same
type to set the members value. For example:
!Marvin.Answer(65)
To use a method of the same name as a member returning a value of the same type
but with no arguments to get the members value. For example:
!Number = !Marvin.Answer()
2.9.2
2.9.3
!SomeObject.SetValue(100)
will invoke the method with the REAL argument, but
!SomeObject.SetValue(Priceless )
will invoke the method with the ANY argument.
2:11
12.0
2.9.4
2.9.5
2.10
!S = !X.String()
S = !X.String()
!X = BOOLEAN()
!S = !X.String()
!X = STRING()
!S = !X.String()
!X = ARRAY()
!S = !X.String()
Other variable types are system-defined variables. Most of these have adopted the unset
string Unset. For example:
!X = DIRECTION()
!S = !X.String()
2:12
12.0
User-defined data types can also provide a String() method. These also support an
UNSET representation, and usually adopt the UNSET representation Unset.
2.11
!X = REAL()
Variables with an UNSET value can be passed around and assigned, but use of an UNSET
value where a valid item of data is required will always result in a PML error.
The presence of an UNSET value may be tested either with functions or methods:
Functions
Methods
if ( Unset(!X) ) then
if ( !X.Unset() ) then
if ( Set(!X) ) then
if ( !X.Set() ) then
An UNDEFINED variable is one that does not exist. The existence of a variable may be
tested with these functions:
if ( Undefined(!!Y) ) then . . .
if ( Defined(!!Y) ) then
There is no equivalent method call. If the variable does not exist, attempting to call a method
would result in an error.
2.12
!!Y.Delete()
Warning: You must not attempt to delete members of objects or forms.
2.13
2:13
12.0
2:14
12.0
3.1
There is a check that they have been called with the right type of arguments.
PML Macros are normally stored in a directory under the PDMSUI search-path.
PML Functions are automatically loaded from a directory under the PMLLIB search-path.
3.1.1
You can also use $* to add an inline comment to any line of PML:
!Z = !X + !Y
A comment may extend over several lines provided it is enclosed in the escape sequences
$( and $) .
$( A comment containing
3:1
12.0
$(
skip if (!X EQ !Y)
$)
3.1.2
if ( count EQ 0 ) then
return
endif
For clarity, return can be used as the final line of a PML Macro. However, the use of this
command is not essential and there will be no error if it is not used.
Note: This is a different use of return from the command used to set return values of
variables.
3.2
Case Independence
Everything written in PML, including keywords such as if, do and else means the same
thing in upper or lower case.
The exception is text enclosed between quotes or vertical bars, which is used exactly as it is
typed. Throughout this document you will see examples of PML code using a mixture of
upper and lower case for readability.
3.3
Abbreviations
Many commands have a minimum abbreviation which can be used in place of the full
command.
For readability it is recommended that you use the full form of the command note that it is
not less efficient than using the abbreviation.
PML keywords such as if, else and do have no abbreviations.
3.4
Special Character $
The $ character has a special meaning in PML. It is an escape character, which means
that together with the character which follows it are treated as a special instruction to PML.
The pair of characters beginning with $ is known as an escape sequence. $P is a
commonly encountered example, which is used to output a message to the screen:
3:2
12.0
A number of other escape sequences will be described later in this manual. The important
point to note here that if you need the dollar character itself as part of a command, you will
need to double it:
$$
As the last character on a line, $ means that the next line is a continuation line.
For example:
3.5
Text Delimiters
Text strings must be enclosed in either single quotes, or apostrophes, or vertical bars:
'apostrophes' or vertical bars.
The apostrophes and vertical bars are known as delimiters. Take great care to avoid unmatched delimiters as this can lead to many lines of PML code being included as part of the
text string, and so lost.
3.6
Filename Extensions
The naming conventions are as follows:
Extension
Use for
.pmlfnc
.pmlobj
.pmlfrm
3.7
pml rehash
3:3
12.0
This command scans all the files under the first directory in your PMLLIB path, and updates
the pml.index file.
If other users have added PML Files and updated the appropriate pml.index files, you can
access the new files by giving the command:
pml index
This command re-reads all the pml.index files in your search path without rebuilding
them.
When you are not running an AVEVA product, you can update the pml.index file in a given
directory by giving the command:
pmlscan directory_name
This command runs a utility supplied with AVEVA products.
Note: The commands pml rehash and pml index are a type of command known as
PML directives: they are used outside PML Files to direct PML itself to take certain
actions. More information about using PML directives with form definitions is given in
PML Directives
3.7.1
3.7.2
3:4
12.0
PML Expressions
An expression consists of operators and operands.
For example:
2 + 3
is an expression, where 2 and 3 are the operands, and + is the operator. The result of this
expression is 5.
Expressions can (and normally do) contain variables and, in PDMS, expressions will often
contain names of PDMS element types, attributes and pseudo-attributes.
Expressions can also contain arithmetic and trigonometric functions, such as the SIN
trigonometric function.
Often, expressions will contain logical operators:
!height GT !width
Here the logical operator GT is being be used to test if !height is greater than !width.
Each expression has a type such as REAL, STRING or BOOLEAN. All the elements in an
expression must be of the correct type. For example:
!X + 'text'
is meaningless if !X is REAL and will result in an error. (But see Concatenation for using the
concatenation operator to convert different types to STRING.)
4.1
Format of Expressions
The use of brackets, spaces and quotes in an expression is important. If you do not follow
the rules given below you will get error messages:
Text must be enclosed in quotes, either ' apostrophes or | vertical bars:
!X + !Y
In general, you do not need spaces before or after brackets, except when a name is
followed by a bracket. If there is no space, the bracket will be read as part of the name. For
example:
(Name EQ /VESS1 )
4:1
12.0
4.2
Operator Precedence
Operators are evaluated in the order of the following list: the ones at the top of the list are
evaluated first.
Operator
Comments
BRACKETS
FUNCTIONS
*/
+NE NEQ GT LT GE GEQ LE LEQ
NOT
AND
OR
4.3
Boolean Operators
The Boolean, sometimes called Logical, operators have the following meanings:
The logical operators available are:
Operator
Comments
EQ
NE
LT
GT
LE OR LEQ
GE OR GEQ
NOT
AND
OR
Note: The operators EQ, NE, LT, GT, LE and GE are sometimes referred to as comparator
or relational operators; NOT, AND, and OR are sometimes referred to as Boolean
4:2
12.0
4.4
Concatenation
The & operator concatenates two STRING values (joins them end-to-end) to make a result
of type STRING. Values of any type are automatically converted to a STRING first:
!X = 64
!Y = 32!
Z = !X & !Y
4.5
Nesting Expressions
Expressions can be nested using brackets. For example:
( (SIN(!angleA) * 2) / SIN(!angleB) )
4.6
4.7
PML 2 expressions are used in if and do commands and when giving a PML Variable
a value using the = (assignment) operator.
PML 1 expressions must be used in all other situations, in particular when using an
expression as an argument to a command. For more information regarding PML 1 refer
to Database Management Reference Manual.
PML 2 Expressions
PML2 introduces enhanced facilities for expressions in if and do commands and when
giving a PML Variable a value using = assignment.
For example, the value of a PML Variable can be used by giving the variable name as the
expression to the right of =:
!SavedValue = !Number
Users familiar with PML1 should note the absence of $ preceding the variable name. These
new expressions do not need to be enclosed in ( ) parentheses:
!NewValue = !OldValue + 1
PML 2 expressions:
May include form gadget values (described later), and object members and methods.
For example:
4:3
12.0
4:4
12.0
Control Logic
There are four types of construct for implementing control logic within a PML Function or
Macro. These are:
5.1
The do command for looping and the associated break and skip.
IF Construct
The full form of an if-construct is as follows:
5:1
12.0
if ( !Number LT 0 ) then
!Negative = TRUE
endif
You may not concatenate the commands into one line, so the following are not allowed:
if ( !Number LT 0 ) then
if ( !Number LT 0 )
$* WRONG
$* WRONG
!Negative = TRUE
if ( !TrueValue OR !UnsetValue)
if ( !FalseValue AND !UnsetValue)
ignore !UnsetValue if the value is not required to determine the outcome of the expression.
The same is true for PML Functions which have returned an error.
5.1.1
Nesting if-constructs
Any if-construct may contain further if ... elseif endif constructs:
if (!Number LT 0) then
!Negative = TRUE
if (!Number EQ -1 ) then
!OnlyJustNegative = TRUE
endif
endif
It is particularly helpful with nested if constructs to indent the code so that the logic is clear.
5.1.2
if (!NewValue - 1 GT 0) then
The expression can be a simple variable provided it is a BOOLEAN type variable:
!Success = !NewValue GT 0
if (!Success) then
The expression could be a user-defined PML Function provided it returns a BOOLEAN result:
if (!!MyFunction() ) then
Note: The BOOLEAN constants TRUE, FALSE, YES and NO and their single-letter
abbreviations not enclosed in quotes return BOOLEAN results and so can be used
directly in expressions. For example:
Code
Result Type
if ( TRUE )
BOOLEAN
if ( FALSE )
5:2
12.0
if ( T )
BOOLEAN
if ( F )
if ( YES )
BOOLEAN
if ( NO )
if ( Y )
BOOLEAN
The following do not return BOOLEAN values and are therefore invalid:
Code
Result Type
if ( 1 )
REAL
if ( 0 )
if ( TRUE )
STRING
if ( FALSE )
if ( T )
STRING
if ( F )
Variable = 1
REAL
if ($Variable)
For upward compatibility with PML1, STRING variables set to TRUE, FALSE, YES or
NO or their single-letter abbreviations can be used in an if-test as long as they are
evaluated with a preceding $. For example:
Code
Result Type
Variable = TRUE
STRING
if ($Variable)
There is a built-in PML Method and a function for converting a value to BOOLEAN:
!MyString = TRUE
if (!MyString.Boolean() ) then . . .
The Boolean conversion is as follows:
Code
Result
REAL zero
FALSE
TRUE
FALSE
FALSE
TRUE
TRUE
5:3
12.0
5.1.3
IF TRUE Expression
IFT/RUE will return a value of the type defined by the second and third arguments.
If the initial Boolean expression is true, the result of the first expression is returned. If false,
the result of the second expression is returned.
Both the second and third arguments are fully evaluated regardless of the value of the
Boolean. This is because the function is evaluated using reverse polish procedure (as is the
case of the expression design). This allows the IF statement to be nestable with any of the
arguments to IF capable of including other IF functions.
If logical1 expression is set to true, then value of typeX1expression is returned
If logical1 expression is set to false, then value of typeX1 expression is returned
typeX1 and typeX2 are two arguments of the same type which may be:
Logical
Logical Array
Real
Real Array
ID
ID Array
Text
Position
Direction
5.2
DO Loops
A do-loop enables a series of commands to be repeated more than once. The number of
times the series is repeated is controlled by a counter.
Control is passed to the line following the enddo command after executing the commands
within the loop with the counter variable at its final value.
The full format of the do-loop is as follows:
do !x from 10 to 100 by 10
!Total = !Total + !X
enddo
The enddo must be present at the end of the loop.
The !X , from, to and by are optional, therefore in its simplest form you may use:
do
commands block
enddo
This will loop forever unless something in the commands block stops the loop (see break
and golabel below)
5:4
12.0
Purpose
!X
from
to
by
5.2.1
do !Number
if (!Number GT 100) then
break
endif
!Result = !Result + !Number
enddo
do !Number
break if (!Number GT 100)
!Result = !Result + !Number
enddo
Any expression may be used with the breakif command, even a PML Function, provided
the result is BOOLEAN (i.e. TRUE or FALSE).
The ( ) parentheses are optional but recommended.
The loop counter variable retains the value it had when the break from the loop occurred.
5:5
12.0
5.2.2
do !X
!Number = !Sample[!X]
if ((INT(!Number/2) NE (!Number/2)) then
skip
endif
!Result = !Result + !Number
enddo
The skip if command can sometimes be more convenient:
do !X
!Number = !Sample[!X]
skip if (INT(!Number/2) NE (!Number/2))
!Result = !Result + !Number
enddo
5.2.3
Nested DO Loops
You can nest do-loops one inside the other. The counter for each loop must have a different
name.
do !X to 10
do !Y
!Z = !X + !Y
break if (!Y GT 5)
enddo
enddo
The inner-most loop goes through all the values of !Y before the outer loop moves on to the
second value of !X. Note that the break (or skip) command acts just on the loop
containing it - in this case the inner-most loop.
5.3
LABEL /FRED
:
:
5:6
12.0
GOLABEL /FRED
The label name /FRED has a maximum length of 16 characters, excluding the / slash which
must be present. The next line to be executed will be the line following LABEL /FRED,
which could be before or after the GOLABEL command.
5.3.1
$P Total is $!Total
If the expression is FALSE, PML execution will continue with the line:
!Total = !Total + !C
5.3.2
Illegal Jumping
The following is an illegal jump into a nested do block. It is however permitted to jump out of
a nested block to a line in an enclosing block.
$* WRONG
golabel /illegaL
do !Count to
5:7
12.0
5:8
12.0
Arrays
6.1
!NewArray[1] = !NewValue
This will create an array variable named !NewArray if it does not already exist and will set
element 1 of that array to the value of !NewValue. If !NewArray already exists as a simple
variable, you will get an error message and the command will be ignored.
Note: How array elements are referred to by means of a subscript expression in [ ]
square brackets and that there must be no space between the end of the array name
and the subscript. Array elements are accessed in the same way when using the
value in an expression:
!Average =
!X = ARRAY()
The array subscript may be any expression which evaluates to a number. One of the
simplest expressions is the value of a variable:
!MyArray[!Next] = 15
The individual elements of an array variable can be set independently and in any order.
Thus you can set !X[1] and !X[10] without setting any of the intervening elements
!X[2] to !X[9]. In other words PML arrays are allowed to be sparse and to have gaps
between the subscript numbers which have values set.
Note: Negative subscripts are no longer permitted.
An array subscript of zero is allowed but you are advised against using it as many of the
array facilities ignore array element zero.
An array subscript may be an expression of any complexity provided it evaluates to a
positive REAL result and may even include a call to a PML Function:
6:1
12.0
Non-existent array elements after the last set element of the array and the non-existent
elements in the gaps of a sparse array are all UNDEFINED: the function Undefined()
will return TRUE and the function Defined() will return FALSE for all these subscripts.
6.2
!Surname = 1
!Forname = 2
!Employee[1][!Surname] = 'Smith'
!Employee[1][!Forname] = 'Samuel'
!Employee[2][!Surname] = 'Bull'
!Employee[2][!Forname] = 'John'
!FullName = !Employee[1][!Forname] & ' ' &
!Employee[1][!Surname]
The value !FullName will be 'Samuel Smith'.
It is also possible to assign an entire array to an array element:
!TempName[!Surname] = 'Truman'
!TempName[!Forname] = 'Harry'
!Employee[3] = !TempName
!FullName = !Employee[3][!Forname] & ' ' &
!Employee[3][!Surname]
The value !FullName will now be 'Harry Truman'.
6.3
Array Methods
Note: The Software Customisation Reference Manual contains a list of all PML Methods.
Array methods are built-in functions for performing a variety of operations on the array.
These methods are invoked with a dot following the array name. The method name must be
followed by ( ) parentheses - even if the function has no arguments:
!Nelements = !MyArray.Size()
This method sets !Nelements to the number of elements currently in the array. This is an
example of a no-effect method which does not alter the array but returns a REAL result
which can be assigned to another variable.
Here is method which does change the array:
!MyArray.Clear()
This is an example of a method which modifies the array by deleting all the array elements
but produces no-result value, so there is nothing to assign to another variable.
There is a third kind of method which both changes the array and returns a result value.
Here the result is assigned to a variable:
6:2
12.0
!NewArray = !OldArray.RemoveFrom(5,10)
This is an example of a method result which modifies the array by removing 10 elements,
starting at element 5. NewArray value is a new array containing the 10 removed elements.
If not required, the result can be simply discarded by invoking the method as a command
and not assigning the result to a variable:
!OldArray.RemoveFrom(5,10)
Note: Always use an array method, if one is available, in preference to constructing a doloop as it is far more efficient.
6.3.1
!Result.Append(!NewValue)
The new array element to be created is determined automatically by adding 1 to the highest
existing index for the array !Result. The data are stored in !Result[1] if the array does not
yet contain any elements.
The array !Result must exist before you can call this method. If necessary you can create
an empty array (with no elements) beforehand:
!Result = ARRAY()
If !Result exists already as a simple variable, you will get an error and the command is
ignored.
6.3.2
!MyArray[N].Delete()
The deleted array element would test as UNDEFINED. Note that the array continues to exist
even when you have deleted all its elements.
6.3.3
Deleting an Array
To delete the entire array, together with any existing array elements, use the Delete method
on the array itself:
!MyArray.Delete()
6.3.4
6:3
12.0
The white-space is treated as a special case since consecutive white-spaces are treated as
a single delimiter and white-spaces at the start or finish of the string are ignored.
The result is to create the array-variable !ArrayOfFields (if it does not already exist) with
the element !FIELDS[1] set to '123', !FIELDS[2] set to '456', and !FIELDS[3] set to '789'.
To specify a different delimiter, specify the required (single) character as the argument to the
Split() method:
!ArrayOfFields.AppendArray(!Line.Split())
6.3.5
!LIST[1]'One'
!LIST[2]'Two'
!LIST[3]'Three'
The command:
!Width = !List.width()
would set to !Width to the value 5, the length of the longest element in !LIST, which is
Three'.
Note: If the array contained elements that were not strings, these are ignored when
calculating the maximum width.
6.4
!MyArray.Sort()
This is a no-result method that modifies the array by performing a sort in-situ.
6:4
12.0
The sort is into ascending order and will be an ASCII sort on an array of STRING elements
and a NUMERIC sort on an array of REAL values.
The Sort() method returns the array itself as a list result, so it is possible to follow the call
to the Sort() method immediately with a call to the Invert() method, which will return a
descending sort:
!MyArray.Sort().Invert()
An alternative approach is an indirect sort using the method SortedIndices() which
returns a REAL array representing new index positions for the array elements in their sorted
positions:
!NewPositions = !MyArray.SortedIndices()
The array can be sorted by applying the new index values using the ReIndex() method:
!MyArray.ReIndex(!NewPositions)
More important, the index values in !NewPositions can be used to sort other arrays as well.
To use some simple examples, imagine we had the array !Animals that contained:
Index
Animal
[1]
Wombat
[2]
Kangaroo
[3]
Gnu
[4]
Aardvark
[5]
Antelope
The command:
!Animals.Sort ()
Would move the array elements so that they now appeared in the following order:
Index
Animal
[1]
Aardvark
[2]
Antelope
[3]
Gnu
[4]
Kangaroo
[5]
Wombat
!Index = !Animals.SortedIndices()
6:5
12.0
Would create a new array !index representing the subscript values of the array elements in
!Animals then sort these index values (without altering the array !Animals). After the sort,
!index would look like this:
Index
Subscript
[1]
[2]
[3]
[4]
[5]
The command:
!Index.Invert()
Would result in !index now looking like:
6.5
Index
Subscript
[1]
[2]
[3]
[4]
[5]
!Colour
!Year
[1]
CHARIOT
MUD
26
[2]
FORD
RED
1978
[3]
VAUXHALL
YELLOW
1990
[4]
FORD
YELLOW
1993
[5]
FORD
(Unset)
1986
[6]
(Unset)
BLUE
1993
6:6
12.0
[7]
Ford
GREEN
(Unset)
[8]
(Unset)
(Unset)
(Unset)
[9]
vauxhall
YELLOW
1994
[10
FORD
YELLOW
1993
If you want to sort the arrays by car model, then on colour, and then on year, you would give
the command:
VAR
!Index
SORT
!Car
CIASCII
!Colour
!Year
NUMERIC
The sorted index values are put into the array !Index, (for clarity shown here with the rows
of the original arrays they are pointing to),
!Index
!Car
!Colour
!Year
[1]
CHARIOT
MUD
[2]
Ford
GREEN
(Unset)
[3]
FORD
RED
1978
[4]
FORD
YELLOW
1993
[5]
10
FORD
YELLOW
1993
[6]
FORD
(Unset)
1986
[7]
VAUXHALL
YELLOW
1990
[8]
vauxhall
YELLOW
1994
[9]
(Unset)
BLUE
1993
[10
(Unset)
(Unset)
(Unset)
By default values are sorted ascending order using the ASCII character-codes. Other
options which may be supplied after each array name are:
Sorting Option
Effect
CIASCII
DESCENDING
CIASCII DESCENDING
NUMERIC
NUMERIC
DESCENDING
6:7
12.0
You can also modify the array to eliminate empty or repeated elements:
Option
Effect
UNIQUE
NOUNSET
NOEMPTY
To sort these arrays and identify the last occurrence of each group of the same car type, use
the LASTINGROUP option:
VAR
!Index
SORT
!Car
LASTINGROUP
!Group
This would create !Index by sorting the values in the array !Car , but would also would also
create the array !Last:
!Index
!Group
[1]
[2]
!Car
CHARIOT
FORD
[3]
FORD
[4]
FORD
[5]
10
FORD
[6]
VAUXHALL
[7]
ford
[8]
vauxhall
[9]
(Unset)
[10]
(Unset)
6:8
12.0
6.6
VAR
!Index
SORT
!Car
!Value
LASTINGROUP
!Group
!Group
!Car
!Value
[1]
CHARIOT
50000
[2]
FORD
1000
[3]
FORD
100
[4]
FORD
8000
[5]
10
FORD
7000
[6]
VAUXHALL
2000
[7]
ford
1000
[8]
vauxhall
3000
[9]
(Unset)
9000
[10]
(Unset)
6000
We can then generate an array of subtotals for each type of car with the following command:
VAR
!Totals
SUBTOTAL
!Values
!Index
!Group
!Group
!Car
!Value
!Totals
[1]
CHARIOT
50000
50000
[2]
FORD
1000
[3]
FORD
100
[4]
FORD
8000
[5]
10
FORD
7000
16100
[6]
VAUXHALL
2000
2000
[7]
ford
1000
1000
[8]
vauxhall
3000
3000
6:9
12.0
[9]
[10]
(Unset)
9000
(Unset)
6000
6.7
Arrays of Objects
6.7.1
15000
Imagine that we have already defined an object type EMPLOYEE which contains a member
of type STRING. We create an array of EMPLOYEE objects and set the Name member of
each of each object.
!Names = ARRAY()
do !Person VALUES !MyEmployees
!Names.Append(!Person.Name)
enddo
Each time round the do-loop, the do-variable becomes an object of the appropriate type,
even if the array is a heterogeneous mixture of objects.
6.7.2
!ExtractName = object
BLOCK('!MyEmployees[!Evalindex].Name')
Note that the command is enclosed in quotes. The special variable Evalindex is
automatically incremented during the evaluation of the block to the index of each array
element in turn. Finally we use the Evaluate() method to invoke block processing on the
array:
!Names = !MyEmployees.Evaluate(!ExtractName)
!Names will contain the names as an array of STRING elements.
6:10
12.0
Take care that the expression does in fact return a value so that new arrays elements can
be created and assigned to it.
Alternatively, you may use an evaluation expression which does not return a result provided
you invoke the evaluation directly without trying to assign the result. For example, if you
have defined a !!Print() function the following is valid:
!Output = object
BLOCK('!!PRINT(!MyEmployees[!Evalindex].Name)')
!MyEmployees.Evaluate(!Output)
Note: In this release there is no object-oriented shorthand representing this element. It is
necessary to give the array name along with a subscript expression using
!Evalindex.
6.7.3
6.7.4
!Pump[1] = Fred
!Pump[20] = Tom
!Pump[10] = Jim
do !Name values !Pump
Sp array element is $!Name
enddo
This will produce the following output:
6:11
12.0
On exit from a do values loop PML destroys the loop counter variable.
With do indices the counter takes the value of each array subscript at which an array
element is stored:
!Pump[1] = Fred
!Pump[20] = Tom
!Pump[10] = Jim
do !IN indices !Pump
!Value = !Pump[!IN]
SP Array Element $!N is $!Value
enddo
This will produce the following output:
6:12
12.0
Macros
7.1
PML Macros
Macros are command sequences that are stored in text files.
To access a macro file and input the command sequence to your program, you run the
macro. The file is scanned line-by-line, with exactly the same effect as if you were typing the
lines in from a keyboard.
Macro files may include synonyms and user-defined variables. They may also act on data
which you define when you give the command to run the macro (parameterised macros).
Macros are permanent records which may be called from within any working session of a
program.
7.1.1
$M
filename
where filename is the pathname of the macro file. The filename may optionally be
preceded by a slash (/) character.
If you give only the name of the file, the program will look for it in the directory from which
you executed the program.
An error message will be output if the file cannot be found and opened for reading.
7.1.2
7:1
12.0
$n
where n is an integer in the range 1 to 9. These arguments are then specified as part
of the command to run the macro. They follow the macro name, with spaces
separating the individual arguments.
For example, if a macro named beam.mac includes the command line
$P First Argument is $1
$P Second Argument is $2
$P Third Argument is $3
and is called by the command
$,
instead of a space.
If you do this, you must end the argument list with the escape code
$.
7:2
12.0
Note: The full stop is part of the escape code, not punctuation.
Using this convention, the preceding example becomes
$Dn = default_string
where n is the argument number (1-9) and default_string is any sequence of
characters ending in a newline.
The default setting may be specified at any point in the macro, but it will only be applied from
that point onwards. It is usually best, therefore, to define any defaults at the beginning of the
macro. If an argument has been specifically defined, a subsequent default specification is
ignored.
Arguments may be omitted in the following ways:
If the normal macro calling sequence is used (spaces as separators, Return as end-ofline marker), trailing arguments may simply be omitted
If the $, argument separator is being used, the argument may be omitted from the list
For example, if the macro demo.mac expects three arguments, the following calls to run the
macro all omit one of the arguments:
7.2
Macro Call
Effect
$M/demo.mac $,$,arg2$,arg3$.
7:3
12.0
7.3
7:4
12.0
8.1
Composing Text
It is sometimes necessary to arrange text in multiple columns. The COMPOSE facility of the
VAR command will help you do this.
For example, if we create the following text string:
Value
[1]
The quick
[2]
brown fox
[3]
[4]
the lazy
'
[5]
dogs
'
COMPOSE always returns an array with at least one element. The number of array
elements depends on the length of the text strings supplied and the width of each column.
Notice that all of the STRING array elements are space-padded to the same length.
8:1
12.0
Following the COMPOSE keyword is a list of column definitions. For each column, there is a
text string, such as |$!A| which evaluates to a text string, followed by the column layout
keywords in any order:
Keyword
Effect
WIDTH n
SPACES n
LEFT
R
RIGHT
C
CENTRE
DELIMITER ' '
8.2
VVALUE( !X )
VTEXT( !AString )
VLOGICAL( !Aboolean)
8.3
8.3.1
8:2
12.0
!Item = !!CE
!!CE is of type DBREF so the new variable !Item will also be of type DBREF. The dot
notation can be used to access attributes and pseudo-attributes of a database element:
!Bore = !Item.bore
This form of access can also be used directly on the !!CE variable:
!Owner = !!CE.owner
It is also possible to follow references between DB elements using this mechanism:
!Rating = !!CE.cref.pspec.rating
Assigning a new reference to the !!CE variable makes the new reference the current
element by navigating to it in the database:
!!CE = !!CE.owner
P-points are accessed using the P-point number like an array subscript. For example, to
access the direction of P-point[1]:
!Dir
= !!CE.Pdirection[1]
!Pos = !!CE.Pposition[3]
if ( !MyDBRef
EQ
NULREF ) then
8.3.2
!!CE.Built = TRUE
You can still assign an attribute value in this way even if the PML DBREF is not the current
element':
!A = !!CE
!!CE = !!CE.Owner
!A.Built = TRUE
You can even assign a PML object, such as POSITION, where this corresponds to the type
of the attribute:
8:3
12.0
!Pos = !!CE.Position
!Pos.Up = 2000
!!CE.Position = !Pos
8.3.3
Current Session
Sessions
Projects
Teams
Users
MDBs
DBs
These commands can be used as in the following example. The SESSION object has a
method that returns name of the MDB in the current session. Hence:
!C = current session
!CurrentMDB = !C.MDB()
This will set !CurrentMDB to the name of the current MDB.
8.3.4
Where:
!Array
is the name of the array that will be created to contain the results of
(expression) for all the elements selected within select.
(expression)
is the expression that will be carried out for all the elements that
match the select criteria.
8:4
12.0
select
COUNTVAR
You can append the results of such an evaluation to an existing array using the APPEND
keyword. For example:
8.3.5
VAR !VarName
RAW
where represents any of the standard VAR syntax for setting variables.
8.3.6
MARKDB text
where text is an optional description to be included with the mark. This causes a mark
to be made in the database and an entry made in the undo stack. You should make
your database changes, and then use the command
ENDMARKDB
By creating a PML undoable object and adding it to the undo stack. See the Software
Customisation Reference Manual for a fuller description of this object. You should
create an undoable object, set up the undo and redo execution strings, and then call
the method add() to mark the database and add the undoable to the undo stack.
Make your database changes, and then call the method endUndoable().
8:5
12.0
After an undoable has been removed from the stack and its state recovered then the usersupplied method on the PostEvents object is called, and will be passed the description text
that was associated with the undoable object.
8:6
12.0
9.1
Assignment
Assignment always makes a copy of the right-hand-side to replace what is on the left-handside. The following command copies !Y into !X:
!X = !Y
If, for example, !Y is an array, this command will duplicate the entire array making a copy of
each of the array elements. The same is true if !Y is an OBJECT.
The technical term for this is deep copy. Following this copy
9.2
!X = !!Form.Gadget
!X is now a new reference, but the gadget itself has not been copied. Both PML Variables
now refer to the same gadget.
9.3
DB References
!!CE is a DB reference. Following the command:
!X = !!CE
!X is now a new reference to the same DB element, but the element itself has not been
copied.
!Value =
!X.Attribute
9:1
12.0
!!CE = !X
will navigate to a new current element provided !X is another DB reference. In this
example the command would navigate to the previous current element.
9.3.1
!X = !Y
will make a deep copy of !Y. However, if any of the array elements is a reference (e.g.
to a gadget or DB element), a copy is made of the reference, but not of the object it
refers to. In other words a deep copy stops copying when it reached a reference.
9.4
Function Arguments
A function argument is a PML reference to a value or object outside the function. In effect
the argument is another name for the original PML Variable.
If we define a PML Function such as the following:
9.4.1
9:2
12.0
!S = Old Value
!!ChangeString ( !S )
However, the following will result in a PML error message because the value passed to the
function as an argument is a CONSTANT STRING value which cannot be modified.
!!ChangeString ( 'OldValue' )
9.4.2
$* WRONG
9.5
Database Attributes
PDMS Database Attribute are read only, and so they cannot be given new values by
assigning to them.
9:3
12.0
9:4
12.0
10
10.1
Error Conditions
An error condition can occur because a command could not complete successfully or
because of a mistake in a PML Macro or function. The presence of an error normally has
three effects:
This example of an error is caused by an attempt to use a PML Variable that does not exist:
10.2
Handling Errors
An error arising during the processing of a PML Macro or function does not immediately give
rise to an error message - this depends on the next line of input.
Provided the next command processed by PML is a matching handle command, the error is
not output and the commands within the matching handleblock are processed instead.
elsehandle blocks may optionally also be present if the handle block does not match
the error one elsehandle will be processed if it matches the error:
$* a command causes Error(46, 28)
handle (46, 27)
$* handle block - not processed this time
elsehandle (46, 28)
$* The commands in this matching handle block are processed next
elsehandle ANY
$* An ANY Handle Block is processed for any errors. I
$* n this position it would handle errors other than
6, 28)
elsehandle NONE
$* A NONE Handle Block is processed only if there were no errors
10:1
12.0
endhandle
$* This line is processed after the handle block
If (46,27) matches the error, PML processes the commands in that handle block instead of
outputting an error. Processing of the current PML Macro or function continues at the line
after the endhandle command.
Note: The keywords ANY and NONE which can optionally be used in place of a specific
error code.
If the line following a line which has caused an error is not a handle command, the outcome
depends on the current setting of onerror (see next section). The default setting is
onerror RETURN and the current macro or function is abandoned.
However, if in the calling PML Macro or function the next command to be processed is a
handle command, it now gets an opportunity of handling the error (and so on up the calling
PML).
10.3
onerror RETURN
which causes the current macro or function to be abandoned and gives the calling macro or
function an opportunity to handle the error.
Another possibility is to jump to one section of code in the same macro or function after any
unhandled error:
onerror GOLABEL /LabelName
$* The first command to be executed after the label
$* must be a handle command - it could for example be a handle ANY
command:
label /LabelName
handle ANY
$* handle block
endhandle
To suppress errors, and error alerts the following may be used, for example during noninteractive testing:
onerror CONTINUE
Although the effect of errors is suppressed, any error messages generated will still be
output. Beware of using this option which can have unpredictable effects. The option is
applied to all subsequently nested PML Macro and function calls.
When debugging, you may interrupt normal PML processing after an error:
onerror PAUSE
The effect is the same as $M- and will allow you to type commands while the PML File is
suspended. You must type $M+ to resume the PML processing.
Do not leave this in working code.
10:2
12.0
10.4
To abandon the running PML Macro or function, but to re-instate the error so that the calling
PML code has a chance to handle it, you can use the command:
return error
You can also re-instate the error but suppress the alert using the command:
return error noalert
To generate a new error (or replace an error with your own error) plus an optional message,
use one of the following
return error 1
return error 1 Your error message
return error 1 NOALERT
10:3
12.0
10:4
12.0
11
11.1
11.1.1
Example
This example reads pairs of numbers from file data, adds them together and writes the
answers to file RESULTS.
!Input = object FILE('DATA')
!Input.Open(READ)
!Output = object FILE('RESULTS')
!Output.Open(WRITE)
do
!Line = !Input.ReadRecord()
if (!Line.set()) then
!array = !Line.Split()
!Total = !Array[1].Real() + !Array[2].Real()
!Output.WriteRecord( !Total.String() )
else
11:1
12.0
break
endif
enddo
!Output.Close()
!Input.Close()
11.2
11.3
Writing to Files
When you open a file for writing, the file will be created if it does not already exist.
If you use Open(WRITE) and the file already exists, the user will be shown an alert
asking whether the file can be overwritten. Alternatively you may specify OVERWRITE, to
force overwriting of a file if it already exists or APPEND if you want to add to the end of a file
if it already exists.
11.4
!Total.String() )
enddo
!Output.WriteFile('WRITE', !ResultArray)
Note: With the ReadFile() method you may optionally specify the maximum number of
lines you are prepared to read and an error is raised if this number is exceeded. If not
specified, a limit of 10000 is imposed.
11:2
12.0
11.5
11:3
12.0
11:4
12.0
12
12.1
PML Tracing
If a PML File does not appear to be running in the way you expect the easiest thing to do is
to turn on PML Tracing with the command:
PML TRACE ON
With PML Tracing switched on, each of the PML lines executed is output to the parent
window together with its line number. Additional tracing messages tell you when you have
begun to process a new PML File and when it has finished. To turn off tracing type:
12.2
12:1
12.0
12.3
Alpha Log
The alpha-log is a file containing a record of all the commands processed together with any
text output and error messages. To start recording use one of the following commands:
12.4
$R102
Refer to the online help given by $HR for other options.
12.5
$MYou can then query the values of any PML Variables of interest and even change the values
of some PML Variables. To resume processing of the suspended PML File type into the
command line:
$M+
Use these facilities only for debugging and do not leave these commands in a finished PML
File.
Note: These facilities do not apply to PML Functions or methods.
12.6
Querying PML
A variety of queries are available to help diagnose problems. Typically you might type these
queries into the command line whilst a PML File is suspended. Alternatively you could
include any of these commands temporarily within a PML File.
12:2
12.0
12.6.1
$QM
12.6.2
12.6.3
Command
Effect
q var !LocalName
q var LOCAL
q var !!GlobalName
q var GLOBAL
q var !MyArray[1]
q var !MyArray
q var !MyArray.Size()
$Q
This will produce a list of every valid command word or argument type that you may enter
next.
The list may be long and could include every valid command for the module that you are in.
The $Q facility is also useful to establish what is allowed as the next component of a
command. Type the beginning of the command sequence followed by $Q.
12:3
12.0
12:4
12.0
13
13.1
Overview
As a user of AVEVA products, you will already be familiar with forms, and the gadgets
(buttons, textboxes, etc) on them.
In PML 2, forms are a type of object represented by a global variable the forms name, for
example !!EntryForm. This means that a form cannot have the same name as any other
object type, global variable, or any other form.
The form object owns a set of predefined member variables and built-in methods. In
addition, you can define your own members form variables and form gadgets and your
own form methods. All these will determine the content and functionality of the form.
Gadget objects are user-defined members of the form object. Form members are always
accessed using the dot notation, for example
!!EntryForm.TextField
Gadgets own a set of predefined member variables and built-in methods which determine
the content and functionality of the gadget. For example, the value of a text field gadget is
accessed by:
!!EntryForm.TextField.val.
Note that gadgets do not support user-defined member variables or user-defined gadget
methods.
Note: All the in-built members and methods of forms and gadget-types are listed in the
Software Customisation Reference Manual.
Callbacks are user-defined actions assigned to a form and its gadgets and that are
executed when the operator interacts with the form, for example, by clicking a mouse button
on a gadget.
The callbacks are supplied as text strings and may be any valid PML expression, PML
Function or method call, including any form, gadget or user defined object method.
Effectively, these callbacks determine the intelligence of the form.
13.2
13:1
12.0
!!EntryForm.GadgetName
!!EntryForm.GadgetName.val
Note: That total length of the name is limited to 256 characters even though the length
each individual components is limited to 64 characters. Within the form definition, the
members of the form should be referred to by using !This to replace the form name
part of the gadget name. For example:
!This.GadgetName
!This.GadgetName.val
Note: The obsolete convention of using an underscore to replace the form name has been
retained for compatibility with earlier versions.
13.3
Simple Form
You define a form using a command sequence that starts with:
exit
Between these two commands come a number of optional subcommands which define the
gadgets on the form.
The following example defines a small form, containing the text Hello World, and a
button labelled Goodbye, which removes the form when pressed:
Some points to note about the above definition and the form it creates are:
there are no user-defined methods on this form, and so the setup form . . . exit
sequence is the complete form definition;
the paragraph command adds a paragraph gadget to the form (a paragraph gadget is
just a piece of text displayed on the form). The name of the gadget is Message, and
the dot before the name indicates that the gadget is a member of the form. The text
itself is specified after the keyword TEXT.
the button subcommand adds a button gadget named .bye. The text on the button
will be Goodbye. The keyword OK is a form control attribute that specifies that the
13:2
12.0
action of this button is to remove the form from the screen. (For more about form
control attributes, see Form Attributes.)
To display the form in this example, you can use the command:
show !!Hello
13.3.1
Change
message
callback
|!this.message.val
!this.message.val = Modified
will set the value member of the gadget Message on this form to read Modified rather
than Hello world.
Note: The gadget callback could have been
|!!OtherForm.Para.val = Modified|
to change the value of a paragraph gadget on another form named !!OtherForm.
Typically a callback will involve many commands and could be a complex piece of code in
its own right. In practise, the recommended way of defining a complicated callback is to use
a form method.
First though, we need to store our form definition in a file so that we can edit it at will.
13:3
12.0
13.4
13.5
The form definition between setup form and exit. This includes the commands
which create the form itself, and set its attributes, such as its size and title, the
commands which create the gadgets on the form and specify how they are arranged,
and the definitions of any variables which are to be members of the form.
Any method definitions should follow the exit command, each method beginning with
the define method command and ending with endmethod. Methods on forms are
just like methods on any other kind of object - see Methods on User-Defined Object
Types
In particular, it will contain the form's default constructor method. This is a method
with the same name as the form, and no arguments. It is the only method called
automatically when the form is loaded, and so it can be used, among other things, to
set default values for the gadgets on the form.
The form may be given an initialisation method, which is run whenever the form is
shown (as opposed to when it is loaded). See Form Initialisation Callback.
No executable statements should appear in the file outside of the form definition or
form methods. The effect of misplaced executable statements is indeterminate. You
can put comments anywhere in the file.
show !!formname
Sometimes it is useful to force the loading of a forms definition file before the form is
actually displayed, so that you can edit the form or gadget attributes from another forms
callbacks before the form is actually displayed. Using the command
loadform !!formname
from a callback will force load the definition if the form is unknown to PML, but do nothing if
the form has already been loaded.
Once a form has been displayed you can remove it from the screen using the command
hide !!formname
13:4
12.0
Note that if you show it again it will appear on the screen, but its definition is already known
to PML and so it will not be loaded.
It is possible to remove a form definition from PML using the command
kill !!formname
The form is then no longer known to PML until a new definition is loaded.
Note: Earlier AppWare used form definitions in macro files which had to be loaded explicitly
via $M path-name-to-file. This mechanism still operates for backwards
compatibility, but is strongly discouraged when writing new AppWare.
13.6
PML Directives
PML directives are commands used to control PML itself.
For example, you use a PML directive to instruct PML to re-make its index when you have
added a new file. Some of these directives have been described in Storing and Loading
PML Files: the information is repeated here, with additional directives for loading forms.
Note: Unlike the PML commands described in How Forms are Loaded and Displayed, PML
directives should not be included in callbacks, but are generally for command line
use.
You will need to use PML directives when you are developing new form definitions or
modifying existing ones. PML directives are commands of the form pml . . .
The table below gives some useful PML directives.
Command
Effect
pml rehash
This
command
re-reads
all
the
pml.index files in your search path
without rebuilding them.
pml index
13:5
12.0
kill !!formname
pmlscan directory_name
See Developing PML Code for tracing commands that will be useful when you start to
develop PML code.
13.7
First save the definition in a file called hello.pmlfrm and ensure that its directory is in
your PMLLIB search-path.
13:6
12.0
text adds a TEXT field gadget with tag Enter message, width 15 chars, to hold
data of type STRING.
defines the callback on the text input field: to insert its value into the paragraph;
sets the form member Okcall (in the line beginning !this.Okcall). This is a
callback that gets executed when a button with control-type OK is pressed.
PML rehash
show !!Hello
This will auto-load the form definition and execute the default constructor method (which will
set the message paragraph to Hello world and define the gadget and form callbacks).
It will also display the form.
Type your message into the Enter message field and press the Enter key. This will
execute the fields callback, which will write your typed text into the message paragraph.
Type a new message and press Enter. The paragraph updates to reflect what you typed.
Click the Goodbye button. This executes the forms Okcall action which calls the
success() method. The success() method sets the paragraph to Hello again and
blanks out the text field. Finally, the OK control action hides the form.
Show the form again and observe that the paragraph reads Hello again and not
Hello world. This demonstrates that when you re-show the form the forms constructor
is not run, because the form is already loaded.
If you want to reset the form every time it is shown, you must define a form initialisation
callback see Form Initialisation Callback.
13:7
12.0
13:8
12.0
14
14.1
Callbacks: Expressions
A callback may be any valid expression, including any AVEVA product commands. For
example, the following is a PDMS command:
$m %pathname%/MyMacro
to execute a given command macro, or
q var !!form
q var !!form.gadget
which will write out details of the form or gadget and its members. You might like to try
it on the form !!Hello.
Typical expressions for a callback are
!this.gadget.val = !MyObject.count
!MyObject.count = !this.gadget.val
which get or set the value of a gadget on the form from or to a user-defined object.
14.2
14:1
12.0
The advantage of using form methods as callbacks is that this keeps the whole form
definition in a single file. Forms defined in early versions of PML 2 used PML Functions as
callbacks. This is still valid and is sometimes even essential as you may need to manage a
group of forms; but mostly the callbacks on a form are specific to that form.
You should note that a method or function invocation is just a valid PML expression, but
such a powerful expression that it is worthy of its own section in the manual.
We have already used a form method as a callback in the revised !Hello form in Form
Concepts chapter:
setup form !!hello
title Display Your Message
paragraph .Message width 15 height 1
text .capture Enter message width 15 is STRING
button .bye 'Goodbye' OK
exit
define method .hello()
--default constructor - set gadget default values
!this.message.val = Hello world
!this.capture.callback = !this.message.val = !this.capture.val
!this.Okcall = !this.success()
endmethod
define method .success()
!this.capture.val =
endmethod
The .success() method above could only deliver a fixed string Hello again to the
message PARAGRAPH gadget. The great advantage of methods is that you can pass
variables as arguments to the method, so it can be used more generally, for example as the
callback to several gadgets.
define method .success( !output is GADGET, !message is STRING, !input
is GADGET )
output.val = !message
input.val =
endmethod
We have added three arguments, an output gadget, an input gadget and a message string
variable. This has made the method very general. We can still use it as the Okcall callback:
!this.Okcall
=
!this.capture )|
|!this.success(
!this.message,
Hello
again,
When the OK button is pressed the Okcall action will invoke the success() method,
passing to it the message paragraph gadget as !output, the message text Hello
again as !message and the text input field gadget capture as !input. The method will do
just what it did before.
However, we could use it differently. We could add another button gadget, Restart, to the
form:
button .restore 'Restart' callback
Hello world, !this.capture )|
14:2
|!this.success(
!this.message,
12.0
When clicked, the Restart buttons callback will execute and set the message paragraph
gadget to read Hello world and clear the capture text field, thus restoring the form to
its state when it was displayed for the very first time.
If we invoked the success() method as:
!this.success( !this.capture, Hello world, !this.message )
it would set the value Hello world into the capture text input field and clear the
contents of the message PARAGRAPH gadget. Not what you need here perhaps, but you
can see how versatile methods can be!
Note: The arguments to methods can be any valid PML object types, built in or user
defined.
14.3
14.3.1
Events
When the operator interacts with a GUI, an event occurs. For example, when the operator:
14.3.2
!object is a Forms and Menus object, for example, a form, gadget or menu.
14:3
12.0
!action is the meta-event that occurred on the object and represents the action to be
carried out by the method.
'!this.MethodName('
Note: The open bracket '(', no arguments and no closing bracket. The callback is to an
open method or function.
We might assign an open callback to a multi-choice list gadget as follows:
setup form !!Open
title 'Test Open Callbacks'
list .choose callback '!this.control(' multi width 15 height 8
exit
define method .open()
do !i from 1 to 10
!fields[!i] = 'list field $!i'
enddo
this.choose.dtext = !fields
endmethod
define method .Control( !object is GADGET, !action is STRIN
G)
if ( !action eq 'SELECT' ) then
--find out all about our gadget object
!form = !object.owner()
!type = !object.type()
!name = !object.name()
!field = !object.PickedField
$*get picked
number
!s = !object.DTEXT[!field]
$*get DTEXT
field
$*get picked
number
field
14:4
12.0
endif
endmethod
Note in the constructor method open() we have initialised the list so that field n will
display list field n. DTEXT is a shorthand for display-text, that is the text displayed in
the list field.
Control is the method which manages interaction with the list. Note the open callback
defined in list choose.
Note the use of the printing command $p to print information to the system Request
channel. $!form replaces the variable !form with its current value as a string - this
only works for PML simple in-built scalars types REAL, STRING, BOOLEAN. $n in
$!form$n.$!name is a separator needed to separate $!form from the following .
which would otherwise be thought of as part of the variable name.
When a list field is clicked, the lists callback will be examined and if it is an open
method or function, then the Forms and Menus software will supply the arguments
required to complete the function.
Thus in this case the actual callback string executed will be
!type = !object.type()
!form = !object.owner()
All the in-built members and methods of forms and gadget types are listed in the Software
Customisation Reference Manual.
14.3.3
14:5
12.0
14.3.4
Object
Object
Classification
Callback
LIST multichoice
Gadget
LIST singlechoice
Gadget
SELECT, UNSELECT
OPTION
Gadget
SELECT, UNSELECT
ALPHA VIEW
Gadget
SELECT
BUTTON
Gadget
SELECT, UNSELECT
Gadget
SELECT, UNSELECT
Menu
SELECT, INIT
Menu
FORM
Form
TEXT
Gadget
SLIDER
Gadget
FRAME
Gadget
SELECT, UNSELECT,
SHOWN, HIDDEN
NUMERICINPUT
Gadget
SELECT, MODIFIED
COMBOBOX
Gadget
Note: The 2D and 3D VIEW objects supplied in AVEVA products, in particular those in
which Event-Driven Graphics are used, may have open PML Functions as
callbacks. However, open callbacks on these gadgets are not supported as a userdefinable facility at this release.
14.4
14:6
12.0
14.5
14:7
12.0
14:8
12.0
15
Forms
Before reading this section you should have read the Form Concepts chapter.
15.1
15.1.1
A Main form to present the default application and to control access to the other
applications.
Application Window
The Application Window(AppWin) normally occupies most of the screen. When a Main
form is assigned to it, the AppWins components are replaced by corresponding
components of the Main form.
From top to bottom the AppWin comprises:
The AppWin Menu Bar, which contains the Main forms menu bar.
The Dialog Bar, which contains the Main forms tool bars.
The Document Area, which contains one or more document forms, which are normally
constrained to this area.
The Status Bar, which displays current status, messages and general information to the
user.
The picture below shows a typical example of what the AppWin looks like:
15:1
12.0
15.1.2
Current Document
The AppWin supports the Windows Multi-Document Interface(MDI) to manage its forms of
type Document. When a document is shown, or when the user selects a document from the
menubars Window menu or from the window management buttons on the status bar, the
document becomes current, and may then be the target for actions selected from menubar,
toolbars or from any docking dialogs currently displayed.
For more detailed information about forms in the context of a AVEVA PDMS application, for
example, how to define an AppWindow and a main application form and swap to it, see
Chapter Error in Cross Reference.
15.2
Defining a Form
The structure of a form definition file has already been discussed in Form Definition File.
The form definition is a command sequence starting with:
exit
The sequence includes:
The commands which create the form itself and set its attributes, such as its minimum
size and title.
15:2
12.0
15.2.1
The commands which create the gadgets on the form, and specify how they are
arranged.
Form Attributes
All the form attributes are optional and have sensible defaults. Those which can only be set
once must be specified on the setup form line. These are:
15.2.2
Form type.
Minimum size.
Resizability.
Docking.
Form position
NOQUIT
NOALIGN
Other attributes are specified as sub-commands in the setup form . . . exit
sequence. They can be edited after the form has been created by means of the forms
in-built members and methods.
Form Type
The appearance and behaviour of a form is determined by its Type attribute:
Type of Form
Description
MAIN
DOCUMENT
DIALOG
This is the default type the form will assume if you give no type
when you set up the form. The default DIALOG form will be
non-resizable, floating, and non-docking. You can specify the
DOCKING attribute to allow the form to be docked within the
application frame. By default, a docking dialog is displayed
floating, and you can interactively dock it. When a dialog is
docked it will be resized to match the application frame edge
to which it is docked, and so is resizable by default. The
qualifiers LEFT, RIGHT, TOP, and BOTTOM, specify the edge
of the application frame to which the dialog form will be
docked when first displayed.
BLOCKINGDIALOG
Normal form layout and content, but will block access to all
other forms while it is displayed.
15:3
12.0
Here are some examples of ways you can set up forms of different types:
15.2.3
Setup Code
Description
Creates
dialog;
non-resizable
floating
Creates
dialog;
non-resizable
floating
Creates a
document;
resizable
MDI
child
Character width is the notional character width for the selected character font.
Line height is the height of the tallest single line gadget, that is a TOGGLE, BUTTON,
RADIO BUTTON, OPTION gadget or single-line PARAGRAPH for the selected
character font.
The RESIZABLE command means that the form will be displayed with re-sizing controls at
its corners. This will allow the user to change the size of the form. Docking forms and all
document forms are resizable by default; but for other types, if you do not include the
RESIZABLE command, the size of the form is fixed.
15.2.4
15:4
12.0
However, the DOCK and ANCHOR attributes are mutually exclusive: setting the DOCK
attribute resets the ANCHOR to the default; setting the ANCHOR attribute resets DOCK to
none.
ALPHA and VIEW gadgets do not support DOCK or ANCHOR attributes. They do,
however, expand to fill their containers, so you can put them in a frame and set the frames
DOCK or ANCHOR attributes to get the behaviour you desire.
15.2.5
15.3
Form Members
15.3.1
15.3.2
INITCALL !This.InitCallBack()
15:5
12.0
or directly by
!!MyForm.initcall = This.InitCallBack()
Note: The form initialisation callback must not attempt to display another form. You may
invoke an ALERT object but not otherwise seek any input from the user.
If the callback discovers an error so serious that the form cannot be displayed it can abort
the display of the form by returning an error. You can supply the text of an error message
that is to be presented in an error alert in place of the form:
15.3.3
OKCALL CallbackString
You can modify the Okcallback at any time using
!this.Okcall = CallbackString
The CANCELCALL callback is executed when a forms CANCEL button is pressed, when the
CANCEL button of a forms ancestor is pressed (see section on Form Families) or when the
windows CLOSE gadget is used. It allows the operator not to proceed with the functions of
the form.
The form is then removed from the screen and all gadgets are automatically reset to the
values they had when the form was displayed or when any APPLY button was last pressed.
Typically this callback allows you, the PML programmer, to undo any actions already carried
out on the form that ought to be undone in the light of the CANCEL request
15:6
12.0
You can assign to the forms CANCELCALL member using the command
CANCELCALL CallbackString
You can modify the CANCELcallback at any time using
!this.Cancelcall = CallbackString
See also Button Gadgets.
15.3.4
Quit/Close Callback
All Forms have a QUITCALL member that you can pass a standard callback string. This is
executed whenever the user presses the Quit/Close icon (X) on the title bar of forms and
the main application window.
If an open callback is used then it is called with the FORM object as its first parameter and
QUIT as its action string.
QUITCALL for MAIN Forms
For forms of type MAIN, the QUITCALL callback is executed, if present. This permits the
user to terminate the application, and so the associated PML callback should prompt the
user for confirmation.
If the user confirms the quit, then the callback should close down the application, and not
return. If the user decides not to quit, then the callback should return an error to indicate the
decision to F&M.
Use return errornoalert if you want to avoid displaying an error alert. If the form
has no QUIT callback, then the QUIT event will be ignored.
The following example shows a (global) PML function, that you could be use from all forms
of type MAIN:
15:7
12.0
quitCall !!quitMain( )
. . .
exit
define method .myApplication( )
-- Constructor
!this.quitCall = !!quitMain( )
. . .
endmethod
QUITCALL for Other Forms
Essentially, if no QUIT callback is present, then the form is cancelled (hidden with reset of
gadget values). If a QUIT callback is provided then you can prevent the default Cancel
action by returning a PML error, but you must hide the form from your callback method (It is
more efficient the use !this.hide(), rather than hide !!myform from your form methods).
Note: F&M does not display an alert for the returned error, it is merely for communication.
You dont need a QUIT callback if you just want to allow the form to be hidden. For
DOCUMENT forms (MDI children) only, the callback must not display an alert as
this will cause some gadgets to malfunction afterwards.
15.3.5
FIRSTSHOWN callback
Typically assigned in the Constructor by
!this.FirstShownCall = '!this.<form_method>'
The purpose is to allow the user to carry out any form actions which can only be completed
when the form is actually displayed. There are a variety of circumstances where this arises
and it is often difficult to find a reliable solution. A couple of examples are given below.
Commands which manipulate form, menu or gadget visual properties, executed from a PML
macro, function or callback may not happen until control is returned to the window
manager's event loop. For example, in the application's start-up macro the command
sequence show !!myForm hide !!myform will result in the form not being displayed, but
also not becoming known at all to the window manager. Attempts to communicate with this
form via the External callback mechanism (possibly from another process) will not work.
This can be rectified by doing the '!this.hide()' within the FIRSTSHOWN callback, because
the form will be guaranteed to be actually displayed (and hence known to the window
manager), before it is hidden.
It is sometimes difficult to achieve the correct gadget background colour setting the first time
the form is displayed. This can be resolved by setting the required colour in the
FIRSTSHOWN callback.
15.3.6
KILLING callback
Typically assigned in the Constructor by
!this.KillingCall = '!this.<form_method>'
The purpose is to notify the form that it is being destroyed and allow the assigned callback
method to destroy any associated resources, e.g. global PML objects which would
15:8
12.0
otherwise not be destroyed. This may be necessary because PML global objects will survive
an application module switch, but may not be valid in the new module.
Notes:
1. The callback method MUST NOT carry out any modifications to the Gadgets belonging
to the form or to the Form itself (e.g. don't show or hide the form). Attempts to edit the
form or its gadgets may cause unwanted side effects or possible system errors.
2. Form callbacks designed for other Form events (e.g. CANCEL, INIT) are rarely suitable
as killing callbacks.
3. Restrict form and gadget operations to querying.
15.3.7
!!MyForm.MyNumber = 42
!Text = !This.MyNumber
!ThirdValue = !This.MyArray[3]
In a callback function you can use !this. to represent 'this form':
!ThirdValue = !This.MyArray[3]
15.3.8
q var !!formname
This will list all the attributes of the form, and all the gadgets defined on it. This is a useful
debugging aid.
To query all the gadgets of a form (excludes USERDATA gadget) use:
!!gadgetsarray = !!MyForm.gadgets()
15:9
12.0
15.4
15.4.1
15.4.2
show !!formname
used in the gadgets callback. In either case the form becomes a child of the menus or
gadgets owning form.
A form may be displayed free-standing, i.e. not as a child, by:
15:10
12.0
loadform !!formname
from a callback will force load the definition if the form is unknown to PML, but do
nothing if the form has already been loaded.
If you are sure that a forms definition has been loaded then you can show the form as a
child or free-standing respectively using the form methods:
!!formname.show( )
!!formname.show( free )
but note that this will not dynamically load the form definition.
15.4.3
15:11
12.0
15.4.4
Hiding Forms
The recommended way for a form to be removed from the screen is for the user to press a
button with the OK or CANCEL attribute.
Forms may also be cancelled by using the windows close controls. Both these
mechanisms will remove the form and any of its children executing their OK or CANCEL
callbacks appropriately.
Sometimes it is required to hide a form and other forms which are functionally associated
but not part of the form family or as a result of a button press on a form you may want to hide
other associated forms but not the form whose button was pressed. The hide command or
method allows this:
hide !!MyForm
!!MyForm.hide()
Note: When you explicitly hide a form in this way its gadgets will be reset to their values at
display or at the last APPLY, just like a CANCEL action, but the CANCELCALL
callbacks for the form and its nest will not be applied. This means that before you
execute the hide you should use any pertinent data from the forms to be hidden.
15.4.5
Killing Forms
You can destroy a loaded form definition using the command
kill !!MyForm
The form is hidden and then its definition is destroyed so that it is then no longer known to
PML. You cannot then access the form or its gadgets, members or methods (including its
.show() method.
Normally you will not need to include the kill command within your AppWare.
If you re-show the form using the show command then the system will attempt to demand
load the definition again. This is slow and expensive if you havent modified the definition so
avoid it: use loadform !!MyForm and !!MyForm.show() instead.
If you execute a setup form !!MyForm... while the form !!MyForm already exists then
it is killed and a new definition is started. This mechanism may be useful because it makes it
possible to generate a form definition interactively from within your AppWare.
This can be very powerful but should be used with great care.
15.4.6
. . .
NOQUIT
By default, you can quit from any user-defined form, except for the current system Main
form.
15.5
15:12
12.0
16
Menus
Menus are always members of forms but can be employed in various ways by the form and
its gadgets.
Menus come in two types: main menus and popup menus. You determine what type a menu
is when you create it. If you do not specify the type, then the system will try to infer its type
from the context of its first use.
For example, if the first action in a form definition is to define the menubar, then the system
will infer any menus referenced in the barexit sequence to be of type MAIN.
Or, as a second example, if a menu is assigned as the popup for the form or a gadget of the
form, then the system will infer the menu to be of type POPUP.
16.1
16.1.1
Each menu belongs either to the Main menu system or to the Popup menu system,
but cannot belong to both.
A menu in the Main system can appear only once. i.e. it cannot be a sub-menu of
several menus.
A menu in the Popup system may appear only once in a given popup tree, but may be
used in any number of popup trees.
A menu cannot reference itself, either directly as a pullright of one of its own fields or
be a pullright of another menu in its own menu tree.
Any pullright field of a menu references a sub-menu that will be inferred to be of the
same type as the referencing menu.
It is not necessary to specify the menu usage type on a form that has only main menus
(and this includes most Main forms).
It is always necessary to specify the usage-type for menus that are part of the POPUP
menu system.
16:1
12.0
16.1.2
Next define menus of the popup system, declaring them all as type POPUP.
If you are dynamically creating new menus with the NewMenu() form methods, then
always specify the menu type. This will maximise the systems chance of alerting you to
any errors.
16.2
The Choose option when picked will open Menu1 as a pull-down (assuming you have
defined Menu1).
Note: That Menu1 need not exist when the bar is defined, it can be defined later in the form
definition, but it must exist before the Choose option is selected or an error alert will
be raised.
16:2
12.0
16.2.1
The Window and Help options will open the Window and Help system menus
(described later in this chapter).
Execute a callback.
Display a form.
Display a sub-menu.
You can also add a visual separator between fields.
toggle
field
as
ticked
(typically
in
the
The SAVE field when picked will execute the callback command this.SaveFile().
The Save as... field when picked will load and display the form !!SaveFile. By
convention, the text on a menu field leading to a form ends with three dots, which you
must include with the text displayed for the field.
The SEPARATOR, usually a line, will appear after the previous field.
The Pull-right1 field when picked will display the sub-menu !this.Pull1 to its right. A
menu field leading to a sub-menu ends with a > symbol: this is added automatically.
16:3
12.0
Description
<FieldType>
<Dtext>
<Rtext>
<FieldName>
16.2.2
Window Menu
You can add the system Window menu to a bar menu using:
16.2.3
16:4
12.0
When selected, this Help option displays a system-help pull-down menu that gives access
to the application help system. The fields are:
Field
Description
Contents
Index
Search
About
You can access On Window help by pressing the F1 key while the form has keyboard
focus, or by including a Help button in the forms definition.
Note: By convention, the help menu should be the last one defined for the menu bar, which
will ensure that it appears at the right-hand end of the menu bar.
16.2.4
Popup Menus
You can use any of your defined menus as popup menus for most interactive gadgets and
for the form background as long as you have specified them as belonging to the popup
menu system.
When the cursor is moved over it with the popup mouse button pressed down, and then
released, the menu will be displayed, and you can select from it in the normal way.
A popup is added to a gadget or form using its Setpopup() method, with the popup menu
as the argument to the method. Note that the menu pop1 must exist when the Setpopup()
method is executed.
For example:
setup form !!MyForm resizable
menu .pop1 popup
!this.pop1.add( 'MENU', 'Options', 'optionmenu' )
!this.pop1.add( 'MENU', 'More', 'moremenu' )
!this.pop1.add( 'MENU', 'Last', 'Lastmenu'
button .b1 ...
. . .
!this.b1.setpopup( !this.pop1 )
. . .
exit
16:5
12.0
16.2.5
!menu.popupGadget() is GADGET
If the menu was a popup on a gadget then the returned GADGET variable is a reference to
the gadget. If the menu was popped up from a pulldown-menu or from a popup on the form
itself, the value is UNSET.
Example:
!g = !menu.popupGadget()
if !g.set() then
!n = !g.name()
$p menu popped up by gadget $!n
else
!n = menu.owner().name()
$p menu popped up by form $!n
endif
16.2.6
Toggle Menus
A menu TOGGLE field is a menu field with a callback action and a tick-box to show that the
field has been selected or unselected.
By default the field will be unselected so the box will not be ticked. When picked the fields
callback action will be executed and the tick-box ticked.
If you pick the field again the callback action will again be executed and the tick removed.
Note that the open callback is an obvious candidate for toggle menus as the SELECT or
UNSELECT action is returned as the second argument to the callback method. See PML
Functions and Methods
For example, in your form definition you can add a toggle field as follows:
setup form !!Myform Dialog size 30 5
. . .
!menu = !this.newmenu(Test, popup)
!menu.add(
'OnOff' )
'Toggle'
,Active/Inactive,
'!this.toggle(,
. . .
exit
. . .
define method .toggle( !menu IS MENU, !action IS STRING )
!name = !menu.fullname()
!field = !menu.PickedFieldName
$P menu $!name $!action field: $!field
16:6
12.0
endmethod
Note: How we use the PickedFieldName member of the menu object to obtain the last
picked field.
If you pick this menu field the callback method will print:
16.3
16.3.1
Description
<TargetMenuName>
<Dtext>
<FieldName>
For example:
16:7
12.0
If you use the identifier Window or Help as the name of the menu, the system will
interpret them as system Window and Help menus, although they will still be displayed with
the string given in <dtext>.
Named menus and the methods that create them are discussed in more detail in the rest of
this section.
16.3.2
InsertBefore(<TargetFieldName>,<FieldType>,<Dtext>,<Rtext
>,{<FieldName>})
InsertBefore(SEPARATOR,{<FieldName>})
InsertAfter(<TargetFieldName>,<FieldType>,<Dtext>,<Rtext>
,{<FieldName>})
InsertAfter(SEPARATOR,{<FieldName>})
Where the fields have the following meanings:
Field
Description
<TargetFieldName>
<FieldType>
<Dtext>
<Rtext>
<FieldName>
16:8
12.0
16.3.3
Description
<FieldName>
<PropertyName>
Boolean
Note: The property names may optionally be truncated to the first three characters ACT,
VIS, and SEL.
For example:
!bar = !!MyForm.bar
!menu = !!MyForm.Menu1
sets local variables !bar and !menu to be references to the bar gadget and Menu1 of
form !!MyForm.
Then
16:9
12.0
Description
<FieldName>
<PropertyName>
Note: The property names may optionally be truncated to the first three characters ACT,
VIS, and SEL.
For example:
!bar = !!MyForm.bar
sets local variable !bar to be a reference to the bar gadget of form !!MyForm.
Then
16.3.4
16:10
12.0
will make the all the fields invisible for the group currently implied by the separator field
SaveGroup, i.e. the fields SaveGroup, Save and SaveAs.
The combination of named SEPARATOR fields, insertion and field group visibility will be
useful for managing the sharing of menus between co-operating sub-applications. This
facility should be used with great care.
16.3.5
application
actions
according
to
the
field
16:11
12.0
16:12
12.0
17
Form Layout
Typically gadgets are laid out onto the form from left to right and from top to bottom. It is
unnecessary and undesirable to specify absolute positions for gadgets; we recommend
defining the layout of each gadget relative to a predecessor. This allows the layout to be
edited simply, without having to calculate new positions.
17.1
The origin (0,0) of the grid is at the top left-hand corner of the form.
The horizontal pitch is the notional character width for the currently selected font.
The vertical pitch is the notional line-height, which is the height of the tallest single
line gadget for the currently selected font, i.e. the maximum height of a textual
TOGGLE, BUTTON, OPTION or TEXT gadget.
0
1
2
3
4
5
6
7
0 1 2 3 4 5 6 7 8 9
Y
Figure 17:1. Conceptual X and Y Coordinates
At any time the form has maximum and minimum extremities XMIN, YMIN, XMAX, YMAX.
As new gadgets are added to it the form XMAX and YMAX extents grow to include the
gadget boxes.
Note: The grid width is the notional character width because fonts usually have
proportional spacing so different characters in the font typically have different widths.
Thus n grid units does not equate to n characters. Generally, you can get more than
n characters in n grid units unless the string has a high proportion of wide characters
e.g. capitals, numerals, ws, ms. It is important that you understand this concept
when you specify the width and height of gadgets.
17:1
12.0
17.1.1
17.1.2
Gadget Box
A gadget can be thought of as having an enclosing box with an origin at the top left hand
corner. This box will enclose the geometry of the gadget, including its name-tag if specified.
The extremities of a gadget box on the form can be referenced by the gadget pseudovariables:
In their definition gadgets may be positioned at specific form coordinates form, using the AT
keyword followed by the X and Y grid coordinates.
17.2
Positioning Gadgets
It is recommended that you layout a form using one or both of the following schemes. Both
allow the form design to have gadgets added, removed, and repositioned, without having to
adjust the geometry of all other gadgets on the form.
17:2
12.0
Auto-placement uses the PATH, DISTANCE and ALIGNMENT keywords. These keywords
have the following meaning:
Keyword
Description
PATH
DISTANCE
ALIGNMENT
specifies how the next gadget aligns to the previous one for
the current path.
Relative placement means specifying the new gadget position by explicit reference to the
extremities of a previous gadget.
For special cases it is also possible to position gadgets using explicit form coordinates. See
Absolute Gadget Positioning.
17.3
Auto-placement
17.3.1
PATH right
PATH left
PATH up
PATH down
The default direction is PATH right.
The current path direction is set until you give a different PATH command.
17.3.2
HDIST 4
VDIST 2
where the clearance distances are specified in grid units.
The defaults are HDIST 0.25, VDIST 1.0
Note that these specify clearance distances between gadgets (for example, the distance
between the XMAX of one gadget and the XMIN of the next), not the distance between
gadget origins, thus:
17:3
12.0
.GADG1
.GADG2
HDISTANCE
VDISTANCE
.GADG3
Figure 17:2. Distances Between Gadgets
17.3.3
Gadget Alignment
To specify horizontal and vertical alignments" of adjacent gadgets, use the HALIGN and
VALIGN commands, respectively. The options are:
HALIGN left
HALIGN centre
HALIGN right
VALIGN top
VALIGN centre
VALIGN bottom
The defaults are HALIGN left and VALIGN top.
17.3.4
17:4
12.0
VALIGN top
PATH
VALIGN centre
VALIGN bottom
last
next
last
next
next
last
PATH
next
last
HALIGN left
next
last
last
D
next
next
next
next
next
next
D
last
last
last
HALIGN right
PATH
next
HALIGN centre
last
PATH
last
last
Combinations of path, distance and alignment commands allow you to set up correctly
aligned rows and columns of gadgets without the need to calculate any grid coordinates.
For example, the command sequence:
button
$* default placement
.But1
PATH down
HALIGN centre
VDIST 2
paragraph
toggle
.Par2
width 3 height 2
$* auto-placed
$* auto-placed
.Tog3
PATH right
HDIST 3.0
VALIGN bottom
list
$* auto-placed
PATH up
HALIGN right
paragraph .Par5
width 3 height 3
17:5
$* auto-placed
12.0
.But1
.Par5
The gadgets are created in numeric order with the
arrow direction showing the current path when each
new gadget was placed (down, down, right, up).
.Par2
.Lis4
.Tog3
17.3.5
17.4
Relative Placement
Relative Placement uses the AT keyword, followed by keywords and values that define the
offsets from another gadget. The complete AT syntax is given in AT Syntax.
17.4.1
17:6
12.0
0
1
2
3
4
5
6
0 1 2 3 4 5 6 7 8 9
Y
new toggle gadget
XMIN
0
1
2
3
4
5
6
0 1 2 3 4 5 6 7 8 9
Y
XMIN-2
XMIN
Note: The new gadget need not be adjacent to the referenced gadget(s). The same rules
apply even if there are other gadgets positioned between them. You will probably find
form design easier, however, if you add gadgets in two directions only (right and
down, say), rather than in a more random order
17.4.2
17:7
12.0
Y-direction the gadgets origin is placed at the forms current maximum depth and extends it
to include the gadgets height. The result is:
0
1
2
3
4
5
6
7
0 1 2 3 4 5 6 7 8 9
17.5
PATH down
. . .
toggle .t3 AT xmin.Gadget1
places .t2 with respect to XMIN of gadget .Gadget2, whilst the Y coordinate for .t2 is autoplaced at current VDIST value below the last placed gadget.
17.6
toggle .OnOff at 3
TOGGLE .ONOFF AT X 3
3.5
Y 3.5
X 3
positions a new toggle gadget with its origin at grid coordinates (3, 3.5), thus:
17:8
12.0
0
1
2
3
4
5
6
7
0 1 2 3 4 5 6 7 8 9
Y
Figure 17:8. Positioning at a Known Location
Note: You can position gadgets anywhere on the grid, not only at the grid intersection
points.
17.7
AT Syntax
The AT syntax is used to define the position of a gadgets origin within a form. The position
may be specified absolutely (in form layout grid units) or relative to the extremities of
existing gadgets, or relative to the size of the form and the gadget.
The rest of the Forms and Menus syntax is described in Software Customisation
Reference Manual: refer to that manual for more information, in particular about
conventions used in the syntax graphs. The AT syntax graph is the easiest way of
illustrating all the possible options, and so it is included here, as well as in the Software
Customisation Reference Manual.
17:9
12.0
where <FGREL>, shown below, sets the gadget position relative to another gadget or the
forms extent. For example, it can be used to position a gadget half-way across the width of
a form:
>----+- <gname> -.
|
|
+-- FORM ---|
|
|
-----------+- * val --.
|
|
+- + val --|
|
|
+- - val --|
|
|
+- val ----+--- + val * SIZE --.
|
|
|
|
+-- - val * SIZE ---|
|
|
|
|
+- - SIZE ---------|
|
|
|
|
-------------------|
|
|
+-- SIZE ----------------------|
|
|
--------------------------------->
Figure 17:10. Syntax Graph -: Placing a Gadget Half-Way across the Form
17:10
12.0
Below are some examples of how you can use the syntax:
17.8
Syntax
Effect
AT 5 7.5
AT X 5.5
AT YMAX+1
AT XMIN.GAD1-2 YMAX.GAD2+1
17:11
12.0
17.8.1
17.8.2
17:12
12.0
18
Frames
Frames are very special and versatile gadgets that are crucial for form definition, layout, and
run-time behaviour. This section gives an overview of frame gadgets. Their definition and
details are discussed in Gadgets and their Attributes.
18.1
Types of Frame
There are five different types of frame, and each type has different constraints on what
gadgets it can contain and what it can be used for.
The five types of frame are normal, tabset, toolbar, Panel and Foldup Panel.
18.1.1
Normal Frames
A frame of type NORMAL is a container with a visible border that can contain any type of
gadget, including other frames. This simple frame displays its tag as a title to describe the
gadget group as a whole.
Normal frames can be nested and as an inner frame expands, an outer frame will also
expand to accommodate it.
The FRAME has a Radio Group property which operates on the set of RTOGGLE gadgets
(radio buttons) owned directly by it.
18.1.2
Tabset Frames
The TABSET frame type defines a container for a set of tabbed page frames. The container
has no visible border and no tagtext i.e. it has no displayed title.
Any normal frame defined directly within a TABSET frame will become a tabbed page
frame, with its tag text displayed in the tab; selecting a tab will bring the tabbed page frame
to the front, and hide the previously shown tabbed page frame. A page SHOWN event is
raised for the tabbed page frame whenever the user interactively selects a new page tab.
Handling this event allows the AppWare to modify the content of gadgets on the selected
page before it is displayed to the user.
Only one page can be visible at one time, and one page is always visible. To change the
visible page programmatically, you have to set the new page visible setting the current
page invisible has no effect. Its not possible to remove a tabbed-page from the set, but you
can deactivate it, so it is inaccessible to the user.
The tabbed page frame may contain any form gadgets including normal frame gadgets.
18:1
12.0
To create a multi-paged tabbed form, you can define a dialog or document form with a
single TABSET frame that contains a set of tabbed page frames, with each page containing
the gadgets for the desired page of the form.
The form will automatically resize to take account of the largest tabbed page frame. There is
an example showing the definition of tabbed frames in Complex Form Layout
You cannot nest TABSET frames.
18.1.3
Toolbar Frames
Main forms support the notion of user-defined toolbars (see Modules and Applications). You
can create these using toolbar frames.
The frame type TOOLBAR allows you to define formal toolbars that contain all the gadgets
within the frames scope.
A toolbar frame can contain only a subset of gadget types: BUTTON, TOGGLE, OPTION,
TEXT, COMBOBOX, SLIDER and NUMERICINPUT. It must have a name and can appear
only on main forms; moreover, a toolbar frame is the only kind of frame a main form can
contain.
The frame gadgets visibility attribute allows you to show and hide toolbars, as well as
individual gadgets within the toolbar.
Note that any gadgets belonging to a main form and defined outside of a formal toolbar
frame definition are interpreted as a default toolbar with the name Default.
18.1.4
PANEL Frames
This is like a Normal frame but with no enclosing box (by default). You can formally follow
the PANEL keyword by INDENT to get a 3D indented surround. The Panel also never
displays its tag text.
18.1.5
18:2
12.0
19
Purpose
FRAME
PARAGRAPH
BUTTON
COMBOBOX
LINE
NUMERIC INPUT
TOGGLE
OPTION
Has a pull-down list of options associated with it, from which the
user can choose one only.
LIST
Displays one or more lines of data from which the user can select
one or several. The list can be scrollable if there are too many
options to be displayed at once.
CONTAINER
DATABASE
SELECTOR
Used to display a list of database elements, from which the user can
choose.
TEXT
19:1
12.0
19.1
Gadget
Purpose
TEXTPANE
An area where the user can type and edit multiple lines of text, or
cut and paste text from elsewhere on the screen.
VIEW
SLIDER
RTOGGLE
name
{common properties}
{specific properties}
.mytext
at X... Y...
width 10 lines 3
For example:
paragraph
tag
tooltip
callback
anchor
docking
Specific properties: the order of these commands generally matters. See the syntax
graphs in the Software Customisation Reference Manual for definitive information about
order.
Most gadget properties can be changed using the members and methods supported by
each gadget object.
Note: You cannot change a gadgets geometry except for a VIEW gadget on a resizable
form.
The Software Customisation Reference Manual contains detailed information on the
methods available for editing each type of gadget. This chapter describes some of the most
frequently used editing operations.
19:2
12.0
19.2
Type
Purpose
type
STRING
Read only
name
STRING
Read only
The following methods are shared by all gadgets regardless of gadget type:
Method Name
Result
Purpose
subtype()
STRING
container( )
FMO
GADGET or FORM
19.3
19.3.1
19.3.2
SLIDER, PARAGRAPH, G3D and G2D do not display their tag at all and so it doesnt
affect the gadget size.
FRAME gadgets display their tag as an integral part of their surrounding box, and so
the tag contributes to the gadget width and height.
LIST, SELECTOR and TEXTPANE gadgets display their tag above the gadgets box. It
affects the gadget height but not the width.
OPTION, TEXT, TOGGLE and RADIO_BUTTON gadgets display their tag horizontally
next to the gadget glyph and/or value indication and it affects the width but not the
height.
BUTTON gadgets display their tag inside the button glyph whose width and height can
now be specified independently of the actual tag string.
ALPHA gadgets (not in FRAME gadgets) display their tag above the bounding box, so
it affects the gadget height but not the width. ALPHA views within FRAMES do not
display their tag at all, so it has no effect on the size.
19:3
12.0
The Tagwidth specifies the size of the gadgets tag field in grid width units including any
padding space, regardless of the actual tag string.
The actual tag may have more characters than the declared Tagwidth and still fit in the tag
field (typically a mostly-lower-case string of 10 characters occupies only about 60-70% of
the 10 X notional character spaces). Similarly the actual strings may have fewer characters
than the declared Tagwidth and the extra space will be blank padded.
If the tag width is not explicitly given then it is assumed to be the number of characters in the
tagtext string multiplied by the horizontal grid size (the notional character width for the font).
Note: You can specify the tag width without specifying any tagtext at definition time; you
can add this at run time.
The Tagwidth is not needed for gadgets with an explicit area specification (width and
height, lines or length). FRAME, LIST, SELECTOR, TEXTPANE and PARAGRAPH can
always force an explicit width (excluding any padding).
For example, the following PARAGRAPH gadget definition displays the given string, which
has 14 characters without lots of unused space:
paragraph .oneline text a single line!
19.3.3
width 9
Form layout syntax has been extended to allow a gadgets width and/or height to be
specified relative to the size of previously placed gadgets:
frame f1 |Frame 1| At Xmin.gad6 Ymax.gad8 + 0.6 ... Width.gad6 Height
The frames width is set to the width of gadget gad6 and its height is set to the height of the
last (most recently defined) gadget.
19.3.4
An Example Form
The following example illustrates the combination of gadget relative size and tag width.
19:4
12.0
width 20 is
width.text1
width.option1 height
5
. . .
exit
exit
The text field gadget text1 reserves 6 grid units for its tag and 20 grid units for its text
display, but the actual gadget length is greater than this because of padding space in the
strings and space for the visible box.
The option gadget option1 also reserves 6 grid units for its tag (though the tag text has 8
characters, rather than 6) and specifies the text display (including the drop-down glyph)
must be the same width as the text1s text display (excluding the tag).
The two gadgets have the same X co-ordinate and their display fields will align at their start,
because they have the same tag width, and at their end because they have the same
length.
The frame gadgets X co-ordinate is incremented with the desired tag width and so will also
align at its start with option1. The frames width is set to be the same as the width of option1
(excluding the tag) and so aligns with the end of option1 also.
All the gadgets have their anchor attributes set to allow the form to be resizable. When the
form is interactively resized the gadgets will adjust to maintain their initial alignments. The
frame will expand to take up any spare space on the form.
19:5
12.0
19.3.5
Gadget
TEXT, OPTION
Boxed
gadgets
optional tag above.
with
LIST,
SELECTOR,
TEXTPANE
Gadgets
that
never
display a tag: FRAME,
SLIDER, G2D, G3D,
GM3D, PARA.
TOGGLE RTOGGLE
The relative gadget extent settings define the data display part of the gadget being defined
in terms of the data display part of the specified or implied gadget.
19:6
12.0
For the three categories defined above, WIDTH.GAD and HEIGHT.GAD have the following
meanings:
19.4
Category
Gadget
TEXT, OPTION
Boxed
gadgets
optional tag above.
with
LIST,
SELECTOR,
TEXTPANE
Gadgets
that
never
display a tag: FRAME,
SLIDER, G2D, G3D,
GM3D, PARA.
TOGGLE RTOGGLE
19.4.1
19.4.2
If only one pixmap file is supplied, it will be used for all states.
AddPixmap Method
The AddPixmap method is the best way of setting or changing a gadget's associated
pixmaps.
AddPixmap( !pixmap is STRING )
!pixmap is a string holding the file pathname of the required .png file.
AddPixmap( !pixmap1 is STRING, !pixmap2 is STRING )
19:7
12.0
!pixmap1 corresponds to the Un-selected state of the gadget, and pixmap2 corresponds to
the Selected state. Specifying !pixmap1 as the null string ' ', will leave the current Selected
pixmap unchanged.
PARAGRAPH gadgets only have one pixmap which is represented by the .VAL member,
and so can be directly set or changed using !this.myPara.val = '<pixmap-pathname>.
Notes:
1. The PML function !!PML.GetPathname( '<myPixmap>.png') returns the required
pixmap pathname for pixmaps held in the standard PDMS Appware pixmap directories.
2. It is recommended that when you define the gadget you set its size to encompass the
largest pixmap which you will later add. Failure to do this may give rise to unexpected
behaviour.
3. Historically you could add a third pixmap which was used when the gadget was deactivated. This practice is no longer necessary as the gadget pixmapped is
automatically greyed-out on de-activation.
19.5
!!MyForm.List.Active = FALSE
You may re-activate a gadget by setting its active status to TRUE:
!!MyForm.List.Active = TRUE
When a gadget is de-activated, it has a greyed-out appearance and the user is prevented
from interacting with it.
19.5.1
!MyForm.setactive( TRUE )
!MyForm.setactive( FALSE )
If you want most of the gadgets on the form to be de-activated, you can de-activate all
gadgets on the form (and then activate the ones you want, individually) using the following
form methods:
SetGadgetsActive( false )
Greys-out all gadgets on the form and sets their active status to inactive i.e. their previous
active state is lost. Similarly
SetGadgetsActive( true )
19:8
12.0
19.6
!!myform.mygadget.visible = false
And to check a gadgets visibility:
!bool = !!myform.mygadget.visible
19.6.1
!!form.f1.visible = false
will cause the frame and all its gadgets to be hidden, but the query:
!value = !!form.b1.visible
will still return true.
19.7
!!MyForm.keyboardfocus = !!Myform.gadget
The keyboard focus can be moved at any time to one of the following types of gadget by
calling the setfocus() method on the target gadget:
TEXT
TEXTPANE
BUTTON
TOGGLE
RTOGGLE
SLIDER
LIST
SELECTOR
OPTION
ALPHA VIEW
19:9
12.0
For example:
!!MyForm.Textfield.Setfocus()
19.8
Refreshing Gadgets
In general, when a gadget is edited inside a PML callback function or macro the visual
change is delayed until the entire callback has been executed or interactive input is sought.
If you want to see the gadgets appearance change immediately it is edited, use the method
!Gadget.Refresh()
You can also use the command REFRESH to refresh all gadgets of all displayed forms.
Warning: Refreshing gadgets should be used sparingly, especially for View gadgets,
as it will cause their content to be regenerated.
19.9
Some gadgets do not support this property in all circumstances, e.g. gadgets showing
a pixmap.
Gadgets whose colour has not been set explicitly, may not have a colour with a known
colourname. In this case an error is raised.
19:10
12.0
20
Gadget Set
This chapter describes all the gadgets supported by PML. It covers their definition and use.
Note: In some of the examples the relative gadget positioning commands are indicated by
the AT keyword followed by three dots . . . See Relative Placement for details.
20.1
Examples
This section contains two examples, which illustrate much of the functionality described in
this manual.
20.1.1
20:1
12.0
This file layout1.pmlfrm, shown below, defines the form shown in Figure 20:1.:
Examples of different types of gadgets. Note that all the gadgets on the form are dummies:
there are no callbacks. The PATH commands control the layout: they are described in
Positioning Gadgets on a Defined Path.
-- PDMS Customisation User Guide
-- Form layout1 - Demonstrate simple form layout
setup form !!layout1 DIALOG
title 'Form !!Layout1'
-- defaults: path right, hdist 1.0, vdist 0.2
paragraph .Message text 'This form illustrates simple form
layout'
path down
frame .frame1 'Frame 1'
paragraph .Message1 text 'This frame contains toggle buttons'
toggle .Bold
'Bold
'
'Italic
'
path RIGHT
toggle .Italic
exit
frame .frame3 'Frame 3' at XMIN FORM
paragraph .Message3 width 20 lines 2
.Vertical 'Alignment'
add tag 'Top
exit
button .CANCEL CANCEL
button .OK at XMAX FORM-SIZE YMAX FORM-SIZE OK
exit
define method .layout1()
20:2
12.0
-- Constructor
!this.Message3.val = 'This frame contains a vertical group of
radio buttons'
endmethod
20.1.2
20:3
12.0
The file layout2.pmlfrm, shown below, defines the form shown in Figure 20:3.: The
tabbed pages of a complex form. Within the form definition the TABSET frame is defined
and directly contains a frame gadget for each tabbed page. Note its ANCHOR ALL setting
which maintains the distance between each edge of the frame and the corresponding edge
of the form, when the form is resized by the user. This allows the frame to grow and shrink
without overwriting gadgets outside of it.
Each tabbed page frame contains the definition of all the gadgets belonging to the page.
Note the use of the DOCK FILL setting which allows each edge of the tabbed page frame to
stick to the corresponding edge of the TABSET frame so that they grow and shrink in
unison.
Alternatively, when you define a TABSET FRAME, you can specify its DOCK or ANCHOR
attributes to determine its resize behaviour within your form.
For each tabbed-page frame within the TABSET, it is no longer necessary to specify any
DOCK or ANCHOR attribute settings, and any that are specified will be ignored. Each
tabbed-page frame will always fill the available space in its TABSET parent (it exhibits
DOCK FILL behaviour).
The gadget ANCHOR attribute is used extensively to allow resizable gadgets to expand in
specific directions and not others for. It is also used by non-resizable gadgets, e.g.
20:4
12.0
BUTTONs, to allow them to move with edges of their containers and so avoid being overlaid
by resizing gadgets.
Note also, the extensive use of form methods to provide the intelligence of the form as
gadget callbacks. In particular the method listCallback(!list is GADGET, !event
is STRING), which just reports details of select and unselect events on list fields, has the
standard form of an open callback, and is used as such on the list gadgets LI1 and LI2, i.e.
!this.Li1.callback = |!this.listCallback(|. Open callbacks are described in
PML Open Callbacks.
'Area
halign left
button .b3 |area| tooltip'calculate the area'
exit
frame .frame5 'Frame 5' at Xmax.frame4+2 Ymin.frame4
anchor All
paragraph .Message5 text 'This is a multi-choice
list gadget' wid 20
list .Li1 'Select some of these' anchor all
MULTIPLE width 20 height 10
20:5
12.0
20:6
12.0
values
and
hide
the
--Page 1
-- frame 4
-- option
!ColourArray[1]='Black'
!ColourArray[2]='White'
!ColourArray[3]='Red'
!ColourArray[4]='Green'
!ColourArray[5]='Blue'
!This.Colour.Dtext=!ColourArray
-- set callback - make paragraph the selected colour
!this.colour.callback = |!this.message4.background =
!this.colour.selection('dtext')|
-- frame 5
-- multi-choice list
do !i from 1 to 20
!Elements[!i] = 'list element $!i'
enddo
!This.Li1.Dtext= !Elements
-- set callbacks
!this.b1.callback =
|!this.printListSelections(!this.li1)|
!this.li1.callback = |!this.listCallback(|
-- frame 6
-- make Area read-only
!this.Area.seteditable( false )
!this.Width.val = 6.0
!this.Height.val = 3.5
!this.b3.callback = '!this.calcArea()'
--Page 2
-- frame 7
-- textpane - add data
20:7
12.0
Price'
!this.li2.setHeadings(!a)
-- set up dtext rows as array of array
!Row1[1] = 'Landrover'
!Row1[2] = 'RangeRover'
!Row1[3] = '62000'
!Row2[1] = 'Landrover'
!Row2[2] = 'Defender'
!Row2[3] = '23999'
!Row3[1] = 'Lancia'
!Row3[2] = 'Delta'
!Row3[3] = 'not for sale'
!Row4[1] = 'Fiat'
!Row4[2] = 'Tempra'
!Row4[3] = 'offers'
!Row5[1] = 'VW'
!Row5[2] = 'Golf GTi'
!Row5[3] = 'p.o.a.'
do !i from 1 to 5
!dtext[!i] = !Row$!i
enddo
!this.li2.setRows( !dtext )
-- Add data
20:8
12.0
do !i from 1 to !dtext.size()
!rtext[!i] = 'row $!i'
enddo
!this.li2.rtext = !rtext
-- set callbacks
!this.b2.callback =
|!this.printListSelection(!this.li2)|
!this.li2.callback = |!this.listCallback(|
endmethod
define method .listCallback(!list is GADGET, !event is
STRING)
-- open callback to report on list events
-- can be used for any list gadget
!n = !list.pickedField
!sel = !list.dtext[!n]
!name = !list.fullname()
$P $!event callback on field $!n<$!sel> for list $!name
endmethod
define method .calcArea()
-- calculate the area
!area = !this.width.val * !this.height.val
!this.Area.val = !area
endmethod
define method .printListSelection(!list is GADGET)
-- report single-selection list gadget selection
-- can be used for any single-choice list
!sel = !list.selection('Dtext')
!num = !list.val
!name = !list.fullname()
$P ----------------------------------------------$P selected field for list $!name
$P Field $!num: <$!sel>
$P ----------------------------------------------endmethod
define method .printListSelections(!list is GADGET)
20:9
12.0
20.2
Frame Gadgets
Frame gadgets provide visual grouping of gadgets on a form, and aid form layout.
The grouping is more than visual: a frame is a genuine container and manages the gadgets
it contains, so gadgets are positioned relative to the frames origin rather than the forms
origin.
When a container resizes it will adjust the layout of all its children, taking account of their
anchor or dock properties.
The frame gadgets properties visible and active will automatically apply to all of its
children, but will not overwrite their corresponding property values. So, for example, frame
.f1 contains button .b1 and f1 and b1 both have visible = true.
The command:
!!form.f1.visible = false
will cause the frame and all its contained gadgets to be hidden, but the query:
!value = !!form.b1.visible
will still return true.
20.2.1
Defining a Frame
You define a frame using a command sequence beginning with the command frame and
ending in exit.
All the gadgets defined after the frame command and before exit will be included in and
contained by the frame.
The following example defines Frame1 and its gadget collection. The frame sub-type
<frame-type> is either one of the supported types TABSET, TOOLBAR, PANEL,
FOLDUPPANEL, or omitted for a 'normal' or 'tabbed page frame'.
20:10
12.0
.Message1
text
'This
frame
contains
toggle
PATH DOWN
toggle .Bold 'Bold'
PATH RIGHT
toggle .Italic 'Italic'
toggle .Underline 'Underline'
exit
Frame Size
During form definition, once a frame is positioned on the form the origin remains fixed but
the frame automatically expands downwards and to the right to contain any gadgets added
to it. You cannot add gadgets to it at negative coordinates relative to the frames origin. You
can optionally declare a minimum size for the frame. For example:
20.2.2
The radio group action only applies to FRAME gadgets of type NORMAL, PANEL,
FOLDUPPANEL.
You can add RTOGGLE to a FRAME with the usual positioning and layout commands.
The FRAME has a value member, VAL, which is the index of currently selected RTOGGLE
for the radio group. You can use this to change the selected RTOGGLE.
Similarly, you change the value of the FRAME by setting the VAL member of any of the
groups RTOGGLEs to true.
20:11
12.0
Note that the FRAME group value may be set to zero, indicating that there is no selected
RTOGGLE. Similarly if the selected RTOGGLE value is set to false, then it becomes
deselected and the FRAME value will then be zero.
The default value for an RTOGGLE gadget is FALSE, and the default value for a FRAME
gadget is zero, i.e. no selected RTOGGLE.
Frame Callbacks
The FRAME gadget can have an assigned callback, which is executed when the radio
group selection is changed, i.e. whenever the user selects an unselected radio-toggle. As
there is only a SELECT action supported, it can be either a simple callback or an open
callback.
The form definition below is a simple TV and radio station selector, shown above.
= '!this.RGroupSelectionChanged( )'
20:12
12.0
= !Frame.RToggle(!index)
!value = !rTog.onValue
-- Do some application actions
if( !value eq 'radio' ) then
!value = !this.Radio.selection('dtext')
endif
!this.choice.val = !value
endmethod
The callback on the RADIO option gadget, detects if the Radio: RTOGGLE rad5 is current
and if so it deselects it leaving no current selection, and clears the Selection text field.
20:13
12.0
20.2.3
20.2.4
20:14
12.0
The default state is 'unfolded' and the EXPANDED property allows the user to initialise a
panel to unfolded (true) or folded (false).
When the panel expands or collapses, any gadgets which lie below the panel and between
(or partially between) the panel's horizontal limits will be moved down or up the form.
If the form's AutoScroll attribute is selected, then a scroll bar will automatically appear
whenever gadgets have been moved off the bottom of the form, so that the entire form is
always accessible.
20.3
CONTAINER Gadget
The Container gadget allows the hosting of an external Control, e.g. a PMLNet, control
inside a PML defined form. It allows the user to add an external .Net control, which may
raise events that can be handled by PML. In order to customise the context menus of the
.Net control, the Container may have a PML popup menu assigned to it. This is shown when
the .Net control raises a 'popup' event.
20:15
12.0
20.3.1
OnCheck - raised when the NET toggle is clicked. Callbacks keep the PML toggle and
the .NET toggle in step.
OnPopup - raised when the right mouse button is pressed while the cursor is over the
background of the control. Callbacks edit and show the PML popup menu.
20:16
12.0
OnColour raised when the user selects a colour from the standard Winforms colour
picker (as pictured), which is shown when the colour button is pressed. Callback prints
the RGB components of the selected colour.
The PML code to create and manage the form is given below. Purple is used to highlight the
PMLNet commands, the rest is just standard PML2. It includes rudimentary callback
methods to service the PMLNet controls events.
In your Appware you need an import command (to load the dll containing your
PMLNetControls)
import 'PMLNetTest'
This is usually done once only, probably in application start-up macro. You can have it in
your form, but you will then need to handle the error which will occur if the import is
executed more than once:
import 'PMLNetTest'
handle (1000,0)
-- PMLNetTest dll already loaded
endhandle
20:17
12.0
20:18
12.0
20.4
Paragraph Gadgets
Paragraph gadgets allow a text or a pixmap to be displayed on a form. This gadget has no
associated name-tag and no call-back command: it is passive so cannot be selected by the
user.
A paragraph gadget can contain text or a pixmap. Once it has been defined, a textual
paragraph cannot be changed to a pixmap paragraph or vice versa.
Paragraph gadgets support the DOCK and ANCHOR attributes.
20.4.1
20.4.2
paragraph .picture AT . . .
pixmap /filename
paragraph .picture AT . . .
pixmap /filename width 256 height 200
The pixmap may be changed at any time by assigning a new value to the .val member:
!!MyForm.picture.val = /newfilename
20:19
12.0
20.4.3
20.5
Button Gadgets
When the user presses a button gadget (control button) it will usually display a child form
or invoke a call-back - typically a PML Form method.
Buttons have a tag-name or pixmap which is shown within the button rectangle. The tag,
pixmap, call-back, and child form are all optional.
(See Gadgets that Support Pixmaps for more about pixmaps).
For example:
button .SubForm 'More . . .' FORM !!ChildForm
button .SubForm pixmap /filename FORM !!ChildForm
button .Calculate 'Calculate' CALLBACK
!this.CallbackFunction()
You can specify the width of the BUTTON independently of any tag text string it contains
using the WIDTH syntax. You can also define its height with the HEIGHT syntax, allowing
you to define textual BUTTONs taller than a single character in height.
For example:
20.5.1
20:20
12.0
20.5.2
The sub-type of any Button gadget can be queried using the it's Subtype method.
20.5.3
Purpose
OK
APPLY
CANCEL
20:21
12.0
Control Attribute
Purpose
RESET
HELP
The effect of OK and CANCEL on gadgets is more extensive if a form family is involved, as
described in Free Forms and Form Families.
Examples:
button .Ok AT . . . 'OK' CALLBACK '!!MyOkFunction()' OK
button .Apply 'Apply' CALLBACK '!!MyApplyFunction()'
APPLY
CANCEL
20.5.4
20.6
Toggle Gadgets
TOGGLE gadgets are used for independent on/off settings as opposed to a radio group. A
TOGGLE should be given a tag name or pixmap, which is displayed to the right of the
TOGGLE button.
In Figure 20:1.: Examples of different types of gadgets., the three TOGGLES are defined by
the following lines.
20:22
12.0
The value of a toggle gadget is set and used via the .val member which is a BOOLEAN
value:
20.7
RToggle Gadgets
The RTOGGLE gadget is very similar to the TOGGLE gadget, but is allowed only in
FRAMEs, where they operate together to form a set of radio buttons, only one of which can
be selected at any one time.
You can add RTOGGLE gadgets to a FRAME with the usual layout and positioning
commands. The RTOGGLE gadgets are implicitly numbered 1, 2, 3, n as they are added.
RToggle Callbacks
The RTOGGLE gadget can have an assigned callback, which is executed whenever its
selected status is changed. When the group selection is changed, by the user clicking an
unselected radio button, the current button (if any) is unselected and the new button is
selected. An open callback (PML function or form method) is necessary as the events
UNSELECT and SELECT need to be reported.
The PML code below shows a modification to our example form, which makes use of open
callbacks on the RTOGGLEs instead of a simple callback on the FRAME radio group. The
Constructor and the RgroupSelectionChanged methods are modified accordingly.
Note: The behaviour of the two versions is identical. Both mechanisms are equally valid,
and are provided to minimise the work required in replacing the RGROUP and
(deprecated) RADIO gadgets.
20:23
12.0
this.rad5.callback = '!this.RGroupSelectionChanged('
-- Radio choices
!this.rad5.setTooltip(|select your Radio option|)
!radio[1] = 'Q103'
!radio[2] = 'Hereward'
!radio[3] = 'Cambridge'
!radio[4] = 'ClassicFM'
!radio[5] = 'Caroline'
!this.Radio.dtext = !radio
!this.Radio.setTooltip(|change your Radio option|)
!this.Radio.callback = '!this.selectProgram(!this.rad5)'
-- set initial value
!this.rad2.val = true
!this.RGroupSelectionChanged( !this.rad2,'SELECT' )
endmethod
define method .RGroupSelectionChanged( !rtog is GADGET,
!event is STRING )
-- Service specified radio-toggle events
if( !event eq 'UNSELECT' ) then
-- Do some application actions
!this.choice.clear()
elseif( !event eq 'SELECT' ) then
!value = !rtog.onValue
-- Do some application actions
if( !value eq 'radio' ) then
!value = !this.Radio.selection('dtext')
endif
!this.choice.val = !value
endif
endmethod
Order of Event Generation
Events for the radio group FRAME and its radio-toggles happen in the following order, when
an RTOGGLE is selected:
UNSELECT on previously selected RTOGGLE (if any)
SELECT on new RTOGGLE
20:24
12.0
SELECT on FRAME
20.8
Combobox gadget's display text field is editable, just like a TEXT gadget.
The drop-down list is very similar to a Single Choice List Gadget and supports DTEXT,
RTEXT, ZEROSELECT and NORESELECT properties. The same methods are used to
manage the list's content. In just the same way it also allows UNSELECT and SELECT
events.
When the user presses the option gadget, the entire set of items is shown as a drop-down
list and the user can then select a new item by clicking the option required.
There is always a selected value unless the option list is empty.
You can use the Add methods to add a single new entry to be appended to OPTION
gadgets:
20.8.1
width 10
The current value in a textual option gadget is scrollable using the left- and right-arrow keys.
This means you can specify a gadget thats narrower than the options displayed in the dropdown list.
The OPTION gadget actually contains two parallel lists of the same length, the display
values (or Dtext ) and the replacement values (or Rtext). The list of display values must
be supplied by assigning an array of values to the gadget's Dtext member. This is the list of
choices displayed to the user.
In Figure 20:1.: Examples of different types of gadgets., Examples, the lines in the default
constructor method define the Colour option gadget values as follows:
!ColourArray[1]='Black'
!ColourArray[2]='White'
!ColourArray[3]='Red'
20:25
12.0
!ColourArray[4]='Green'
!ColourArray[5]='Blue'
!This.Layout2.Colour.Dtext=!ColourArray
Other examples:
20.8.2
callback '!!MyFunc()'
Combobox Gadgets
A COMBObox is a combination of an option gadget and text field. It can be defined by the
command
combo .Colour tagwid 5 'Colour' scroll 20 width 5
When the ComboBox is editable(default), with the drop-down list closed, the user can
search for a required option by typing the first few letters into the display field and clicking
the down-arrow. The list will open with the first matching option highlighted. This is useful for
large lists.
The display field is accessible to the user, who can edit the contents by typing or pasting text
into the field. If the user clicks ENTER while the gadget's text field has focus and has been
modified, a VALIDATE event is raised. You can trap this event by assigning a PML Open
callback to the gadget. This allows you to give meaning to the action of typing text into the
display field. The Open callback is necessary to differentiate the VALIDATE event from the
SELECT and UNSELECT events.
On receipt of the VALIDATE event, your callback method can retrieve the displayed text by
means of the DisplayText method and decide what action is associated. Additionally you
can assign a popup menu to the gadget, which gives the user the choice of several actions.
For example, you might append the current display text to the drop-down list as a new
choice, possibly ensuring that there are no duplicate entries. An assigned popup menu
could allows options to be removed from the drop-down list and the editable status of the
combobox to be toggled.
(PML example code is available as User Manual example Layout2.pmlfrm which can be
obtained from AVEVA's support web site.)
20.8.3
!CircleDtextsArray[1] = '/directory/circle/cenrad'
!CircleDtextsArray[2] = '/directory/circle/3pts'
!!MyForm.Circle.Dtext = !CircleArray
option .Circle1 AT . . .
width 256 height 128
callback '!!MyFunc()'
PIXMAP
option .Circle2 AT . . .
width 256 aspect 2.0
callback '!!MyFunc()'
PIXMAP
20:26
12.0
The replacement-texts, if needed, are set by assigning an array of values to the Rtext
member.
'Pick
three
points
on
the
!!MyForm.Circle.Rtext = !CircleArray
20.8.4
!!Layout2.Colour.select('Dtext','Orange')
!!MyForm.Circle.select('directory/circle/cenrad')
You can read the current selection using the selection() method. This will return the
replacement-text (or the display-text if replacement-texts were not defined):
!SelectedText = !This.List.Selection()
The .val member reads the index number of the currently selected value:
!ChosenNumber = !!Form.List.Val
The clear() method will discard both display- and replacement-text lists:
!!MyForm.Colours.clear()
20.9
Slider Gadgets
The SLIDER gadget allows you interactively to generate values in a specified range, at
specified steps. PML supports both vertical and horizontal SLIDERS.
An example SLIDER definition is:
Frame .fr2 |Slider 2| at xmin form ymax anchor All width 20
text .t2
wid 3 is REAL
slider .sl2
height 5
vertical
anchor All
exit
20:27
12.0
20.9.1
Event Callbacks
The SLIDER gadget responds to left-mouse SLIDER START, MOVE, and stop events at
which it executes the gadgets callback if one is defined.
We recommend you use an open-callback (PML function or form method) as it includes the
action START, MOVE or STOP. For example the form method .serviceSlider would
have the signature
20:28
12.0
ndp is REAL -
editable is BOOLEAN -
modified is BOOLEAN -
setRange
The NumericInput gadget supports SELECT and MODIFIED events, and users may provide
a callback method to service these events. Note that often no callback is required, and the
numeric input value is merely read and used by other gadgets of the form.
A SELECT event is raised whenever the user clicks ENTER while the numeric input display
field has focus. Typically this happens after the user has typed in a required value, but will
also apply if the user enters the field after modifying the values using the up/down arrows.
The callback can be a simple or an Open callback.
A MODIFIED event is raised for each modification of the displayed value using the up/down
arrows. Modified events are only reported if they are enabled and the user has provided an
Open callback, as this allows differentiation from the SELECT events. The default state is
modified events disabled.
20:29
12.0
width 10 height 15
MULTIPLE width 10
list
.Elements
'Elements
to
be
deleted'
'!this.Delete' MULTIPLE width 10 length 15
callback
As with the option gadget, the list gadget actually contains two parallel lists, of the same
length, one containing display values (Dtext) and the other containing replacement values
(Rtext).
The Dtext values must be supplied, but the replacement values are optional.
If you dont supply the Rtext values they will default to the Dtext values. The Rtext values
are often used to assign callback strings to each field of the list.
Resetting a list gadget's display-texts automatically deletes the existing display and
replacement-texts and clears any current selections. For example, the contents of gadget
List of Figure 20:3.: The tabbed pages of a complex form could be replaced by:
!Choices[ 1 ] = 'Tea'
!Choices[ 2 ] = 'Coffee'
!Choices[ 3 ] = 'Chocolate'
!This.List.Dtext = !Choices
You can replace the lists Rtext with a new array of the same length without disturbing the
current Dtexts:
!newRtext[1] = drink6
!newRtext[2] = drink4
!newRtext[3] = drink12
!This.List.Rtext = !newRtext
You can use the new Add methods to add a single new entry to be appended to LIST and
SELECTOR gadgets:
20:30
12.0
list .List
length 10
|Cars|
Anchor all
single zerosel
width 25
The val member now allows programmatic de-selection of the current field.
For Single choice lists the keyword NORESELECT disables UnSelect and Select events
when the currently selected field is clicked with the mouse, for example:
!Xarray = !This.List.selection()
replacement texts by default
returns
!Xarray
=
!This.List.selection(Dtext)
selected display texts
selected
returns
To read the index numbers of the currently selected fields of a multi-choice list gadget:
20:31
12.0
!ChosenNumbersArray = !!Form.List.Val
You can read back the current Dtexts or Rtexts as an array or simply as strings:
!This.List.select(Rtext, Tea)
!This.List.select(Dtext, !array)
Callbacks on Multi-Choice List Gadgets
At PDMS11.6 we introduced support for Extended Selection Mode for multi-selection lists,
whereby CTRL and SHFT keys can qualify the list selection. As a result a whole set of
UNSELECT events followed by a whole set of SELECT events can result from a single
(atomic) user action. These events are presented in sequence, but AppWare cannot tell
where the sequence starts or ends.
At PDMS11.6, problems may arise if a multi-selection list is programmatically changed
during the list callback. Modifying the list content or its selection during the sequence can
cause unexpected results or even software crashes.
At PDMS11.6Sp1 we nave introduced new START and STOP batch actions to bracket the
sequence of UNSELECT and SELECT event actions.
For maximum upwards compatibility, the START and STOP batch actions are only notified
to PML if the user has assigned an open callback, since this is the only way that different
event types (actions) can be differentiated.
AppWare which used simple callbacks and worked at PDMS11.6 will continue to work
because START and STOP events will not be notified to it.
AppWare which used open callbacks and worked at PDMS11.6 will continue to work if the
SELECT and UNSELECT meta-events were explicitly differentiated in the callback, as
shown below, because the new START and STOP events will be ignored
!list
is
GADGET,
. . .
if( !action eq SELECT ) then
. . .
elseif( !action eq UNSELECT then
. . .
endif
. . .
endmethod
20:32
12.0
If you experience a change in behaviour, then you may need to rewrite your callback to
explicitly handle the new START and STOP events and modify the list content or its
selection only on receiving the STOP event action.
For newly written AppWare we strongly advise you use open callbacks to service multiselection list gadgets.
The number of columns is deduced from the List's data. If the user specifies a set of (1 or
more) column headings before the list is populated, then this will determine the number of
columns. If no headings are pre-specified then the number of columns is deduced from the
display text of the List's first row. This provides upwards compatibility for existing Appware
using single column lists.
A List gadget's headings can be replaced after the list has been populated. If the new
headings specify the same number of columns then the headings are replaced but the List's
data fields and selection remain unchanged. If the number of columns is different, then the
list is replaced by an empty list with the new headings. Invoking the Clear() method will clear
the list's data fields and rebuild the current headings.
The column headings, and hence the number of columns, for the list are assigned as an
array of STRING:
!This.List.SetHeadings( !headings )
The Dtexts for each column of each row of the list can be provided as a PML array, where
each element is an array of STRING. This can be row oriented or column oriented.
!This.List.SetRows( !rows )
20:33
12.0
!rows is an array of row arrays, and its size determines the number of rows in the list.
!This.List.SetColumns( !columns )
!columns is an array of column arrays, and its size must match the number of columns of
the list. The size of all the column arrays must be the same and determines the no of rows in
the list.
The default width of each column is determined by the longest header or Dtext value
supplied. Rtext values for each row are supplied as for single column lists.
Selection within the list applies to the entire row not individual cells, but rows of the list can
be selected on the basis of a columns Dtext:
Supplying Dtext to populate the list or invoke selections will require tab
separated strings as input.
20:34
12.0
selector .Mem AT . . .
selector .Mem
AT . . .
!element = !This.Members.selection()
Note that for a selector gadget, the Rtext and Dtext are always the same as one another.
'Active:'
callback
'!!MyFunction'
is BORE
width
10
is
format !!FormatBore
Specify the WIDTH, which determines the maximum number of character spaces
visible. Optionally you may specify a larger SCROLL width, which is the maximum
number of characters that the gadget can hold, and scroll through. The default scrollwidth is 132 characters. The maximum is 256 characters.
Specify a data type using IS which determines how a value assigned to the gadget will
be displayed and how a value typed by the user will be interpreted by your PML code.
You may also supply a FORMAT object which specifies in detail how a value is to be
displayed or interpreted (see below).
20:35
12.0
Specify a NOECHO keyword that indicates any characters typed should be displayed
as stars: a typical use would be a gadget for the user to enter a password.
Give the text gadget an initial value, which will be the value accessed by your PML
code if it is not modified by the user. To set an initial value for a text input gadget, use its
.val member:
This.name.val = 'PIPE-1'
When a field is actioned its content is read and validated according to the fields type (see
Validating Input to Text Fields). If an error is detected, then the fields background colour
changes to gold to indicate an error and focus is returned to it so that the user can correct
the error. If no error was detected then the text gadgets callback (if any) is executed
To get the current value of a text input gadget:
!Value = !This.name.val
The data type of the variable !Value will be the same as the type of the text gadget. To get
the textual value use:
!string = !Value.String()
To set the keyboard focus so that keystrokes come to this gadget:
!This.name.SetFocus()
20:36
12.0
Note: MODIFIED events are not notified to PML unless the field is editable, modified
events are enabled and an open callback has been specified (so that MODIFIED and
SELECT events cannot be differentiated)
Typically, the first MODIFIED event is used to allow AppWare to gain control and modify the
properties (e.g. ACTIVE status) of dependent gadgets, which possibly the user should not
have access to until the text fields VALIDATE and SELECT events have been handled.
The code fragment below defines an RTOGGLE that allows a user specified TV program to
be typed into an associated TEXT gadget.
rToggle .rad6 tagwid 7 |TV:|
text .TV
width 10 is STRING
!this.rad6.callback = '!this.RGroupSelectionChanged('
-- set open callback on text field and option list
!this.TV.callback = '!this.selectProgram('
!this.Radio.callback = '!this.selectProgram('
- handle first Modified event only
!this.TV.setModified( handleModify, 1 )
The extended (open) callback selectProgram(, shown below, intelligently handles the
TEXT gadget and OPTION list. The open callback RGroupSelectionChanged sets the
value of the TV RTOGGLE from the TEXT gadget.
of
R-toggle
according
to
!rtog = !this.rad6
if( !event eq 'MODIFIED'
) then
-- deactivate R-toggle
!rtog.active = false
elseif( !event eq 'SELECT' ) then
-- reactivate R-toggle
!rtog.active = true
endif
else
-- select radio program from option list
!rtog = !this.rad5
endif
20:37
12.0
Use the mouse Select button to copy and the mouse Adjust button to paste.
Note that user defined Popup menus are not supported on Text gadgets because they clash
with the system-supplied Edit menu.
20:38
12.0
The FORMAT object for a specific data type is normally a global PML Variable used for all
text gadgets of that type.
For example, you can create the FORMAT object for a REAL object, so that a distance is
displayed in feet-and-inches with fractions (rather than decimals) with a maximum
denominator of 4:
!!RealFMT = object
FORMAT()
!!RealFMT.DIMENSION =
!!RealFMT.UNITS =
'L'
'FINCH'
!!RealFMT.FRACTION = TRUE
!!RealFMT.DENOMINATOR = 4
!!RealFMT.ZEROS =
'FALSE'
See the PDMS Software Customisation Reference Manual for more information on the
FORMAT object.
The TEXT gadget is created with type REAL and assigned a FORMAT object for converting
values to and from text:
text
.Dist
!!RealFMT
'Distance:'
width 10
is REAL
format
!!Form.Dist.val = 3505.2
The display-text of 11' 6 will be shown in the text gadget.
To switch from displaying feet-and-inches to displaying mm all that is necessary is to
change the setting of the units member of the format object RealFMT from FINCH to MM.
The associated format also determines what can be typed into the field. If the associated
format is FINCH or MM then you can enter distances in either inches or feet and inches.
For example:
Chosen Units
Typed-in Value
Displayed Value
FINCH
138
11' 6
INCH
138
138
INCH
11' 6
138
MM
3505.2
3505.2
Note that in every case the text field value is actually held in millimetres so that the
command:
q var !!Form.Dist.val
prints
3505.2.
You can use the format object in your PML code when converting a value to a STRING:
!StringValue = !!Form.Dist.val.String(!!RealFMT)
20:39
12.0
!Ths.MyTextclear()
You can also assign an unset value to a text field:
!textField.validateCall = !this.textvalidate(
the corresponding method must be:
20:40
12.0
-- $Header: /dev/eventlayer/PMLLIB/validform.pmlfrm
17/09/04 13:37 Robin.langridge $
20:41
12.0
handle ANY
-- Example: can get errors from evaluation of the String
field
-- with an 'unset'value
return error 1 'Invalid input'
endhandle
-- Validation code -------------------------------------- Check for unset
unexpected errors)
field
first
(to
avoid
possible
20:42
12.0
Endmethod
If the values type fails to match the field type, a trappable error is raised.
If the types match, the value is validated and the VALIDATECALL is run, if there is one.
If the value is invalid then a trappable error is raised. If the field is shown then it is set 'in
error', and the focus is returned to it so that the user can edit the value.
If the value is valid then the method executes the field callback if !doCallback is true.
textpane
.text
'Text:'
AT . . .
width 10
textpane
.text
'Text:'
AT . . . height 20
height 20
aspect 0.5
The value of a textpane is its current contents as an array of strings, where each element of
the array corresponds to a line of text in the gadget.
To make the textpanes contents read-only, use the seteditable method:
!This.text.seteditable(FALSE)
To clear the current contents of a textpane gadget:
!This.text.clear()
To move the keyboard focus to this textpane gadget:
!This.text.setfocus()
To enquire how many lines of text a textpane gadget currently contains, use the count
method:
!Nlines = !This.text.count
To set line 21 in a textpane gadget to 'Hello World':
!Line = !This.text.line(21)
To set the entire contents of the textpane gadget to an array of strings:
20:43
12.0
!LinesArray = !This.text.val
To set the current cursor position within a textpane gadget to line 21, character 15 you can
use:
!This.text.setcurpos(21, 15)
or
!ArrayRowColumn[1] = 21
!ArrayRowColumn[2] = 15
!This.text.SetCurPos( !ArrayRowColumn )
To read current cursor position within a textpane gadget:
!ArrayRowColumn = !This.text.CurPos()
20:44
12.0
ALPHA views for displaying text output and/or allowing command input.
view .MyView AT . . .
contents attributes
viewtype
height 10 width 20
Where the contents and attributes that depend on the type of View.
You do not have to set any attributes and we recommend that you set the views attributes in
the forms default constructor method rather than in the form setup definition.
20:45
12.0
!MyForm.MyView.setpopup( !MyForm.pop1 )
Note: The standard graphical view forms in AVEVA products are normally supplied with
fully customised popup menus, so you should not assign your own ones to these
views
channel REQUESTS
!This.InputOutput.SetFocus()
To clear the text output region of an alpha view use:
!This.InputOutput.clear()
It is not an error to have no associated channels when an alpha gadget is created.
Removing the Requests IO-channel from Alpha View Gadget
To delete the requests channel from an alpha view gadget us the command:
20:46
12.0
!alpha.removeRequests()
This command will also dissociate the view from the current PDMS Requests IO-channel if
necessary.
Thus request output will no longer go to this alpha view.
The method is valid only on a form that has been mapped to the screen, and will cause an
error (61, 605) if used during form definition.
Note that you can add the channel back again by assigning to the gadgets .channel
member:
!alpha.channel = Requests
This adds the requests channel to the gadget, and causes it to become the current PDMS
requests region, if the form is mapped, and displaces any previous association.
When the user clicks on an alpha view it will automatically associate itself with the PDMS
requests channel or the commands channels, or both, according to its current channel
settings.
The Alpha view gadget supports multiple line copy and paste of text. The simple command
window form below shows the appearance.
The bottom-most empty blank line is the command input line, where the user can type in,
and execute the next command by pressing the 'enter' key.
The Alpha view allows the following operations:
1. Double-click of any displayed line will copy that line to the command input line ready for
editing and execution.
2. You can use standard techniques to select text in the view and then use the rightmouse menu to copy your selection.
3. You can use the right-mouse menu to paste a selection into the command line. Each
line of the pasted selection is executed in turn as if you had typed it in, except the last
line, which becomes the command input line ready for editing and execution.
4. You can use the right-mouse menu to paste a selection as a macro. The set of pasted
lines are captured to a temporary file which is then executed as PML macro. The
selected lines are not echoed into the view's display.
5. Pasting of multiple lines is aborted if an error alert is displayed.
6. You can drag and drop a PML file into the view, and it will then execute as a PML
macro.
20:47
12.0
7. The right mouse menu also allows you to clear the contents of the view and to change
the size of text in the view's display.
For a full list of View members and methods, see the Software Customisation Reference
Manual.
View Borders
Graphical views have optional sliders or scrollbars which allow modification of the views
geometric transformation. They can be turned on or off using the .borders( !Boolean)
method. If the border is switched off, the actual display area expands to fill the available
space.
Aspect Ratio and View Gadgets
Note when specifying the aspect ratio for a view that the corresponding ratios for ISO
drawing sheet sizes are 0.7071 for portrait format and 1.414 for landscape format.
View Colours
Graphical views have two colours associated with them, namely the background colour
and the highlight colour.
Setting Background and Highlight Colours
These can be set using the Background and Highlight members, either as integer colour
indices or as colourname text strings:
!view.Background = black
!view.Background = 2
The highlight colour is the colour used to highlight selected items in the view. You can set it
by
!view.Highlight = white
!view.Highlight = 1
Some view subtypes ignore this attribute.
20:48
12.0
!colourname = !view.Background()
Would give us the string black, and
!colourindex = !view.background
Would give us the integer colour index value 2.
Cursor Types
When the mouse cursor enters a graphical view, the View gadget determines what cursor
type should be displayed for the user, or the representation to be assumed during a
graphical interaction.
Some of he types of cursor available are:
POINTER
NOCURSOR
PICK
PICKPLUS
CROSSHAIR
Note: The initial cursor type for VOLUME views is a Pointer, and you cannot re-define this.
You can specify the initial setting for the cursor type in a 2D view when you define the view
gadget.
The default values for initial cursor types for PDMS 2D view types are:
2D View Gadget
Cursor Type
PLOT
CROSSHAIR
DRAFT
CROSSHAIR
COMPARATOR
POINTER
You may use the CURSortype attribute command to set an initial cursor type within the
gadget definition. For example:
20:49
12.0
Cursor
POINTER
AT . . .
..
exit
define method !!MyForm( )
-- form constructor method
...
-- Initialise plot view gadget
!this.diagram.borders = false
!this.diagram.background( darkslate )
!this.diagram.add( plot1-1 )
...
endmethod
The area occupied by borders and scroll bars is within the area you specify for the gadget If
the border is switched off, the actual display area expands to fill the available space.
When borders are turned off, the PLOT view becomes fixed and you cannot zoom or pan
within it. This may be useful if you are using PLOT views as if they were paragraphs; for
example, as decoration or for information.
To define the contents of the view specify the PLOTFILE pathname with the .add()
method.
To remove a PLOT from the current PLOT view, use the .clear() method. This will leave
the view empty. If you want to replace the displayed PLOT with another one then just re-use
the .add() method with the new PLOT file.
20:50
12.0
exit
define method !!MyForm( )
-- form constructor method
...
-- Initialise AREA view gadget
!this.drawing.borders = true
!this.drawing.background( beige )
!this.drawing.highlight( red )
...
endmethod
The area occupied by borders and scroll bars is within the size you specify for the gadget.
To define the colour items are displayed in when picked with the left-hand mouse button,
use the .sethighlight() method with one of the standard DRAFT colour numbers or
colournames.
The contents of the view may be any valid 2D graphical element, such as a DRAFT SHEET,
VIEW, LIBRARY, etc. To define the contents of the view, use the put command thus:
Command
Effect
put CE
put /SITE1/SHEET
If you put new content into an area view that already contains a drawing, the original content
will be replaced by the new, not added to it.
Note: There is currently no .put() method! So to replace the view contents you will need
to use the old PML1 syntax:
edit view !!MyForm.drawing AREA put /site/sheet2
The maximum limits for the drawing, such that only part of the specified content is drawn
(giving, in effect, a window on the overall content area), may be set using the limits
keyword as above or using the .limits() member in the forms constructor method, as
follows:
!box[1] = 200
!box[2] = 100
!box[3] = 600
!box[4] = 500
!this.drawing.limits = !box
where the limits define two opposite corners of the required area in sheet co-ordinates.
For a full list of VIEW members and methods, see the Software Customisation Reference
Manual.
20:51
12.0
20:52
12.0
The view direction is controlled via the 3D views .direction member. This is a 3 element
array of REAL, representing a direction vector in model space ( DEAST, DNORTH, DUP ).
So to look east you would specify:
!dir[1] = 1.0
!dir[2] = 0.0
!dir[3] = 0.0
!this.model.direction = !dir
The 3D view gadget labels the view direction information in 'ENU' (East-West, North-South,
Up-Down) format within the view status line and scroll borders, by default. The
LABELSTYLE member allows the user to specify either 'ENU' or 'XYZ' labelling. In the 'XYZ'
scheme the form of the view direction display in the status line is
x<bearing>y<elevation>z, where the bearing angle is in the range [-180, 180] and the
elevation angle is in the range [-90, 90]. The corresponding scroll bars are labelled "-Y -X Y
X -Y" and "-Z 0 Z".
The centre of interest or through point of the 3D view is controlled via the .through
member. This is a 3 element array of REAL, representing a position in model space (EAST,
NORTH, UP).
!thru[1] = 1000.0
!thru[2] = 5600.5
!thru[3] = 500.0
!this.model.through = !thru
The .radius() method specifies the radius of the view in current units - an imaginary
sphere around the through point.
The .range() method specifies how far away from the Through point you want the Eye
point to be.
To enable WALKTHROUGH mode specify .walkthrough(true) together with the
walking step size in current units using the .step() method.
For a full list of View members and methods, see the Software Customisation Reference
Manual.
Setting Aspects of the View Transformation by Syntax
The commands below allow aspects of the view transformation to be set using the old
syntax:
Effect
ELEVATION EAST
LOOK EAST
LOOK N45W75D
20:53
12.0
Modifier
Effect
Looks through
origin.
LOOK THROUGH ID @
LOOK FROM ID @
ISOMETRIC 3
Looks in
direction.
PLAN
named
specified
elements
isometric
View Limits
Each volume view needs to know what region of the design model is to be displayed (the 3D
view limits). The DESIGN application has the notion of a current limits box. Unless you
specify otherwise, the limits set for a new view gadget will default to the DESIGN current
limits box (as set by, say, the LIMITS or AUTOLIMITS commands in DESIGN).
In addition, each volume view has an AUTO flag associated with it, which determines
whether or not that view will be updated automatically whenever the DESIGN current limits
box is changed.
To define the view limits and the way in which they are to be updated, use the old syntax:
Effect
LIMITS AUTO
LIMITS LOCAL
20:54
12.0
Modifier
Effect
You can set the limits box for a view, subject to the current AUTO flag using the .limits()
method:
-- limits brick set as array of REAL [E1, E2, N1, N2, U1,
U2]
!brick[1] = 1000.0
...
!brick[6] = 1560.4
!this.model.limits( !brick )
In DESIGN you may change the current limits box at any time by using one of the command
formats
!!view.saveview( !n )
!!view.restoreview( !n )
Where 1 <= !n <= 4.
20:55
12.0
20:56
12.0
21
Alert Objects
The ALERT object provides a simple way of giving a warning or getting a response from the
user, avoiding the necessity of creating a separate form. Where an alert needs a simple
acknowledgement by the user, this can be achieved by pressing the spacebar or the Enter
key on the keyboard.
Note: Alerts are always blocking: they prevent interaction with any other form until they
have been actioned.
Below are some examples of how you can define different kinds of alerts:
Code
Effect
21:1
12.0
Code
Effect
!Answer = !!Alert.Question( OK to
format entire disc? )
21.0.1
Position of Alerts
By default, an Alert is automatically positioned when it is displayed so that it is under the
cursor. Optionally, two more arguments may be supplied to specify the screen position at
which an alert is to appear:
21.0.2
Input Alerts
This allows the user to obtain textual input from the operator via a blocking alert which
overrides all other interactive activities. The alert can be summoned by the alert methods:
Var !x read
which is only valid in tty mode.
To achieve flexible, user-friendly interfaces, the input alert should be used sparingly
because of its blocking nature.
21:2
12.0
22
22.1
.SetDefaultFormat(!!fmt is FORMAT)
where !!fmt must be a global variable.
After calling the method the value can later be retrieved by referencing the DefaultFormat()
method as follows:
!text=!myVar.String(!!fmsys.DefaultFormat())
22.2
!info = !!FMSYS.FMinfo()
where !info is an array of STRING, currently with seven entries:
Array Index
String
[0]
Modules name
[1]
Modules version
[2]
AppWare version
[3]
Not used
[4]
[5]
Not used
[6]
22:1
12.0
22.3
Method
Purpose
!form = !!FMSYS.main ()
!formsarray = !!FMSYS.forms()
!formsarray = !!FMSYS.shownForms()
Swapping Applications
You can swap to a new application using the SetMain method:
22.4
!!FMSYS.setInterrupt(!!fmstop.stopButton )
Then at an appropriate point in the callback, check to see if the stop button has been
pressed using the FMSYS Interrupt method, and exit with a message if it has:
...
22:2
12.0
width 20
exit
define method .fmstop()
--Constructor
-- define callbacks
!this.firstShownCall = |!this.stopButton.background =
'red'|
endmethod
22.5
!!FMSYS.Refresh()
22.6
!!FMSYS.CheckRefs( true )
Using this method can significantly improve performance.
22:3
12.0
22.7
Splash Screen
There are occasions when the PDMS entry screen, known as a splashscreen, may be left
on the screen when PDMS fails to start up, for example, if it has not been correctly installed,
or there are network problems.
To ensure that this screen is removed from the display, you can use the SplashScreen
method:
! !FMSYS.SplashScreen( false )
22.8
!!FMSYS.DocsAtMaxScreen( false )
This method may be useful for wide screen and twin screen devices
22.9
CurrentDocument() Method
This method returns the current Document of the application framework as a FORM object.
If there is no current document then the returned form has value Unset.
It has the following declaration:
!!FMSYS.currentDocument( ) is FORM
This method should be useful when constructing callbacks for menus or gadgets which are
not on the current document form, i.e. where the context is not the current form or view
gadget, e.g. callbacks from the Main menu system or from pullright menus on Addin forms.
!myDocument = !!FMSYS.currentDocument()
22:4
12.0
'User exit from submode' so that Appware can handle the effective 'quit', and then
raises error (61, 115): 'Invalid command when GRAPHICS OFF or no suitable
view available', which will alert the user unless trapped by the Appware. However, only
the Appware knows exactly which graphical views can handle the specific cursor command,
so the OKCurfnView methods have been provided to allow Appware to use this knowledge
intelligently.
Methods provide are:
Query whether graphical views of the specified view type are displayed. Graphical view
types supported are: 'G2D'; 'G3D'; 'ANY' and any view subtype is implied.
OKCurfnView( !viewtype is STRING ) is BOOLEAN
Query whether graphical views of the specified view type and subtype are displayed.
Graphical view types supported are: 'G2D'; 'G3D'; 'ANY'. View subtypes supported are:
'ANY' and for
G2D: 'NORMAL' (Draft); 'PLOT'; 'ISOSPOOL'
G3D: 'NORMAL' (Design)
OKCurfnView( !viewtype is STRING, subtype is STRING ) is BOOLEAN
Example using FMSYS OKCurfnView methods:
The following code snippet shows an example of use within a form method:
22:5
12.0
22:6
12.0
23
PML Add-ins
A PML add-in can:
It is important to distinguish between an add-in that defines an application and one which
just modifies an existing application. An application add-in must have an entry in the
Applications main menu so that it can be switched to.
This chapter describes mechanisms to allow a number of cooperating applications to be
added into a module. Each AppWare application is a PML Add-in. Users can add their own
applications or add additional functionality to applications using PML Add-ins.
Warning: These mechanisms are still being developed and so may change at future
releases of PDMS. Even though we will make every effort to preserve the validity of addedin functional code, we recommend you isolate the definition of PML Add-ins from the
functional code of your applications to minimise the impact of any such changes.
It is important to distinguish between an add-in that defines an application and other addins. An application appears on the Applications menu and can be switched to, whereas an
add-in simply provides a means of adding functionality to a module.
23.1
Application Switching
The application switching mechanism makes use of the following objects to control
applications, toolbars, forms, and menus.
Object
Description
appCntrl
appDefn
appTbarCntrl
23:1
12.0
Object
Description
appMenuCntrl
appFormCntrl
23.1.1
Main Form
The main form is now stored as a PML Form.
For DESIGN, this is !!appDesMain and for DRAFT it is !!appDraMain. These contain the
basic gadgets and menu-options common to all applications in the module. All others are
created using add-ins.
We recommend that you create add-ins to modify the main form rather than editing the
standard product code.
23.1.2
Callbacks
Each application can now have different types of callback, which are run at different times:
23.1.3
Callback
When Executed
startup calls
switch calls
Defining an Add-in
Each add-in is specified by an add-in definition file, with pathname
%PDMSUI%\<module>\ADDINS\<addin>, where <module> is the module name e.g.
DES, and <addin> is the addin name e.g. MYADDIN. This is similar in format to the entries
in the old APPLICATIONS file. Each line contains a key and description, separated by a
colon.
Some keys are available to all add-ins; others can be used only for applications, since they
refer to menu text, gadgets and callbacks specific to that application.
23:2
12.0
Description
Name
The unique identifier of the add-in, e.g. GEN, DES. This key
is compulsory.
Title
ShowOnMenu
Object
ModuleStartup
StartupModify
SwitchModify
The following keys are only used for applications, i.e. add-ins that have a menu option on
the Applications menu and can be switched to.
Key
Description
Menu
Directory
Name
of
the
%PDMSUI%\module
Synonym
Group
GroupName
23:3
application
directory
under
12.0
Key
Description
Gadgets
Type
SwitchCall
StartupCall
To make it easier for user-defined add-ins to modify existing applications, it is possible for an
add-in to add a startup or switch call to an application. You can do this by adding the lines of
the following form to the add-in file.
23.1.4
Add-in Object
A user-defined object type, the add-in object, is used to define toolbars and menus for an
add-in. An instance of this object is created and its methods are run at specific points.
The method .modifyForm() of each add-in object is called during definition of the main
form so that add-ins may create their own toolbars on the main form; .modifyMenus() is
called when starting PDMS to create menu options.
For applications, .initialiseForm() is called when switching to the application.
It is not mandatory for all these methods to be present, and if one is not present, execution
continues as normal. It is possible to specify no object if you wish only to use the other
properties of the add-in.
23.1.5
Initialisation
The add-in definitions are loaded from file during PDMS startup using the function
!!appReadAppDefns(!module is STRING), where !module is the 3-letter identifier for
the module.
This creates the global !!appCntrl object, which holds information about the add-ins.
After reading the definitions of all add-ins, !!appCntrl assigns all callbacks to the
applications.
For backward compatibility, if an add-in is an application, a directory is specified for the
application and the file module\directory\INIT exists, then this file is added as a
23:4
12.0
23.2
Menus
Each add-in is able to specify menus that can be shown, along with the application in which
they can be shown. Menus are created by running the .modifyMenus() method of the
add-in object when PDMS first starts.
23.2.1
APPMENU Object
The APPMENU object provides a means of adding menu fields to menus using the usual
PML 2 syntax while allowing the menu controlling object to retain information about the
fields created. This information is used to show and hide menu fields depending on the
application.
23.2.2
'<FieldType>',
!menu.insertAfter('<TargetFieldName>',
'<Dtext>', '<Rtext>' {, '<FieldName>'})
'<FieldType>',
23:5
12.0
!menu.insertBefore('<TargetFieldName>',
'<FieldName>'})
!menu.insertAfter('<TargetFieldName>',
'<FieldName>'}).
'SEPARATOR'
'SEPARATOR'
{,
{,
!appMenuCntrl.addMenu('APP', !menu)
It is possible to add menu fields from an APPMENU object to every application. The
application name 'ALL' has been reserved for this purpose.
23.2.3
23.2.4
Description
!bmenu.insertBefore('<TargetMenuName>', '<Dt
ext>', '<MenuName>')
To insert menus
existing menus, use
relative
to
!bmenu.insertAfter('<TargetMenuName>', '<Dtex
t>', '<MenuName>')
23:6
12.0
23.3
Command
Description
!bmenu.remove(!menuName is STRING
!!appMenuCntrl.addBarMenu('APP', !bmenu)
Toolbars
Toolbars are defined by creating a frame of type toolbar on the main form, using the
following syntax.
23.3.1
Toolbar Control
Toolbar visibility in different applications is controlled by the object APPTBARCNTRL. A
global instance !!appTbarCntrl is created when starting PDMS.
Each toolbar is registered with a list of applications in which it can be active. !!appTbarCntrl
then controls the active state and visibility of the toolbar as applications are switched.
Toolbars can be shown and hidden using the popup menu which appears when you right
click on a toolbar or menu bar of the main form. If a toolbar is shown in one application, then
it will be shown in all other applications for which it is registered.
To register a toolbar, use the command
!!appTbarCntrl.addToolbar(!toolbarName is STRING, !appName is
STRING, !visible is BOOLEAN),
where !toolbarName is the name of the toolbar frame, !appName is the name of the
application to register the toolbar in and !visible is whether the toolbar is visible by
default.
Instead of registering a toolbar in multiple applications individually, an array of applications
can be specified:
!!appTbarCntrl.addToolbar(!toolbarName is STRING, !appNames is
ARRAY, !visible is BOOLEAN)
registers the toolbar for all applications in the array !appNames.
The application name 'ALL' has been reserved so that toolbars can be registered in every
application. To register a toolbar in all applications, use
!!appTbarCntrl.addToolbar(!toolbarName
!visible is BOOLEAN)
23:7
is
STRING,
'ALL',
12.0
where !toolbarName is the name of the toolbar and !visible is whether it is visible by
default.
The command
!!appTbarCntrl.addToolbar(!toolbarName is STRING)
is a shorthand way of registering the toolbar to be visible in all applications, making it
visible by default.
Any toolbars on the main form that are not registered are automatically detected and hidden
by default. They are registered to be active in all applications.
23.3.2
is
STRING,
!appName
is
where !gadgets is a space-separated list of the gadgets to hide and !appName is the
name of the application in which to hide them.
23.3.3
is
STRING,
!appName
where !gadgets is a space separated list of the gadgets to inactivate and !appName
is the name of the application in which to hide them.
23.4
Forms
The APPFORMCNTRL object allows control of the behaviour of forms on application
switching and re-entering PDMS. A global instance !!appFormCntrl is created when PDMS
starts.
If a form is to be controlled, it must register itself with the object during the constructor
method.
23.4.1
Registering a Form
The most complete syntax for registering a form is
!!appFormCntrl.registerForm(!form
ARRAY, !requiredApps is ARRAY,
!showOnSwitch is BOOLEAN)
is FORM, !visibleInApps is
!showOnStartup is BOOLEAN,
23:8
12.0
If !visibleInApps is an empty array, then the form will be visible in all applications.
However, this can be simplified for most forms that do not need all this information stored
about them.
The command:
!!appFormCntrl.registerForm(!form is FORM)
registers the form so that it is re-shown on startup if it is shown when PDMS exits. It
does not depend on any other applications and its visibility is not otherwise controlled.
23.4.2
!visibleInApps,
ARRAY(),
The form will be hidden when switching from the application 'APP' and will not be shown
next time the application is started.
23.4.3
23:9
12.0
ARRAY(),
ARRAY(),
true,
23.5
23.5.1
23:10
12.0
file. Exactly the same syntax can be used here, but if you wish alter any gadgets after they
have been created you must refer to the main form explicitly rather than using the keyword
!this, which will refer to the object.
Toolbars can be registered - see Toolbar Control - any unregistered toolbars will be hidden
by default and will not be controlled.
23.5.2
23.5.3
!convert.convert().
This creates the add-in object and corresponding add-in file. Information about the existing
application is stored in the %PDMSUI%\module\DFLTS\APPLICATIONS file. This
information is read and used to create the add-in file. Therefore, the application being
converted must have an entry in the APPLICATIONS file in the current PDMSUI path.
23.6
Example Application
23.6.1
23:11
12.0
The same method can be used to add fields to any menu descended from the bar menu of
the main form.
A sample add-in object definition, which must be put in the PMLLIB path, is shown below.
define object APPADDQUERYMENUFIELD
endobject
define method .modifyMenus()
!this.queryMenu()
endmethod
define method .queryMenu()
-- define APPMENU object associated with the Query menu
!menu = object APPMENU('sysQry')
-- add field to query the owner of the current element
!menu.add('CALLBACK', 'Query Owner', 'q owner', 'QueryOwner')
-- register the APPMENU object to be visible in the Pipework
-- application
!!appMenuCntrl.addMenu(!menu, 'PIPE')
endmethod
The corresponding add-in definition file, which is put in the DES\DFLTS\ADDINS directory,
contains:
Name: ADDQUERYMENUFIELD
Object: APPADDQUERYMENUFIELD
23.6.2
!menu.remove('CE')
Insert a new menu option to replace it
!menu.insertAfter('CE', 'CALLBACK', 'Delete',
'!!customDelete()', 'customDeleteCE')
Register the object with !!appMenuCntrl so the menu item is replaced in all applications.
!!appMenuCntrl.addMenu(!menu, 'ALL')
The callback of the Delete button on the main toolbar can also be modified.
!!appDesMain.mtbDeleteCe.callback = '!!customDelete()'
A sample add-in object definition, which must be put in the PMLLIB path, is shown below.
23:12
12.0
Name:CUSTOMDELETE
Object:APPCUSTOMDELETE
23:13
12.0
23:14
12.0
A.1
In general the PML form definition for a core managed form should not define the following
PML callbacks:
INITCALL
AUTOCALL
OKCALL
CANCECALL.
A:1
12.0
If OK, CANCEL or APPLY buttons are present they should also be declared as CORE
managed.
A.1.1
CORFRM ( int FORM, int WMID, int FTYPE, int LSTR, string
FORMNAME )
where:
WMID is the DRUID driver handle for the form. This will allow the core-code to provide
its own callbacks for the form, and to organise the forms core managed gadgets and
menus.
The following F&M routines will be useful for core code management of PML forms with
core managed gadgets:
A.2
Command
Effect
Hide
the
given
form.
If
CANCEL=true it means the form
was cancelled. Otherwise it was
OKed
BUTTON
TEXTIN
TOGGLE
A:2
12.0
FRAME
gadget tag
gadget position
Core managed gadget
When a form is first displayed its gadgets are created. If a gadget is declared as core
managed then F&M, builds the DRUID widget, sets its active, visible and selected states,
and then calls core code to allow it to take over the gadget callback.
Core code may plug in its own callback function to DRUID so that events on that gadget will
go directly to core code and will no longer go to F&M. Core code will then be responsible for
managing the gadgets state in conjunction with PML AppWare. For gadgets with no
callback, the core code may merely wish to read values from or write to the gadget. You
must exercise great care to avoid clashes between AppWare and software management.
TEXTPANE and PARAGRAPH gadgets do not have a callback.
A.2.1
CORGDT ( int GADGET, int WMID, int GTYPE, int LSTR, string
GDTNAME )
where:
WMID the DRUID driver handle for the gadget. This will allow the core-code to provide
its own callback for the gadget.
The element GADGET is owned by an F&M FORM element, so its owner (in this case the
form) element can be obtained from:
A:3
12.0
where:
A.3
The F&M menu fields are mapped to DRUID MenuItem widgets at the first UI_FILL_MENU
or UI_UPDATE_MENU event. At this point F&M, builds the DRUID menufield widget, sets its
active, visible and selected states, and then calls core code to allow it to take over the field.
Core code will plug in its own callback function into DRUID so that events on that field will go
directly to core code and will no longer go to F&M. Core code will now be responsible for
managing the menu fields state in conjunction with PML AppWare.
A.3.1
CORMNU ( int FLDELT, int WMID, int FTYPE, int LSTR, string
FLDNAME )
where:
WMID is the DRUID driver handle for the field. This will allow the core-code to provide
its own callback for the menu item.
FTYPE is the field type (0-4). These values correspond to the following types:
CALLBACK, TOGGLE, SEPARATOR, MENU, FORM.
FLDNAME is the core-code/AppWare agreed menu field name string of length LSTR
F&M will provide the following interface for core-code to manage the menufield state: Menu
field active, visible, checked.
FSTMFA( int FLDELT, logical ACTIVE, logical UPDATE=.TRUE. )
FSTMFV( int FLDELT, logical VISIBLE, logical UPDATE=.TRUE. )
FSTMFT( int FLDELT, logical CHECK, logical UPDATE=.TRUE. )
The last example will be ignored by non-toggle fields
Note that the FLDELT element is owned by an F&M MENU element, so its owning menu
and form elements can be obtained from:
FQUOWN( int FLDELT, int FORM, int OWNER )
A:4
12.0
A.3.2
A:5
12.0
A:6
12.0
Manipulating VIEWS
This Appendix tells you how to interact with the 2D and 3D View gadgets.
B.1
Manipulating 2D Views
This section describes the standard mechanisms provided by the Forms and Menus
software for manipulating a 2D view using the mouse buttons, number keypad and function
keys.
The mouse buttons are referred to as:
MB2 (adjust)
MB3 (popup)
If you press MB3 when the cursor is over the view canvas, the popup view menu is
displayed. This will either be the default menu supplied by the Forms and Menus
software, or an application-specific version if provided, either by AVEVA or by your own
customisation.
This section describes the default menu.
Move the cursor to a corner of an imaginary box enclosing the area of interest. Hold
down MB2, move the mouse to the diagonally opposite corner and release the button.
Click MB2 to zoom out from the cursor position by factor of 1.5.
Hold down Shift and click MB2 to zoom in on the cursor position by factor of 1.5.
B:1
12.0
Effect
4, 6, 8, 2
Page Up
Page Down
Home
The arrowheads move the bubble by approximately 1/100 of the bar per click.
Click in the slider trough to move the bubble by approximately 1/10 of the bar per click.
Hold down Ctrl and click in the slider trough to pan to the opposite side in one step.
B:2
12.0
!myview.SaveView(
!storeNumber )
!myview.RestoreView( !storeNumber )
where !StoreNumber must be in the range 1 to 4.
If the views picture is deleted or changed then the view-stores are all cleared. The stored
view settings should survive saving and restoring to a binary-save file.
B.2
Manipulating 3D Views
This section describes the standard mechanisms provided by the Forms and Menus
software for manipulating a 3D view using the mouse buttons, number keypad and function
keys.
The mouse buttons are referred to as:
MB2 (adjust)
MB3 (popup)
If you press MB3 when the cursor is over the view canvas, the popup view menu is
displayed. This will either be the default menu supplied by the Forms and Menus
software, or an application-specific version if provided, either by AVEVA or by your own
customisation.
n25w12d Pers<36
Model
Rotate Fast
Meaning
n25w12d
Pers<36
Model
B:3
12.0
Field
Meaning
Rotate
Fast
Popup Menu
Pressing MB3 displays the popup view menu when the cursor is over the view canvas.
This will either be the default menu supplied by the Forms and Menus software, or an
application-specific version if provided, either by AVEVA or by your own customisation.
This Section describes the default menu.
Menu option
Function Key
Zoom
F2
Pan
F3
Rotate
F5
Walk
F6
Eye
F7
Shade
F8
Borders
F9
Persp
F4 (F10)
Restore >
Rn
Alt n
(n is 1, 2, 3 or 4 on keypad)
Save >
Sn
Alt Ctrl n
(n is 1, 2, 3 or 4 on keypad)
Options >
The Zoom, Pan, Rotate and Walk options select a mouse mode.
The Options pullright toggles the states of Eye/Model, Shaded/Wireline, Borders On/Off,
and Perspective/Parallel.
B:4
12.0
These actions may also be achieved by pressing the relevant Function Button while the
view canvas has keyboard focus.
The Restore and Save pullrights allow up to four view settings to be saved and restored. To
save the current view settings, choose S1, S2, S3 or S4 from the Save menu. To restore a
previously saved setting, choose R1, R2, R3, or R4 from the Restore menu. These
functions are also available using Alt, Ctrl, and the keypad numerals 1 to 4 while the canvas
has keyboard focus.
Mouse Modes
Mode
Navigation
Zoom (F2).
Pan (F3).
Rotate (F5).
Walk (F6).
Eye (F7).
B:5
12.0
In all modes, clicking MB2 will shift the point under the cursor to the centre of the view. In
perspective, the from-point will be maintained, and the view direction and through-point will
be modified. In parallel, the view direction will be maintained, and the from- and throughpoints moved. The only exception to this rule is in Pan Mode, when the view direction is
never modified.
Manipulating 3D Views Using the Numeric Keypad
Important: Some keyboards have auto-repeat on keypads and function-buttons.
When the keyboard focus is in the view, the following functions are available.
7
Key
Effect
4, 6, 8, 2
7, 1
9, 3
Effect
F2
F3
B:6
12.0
F4
F5
F6
F7
F8
F9
Effect
Walk in or out.
Perspective
Shift
Parallel
Shift
Ctrl + Shift
Alt
Arrow Keys
Shift
Ctrl + Shift
Alt
B:7
12.0
Alt + Ctrl
Rotate by 10-step-distances.
Alt + Shift
4, 6, 8, 2
Ctrl
Shift
Ctrl&Shift
Alt
Alt + Ctrl
Rotate by 10-step-distances.
Alt + Shift
Ctrl
Shift
Ctrl + Shift
Walk in or out.
9, 3
Perspective
Parallel
Shift
Notes:
The eye mode can be selected by setting Walkmode On, and Model by setting
Walkmode Off.
If your keyboard focus is set by moving the cursor into a window without having to click
a mouse button, it is possible to get the state of the Ctrl and Shift keys confused. If
you move the mouse into the view, press Ctrl, press Adjust, move the mouse out of
the view, release Adjust, then release Ctrl, you will still be in Fast mode, as shown in
the status line. To remedy this, move the cursor back into the view, and then press and
release Ctrl.
B:8
12.0
Index
Symbols
!This notation . . . . . . . . . . . 2:10, 13:2, 13:3
$ character . . . . . . . . . . . . . . . . . . . . . . . 3:2
$M command . . . . . . . . . . . . . . . . . . . . . 7:1
Numerics
2D views:See Views . . . . . . . . . . . . . . 20:48
3D views:See Views . . . . . . . . . . . . . . 20:48
A
Abbreviations:dont use! . . . . . . . . . . . . . 3:2
Aborting forms . . . . . . . . . . . . . . . . . . . . 15:6
Absolute positioning . . . . . . . . . . . . . . . 17:8
ALERT object . . . . . . . . . . . . . . . . . . . . 21:1
Alert objects:position . . . . . . . . . . . . . . . 21:2
Alignment of gadgets . . . . . . . . . 17:2, 17:4
Alpha log . . . . . . . . . . . . . . . . . . . . . . . . 12:2
ALPHA views:See Views . . . . . . . . . . 20:45
Applications . . . . . . . . . . . . . . . . . 22:2, 22:3
Apply button . . . . . . . . . . . . . . . . . . . . 20:21
Area view gadget . . . . . . . . . . . . . . . . 20:50
Arguments . . . . . . . . . . . . . . . . . . . . . . . . 2:5
Arguments:type . . . . . . . . . . . . . . . . . . . . 2:8
Array methods . . . . . . . . . . . . . . . . . . . . . 6:2
Array variables . . . . . . . . . . . . . . . . . . . . 2:2
Array variables:evaluating ( only) . . . . . . 8:4
Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . 6:1
Arrays:appending to . . . . . . . . . . . . . . . . 6:3
Arrays:creating . . . . . . . . . . . . . . . . . . . . 6:1
Arrays:creating using VAR . . . . . . . . . . . 8:1
Arrays:deleting . . . . . . . . . . . . . . . . . . . . 6:3
Arrays:deleting elements . . . . . . . . . . . . 6:3
Arrays:delimiters . . . . . . . . . . . . . . . . . . 6:4
Arrays:elements . . . . . . . . . . . . . . . . . . . 6:1
Arrays:gaps in . . . . . . . . . . . . . . . . . . . . 6:1
Arrays:heterogeneous . . . . . . . . . . . . . . 6:1
Arrays:length of string elements . . . . . . . 6:4
Arrays:multidimensional . . . . . . . . . . . . . 6:2
Arrays:reading . . . . . . . . . . . . . . . . . . . 11:2
Arrays:referring to . . . . . . . . . . . . . . . . . 6:1
Arrays:sorting . . . . . . . . . . . . . . . . . . 6:4, 6:6
Arrays:sparse . . . . . . . . . . . . . . . . . . . . . 6:1
Arrays:splitting text into . . . . . . . . . . . . . 6:3
Arrays:subscripts . . . . . . . . . . . . . . . . . . 6:1
Arrays:subtotalling . . . . . . . . . . . . . . . . . 6:9
Arrays:writing . . . . . . . . . . . . . . . . . . . . 11:2
ARRAYWIDTH function . . . . . . . . . . . . . 6:4
Assignment . . . . . . . . . . . . . . . . . . . 4:3, 9:1
AT command . . . . . . . . . . . . . . . . . . . . 17:9
AT command:Gadget positioning . . . . . 17:2
Attributes . . . . . . . . . . . . . . . . . . . . . . . . 2:1
AUTOLIMITS command:Volume view 20:55
Auto-placement of gadgets . . . . . . . . . 17:3
B
Background colour . . . . . . . . . . . . . . . 20:48
Bar menus . . . . . . . . . . . . . . . . . . . . . . 16:2
Boolean expressions . . . . . . . . . . . . 5:1, 5:2
Boolean IF constructs . . . . . . . . . . . . . . 5:2
Boolean operators . . . . . . . . . . . . . . . . . 4:2
Boolean variables . . . . . . . . . . . . . . . . . . 2:2
Built-in variable types . . . . . . . . . . . . . . . 2:1
Button gadgets . . . . . . . . . . . . . . . . . . 20:20
Index page i
PDMS 12.0
C
Callbacks . . . . . . . . . . . . . . . . . . 13:1, 13:3
Callbacks: PML Functions . . . . . . . . . . 13:3
Callbacks:cancelcall . . . . . . . . . . . . . . . 15:6
Callbacks:form methods . . . . . . . . . . . . 14:1
Callbacks:functions . . . . . . . . . . . . . . . . 14:1
Callbacks:initialisation . . . . . . . . . . . . . . 15:5
Callbacks:okcall . . . . . . . . . . . . . . . . . . 15:6
Callbacks:open . . . . . . . . . . . . . . . . . . . 14:3
Callbacks:PML expressions . . . . . . . . . 14:1
Cancel button . . . . . . . . . . . . . . 15:6, 20:21
Case independence . . . . . . . . . . . . . . . . 3:2
Case, in variable names . . . . . . . . . . . . 13:2
CHANNEL command:Alpha view . . . . 20:46
Child forms . . . . . . . . . . . . . . . . . . . . . 15:10
Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2:1
CLEAR command:Plot view . . . . . . . . 20:50
Colours:in views . . . . . . . . . . . . . . . . . 20:48
Columns of text . . . . . . . . . . . . . . . . . . . . 8:1
Comments . . . . . . . . . . . . . . . . . . . . . . . . 3:1
Comparator operators . . . . . . . . . . . . . . . 4:2
Compatibility . . . . . . . . . . . . . . . . . . . . . . 1:2
COMPOSE command . . . . . . . . . . . . . . . 8:1
Concatenation operator . . . . . . . . . . . . . 4:3
Conditional construct . . . . . . . . . . . . . . . 5:1
Constructor method . . . . . . . . . . 2:10, 2:11
Copies . . . . . . . . . . . . . . . . . . . . . . . . . . . 9:1
Cursor types:in View gadgets . . . . . . . 20:49
D
Database attributes . . . . . . . . . . . . . . . . . 9:3
Database Selector gadget . . . . . . . . . 20:34
DB references . . . . . . . . . . . . . . . . . . . . . 9:1
De-activating menu options . . . . . . . . . 16:9
Deep Copy . . . . . . . . . . . . . . . . . . . . . . . 9:1
Default constructor method . . . . . . . . . . 2:10
Delimiters, for text . . . . . . . . . . . . . . . . . . 3:3
Delimiters:arrays . . . . . . . . . . . . . . . . . . . 6:4
Diagnostics . . . . . . . . . . . . . . . . . . . . . . 12:1
Directives . . . . . . . . . . . . . . . . . . . . . . . 13:5
Dismiss button . . . . . . . . . . . . . . . . . . 20:22
Displaying forms . . . . . . . . . . . . . . . . . . 13:4
DO loops . . . . . . . . . . . . . . . . . . . . . 4:3, 5:4
DO loops:breaking out of . . . . . . . . . . . . 5:5
DO loops:nested . . . . . . . . . . . . . . . . . . . 5:6
DO loops:skipping commands . . . . . . . . 5:6
E
ELEVATION command . . . . . . . . . . . . 20:53
ELSE command . . . . . . . . . . . . . . . . . . . 5:1
ELSEIF command . . . . . . . . . . . . . . . . . . 5:1
F
File handling . . . . . . . . . . . . . . . . . . . . . 11:1
File object . . . . . . . . . . . . . . . . . . . . . . . 11:1
filename extensions . . . . . . . . . . . . . . . . 3:3
Files:reading . . . . . . . . . . . . . . . . . . . . . 11:2
Files:writing . . . . . . . . . . . . . . . . . . . . . 11:2
FMSYS object . . . . . . . . . . . . . . . . . . . 22:1
Form attributes . . . . . . . . . . . . . . . . . . . 15:3
Form control attributes . . . . . . . . . . . . 20:21
Form definition file . . . . . . . . . . . . . . . . 13:4
Form Family . . . . . . . . . . . . . . . . . . . . 15:10
Form icon title . . . . . . . . . . . . . . . . . . . . 15:5
Form initialisation:of forms . . . . . . . . . . 15:5
Form methods . . . . . . . . . . . . . . . 13:1, 13:3
Form position . . . . . . . . . . . . . . . . . . . 15:11
Form References . . . . . . . . . . . . . . . . . . 9:1
Form Title . . . . . . . . . . . . . . . . . . . . . . . 15:5
Form variables . . . . . . . . . . . . . . . . . . . 15:9
FORMAT object . . . . . . . . . . . . . . . . . 20:39
Forms:aborting . . . . . . . . . . . . . . . . . . . 15:6
Forms:attributes . . . . . . . . . . . . . . . . . . 15:3
Forms:coordinate system . . . . . . . . . . . 17:1
Forms:defining . . . . . . . . . . . . . . . . . . . 15:3
Forms:displaying . . . . . . . . . . . . . . . . . 13:4
Forms:free . . . . . . . . . . . . . . . . . . . . . 15:10
Forms:hiding . . . . . . . . . . . . . . . 13:4, 15:12
Forms:introduction to . . . . . . . . . . . . . . 13:2
Forms:killing . . . . . . . . . . . . . . . . 13:4, 15:12
Forms:loading . . . . . . . . . . . . . . 13:4, 15:10
Forms:members . . . . . . . . . . . . . . . . . . 13:1
Forms:naming . . . . . . . . . . . . . . . . . . . 13:1
Forms:position . . . . . . . . . . . . . . . . . . 15:11
Forms:positioning . . . . . . . . . . . . . . . . . 22:4
Forms:positioning gadgets . . . . . . . . . . 17:1
Forms:resizable . . . . . . . . . . . . . . . . . . 15:4
Index page ii
PDMS 12.0
IF constructs:boolean . . . . . . . . . . . . . . . 5:2
IF constructs:nesting . . . . . . . . . . . . . . . 5:2
If statements . . . . . . . . . . . . . . . . . . . . . . 4:3
Imperial units:in text gadgets . . . . . . . 20:38
Initialisation:of forms . . . . . . . . . . . . . . 15:5
Inserting bar menus . . . . . . . . . . . . . . . 16:7
Inserting menu fields . . . . . . . . . . . . . . 16:8
Integer variablesSee Real variables . . . 2:1
Interactive 2D views:See Views . . . . . 20:45
Interactive 3D views:See Views . . . . . 20:45
ISOMETRICcommand . . . . . . . . . . . . 20:53
H
HALIGN command . . . . . . . . . . . . . . . . 17:4
HDISTANCE command . . . . . . . . . . . . 17:3
Help button . . . . . . . . . . . . . . . . . . . . . 20:22
Help options . . . . . . . . . . . . . . . . . . . . . 16:4
Hiding forms . . . . . . . . . . . . . . . 13:4, 15:12
Highlight colour . . . . . . . . . . . . . . . . . . 20:48
I
Icon title for form . . . . . . . . . . . . . . . . . . 15:5
IF construct . . . . . . . . . . . . . . . . . . . . . . . 5:1
J
Jumping to a labelled line . . . . . . . . . . . . 5:6
L
LABEL command . . . . . . . . . . . . . . . . . . 5:6
Late evaluation of variables . . . . . . . . . . 8:2
LIMITS command . . . . . . . . . . . . . . . . 20:54
LIMITS command:AREA view . . . . . . 20:51
Line feeds . . . . . . . . . . . . . . . . . . . . . . . . 8:5
Linking files . . . . . . . . . . . . . . . . . . . . . . 3:3
Linking new form files . . . . . . . . . . . . . . 13:5
List gadget . . . . . . . . . . . . . . . . . . . . . 20:29
Loading forms . . . . . . . . . . . . . . . . . . . 13:4
Logical operators . . . . . . . . . . . . . . . . . . 4:1
LOOKcommand . . . . . . . . . . . . . . . . . 20:53
Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . 5:4
M
Macros . . . . . . . . . . . . . . . . . . . . . . . 3:1, 7:1
Macros:argument defaults . . . . . . . . . . . 7:3
Macros:argument separators . . . . . . . . . 7:2
Macros:arguments . . . . . . . . . . . . . . . . . 7:1
Macros:arguments, omitting . . . . . . . . . . 7:3
Macros:arguments, separating . . . . . . . . 7:3
Macros:naming . . . . . . . . . . . . . . . . . . . . 7:1
Macros:running . . . . . . . . . . . . . . . . . . . 7:1
Macros:suspending . . . . . . . . . . . . . . . 12:2
Members (of objects) . . . . . . . . . . . . . . . 2:1
Menu bar:defining . . . . . . . . . . . . . . . . 16:2
Menu Bars:inserting menus . . . . . . . . . 16:7
Menus . . . . . . . . . . . . . . . . . . . . . . . . . 16:1
Menus:bar . . . . . . . . . . . . . . . . . . . . . . 16:2
Menus:de-activating options . . . . . . . . 16:9
Menus:defining . . . . . . . . . . . . . . . . . . . 16:3
PDMS 12.0
Q
Querying: PML . . . . . . . . . . . . . . . . . . . 12:2
Querying:pathname of PML Files . . . . . . 3:4
N
Naming conventions . . . . . . . . . . . . . . . 13:1
Numeric Keypad Keys . . . . . . . . . . . . . . B:2
O
Object class . . . . . . . . . . . . . . . . . . . . . . 2:1
Object methods . . . . . . . . . . . . . . . . . . . . 2:9
Object methods:user-defined . . . . . . . . 2:10
Object types . . . . . . . . . . . . . . . . . . 2:1, 2:2
Objects . . . . . . . . . . . . . . . . . . . . . . . . . . 2:1
OK button . . . . . . . . . . . . . . . . . 15:6, 20:21
Operands . . . . . . . . . . . . . . . . . . . . . . . . 4:1
Operators . . . . . . . . . . . . . . . . . . . . . . . . 4:1
Operators:boolean . . . . . . . . . . . . . . . . . 4:2
Operators:concatenation . . . . . . . . . . . . . 4:3
Operators:logical . . . . . . . . . . . . . . . 4:1, 4:2
Operators:precedence . . . . . . . . . . . . . . 4:2
Orientation () . . . . . . . . . . . . . . . . . . . . . . 2:2
Overloading, of methods . . . . . . . . . . . . 2:11
P
Panning . . . . . . . . . . . . . . . . . . B:1, B:3, B:4
Paragraph gadgets . . . . . . . . . . . . . . . 20:19
Parent forms . . . . . . . . . . . . . . . . . . . . 15:10
PATH command . . . . . . . . . . . . . . . . . . 17:3
Pausing a running file . . . . . . . . . . . . . . 12:2
PERSPECTIVE mode . . . . . . . . . . . . . 20:52
Pixmaps . . . . . . . . . . . . . . . . . . . . . . . . 19:7
PLANcommand . . . . . . . . . . . . . . . . . . 20:53
Plot view gadget . . . . . . . . . . . . . . . . . 20:50
PML directives . . . . . . . . . . . . . . . 3:4, 13:5
PML Files . . . . . . . . . . . . . . . . . . . . . . . . 3:1
pml index command . . . . . . . . . . . 3:4, 13:5
pml index file . . . . . . . . . . . . . . . . . . . . . . 3:3
pml rehash command . . . . . . . . . . 3:3, 13:5
pml reload form command . . . . . . . . . . 13:5
R
RAW operator (array variables) . . . . . . . 8:5
Read-only text gadgets . . . . . . . . . . . 20:38
References . . . . . . . . . . . . . . . . . . . . . . . 9:1
Refreshing gadgets . . . . . . . . . . . . . . 19:10
Relational operators . . . . . . . . . . . . . . . . 4:2
Relative positioning (gadgets) . . . . . . . 17:6
RELOAD command . . . . . . . . . . . . . . . 2:12
Reset button . . . . . . . . . . . . . . . . . . . . 20:22
Result, of expression . . . . . . . . . . . . . . . 4:1
RETURN ERROR command . . . . . . . . 10:3
S
Sessions (), as objects . . . . . . . . . . . . . . 8:4
sessions, as objects . . . . . . . . . . . . . . . . 8:4
SetDefaultFormat . . . . . . . . . . . . . . . . . 22:1
Setup form command . . . . . . . . . . . . . . 15:3
Showing forms . . . . . . . . . . . . . . . 13:3, 13:4
Sorting:arrays . . . . . . . . . . . . . . . . . . 6:4, 6:6
Spaces . . . . . . . . . . . . . . . . . . . . . . . . . . 8:5
splashscreen . . . . . . . . . . . . . . . . . . . . 22:4
Splitting text into array elements . . . . . . 6:3
Status Line . . . . . . . . . . . . . . . . . . . . . . . B:3
Storing PML Files . . . . . . . . . . . . . . . . . . 3:1
String variables . . . . . . . . . . . . . . . . . . . 2:1
Subtotalling arrays . . . . . . . . . . . . . . . . . 6:9
Suspending a running file . . . . . . . . . . . 12:2
Swapping applications . . . . . . . . . 22:2, 22:3
Synonyms . . . . . . . . . . . . . . . . . . . . . . . 7:4
System-defined variable types . . . . . . . . 2:2
T
Text gadgets:formatting . . . . . . . . . . . 20:38
Text gadgets:imperial units . . . . . . . . 20:38
Index page iv
PDMS 12.0
U
Undefined variables . . . . . . . . . . . . . . . 2:13
Unset variables . . . . . . . . . . . . . . . . . . . 2:13
User-defined variable types . . . . . . . . . . 2:2
V
Validating text fields . . . . . . . . . . . . . . 20:43
VALIGN command . . . . . . . . . . . . . . . . 17:4
VAR command . . . . . . . . . . . . . . . . 6:6, 6:9
Variable type . . . . . . . . . . . . . . . . . . . . . . 2:1
Variables . . . . . . . . . . . . . . . . . . . . . . . . . 2:1
variables:array . . . . . . . . . . . . . . . . . . . . 2:1
variables:boolean . . . . . . . . . . . . . . . . . . 2:1
Variables:creating . . . . . . . . . . . . . . . . . . 2:4
Variables:deleting . . . . . . . . . . . . . . . . . 2:13
Variables:global . . . . . . . . . . . . . . 2:3, 2:13
Variables:in rules . . . . . . . . . . . . . . . . . . 8:2
Variables:integerSee Real variables . . . . 2:1
Variables:late evaluation . . . . . . . . . . . . . 8:2
Variables:local . . . . . . . . . . . . . . . . . . . . . 2:3
Variables:names . . . . . . . . . . . . . . . . . . . 2:3
Variables:naming conventions . . . . . . . 2:13
variables:PML 1 . . . . . . . . . . . . . . . . . . . 2:1
variables:real . . . . . . . . . . . . . . . . . . . . . . 2:1
Variables:string . . . . . . . . . . . . . . . . . . . . 2:1
Variables:type . . . . . . . . . . . . . . . . . . . . . 2:1
Variables:undefined . . . . . . . . . . . . . . . 2:13
Variables:unset . . . . . . . . . . . . . . . . . . . 2:13
VDISTANCE command . . . . . . . . . . . . . 17:3
View popup menu . . . . . . . . . . . . . . . . . . B:4
View radius . . . . . . . . . . . . . . . . . . . . . 20:53
View range . . . . . . . . . . . . . . . . . . . . . 20:53
Views: . . . . . . . . . . . . . . . . . . . . . . . . . 20:45
Views:ALPHA . . . . . . . . . . . . . 20:45, 20:46
Views:AREA, defining . . . . . . . . . . . . . 20:51
Views:aspect ratio . . . . . . . . . . . . . . . . 20:48
Views:AUTO flag . . . . . . . . . . . . . . . . . 20:54
Views:background colour . . . . . . . . . . 20:48
Views:borders . . . . . . . . . . . . . . . . . . . 20:48
Views:colour shaded . . . . . . . . . . . . . . 20:52
Views:colours . . . . . . . . . . . . . . . . . . . 20:48
W
WALKTHROUGH mode . . . . . . . . . . . 20:53
White space . . . . . . . . . . . . . . . . . . . . . . 6:4
Window menu . . . . . . . . . . . . . . . . . . . 16:4
Z
Zooming . . . . . . . . . . . . . . . . . .B:1, B:3, B:4
Index page v
PDMS 12.0
Index page vi
PDMS 12.0