[Omnis-Newsletter] Omnis Technical Newsletter

omnis-news-admin@omnis.net omnis-news-admin@omnis.net
Wed, 27 Jun 2001 15:56:57 +0100


Omnis Tech Newsletter June 27th, 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 and thank you for subscribing to the 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.

In the first article in this newsletter, Geir Fjaerli takes a look at the
code generated by the SQL Form wizard and steps through line by line
explaining how Omnis interacts with your database using the built-in SQL
methods. In the second article, David Swain describes the differences
between the "Heading List" for Remote Forms and the "Headed List" for Window
Classes and shows how you can manipulate the appearance of these objects
using the Omnis notation. 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 part 7: What did the wizard build, and a tour of the method
editor, by Geir Fjaerli.
-About Omnis web site downloads
-Heading Lists in Remote Forms, by David Swain
-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, now available 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 part 7: What did the wizard build, and a tour of the method editor.
By Geir Fjaerli, Sunshine Data
Email: geir@sunshinedata.net
Web: www.sunshinedata.net

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

Welcome to a new chapter of our Basic Omnis tutorial. After building our
first basic application in the first chapter, we started looking at the
details of an Omnis application. They included:
Part 2: The Studio Classes. The classes are the building blocks of an
application, and include windows, reports, menus etc.
Part 3: The Omnis Integrated Development Environment. Here we looked at the
Component store, the Property Manager and the Catalog.
Part 4: The Data Structure. Including basic terminology.
Part 5: Creating schemas and database tables.
Part 6: Adding table classes and using our schemas in application windows.

As usual, I strongly suggest that you refer to these chapters if you haven't
already, or if you feel uncertain about the details. From part 5 onwards,
each new chapter will base on programming done in previous parts, so you
have to follow them all. (For back issues, please go to
www.omnis.net/newsletter and click on the Newsletter Archive link.)

Note: As we go into the depths of Omnis, we will encounter at lot of
features that fall outside the scope of this tutorial, or which we do not
have time or space to discuss in detail. You will find these discussed in
the Omnis Studio manuals. Please refer to them for more information on
subjects that we only briefly touch on.

Last time we added table classes to our library, and then used the SQL form
wizard to create a working window based on our tables. No hand coding was
required, we started using our window right away. Today we shall inspect the
window and see what the wizard created. This will serve to illustrate the
SQL logic in Omnis Studio. Before we do, we will take a quick guided tour of
the method editor.

OPEN STUDIO AND THE TASKS LIBRARY.
Open Omnis Studio and the library named Tasks. You can either double-click
Omnis and then open the task from the library menu, or double-click the
library file to open both. Also, if you have not worked on any other
libraries since we last worked on Tasks, the Open last button on the Welcome
screen will open it for you.

Note: Since we have not yet built a logon window, we still need to manually
log on to the database. To do so open the SQL Browser and from the Session
menu select the session names TASK. That will open the session for us. Now
close the SQL Browser as you do not need it.

Now open the Browser, double-click the Tasks library and then wEmployee.
This will open the window in design mode.

To see the methods for the window, you can either: Double-click the window
background. (Note: If you double-click a window object, you will see the
methods for that object.), or Select Class Methods from the context menu.

THE METHOD EDITOR:
The window that opens is the Studio method editor. While it looks a bit
daunting at first, it is neatly organized and very powerful. Let us have a
look. (The following assumes that you still have the standard setup for the
window, Studio lets you customize almost every bit of it, so adventurous
souls may have a different setup. For now I strongly recommend that you
leave everything in the original shape and setup.)

The Method editor has 7 menus:
Modify: This menu is where you insert and delete lines of code, comment
them, and test run them.
View: This is where you decide what to see in the window. As I said, let it
be for now.
Debug: Studio has a very powerful debugger, which lets you step through your
code to see what happens.
Options: Various debugger options
Breakpoint: Lets you set breakpoints in the code. A breakpoint causes the
code execution to stop at that point, so that you can inspect the variables,
or step through the following code. Under the menus is a toolbar with icons
for the debugger items.
Instances: Lets you see the open instances. If we had gone into the method
editor from the open window (maybe at a breakpoint) we would have seen our
window instance here.
Stack: Lists the methods in the order they have been called, only during
method execution.

