Sw4   >   Windows   >   Windows (All Contents)

Windows

This section covers topics related to window instances and window classes.

Window Instances List Editor

All window instances in StudioWorks must included in the oWindows master list before you can open an instance of the window. Each module adds their own window instances to the master list. The window instances lists are returned by the $:WindowInstancesList property method of the oWindowList objects.

StudioWorks automatically declares a default list and edit window instance for each schema class in your application. For example if you have a schema class named sAuthor with a server table name Author, StudioWorks will automatically declare the window instances AuthorList and AuthorEdit for you. Each window instance is called a wininstid and must be unique across all modules.

For each default window instance StudioWorks automatically sets its sqlclassname property by searching for a query or schema class based on naming conventions. The following example is for the AuthorList window instance:

  1. qAuthorList - if found uses it. (For AuthorEdit StudioWorks looks for qAuthorEdit)
  2. qAuthor - if found uses it.
  3. sAuthor - uses the base schema class if one of the above query classes is not found

You add/modify/delete window instances using the Window Instances List Editor located under a tab of the Window Instances Browser. Instruction are provided in the editor window. If you change any of the properties of a default wininstid you become responsible for all further changes.

Tooltips are provided for the various fields (window instance properties) of the Window Instances List Editor.

Default Commands

attachChangeRecordObserverYou can set default commands to be excecuted when an evOK or evCancel is received by the wShell window.

The wShell window's $event method traps On evOK and On evCancel and sends a $doCmnd(pCmnd) message to the current subwindow. The pCmnd parameter is set as follows:

The $doCmnd method, checks for pCmnd = to 'defaultokay' or 'defaultcancel' and changes the parameter value to the return value of $:DefaultCmnd_evOK or $:DefaultCmnd_evCancel as applicable.

The base window defaults have been set to the following:

If you want to change the defaults you can either override the $_constructSetDefaultCmnds method in your subclass window, or you can set the values by sending using the $:DefaultCmnd_ev...$assign method.

MainScrollbox

You should place your edit fields for any edit type subwindow inside of a kScrollbox object named MainScrollbox. (The exact name and case is important.)

The reason for doing this is to allow the edit fields to scroll independent of the swToolbar subwindow field which is normally at the top of an edit type subwindow.

If we were to set the entire edit subwindow field to scroll, the swToolbar would scroll off the top if user scrolled down the subwindow field. The solution was to put all of the edit fields inside of a kScrollbox field, thereby allowing the user to scroll down the MainScrollbox field without affecting the swToolbar field.

Note

When you Developerize a wEdit_autoconfig window, StudioWorks automatically adds the MainScrollbox for you and puts the edit fields inside the scrollbox.

If you have an edit subwindow that does not scroll when it should, you likely need to add a MainScrollbox field to the subwindow. Here's what to do:

  1. Add a kScrollbox field to the window class.
  2. Set the following properties of the scrollbox

    $name - MainScrollbox
    $fieldstyle - CtrlSubWindow
    $horzscroll - kFalse
    $vertscroll - kFalse
  3. Stretch the window class size, then stretch and move the kScrollbox field so that it is big enough to contain the edit fields.
  4. Move all the edit fields into the MainScrollbox.
  5. Set the $edgefloat property of the MainScrollbox to kEFposnClient
All going well the next time you open an instance of the subwindow vertical and horizontal scrollbars will appear as needed whenever the window is sized smaller than the space needed to display all of the edit fields.

Replace Shared WinInstID

There are several shared module libraries that are included with StudioWorks. You set whether or not to include each library in the $:AppLibsList method of oConstants. (See Configuration > App Libraries List)

When you include a library, the window instances declared by the library's oWindowList object are automatically added to your application's windows list and navigation menus.

You may have a situation where you want to modify or replace a window instance declared by a StudioWorks shared library. Making the change directly in the shared library would be a problem because StudioWorks updates would wipe out your changes.

One technique you can use is to add the exact same WinInstID to the oWindowsList object in your main library or one of your module libraries. For the WinInstID which you add, you would point it to your custom window class.

You then need to remove the shared library WinInstID from the master windows list. You can accomplish this as follows:

  1. Create a subclass of swGui4.oWindows in your main library.
  2. Point your main library startup task variable, wn, to the oWindows class in your main library.
  3. Override the $buildWindowsList method of oWindows and add code which runs the superclass method and them removes the shared library WinInstID. The following code snip gives you an idea of what the code would look like.

    ; Superclass builds the list of all WinInstIDs.
    Do inherited Returns FlagOK
    If FlagOK
       
       ; Find and remove the shared library WinInstID that is being replaced by a custom WinInstID.
       Calculate HomeLibName as 'swContacts4'
       Calculate WinInstID as 'PersonEdit'
       If iWindowsList.$search($ref.homelibname=HomeLibName&$ref.wininstid=WinInstID,1,0,0,0)
          Do iWindowsList.$remove(kListDeleteSelected)
       End If
    End If
    Quit method FlagOK

