[Omnis-Newsletter] Omnis Technical Newsletter

omnis-news-admin@omnis.net omnis-news-admin@omnis.net
Wed, 23 Jan 2002 15:55:11 -0000


 January 23rd, 2002

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

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 exploring how you can control the visible and enabled state of data entry
buttons depending on the user mode of the window. In the second article,
David Swain discusses the various properties or options available for
pushbuttons. 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 16: Implementing the data entry interface, part 5., by Geir
Fjaerli
-StudioTIPS: an update of the Lite version
-Pushbutton Options, 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. Geir is a regular contributor to the
annual Omnis Developer conferences in the US and Europe, speaking about
Object-Oriented programming in Omnis and SQL development, amongst other
things.

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. Latterly, David has
appeared at Omnis Developer conferences providing his own Studio 101
introductory course to Omnis programming and application development.


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

Building part 16: Implementing the data entry interface, part 5.
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.
Part 12: Implementing the data entry interface.
Part 13: Implementing the data entry interface, part 2.
Part 14: Implementing the data entry interface, part 3.
Part 15: Implementing the data entry interface, part 4.
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.)

WHERE ARE WE?
Today marks the end of a marathon development effort: We have spent the last
4 issues implementing the controls and the code for our task window data
entry and retrieval. After implementing the delete button today, we have a
working window.

CORRECTIONS.
Before we start, let me correct a couple of typos and errors from previous
issues. I will mention some older ones for the benefit of new readers as
well. Thanks to Kelly Burgess who has reported these, and thanks to all for
your feedback. Note we will correct the back issues in due course.

Part 10: It seems I asked you to "open the Component store and drag either a
drop down or pop up menu control into the window." That of course should
have been "a drop down or pop up LIST control"...

Part 12: In my Calling Methods I mistakenly used the tkFind constant in the
$new method. it should of course have been tkNew, and the command should
then read:
Do method PreEntry (tkNew)

Part 15: In my calculations for the headed list box, I had typed
iTaskList.ctask. The column of course is cTask, with uppercase T. Since
column names are case sensitive in Omnis, this breaks our code.

My apologies for the inconvenience or minor frustrations this may have
caused you.

THE PRIMARY KEY.
A couple of you have reported a more serious issue, namely duplicate primary
keys. As you will remember from earlier discussions, the primary key serves
as the unique identifier for the row. However, so far we have left it up to
the user to manually enter the key. This of course is asking for trouble.
Also, it allows for small glitches in our code.

So how do we solve this issue? By having the primary key generated for us.
Now there are two basic approaches to this:
1. Let the database engine generate the key.
2. Generate the key in our own code, using some variant of cID=lastID+1

In a coming issue we will look at this in more detail and create a solution.

Now onto some other minor fixes to our wTask window, so open your library
and log on to the database, and then open the wTask window in design mode:

THE CANCEL BUTTON.
It seems that I may have forgotten to call the $cancel method from the
Cancel button $event. So if your Cancel button has no code except On
evClick, then add the following call below it:

  Do method $cancel

THE DELETE BUTTON.
There is still one button we have not made any code for. The Delete button
fortunately is a lot simpler than the other three. Find, New and Edit all
required setting a mode, letting the user type into the window, and then
acting on that after OK. Delete on the other hand is a simple action: The
user clicks the button, and we delete the record.

Just like the other methods we have looked on, the table class has a
built-in method for delete as well, called $delete. So open the $event
method for the Delete button (double-click the button) and add the following
code below the On evClick:

  Do iTaskRow.$delete()

Now this deletes the row from the database, but it does not clear it from
memory. So we need to add:

  Do iTaskRow.$clear()

As usual, we also need to redraw the window for the change to be visible:

  Do $cinst.$redraw()

That is it. Now as usual, this method could do with a bit of refining. The
main weakness is that it is far too easy for the user to accidentally delete
something that should not be deleted. We want to make sure the delete is
intentional, so let us prompt the user before we do it:

  Yes/No message Delete? {Are you sure you want to delete the current task?}

This will bring up a message box on screen asking the user if they really
want to delete. The message box is modal, meaning they have to answer by
selecting Yes or No before they can continue.

There are two versions of this message box: The Yes/No and the No/Yes
message. The difference lies in the default button, that is the button that
corresponds to the Enter key on the keyboard. The No/Yes message adds an
extra protection, as the user has to move the mouse and click on the Yes
button to confirm.