Don't worry about the details, for now you won't need any of them.

THE 6 PARTS OF THE METHOD EDITOR WINDOW.
The top left part of the window (under the menus and toolbars) is the
Variable pane. This is where we set up our variables, with type and initial
values. A variable is a designated place in memory to store temporary data
of a given type, that could be text, numbers, dates, pictures etc. These
values are not stored on disk, that is what we use our database logic to do.
The variables have different scope, determined by the tabs below the
variable list. This scope determines the visibility of a variable. Should it
be visible to just this window, or to all parts of the application. We shall
look closer on scope later.

Top right is the Watch variable pane. This lets us monitor our variables
during debugging.

Middle left is the methods list for the class we are editing. This is a tree
list, with one entry for the class methods, and one for each component on
the window. Both class and component methods can have a number of methods
each.

Middle right displays the currently selected method. This is where we see
the code that we (or in this case the wizard) have created.

Bottom left is the commands list. This shows all the available 4GL commands
in Omnis. We can enter commands by clicking them or by typing them. Omnis
has a syntax checked editor, and it will automatically locate the first
command matching what you have typed so far. Type 'E' and you get Else, 'EN'
is End if, and add a T and you get Enter Data. Be careful not to test this
in an existing method, as what you type may overwrite what is already there.

Bottom right: Depending on the command selected at the right, this will show
the attributes and arguments for this command. This is where we fill out the
details, say the value to assign and which variable to assign it to.

THE METHODS THE WIZARD CREATED.
We are not quite ready to program. Today we are only going to look at the
code the wizard made.

In the method editor for wEmployee, make sure class methods are selected. It
will be if you opened the class methods from the context menu, or
double-clicked the window background. If you clicked a component on the
window (field or button), it will show those methods instead. If so, simply
scroll to the top of the method list (The middle left pane) and open the
class methods by clicking the plus sign in front of it to expand the tree
list.

You will see two methods: $construct and $event. These are special methods
in Studio:
$construct is the method that is run when the window is opened. (Or a menu
installed etc.) We don't have to call $construct from our code, Omnis will
do that for us when we open the window. An open window or installed menu is
called an instance, and $construct contains the code we want to execute when
the instance is created. So for our window, it is natural to perform set up
for our window there.
$event is the method that is run when an event occurs. An event is usually
triggered by a user action. Say they click on a button, tab in to or out of
a field, close a window. All these are events that are reported to Omnis,
which passes them on to the appropriate event handler. This can be $events
for a field or for the window. More on events later.

Notice that both these method names start with a dollar sign. All the built
in methods in Studio do. The $-prefix also means that this is a public
method. A public method is something that is visible outside the instance. A
method without a $-sign is a private method, which means it is only visible
inside the window or component where it was defined, and can therefore only
be used for subroutines. We shall worry about these concepts in detail on
another occasion.

$construct
Click $construct. You will see the method text created by the wizard to the
right. It is a short method, only three lines:

Set current session {TASK}
Do iSqlRow.$definefromsqlclass('sEmployee')
Do iSqlRow.$select()

The first line simply sets the SQL session this window works under. As we
can have multiple open sessions at any time, it is important that the SQL in
this window uses the right one. Remember that we selected that task when we
used the wizard.

The next two lines work on something called iSqlRow. This is a variable,
defined in the variable pane on the top left. The 'i' in front suggests that
this is an instance variable, so click on the Instance tab in the variable
pane. You will see two variables, iSqlRow is one of them. Both are of type
Row. We shall look at the variable types and subtypes later, for now let us
just say that a row is a variable with multiple columns, but only one row.
(If it had multiple rows as well, it would be a list.)

Note: the 'i' prefix is just a naming convention, some developers use them,
others don't. You can call your variable anything you like, but stick to
alphabetical characters and numbers. I shall try to be consistent with my
naming.