Field Handling

The StudioWorks field handling classes provides you with the following benefits:

  1. The entry field which has the focus is highlighted with a colored border.

    Blue border for normal fields. Red border for lookup fields. Dashed border for uppercase only fields.

    The colored border makes it easier for the user to spot the current field, and hints to them if it is a lookup type ahead field. If the field is uppercase the dashed border hints to the user that they do not need to press the shift or caps lock key.
  2. Decorator types can be assigned to fields.
    click = cream color
    displayonly = grey color
    notes = blue color
  3. The decorator types can be mode sensitive.
    For example a clickedit decorator type will be:

    normal in new mode
    click in edit mode
    displayonly in view mode

    All fields are displayonly in view mode.
  4. The displayonly decorator type allows users to select text and copy it to the clipboard, but rejects any keystrokes which would modify the text.
  5. For windows where space is limited a notes decorator type can be used for entry fields that have lengthy text. The entry field in the window can be the size of a normal entry field. When the user clicks on the blue color notes field, the notes field handler opens a multi-line field/window for the user to edit or view the lengthy text. When the user leaves the large multi-line field the column in the data list is updated and the multi-line field/window is closed.
  6. Lookup fields which automatically find and display a list of matching lookup records as the user types in the lookup field. After the user selects a lookup record the lookup field handler automatically sets the foreign key and updates the related fields in the window.
  7. The notes and lookup fields work with complex grids. This is a big benefit!
  8. The decorator types are set in the SQL meta-data allowing the field handling and decoration to be controlled by the schema or query class which is being used for a particular window instance. You can override the schema class decorator type for a field in a query class that is used for a particular window.

FieldDecoratorTypes_new.gif

FieldDecoratorTypes_new.gif

Note

A new field handler structure and classes was introduced in the 2008-03 release of StudioWorks. The new field handling structure and classes make it easier for StudioWorks developers to override and customize field handling. The field handler classes are located in the Field Handling folder of swGui4.

Note

StartNewApp has a Field Handler Demo module with simple windows that demonstrate various aspects of the field handler. You can shift+click on buttons in the demo windows to look at the code behind the window and step through the field handler code.

Meta-Data Decorator Types

The easiest way to control field handling is by setting the Decorator Type under the Display Properties section of the SQL Meta-Data Editor.

The decorator types supported in StudioWorks are:

  1. normal (default) - white background, always enabled.
    If the decorator type is not set it defaults to normal.
  2. displayonly - grey background and disabled. User can click on the field to select and copy text to clipboard, but can not enter, delete, or paste text into the field. All fields default to displayonly in view mode.
  3. displayonlyedit - uses displayonly properties in edit mode. Default properties in new mode.
  4. click - cream forecolor and disabled. User can click on the field to enable the field.
  5. clickedit - uses click properties in edit mode. Default properties in new mode.
  6. clicknew - uses click properties in new mode. Other modes use displayonly.
  7. notes - blue forecolor and disabled. Pops opens a multi-line entry field in a frameless window for editing if the user clicks on the field. The frameless window is positioned over the notes field and looks and behaves like a simple multi-line entry field. The use can escape, tab, enter, or click elsewhere to close the frameless window.
  8. notesdisplay - has the same behavior as the notes field except the multi-line entry field in the frameless window is set to displayonly.
Note

The decorator type values can each field are listed in the decoratortype column of the SQL lists columns list. You can get the columns list from a StudioWorks SQL defined list from the $:ColsList property method.

Decorator Types List

The FieldHandlersFactory_Task provides oFieldHandlerController with a $:TypesList. The list has 4 columns:

  1. decoratortype - The SQL Meta-Data specified decorator types previously mentioned.
  2. handler_new - The field handler to be used in new mode.
  3. handler_edit - The field handler to be used in edit mode.
  4. handler_view - The field handler to be used in view mode.

For example a clickedit decorator type would have the following values:

  1. decoratortype = clickedit
  2. handler_new = normal
  3. handler_edit = click
  4. handler_view = displayonly
oFieldHandlerController uses the types list to set the handler_[mode] columns in its field properties list.

Field Properties List

The field properties list is a list of all the fields in a window instance which the field handler could be involved with.

