Tips_gui   >   Windows   >   Windows (All Contents)

Windows

The window is the where the user and your application intersect, hence an important part for your application, both visually and functionally.

This section includes various tidbits of code and ideas which apply to window classes.

$cfield vs $cobj

$cobj

is the current object where the event is occurring.

$cfield is the current field which the method belongs to.

$cobj can be used in event handling method code.

; $event method of a field.

On evAfter

If len($cobj.$contents)=0
   ; The field is empty.
Else
   
End If

If you want a method to run in the $construct of a field, $cfield is the reference to that field.

; $construct method of a button.

; Set a reference to the button field.
Set reference irOkayButton to $cfield

Group and Scroll Boxes

Do you use group boxes to group your radio buttons? They work great especially as containers for radio buttons. Move the group box around in design mode and you're moving its contents just as you laid them out. Disable it and you've disabled its contents.

Tip

I prefer scroll boxes over group boxes. The top edge of the group box takes up too much space if I'm not using the group box title. If you set the vertical and horizontal scrollbars to kFalse in a scroll box you've got a group box with narrow borders. To make the scrollbox invisible set the $background property to kBGThemeParent and $borderstyle to kBorderNone

Naming Window Objects

Some developers prefix their window object names with 2 lower case letters to indicate the type of object. For example:

There is less need for this naming convention in the more recent versions of Omnis Studio. If you are looking at the method editor of a window class click the View menu in the IDE toolbar, and select Show Icons. Omnis Studio will then display a small icon beside each window object to indicate the type of object. The icons match those in the F3 Component Store.

If you right-click on the window class in design mode and select Field List... Omnis Studio opens a treelist window which lets you to browse the window objects. This treelist also displays a small icon beside each window object to indicate the type of object.

Remove objects

There are several ways to remove objects from a window instance:

Build a list of the objects and then loop through the list.

; Make a list of the objects.
Do $cinst.$objs.$makelist($ref) Returns List

; Loop through the list removing the objects.
For List.$line from 1 to List.$linecount step 1
   Do $cinst.$objs.$remove(List.C1)
End For

Use $sendall to remove all the objects in a single line.

; One line $sendall to remove all the objects.
Do $cinst.$objs.$sendall($cinst.$objs.$remove($cinst.$objs.1.$ref))

Note

The key to this is that $ref always points to the last object referred to. If you were to say: $cinst.$objs.$remove($ref), it would point to the collection $cinst.$objs - in other words trying to remove the collection from itself. But, the first member of the collection can be referenced as $collection.1.$ref. What the above sendall does is, for each object in the collection, removes the first object in the collection (in other words, itself). (The sendall code and explanation was provided by Tim Stewart)

To use $sendall to remove a specific type of object you need to make the objects invisible rather than remove them.

; One line $sendall to hide a specific type of object.
Do $cinst.$bobjs.$sendall($ref.$visible.$assign(kFalse),$ref.$objtype=kOval)

Another option is to build a list of the objects and then issue a $sendall to the list.

; Make a list of the objects.
Do $cinst.$bobjs.$makelist($ref,$ref.$objtype) Returns List

; Issue a $sendall to the list to remove all the kOval objects.
Do List.$sendall($cinst.$bobjs.$remove(List.C1),$ref.C2=kOval)


Resizable Windows

If you make a resizeable window it's nice to have the objects on the window correctly reposition themselves as the window is beign resized.

A technique I use extensively is to set the $edgefloat property of objects to: kEFposnTopToolBar, kEFposnBottomToolbar, kEFposnLeftToolBar, kEFposnRightToolBar, kEFposnHorzHeader, kEFposnMainHeader, kEFposnVertHeader, kEFposnClient

Many times it works best to add a scrollbox object, set its $edgefloat property, and then add objects inside the scrollbox.

For example a set up pushbuttons along the bottom of the window would be placed in a scrollbox which we name BottomContainer. The object's $edgefloat is set to kEFposnBottomToolBar. Push buttons are added to the BottomContainer scrollbox. Now no matter what size the user resizes the window to, the BottomContainer with its group of buttons will hug the bottom of the window.

