[Omnis-Newsletter] Omnis Technical Newsletter

omnis-news-admin@omnis.net omnis-news-admin@omnis.net
Wed, 2 May 2001 13:28:31 +0100


Omnis Tech Newsletter May 2nd, 2001

========================================

UNSUBSCRIBE OPTIONS: You have been sent this email because you have directly
signed up for, or expressed an interest in receiving a technical newsletter
when you downloaded an evaluation of Omnis Studio, or registered the Lite
version of Omnis Studio. If, however you feel you have received this email
in error you can unsubscribe as well as change your subscription options at
www.omnis.net/newsletter.

N.B.  If you subscribed by checking a box on one of our forms, you will not
have received a password. You will need to submit your email address at
www.omnis.net/newsletter and select the 'email me my password' option on the
next page in order to receive this.


========================================

WELCOME!

Welcome to this the fourth issue of our Omnis Technical Newsletter.
Published fortnightly, it is intended for Omnis developers of all levels and
experience, for those people evaluating Omnis Studio, or for developers
moving from a similar tool. We think you'll find the content both
interesting and useful for your Omnis development needs and hopefully it
will help you become more productive in Omnis application design.

The first article in this newsletter discusses the Omnis IDE and the full
range of tools available to the developer. The second article describes how
you can build selection lists or lookups to enhance and standardize data
entry. You may find it easier to work through the exercises and examples in
this newsletter by printing it out before you begin.

CONTENTS:
-About the Authors
-Building Your First Omnis Application: Part 3 The Omnis IDE, by Geir
Fjaerli
-About Omnis and its History
-Dropdown Selection Lists, by David Swain
-Omnis Studio for Mac OS X
-Copyright and Unsubscribe details


========================================

About the Authors

The Omnis Technical Newsletter contains high quality content from two
leading and well respected Omnis developers, Geir Fjaerli and David Swain.

Geir Fjaerli is based in Norway and has been an Omnis professional developer
for many years as well as a Regional Sales Manager for Omnis. He is
currently working freelance again developing a range of products (in Omnis
of course) including his Prophet5 sales and customer relationship management
solution, soon to be released for Mac OS X.

David Swain is the founder and president of Polymath Business Systems, for
many years a leading provider of Omnis training. His expertise in Omnis
programming and his ability to make complex concepts understandable are
recognized throughout the worldwide Omnis community.


========================================

Building Your First Omnis Application
Part 3 The Omnis IDE
By Geir Fjaerli, Sunshine Data
Email: geir@sunshinedata.net
Web: www.sunshinedata.net

----------------------------------------------------------------------

After building a quick application in the first part of this tutorial (in
the first issue of this newsletter), we spent the second part looking at the
various classes Omnis offers. They include window classes for data display
and entry, schemas for data structure definitions, reports for printing and
so on.

An application is a collection of classes, holding:
* The class definition including components like window fields and menu
lines
* Properties like the window title or the position and color of a field
* Methods to describe the behavior of the classes and components, and the
interaction between them.

In this issue, we are going to look at the various tools that Omnis offers
to allow us to design such applications. But first, let us introduce an
important distinction: So far we have been talking about applications, but
as soon as we are building one we will be talking about libraries. What
gives?

-- THE APPLICATION and THE LIBRARY --
The term application usually refers to a complete program. Microsoft Word is
an application, so is Adobe PageMaker. However, when creating an application
it will often be sensible to split it into several physical files, or
modules. You may want one for customer entry, and another for accounting, or
you may want to split the application into database access logic and display
logic. In Studio, we refer to such modules as libraries.

You may want to start with just one library, in which case your library is
the application.

-- THE OMNIS DEVELOPMENT TOOLS --
Omnis has a large number of tools to help you build your libraries, we are
going to concentrate on the basic ones:
* The Browser
* The Component Store
* The Property Manager
* The Catalog

These are part of the IDE - Integrated Development Environment - together
with the method editor, Notation Inspector and more. They are part of the
Omnis core program, built in C++. Other tools, like the SQL browser, Version
Control System (VCS) and Icon Editor, are add-ons, some built in C++, some
are built using Omnis Studio code.

The Omnis development environment offers two menus: The View menus gives you
access to the IDE tools, while the Tools menu contains add-on tools.