The field properties list is defined from sFieldHandlerProperties_listdef. It has the following columns:

  1. objident - the $ident property of the field.
  2. objname - the $name property of the field
  3. rownum - Default zero (0). Greater than zero is used for complex grid exceptions.
  4. decoratortype - the decorator type specified by the meta-data.
  5. handler_new, handler_edit, handler_view - one column for each mode specifying the field handler to be used for the mode.
  6. lookup - true if the field's schema class is related to the base schema via a foreign key.

    Following the lookup column is a series of columns for all lookup related properties (lookuprefs, lookupsqlclassname, lookuprefsgroup, lookuprefssubgroup, lookupmandatory, lookupcaninsert, lookupstartchar, lookupwheretext, lookupcolname, lookupservertablename, lookupmaincolname, lookupsetcolslist, lookuplistfieldcalc, lookupcontains, lookupisnumber)

A window class can include a field properties list with the $initialize message it sends to oFieldHandlerController. To improve performance oConcretizer builds and stores a field properties list in the $userinfo property of a runtimized window class when it creates it.

If the field properties list is not supplied, oFieldHandlerController builds a list on-the-fly by sending a $retFieldPropertiesList message to the oFieldHandlerPropertiesList object.

If the data list of a window is based on a query class that joins multiple tables any field which is not part of the base schema of the query class is automatically assigned a decoratortype as follows:

  1. lookup - If the base schema has a foriegn key which references a column in the field's table. A parent record.
  2. displayonly - If the base schema does not have a foreign key referencing a column in the field's table. The table could be a grandparent, great-great grand parent...

When oFieldHandlerController receives an $event message, is searches the field properties list for a matching objident, then based on the current mode, it sends a message to the appropriate field handler object.

You can change the decoratortype for any field, add new fields, or add complex grid exceptions to the field properties list, using the $addsetFieldDecoratorType method of oFieldHandlerController.

You can modify the field properties list by getting it from the field handler controller, modifying the list, and then setting it back to the controller.

See the section on Overriding Field Handling for more details.

Field Handler Modes

The current mode affects which field handler object is called to handle a field's events. The handler_new, handler_edit, handler_view columns in the properties list specify which field handler object to use for the appropriate mode.

For a clickedit decorator type the handler_[mode] column values would be as follows:

  1. handler_new = normal
  2. handler_edit = click
  3. handler_view = displayonly

Each time the mode is set in the window class, the window must send a $setMode(pMode) message to the oFieldHandlerController.

If the value of pMode is different than the controller's current mode, the controller sets its internal iMode state to the specified mode, and then loops through the field properties list sending a $setField message to the appropriate field handler specified in the handler_[iMode] column.

When an $event message is passed to oFieldHandlerController it searches for the current field in the field properties list, and then forwards the message to the field handler type specified in the appropriate handler_[iMode] column.

With the clickedit decorator type example above the oFieldHandlerController will send messages to the following field handlers for the specified modes:

  1. new mode - send messages to oFieldHandler_normal
  2. edit mode - send messages to oFieldHandler_click
  3. view mode - send messages to oFieldHandler_displayonly

Field Handler Objects

The oFieldHandlerController delegates most of the field handling to field handler objects.

The field handler object naming syntax is: oFieldHandler_type

The following field handler objects can be found in swGui4.

The lookup and lookupclick field handlers are assigned to lookup fields by StudioWorks when the field properties list is built.

Field Decorator Objects

The field handler objects delegate field decoration to field decorator objects.

The field decorator object naming syntax is: oFieldDecorator_type

The following field decorator objects can be found in swGui4.

Normally a field is decorated by setting its $fieldstyle property. There are a series of StudioWorks field styles which you must have in the #STYLES class of your library in order for field decoration to work. The StudioWorks field styles are:

Tip

You can use the StudioTips SyncStyles utilities to copy the StudioWorks field styles from swGui4 to your library's #STYLES class.

Complex grids add a wrinkle to field decoration because you can not set the $fieldstyle for an individual cell. For complex grids we have to set each property separately to change the field decoration of each field. The StudioWorks field styles for complex grids are:

The oFieldDecorator_type objects will use the inset border or no border style based on which $fieldstyle the field is set to in the complex grid.

Field Handlers Factory

The FieldHandlersFactory_Task is a task class which is used to reduce memory use and improve performance. The StudioWorks field handling structure is based on the flyweight design pattern.

When oFieldHandlerController is initialized it checks for a task instance of the FieldHandlersFactory_Task. If found it uses the existing instance. If not found, it opens an instance.

The FieldHandlersFactory_Task finds and creates a single instance of each oFieldHandler_type object class and a single instance of each oFieldDecorator_type object class. Object reference datatypes are used to ensure that just one instance of each of these object classes is created. The object reference to each of these instances are stored in an iHandlersRow and an iDecoratorsRow in the FieldHandlersFactory_Task.

The handler and decorator type suffix is used for the row column name.

iHandlersRow would have columns named: click, displayonly, lookup, notes, notesdisplay

iDecoratorsRow would have columns named: click, displayonly, hasfocus, normal, notes