If you want to have the buttons hug the right bottom edge of the window, put another scrollbox object inside the BottomContainer and name it BottomContainerRight setting its $edgefloat to kEFposnRightToolbar. Size its width correctly and move the buttons into the BottomContainerRight scrollbox. The group of buttons will now always hug the bottom right edge of the window.

Tip

To make life easier in design mode, for any kEFposnClient objects, set the object to kEFnone in the F6 Property Manager, and add a $construct method to the object with a single line of code: Do $cfield.$edgefloat.$assign(kEFposnClient)

Press the Run Demo to see how scrollboxes can be used to keep window objects where you want them.

Revert Field Properties

If you want to revert the properties of objects in a class instance to their original property values, you can do this by looping through the objects in the instance and setting them to match the objects in the class.

; Set a reference to the instance's class.
Set reference rClass to $cinst().$class

; Loop through the instance's objects.
Do $cinst.$objs.$first() Returns rInstObj
While rInstObj
   
   ; Find the matching object in the class.
   Do rClass.$objs.$findident(rInstObj.$ident) Returns rSourceObj
   If rSourceObj
      
      ; Revert the instance property.
      Calculate rInstObj.$PropertyName as rSourceObj.$PropertyName
      
   End If
   Do $cinst.$objs.$next(rInstObj) Returns rInstObj
End While

Warning

This solution won't work for objects that are in the superclass of the class which your are executing the code.

Sendall to objects

The $sendall method is really handy when you want to Do something to all the objects in your window.

The following are a examples of some scenarios and the sample code for doing them.

Change a property of all objects.

; Change the foreground property of all objects.
Do $cinst.$objs.$sendall($ref.$forecolor.$assign(kColor3DFace))

Change a property of selected objects using the search criteria option.

; Disable any objects with a kColor3DFace forecolor.
Do $cinst.$objs.$sendall($ref.$enabled.$assign(kFalse),$ref.$forecolor=kColor3DFace)

Note the extra search criteria after the comma in the $sendall. What follows is the search criteria of which objects are to be sent the $sendall message. You can make the search filter as complex as you like.

Change a property of all objects relative to the object's current property.

; Move all objects right by 50 pixels
Do $cinst.$objs.$sendall($ref.left.$assign($ref().$left+50))

Note the $ref() inside the $assign(). This tells OMSt to evaluate $ref now, then continue evaluating the string.

Change a property of objects inside a single row of a complex grid (requires using grid exceptions).

; Set the forecolor for all the objects on a particular line of a complex grid.
Do irGrid.$objs.$sendall($ref.[iList.$line].$forecolor.$assign(kGray))

Shape Fields

What are they are and why would anyone use them? Shape fields look like background objects but they can receive events and can contain methods.

The field has a $shape appearance property which can be set to: kText, kRect, kRoundRect, kRect3D, kOval, kLine, kBackPicture

You can use a shape field for column headings in a complex grid by setting the $shape property to kText and then putting your sort list code into the $event method of the shape field. Click the Run Demo button in the StudioTips Browser to see an example.

The only problem I have with shape fields is that they look like text fields so I keep double-clicking them when I want to change the text ... oops ... you can only change the text in the Property Manager.

Slow Window Instantiation

One of the first complaints I had when starting development with Omnis Studio was the length of time it took to open windows. In one of my first windows I loaded it with a 5 layer tab pane and about 100 fields. When the window opened I was shocked at how slowly it opened. Complaints to Tech Support didn't change things. The simple fact is that all the great benefits of object-oriented programming cost something when it comes to instantiating windows. You can have multiple instances of any window, you can have superclass inheritance, and you can have $construct methods at every object and class, so just imagine all the work that Studio has to do when you instantiate a new window instance.

The thing I did to speed up window instantiation is to use empty subwindows in page panes and tab panes.

