[Omnis-Newsletter] Omnis technical news

omnis-news-admin@omnis.net omnis-news-admin@omnis.net
Wed, 14 Nov 2001 15:53:04 -0000


 November 14th, 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 continues his tutorial
by adding the basic data entry functions and buttons in the task window. In
the second article, David Swain looks further into the Component Store and
describes how you can modify the default class templates to save even more
time when developing with Omnis. 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 12: Implementing the data entry interface.
-The Omnis News
-BusinessBase: An Omnis Success!
-Component Store Templates and Class Defaults
-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 12: Implementing the data entry interface.
By Geir Fjaerli, Sunshine Data
Email: geir@sunshinedata.net
Web: www.sunshinedata.net

Hi, and welcome back to our Omnis Studio tutorial.

Here is our work so far:
Part 1: Hello World, our first little Studio application
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.
Part 7: What did the wizard build, and a tour of the method editor.
Part 8: The logon window and object class.
Part 9: The task window.
Part 10: The task window continued.
Part 11: Designing a proper data entry interface.

You should have those handy, and your "tasks.lbs" library file.

As usual, I strongly suggest that you refer to these parts if you haven't
already, or if you feel uncertain about the details. From part 5 onwards,
each new part will be based 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.)

In our last issue we discussed the issues of creating a proper data entry
interface. They included modal vs. modeless interfaces, locking and user
access. Basically, I asked you to consider the issues up front, and then
decide on the proper implementation. Please reread that part if you are
unsure of its contents.

As we go on implementing data entry controls for our wTask window, we will
have to decide on which strategy to use. It may shine through last issues
comments that I favour a tight control of the user actions, in other words a
modal interface. I have had the privilege of working on several projects
over the 14 years I have been using Omnis (and other tools before that),
trying both approaches. I really like modeless interfaces, you can make the
quite stunning, I am just not sure you can trust them...

Anyway, whether you end up using one or the other, it is important that you
know how to design them both. As we have already got a modeless model for
our wEmployee window, this time we will go for a modal interface. So this
will be our project for today. We will leave the locking and access issues
for later.

But first let me clear up one issue that may have confused some of you: Our
logon window starts the database session for us. You may have discovered
that sometimes even if it did report a successful logon later it did not
work. This is because the window contains code to stop the session in its
$destruct method. $destruct is executed when an instance is closed, so if
you have closed the logon window after logging on, then you would also close
the session. So if you do not want to have to keep the logon window open all
the time, then open it in design mode, double click to see its methods, and
in the $destruct method simply remove the "Do iSession.$logoff" line. Note
that if you do, you will explicitly have to log off the session by closing
Omnis. If you simply restart the application, you will end up with multiple
"hanging" sessions. Later we will change our application so that it takes
care of our session properly.

THE DATA INTERFACE.
Our wTask window currently has all the fields on it, including the entry
fields, radiobuttons and drop down/pop up list. Our next step is to add
buttons for the data interface. So what buttons are needed? Well, let us
list the main functionality required for our window.
1. Insert tasks. We need to be able to insert a new task for an employee.

2. Update tasks. We want to change the details of a task, e.g. when a task
is completed with want to set its date done.

3. Delete tasks. We may also want to allow the user to delete a task record.
Arguably, to keep track of tasks assigned and completed we would not delete
any of them, but for now let's assume we do.

4. Navigate tasks. We need a way to find a given task record, and to
navigate between them. The are two main approaches to this:
a. A find function that finds that first matching record for your search
criteria, and the "next" and "previous" buttons to navigate backward and
forward in the data.
b. The find function creates a list of all matching records, and we click in
the list to navigate to a given record.
The two can be combined, so we could have both a list and next/previous
buttons. But here we run into a problem: Many databases do not support the
next and previous type of navigation. In SQL databases that is usually true.
If you look back on the wEmployee window that the wizard created for us, you
will notice the absence of a "Previous" button. It does have a next button,
which simply fetches the next row of the result set after the select.
Instead we can fetch all rows at once into a list. So we will stick to the
second approach.

So we need buttons for Insert, Update and Delete, plus some sort of find.
This looks like our Employee window, but there is one important distinction.
In our modal window the buttons should not really do an insert or update,
instead they should just open the window for edit and prepare for the update
which happens after the user has said OK. So we will change the names of the
buttons to reflect that. We therefore end up with the following buttons:

1. New. This button prepares the window for inserting a new record. So it
clears any old contents, and opens for data entry. Actually we may want two
"New" buttons: Sometimes we may want to enter a completely new record,
sometimes we may want to duplicate an existing one. So we will make two, one
called "New" and one called "Duplicate".

2. Edit. This will prepare the window for editing an existing record. The
user can edit the details which will then be saved as they say OK.
The New, Duplicate and Edit functions just prepare the window for data
entry. So we need a way to confirm or discard the changes. This is done
through two special buttons called OK and Cancel. (The actual text on the
buttons may differ, say "Save" and "Discard".) Standard interface rules say
that the OK button should map to the Return/Enter key and the Cancel button
to the Esc key (Command-period on Mac).

3. The Delete function is different. There is no editing required here, so
the Delete button actually performs the delete immediately. We should
however warn the user that they are about to perform an irreversible action,
and give them a chance to change their mind.

4. Finally, our find/list function. We will implement this as a "Find
button", to show the resulting list in a separate tab on the window. So we
will need to add a tab pane to the window, and move our fields inside it.

ADDING THE TAB PANE.
So, let us start by adding the tab pane. Open the wTask window in design
mode. Unfortunately we cannot drag the tab pane in and have it contain the
existing controls in one operation. So we have to add the tab pane first,
and then drag the other controls inside it. You should therefore enlarge the
window first so you can add the tab pane below the fields. Drag a tab pane
in from the Component Store (F3/Cmnd-3 to open the store) and resize it to
make it big enough to hold all the other controls. The tab pane will
initially have two tabs, which is what we want.

Now select the other controls by dragging a selection rectangle around them,
and drag them onto the tab pane. When the cursor enters inside the
boundaries of the tab pane a rectangle will appear to indicate that we are
inside it. Release the mouse button to place the controls on the first page
of the tab pane. You may need to adjust the size of the tab pane so that all
the controls are visible.

Now drag the tab pane up into its proper position. You should leave some
room at the top of the window (above the tab pane) for the navigation
buttons.

The tab pane is basically working already. Try to open the window in runtime
(Ctrl/Cmnd-T to toggle between runtime and design mode) and click the tabs
to see that they are indeed functional without further programming. We do
however want to make a couple of small adjustments. First is to name the
tabs. So in design mode make sure the first tab is selected, and open the
Property Manager (F6/Cmnd-6). You will notice that it now has five panes,
the last one is named Pane. Select that one, and enter Details as the tab
caption. Go back to the window and select the second tab, and set its tab
caption to List in the same way.

That's it for now, the tab pane also has a number of other properties to
adjust its appearance, try them out if you want to.

MAKING THE WINDOW MODAL.
When you tested your tab pane a few seconds ago, you probably noticed that
the window was enterable. In our modal interface the window should initially
be non enterable, so we need to change this. We do that in the Property
Manager for the window class. So while the window is in design mode, open
the Property Manager and select the Action tab. Set the modelessdata
property to false. Now try to open the window in runtime mode again. You can
no longer click into a field and enter anything in it.

You will notice another effect as well: Our radio buttons are grey. That
indicates that they are not enabled. But as they contain vital information,
we may not want them to be grey, as it makes them hard to read. So let us
change that right away. Open the window in design mode again and select the
radiobuttons. Omnis allows us to change the properties of multiple controls
at once, so you can select all four radiobuttons. Note that since they are
inside another object (the tab pane) if you want to drag a selection
rectangle around them you have to press the Ctrl key first, or else you will
select the tab pane. Open the Property Manager and select the Appearance
tab. Set the nogray property to true. Now the radiobuttons will stay black,
even if they are disabled.

The next step is to add the buttons. We will start using normal pushbuttons,
then in a later issue we will replace them with a proper toolbar. To make
the transition easy, we will put all the code in class methods and call them
from the buttons.

So start by adding four buttons in a horizontal row to the top of the
window. Open the window in design mode, and drag a button in from the
Component Store. Repeat this until you have four buttons, or if you like you
may duplicate the first button as described in a previous part of the
tutorial. Then select each of the buttons and in the General tab of the
Property Manager set both the name and the text property to Find, New, Edit
and Delete respectively. The name property is used in our code to identify
the control, the text property is the text that the button displays to the
end user.