When oFieldHandlerController is initialized it gets these rows from the FieldHandlersFactory_Task by sending it a $:HandlersRow message and a $:DecoratorsRow message. Each column in the row has a pointer to the single instance of the respective handler or decorator. All instances of oFieldHandlerController point to the exact same instance of each handler and decorator.

Developers can create their own oFieldHandler_type and oFieldDecorator_type classes in their main library. Handlers and decorators found in the main library take priority over ones by the same name in swGui4. As long as your handler has the same methods and parameters at those found in swGui4 you are free to tweak your handler or decorator code to suit your application's needs.

Warning

One potential design flaw in the FieldHandlersFactory_Task is that a single task instance of the factory is used for all StudioWorks applications open under the same instance of Omnis Studio. When a second StudioWorks app is opened it finds and uses the existing task instance of the factory. If the second StudioWorks app has custom field handlers or decorators they will be ignored. The solution is to have each StudioWorks app open its own task instance of the factory. This is relatively easy to implement, but it uses more memory, and will likely never be an issue for 99% of StudioWorks developers.

If you add custom handlers or decorators you need to quit Omnis Studio and reopen it in order to destroy the factory task instance. Just closing and reopening the StudioWorks app won't destroy the factory task instance.

Field Handling with Complex Grids

StudioWorks normally decorates fields by changing the $fieldstyle of the field to the appropriate type.

For a click field, the $fieldstyle is set to swFieldNoFocus_click, until the user clicks on the field as which time the $fieldstyle is set to swFieldFocus

Complex grids increase the complexity of field handling because you can not set the $fieldstyle for individual cells of a complex grid. To decorate a field in a cell of complex grid we must set each field property individually, and we must specify the row number in the complex grid for each property we set.

The oFieldHandler_type and oFieldDecorator_type series objects check if the specified field is inside a complex grid and if so, executes complex grid friendly handler and decorator code.

One advantage of the StudioWorks field handling structure is that lookups and notes decorator type fields automatically work in complex grids. This is a big benefit!

FieldHandlerComplexGrid.gif

Field Handling Sequence of Events

The field handler classes are located in the Field Handling folder of swGui4.

The oFieldHandlerController object is instantiated by the ivar ifld in the window instance. There will be one instance of oFieldHandlerController for each window (subwindow) instance.

When the window is instantiated the following sequence of events relating to field handling normally takes place:

  1. Omnis Studio sends a $construct message to the window instance.
  2. The $construct method of the window class sends an $initialize message to oFieldHandlerController passing in a reference of the window instance, the window's main data list, and optionally the field properties list.

    Note

    Runtimized window have the field properties list stored in the $userinfo property of the runtimized window class.

  3. If a field properties list is not passed from the window to the controller, the controller builds the field properties list by sending a $retFieldPropertiesList message to the oFieldHandlerPropertiesList object class.
  4. The window instance receives a $_setMode message from StudioWorks.
  5. The $_setMode methods sends a $setMode(pMode) message to oFieldHandlerController. If pMode is different that the current mode state of oFieldHandlerController it loops through the field properties list and decorates all of the fields for the specified mode.

    Field decoration is accomplished by sending a $setField message to the appropriate oFieldHandler_type object class which then delegates decoration to the appropriate oFieldDecorator_type object class.
  6. The window instance receives a $setCurrField message from StudioWorks.
  7. The $setCurrField methods sends a $retFirstEnabledEntryFieldName message to the field handler controller which then searches the field properties list for the first enabled field and returns the field name to the sender, who then set the current field by issuing a Queue set current field {FieldName}.
  8. The currrent field receives an $event message from Omnis Studio with the pEventCode = evBefore.
  9. The field allows the event to pass up to the window's $control method.

    Note

    Note: If you have an On evBefore in the field $event method, the event will not pass to the window's $control method. If you want StudioWorks field handling for the field you must either remove the On evBefore or end it with Quit event handler (Pass to next handler). The same goes for evAfter, evClick, and evKey. For evKey the field or library's $keyevents property must be set to kTrue.

  10. The $control method of the window class sends an $event message to oFieldHandlerController which then searches for the field in the field properties list and delegates the $event message to the appropriate oFieldHandler_type object class for the current mode. The delegate field handler does the appropriate event handling and then delegates field decoration to the appropriate oFieldDecorator_type object class.
  11. If the field is a lookup type field, oFieldHandlerController then sends a $control message to oFieldHandlerLookupTypeAhead which takes care of all the lookup related work.
  12. When the user is in the field each keystroke generates an evKey $event message which is passed on to oFieldHandlerController. For displayonly fields the handler discard keystrokes which would change the contents. For lookup fields oFieldHandlerLookupTypeAhead use the keystrokes to find and display matching lookup records.
  13. When the user leaves the field Omnis Studio sends an evAfter $event message to the field. The field handler controller forwards the $event message to the appropriate field handler which then reverts the field to its no focus state.

