Author Archives: Captain C

Adding Custom Properties in the Form Designer

User-defined properties (“UDPs”) have always been supported at run-time in OpenInsight by giving the desired property a name prefixed with an “@” symbol and then setting a value for it, e.g:

Call Set_Property( CtrlEntID, "@MYPROP_1", "SomeVal" )
Call Set_Property( CtrlEntID, "@MYPROP_2", "SomethingElse" )

Value = Get_Property( CtrlEntID, "@MYPROP_1" )

// etc...

With the upcoming release of version 10.0.8, design-time support for these has been added in the Form Designer via the new “CustomProperties” property.  This is simply a list of UDP  property names and values that can be specified and stored in the Form definition record, which are then processed during form creation to create UDPs that can be accessed in the normal way by Get_Property and Set_Property.

For example, if you enter a couple of custom properties in the Form Designer called MYPROP_1 and MYPROP_2:

CustomProperties Property

Editing the CustomProperties property

You may then use these with Get_Property and Set_Property at runtime, by referencing them with an “@” prefix (i.e. “@MYPROP_1” and “@MYPROP_2”) like so:

// Value will contain "SomethingElse"
Value = Get_Property( CtrlEntID, "@MYPROP_2" )

Two things to note:

1) You don’t need to specify an “@” prefix for the property name in the CustomProperties editor, and

2) You are not limited to simple strings when entering CustomProperties values – you may also use the standard “[,]” syntax for entering dynamic arrays just like you would for QuickEvent parameters:

E.g. to enter an @fm-delimited array you enclose the list of items in ‘[]’ brackets, and delimit them with a comma like so:

An array like this: 

   <1> ItemOne
   <2> ItemTwo

Can be entered as:

   ['ItemOne','ItemTwo']

Note that each array item must be single-quoted, but you can escape a quote in the data by using two single quotes, e.g.

An array like 

   <1> ItemOne
   <2> ItemTwo's Stuff

Can be entered as:

   ['ItemOne','ItemTwo''s Stuff']

For arrays with lower-level delimiters like @vm and @svm you add a set of nested ”[]’ brackets for each level, e.g:

An array like:
   <1>      ItemOne
   <2,1>    ItemTwo_A
   <2,2>    ItemTwo_B
   <2,3,1>  ItemTwo_C_1
   <2,3,2>  ItemTwo_C_2
   <3>      ItemThree

Can be entered as:
   
   ['ItemOne',['ItemTwoA','ItemTwoB',['ItemTwo_C_1','ItemTwo_C_2']],'ItemThree']

And so on.

Hopefully you find this new feature useful and help to reduce the amount of code you need to write.

A focus on the FOCUS property

There are two methods for setting the input focus in OpenInsight, and there is a subtle implementation difference that can impact your applications if you’re not careful how you apply them.

The first method is to use an object’s own FOCUS property and set it to TRUE$ like so:

Call Set_Property_Only( @Window : ".EDL_SURNAME", "FOCUS", TRUE$ )

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:

Call Set_Property_Only( "SYSTEM", "FOCUS", @Window : ".EDL_SURNAME" )

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:

Call Set_Property_Only( "SYSTEM", "BLOCKEVENTS", TRUE$ )
Call Set_Property_Only( "SYSTEM", "FOCUS", @Window : ".EDL_SURNAME" )
Call Set_Property_Only( "SYSTEM", "BLOCKEVENTS", FALSE$ )

This prevented any LOSTFOCUS and GOTFOCUS events from being raised, but the MENUDROPDOWN event was still in the queue.

Using the SYSTEM FOCUS 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.

 

Coloring your tabs with the ITEMSTYLE 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)
  • Disabled
  • Selected
  • 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:

  • ForeColor
  • BackColorFrom
  • BackColorTo
  • Bold
  • Italic
  • Underline
  • Translucency
  • CloseButton ForeColor
  • CloseButton BackColor

To do this at run-time you can use one of the following properties:

  • The ITEMSTYLE property
  • The ITEMSTYLES property

and we’ll take a look at each of these in turn.

 

The ITEMSTYLE property

   prevStyle = Get_Property( ctrlEntID, "ITEMSTYLE", itemState )
   currStyle = Set_Property( ctrlEntID, "ITEMSTYLE", newStyle, itemState )