Next add the OK and Cancel buttons. This time add them inside the tab pane,
on the bottom of tab one. So select the Details tab, and resize the tab pane
vertically so there is room for the buttons. Then in the bottom right corner
add two pushbuttons, name the left one Cancel and the other OK. These
buttons will be special, because they will only be visible when in entry
"mode". More on that later.

Finally resize the window itself if needed. You may want a suitable border
area around the tab pane. Note that both Apple and Microsoft have published
so called Interface Guidelines that tell you what fonts to use, what
distance to have between fields etc. You may want to consult these before
you finalise your windows. This can easily be done at a later stage,
however.

THE DATA ENTRY LOGIC.
Back to our buttons. We now have three "modal" functions in the window:
Find, New and Edit. The fourth, Delete, is not, as we have discussed earlier
in this chapter. But for the three others the same principle will apply: The
user selects the function, enters some information and hits OK to execute
the desired operation (or Cancel if they change their mind.)

Now for modal interfaces like this, Omnis has a built in command called
Enter Data. It makes the window enterable, and pauses the methods while the
user enters their information, and then continues after they click OK. So
our method would look something like this, in pseudo code:

Prepare for the data entry
Enter data ;; The method will stop here while the user enters their data
If flag true
    Perform save
End if

A similar command exists for native finds.

Sounds good? Well, unfortunately we are not going to use the Enter Data
command. The reason is it is global, and does not work well if we have more
than one window open. (It was written before that was an issue.) So we
archive that under "legacy" stuff, and roll our own.

Our custom entry logic will be similar to the Enter data, with one important
distinction. It will not pause method execution. Instead we will set the
window enterable, and let the method finish. This means our OK button has to
trigger a separate method to make the window not enterable and save/find the
data as required. So again in pseudo code we will have something like:

Under the Find, New or Edit button:
Prepare for data entry
Make window enterable

Under the OK button:
Make window not enterable
Do save or find stuff

Not really much more complicated, but it does introduce one problem that we
would not have with Enter Data. In the first example the New or Edit method
continued after Enter Data, so it knew what to do. In our second example the
post entry logic is triggered by the OK method. So it needs top know whether
we are in Edit, New or Find mode, as it will trigger different actions.

We will use a "flag" to remember which function we selected. This flag will
be an instance variable in the window. We use an instance variable because
it is unique to the open window, and because it is alive as long as the
window is open. So if we later open multiple windows, they will each have
their own flag.

Note: The word flag in programming means an indicator that other logic can
read to know the status of something. Omnis has a built in boolean flag,
often referred to as "the flag" or #F, which is set by a number of commands.
(Boolean means it can either be true or false.) Our flag can hold more
values. Therefore, the term flag may not be 100% appropriate, it is more
like a set of flags, our variable being the flag holder...

To add the variable, open the window in design mode, and double-click to
open the method editor. As described in an earlier chapter, the variables
are defined in the variable pane at the top left of the method editor
window, not in the methods themselves. Click on the Instance tab to add
another instance variable. It already has three, as our row variables and
employee list are already defined there. So click the fourth line and add a
variable named iEntryType. We will use numbers to identify the types, so
make it a Number, subtype Short Integer. Our types and their numbers will
be:
Find = 1
New = 2
Edit = 3
None = 0

Now numbers are a bit hard to remember, so we may want a more descriptive
way to reference them. To do this we will add named constants for each of
them. A constant is a named value that cannot be changed. Omnis however,
does not have user defined constants (it does have a lot of built in ones),
so we have to use variables to act like them. Since the types will be common
for all windows, we can make them global. So we will use task variables for
this. (They are only global within a task, but since we only have one task,
that will do.)