Overrriding Field Handling

Overriding field handling and decorating is relatively easy. Knowing the sequence of events, you can decide where to best intercept and modify the field handling.

  1. Meta-Data - If possible, control field handling and decorating in the meta-data. You set the decoratortype for a column in the schema class. If you want a different decoratortype for a particular window, create a query class for the window, and in the meta-data editor for the query class right-click and Override the schema class decoratortype and set it to the whatever decorator you want for that query/window.
  2. $event method - You can intercept any event in the $event method of the field. You can trap any event with the On series of commands (On evBefore, On evAfter, On evKey, On evClick) The event you trap will not get to oFieldHandlerController unless you end with Quit event handler (Pass to next handler). This gives you full control over event handling.

    You can manually decorate a field from the $event method by sending a $decorateField(prField,pDecoratortype) message to oFieldHandlerController. This only decorates the field, it does not affect the field handling.
  3. Field Properties List - You can modify the field properties list which oFieldHandlerController uses.

    To add a field or set an existing field in the field properties list you can send a $addsetFieldDecoratorType message to the field handler controller. This method is particularly useful if you want to set special complex grid exceptions.

    ; Set the OrderID field in row 2 of the complex grid to 'displayonly'
    ; (prField,pDecoratorType,pRowNum_opt)
    Do ioFieldHandlerController.$addsetFieldDecoratorType($cinst.$objs.OrderID,'displayonly',2)

    ; Add a displayonly Status field.
    ; (prField,pDecoratorType,pRowNum_opt)
    Do ioFieldHandlerController.$addsetFieldDecoratorType($cinst.$objs.Status,'displayonly')



    To modify the entire field properties list you can get it from the field handler controller by sending a $:FieldPropertiesList message to the controller, modify the list, then assign it back to the controller using the $:FieldPropertiesList.$assign method.

    ; Get the field properties list and add a special 'approved' mode.
    Do ioFieldHandlerController.$:FieldPropertiesList() Returns List
    Do List.$cols.$add('handler_approved',kCharacter,kSimplechar,50)

    ; Set the handler to click for any displayonly fields.
    Do List.$sendall($ref.handler_approved.$assign('click'),List.handler_edit='displayonly')

    ; Assign the modified field properties list back to the field handler controller.
    Do ioFieldHandlerController.$:FieldPropertiesList.$assign(List)

  4. Field Handler and Decorator Object Classes - You can copy or subclass any of the StudioWorks field handler and decorator object classes to your main library and override the code in those objects in your main library. When the FieldHandlersFactory_Task is instantiated it looks for field handlers and decorators in your main library and uses yours ahead of the ones by the same name in swGui4.

You can also create additional field handlers and decorators. As long as you following the naming convention syntax for field handlers and decorators, the FieldHandlersFactory_Task will find and add them to the handlers row and the decorators row. If you get to this level of overriding StudioWorks field handling you will likely need to also take over the FieldHandlersFactory_Task. Ask questions via the StudioWorks members list if you get to this stage.

Note

StartNewApp has a Field Handler Demo module with simple windows that demonstrate various aspects of the field handler. You can shift+click on windows in the demo to look at the code behind the window and step through the field handler code

Lookups

In a relational database you reduce data duplication by putting similar data into separate tables and linking the data with foreign keys. Rather than storing the same CountryName over and over in the Address table, we store the all the countries in a Country table and then save the primary key of the correct Country record in each record of the Address table.

When the user creates a new Address record and they get to the CountryName field it is helps to provide the user with a list of countries from the Country table. If they type the letter U they appreciate it if you open a droplist of all the countries in the Country table that begin with the letter U. After they select a country, the Country_fkey field needs to be set in the Address record to the primary key of the selected country.

Based on the SQL meta-data the StudioWorks framework can automate all of the above. The StudioWorks tutorial takes you through the basics of setting this up. This section dives into the details of controlling lookups in StudioWorks.

Refs Lookups, a non-linking variation of the above, is also covered in this section.

Lookups Meta-Data

