By Dr Caroline Wilkins
iB2B Systems Ltd (Note the Web Client is now obsolete but you may find useful some of the techniques discussed here regarding web form design and web applications: you should now use the JavaScript Client to create web & mobile apps.)
The Omnis web client is a proprietary technology. It
takes the form of either an ActiveX or Netscape plug-in on the web browser
client and delivers a full software application within a single page of
html. Web client can be used to create attractive and sophisticated graphical
user interfaces that the client can use over the web to interact with
a database running on a remote web server. The developer creates web client
applications using very similar looking components and code to that which
is used to create a thick-client desktop application. This means that
an Omnis developer that has hitherto worked on non-web applications can
easily deploy their skills creating web client applications.
The web client plug-in
In
order to use a web client application, the user must download and install
the Omnis web client. This is a one time operation that does not need
to be repeated for subsequent visits to any web client applications. The
Omnis web client plug-in is around 500k for Internet Explorer and Netscape.
When the user installs the web client, a group of core DLL's (listed below)
are copied on to their machine. These DLL's represent the basic functions
of the web client. However, many web client applications will take advantage
of the additional web client components, such as the Sidebar, shown here.
The Sidebar is an attractive and useful component that is typically used
to control the selection of pages in a Paged Pane component.
The Sidebar is not included in the core group of DLL's
for the initial web client installation. When the user first visits a
web client application that uses this component, the "Formsbar.dll" is
automatically requested and downloaded from the web server. This is a
quick and seamless operation.
The advantage of this piece-meal approach to component download/installation
is that the user is not required to download any more software than will
be required to use the applications that they visit. However, once that
component has been downloaded to the client machine, it is there for future
use with any web client application. This might be contrasted with Macromedia
Flash presentations which are downloaded each time they are visited
or ActiveX and Java Applets which do not share elements of client-side
software in this way that web client applications do.
Whereas many competing plug-in technologies require large and often redundant
downloads, Omnis web client is optimized to deliver maximum interface
function and form with minimum inconvenience to the user. Nevertheless,
the developer is not constrained to work with the "download as required"
approach to component installation on the client. Instead it is entirely
possible to create a customized installer containing all the components
required for a particular web client application. This can be a preferable
route with clients on large corporate networks where download and installation
of software may be restricted by IT policy and firewalls.
For reference, the core DLLs are as follows.
Core dlls
orfc.dll - ActiveX
orfc.dat - ActiveX
np_orfc.dll - Netscape
np_orfc.dat - Netscape
orfc.class - Netscape
orfcgui.dll - web client GUI
orfcmain.dll - web client engine
omupdate.exe - updates components
orfcexec.dll - executes client methods
formflds.dll - remote form fields
formback.dll - background components
The Omnis Server
In order to host an Omnis Studio web client application,
you must consider three elements: the web server (e.g. IIS/Apache), the
Omnis Server and the database.
These elements may all be situated on a single machine
or they may reside on seperate machines. Typically, the Omnis Server is
installed on the same physical machine as the web server and the database
is a seperate SQL RDBMS; in which case Omnis connects to the database
across a network using middleware and Data Access Modules (DAMs).
The web client application is contained within an Omnis
Studio library, which is loaded on the Omnis Server. Libraries are normally
written on development machines and copied as files to the Omnis Server.
Libraries can be changed and updated as often as required.
No compilation is required; it is a simple matter of swapping a library
file. The advantage of this is that the web client application need only
be changed on the Omnis Server. The next time the user logs on to the
application, they will be working with the new version. This can give
web client an advantage over thick-client solutions, which can be hard
and expensive to upgrade.
Multiple libraries can be run within the same Omnis Server and, if separate
licenses are used, multiple instances of Omnis can be run on the
same machine, although this is not a standard or popular configuration.
In practise, a dedicated server is normally required to host an
Omnis Server. Dedicated servers are widely available at ISPs and,
in the UK during the summer of 2002, typically cost around £100/month
for a Linux server and £200/month for a Windows server. If the
Omnis Server is incorporated into a corporate network, these costs
do not directly apply.
Event Handling: The Basics
By David Swain
Polymath Business Systems
I hope you had a great break for the past two months. (I was tempted
to say "summer break", but we have a large constituency from
south of the equator so that wouldn't be at all accurate!) Now it's time
to get back to work.
In my last article, I discussed the basics of "Methods and Messages".
This was done as an introduction to a series of articles on "Event
Handling" that begins now. In this article I introduce some theory
and explain some basic terms in event management. This will be followed
by other articles on practical examples and more advanced topics on event
handling.
Interactivity
Using computers would be pretty boring, and not 10% as useful as we find
them today, if we couldn't interact with them. Not that they provide sparkling
conversation, but they provide users with options during the course of
their work rather than having the work be entirely linear and repetitive.
Young people today take this for granted, but I began in this business
when we not only had to punch 80 column cards and submit our programs
for batch operation, but occasionally we actually had to rewire the computer
for specific tasks! The technology has certainly come a looooong way in
my lifetime! (I guess it didn't really exist much before I did anyway...)
In personal computers today, we usually interact with a program by performing
"user-initiated events". That is, we perform some action using
a peripheral device, like a keyboard or a mouse, that the program is designed
to detect and respond to. In this way, non-programming end users can do
meaningful work without needing to understand the inner workings and programming
syntax of the underlying programming language.
In the "old days", this interactivity was rather limited and
still very linear, in that the user came to a point where a choice must
be made among a small number of options in order to continue working.
As programming techniques evolved, more flexibility was added - first
by allowing the user more keyboard actions (which had to be memorized),
but then by offering additional input devices and programming constructs
like dropdown menus, pushbuttons, toolbars and other devices. But what
makes work simpler for the user creates more interesting work for the
programmer...
And Now, A Message From Your User
In the last issue of Omnis Tech News, I discussed how objects within
an Omnis Studio program interact with one another through "messaging".
An event is nothing more than a "user-initiated message" that
indicates to the program that it should perform some action in response.
Consider the following illustration:
Here the user clicks on a "control" object (in a toolbar in
this example). This sends a "click" message to the event handler
for that object. There may be many types of events that are "meaningful"
to that event method, but only the code for a click is executed. In this
case, another method ($next at the "instance of the class" level)
is invoked. This is just one of a variety of actions the user can perform
with this window.
Methods are Methods, But
Event handling methods are special in a number of ways. The most significant
of these is that we can't (or at least shouldn't) call them from other
methods in the manner discussed in the previous article. Yes, event handling
methods are "public" methods in the general sense, but the way
they are constructed (if written properly) they would never execute any
code lines since we can't pass an "event" when we invoke a method.
Event methods are made up of blocks fo code preceded by "On"
commands. Each "On" command line indicates one or a number of
events that cause its associated block of code to be executed. There is
an implied "OR" between multiple events in a single "On"
command. Here is an example of an "On" command that reacts to
a click or a double-click event:
In the Method Editor, Omnis Studio only offers us "event variables"
that are valid for the object to which the $event method belongs.We just
select the appropriate event(s) from the list provided.
Direct Event Handling Methods
Any GUI object (a window, menu, toolbar and remote form class or the
component object of such a class) that can contain methods can have a
method for trapping and reacting to events that happen directly to it.
The name given to this method must be "$event". In operation,
if a detectable event is perpetrated on an object, Omnis Studio executes
the $event method of that object to see whether the programmer gave that
event meaning and purpose.
The trick here is to understand what eventsw apply to which types of
objects. For example, we can detect "before" and "after"
events for entry fields (or any field object that can receive the focus),
but "OK" and "close" events happen directly to a window
(and are not directly detectable at the field level).
What "Events" Invoke an Event Handler?
Occasionally questions come up (in classes or on the Omnis Underground
listserver) about how to detect events for certain things. Comments like
"I can't detect a double-click on the status bar", "I can't
detect whether the Shift (or Control, or Function) key is held down when
the user selects a menu item.", etc., are common among new Omnis
Studio programmers. Let's address these two most common misconceptions
in order:
First, we can only detect events that happen to objects that can contain
methods. Since a method must be used to handle the event, this only makes
sense. Neither the status bar of a window nor the Omnis Studio help bar
can contain methods in the current version of Omnis Studio (and I'm not
suggesting an enhancement request here), so we can't possibly detect "events"
that happen to them. Reports are also not interactive, so we can't perform
"drill down" operations on them. (This is an enhancement request...)
A method named "$event" in a report object has no special meaning.
Menu lines are a special case. The only event that a menu line can react
to is being selected. For this reason, it is not necessary to use an "On
evWhatever" command to begin the code block in a menu line's $event
method.(Of course, we used to say something similar about pushbutton fields
only reacting to clicks not too many years ago...). But let's examine
how that menu line can be "selected". There are two basic methods:
the user can open the menu with the mouse and release the mouse button
over that line (or do the "sticky menus" second mouse click
on the menu line) or the user can type the keyboard equivalent for that
menu item. (Windows users also have the "alternate" keystroke
string - a sort of hierarchical walk through the menu structure that is
somewhat analogous to the "sticky menu" approach.) These two
(or more) techniques are intended to be exactly equivalent in their effect
on a program, so there is (or should be) no way to tell them apart. Since
the Shift or Control/Command key can be a part of the keyboard equivalent
to "physically" selecting a menu line, having separate functionality
for that menu line if one of those keys is held down from that if it is
not held down conflicts with the very nature of keystroke equivalents.
For this reason it should not be (and isn't) allowed. We can, however,
build in separate responses for modifier keys held down when a click is
detected on a pushbutton or other similar control by testing whether #SHIFT,
#CONTROL (#COMMAND) or #OPTION (#ALT) are non-zero.
To determine what events are available for a specific object, just go
to the $event method for that object in the Method Editor, place an "On"
command on a method line and examine the list of available events offered.
A Bit of Grammar
Usually when we use the term "object" in Omnis Studio, we use
it in the "nominative" sense. That is, we are talking about
a "type" of thing and giving it the name "object"
as a general category. When we use this term in discussing events, we
use it in the "objective" sense, as the item being acted upon
by the (user causing the) event. (Most non-English speakers are familiar
with the concept of "objective case". For English-only speakers,
remember "the object of a preposition" from your "grammar
school" days...the use of "him" vs. "he" in referring
to a person...)
Omnis Studio gives us a special notational reference to the "current
object" (the "object of the current event"). It is referred
to by "$cobj", a very powerful reference as we shall see in
the example article in the next issue. I only bring this up in an attempt
to avoid confusion with "object classes and instances", "object
variables", "component objects on a GUI class" and the
general "object-oriented" notion of an "object". (There
just aren't enough words for all possible meanings sometimes!)
Event Parameters
Occasionally, just detecting that a type of event has occurred is not
enough information to thoroughly define the event. Some events require
additional information. For example, knowing that a click has occurred
on a list display field may not be enough information to act on. We most
likely also need to know which line of the list was clicked upon so we
can perform an appropriate task.
Omnis Studio helps us out in this way by providing "event parameter
variables" that contain further details describing the event. All
events contain a variable named "pEventCode". This contains
the name of the current event. Other event parameters can be viewed in
the "Variables" pane of the Catalog by selecting the "Event
parameters" item in the left column as shown in the screen shot below:
Since different events can have different parameters, this display is
context sensitive down to the command line level. That is, the proper
event parameters are shown for the "On" code block to which
the currently selected command line belongs.
Event parameters are different from "Parameter" variables.
Parameters variables are "local" in scope and apply to the entire
method for one thing, and they are used to receive values or references
passed in from a calling method. Parameter variables declared for an event
handling method (in the Variables Pane of the Method Editor) will act
as any other local variable. There is no way for the user to pass values
or references to such variables.
Container Objects and Event Handling
We can not only detect and react to events that happen directly to an
object in Omnis Studio, but we can also detect events that happen to any
object contained in an object. For example, a window instance contains
component objects, so we can detect and react to events that happen to
any of the component objects at the window "level". Also, many
types of component object can contain other objects as well. We can also
react to events that happen to objects contained in a "container"
object (like a tab pane) in the same way.
To do this, Omnis Studio also gives special significance to a method
named "$control". This is an event handling method used to detect
and react to events "passed up" to the object that directly
happened to objects it contains. A container object can have both a $event
method (used for events that happen directly to it) and a $control method.
The total potential "path" that event handling can follow is
the $event method of the original "object" of the event and
the $control methods of every container in which that object is nested
up to the task in which it resides. So in the example below, events that
happen to the radio button named "End user" can potentially
be handled by any of the containers in which it resides.
But there are limits to this to help streamline the process, as we shall
see a bit later...
Resulting Actions and Redirection of Events
Part of the reason for detecting events is to determine whether to allow
the event to take place in the "normal" way. An event handling
method is a sort of "decision point" in a program in that a
choice can be made as to whether to allow the normal result of the user
action to occur or to trigger some other action instead.
The method is programmed to examine the event parameter values and values
of other variables to make this determination. The simplest determination
is whether or not to allow the event to proceed. The key thing to understand
here is that the event has not actually taken place at the point in time
when the event handling method is invoked. The event message variable
that is "turned on" by the user action indicates the intent of the user to have this event occur. It is not an indication that
the event has, in fact, occurred. Consider this simple example:
The user is entering data into an entry field and then presses the "Tab"
key to move to the next field. The first event that is detectable is an
"After" event for that entry field, indicating the user's intention
to shift the focus somewhere else. The "After" event is accompanied
by a parameter named "pNextCode" that indicates how the "After"
was triggered. In this case, pNextCode contains "evTab". In
the field's $event method, we can use the code block that follows a line
"On evAfter" to perform data validation (among other things)
to determine whether the shift of focus should be allowed. If the value
typed into the field by the user is out of range or otherwise inappropriate,
we can disallow the event and keep the focus where it is.
Quiting the Event Handler
To do this, we must "discard" the event. This is accomplished
using the "Quit event handler" command and its "Discard
event" option. Here is the syntax for that command:
Notice the second option named "Pass to next handler". We will
deal with this later in this article.
The "Quit event handler" command quits the current method just
like the "Quit method" command, but it differs in a couple of
ways. "Quit method" has an optional calculation that generates
a "return value". Event methods are not invoked by messaging
from another method, so there is nowhere for a return value to be returned.
"Quit event handler" has the two options shown above and is
the only wayh to provide those actions.
Queuing Events
In parallel with the options of "Quit event handler", we can
also queue other events to occur. There is a series of "Queue"
commands for this purpose. In the example above, perhaps certain values
left in the field require that data entry be redirected in a different
order (perhaps skipping over the next field or two). We could then queue
a "set current field" event to force the focus in a different
direction. Here is the syntax for that command.
Of course, we must execute this command before the "Quit event handler",
so the concept of "queuing up" the action is quite appropriate.
Whether we discard the current event or not, we can queue other events
to occur. Queued events occur in the order in which they are placed in
the queue and after the result of the current event (if it hasn't been
discarded) when the event handling method completes execution. Perhaps
a diagram of this whole process will help in the explanation...
The Event Cycle
The flow of control of event handling begins at the upper left corner
of the following diagram:
The operator performs some action and sets of the event handling mechanism.
The event code for the intended action of the user is put into pEventCode
and passed to the $event method of the object acted upon. This object
can be referred to at any level of event handling methods by the notation
shorthand "$cobj". If the $event method of that object exists
and contains an "On" block that detects the current event code,
that code block is executed and any resulting actions are placed on the
event queue.
If no $event method exists, if no "On" block detects the current
event code or if the "Pass to next handler" option of a "Quit
event handler" command is executed, the $control method for the container
of the current object is invoked (if it exists). If it contains an "On"
block that detects the current event code, that block is executed in the
same manner as a $event method. this continues until the event is handled
without passing on the event handling responsibilities of until the task
$control method is invoked. The resulting actions are then executed and
then control returns to the user. A few initial events (like "evAfter")
imply additional iterations of this cycle before control returns to the
user, but we'll leave those until next time...
Well, that's enough for today. Next time we'll flesh this out with some
examples. |