So our row var has multiple columns. We need to tell Omnis how many columns
there should be, and the type of each column. We could do that manually. But
in this case that information already exists, in the schema, so we can save
time by simply defining the row from the schema. That is what the second
line does:

Do iSqlRow.$definefromsqlclass('sEmployee')

We call the built in method $definefromsqlclass and send the sql class to
use as an argument. In this case it is our schema, sEmployee. Notice that
the built in method has a descriptive name, $definefromsqlclass means
'define from sql class'. So once this line is executed, our row var will
know the columns and their types as defined in the schema.

To see this, do the following. Close the method editor to return to the
design window, and type Ctrl/Cmd-T or open the context menu and select 'Open
window' to open the window in runtime mode. This will run $construct. With
the window open, open the Catalog (F9), click the Variables tab and then
select Instance from the list on the left. That will list our two instance
row vars. Now right-click (option-click) iSqlRow, and from the context menu
select the first menu line, the one that says 'Variable iSqlRow...'

This will open a window to display the contents of the variable. There is no
data in the row, (unless you put some in after you opened the window), but
you can see the columns are defined. That is what $definefromsqlclass did
for us.

Now close that variable window, click on the runtime window and press
Ctrl/Cmd-T to go back to design mode. (The design window is actually open
behind the runtime one, so closing the runtime window from the close box
will also get us back to the design window.) Open the method editor and the
$construct method again.

The last line of code is: Do iSqlRow.$select()

This tells Omnis to execute the built-in method for selecting data from the
database. This requires a bit of explaining. Remember last time we added
table classes to our library and set them to belong the schemas? We said the
table classes is what holds the SQL logic, while the schemas holds the
definitions. Well, we also said that if we do not use a table, Omnis will
assume we want to use the standard built in methods. The wizard prompted us
for and used the schema name, so that is what happens here. Later we shall
modify this to use the table class, but for now the schema is fine.

Explaining SQL is beyond the scope of this tutorial, so if you are not
familiar with it then please refer to one of the many good books on the
subject. Omnis provides several built-in methods toperform the standard SQL
functions, including the following:

$select: This instructs the database to locate all the rows in a table (or
tables) that matches the where clause (search criteria) added. (Or all rows
if there is no where clause.) It doesn't send them to us, just finds them
and builds a select table so that it can return them later.
$fetch: Retrieves some or all of the previously selected rows from the
database into our application.
$insert: Inserts a new row into the relevant table with the data in our
variable.
$update: Updates an existing row in the database with the data in our
variable.
$delete: Deletes a row from the database.
They are triggered by buttons on our window. We shall cover them in more
detail below.

So, our Do iSqlRow.$select() told the database to locate all rows (since
there is no where clause), in the table Employee. It knows which table
because iSqlRow is defined from the sEmployee schema, which again is linked
to the database table.

This all happens in $construct. So when the window has opened, we have set
the session, defined our row variable, and asked the database to select all
rows so we can start retrieving them.

$event
There is one more class method, called $event. It has two lines:

On evToTop
Set current session {TASK}

We said above that $event handles user actions or other system events. Most
often we want our code to respond to a specific event, so we need to tell
Omnis which one. In this case evToTop, which is reported to the window when
the user clicks it to bring it to top, or if our code does it for them. Note
that the $event class method only handles events on the window, if the user
say clicks a button on the window, that is reported to the button's own
$event instead.

Since this event was triggered by the window coming to front, it means
another window must have been clicked. This window may have changed the
current session, so we set it back to TASK to make sure our code works on
the right one. The alternative would be to set the session in front of every
database related operation.

NEXT:
Now click the plus sign in front of the entry named Next in the methods
list. This is the button named 'Next' in our window, which we used to browse
through our data, one row at a time. That will open this entry and display
the methods for the button. There is only one, $event. Click that to show
the method. This method is a bit longer:

On evClick
 Do iSqlRow.$fetch() Returns lStatus
 If lStatus=kFetchFinished|lStatus=kFetchError
  Do iSqlRow.$select()
  Do iSqlRow.$fetch() Returns lStatus
 End If
 Calculate iOldRow as iSqlRow
 Do $cwind.$redraw()