Lets say you have a 5 layer tab pane. When the user opens the window they only need to see the first pane. That might be all the information they need before closing the window. They might click on tab 3 and then close the window. You can speed up window instantiation by putting an empty subwindow field set to kEFposClient on each layer of the tab pane and leaving the $classname empty. In the $event method of the tab pane object you trap the On evTabSelected event and based on pTabNumber you $assign the $classname before the tab is displayed. In the $construct of the window or the tab pane you $assign the $classname for tab one.

On evTabSelected
Switch pTabNumber
   Case 1
      Do irswTab1.$classname.$assign('wWindowClassName1')
   Case 2
      Do irswTab2.$classname.$assign('wWindowClassName2')
   Case 3
      Do irswTab31.$classname.$assign('wWindowClassName3')
End Switch

The other advantage of using this subwindow technique besides speed is that each subwindow is flat, making it a lot simpler to layout, modify, etc. It takes a while to get used to using subwindows, but once you get past that I think you'll enjoy them.

You can also do some data checking before switching to tab 2 or 3. If the user hasn't entered sufficient information, you can discard the event and not even bother setting the $classname.

Note

If you assign the same $classname a second time Omnis Studio is smart enough not to reinstantiate it.

$framehwnd and $hwnd

The $framehwnd and $hwnd properties were always a mystery to me. The F1 Help documentation didn't fully clarify these properties to me, so when I needed to align a kNoFrame window exactly over a field in another window, I created a demo in StudioTips and started testing these properties in an attempt to understand them. Eventually I emailed Tech Support for further clarification.

Here's what the F1 Help says about $framehwnd:

The window identifier of the outermost enclosing window of the object. The properties of $framehwnd are not assignable. Set a reference to object.$framehwnd.$ref, to access its properties.

The documentation on $hwnd is similar:

The window identifier of the main window of the object. The properties of $hwnd are not assignable. Set a reference to object.$hwnd.$ref, to access its properties

The properties of $framehwnd and $hwnd in the F1 Help are as follows:

As far as I know hwnd is an abbreviation for handle on window.

It sounds like $framehwnd and $hwnd only apply to a window instance, but as I discovered these properties can be applied to window objects; complex grids, tab panes, etc.

This became important when I was attempting to align a frameless window precisely over a field inside a complex grid. Using the $width and $height properties of the complex grid object was close, but not close enough. Changing the complex grid border to an embossed border cause a slight offset. I tried using $framehwnd for the complex grid and discovered its properties worked for the grid.

Tech Support confirmed my discovery as follows:

Objects on windows have borders (in this case the complex grid has an embossed border )

The $framehwnd has the following :

$framehwnd.$width should be the same as grid.$width

Well that clarified things better than the F1 Help!

Click the Run Demo button in the StudioTips Browser to try out the $framehwnd and $hwnd properties. If you shift+click any of the buttons you can step through the code.

The oScreenCoordinates object does all the work for you in figuring out where any field object is in relation to the parent window or in relation to the global screen are. You can copy the oScreenCoordinates object to your own application.

A related discovery is that Omnis Studio will not give you the window height including the titlebar. I had to hard code the titlebar height for each operating system in the oScreenCoordinates object.

Another gotcha I ran into was that Omnis Studio will not give you the size of the window toolbar, window menus bar, window status bar, or window scrollbars. The presense of any of these affects the positioning of fields relative to the screen area. You need to adjust the screen position by each of these elements as applicable. You have to figure out these values for each platform. Toolbar sizes vary based on small or larges icons and whether or not the button text is shown. All of this has been done for you in the oScreenCoordinates object.

The oScreenCoordinates object and demo can be downloaded separately from the studiotips.net website.

oScreenCoordinates also includes public methods which return the overall screen width and height for Mac OS X computers with dual monitors.

$redraw

$redraw(bSetContents=kTrue,bRefresh=kFalse)

Method to redraw the contents and/or refresh a field or window.

The $redraw parameters have been a mystery to me for a long time. Hopefully this tip and demo will help to demystify it for others as well.