This property allows you to get or set the ITEMSTYLE for a single state.  The property itself is an @fm-delimited array of styling information like so:

    <1> ForeColor             (COLORREF)
    <2> BackColor From        (COLORREF)
    <3> BackColor To          (COLORREF)
    <4> Bold                  (1/0)
    <5> Italic                (1/0)
    <6> Underline             (1/0)
    <7> Translucency          (0-100)
    <8> CloseButton ForeColor (COLORREF)
    <9> CloseButton BackColor (COLORREF)

Equates for these array positions can be found in the PS_TABCONTROL_EQUATES insert record:

   equ TCIS_POS_FORECOLOR$           to 1 ; * // COLORREF
   equ TCIS_POS_BACKCOLOR_FROM$      to 2 ; * // COLORREF
   equ TCIS_POS_BACKCOLOR_TO$        to 3 ; * // COLORREF
   equ TCIS_POS_BOLD$                to 4 ; * // Boolean
   equ TCIS_POS_ITALIC$              to 5 ; * // Boolean
   equ TCIS_POS_UNDERLINE$           to 6 ; * // Boolean
   equ TCIS_POS_TRANSLUCENCY$        to 7 ; * // UInt (0-100)
   equ TCIS_POS_CLOSEBTNFORECOLOR$   to 8 ; * // COLORREF
   equ TCIS_POS_CLOSEBTNBACKCOLOR$   to 9 ; * // COLORREF

You must also use the index parameter with the Get_Property and Set_Property to specify the tab state that you are setting, which is an integer between 1 and 5:

   equ TCIS_NORMAL$                   to 1
   equ TCIS_HOT$                      to 2
   equ TCIS_DISABLED$                 to 3
   equ TCIS_SELECTED$                 to 4
   equ TCIS_HOTSELECTED$              to 5

Example: Setting the Hot and Hot Selected styles

   $insert colors
   $insert logical

   // Set the mouseover text to change to red 
   itemStyle = ""
   itemStyle<TCIS_POS_FORECOLOR$> = RED$
   
   Call Set_Property_Only( ctrlEntID, "ITEMSTYLE", |
                           itemStyle,              |
                           TCIS_HOT$ )

   // 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", |
                           itemStyle,              |
                           TCIS_HOTSELECTED$ )

 

The ITEMSTYLES property

   prevStyles = Get_Property( ctrlEntID, "ITEMSTYLES" ) 
   currStyles = Set_Property( ctrlEntID, "ITEMSTYLES", newStyles )

This property is very similar to the ITEMSTYLE property except that it allows you to get or set the styles for all states at once.

The property itself is an @fm/@vm delimited array: each state is delimited by @fm, and the styling information for each state is delimited by @vm.

   <1> ItemStyle for the Normal State
   <2> ItemStyle for the Hot State
   <3> ItemStyle for the Disabled State
   <4> ItemStyle for the Selected State
   <5> ItemStyle for the Hot Selected State

(You will note these map onto the “TCIS_” state equates shown above)

For each one of these states the ItemStyle information is an @vm delimited array using the same structure as for the ITEMSTYLE property, i.e:

   <0,1> ForeColor             (COLORREF)
   <0,2> BackColor From        (COLORREF) 
   <0,3> BackColor To          (COLORREF) 
   <0,4> Bold                  (1/0) 
   <0,5> Italic                (1/0) 
   <0,6> Underline             (1/0) 
   <0,7> Translucency          (0-100) 
   <0,8> CloseButton ForeColor (COLORREF) 
   <0,9> CloseButton BackColor (COLORREF)

So you can use the “TCIS_POS_” equates shown above as well.

Example: Setting the Hot and Hot Selected styles using ITEMSTYLES

   $insert colors
   $insert logical

   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:

TabControl ItemStyles Editor

(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)

Tracking the SAVEWARN property

As veteran OpenInsight programmers know, the system uses a simple boolean flag (exposed as the SAVEWARN property) to determine if the contents of a data-bound form have changed.  This flag can be updated in several ways, the most common being:

  • From the LOSTFOCUS event of a control.
  • From the POSCHANGED, INSERTROW and DELETEROW events of an EDITTABLE control.
  • From setting a control’s DEFPROP property.
  • From the CLOSE event of a form when the control with focus is inspected to see if it has changed.

It is checked during the CLEAR and CLOSE events to see if it has been set and an “Unsaved Changes” warning issued to the user if so.  Most of the time this system works quite well, but (as anyone who has spent several years working with OI systems knows) sometimes it gets triggered when you least expect it, and you’re left with no clue as to why.