The message boxes will set the system flag in Omnis, so we can test the user
response by reading the flag. As we have seen earlier, Omnis has these
specific If-tests built in for your convenience. So adding the message and
the test, our method now looks like this:

  On evClick     ;; Event Parameters - pRow( Itemreference )
    Yes/No message Delete? {Are you sure you want to delete the current
task?}
    If flag true
      Do iTaskRow.$delete()
      Do iTaskRow.$clear()
      Do $cinst.$redraw()
    End If

One little extra touch: The employee drop down list will still show the
employee for the just deleted task, so we should reset that. So insert an
empty line before the redraw and add:

  Calculate iEmployeeList.$line as 0

That is it.

ENABLE AND DISABLE BUTTONS.
Now our window starts to look pretty good. But there is one major omission
still that is bound to confuse our users. There is no control over the state
of our pushbuttons. A user can click Find and then New, leaving them unsure
of which mode they are in. They can click OK without having selected a mode
first. And they can click Delete even if there is no current task to delete.

We need to let the user know which options are usable at any time. So if
they select New, the only options they should have is OK and Cancel. If
there is no current task, they should not be allowed to Edit or Delete.

We do this by enabling and disabling, or showing and hiding, the buttons.
Note that we have two options here:
1. We can enable and disable the buttons. This is done by setting the
$enabled property. A disabled button is greyed out and does not respond to
clicks. Or
2. We can hide and show them. This is done by setting the $visible property.
A hidden button isn't visible at all.

My preference is to use hide and show for OK and Cancel buttons only, and
enable and disable for all other buttons. Now let us look at how we want
these buttons to appear:

The Find and New buttons are basically the same. They should be enabled at
all times, except when we are already in entry mode, to prevent the modes
from being "stacked".

The Edit and Delete buttons are different, in that we should only be allowed
to click them when there is a row to edit or delete. But they also should be
disabled in entry mode.

And finally, the OK and Cancel buttons should only be visible when in Entry
mode, and hidden when not.

Now we could handle this on a button by button basis. But if we look closer
at the above, we see that it is possible to group these button "states" into
four discrete events:
1. A task record is found. This happens as a result of a Find. The Edit and
Delete buttons should be enabled.
2. The task row is cleared. This happens when we delete a row. The Edit and
Delete buttons should be disabled.
3. We enter data entry mode. This happens when we click Find, New or Edit.
The Find, New, Edit and Delete buttons should be disabled. OK and Cancel
should be shown.
4. We leave the data entry mode. This happens when we click OK or Cancel.
The Find, New, Edit and Delete buttons should be Enabled. OK and Cancel
should be hidden.

We actually only need two methods for this, one for setting $enabled and
$visible when we enter or leave entry mode, and one for setting $enabled for
Edit and Delete when we find/clear a row.

THE SETBUTTONSTATE METHOD
We could make our method something like this, assuming we pass the current
mode in as a parameter called pMode:

  If pMode>0
    Calculate $cinst.$objs.Find.$enabled as kFalse
   ;  Add the other buttons here
  Else
    Calculate $cinst.$objs.Find.$enabled as kTrue
    ;  Add the other buttons here
  End If

However, we can make that code even shorter by using Boolean expressions
with Omnis' ability to do automatic type conversions. Our if test says "If
pMode>0". This actually is based on evaluating the expression "pMode>0" to
see if it is true or false. So we can use this in our Calculate directly,
like this:

  Calculate $cinst.$objs.Find.$enabled as pMode=0

Now if pMode=0, that means no entry mode is selected, and so the Find button
should be enabled. If pMode is not zero, meaning we do have an entry mode,
then the expression evaluates to false and the button is disabled.

So we don't need the If structure at all. Just the calculates for each of
the fields with the appropriate condition in the calculation. So we end up
with this:

Create a class method in the wTask window called SetButtonState, and add a
parameter called pMode, of type Short Integer. (Right-click Class methods in
the method editor and select "Insert New Method". In the variable pane
select Parameters and click the first line to add the pMode parameter.)

Then add this code to the method:

  Calculate $cinst.$objs.Find.$enabled as pMode=0
  Calculate $cinst.$objs.New.$enabled as pMode=0
  Calculate $cinst.$objs.Edit.$enabled as pMode=0
  Calculate $cinst.$objs.Delete.$enabled as pMode=0
  Calculate $cinst.$objs.Cancel.$visible as pMode>0
  Calculate $cinst.$objs.Save.$visible as pMode>0