-- THE IDE TOOLS --
Looking at the description of an application above, it is clear that we need
to be able to do the following when building our application:
* Add classes, and add fields/controls/components to those classes.
* Set the properties for those classes and components.
* Build methods to describe class behavior and database access etc.

This is done using the IDE tools. Here is how they work:

-- THE BROWSER --
The Browser is our "folder" view of libraries and classes. It is opened by
the first line in the View menu (or pressing F2), and opens as a detail or
icons list window, by default in the upper left of the screen. The Browser
has two purposes:
1. Display all open libraries. You can create, open, close and save
libraries from here. If you double-click a library, you will "drill down" to
2. Display the classes of the selected library. You can create, delete,
duplicate and lock classes here, as well as open to modify them and get at
their methods. If you double-click on a class in the Browser, Omnis will
open the GUI editor (for visual classes), method editor (for non-visual) or
column editor (for definition classes) appropriate for the selected class.

The Browser has three menus:
* Library. The library menu lets you create new libraries, open or close
them, and save them. It also gives you access to externals and to check into
the VCS (we shall leave those two for later.) Once you have opened a few
libraries, you will notice that Omnis keeps a list of them at the bottom of
the Library menu, so that you can reopen them easily later.
* Class. This is only enabled when displaying the class list. From here you
can create new classes or invoke class wizards, delete and duplicate etc.
* View. This lets you set the "look and feel" of the Browser, including the
type of list (icon or details), the sort order etc. Note the Browser Option
item, it opens a window to let you select the types of classes to display.
If you cannot find something, check here if it is selected. System tables
(icons, date formats etc.) are deselected by default. You can change the
position and look of the Browser and then use the save window setup to store
your settings so it will open the same way next time.

The browser also supports "right click" context menus.

NOTE! The context menus contains two critical items: "Always private" for
libraries, and "Lock" for classes. These let you protect your application so
that no one can get at your code later, e.g. at a customer site. But beware
that this includes yourself, there is no "unlock" mechanism. Make sure that
you have an unlocked backup before using either of these.

-- THE COMPONENT STORE --
The Component Store (second item on the View menu/F3) is the repository for
all the elements that we want to add to our library. It changes to reflect
the current level of the Browser/Editor: If the top window is the Browser,
the Component Store will display classes and class wizards, if the top
window is a GUI class editor, it will display the components relevant to
that type of class.

The Component Store opens by default as a small icon list at the bottom left
of the screen. You can "right click" to add text or change to large icons,
and as with the Browser you can save your setup if you change it.

The Component Store is split in two panes by a horizontal line. The top part
is a group selector, the bottom is the item selector. The group selector
scrolls horizontally, if necessary, the item selector scrolls vertically.

As mentioned, the contents of the Component Store changes to reflect the
surroundings. If the top window is:
* The Browser: The top pane will show logical groups of classes, templates
and wizards, e.g. one for windows, one for reports, one for net wizards and
so on. The bottom shows the class templates and wizards for the selected
group. The first group is the default classes. They are "empty" classes,
e.g. a window with no fields on it, a menu with no lines. Other templates
may have some predefined settings and components added.

* A GUI Editor: If the top window is an editor for a class that can have
components (windows, reports, remote forms and toolbars), the Component
Store will display the components that are relevant to the type of class.
The top pane will show the component groups, for a window that will be
Standard (built in) fields, background objects, external and background
components, and subwindows. The bottom pane lists the components of the
selected group.

To use a component, drag it from the Component Store and drop it into the
desired Browser/Editor window.

We shall look at the various wizards and templates in a later issue, and
also show you how to modify or add to the contents of the Component Store.

-- THE PROPERTY MANAGER --
The Property Manager (5th item on the View menu/F6) is the window that
displays and allows us to edit the properties or attributes of our
application elements, and even Omnis itself. Such properties may include a
window title, the color and font of a field etc. The Property Manager is an
enterable list which by default appears on the right edge of the screen.

The Property Manager will reflect the element chosen. So if you click a
library in the Browser, it will show you the properties of the library, if
you select a class you get the properties of that class, and if you add a
field to a window, you can set the field properties.

Note: Some properties do not appear in the Property manager, e.g. you set
the data type for a schema column in the schema editor, not the Property
manager.

The number of properties depends on the type of element. A schema has only a
few, most of them shared with all classes. A window object may have several.
If the list is long, the properties will be grouped, and Omnis will display
them in tabs, e.g. a window field has General, Appearance, Action and Text
tabs.