The method starts with On evClick, which means that the user has clicked on
the 'Next' button.

Then we 'Do iSqlRow.$fetch() Returns lStatus'

Remember that $construct did a $select to instruct the database to prepare
all the rows to send to us. Now we start fetching them into our iSqlRow
variable. Since the row variable only has one line, we only fetch the data
one at a time. The $fetch method 'Returns' a value telling us if the fetch
was successful, if it was the last row or if there are more to fetch, etc.

In the third line we test this return value, and say 'If
lStatus=kFetchFinished|lStatus=kFetchError', meaning if this fetched the
last row, or if it didn't fetch any because of some error. The IF statement
evaluates the argument to either True or False. The code between the 'If'
and the 'End If' a bit further down is only executed if the argument
evaluates as True. If not Omnis jumps to the line below the End If and
continues there.

So what this says is if the fetch didn't fetch anything, then we need to
issue another select and try to fetch again.

That leaves two lines of code:
Calculate iOldRow as iSqlRow.
This simply makes a copy of our iSqlRow called iOldRow. (Which was also
defined as an instance var in our variable pane.) The calculate copies both
the definition and the values. iOldRow will be used by $update to locate the
correct database row to update.

And finally: Do $cwind.$redraw().
This tells Omnis to redraw the (current) window. When we did our $fetch, the
variables displayed in the window got new values. But Omnis does not refresh
the window automatically. This is because we may have a lot of fields on the
window, and several commands that change their contents, and so all those
redraws would cause an ugly flicker as well as slow down the program. If you
were building a list of 1000 rows, you would not want Omnis to redraw the
window 1000 times. Better just do it once at the end. Omnis leaves that
decision up to us.

INSERT:
Select the event method for the Insert button.
There is only one line of code here, in addition to the event filter 'On
evclick'.
Do iSqlRow.$insert().
This calls the built in method to insert a new row in the database with the
values in our iSqlRow variable. Again, since iSqlRow was defined from the
sEmployee schema, Omnis knows which table to insert into, and the columns to
use.

The database itself doesn't understand any of this stuff. What happens is
that Omnis takes our 'Do iSqlRow.$insert()' and then uses the schema
information to build a proper SQL statement for us, and then passes this to
the database. So our $insert would be translated into 'INSERT INTO
Employee(cID,cFirstName,cLastName,cPhone) VALUES (The values to insert),
which the database can understand.

UPDATE:
Again a one-liner: Do iSqlRow.$update(iOldRow). This tells the database to
update an existing row with new values.

Note that this time we send iOldRow as an argument. This holds the original
values of the row (remember we calculated it in our Next method) and is used
to locate the row to update. Now the way this happens is if we defined one
of the columns in the schema as a primary key, Omnis will use only those to
find the old row on. That is why the primary key has to be unique. If we do
not have a primary key, all columns will have to match. That is clearly not
efficient. A primary key is highly recommended.

DELETE:
That leaves us with the Delete button. This has three lines:
Do iSqlRow.$delete()
Do iSqlRow.$clear()
Do $cwind.$redraw()

The first line tells the database to delete the row. Now it now longer
exists in the database. But our row variable still holds the values and the
window still displays them, so we do a $clear() to empty the variable and a
$redraw to refresh the window, so that the user can see that the row is
gone.

So, after this little tour you know how our window does what it does, and
you know your way around the method editor. Next time we shall start
building a window from scratch, rather than using the wizard. This will be
our Task window, which lets you enter tasks and link them to employees.


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

About Omnis web site downloads

The Omnis web site contains many different downloads, either for full
product evaluation or for enhancing your existing Studio products. For
example, the Studio Evaluation is a full working copy of Omnis Studio
version 3.01 for Windows 95, 98, NT, 2000, Linux and Mac, including Mac OS
X. You can download it from our web site now, and upgrade the same software
to a fully licensed copy very easily. The download area also includes: The
Omnis Web Client plug-in for enabling Omnis Studio internet applications is
downloadable for Windows, Linux and Mac for both Netscape and Internet
Explorer; the  Studio Web Server Extensions for your web server
applications; various Tools and Updates; Omnis Studio Externals; Patch
Installers for Omnis 7 and Omnis Studio; and StudioTIPS Lite, the Studio
learning and resource tool from Vencor Software, is available for free
download.