Note how Cancel and Save test on pMode>0 rather than equal to, and sets
$visible rather than $enabled. They are to be visible when there is a mode,
the others are to be enabled when there is not.

Now we need to take care of the two "special" buttons. Edit and Delete need
to be enabled or disabled depending on there being a current record. We
could make a separate method for this, or we could simply add the condition
to our existing method. To do the latter, add another parameter to the
SetButtonState method. Call it pCurrentRecord, of type Boolean.

Now add the second condition to the Calculations for Edit and Delete as
pCurrentRecord=kTrue. These two lines should then look like this:

  Calculate $cinst.$objs.Edit.$enabled as (pMode=0)&(pCurrentRecord=kTrue)
  Calculate $cinst.$objs.Delete.$enabled as (pMode=0)&(pCurrentRecord=kTrue)

Note that I enclose each condition in () to tell them apart. Omnis follows
normal rules for operator priorities, I still prefer to use () even when not
strictly required, as it makes the intention easier to read out of the
statement.

So we ended up with not two, but one method. Arguably, two methods might
have been a "better" solution because then we would only have handled the
buttons which state was actually changing. But this is OK. The handling of
button state takes little time anyway, and does not cause any flicker if the
state is not changed by the method.

CALLING OUR METHOD.
Now we need to add calls to our new method from where the state is affected.
We identified the situations above. The first was:

1. A task record is found. This happens as a result of a Find. The Edit and
Delete buttons should be enabled.
2. The task row is cleared. This happens when we delete a row. The Edit and
Delete buttons should be disabled.
3. We enter data entry mode. This happens when we click Find, New or Edit.
The Find, New, Edit and Delete buttons should be disabled. OK and Cancel are
shown.
4. We leave the data entry mode. This happens when we click OK or Cancel.
The Find, New, Edit and Delete buttons should be Enabled. OK and Cancel are
hidden.

Now for the last two, we already have common methods that are called:
PreEntry and PostEntry. So we will call our method from here. Now we have
two parameters to pass, the Mode and if there is a current record. We know
the mode, because that is what these two methods are there to handle in the
first place, but we do not know if there is a record or not. So we need to
test that.

Now how do we know if there is a current record? We cannot test for the
existence of a row because the row is always there. It is just empty when
there is no record. So we must test on it being empty. We do not have to
check all the columns, the primary key tells us what we want to know. If the
primary key has a value, then there is a current record, if not, then there
is not. So we can make our test "iTaskRow.cID>0".

Our statement for the PreEntry method then would be:

  Do method SetButtonState (pEntryType,(iTaskRow.cID>0))

The PostEntry method is a bit different, because it hasn't got a pEntryType
parameter. So there instead we will pass the tkNone constant:

  Do method SetButtonState (tkNone,(iTaskRow.cID>0))

OK, so that handles entering and leaving data entry mode. Next is when a
record is found. Now if you look at your methods you will see that when the
user clicks the Save button after a Find, we call the methods in this order:

Save button $event calls $ok, which calls Find.

Now a Find has three possible results:
1. No record found.
2. A single record found.
3. Multiple records found.

The first two are handled by Find directly. And $ok also calls PostEntry,
where we already added a call to SetButtonState. So assuming that the call
to PostEntry comes after the call to Find, it does already set the buttons
states after the Find. If not we need to change the order of these two
calls, because if PostEntry is called first, there won't be a current record
yet, as we haven't done our find.

So this means that the call from PostEntry handles the immediate result of
the Find. But if multiple rows are returned, we do not update iTaskRow
directly, we build iTaskList instead. So there will be no current task
record until we have selected one from the list. So we need to add a call
from the list $event method as well, this time it will be:

  Do method SetButtonState (tkNone,(iTaskRow.cID>0))

Now as soon as we select a line from the list, the Edit and Delete buttons
are enabled.

That leaves the Delete. Here we only need to set the state if we actually
performed the Delete, that is inside the If flag true condition. So insert
an empty line between the $delete and the $redraw statements, and add:

  Do method SetButtonState (tkNone,kFalse)

No need for any tests here. We know that we are not in any state, because
the Delete button is disabled when we are. And we know there is no record,
because we just deleted it.