There are a number of different property types. Some are text, some are
numbers, some are boolean (yes/no). Some are stored as numbers, but the
numbers may represent an icon or color. In those cases, the Property manager
will present you with a pop up icons list or color picker to help you select
a value.

Some properties can be set for the class at design time, some are runtime
properties and are only available when you run the application. We shall
look at the distinction in a later issue. The context menu of the Property
manager allows you to display such runtime properties at design time, though
you cannot see their values.

The context menu also allows you to copy and paste properties from on
element to another. Only common properties are copied, and some never are,
like the ident of the element which is unique.

You may resize and move the Property manager to your desired location and
save the setup as with the other IDE windows.

-- THE CATALOG --
The Catalog (6th line on the View menu/F9) is a dictionary of variables,
constants and other texts and expressions. This saves us from having to
remember all the words needed to program an application. That may be a
variable name we want to insert in the Property Manager, a function in a
method line etc.

The Catalog is a divided by tabs. Each tab shows two vertical panes, the
left showing groups and the right the elements of the selected group. The
tabs are:

*Variables: This tab lists all the variables of our open libraries, plus any
file classes we have created. The variables are grouped into Task, Class,
Instance and Local vars, and also Parameters and Event Parameters. We shall
discuss the purpose of and distinction between these in a later issue.
*Schemas: Lists any schemas created and the columns of the selected schema.
*Queries: Lists any queries created and the columns of the selected query.
*Hash: The hash variables are built in variables in Omnis. Their name is
prefixed by a #-sign, e.g. #D or #MODIFIED. Some are set by the system, like
#D, which returns the date of the system clock. Some are global variables
that you can use, you are well advised to declare your own though, as
another application may be using the same hash-var and thus over-writing
your values.
*Constants: Constants are number values given descriptive names, which makes
them easier to use and remember. Constants names are prefixed with a 'k'. So
to assign the color red you can use the constant kRed, to set your report to
landscape orientation use kOrientLandscape, etc.
*Events: Events are reported by Omnis when an action takes place in the
application. So if the user tabs out of an entry field, Omnis will report an
evTab and an evAfter. Most events are reported after user actions like this,
but some may be triggered by other things, e.g. a timer will trigger an
evTimer event. Event names are prefixed with 'ev'.
*Functions: A function returns a value based on an argument you pass to it.
They have abbreviated names, e.g. upp() returns the upper case version of
the argument: upp('omnis') returns OMNIS. The functions listed in the
Catalog are built into Omnis or Omnis components.

To use an entry from the Catalog, drag it to the desired location and drop
it, or place the cursor in that location first and then double-click the
entry on the Catalog. Unlike most windows, the Catalog does not "steal" the
cursor from its current position.

You may resize and move the Catalog to your desired location and save the
setup as with the other IDE windows.

This was a brief intro to the main IDE tools. We shall be using these a lot
when we start programming our application. Till then, good luck and take
care!


========================================

About Omnis and its History

Over the last twenty years Omnis has evolved into one of the most powerful
and flexible Rapid Application Development (RAD) tools available today.
Omnis lets you develop and deploy all types of multi-tier, enterprise-wide,
and web-based applications for Windows, Linux, and Mac for all market
sectors.

And we've always been at the forefront of software development. We have
achieved many industry firsts in the development tools arena (listed in our
history), and we continue to respond to the changing needs of software
developers, and the converging worlds of information management, web-based
technologies, and mobile communications.

To find out how and where it all started, read about our history at:
www.omnis.net/aboutus/history.html


========================================

Dropdown Selection Lists
By David Swain, Polymath Business Systems Inc
Email: dataguru@polymath-bus-sys.com
Web: www.polymath-bus-sys.com

----------------------------------------------------------------------

In building applications with a modern graphical user interface, we try to
minimize the amount of code memorization our users must endure by offering
some visual means of selecting among valid options for input to certain
fields. Such options display descriptive and meaningful text to the user,
but transmit (often numerically) coded values to the database for storage.
Codes are used to save space in the database, among other reasons.

One common method is to place an array of radio buttons on an entry window.
Each button in the group relates to the same variable (has the same dataname
value), but has different text and represents a different value. In Omnis
Studio, radio buttons are very easy to set up and use, working best with
short integer variables.