So go to the variable pane again, and this time select the Task tab. Again,
there is a variable there already, for the session name. (Which is not
working, but we'll address that issue in a later chapter.) This task
variable was actually inserted by the logon wizard. This means that the task
variables panes will show all task vars for a given task, regardless of
which class we were editing when we defined it.

Omnis prefixes the built in constants with a "k", we will prefix our own
with a "tk", so that we know they are task variables but are meant to be
used as constants. This way, even if we can change them in our methods, we
know that we are not supposed to. So add the four types and name them
tkNone, tkFind, tkNew and tkEdit. They will all be Numbers and subtype Short
Integer, just like our iEntryType variable. Variables as we have said
earlier, can have an initial or default value, we will use this to define
the value of our "constants". So in the "Init val/Calc" column add 0 for
tkNone, 1 for tkFind, 2 for tkNew and 3 for tkEdit.

CALLING METHODS AND PASSING PARAMETERS.
Having set up our "flag", we will start creating the methods. As discussed
above there will be methods to set up the data entry (pre entry) and methods
to perform the appropriate action after an OK (post entry). Apart from
setting our flag to a different value, the pre entry methods are basically
the same. They set the flag to a value and make the window enterable. So why
don't we make them a single method, taking the iEntryType value as a
parameter?

Now for those of you who are fairly new to programming, here is what
happens. A method can call another method to perform an action. This way
common code only needs to be coded once, and can be used in several places
around our application. The syntax varies with the context. If it is a
subroutine (private) call inside a class, we use the following: Do method
MethodName (parameters)

If it is a public method being called from the outside, we use a message:
Do Instance.$MethodName (parameters)

As we can see there are a few distinctions between the two. First of all,
the verb for calling a private method is "Do method", for a public message
it is simply "Do". Secondly, for a message we have to prefix the method name
with the instance we want to call into. For a private method we don't need
that, since it can only be used inside the class anyway. And thirdly, as
discussed before a public method must have a $ prefix in its method name.

Now if the called method uses a value from the calling method, we can send
that along as a parameter. To do that, we set up the parameter in the
variable pane while the method to be called is selected. As with all
variables the parameter takes a name, type and a subtype depending on the
type. We can also set up an initial value to be used if no value is passed
from the calling method.

Now in the calling method we add the parameter value to the method/message
call in brackets, separated by commas if there is more than one. So if we
want to pass a name and age to a subroutine, we would set up to parameter
variables in the subroutine method, of type Character and Integer
respectively. Then our method call would be something like: Do method
Subroutine ('Peter',25)

THE PRE ENTRY METHOD.
So using the technique discussed above, let us make a subroutine for the pre
entry logic. Open the method editor for the wTask window and select class
methods. Right click on class methods an select "Insert new method". Name
the new method PreEntry. While the method is selected, go to the variable
pane and select Parameter. Add a new parameter variable named pEntryType,
type Number and subtype Short Integer.

Now in the method add the following method lines:

Calculate iEntryType as pEntryType
Calculate $cinst.$enterable as kTrue

These method lines are fairly easy to understand. The first one simply sets
our iEntryType flag to hold the value passed in from the calling method.
That value will be one of our type constants, tkFind (=1), tkNew (=2) and so
on. So if you pass in tkNew and check the value of the variable iEntryType
after this method it will be 2.

Why do we calculate iEntryType as pEntryType? Why can't we simply use
pEntryType? The reason is that parameters are local to the method they
belong to, and act like local variables. So as soon as the method stops, the
parameter and any local variables cease to exist. So we need to store the
value in a persistent variable. Our instance variable will exist as long as
the window is open, so it is just what we need.

The second method line sets the window as enterable. $cinst means the
"current instance", in this case the open window that we are currently
inside. We could have said $cwind, the "current window", that would have
been the same thing in this case. $cinst however works for all instances, so
it will be the current menu instance, toolbar instance etc depending on the
instance that we are inside.

You will remember that we set the class property modelessdata to false to
set the initial state of the window to non enterable. So this is a bit
special in that the design (class) and runtime (instance) property have
different names. Mostly they have the same name, for instance $left is both
the initial position set in the class, and the runtime position of the open
window. Old versions of Studio did not have a runtime property for
enterable, so that may the reason why they are different.

THE CALLING METHODS.
Now that we have the generic subroutine, let us create the methods that
calls it. We will need three of them, for Find, New and Edit. Since these
method will eventually be called from the outside, we will make them public
by using the $ prefix in the method name.

So using the same technique as above, (right click on class methods and
select "Insert new method"), add three new class methods. Name them $find,
$new and $edit.

They will have one thing in common: They will all call our PreEntry method,
passing the type of entry. In addition they will do whatever action is
specific for this entry type. For instance, the New and Find methods will
clear any existing information in the window first, while the Edit method
will not.

So here are the methods for each:
$find:
Do iTaskRow.$clear()
Calculate iEmployeeList.$line as 0
Do $cinst.$redraw()
Do method PreEntry (tkFind)

$new:
Do iTaskRow.$clear()
Calculate iEmployeeList.$line as 0
Do $cinst.$redraw()
Do method PreEntry (tkFind)

$edit:
Do method PreEntry (tkEdit)

That is all there is to them. We used "Do method" here because we are
calling from inside the window to a subroutine. Again, the methods are
really simple. The Find and New methods start by clearing our iTaskRow
variable. This is the row variable that all our window fields are defined
from. So by clearing this we reset the fields and radiobuttons. We also set
the "current" line of the employee list to zero, to make sure no employee is
selected. Then we redraw the window to reflect the changes.

Next we need to call these methods from our buttons. To add the calls to the
buttons, navigate to the $event method for each in the method editor, or if
you like you can close the method editor and then double-click the buttons
in the design window to get to the event methods directly.

The methods will look like this:
For the Find button $event:
Do method $find

For the New button $event:
Do method $new

For the Edit button $event:
Do method $edit

You will notice that here we used Do method to call a public method. That is
OK, as long as we call it from inside the class Do method will work.
Strictly we don't need the $ prefix at all here, as mentioned above we have
added it because we expect to call these methods from outside the window
later.

Feel free to test your buttons at this stage. They won't do much, their only
task is to set the entry type and set the window enterable. But it is a
start.

In our next chapter we will continue with the data entry logic. We still
have a lot of things to do.
1. We must add the post entry logic, triggered by the OK button.
2. We must control our interface better. At the moment there is nothing that
stops the user from pressing the New button, and then, while they are
entering a new task, pressing the Edit or Find button. That of course is not
good, so we need to control the access by enabling and disabling the buttons
as appropriate.
3. We must implement the Delete function.
4. We need a list, on our second tab, to show the result of the find.

And when this is done, we will create toolbars and menus, we will clean up
our logon functionality and more. So we have enough work to keep us busy for
quite a few chapters. I hope you will all follow me in the weeks to come.
Your comments are welcome!

Until then, take care!


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

The Omnis News

You can always read the latest Omnis news on our web site: this month we
announced the availability of Omnis Studio 3.1. There's also a 'Featured
Site', which focuses on an Omnis developer and their products: this month
it's the turn of German developers Opix. Plus there's a write up of the
Systems Trade fair in Munich, and there's useful Omnis development tips. Go
to the news now, and Bookmark the page while you're there so you can stay in
touch with what's going on in the Omnis world.
www.omnis.net/news


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

BusinessBase: An Omnis Success!

The Omnis web site has many success stories that detail companies who have
used Omnis to create industry-leading applications. We have just added a new
success story that features BusinessBase, a successful Dutch manufacturer of
business software and CRM solutions. Go to our web site to look at our
developer success stories:
www.omnis.net/successstories

(Note: if you are an Omnis developer and would like to be featured in the
Omnis Success Stories, please contact the Omnis webmaster:
webmaster@omnis.net.)


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

Component Store Templates and Class Defaults
By David Swain, Polymath Business Systems Inc
Email: dataguru@polymath-bus-sys.com
Web: www.polymath-bus-sys.com

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

In my last article, we began to examine ways in which we can modify the
contents of the Component Store - specifically, how we can modify default
field and background component properties and methods for GUI classes. There
are still many more timesaving modifications and additions we can make to
the Component Library.

Templates

A "Template" is a Component Store class that is copied directly from the
Component Library to our current design library when we drag its icon from
the Component Store window to the Library icon or Class Browser. Whatever
property values and methods are contained in that class in the Component
Library are the default contents of each new class introduced using this
template.

The name of the class is just one of these properties, but the name is
treated in a special way. If a class in the design library already uses the
name of a newly introduced template, Omnis Studio appends a number to the
end of the name since it must be unique. It begins using a "1", but
increments that number if the resulting name is also used until it finds a
number that results in a unique name.

The Component Store Type property (discussed in the last article in this
series) of such a class is "Template". These classes are represented in the
Component Store window in the "Wizards and Templates" pane for their class
type. A few template classes are contained in the Component Library as
shipped, but the real intention for this Component Store Type is that we
introduce our own template classes as our work with Omnis Studio matures.

Suppose we have a few window and menu classes we find ourselves using in
library after library. We can add these classes to the Component Library by
dragging them to the Component Library using the Library and Class Browsers
and setting their Component Store Type to "Template". They will appear using
the default icon for their class type, but we can specify any icon we wish
by using the "componenticon" property for that class in the Component
Library.

It is important to make sure that the Component Store Type of each such
class added to the Component Library in this way is set to "Template".
Classes in a design library retain the Component Store Type of the Component
Library class from which they originated, and they continue to retain this
if added back to the added back to the Component Library as well. If they
were originally created from a "New Class Default" class (explained later in
this article), this can be a problem. If they were spawned using a Wizard,
they may well have a Component Store Type of "Hidden" since Wizards often
use hidden classes as part of their processes. Classes intended as templates
will not appear in the Component Store window if their type is "Hidden".

So why would I go to the trouble of adding Templates to the Component
Library?

The Component Library is a convenient place to store commonly used classes.
We can drag them directly from the Component Store window without having to
open another library, which could have complications in some cases. A
developer working at a company that only has one Omnis Studio application
may not have much use for this feature, but an active developer of custom
applications who often takes on new projects for many different clients may
find this invaluable.

Here are some uses I make of this feature. Perhaps it will inspire you and
help you find timesaving uses of your own.

File/Schema Class Templates

There are many data descriptions I find myself using again and again on
different projects. Certainly there are variations from one project to
another, but most of the variables about people, products and certain kinds
of transactions appear over and over again in my work. So why not just have
generic versions of such classes (with a few common variations) readily
available in my Component Store instead of having to seek out specific
instances of them in older project libraries?

Query Class Templates

There may also be a few commonly used views of commonly used Schema classes.
I can have these at my fingertips as well.

Code Class Templates

There are certain maintenance methods I use frequently that I have collected
into a set of Code classes. Instantiation is not an issue with these
processes (I usually launch them directly from the Method Editor using
Control/Command-E) and they are not generally deployed in my finished
applications. But I can save rebuilding these methods from scratch by
storing them in appropriately named Code classes in my Component Library. I
can then simply drag them out as needed and destroy the class in my design
library if I want when I no longer require those methods.

Menu Class Templates

Omnis Studio ships with a "File Menu" template in the Component Library. I
modify this to my liking and then add an "Edit Menu", a "Record Menu" (I
used to call this one "Commands"), and a few others. These are all written
as generically as possible so that they can be used in the broadest range of
circumstances. Some menus are intended to be installed on the main menu bar
while others (like the "Record Menu") are intended for use on the menu bar
of a window class.

Toolbar Class Templates

Most of my Menu class templates have Toolbar class template counterparts.

Object Class Templates

I have developed Object classes for various purposes that I use in many
libraries. There are "function repositories" for special "math functions",
"string functions", "list functions", "html functions" and many others.
There are "helper objects" for managing windows, lists, reports and other
things. And there are "data objects" associated with most of my File classes
as well as an object intended as a superclass for the data objects. When
introducing class templates that rely on the existence of other classes in
the design library, it is important to bring them from the Component Store
in the proper order. In this case, I must first use the File class
templates, then introduce the data object superclass, and finally add the
specific data object classes for each File class. In this way, references to
"superordinate" classes are maintained.

Table Class Templates

I treat these in the same way as "data object" class templates. There is a
superclass with some generic methods and then there may be subclasses for
many of the Schema class templates. The Schema classes and the Table
superclass must be introduced to a design library before the
Schema-associated subclasses are added.

Window Class Templates

Over the years, I have developed a number of specific window types for the
kinds of work I most often do. These include basic entry windows, list-based
entry windows, list-based browsing windows, custom dialogs and many others.
I have added these to my Component Library, generally with names of the form
"Generic ****". Many of these windows contain status bars and are set up to
expect my "Record Menu" and "Record Tools" templates. They may even include
code in their $construct() method to modify the installed instance of those
menu and toolbar classes to add or remove tools or menu lines as appropriate
for the use of that window. (A subject for another article to come soon!)

These window class templates also contain a significant number of generic
methods for the various operations that can be performed using that window
type. Reference to a data class may be passed to the $construct() method as
a parameter and then passed internally to an instance variable, or it may
simply be left empty for me to specify by hand. $event() and $control()
methods are included with code for managing events normally expected for
that window type. The $control() method even passes parameters to a "status"
helper object that converts tooltip content to status pane text when the
mouse passes over a field.

I also include a window superclass to manage the overall appearance
properties of these windows for consistency throughout an application. It is
only necessary to remember that the superclass, the menu and toolbar
classes, any object classes used and any special System Table formats
required must be introduced to my design library before a specific window
template is added.

Report Class Templates

Certain basic reports associated with basic File or Schema class templates
are stored in my Component Library. Again, these must be added to a design
library only after the File, Schema, Table or Object classes on which they
rely and the System Table classes on which their components rely for input
masks, field formats and fieldstyles have been established.

New Class Defaults

A "New Class Default" is a special case of a Template in our Component
Library. The property values and methods contained in a new class default
are the default contents of each new class of that type introduced to our
design libraries using either the "New" command of the "Class" menu or the
class icon from the first ("Class Defaults") pane of the Component Store
window. If we choose, we can open the Component Library and modify those
contents to better suit our programming preferences.

The name of such a class in the Component Library (as shipped) is "New
****", where the "****" is the name of the class type. So "New Window" is
the name of the new class default for window classes. This name is also the
default name of the copy introduced into our design library as with any
other template class.

These classes in the Component Library have their Component Type property
set to "New class default". Only one such class exists in the Component
Library (as shipped) for each type of class, but Omnis Studio does not
prevent us from adding more of them. The problem is, if we do add another
class with its Component Type set to "New class default", only the one
"highest" in alphabetic order will actually be used as the new class
default. The other will only serve as a template. So if we have two
Component Library window classes set to "New class default" named "New
Window" and "New Window alt", "New Window alt" will be the actual default
new window class. If they are named "New Window" and "A new window", "New
Window" is used as the default.

A "New Class Default" appears in the "Class Defaults" pane of the Component
Store window with the generic icon for its class type. In the "Wizards and
Templates" pane for its class type, it appears using an icon that includes a
large check mark on it to indicate its special status as the default class.

We have to be on guard for accidents regarding additional "New Class
Default" classes since classes intended to be "Template" classes dragged to
the Component Library will default to "New class default" if they were
originally created from a new class default.

There can be only one "New Class Default" for each class type, so we should
simply modify the one that exists if, indeed, we wish to modify one at all.
I say this because there may not be any general modification we might want
to make to certain default classes. A menu class, for example, will be
different every time and the base property values work fine for me. On the
other hand, there are a few I would change for my own work, so I present
them here.

New File

I use the native Omnis datafile and associated techniques in my work quite
often and there has always been one thing about a default File Class that
has always irked me from the early days of file modes right up to Omnis 7:
that the default "filemode" property is set to "kReadwrite". For purposes of
multiuser record locking, I prefer to default my non-Memory File Classes to
"kReadonly" and reversibly set it to "kReadwrite" during a method where
read/write access is required. I can now make this adjustment to the default
"filemode" in Omnis Studio.

New Window

I have found myself constantly changing the size, background color and other
properties of windows created from the "New Window" default class, so I
simply change these values in the default class itself to ones more
appropriate to my work. I also add $event() and $control() methods to the
default class since I use these in more windows than not.

If I preferred to work primarily with modal windows, I could set the
"modelessdata" property of the default to "kFalse", but the new $openmodal()
method of a window class (introduced in version 3.1) is perhaps a more
elegant way to handle this now.

New Toolbar

Whenever I use a toolbar class, it usually contains at least two or three
button tools. If I require fewer than this, I usually add them dynamically
to my "Record Tools" instance (topic for the next issue) rather than clutter
up my library with a separate toolbar used for one tool in one place. So to
shorten the process of creating a new toolbar class, I modify the "New
Toolbar" default class to contain at least two button tools. This at least
saves me the effort of dragging out the first couple of tools for a new
toolbar class.

Wrapping Up

This should give you some helpful hints on how you can take advantage of the
Component Store technology given to us in Omnis Studio. It was intended for
us to expand upon in this way, not simply to use as shipped! I apologize
that there is no code given in this article, but the use of the Component
Library is at issue here, not specific code snippets. You are encouraged to
use it how you see fit!

ALERT! Version 3.1 System Tables Update

#MASKS Update

Here is an important update to the information in my last article in Tech
News about the System Tables in the Component Store. New libraries created
with Omnis Studio version 3.1 automatically have four new masks in #MASKS
that relate to date input. Unfortunately, the #MASKS System Table in the
Component Store itself does not contain these. I suggest that you modify the
#MASKS in a new 3.1 library to contain any additional custom masks you like
to use, then open the Component Store in the Library Browser and drag #MASKS
from the new library to the Component Library and overwrite the existing one
there. In this way, your custom set will always be available in the
Component Library along with these new date input masks (which you may also
wish to modify for your localization needs).


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

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 Technical 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