That is almost it. There is only one issue to take care of. So far we set
the correct state of the buttons as a result of a user action. But what
about when the window is first opened? Well we could call our methods from
$construct method in the window, but even better is to set the initial
values for each button in the class itself. So in design mode select each of
the buttons, and then in the Property Manager set their $enabled or $visible
property (as appropriate for each button) as follows:

Find: Enabled = kTrue
New: Enabled = kTrue
Edit: Enabled = kFalse
Delete: Enabled = kFalse
Save: Visible = kFalse
Cancel: Visible = kFalse

And now finally, our buttons state handling is ready for testing. Open your
window and see if it behaves as expected. Actually, there is one very minor
glitch still, I believe. Whoever finds it will get honourable mention in the
next issue.

THE TASK LIST.
Now that leaves one little issue today. It is possible to select a record in
the list while we are in entry mode. That means that we could accidentally
overwrite values we are inserting with values from the list. So we should
disable the list when in entry mode. We could do this in our SetButtonState
method, but that gets called in other circumstances as well. It is more
appropriate to call it from our PreEntry and PostEntry methods. So in your
PreEntry method add:

  Calculate $cinst.$objs.tasklist.$enabled as kFalse

And in PostEntry add:

  Calculate $cinst.$objs.tasklist.$enabled as kTrue

Another option of course would be to disable the second tab instead, so that
the user could not select it at all. But there may be a reason why they
should be allowed to. That is up to you to decide.

So now our window is behaving quite nicely. I am sure there is more we could
do, you are welcome to submit your suggestions for later issues.

Until then, take care!

Geir :)


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

StudioTIPS: an update of the Lite version.

We have recently updated the Lite version of StudioTIPS that is available to
download Free of Charge from this web site. The new version is
StudioTIPS_Lite15a3.

StudioTIPS was created by Vencor Software, an Omnis developer based in
Canada, and includes a tutorial for programmers who are learning to use
Omnis Studio and SQL, and a Basics tutorial which takes you step by step
through building of an application using OmnisSQL. You can download it here:
www.omnis.net/downloads/studiotips.html


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

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

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

What could be simpler than a pushbutton field? We give it some text as a
label, assign some code to its $event method after an "On evClick" line and
go on to our next programming chore. Well, this probably covers the majority
of Omnis Studio pushbutton fields in existence, but there are a number of
options for a pushbutton field that may not be generally known or
understood. Once again, Omnis Studio affords us a great deal of latitude in
interface design.

Height and Width

Let's begin with something simple. Omnis Studio allows us to make our
pushbutton fields any size we wish. We can drag a handle on a selected
pushbutton in design mode or we can modify the "height" and "width"
properties in the Property Manager to achieve our desired results. Omnis
Studio automatically centers our label text vertically within the space we
provide.

Actually, there is a window for text within the borders of our pushbutton
field and our text is clipped to this boundary if we make the field too
small or the font size too large. The text is not allowed to overlap the
field borders. The width of the border varies from one button style to
another.

Border width can also vary from platform to platform for the same button
style. Mac OSX is the most graphically rich of the platforms on which Omnis
Studio runs, so the 3D System Button style on that platform has the widest
pushbutton borders. When designing applications that might possibly be run
on Mac OSX, it is best to size pushbuttons based upon the needs of that
platform. We'll go into more detail on this later in this article.

Multi-line Text

At first glance, it appears that a pushbutton field only allows a single
line of text as a label, like a background "Label" component. While it is
true that the "text" property of a pushbutton field lacks support for square
bracket notation like a Label object, it does support multi-line text. We
use a double forward slash ("//") to force a line break, the same way we do
for the text of a message dialog. There is no practical limit imposed on us
for the number of lines of text on a pushbutton - although I admit only
testing this on a single monitor system.

When we add a double-slash to the text of a pushbutton, an interesting thing
happens. No matter how large or small the original height of a pushbutton
field might be, Omnis Studio automatically adjusts the height of the
pushbutton to the minimum required for proper display of our multi-line text
when the value of the "text" property is modified and given a new
double-slash. The height can be further adjusted from there if desired.
Modifications that do not include adding a new double-slash appear to have
no effect on the height value. The width of the field is unaffected by any
modification of the "text" property and does not adjust to the width of the
"text" value, so it may need manual adjustment after the text is modified,
whether or not a double-slash is introduced.

Vertical Buttons