The downside of using radio buttons is that they can quickly overwhelm an
entry window if there are more than a few choices for the value of the
related variable. To conserve screen real estate, many programmers turn to
dropdown selection lists when radio button groups get out of hand. The
tradeoff is that a little more programming work is involved with selection
lists.

There are two basic issues in the use of code translation mechanisms: First,
values from the database must be translated to their verbose form for
display as records are shown. Second, selected items must be translated back
to their coded form for storage in the database as records are inserted or
updated.

Omnis Studio radio buttons coupled with a short integer variable deal with
this so directly that we don't think about it much. Integer variables take
non-negative values from zero through 255 (way more than the practical limit
for radio buttons!) and radio buttons generate values from zero through one
less than the number of buttons in the group. On display of a record, the
appropriate radio button is shown as selected. On selecting a radio button
during data entry, the associated value for the selected button is
automatically assigned to the related variable. Piece of cake!

But a list used as a selection source to replace a group of radio buttons
poses two immediate and related problems: First, the variable associated
with a dropdown list field is a list variable and not the target variable
whose value will be stored in the database. No matter what other design
choices we make, we need to write at least a little code to transfer values
between our target variable and the selection list used to represent it.
Second, while selecting a line in a list does automatically yield one of a
number of sequential integer values (the selected line number), the range of
values does not match that of a group of radio buttons. A list begins with
line number one -- there is no line number zero!

We need to approach these two problems as a unit. The way that we choose to
use the list will determine the value that we transfer to the target
variable and the way in which we select a line for display with a record.

We have two basic choices in translating a list selection to a variable
value. The simplest is to use the selected line number with an offset of -1.
Here is an exercise to demonstrate this:

Exercise1: Simple list-radio button selector

We will set up a window with a selection list, a group of radio buttons and
a display field to show how values can be transferred between a list and an
integer variable. We will choose "payment type" as our example since the
number of radio buttons to detail this can be unwieldy.