It took a few hours to build the demo for this tip to test and analyze the $redraw method and draw some conclusions as to what bSetContents and bRefresh actually do.

Every entry field has a $dataname property. You enter the name of the variable which you want the entry field to display. $dataname could be a task variable, class variable, or instance variable. The variable could be a column in a list or row, or a simple variable.

Every entry field has a $contents property. The $contents property is normally equal to the value of the variable being represented by the entry field... but not necessarily.

When you enter the field (evBefore), Omnis Studio refreshes $contents with the variable value.

If you modify the field, $contents will be different than the variable value while your are still in the field.

When you leave the field (evAfter) Omnis Studio copies the $contents to the variable. Omnis Studio does this just before sending the $event(evAfter) message to the field. (That's what makes it difficult to test and figure out when $contents is copied to the variable.)

bSetContents=kTrue will set $contents to match $dataname.
bRefresh=kTrue will refresh the field contents.

If you are changing the variable value in your code, you want bSetContents to be true (default) when you redraw so that the $contents will be set from the variable.

If you are changing the $contents from your code, and you do a $redraw, you want bSetContents to be false so that the $contents value isn't wipes out by the variable value.

Note

Changing $contents only seems to work if the focus in on the field. If the focus is on another field $contents gets replaced by the variable value when you enter the field (evBefore).

I had hoped to find a way to push the $contents to the variable using $redraw but didn't succeed. You can test all of the above with the demo included with this tip.

I didn't figure out a lot of use for setting bRefresh to true vs. false. If you know situations where setting bRefresh to true vs. false is important, please let me know and I can add it to this tip.

Entry Fields

This section includes various tips and demos on text entry fields.

$cobj.$contents

Think of the field $contents as a keyboard buffer which gets written into the $dataname variable when focus leaves the field.

Each entry field has a $dataname property. The $dataname points the entry field to a variable which stores the value that the entry field displays.

The $dataname value is loaded into the $contents when the field is redrawn or when the field becomes the current field (On evBefore).

As the user types keys on the keyboard, the $contents value changes, but not the $dataname value.

When the focus leaves the entry field (On evAfter), the $contents value is copied to the $dataname variable.

A situation where you might watch the $contents is in a window where you ask the user to re-enter a new password and want to enable an Okay button as soon as the user matches the the previously entered new password.

; $event method for 're-enter password' field.

On evKey

; Allow the keystroke to be processed.
Process event and continue

; Enable/disable the Okay button based on whether or not
; it matches the new password in the previous field.
Do irOkayButton.$enabled($cobj.$contents=iNewPassword)

A situation where you want to manually copy the $contents to the $dataname is when an entry field has a context menu. The context menu event occurs without an evAfter event. When focus returns to the entry field an evBefore reloads the $contents from the $dataname wiping out any changes the user may have made prior to opening the context menu. The following sample $event method code shows how to copy the $contents to the $dataname before opening the context menu.

; $event method

On evOpenContextMenu

; Copy the entry field $content to the $dataname
Calculate [$cobj.$dataname] as $cobj.$contents

; Continue with opening the context menu.
Process event and continue

$firstsel & $lastsel

In my applications I have entry fields which I classify as click-edit fields. These fields are enabled in new mode but intially disabled in edit mode because the field values normally aren't changed after the record has been inserted. e.g. FirstName and LastName.

When the user clicks on a click-edit field, I enable the field and select all of the text in the field, just as though they had tabbed into the field.

The following $event method code shows how this is done.

On evClick

; Enable the field, set the focus, set the color.
Do $cobj.$enabled.$assign(kTrue)
Do $ctarget.$assign($cobj)
Do $cobj.$forecolor.$assign(kWhite)

; Select all of the text in the field.
Do $cobj.$lastsel.$assign(10000)
Do $cobj.$firstsel.$assign(0)

On evAfter

; Disable the field, set the color.
Process event and continue
Do $cobj.$enabled.$assign(kFalse)
Do $cobj.$forecolor.$assign(rgb(255,255,153))

Note

You can put this code in the $control window class method and then be sure to pass the events from the entry field to the next handler.

Calculated Entry Fields

If you want to have a calculated display entry field in a window or a complex grid there are 2 entry field properties which you need to set:

You can then enter your calculation in the $text property of the entry field, or you can assign the $text property of the field from a method.

If you don't set both properties, Omnis Studio will not display your $text calculation or value.

Click Run Demo to see a calculated entry field being used in window field and a complex grid.

Enter key = Tab key

You can change the behaviour of the Enter key to equal the behaviour of the Tab key. If your application has a lot of numeric data entry your users will appreciate this simple user interface change, and your boss will appreciate the increased productivity.

In my application we have windows for Timesheet entry, Purchase Order entry, Inventory Disbursements, Accounts Payable Invoice entry, Accounts Receivable Invoice entry. All of these tasks make heavy use of the numeric keypad. You would be amazed at the time saved when the user can use the Enter key on the numeric keypad to move from field to field, rather than reach for the Tab key. In our application time sheet entry input increased 30% by this simple change.

Many of the old green screen systems used the Enter key to move from field to field.

The code for doing this is extremely simple.

; Method: $control - of your superclass window class.
On evKey

; Intercept Enter key event, replace with Tab event.
If pSystemKey=kEnter
   If #SHIFT ;; kEnter
      Queue tab (Shift) ;; Do a shift tab instead
   Else
      Queue tab ;; Do a tab instead
   End If
   Quit event handler (Discard event) ;; Discard the enter key event
End If

I recommend putting this code in the $control method of your superclass window. You must make sure that $keyevents is set to kTrue in the properties of all your libraries.

If you have an On evKey in the $event method in any entry fields you must end include Quit event handler (Pass to next handler) in order to pass the event to the $control method.

Keyboard events, evKey

You can trap keyboard events with On evKey in the $event field method. (or the window $control method if the event is passed to that level.)

The evKey event is sent to the $event method with the parameters pKey and pSystemKey.

Tab and Shift+Tab have their own event codes, evTab and evShiftTab. You can (and should) trap On evTab and On evShiftTab rather than their system key equivalents for easier to read code.

In your application make sure $keyevents is set to kTrue in your library properties or the object properties.

Click the Run Demo button in the StudioTips Browser for this topic to find out the values of pKey and pSystemKey for various keys.

Multi-line Entry Field Height

If you have a multi-line entry field that has 2378 characters and random carriage returns, how do you figure out the height of the text in the field?

Tech Support once sent me a library which included some code for figuring out the height of the text in a multi-line entry field. The code worked but the notation is so unconventional that I assume this must be an undocumented feature.

I use this trick for setting the height tip description field in the StudioTips Browser window.

Here is the code:

; "rField" is an item reference variable referencing a multi-line entry field.
; The field is displaying some text.

; rField.$add(21) returns the number of lines in the multi-line field. (Undocumented feature)
Calculate LineCount as rField.$add(21)

; Assign the negative of the linecount to the $height correctly sets the multi-line field height. (Undocumented feature)
Do rField.$height.$assign(-LineCount)

; If you wanted to accomplish the above in a single line of code. :-)
Do rField.$height.$assign(-rField.$add(21))

