OpenInsight 10.2 adds a new event called CELLPOSCHANGED to the EDITTABLE control. This is effectively the same as the normal POSCHANGED event but with the addition of an extra parameter called “ContextFlags” that provides more information on why the event was raised.
ContextFlags is a simple bitmask integer that contains the following flags:
Bit Flag Value
If set then the cell position was changed via a keystroke.
If set then the cell position was changed via the mouse.
Equates for these flags can be found in the PS_EDITTABLE_EQUATES insert record:
Equ PS_EDT_CTF_NONE$ To 0x00000000;
Equ PS_EDT_CTF_KEYSTROKE$ To 0x00000001;
Equ PS_EDT_CTF_MOUSECLICK$ To 0x00000002;
Example – testing to see if the position (CARETPOS) was changed via a mouse click:
If BitAnd( ContextFlags, PS_EDT_CTF_MOUSECLICK$ ) Then
// CARETPOS was changed by using the mouse.
Notes on using the CELLPOSCHANGED event
The default promoted system CELLPOSCHANGED event handler performs the same processing as the default promoted system POSCHANGED event handler (i.e. data validation and required checking etc).
If a CELLPOSCHANGED event handler is defined by the developer then a standard POSCHANGED event will not be raised.
To preserve backwards compatibility with existing applications the default promoted system CELLPOSCHANGED event will not be compiled into a control if there is no CELLPOSCHANGED quick event handler. This is to ensure that POSCHANGED is always executed if CELLPOSCHANGED has not been explicitly set for a control by the developer.
CELLPOSCHANGED is available in OpenInsight 10.2 from the Beta 3 release onwards.
One of the new features that was added to the ListBox and TreeListBox controls in version 10 was the ability to use “in-place” editing on the items in the same manner as the Windows Explorer when you press “F2” or double-click an item. If you’ve done any work with the OpenInsight Menu Designers you will have seen this capability in action.
The READONLY property
Enabling in-place editing is as simple as setting the READONLY property to False – once you’ve done that the user can press “F2” while using the control and edit the text of the currently selected item. Pressing “Enter” when editing will update the item text (as will losing focus or selecting another item), while pressing “Esc” will abandon the changes. Obviously that’s a very simple take on the topic and hardly worth a blog post in and of itself, so next we’ll take a look at some of the properties, methods and events that you can use to tailor the editing process.
The EDITING property
This is a simple boolean property that returns TRUE$ if an item is being edited.
The EDITORHANDLE property
This property returns the HANDLE of the editor control if an item is being edited.
The EDITKEY property
By default, hitting “F2” on an item puts the control into “edit mode”, just like the Windows Explorer. However, if you wish to change this then you may use the EDITKEY property to select another key instead. The edit key is a Windows virtual key code and constants for these codes can be found in the MSWIN_VIRTUALKEY_EQUATES insert record.
The EDITOPTIONS property
This property allows you to fine-tune some of the validation options for the editor:
TextCase – Specifies if the text entered is lower-case only, upper-case only or mixed (See the EDITLINE TEXTCASE property for more details).
ValidChars – Specifies which characters may be entered into the editor (See the EDITLINE VALIDCHARS property for more details).
MaxLimit – Specifies the maximum number of characters that may be entered into the editor (See the EDITLINE LIMIT property for more details).
At runtime this property is an @fm-delimited array – constants for use with this property can be found in the PS_LISTBOX_EQUATES insert record.
The INCLUDEEDITORTEXT property
By default getting item text from the ListBox (e.g. via the LIST property) will include the text from an item currently being edited, even if that edited text has not yet been saved. Setting this property to FALSE$ ensures that the returned item text ignores the value in the editor instead.
The BEGINEDIT method
This method allows you to programmatically put the ListBox into edit mode, as if the user had pressed “F2” (or whatever value the EDITKEY property is set to). You may specify the index of the item to edit, otherwise it will default to the current item.
This method allows you to programmatically stop the item editing process, optionally allowing any changes to be accepted as if the “Enter” key had been pressed (the default is to reject any changes as if the “Esc” key had been pressed).
This event is raised when the item is updated via the editor, i.e. the user hit the “Enter” key, the control lost the focus, or the EDITEDIT method was used with the bUpdate parameter set to TRUE$. The event passes the index of the item that has changed as well as the old and new data:
This event is raised when an item leaves edit mode without being updated, i.e. the user hit the “Esc” key or the EDITEDIT method was used with the bUpdate parameter set to FALSE$. The event passes the index of the item that was being edited:
As you can see, setting up the control is fairly easy, but the bulk of the work needs to be done in the HTTPREQUEST event where you examine the contents of the request and return the appropriate content.
Type of control firing the event – this is always “HTTPSERVER”
Unique identifier for returning a response to the client – this is used with the various “SETRESPONSE” methods that set response data.
An @FM delimited dynamic array of data that describes the request. The format is similar to the HTTPRequest argument used in OECGI programming. The full format is described in the PS_HTTPSERVER_EQUATES insert record.
As mentioned above, the RequestHeaders parameter describes the details of the request using a format similar to the format used in OECGI programming. There are some differences that are worth highlighting however:
For a GET request the query values are already parsed into their own fields (<37> and <38>) as an associated multi-value pair. They are not found unparsed in field <1> as per OECGI.
For a POST or PUT request the content is obtained using the GETREQUESTCONTENT method (see below) – it is not passed in the RequestHeaders variable.
Cookies are already parsed into their own fields (<39> and <40>) as an associated multi-value pair.
Headers are already parsed into their own fields (<35> and <36>) as an associated multi-value pair.
Note that out of the box we do not enforce any restrictions or framework on how you handle the request – compare this to classic OECGI programming where the “PathInfo” field is used to determine which “INET_” procedure is executed to fulfill it (via the RUN_INET_REQUEST stored procedure) There is no such requirement with the HTTPSERVER control, and you may create your own framework if you wish (although see the note on RTI_RUN_HTTPSERVER_REQUEST below).
Returning a response
There are several methods described below that you may use to process the content that you return to the client.
Note: With each of these you must use the unique RequestID parameter passed to you in the HTTPREQUEST event.
IPv6 – if TRUE$ then check the IPv6 bindings, otherwise check the IPv4 bindings
Sends the response back to the client. This method should be called when you have finished setting the response details (Note that this is called by the promoted system HTTPREQUEST handler in case you forgot to do it in your own code!).
CookieValue is an @fm-delimited array formatted as follows:
<4> Expires (internal date format)
<5> Max Age (seconds)
<6> Secure (TRUE$/FALSE$)
<7> HttpOnly (TRUE$/FALSE$)
If you have a file that contains the content you wish to return then you should use this method to let the server read the file and return it to the client itself. This offers better performance than reading the contents via Basic+ and using the SETRESPONSECONTENT method as it avoids any unnecessary copying of data.
As part of version 10.2 we have included a sample HTTPREQUEST event handler called RTI_RUN_HTTPSERVER_REQUEST which you can examine and copy for your own applications if you wish. It emulates the core behavior of the OECGI RUN_INET_REQUEST handler in that it uses the “PathInfo” field to determine the stored procedure to fulfill the request. In this case it looks for a matching procedure that has the prefix “HTTPSVR_” and we have included a couple of example “HTTPSVR_” procedures for you to review as well.
With the addition of the HTTPSERVER control it is now possible to provide HTML content directly from your application, and also provide a means of web-development directly from your desktop without necessarily needing to install a dedicated web-server like IIS.
It is also a good solution for when you want to provide local HTML content in your application’s user-interface via an embedded browser control, because it can avoid the usual security restrictions that browsers enforce for such scenarios.
With the release of version 10.1 a new control type called DIRWATCHER (“Directory Watcher”) has been added to OpenInsight. This is a fairly simple control which allows you to monitor one or more directories on your system and then receive notifications when the contents are changed.
Using the control is very straightforward:
Use the WATCHDIR method to add a directory to monitor for changes.
Handle the CHANGED event to receive notifications of directory changes.
Use the STOP method to stop monitoring directories.
We’ll take a quick look at each of these methods and events below along with a couple of important properties:
The WATCHDIR method
This method allows you to specify a directory to monitor along with some optional flags. It may be called multiple times to watch more than one directory.
The flag values are specified in the MSWIN_FILENOTIFY_EQUATES insert record,
This method returns TRUE$ if successful, or FALSE$ otherwise.
The STOP method
This method stops the control monitoring its specified directories.
bSuccess = Exec_Method( CtrlEntID, "STOP" )
This method returns TRUE$ if successful, or FALSE$ otherwise.
(Note – to resume directory monitoring after the STOP method has been called the WATCHDIR method(s) must be executed again).
The CHANGED event
This event is raised when changes have been detected in the monitored directories.
bForward = CHANGED( NewData )
This event passes a single parameter called NewData which contains an @vm-delimited list of changed items (i.e. notifications). Each item in the list comprises an “action code” and the name and path of the affected file, delimited by an @svm.
Action codes are defined in the MSWIN_FILENOTIFY_EQUATES insert record like so:
equ FILE_ACTION_ADDED$ to 0x00000001
equ FILE_ACTION_REMOVED$ to 0x00000002
equ FILE_ACTION_MODIFIED$ to 0x00000003
equ FILE_ACTION_RENAMED_OLD_NAME$ to 0x00000004
equ FILE_ACTION_RENAMED_NEW_NAME$ to 0x00000005
If a monitored directory experiences a high volume of changes (such as copying or removing thousands of files) it could generate a correspondingly high number of CHANGED events, which in turn could produce an adverse affect on your application and slow it down. In order to deal with this potential issue it is possible to “bundle up” multiple notifications with the NOTIFYTHRESHOLD property into a single CHANGED event so they may be processed more efficiently.
The NOTIFYTHRESHOLD property
The NOTIFYTHRESHOLD property is an integer that specifies the maximum number of notifications that should be bundled before a CHANGED event is raised.
If the NOTIFYTHRESHOLD property is set to a value greater than 1 then the control will try to bundle that number of notifications together before raising a CHANGED event. However, when this is set to a high value it is possible that the threshold may not be reached in a timely fashion and the CHANGED event not actually raised.
E.g. If the NOTIFYTHRESHOLD is set to 1000, and only 200 notifications are received then the CHANGED event would not be raised.
To prevent this problem the NOTIFYTIMER property may be used to specify the amount of time after receiving the last notification before a CHANGED event is raised even if the NOTIFYTHRESHOLD is not met.
E.g. in the example above, if the control had a NOTIFYTIMER of 50, then a CHANGED event would be raised 50ms after the last notification (200) was received, even though the NOTIFYTHRESHOLD of 1000 has not actually been met.
The DIRWATCHER control is intended as a “non-visual” control and should probably be hidden at runtime in your own applications. However, it is actually derived from a normal STATIC control so all of the properties and methods that apply to a STATIC apply to the DIRWATCHER as well, and you may use them as normal if you wish.
The next release of OpenInsight includes a new TABCONTROL property called ALLOWDRAGREORDER, which allows you to drag a tab to a new position within the control. It’s a simple boolean property, and when set to True tabs may be dragged and reordered with the mouse – an image of the tab is moved with the cursor, and a pair of arrows are displayed to mark the potential insertion point.
Here’s an example of a tab being dragged in the IDE:
The tabs may be scrolled while dragging by hovering outside either edge of the control.
This property is not supported for:
MultiLine tab controls
Vertically-aligned tab controls
The LISTBOX control also supports this property for reordering its items – see the “Order Tabs” dialog in the Form Designer for an example, or the list of types in the IDE’s “Open Entity” dialog.
Bitmap controls in OpenInsight 10 have a method called CAPTUREIMAGE, which allows you to “screenshot” the contents of another OI control or form into the Bitmap control’s IMAGE sub-object. As you can see, it has a very simple interface:
The next release of OpenInsight sees a few updates to context menus and the ContextMenu Designer, so in this post we’ll take a brief look at these upcoming changes.
Moving the focus
One important aspect of standard Windows context menu behavior is that the focus is moved (if possible) to the control that the menu belongs to. Current versions of OpenInsight do not follow this pattern so the next release includes a fix for this, and this is something you should be aware of just in case it impacts your application (though to be honest, we’re not really expecting it to!).
The Context-Menu Designer now supports the IDE “Test-Run” feature, so that you can see how your context menu will appear when you use it in your application.
When you test-run your context menu you will see a simple dialog box with an edit control (EDL_TEST) and and a static control (TXT_TEST) like so:
Right-clicking either of these controls displays your context menu:
Selecting an item displays it’s fully-qualified name, which has the standard format of:
The initial release of the ContextMenu Designer in v10.0.8 included check-boxes for two “common menu” options as shown in the screenshot below. Each of these options appends a set of standard menu items to your context menu, and both have been enhanced for the next release and include new artwork as well.
The “OI Menu” appends the following items:
Options – Display options for the current control.
(The definition for these items can be found in the SYSPROG “OIMENU_” and “WINMENU_” ContextMenu entities respectively – you may adjust these if you wish, but be aware that they may be overwritten in future OpenInsight updates, so you should make copies in your own application).
The @MENUPARENT pseudo-control name
When using QuickEvents there are several pseudo-control names you can use, such as “@WINDOW”, “@FOCUS” and “@SELF”, that are resolved to a “real” control name at runtime.
However, in order to be able to reference the context menu’s parent control at runtime we’ve introduced a new pseudo-control name called “@MENUPARENT”. This resolves to the name of the control displaying the menu and should be used in place of “@FOCUS” because it is perfectly possible for controls that don’t accept the focus (like Static Text controls) to have a context menu, and @FOCUS would not resolve to the correct value. Note that @MENUPARENT can only be used with MENU QuickEvents for context menu items – it cannot be used with any other type or event.
Context menus are an essential part of modern user interface design and we encourage you to use them as much as possible in your own applications – hopefully you’ll find that the tools provided in OpenInsight 10 make this easy to achieve!
In the next release of OpenInsight we’ve added a new feature that allows you to quickly display runtime databinding information for the controls in your application – the aptly named SHOWDATABINDING method.
It’s a simple method that is supported by all controls, and can be invoked like so:
Call Exec_Method( CtrlEntID, "SHOWDATABINDING" )
If the control is bound to a database table then it displays a view-only dialog of data binding information for that control. The following example shows the information for a bound column in an EditTable control:
The Description, Validation, Heading and Formula attributes all have their own sub-dialog boxes to display their full details.
If the control is not databound a simple message is displayed to inform the user of the fact.
This method can easily be added to menu or contextmenu QuickEvents in your own applications if you wish to expose this information to your users, or just for your own diagnostic purposes.
This moves the focus to the specified object, but the event queue will be flushed both before and after the focus has been set, thereby preventing any events raised as a result of the focus being moved from being processed. This method was originally designed for use with validation routines so the focus could be reset to an invalid control “safely”.
The second method is to use the SYSTEM object’s FOCUS property:
This moves the focus, but any events triggered as a result of moving the focus (like LOSTFOCUS and GOTFOCUS events) will be processed.
That all sounds straightforward enough, but using the first method can lead to unexpected results if you are relying on an event already in the queue that you subsequently need – not a common situation but one we encountered recently while converting an old form to v10. In our case we had a menu failing to show when the focus was on a specific control, and it turned out that the LOSTFOCUS event for the control was setting it’s FOCUS property to TRUE$. This had the effect of killing a pending MENUDROPDOWN event (new in v10) that created the menu to display, hence no menu.
In this case the solution is to use the SYSTEM BLOCKEVENTS property to turn off events being triggered while the focus is moved and then restore event processing afterwards:
This prevented any LOSTFOCUS and GOTFOCUS events from being raised, but the MENUDROPDOWN event was still in the queue.
Using the SYSTEMFOCUS and BLOCKEVENTS properties in this way is a far “safer” alternative when moving the focus because you have full control over how events are handled, and I would always advocate using this method over using a control’s own FOCUS property.
A new facility introduced in version 10 is the ability to set the styling information for the tabs in the TabControl using the new ITEMSTYLE and ITEMSTYLES properties, and in this post we’ll explain how to use them.
TabStates and ItemStyles
Each tab in a TabControl can be in one of the following states at runtime:
Normal (Unselected tab)
Hot (Mouse is over a Normal tab)
Hot Selected (Mouse is over the selected tab)
And for each one of these states you can specify the following styling information for the tabs:
To do this at run-time you can use one of the following properties:
// Set the mouseover text to change to red
itemStyle = ""
itemStyle<TCIS_POS_FORECOLOR$> = RED$
Call Set_Property_Only( ctrlEntID, "ITEMSTYLE", |
// Set the mouseover text for a selected item to change to red // and bold
itemStyle = ""
itemStyle<TCIS_POS_FORECOLOR$> = RED$
itemStyle<TCIS_POS_BOLD$> = TRUE$Call Set_Property_Only( ctrlEntID, "ITEMSTYLE", |
So you can use the “TCIS_POS_” equates shown above as well.
Example: Setting the Hot and Hot Selected styles using ITEMSTYLES
itemStyles = Get_Property( ctrlEntID, "ITEMSTYLES" )
// Set the mouseover text to change to red
itemStyles<TCIS_HOT$, TCIS_POS_FORECOLOR$> = RED$
// Set the mouseover text for a selected item to change to red
// and bold
itemStyles<TCIS_HOTSELECTED$,TCIS_POS_FORECOLOR$> = RED$
itemStyles<TCIS_HOTSELECTED$,TCIS_POS_BOLD$> = TRUE$Call Set_Property_Only( ctrlEntID, "ITEMSTYLES", itemStyles )
Setting ItemStyles in the Form Designer
Item styling for the tab control can also be specified at design time by using the “ItemStyles” property that is available when you select the tab control in the form designer. When you click the button for this property you will see an ItemStyles editor dialog that lets you specify the styling information:
(Note that in the current release (10.0.6) you will not see this applied to the design control – this will be fixed in an upcoming release)