Since Omnis Studio allows us to create pushbutton fields with multiple text
lines, we can then create "vertical" pushbuttons if we have such a need. We
simply separate each letter in the text for our field with a double-slash
and set the "align" property value to "kCenterJst", adjusting the width to
our needs. In Windows 2000 I was able to narrow a vertical button to 12
points wide using the "MS Sans Serif 9" font without clipping any characters
in the word "Pushbutton". The same pushbutton on Mac OSX required a minimum
of 37 points to display the text without any clipping using the generic
pushbutton "theme font".

Further testing in OSX revealed that pushbutton fields up to 28 points wide
show absolutely no sign of any text (it's entirely clipped), indicating that
the left and right borders for a "normal" pushbutton on that platform must
be 14 points wide. (These are usually considered to be the "end caps" of a
normal "Aqua" pushbutton.) Such a field set to 20 points wide is a very
pleasing vertical lozenge-shaped pushbutton (with no text), indicating the
corner radius is 10 points. If we don't mind using a help tip as a label,
this may have possibilities.

On the other hand, text clipping at the top and bottom of a pushbutton field
in OSX occurs at the very edge of the button (but text is not allowed to
spill into the shadow area).

Icons

Of course, we're not limited to identifying our pushbuttons with labeling
text. We can also assign an icon to the pushbutton field in the "Appearance"
properties. If we assign both labeling text and an icon, they will be
juxtaposed depending on the "align" property setting as follows: "kLeftJst"
places the icon vertically centered to the right of the text, "kRightJst"
places the icon to the left of the text, and "kCenterJst" places the icon
horizontally centered above the text.

For many purposes, though, we may decide that an icon alone is enough to
identify the action taken by a pushbutton. An empty "text" property value is
allowed.

To assign an icon to our pushbutton field we need to set the "iconid"
property which contains two sub-values: the actual ID number of the icon we
desire and a modifier indicating which size of that icon we wish to use.
Usually the size is just that, but sometimes an entirely different icon
resides in a different size slot within the same icon ID on an icon page, so
care must be taken. The modifiers are "kDefSize", "k16x16", "k32x32", and
"k48x48". These actually represent (usually) very long negative integers
(0, -1879048192, -1610612736, and -1073741824 respectively) that combine
with the basic icon ID to exactly identify the correct picture stored in the
icon libraries. If we want to assign an icon ID with a calculation or
notational assignment method, we should add the basic icon ID with one of
these modifiers. For example, to assign the Object Class icon (ID=1712) in
the 48 point size to the current field, we would execute:

Calculate $cfield.$iconid as 1712+k48x48

If we had not included the modifier, our field would display the 16 point
version of the icon since this is the default size for that one.

Cursor

Another icon we can assign to a pushbutton field is the cursor icon that
will appear as the mouse pointer is moved over that field. We control this
with the "cursor" property of the field. Again there are two items for this
property in the Property Manager, a built-in cursor constant and a cursor
icon id number. These appear to be used in a mutually exclusive way as
follows:

If we assign one of our cursor constants to this property and then examine
the value contained in the $cursor property of the field, we will see a
rather large integer as a result. Yet if we look at the value of the cursor
constant itself (which we can do by placing it in a method line - even a
comment), we will see that it has a very small integer value. Further
exploration yields the fact that the value stored in the $cursor property
is, in fact, the value of the constant times 2 to the 24th power (16777216).
This could prove to be useful information if we should ever have to test a
field to determine the current value of this property (and make sense of
it).

Now if we assign a cursor using the cursor ID number, the $cursor property
yields exactly the number we assigned, but the Property Manager now displays
a new cursor constant named "kcursCustom", which we find has a value of 127.
If we select a cursor constant in the Property Manager, the associated
cursor icon ID number is automatically reset to zero. If we assign a (valid)
value to the cursor icon ID number, the cursor constant is automatically
reset to "kcursCustom".

We can speculate about exactly what operations are performed by Omnis Studio
to manage this information internally, but the only thing that directly
helps us as Omnis programmers is knowing how to interpret the values we see
when examining this property.

It is also useful to note that some common cursor icons, most notably the
pointing finger we all use in web browsers, are not included in the Omnis
Studio cursor set. We can easily obtain these from our favorite browser (or
other program) using a resource editing program and then paste them into an
icon page in the #ICONS system table of an Omnis Studio library using the
Icon Editor. But this is a subject for another time...

Button Style

Another dimension of pushbutton appearance is the "buttonstyle" property.
The value of this property determines certain things about how a button
looks and whether labeling text or an icon (or both) can appear on it.

There are a variety of button styles available and we will briefly list them
here:

System

The System, 3D System and "Old" 3D System button styles all appear the same
on the Windows 2000 and Mac OSX platforms. That is, they vary from one
platform to another, but seem to be constant within a given platform. On
Macintosh OS 9 the "Old" 3D System button is slightly more
primitive-looking, but the other two appear identical.

I did not have time to test these on other platforms, but I tend to use the
3D System button style (sized for Mac OSX) for most of my cross-platform
work. This yields an appropriately modern "three-dimensional" look for
pushbutton fields on each platform.


This style allows both labeling text and an icon to appear on a pushbutton
if the proper property values have been set.

Heading

Assigning a value of "kHeadingButton" to the "buttonstyle" property on the
Macintosh platforms yields a square-cornered, slightly less
three-dimensional pushbutton field than the System styles. A heading-style
pushbutton looks much like the column heading of a headed list field. On the
Windows 2000 platform, this style looks exactly the same as the "System"
styles in keeping with the standards of that platform.

This style also allows both labeling text and an icon to appear on a
pushbutton if the proper property values have been set.

No Border

If we assign a value of "kNoBorderButton" to the "buttonstyle" property, our
pushbutton becomes invisible except for any labeling text or icon it might
bear. A pushbutton with this property value will still retain a fill color
on most platforms, so it will still cover background objects. But this fill
color equals the window color, so it will blend into the background and
appear to be transparent. On Mac OSX it is truly transparent, so background
objects can be seen behind (or, more precisely, through) it. This allows us
to have what appears to be a block of text or a simple icon (or both) as a
clickable object on a window without a discernable pushbutton border. The
clickable area is still rectangular and as large as we want to make it, but
we can trim it right down to the actual size of the text block and/or icon
with no excess border if we need.

Think of this as sort of a button area with text and/or icon. It will not
flash when clicked and it is only truly transparent on the Mac OSX platform,
but otherwise the analogy is accurate.

Combo

The "kComboButton" style yields a pushbutton with a down-pointing arrow
(just like the one on a combo box for that platform, hence the name) that is
appropriate for certain interface features. For example, some dialogs or
other windows use such a device for expanding the window to display more
detail.

When this style is selected, the contents of the "text" property are
suppressed. If an icon has been assigned, it is juxtaposed to the arrow that
appears in the field as it would be to the text in a "normal" pushbutton.
However, if center justification is used and the field is only one line
high, the icon will overlay the arrow. Icons are not really appropriate for
this type of pushbutton, though, so I don't assign one for such a field.

On most platforms this field appears with the same border as a System button
at a size dictated by the "height" and "width" properties. On Mac OSX, the
clickable area will be determined by these properties, but the actual button
that appears will be 24 pixels square no matter how much larger the field is
made (although it should be at least 24 wide and high or it will appear
squashed). The OSX version is always the Aqua color and has slightly sharper
rounded corners than a "normal" pushbutton field. If our application is
being developed on some other platform but may be deployed on OSX, and if we
have need for this button style, we should make such pushbuttons 24x24 for
consistency.

This type of pushbutton still responds to clicks like any other. The
difference is in the "interface intent" of this button. It has a rather
limited use, but is appropriate in certain situations.

Round

This is a new button style that was added to version 3.1 of Omnis Studio for
compatibility with Mac OSX. On that platform, this button style appears as a
round button 24 pixels in diameter (no matter how much larger the actual
field boundaries are) with the distinctive translucent Aqua look. It is
clear by default, but takes on that pulsating Aqua blue color when it gets
the focus. On all other platforms it just appears like a (rectangular)
System button.

This button style does not display text, but it will display an icon if one
has been assigned. For compatibility with OSX, only 16x16 icons should be
used as that is the only size that will properly display in the fixed-size
round bubble button on that platform. If this button style is desired but a
label is also necessary, a label object can be placed adjacent to the
pushbutton.

Using this button style is a stylistic choice. It is most often used on
navigation windows or in other places where lengthy text labels are needed.
It provides a cleaner look than a large pushbutton with lots of text on it.

Button Modes

In the "Action" properties for a pushbutton, there is a property named
"buttonmode". this property is used to determine how the button will work.
most pushbuttons have a value of "kBMuser" (user-defined) for this property.
"kBMok" and "kBMcancel" are also popular button modes as pushbuttons of
these types directly generate "OK" and "Cancel" data entry events
respectively.

There are a number of "legacy" button modes that perform specific operations
on the native Omnis datafile. While these have some use for quick "mock-up"
windows, most native Omnis programmers don't use them in finished work
because we can create user-defined pushbuttons that give us greater control
over these processes.

Picker Buttons

But there are three button modes that have very special and important uses.
These are the "picker" button modes: line picker, pattern picker and color
picker. These deserve some detailed explanation.

A "picker" button is used to select a visual property value (line style,
pattern or color) from a dropdown selection dialog. The current value of
this property is displayed on the picker button where an icon would normally
be shown. Because this display is essentially the "icon" for the field, the
"iconid" property value is cleared and made inaccessible (or irrelevant)
when a "picker" mode is set.

Text is still allowed in a "picker" button, but I usually feel it just gets
in the way (personal preference). If it is used, it is juxtaposed with the
value display as it would be with an icon.

When a selection is made using a "picker" field, two things happen. First,
the value of the "$contents" property for the field is set to that of the
selected item, then the $event method is triggered. The $event method is
used to then assign the value in $cfield.$contents to some other item.

Here is a brief description of the three "picker" types and the values they
use:

Line Style Picker

Setting the "buttonmode" property value to "kBMlinestylepicker" turns a
pushbutton field into a line style picker object. There are sixteen line
styles recognized by Omnis Studio, numbered one through sixteen. The first
seven are various weights of solid line, number eight is a hair line, nine
through fifteen are various types of dotted and/or dashed line, and number
sixteen is "none". There appear to be no constants associated with these
line styles, just numbers.

Selecting a line style from the picker dialog that appears sets the value of
the "$contents" property for the picker field to the number of the selected
line style. The line style displayed in the picker can be set by assigning
an appropriate numeric value to its "$contents" property.

Pattern

Setting the "buttonmode" property value to "kBMpatternpicker" turns a
pushbutton field into a pattern picker object. There are sixteen patterns
recognized by Omnis Studio, numbered zero through fifteen. Number zero is
for solid forecolor, number one is for solid backcolor, numbers two through
fourteen are various patterned combinations of forecolor and backcolor, and
number fifteen is "none". There appear to be no constants associated with
these patterns, just numbers.

Selecting a pattern from the picker dialog that appears sets the value of
the "$contents" property for the picker field to the number of the selected
pattern. The pattern displayed in the picker can be set by assigning an
appropriate numeric value to its "$contents" property.

Color

Setting the "buttonmode" property value to "kBMcolorpicker" turns a
pushbutton field into a color picker object. Since colors in Omnis Studio
are generally assigned to items to be displayed on screen, the RGB system
was used to describe colors. In this system, red, green and blue components
of a color can range from zero to 255. These are combined mathematically to
yield a unique integer value for any of the 16,777,216 (there's that number
again!) possible colors that can be viewed on a 24-bit color monitor (2 to
the 24th power colors - 2 to the 8th for each RGB component).

Colors in the Omnis Studio color picker are of two types: raw RGB values and
named color constants. The color picker only presents a sampling of 256
colors from the total palette of over 16 million, but these are good for
most purposes. The picker also presents sixteen (of seventeen total -
"transparent" is left out) "primitive" color constants and sixteen (of 33
total) "system" color constants (basic text, window, highlight and shadow
colors).

The RGB values are generated by adding the Red value to the Green value
times 256 to the Blue value times 65536 (256 squared). This allows Omnis to
store color as a single value instead of as three component values. These
component values can be separated by reversing that process. Omnis Studio
also has an rgb() function that can perform the arithmetic to generate a
color value from RGB component values for us.

Colors derived from the color constants are stored as negative numbers in a
similar way, as an offset from the number -2147483648 (2 to the 33rd power).
This distinguishes them internally from colors stored as raw RGB values.
There is a truergb() function that converts these color constants into
"normal" color values.

Selecting a color from the picker dialog that appears sets the value of the
"$contents" property for the picker field to the number corresponding to the
selected color. The color displayed in the picker can be set by assigning an
appropriate numeric value to its "$contents" property, although not every
color that can be assigned is represented in the picker's palette.

Conclusion

I hope at least part of this has been useful for you. There are more things
we could talk about, such as using drag and drop techniques with pushbutton
fields, but those subject require a good deal of explanation themselves.

Next time we'll hold a similar discussion about radio button and checkbox
options.


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

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 2002. 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