1. Create a new window class named "selList1". Make certain the modelessdata
property of the window is set to kTrue. Use the Property Manager to
check/set this.
2. Open the Method Editor by double-clicking on the window class. Once
there, create three instance variables:
one named variable of Short integer type
one named list of List type
one named string of Character type
To create an Instance variable, click on the Instance tab in the variables
list at the top of the method editor, Right-click on the variable pane and
select Insert New Variable.
3. Put the following lines of code in the $construct() method of the window
class:
Do list.$define(string)
Do list.$add('Check')
Do list.$add('Cash')
Do list.$add('MCard')
Do list.$add('Visa')
Do list.$add('AmEx')
Do list.$add('Discover')
Do list.$add('Account')
Calculate list.$line as 1
4. Close the Method Editor and place a dropdown list field on the window
from the Component Store. Give this field the following property values:
set name to selectList
set dataname to list
set calculation to string
5. Place a radio button field on the window from the Component Store. Give
this field the following property values:
set name and text to Check
set dataname to variable
6. Now double-click the field to open the Method Editor and delete its
$event method. (I'll explain why in a bit...) Then close the Method Editor.
7. Drag-copy six clones of this field. This is done slightly differently on
different platforms. On Windows and Linux, hold down the Control key and
drag the field; on MacOS, hold down the Option key and drag. On either
platform, hold down the shift key once the drag has commenced to keep the
cloned field in line horizontally or vertically with the original.
8. Arrange the clones in field number order and set their name and text
property values to "Cash", "MCard", "Visa", "AmEx", "Discover", and
"Account" (without the quotes) in that order.
9. Place a group box field on the window from the Component Store. Give this
field the following property values:
set name to typeBox
set text to Payment type
10. Drag the radio button fields to the group box and resize the box so that
all fields can be seen. This container will simplify some of our methods.
11. Place an entry field on the window from the Component Store. Give this
field the following property values:
set name to display
set dataname to variable
set enabled to kFalse
This field will be used to display the numeric value set by clicking either
the dropdown list or one of the radio buttons.
12. We first need to create a method to transfer a value to variable when a
line of the list is clicked. In the $event() method of selectList place the
following lines of code:
On evClick
 Calculate variable as pRow.$line-1
 Redraw {typeBox,display}
The parameter pRow is a reference to the list row receiving the click that
is returned as part of the evClick event. This method translates the list
click to the appropriate value for variable and updates the window. Notice
how we can redraw just the group box and all its contents are redrawn as
well. Open the test instance of the window (press Ctrl/Cmnd-T from the
window class) and test selecting a list line.
13. Next we need a way to update the list field when the value of variable
changes. In our case, we will click on a radio button to simulate the value
change, but think of this as emulating the loading of a new record. So that
we don't have to replicate this method into each radio button, we'll use the
group box field instead.
Double-click on the group box field to open the Method Editor to its
$event() method. Now rename this method to "$control" (without the quotes).
Put the following lines of code into the $control() method of the group box
field:
On evClick
 Calculate list.$line as variable+1
 Redraw {selectList,display}
Go ahead and try this out on the test window instance. Notice how the
$control() method of the group box reacts to a click on any radio button
within it. We removed the $event methods of the radio button fields to
prevent them from trapping the evClick event. There are other techniques we
could have used (e.g., clearing the "On evClick" block from each of them or
placing a "Quit event handler (Pass to next handler)" line in those blocks),
but this was simpler here.
14. If we want to be a little more "object oriented" in our approach to this
problem, we could encapsulate the method for updating the list field in the
field itself. To do this we must add another method to the field.
Double-click on the list field to open the Method Editor to its $event()
method, then use the context menu (Right/control-click the method name) to
add a new method and name it $update.
15. This method will need a parameter to which other methods can pass the
line to be set as current in the list variable associated with the field.
Create a parameter variable named line of Short integer type. To create a
Parameter variable, click on the Parameter tab in the variables list at the
top of the method editor, Right-click on the variable pane and select Insert
New Variable.
16. Put the following line of code in the $update() method of the list
field:
Calculate [$cfield.$dataname].$line as line
Note that this method is entirely generic as it does not rely on any
specific variable.
17. Now change the second line of the $event() method of the group box field
so that the method reads:
On evClick
 Do $cinst.$objs.selectList.$update(variable+1)
 Redraw {selectList,display}
This now sends a message to the dropdown list field to update itself. This
may seem like a bit more work for us (and it is), but the result is that we
now have a selection list component that is self-contained and can be copied
to other windows and easily modified to represent other lists.

This technique is very close to the way radio buttons work, but it may be
too limiting in the long run for our application. Consider for a moment that
we may need to add other choices to our list in the future, and that some of
those choices may need to be "interwoven" with the existing ones. This is no
problem with radio buttons because we can arrange them in any physical order
we need on the window while maintaining the proper field order values to
consistently generate our variable values. But list lines will only show up
in the order they appear in the list, so using the line number doesn't allow
us to add new choices between existing ones. Of course, this won't be a
problem with all uses of this technique, but it may arise -- so what then?

We could build the list in such a way that it contains the translation value
as well as the translation text (and perhaps other "helper" values) on each
line. The list could then be sorted for presentation based on alphabetic
order, order of most frequent anticipated use, or some other criteria while
still giving us the means to extract the proper selected code value.

Exercise 2: Selection list containing code values

1. Begin by duplicating the window class from the previous exercise in the
Class Browser. Rename the new window "selList2".
2. Open the Method Editor for this window class and add two instance
variables to those already there:
one named code of Short integer type
one named customOrder of Short integer type
These will become additional columns of the selection list variable.
3. Go to the $construct() method of the window class. We will change the
definition of list as well as the code lines that populate it. We will also
add another line to the list that must be injected between two original
lines. Modify the method to read as follows:
Do list.$define(string,code,customOrder)
Do list.$add('Check',0,1)
Do list.$add('Cash',1,2)
Do list.$add('MCard',2,3)
Do list.$add('Visa',3,4)
Do list.$add('AmEx',4,5)
Do list.$add('Discover',5,6)
Do list.$add('Diners Club',7,7)
Do list.$add('Account',6,8)
Calculate list.$line as 1
4. On the window class, drag-copy another clone of a radio button. Make sure
that it stays within the group box boundary. (You may have to expand the box
slightly.) Name and label the new button "DinersClub" and give it an order
number one greater than that of the "Account" button. Place this button
between the "Discover" button and the "Account" button. (The order property
determines how it deals with the value of variable, not its physical
location relative to the other buttons.)
5. Now open the test window instance and see how our modified list-radio
button combination works. Notice that selecting "DinersClub" or "Account" by
either method takes the two out of synch. The radio buttons generate the
correct value, but the list does not because we are still relying on the
line number. We must change two things: the way selecting a line generates a
value and the way a list line is selected if the value of variable is
changed outside the list.
6. Go to the $event() method of the dropdown list field. Change the second
line of the method so that it reads:
On evClick
 Calculate variable as pRow.code
 Redraw {typeBox,display}
This will now generate the correct value for a selected line no matter what
order the lines are in. Open the test window instance and try selecting
various list lines.
7. Go to the $control() method of the group box. We must now send the actual
value of variable since we are no longer dealing with the line number and an
offset. Change the second line so that the method now reads:
On evClick
 Do $cinst.$objs.selectList.$update(variable)
 Redraw {selectList,display}
This is only part of the answer. We must still modify the $update() method
in the list itself to locate a line based on this passed value.
8. Go to the $update() method of the dropdown list field. Change the name of
the parameter variable to "value" (as this is more descriptive of its new
use). Now change the single line of code in the method to read:
Do [$cfield.$dataname].$search($ref.code=value,kTrue,kFalse,kFalse,kFalse)
This requires a little explanation. The square bracket part resolves to the
name of the list variable represented by the dropdown list field. The first
parameter is a search calculation that specifies a column in the list and a
value we wish to locate. ($ref is a reference to the list on which the
search is being performed.) The four other (normally optional) parameters
are required since we don't want to use the default values for the third and
fourth ones. Parameters four and five must be kFalse if we want a line to be
selected and made current as a result of the search. Parameter two indicates
whether we want the search to start at the beginning of the list (we do) and
parameter three indicates whether we want to search only lines that are
already selected (we do not). List searching is a topic for another time --
the point is here that we can do this in a single line of code.
9. To prove that this will work for any order, we can add pushbuttons to the
window to sort list on any of its columns. Drag a pushbutton field from the
Component Store onto our window class. Name and label the field "Alpha".
10. Double-click the field to go to its $event() method. Create a local
variable of Short integer type and put the following lines of code in the
method:
On evClick
 Calculate value as list.code
 Do list.$sort($ref.string)
 Do $cinst.$objs.selectList.$update(value)
 Redraw {selectList}
We must reserve the code value from the current line before the sort since
sorting the list only changes the order of the lines, not which line is
current. We then send this value to the $update() method of the list field
so it can locate the proper line in the sorted list and make it current
again.
11. Drag-copy two clones of this pushbutton field. Name and label one of
them "Code" and the other "Custom".
12. In the Code pushbutton, revise the third method line in $event() to
read:
Do list.$sort($ref.code)
In the Custom pushbutton, revise the third method line in $event() to read:
Do list.$sort($ref.customOrder)
Now open the test instance of this window and have a good time exploring
what we have built!

There are many possible extensions of this technique, but this article is
long enough already! One more thing you may wish to consider is to put the
two selection list components back into the Component Store so you can use
them again at some later time with a minimum of additional programming. More
on that next time.


========================================

Omnis Studio for Mac OS X

Did you know we released Omnis Studio for Mac OS X a day before Apple
released its ground breaking new OS? On March 23rd Raining Data announced
the availability of the Mac OS X version of Omnis Studio 3 providing
developers with the power and flexibility of Studio with all the fantastic
new features of Apple's new Mac OS X, released on March 24th.

The Mac OS X release of Omnis Studio gives you even more flexibility for
development and deployment in Omnis Studio: now you get Mac OS X as well as
Windows 95/98, Windows NT/2000, Linux, and Macintosh OS 8.6/9 versions of
our development product, as well as the thick client runtime engine for Mac
OS X, and the thin client web server edition for your web applications.

For more information, screenshots, and further Mac OS X links, please go to:
www.omnis.net/macosx

========================================

I hope you've found this issue of the Omnis Tech Newsletter both interesting
and informative. Please send me your comments and feedback, and include
suggestions for future articles if you like. We would like to hear from you.

Regards,
--Andrew Smith.
Omnis Tech Newsletter Editor
Email: editor@omnis.net

========================================

No part of this newsletter may be reproduced, transmitted, stored in a
retrieval system or translated into any language in any form by any means
without the written permission of Raining Data.
© Copyright Raining Data, Inc., and its licensors 2001. All rights reserved.
Omnis® is a registered trademark and Omnis 7T, and Omnis Studio are
trademarks of Raining Data UK Ltd. Other products mentioned are trademarks
or registered trademarks of their corporations.

========================================

To unsubscribe from this newsletter or change your subscription options,
please go to:
www.omnis.net/newsletter