To help with this situation the next version of OpenInsight introduces SAVEWARN tracking, so you can see which parts of the system update the SAVEWARN property and when they actually do it. In previous versions the system updated the SAVEWARN flag directly (it’s a simple variable in the “window common area”) but this has been changed to use the Set_Property function so it can be monitored effectively from a single point.

To track SAVEWARN you have two choices:

  • Use the SYSMSG event
  • Use the System Monitor

 

Using the SYSMSG event to track SAVEWARN

Every time SAVEWARN is set a standard SYSMSG event is raised with a SAVEWARNINFO code; the system itself does nothing with this message, but it’s there for you to use if you wish.  This option is probably more suited for run-time tracing as it’s something you could add to your applications easily if you needed to.

The PS_EQUATES insert record defines the SAVEWARNINFO message number that you can intercept:

equ SYSMSG_SAVEWARNINFO$ to 21  ; // Save warn has been changed - null msg

The Auxiliary parameter passed to the  SYSMSG event contains information that describes why the SAVEWARN property was changed.

 

Using the System Monitor to track SAVEWARN

The SetDebugger() function has been updated to support a new method called “SAVEWARN” that enables SAVEWARN tracking so that changes are displayed in the System Monitor.  This option is probably more suited to development use rather than run-time.

From the System Monitor execute:

setdebugger savewarn 1

to turn on tracing, and:

setdebugger savewarn 0

to turn it off.

E.g:

SAVEWARN tracing in the System Monitor

SAVEWARN tracing in the System Monitor

Setting the SAVEWARN property

If you wish to set SAVEWARN yourself you may use the “index” parameter to pass a description for the change, so this can be picked up in any tracing scenario like so:

Call Set_Property_Only( @Window, "SAVEWARN", TRUE$, "From My Stuff" )

This description is then passed in the Auxiliary parameter of the  SYSMSG event as noted above.

 

Hopefully you will find this facility useful if you ever suffer from problems with SAVEWARN in the future.

Extended Precision Mode

One of the common problems faced by many programmers is the ensuring the accuracy of floating point arithmetic, mainly due to the rounding errors that can occur when calculations are performed on numbers that cannot be fully represented in a binary floating-point format (The size of this format determines the precision, and therefore the accuracy of calculations).

OpenInsight is built with the Microsoft C++ compiler, which limits the floating-point format to 64-bits (known as the “Double” type), and Basic+ variables that represent non-integer numbers use this type internally.  The use of this 64-bit format is one of the problems commonly noticed by developers who have moved their systems from the older Advanced Revelation platform to OpenInsight, because the internal floating-point format for R/Basic variables was the 80-bit “Long Double” type instead.  This means that calculations from the same RBasic/Basic+ code running on ARev and OI may produce different results due to this reduction in precision.

(The Long Double is unfortunately not supported by the MS C++ compiler as, according to the VC++ compiler team back in 2006: “The major reason is that FP code generation has been switching to the use of SSE/SSE2/SSE3 instruction sets instead of the x87 FP stack since that is what both the AMD and Intel recent and future chip generations are focusing their performance efforts on. These instruction sets only support 32 and 64 bit FP formats”).

The “integer workaround”

A common workaround for precision problems is to control the calculations at each step so you are effectively dealing with integer operations – this can be done by using the “MD” IConv/OConv functions to control the precision, or by simply multiplying values by a known factor and dividing the result again afterwards.

Both of these methods can make the code messy and obscure the intent, and, depending on the values used, may result in integer overflow if they are too large (though this is less likely to happen on a 64-bit system like OpenInsight 10 however).

The “Extended Precision Operators”

Another option to help mitigate these calculation problems are the Extended Precision Operators that were introduced in OpenInsight 9.3:

  • _addx
  • _subx
  • _mulx
  • _divx

These do allow you to specify the precision to use at each step, but suffer from the need to rewrite existing code to use them. It is also easy to lose precision if you inadvertently use a “normal” operator in between them as well, as the following example demonstrates:

a = _divx( c, b, 24 )   ; // "a" is full precision (24)
if ( a > 1 ) then       ; // "a" converted to "double" by ">" operator
   // do stuff
end
b = _addx( a, z )       ; // "a" is no longer full precision passed to _addx

The new “Extended Precision Mode”

With the upcoming release of version 10.0.7, OpenInsight now supports a new feature for dealing with high precision calculations called Extended Precision Mode (EP Mode). When this mode is enabled all the normal maths operators switch into an “extended mode” and the results of calculations are stored in Basic+ variables using a new internal type introduced specifically for maintaining the precision.