Click the Run Demo button to test the code.

Tab and Shift+Tab Events

If you tab or shift+tab out of a field, Omnis Studio sends an evTab or evShiftTab $event message to the field. You can trap these tab key related events with On evTab and On evShiftTab in the $event method of the field.

Type Ahead Combo Droplist

Many of the popular software programs (Outlook Express, Quicken,...) try to finish data entry for the user by finding matching entries and adding the text from the first match to the field while the user is typing. I call this behavior type-ahead.

The kCombo field allows you to build a droplist which the user can select, but it doesn't handle type-ahead in the same way as you experience it in Quicken or Outlook.

You can combine a kEntry field and a kCombo field to obtain type-ahead behavior.

Click the Run Demo button in the StudioTips Browser to try out the type-ahead demonstration.

The code in the demo window is in the field $event methods and is well commented.

Set Icon Size

When you assign the $iconid property to a pushbutton Studio defaults to a k16x16 size icon. To control the size of the icon when assigning the $iconid you need to do the following:

Do PushButtonRef.$iconid.$assign(2105+k48x48)

Adding the icon size constant to the icon ID controls the size of the icon.

Window Positioning

This section covers various topics relating to opening windows and controlling the size and position of the window instance.

When you open a window instance of a window class, by default, the window size and location matches the size and location of the window class.