The following meta-data properties are used to control lookups:

  1. lookupsqlclassname - This specifies which SQL class is to be used when fetching and displaying lookup records of a schema class. By default the schema class points the lookupsqlclassname to itself. A developer can create a special lookup query class and then point the schema class's lookupsqlclassname to the query class.
  2. lookupcaninsert - If set to false users will not be able to insert new lookup records from the lookup droplist field. They will only be able to add new lookup records by opening to the lookup records window. If set to true and the user has security clearance, the user can create new lookup records on-the-fly.
  3. lookupstartchar - This is the number of characters the user has to type before matching records are fetched from the database. If the lookup table is a fetchall table, the lookupstartchar is set to 1, otherwise the default is 2. A developer can increase or decrease the lookupstartchar value except for fetchall tables which must be 1. The larger the number of lookup records, the higher you will want to set the lookupstartchar property.

    If you set the lookupstartchar to a value greater than the length of the field, (e.g. 99), the field handler will not do a lookup until the user leaves the field On evAfter. If a single matching record is found it will be used, if more than one match is found the user will be prompted to select a record.
  4. lookupcontains - If set to true, the any lookup record which contains the user entered value will be fetched. If set to false only records which begin with the user entered value will be fetched. If the lookupstartchar is set to 1, lookupcontains must be set to false, otherwise far to many lookup records will be fetched. The default value for lookupcontains is true if the lookupstartchar is great than one.
  5. lookupwhere - If lookupwhere text is entered in the meta-data it will be added to the SQL text that is used to fetch lookup records.
  6. includeinprompts - Only the columns which are checked as includeinprompts are displayed in the lookup droplist. It is important that at least one column in the lookup SQL class is set to includeinprompts.

There are 3 different locations in the SQL Meta-Data Editor where you can set lookup meta-data:

  1. Schema class meta-data In the schema class meta-data you can set the lookupsqlclassname, lookupcaninsert, lookupstartchar, lookupcontains properties.
  2. Query class meta-data In the query class meta-data you can set the lookupcaninsert, lookupstartchar, lookupcontains properties.

    By default these properties are inherited from the base schema class. You can right-click and override any of the properties to set them to something different than the base schema.
  3. Query columns meta-data In the query columns meta-data you can set the lookupsqlclassname, lookupcaninsert, lookupstartchar, lookupcontains, lookupwhere properties.

    By default the lookupsqlclassname property is set to the lookupsqlclassname property of the lookup column's schema class. The lookupcaninsert, lookupstartchar, lookupcontains, properties are then inherited from the SQL class specified by the lookupsqlclassname. Every one of these lookup properties can be overridden at the query column level and set by the developer. The lookupwhere property can be entered by the developer.
As you can see, the developer has fine tune control over lookups in the meta-data. For each query class column you can decide which lookup query class to use, whether or not the user can insert new lookup records on-the-fly, the number of characters to type before starting the lookup, whether to search for contains or begins with, and additional where text to include when fetching records. To minimize the amount of meta-data you have to enter and maintain all the lookup meta-data properties are inherited from the schema class.

Lookup Fields

Lookup fields help the user join a record in the main table to a record in the lookup table. e.g. Address to a Country.

To use the StudioWorks framework lookups you will need an edit query class which joins the main table to the lookup table and includes at least one includeinprompts column from the lookup table. e.g. qAddressEdit which includes all the Address table fields plus the CountryName from the Country table.

When the field handler generates the field properties list is detects any lookup columns in the query class and sets the lookup... properties based on the meta-data.

When the lookup field gets the focus (evBefore) the field handler detects that it is a lookup field and the oFieldHandlerLookupTypeAhead object is asked to handle the field events.

Based on the lookup meta-data the field handler will suggest to the user possible lookup records to choose from based on the characters they type into the lookup field.

There are some special power user tricks built into the lookup field handler:

  1. If the user types a ? character a list of all possible lookup records up to a the fetch batch limit of 50 records will be listed. A more button appears at the bottom of the droplist if there are more records to fetch.
  2. If the user types one or more characters followed by a ? character a list of all possible lookup records matching what they entered less the ? character will be listed. The fetch batch limit will be ignored.
  3. If the user types +? a list of all possible lookup records with no fetch batch limit will be listed.
After the user leaves the lookup field (evAfter) the field handler updates the foreign key in the main table with the selected lookup record.

Intercepting Lookups

The developer can intercept the lookup field handler at several locations:

  1. In the $event method of the lookup field.

    You can trap any event (evBefore, evKey, evAfter) in the $event method of the field. Always remember to Quit event handler (Pass to next handler) if you want the field handler to do anything.
  2. Add a $setLookup method to the lookup field with the parameter pLookupRow.

    When the lookup field handler has finished doing its work it sends a $setLookup message to the lookup field and passes the current lookup list as a parameter.
  3. Add a $setMainListValues method to the lookup field with the parameter pLookupRow to intercept the lookuup field handler setting the main list values. If a $setMainListValues method exists the $setLookup message will not sent to the lookup entry field.
  4. Add a $promptLookupRecordNotFound and/or$promptNewLookupRecord and/or $promptNewCopyLookupRecord method to the lookup field if you want to take over these methods. The lookup field handler checks for these methods before prompting the user, and if found, passes control to the lookup field method.
  5. $:LookupOrderBy - If you add this method to the lookup entry field, the lookup field handler will take the value you return from this method and pass it as the pExtraSQL_opt parameter it sends with the $selectWhere(pWhere) message to the table class when fetching the records to be listed in the lookup droplist. The $:LookupOrderBy method must return a valid SQL string that begins with ORDER BY
  6. $:LookupWhere - If you add this method to the lookup entry field, the lookup field handler will take the value you return from this method (must be a WhereRow) and pass it as the pWhere parameter it sends with the $selectWhere(pWhere) message to the table class when fetching the records to be listed in the lookup droplist. The $:LookupWhere method can be as complicated as you like. It is often used to reduce the list of lookup records based on data the user has entered in other fields in the same window.
  7. $:SelectDistinct - If you add this method to the lookup entry field and return kTrue, the lookup field handler will set to kTrue the pbSelectDistinct parameter it sends with the $selectWhere(pWhere,pExtraSQL,pbSelectDistinct) message to the table class when fetching the records to be listed in the lookup droplist. You will need to create a special query class when using select distinct because the combination of all the columns in the query class must be distinct. e.g. If you include the primary key in the select distinct all of the records would be listed, negating the effect of using select distinct.