This means that existing code can be reused by simply adding statements to activate and deactivate the mode as needed via the new SetEPMode() function , e.g:

Compile Function Mickey_Mouse_EPM_Test( void )

   Declare Function GetEPMode
   $Insert Logical
 
   epMode = GetEPMode()

   Call SetEPMode( FALSE$ )
   GoSub runTest
   
   Call SetEPMode( TRUE$ )
   GoSub runTest
   
   Call SetEPMode( epMode )

Return

runTest:
   a = 10.12346 * (22/7); 
   b = 100000; 
   For x = 1 To 1000 
      b -= a 
   Next
   Call Send_Dyn( b ) 
Return

Output:

(Normal) 68183.4114285739 
(EPMode) 68183.41142857142857142857142857143725

The following operators are affected by the EP Mode:

+
+=
-
-=
*
/
== or =
!= or <>
>
<
>=
<=
mod()
int()
abs()
atan()
cos()
exp()
ln()
pwr()
sin()
sqrt()
tan()

Performance considerations

By default EP Mode is not enabled because calculations are slower due to the extra processing needed to maintain precision, and this would have a detrimental effect on the performance of your applications if it were permanently enabled, so you should only use it when absolutely required.

You should also note that the EP Mode and Precision are set to their default values for each new request made to the engine (i.e each event or web-request).  This is to protect against situations where an error condition could force the engine to abort processing before the EP Mode settings could be reset, thereby leaving it in an undesired state (This is similar to the way UTF-8 mode works so that data integrity is preserved).

Controlling the precision level

The actual level of precision (i.e. the decimal places) is controlled by another setting, which is updated by the new SetEPModePrecision() function (The greater the precision the longer calculations will take to perform).   By default the precision is set to 32.

   decPlaces = GetEPModePrecision()
   Call SetEPModePrecision( 24 )

 Default EP settings

The default settings can be changed in the Application Properties dialog launched from the IDE Settings menu.

Further reading on floating point arithmetic

More information on floating point arithmetic can be found by following the links below:

EditTables – The Sub-Object Interface

One of the changes we wanted to make with EditTables in v10 was to expose all of their runtime functionality easily through the normal property/method API.  Some of this was already available in previous versions, but usually involved a method to set a plethora of style bits, which is not really satisfactory unless you’re a C++ programmer (as we have been reminded by various developers on several occasions!).

Besides the EditTable as a whole, there are essentially three other main programmable areas:

  1. Columns
  2. Rows
  3. Cells

Providing unique properties and methods to address the capabilities of these areas is not really practicable, as it leads to an “explosion” of property names: For example, accessing the “text” associated with each of these would require three new properties such as:

  • COLUMNTEXT
  • ROWTEXT
  • CELLTEXT

Of course that’s only three, but considering that each of the aforementioned areas has something like 30 properties, you would suddenly add 90+ new property names to the product!  Bear in mind also that columns, rows and cells each have many properties like TEXT in common, so a new name for each of these seems extremely wasteful and unnecessary.

(We could also have used something similar like the existing TEXTBYPOS method to achieve this, but then we’d have 30’ish new “BYPOS” methods instead, and methods aren’t properties anyway).

So, to keep the property namespace under control we decided to use this commonality and implement a set of  “sub-objects” instead, one for each area.  Unsurprisingly these are named:

  • COLUMNS
  • ROWS
  • CELLS  

Each of these sub-objects are indexed and can be used to access a specific column, row or cell in the EditTable.  They all share many common properties and methods, but also expose a few type-specific ones as well.   As an example, this is how to set the CUEBANNER property for each sub-object:

ctrlEntID  = @window : ".EDT_TEST"
cueBanner  = "Test"

// Set the CUEBANNER for the second column
call set_Property( ctrlEntID : ".COLUMNS", "CUEBANNER", cueBanner, 2 )
// Set the CUEBANNER for the third row
call set_Property( ctrlEntID : ".ROWS", "CUEBANNER", cueBanner, 3 )

// Set the CUEBANNER for the cell at column 5, row 7
call set_Property( ctrlEntID : ".CELLS", "CUEBANNER", cueBanner, 5 : @fm : 7 )

// And here's the same using Object Notation Syntax
@ctrlEntID.columns{2}->cueBannner = cueBanner
@ctrlEntID.rows{3}->cueBannner    = cueBanner
@ctrlEntID.cells{5,7}->cueBannner = cueBanner

We’ll take a look at each of these sub-objects in turn over the next few posts to examine their functionality in more detail.