There are several problems you can run into with this default behavior:

  1. If the user's screen size is small than the default window class, the window instance will run off the side or bottom of the user's screen.
  2. If you are using the same window class for different instances you may want to have different window sizes for each window instance. This is especially a problem if you want to store the last window size and location for each window instance and user so that you can later reopen it to that same size for the user.

Open Window Instance

You can open a window instance using $open, $openonce, or $openmodal.

; Open a single instance of a window class.
Do $clib.$windows.wWinClass.$openonce('WinInstName') Returns rWin

If you reuse a window class for different window instances you can not use $openonce.

In my applications I declare a unique WinInstID for each window instance. The WinInstID is langauge independent, so that the window title can be mapped by the WinInstID to different languages.

; Open a new instance of a window class.
Do $clib.$windows.wWinClass.$open('UniqueWinInstName') Returns rWin

The possible iLocn parameters are found in the F9 Catalog > Constants tab. (e.g. kWindowCenter, kWindowCenterRelative,kWindowNormal, kWindowStack, kWindowMaximize, kWindowMinimize, ...)

; Open a new instance of a window class.
; kWindowNormal = the same size and position as the window class.
Do $clib.$windows.wWinClass.$open('*',kWindowNormal) Returns rWin

; Open a new instance of a window class.
; kWindowStack = offset from the top window.
Do $clib.$windows.wWinClass.$open('*',kWindowStack) Returns rWin

; kWindowCenter = centre of the screen.
Do $clib.$windows.wWinClass.$open('*',kWindowCenter) Returns rWin

; kWindowCenterRelative = centred relative to the specified window instance.
Do $clib.$windows.wWinClass.$open('*',kWindowCenterRelative,$topwind) Returns rWin

; kWindowMaximize = maximized
Do $clib.$windows.wWinClass.$open('*',kWindowMaximize) Returns rWin

Warning

kWindowMaximize will only work if the window class property $zoombox is set to kTrue.

kWindowMinimize will only work if the window class property $minimizebox is set to kTrue. (I could not get kWindowMinimize to work on opening a new window instance with Omnis Studio v4.1.5 on Mac OS X.)

Click the Run Demo button in the StudioTips Browser for a demo of opening window instances using the various kWindow... constants.

Check Size Window And Position

When a window is instantiated you should check the size and location to make sure it fits within the boundaries of the user's screen and then if necessary move and resize the window so that it does fit.

The following code is used to check the size and position of window instances in StudioTips to make sure they fit on the user's screen. This method can be called from the $construct method, or the $event method On evToTop.

; Superclass method: $setWindowSizeAndPosition

; Only run this code if we are not a subwindow.
If $cinst=$cwind
   
   ; Only run this code if we are not currently minimized.
   If not($cinst.$minimized)
      
      ; Make sure the window is small enough to fit in the screen.
      If $cinst.$height>$modes.$height
         Do $cinst.$height.$assign($modes.$height-25)
      End If
      If $cinst.$width>$modes.$width
         Do $cinst.$width.$assign($modes.$width)
      End If
      
      ; If the window is too low or high on the screen, adjust it.
      If $cinst.$top+$cinst.$height>$modes.$height
         Do $cinst.$top.$assign($modes.$height-$cinst.$height-25)
      Else If $cinst.$top<0
         Do $cinst.$top.$assign(0)
      End If
      
      ; If the window is too far left or right on the screen, adjust it.
      If $cinst.$left<0
         Do $cinst.$left.$assign(0)
      Else If $cinst.$left+$cinst.$width>$modes.$width
         Do $cinst.$left.$assign($modes.$width-$cinst.$width)
      End If
      
   End If
   