Lookup List

The lookup list is actually a window class, wLookupList, which is positioned directly below the lookup field and made to appear as a droplist.

The oFieldHandlerLookupTypeAhead controls the lookup list.

The lookupsqlclass is used to define and fetch the lookup records list. Only the includeinprompts columns are displayed in the lookup list.

Refs Lookups

The Refs table can be used for non-relational lookups. These would be lookups where you want to copy the lookup list value to the record. You may want to give the user a list of suggested job titles or give the user 3 possible choices to enter for the person's gender. (F=Female, M=Male, Blank=Unknown)

To use the Refs table for lookups you need to create a set of Refs records with the RefsType set to lookup.

The following schema class columns meta-data properties are used to point to the Refs table lookup records which you create:

  1. validaterefsgroup - The RefsGroup column value in the Refs table records.
  2. validaterefssubgroup - The RefsSubGroup column value in the Refs table records.
  3. validaterefsmandatory - If set to true, the user must select one of the Refs lookup records. If set to false, the user can select one of the Refs lookup records or enter their own value.

The RefsSortOrder can be used to controlling the order of the lookup records in the list. The RefsDisplayText and RefsDesc will appear in the lookup list. (If they match, only the RefsDisplayText will appear in the lookup list). The RefsDisplayText will be copied to the actual lookup field.

If you want to permit the user to leave the field empty you need to create a Refs lookup record in your group/subgroup that has the value set to blank.

Troubleshooting Lookups

There are several common problems that will stop the lookup field handling from working.

  1. The $keyevents property must be set to kTrue in the Action tab properties of the library which contains the window class.

    If you use Programmer Menu > Create New Module..., the StudioWorks framework should set the $keyevents property to kTrue for you.
  2. The $event method of the lookup entry field has On evBefore and/or On evAfter in the method and nothing to pass the event up to the next handler.

    On evBefore
    ; This traps the evBefore event but fails to pass it to the field handler.

    On evAfter
    ; This traps the evAfter event but fails to pass it to the field handler.



    This is easy to do if you manually drag an entry field out of the Component Store. To prevent this from happening I usually remove the On ev... lines of code from the kEntryField inside the component store.
  3. You have added some custom event handling code to the lookup entry field and have not ended your custom code with Quit event handler (Pass to next handler).

    On evBefore

    ; Do my special custom code.

    ; Always end with this next line, unless you don't want the field handler to process the event.
    Quit event handler (Pass to next handler)

  4. You are attempting to do a lookup on a number field. It is not possible in SQL to do a LIKE on number fields. The lookup field handler will switch to fetching exact matches if you try to do a lookup on a number field.

Field Handler OLD

StudioWorks has a field handler object, oFieldHandler, which is responsible for field decoration, enabling and disabling fields, and type-ahead lookups.

The type-ahead lookups field handling is delegated to oFieldHandlerLookupTypeAhead and are discussed under the topic, Lookups.

The default field handling for entry fields in StudioWorks is as follows:

  1. evBefore the field is given a white forecolor and a blue border to indicated that focus is on that field.
  2. evAfter the field is reverted to its previous state.

Non-standard field handling is accomplished by setting the Display Properties - Decorator Type in the SQL Meta-Data Editor. The decorator types supported are:

  1. displayonlyalways - grey background, use can click on the field to select and copy text to clipboard, but can not enter or paste text into the field.
  2. displayonlyedit - uses displayonlyalways properties in edit mode. Default properties in new mode.
  3. clickalways - cream forecolor and disabled always. User can click on the field to enable the field.
  4. clickedit - uses clickalways properties in edit mode. Default properties in new mode.
  5. autoexpand - field expands to multi-line entry on evBefore and returns to normal size on evAfter.
  6. notes - blue forecolor and disabled. Pops opens a multi-line entry field in a small window for editing if the user clicks on the field.
  7. multiline - Default properties, but uses a kMultilineEntry field instead of a kEntry field for autoconfig and developerize windows.