For Omnis Studio downloads, please go to:
http://www.omnis.net/downloads/index.html


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

Heading Lists in Remote Forms
By David Swain, Polymath Business Systems Inc
Email: dataguru@polymath-bus-sys.com
Web: www.polymath-bus-sys.com

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

The "Heading List" for Remote Forms has a lot in common with the "Headed
List" component for Window Classes (note the slight difference in their
names), but there are some significant functional differences. Some of these
differences point to a lower functionality, as one might expect for a remote
form component, while others offer a significant improvement over the window
component counterpart. Let's examine this point by point.

First, the similarities. Each component has a name (which we use to refer to
the field with Notation) and a dataname (which we use to specify which list
is to be displayed). Each component has size and location coordinates (top,
left, height and width), an order property, and the visible, active and
enabled properties.

There are also many appearance and text properties these components have in
common. Horizontal and vertical scrollbars, the full range of 3D effects and
edgefloat properties, and even the ability to drag the field to a different
size (if the edgefloat property is set to one of the kEFposn... constants)
can be set for either. Both component types can accept fieldstyles or can
have their fontname, fontsize, fontstyle or textcolor properties set. We can
even set a special mouse cursor to appear when the mouse passes over the
field (as long as the proper icon page is sent to the Remote Form). All of
these properties have the same names for both component types. Properties
for a windows Headed List field can be copied in the Property Manager and
pasted into a Heading List on a Remote Form and all the above-mentioned
properties will be cloned intact. (Right-click on the Property Manager to
Copy/Paste properties from one object to another; note only the properties
the two objects have in common will be copied, other properties will be
ignored.)

Other properties are shared, but not by name. The names are very similar,
but the properties for the Remote Form (external) component are prepended
with a double colon (::). For example, the "columnnames" property of a
Headed List becomes "::columnnames" for a Heading List. These five
properties are all found in the "Custom" properties tab of the Heading List
component. They are "::columnnames", "::enableheader", "::boldheader",
"::hideheader", and "::multipleselect". Their values are definitely NOT
cloned by Property Copy and Paste from a Headed List.

There might be some other surprises when performing this bit of Omnis Studio
magic. For example, if a Headed List field is colored by a pattern that
displays its forecolor, or using a pattern that combines forecolor and
backcolor, the Heading List that receives the pasted properties will look
decidedly different. This is because a Heading List component ONLY has a
"backcolor" property, not "forecolor" or "backpattern" -- nor does it have a
"bordercolor" property. Also, the "dataname" property of the Headed List
component will not be cloned in this process -- because that variable (no
matter what scope it has) doesn't exist in the world of the Remote Form.

At the risk of appearing to be negative, here are a few other Headed List
properties that are not available with Heading Lists. The point here is only
to make you aware of the differences -- Heading Lists still have a lot to
offer! Most of the "missing" properties are actually "missing" from ALL
Remote Form components since they either depend on having a copy of Omnis
Studio installed on the computer or they could generate excess internet
traffic. The rest are pretty superficial and probably won't be missed. So
there is no "tooltip", "contextmenu" or "helpfile" property; we cannot react
to keystroke, mouse or status events; and there is no support for
drag-and-drop processes. We cannot separately set the text or background
color for the header (it's always gray and uses the same color set in the
"textcolor" property). We have no "showcolumnlines" property, so we can't
turn off the dotted lines that separate the columns. A Heading List field
cannot be made "local" to another field. Heading Lists are also display-only
fields, so there is no "maxeditchars" property and we can't enable a cell
for data entry. (If you need to have data entry into a list on a Remote
Form, check out the Data Grid Remote Form component to be covered here in
the next issue.)