End If

Quit method kTrue

Save Window Size And Position

When a window instance is being close you can save the window size and position so that when the window instance is reopened you can restore it to the same size and location.

If the window class is being used for a specific window instance you can save the window instance $left, $right, $width, and $height properties to the window class properties.

; Superclass method: $saveWindowSizeAndPosition

; Save the window instaance size to the window class size if we are not a subwindow.
If $cinst=$cwind
   
   ; If the window is minimized, bring it to the front, then save the current size/location.
   If $cinst.$minimized
      Do $cinst.$bringtofront()
   End If
   
   ; Set a reference to the window instance's class.
   Set reference rClass to $cinst().$class
   
   ; Top and left can not be assigned as negative values.
   Do rClass.$top.$assign(pick($cinst.$top>0,0,$cinst.$top))
   Do rClass.$left.$assign(pick($cinst.$left>0,0,$cinst.$left))
   
   Do rClass.$width.$assign($cinst.$width)
   Do rClass.$height.$assign($cinst.$height)
   
End If
Quit method kTrue

Warning

If you attempt to assign a negative value to the $left or $top window class property Omnis Studio will not assign the value. That is why the sample code above uses the pick() function for the $left and $top values.

In my applications I reuse the same window class for many different window instances. Saving the window instance size and location to the window class is not really a great solution because when you issue a new release to the client, they will lose all of their last window size and location. A better solution is to use a unique ID for each window instance and then save/restore the window size and location for each window instance in the user's profile in the database. Normally you will have a table in the database where you have a record for each user and store information like their password, email address, etc. Added a WindowsUsedList column to this table and on opening the application load the WindowsUsedList into memory, and on closing the application save the WindowsUsedList back to the user profile in the database. The list definition of the WindowsUsedList could be: (WinInstID, Left, Top, Width, Height, LastUsedDateTime). Instead of saving and restoring the window size and location from the window class, you save and restore the window save and location from the WindowsUsedList.

In my applications I create an oWindows object class which is instantiated by the Startup_Task variable wn. The oWindows object can be made responsible for loading and saving the WindowsUsedList. The oWindow methods $saveWindowSizeAndLocation(prWinInst) and $restoreWindowSizeAndLocation(prWinInst) are called from the $construct and $destruct methods of each window instance as follows:

; Superclass window $construct method.

; Set the window size and location.
Do wn.$restoreWindowSizeAndLocation($cinst) Returns FlagOK

; Superclass window $destruct method.

; Save the window size and location.
Do wn.$saveWindowSizeAndLocation($cinst) Returns FlagOK

Working Messages

Omnis Studio provides a working message which you can open to notify the user whenever an operation is going to take some process. If you are looping through a list of records you can recalculate the working message text to include the current line being processed and the number of lines to be process. If you recalculate the working message text, you must redraw the working message.

Calculate cWorkingMssg as con("Looping through list...")
Working message {[cWorkingMssg]}

For %N from 1 to cCount step 1
   
   Calculate cList.$line as %N
   
   ; We are looping through the list.
   Calculate cWorkingMssg as con("Looping through list...",kCr,%N," of ",cCount)
   Redraw working message
   
End For

Speed Tests

Working messages will reduce performance in a loop each time you redraw a working message. You should avoid redrawing a working message every time around the loop. Use the mod() function to only draw the working message every 100 or 500 records depending on the size of the list and the speed which the loop is expected to be processed.

Calculate cWorkingMssg as con("Looping through list...")
Working message {[cWorkingMssg]}

For %N from 1 to cCount step 1
   
   Calculate cList.$line as %N
   
   ; We are looping through the list.
   If mod(%N,500)=0
      Calculate cWorkingMssg as con("Looping through list...",kCr,%N," of ",cCount)
      Redraw working message
   End If
   
   
End For