How the Field Handler Works

The oFieldHandler object is instantiated by the ivar ifld in the window instance which makes use of the field handler. There is one instance of oFieldHandler in each window (subwindow) instance.

When the window is instantiated at the end of the $construct method a $runtimeWinInst message is sent to oConcretizer.

Do conc.$runtimizeWinInst($cinst,iSQLClassName,iWinInstID)

One of the things the concretizer does is generate a list of the fields in the window and the properties for each field based on the SQL meta-data of the SQL class defined from that window. It is a fairly complex operation with the end result of a field properties list that is defined using sFieldProperties_listdef. The field properties list is cached in the $userinfo property of the window instance (or window class for when the window is runtimized)

The field properties list is what drives the field handling behavior.

When the window receives $_setMode message, the $_setMode method sends an $initializeFields message to oFieldHandler.

Do ifld.$initializeFields($cinst,pMode,pSQLClassName,pExtraFieldsList_opt)

The $initializeFields method gets the fields list from $userinfo, if it hasn't already been fetched for the instance, and then proceeds to set the properties of each field in the window for the specified mode.

Each $_setMode message initializes the fields for the specified mode.

When the user enters a field, Omnis Studio sends a $event message to the field. The $event message passes to the $control method of the window class where you will see code which forwards the message to the field handler.

Do ifld.$control($cobj,$cinst,iMode)

Overrriding Field Handler Properties

You can override the field handler properties, or add fields to the field handler. This can be accomplished through the pExtraFieldsList_opt parameter.

The $_setMode methods sends an $initializeFields message to the field handler each time the mode is set.

Do ifld.$initializeFields($cinst,pMode,pSQLClassName,pExtraFieldsList_opt)

You can get an empty fields properties list from the field handler using the $:EmptyFieldPropertiesList method.

The following $_setMode method sample code adds an extra field to the field handler. The field is one from the sSecurityInfoRow_listdef schema class which has added to the wUsrEdit window class.

; Set the mode var.
Calculate $cinst.iMode as pMode

; Add extra fields from the security row.
If iExtraFieldsList.$colcount=0
   
   ; Define the field handler extra fields list.
   Do ifld.$:EmptyFieldPropertiesList Returns iExtraFieldsList
   
   ; Add the 'securitytimeoutminutes' field as a 'clickalways' field.
   Do iExtraFieldsList.$add()
   Do iExtraFieldsList.$line.$assign($ref.$linecount)
   Do $cinst.$objs.$findname('securitytimeoutminutes') Returns rObj
   Calculate iExtraFieldsList.objident as rObj.$ident
   Calculate iExtraFieldsList.objtype as rObj.$objtype
   Calculate Suffix as mid(rObj().$fullname,len($cinst().$fullname)+2)
   Calculate iExtraFieldsList.refsuffix as Suffix
   Calculate iExtraFieldsList.visible as kTrue
   Calculate iExtraFieldsList.focusfieldstyle as 'swFieldFocus'
   Calculate iExtraFieldsList.nofocusfieldstyle as 'swFieldNoFocus_click'
   Calculate iExtraFieldsList.active as kTrue
   Calculate iExtraFieldsList.click as kTrue
   Calculate iExtraFieldsList.focusfieldstyle_new as 'swFieldFocus'
   Calculate iExtraFieldsList.nofocusfieldstyle_new as 'swFieldNoFocus_click'
   Calculate iExtraFieldsList.active_new as kTrue
   Calculate iExtraFieldsList.click_new as kTrue
   Calculate iExtraFieldsList.focusfieldstyle_view as 'swFieldFocus'
   Calculate iExtraFieldsList.nofocusfieldstyle_view as 'swFieldNoFocus_click'
   Calculate iExtraFieldsList.active_view as kTrue
   Calculate iExtraFieldsList.click_view as kTrue
   
End If

; Send an $initialFields message to the field handler, to update all the field objects to the new mode.
Do ifld.$initializeFields($cinst,pMode,iExtraFieldsList) Returns FlagOK
If FlagOK
   
   ; Update the button menu bar
   Do $cinst.$updateActiveCmnds() Returns FlagOK
End If
Quit method FlagOK

The first time the $_setMode method is run it builds the iExtraFieldsList. Each subsequent time it skips directly to sending the $initializeFields message to the field handler.

You can view all of the swField... styles in the #STYLES class of swGui4.

You can view all of the extra fields list columns in the F9 Catalog > Schemas > swGui4 > sFieldHandlerProperties_listdef. Hovering of a columm gives you a tooltip with more information.

If you put a breakpoint in the $initializeFields method of oFieldHandler you can look at the contents of the iFieldsList to get a sense for what the various properties can be set to.