A couple of properties are just named differently in a Heading List as they
are in a Headed List. The Headed List "designcols" becomes the "::colcount"
property for a Heading List. This is used to set the number of columns the
field will display. The "canresizecolumns" property of a Headed List
translates to the "disableresizecolumns" property of a Heading List -- the
difference being that they are the Boolean opposite of one another. So if
you want the user to be able to resize the columns, this property has to be
set to kFalse on a Heading List, while it is set to kTrue on a Headed List.

On the other hand, some things just work differently for a Heading List.
These are primarily due to the limitations of the environment in which a
Remote Form finds itself. For example, the dataname assigned to a Heading
list MUST be an instance variable list. No other list variable will do! This
should not be surprising since the only variables that exist on the browser
side are instance variables, so ALL data-related fields on a Remote Form
MUST represent instance variables.

There is no "calculation" property. The Heading List displays the columns
from its associated (instance) list variable in the order they are defined.
If it is necessary to display composite values in certain columns, to show
columns in other than their defined order, or to not show columns that occur
between columns intended to be visible, some additional work must be done.

First, we have some say in how the list is defined. Whether we are using the
native datafile or a SQL database, we can use the actual column names from
the database to define the columns of our list variable -- and we can even
use "listname.columnname" notation to display specific cell values from the
list elsewhere on the form without having to declare instance variables for
each column. (For native Omnis, use the UNqualified fieldname -- the name of
the File Class variable without the File Class name prepended.) So we can
define the list in such a way as to show only those columns we wish to
display -- and in the preferred order. With native Omnis datafile
techniques, this is simply the order in which we specify the columns in the
list definition. Using SQL techniques, the most convenient technique might
be to use a Query class to specify and order the columns to be included. We
can then use either the "Build list from file" command (native) or the
"$select()" and "$fetch(kFetchAll)" methods (SQL) to populate the list.

Displaying composite values is another matter. For this we must first create
an instance variable for each such column, then we must add each line to the
list calculating each composite value in the process. For the native
database, this can be done in a single loop where the records are read into
the CRB, processed and added to the list. (The list may require subsequent
sorting if no appropriate index can be followed.) For a SQL database, record
images must be selected and fetched into a scratch list (an ORDER BY clause
can be used to avoid additional sorting within Omnis Studio), then the
contents must be processed a row at a time and added to the final display
list variable. The display list must be defined from instance variables, so
the contents of each variable must be calculated. (This can be done in the
parameters of the $add() method for the list.) Corrections or additions to
these techniques are welcome if anyone has discovered any, by the way.

We set the width of each column separately using the "::columnwidths"
property. While we can allow our users to drag these columns to different
widths, we cannot do so at design time. Rather, we must specify a
comma-delimited list of column widths just as we supply a comma-delimited
list of column names. To obscure a column in a simple list, we can set its
width to 1 pixel (0 is not allowed and will automatically be reset to the
default of 100). But the clever and determined user will notice the double
line thickness displayed by such a column and will find a way to open it up
if we allow the columns to be resized. Best to obscure a column by not
including it in the first place.

While we cannot set a backcolor or separate textcolor for the heading of a
Heading List, we CAN set the alignment of each column and an independent
alignment for its associated heading text using the "aligncolumns" and
"alignheadings" properties. These both work in the same way. We simply
specify a letter for each column, creating a string that has a character for
each column. L = Left, C = Center and R = Right. If we make the string too
short, Omnis Studio will automatically fill in with enough Ls to cover all
the columns. Since there is no other way to specify alignment for this
component type, Left is the default -- even if we use a fieldstyle that
normally would force another alignment.

The list of events to which a Heading List can respond is more limited than
that for a Headed List. There are only five: evBefore, evAfter, evClick,
evDoubleClick and evHeaderClick. With no status, keystroke or mouse events
and no drag-and-drop, there isn't much else that a list can do. Remember
that we must enable specific events for Remote Form components in addition
to creating an "On" block in the fields $event() method.

Configuring a Report Data Grid from a Remote Form Heading List

In the last issue we discussed how to create a report with the report Data
Grid component that could print the contents of any Headed List field on a
window. We can do the same thing with a Heading List on a Remote Form with a
few modifications to the process. (Get a copy of that issue so you can
follow along since the bulk of it won't be repeated here.) Here are the
issues we have to resolve:

First, Remote Form components don't support the notational shortcut
"$cfield". In fact, we can't even access the properties of the component
using "$cinst.$objs.fieldname.propertyname". On the other hand, we can't
modify such properties either, so it's OK to access them at the class level.
Omnis Studio does allow us to use "$cclass().$objs.fieldname.propertyname",
so that's what we will use in our $getcolprops method.

Next, there is no "calculation" property from which to extract calculations
for columns. That's OK, though, because the calculation for each column is
simply the name of the variable in the list definition for that column
number. We can access this using " listname.$cols.[colcount].$name" in the
For loop where we process the other column properties. ("colcount" is the
variable controlling the For loop.) This also frees up the calcSource
variable from last issues example.

A third issue is that we no longer have a $getcolumnalign() method for
retrieving the alignment for a column. This is fine, though, because we have
an "$aligncolumns" property containing "L"s, "R"s and "C"s that we can parse
like we did the column names and widths. (We deal with those as before.) We
can recycle the old "calcSource" variable into the "alignSource" variable
and use it in a similar way, bearing in mind that there are no delimiting
commas, but that each character represents the alignment for a separate
column. If we also bear in mind that kLeftJst = 0, kRightJst = 1 and
kCenterJst = 2, we can reduce our code in the loop for parsing column
alignments to:

Calculate columnalign as pos(mid(alignSource,1,1),'LRC'
Calculate alignSource as mid(alignSource,2,len(alignSource))

The column widths we work with will not be those manually set by the user
since we don't have access to the instance properties, but that's OK because
our columns are all set to "autosize" and the width we provide will only be
the minimum width.

Given a list instance variable named "memList" and a heading List object
with the same name, the resulting $getcolumnalign() method becomes:

Begin reversible block
    Set current list propList
End reversible block
Define list
{columnheader,calculation,columntype,columnwidth,columnfontstyle,columnalign
,columntextcolor}
Calculate alignSource as $cclass().$objs.memList.$aligncolumns
Calculate headerSource as $cclass().$objs.memList.$::columnnames
Calculate widthSource as $cclass().$objs.memList.$::columnwidths
For colcount from 1 to $cclass().$objs.memList.$::colcount step 1
    Calculate columnheader as
pick(pos(',',headerSource)<>0,headerSource,mid(headerSource,1,pos(',',header
Source)-1))
    Calculate headerSource as
mid(headerSource,pos(',',headerSource)+1,len(headerSource))
    Calculate calculation as memList.$cols.[colcount].$name
    Calculate columnwidth as
pick(pos(',',widthSource)<>0,widthSource,mid(widthSource,1,pos(',',widthSour
ce)-1))/sys(87)*25401
    Calculate widthSource as
mid(widthSource,pos(',',widthSource)+1,len(widthSource))
    Calculate columnalign as pos(mid(alignSource,1,1),'LRC'
    Calculate alignSource as mid(alignSource,2,len(alignSource))
    Add line to list
End For
Quit method propList

Our method for printing the report is a bit more complex because we will be
printing to the "Printing Control" Remote Form component. This requires
printing the report to a binary variable in memory, then transferring the
report data to the $reportdata property of that component for transport to
the remote browser. We can put the code in a simple pushbutton field on the
Remote Form. Without going into the details (a topic for another time), here
is that code (the Printing Control component is cleverly named
"printControl...):

On evClick
    Calculate $cdevice as kDevMemory
    Do $root.$prefs.$reportdataname.$assign(reportData)
    Do $root.$prefs.$paper.$assign(kPaUsLetter)
    Do $root.$prefs.$orientation.$assign(kOrientPortrait)
    Set report name headListPrint
    Print report {(memList,$cinst.$objs.memList.$getcolprops())}
    Do $cinst.$objs.printControl.$reportdata.$assign(reportData)
    Do $cinst.$objs.printControl.$action.$assign(kFPriSendToScreen)


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

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.
(c) Copyright Raining Data, Inc., and its licensors 2001. All rights
reserved.
Omnis(r) is a registered trademark and Omnis 7(tm), 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