Tag Archives: Controls

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)

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.

 

EditTables – The new “CELL” events

The OpenInsight EditTable has always supported a set of cell-related common events that are fired when a user interacts with the control:

  • CHAR
  • CHANGED
  • CLICK
  • DBLCLK
  • OPTIONS

In order to process these events properly however, it is necessary to know which cell they relate to, and this can ostensibly be found by using the NOTIFYPOS property, which is set to the position of the cell that raised the event.

In theory this approach works well, but in practice it can exhibit problems:  Events in OpenInsight are nearly always raised in an asynchronous fashion, which means that if two of those events where executed in quick succession for different cells, then NOTIFYPOS could be set to the position of the second cell, before the Basic+ event handler could process the event for the first cell, thereby leading to incorrect results.

In order to handle this better the EditTable now supports a series of corresponding “CELL” events:

  • CELLCHAR
  • CELLCHANGED
  • CELLCLICK
  • CELLDBLCLK
  • CELLOPTIONS

The only difference here is that these events pass the indexes of the cell that raised them as arguments to the event handler, thereby preserving their origin accurately.

E.g. The signature for the old CHANGED event looks like this:

Function Changed( CtrlEntID, CtrlClassID, NewData )

Whilst the signature for the CELLCHANGED event looks like this:

Function CellChanged( CtrlEntID, CtrlClassID, ColNum, RowNum, NewData )

Note that when a “CELL” event is defined the EditTable will non longer raise the ordinary event to prevent the notification from being processed twice.

(One useful example of the benefits of having the “CELL” events is that you can now use the new CELLCHANGED event for cell validation, rather than the usual POSCHANGED event, due to the fact that you know precisely where the change originated from. You also know that there actually was a change, rather than having to compare the cell’s current contents to it’s GOTFOCUSVALUE to discover this).

 

 

 

The EDITSTATECHANGED event

One of the requirements we needed when developing the new IDE was the ability to detect when the state of a control changed in such a fashion that might affect the operations that could be performed on it.

A classic example of this is highlighting text in an edit control so that it can be cut or copied, or perhaps replaced with a paste operation: At this point an item like a Cut or a Paste button might need enabling so the UI is in sync with the state of the control.

To enable this functionality several controls now support a new event called EDITSTATECHANGED, which is fired when the “edit state” is changed.  The edit state is defined as one of the following operations:

  • Undo
  • Redo
  • Cut
  • Copy
  • Paste
  • Select All

So, if a user takes an action in the control that enables or disables one of these options you can respond to it via the EDITSTATECHANGED event.

The EDITSTATECHANGED event passes a single parameter called “EditState“, which is a dynamic array of Boolean flags with the following structure:

<1> CanUndo      : TRUE$ if the control allows an UNDO operation
<2> CanRedo      : TRUE$ if the control allows a REDO operation
<3> CanCut       : TRUE$ if the control allows a CUT operation
<4> CanCopy      : TRUE$ if the control allows a COPY operation
<5> CanPaste     : TRUE$ if the control allows a PASTE operation
<6> CanSelectAll : TRUE$ if the control allows a SELECTALL 
                 : operation

(You may notice that these flags closely follow the items in a standard “Edit” menu).

Here’s a simple example to set the state of some Cut/Copy/Paste buttons:

   objxArray =        @window : ".BTN_CUT"
   propArray =        "ENABLED"
   dataArray =        editState<0,3>

   objxArray := @rm : @window : ".BTN_COPY"
   propArray := @rm : "ENABLED"
   dataArray := @rm : editState<0,4>
   
   objxArray := @rm : @window : ".BTN_PASTE"
   propArray := @rm : "ENABLED"
   dataArray := @rm : editState<0,5>

   call Set_Property_Only( objxArray, propArray, dataArray )

The following controls support the EDITSTATECHANGED event:

  • COMBOBOX
  • EDITLINE
  • EDITBOX
  • EDITTABLE
  • LISTBOX
  • PROPERTYGRID

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).

(EDIT: 8th May 2018 – Variable name changed from NewEditState to EditState to match released version)

The FILEPREVIEW control

One of the new controls added to version 10 is the FILEPREVIEW control, which taps into the Windows Shell interface to expose the same  functionality provided by the Windows Explorer when previewing the contents of files, as per the example below:

explorer_preview

Windows Explorer PowerPoint preview

Using the FILEPREVIEW control is quite easy – simply set the FILENAME property with the name and path of the file you want to preview, and if the OS has handlers installed for that file type then the control will render them.

Here’s an example of previewing a PowerPoint file:

  fileName = get_Property( @window : ".EDL_FILENAME", "TEXT" )
  if bLen( fileName ) then
     call set_Property_Only( @window : ".FPV_VIEWER", "FILENAME", |
                             fileName )
  end
filepreview

FILEPREVIEW control example

The devil of course is in the details – your OS must have the correct handler DLLs installed for you to view the preview of the file, and this may rely on third party software being installed as well.  For example, to preview Word and Excel documents you must have Office installed, to preview PDF files you must have something like Adobe Reader installed and so on.  You can test for this at runtime by using the PREVIEWHANDLER method, which returns a GUID identifying the preview handler DLL if it is installed.  You simply pass the extension you wish to look up and check for a returned GUID like so:

  handlerGUID = exec_Method( previewCtrl, "PREVIEWHANDLER", "pdf" )
  if bLen( handlerGUID ) then
    // We have a PDF preview viewer on the workstation ...
    call set_Property_Only( previewCtrl, "FILENAME", "c:\temp\test.pdf" )
  end 

The control also supports an ACTIVE property which returns TRUE$ if the control currently has a file loaded for previewing.

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).

 

 

The AUTODROPDOWN property

One of the areas in which dropdown comboboxes have always been lacking is the ability to fully control when the list is populated in a visually pleasing manner.  Under most circumstances the contents are loaded well before the dropdown button is clicked, and this is fine, but in situations where the items are context -sensitive it is necessary to load them just before the list is shown, and in previous versions this can look a little messy.

You can see an example of this in the OI9 System Editor++: there is a very handy pair of dropdowns at the top if the form that allow you to navigate your source code using inserts and labels, but when you use them you see the list contents loaded just after it has been dropped; this looks somewhat jarring and usually causes issues with the vertical scrollbar too.

Of course, the reason for this is that OpenInsight events are normally fired in an asynchronous manner, and the DROPDOWN event won’t run until after the list has been shown.  While you do have the ability to fire events in a synchronous fashion, this can introduce other complications and is something that should be avoided whenever possible.

So, in order to fix this, OpenInsight 10 provides a new property called AUTODROPDOWN that allows the list to be loaded during the DROPDOWN event and then displayed precisely when needed.

AUTODROPDOWN is a boolean property and is set to TRUE$ by default.

  • When set to TRUE$ the combobox behaves in exactly the same way as previous versions.
  • When set to FALSE$, the list is not shown until you explicitly set the DROPDOWN property to TRUE$. This gives you ability to load the list contents and then show them when ready.

Here’s an example of loading a list of STPROC repository items in a DROPDOWN event when AUTODROPDOWN is FALSE$

  // Get a list of items
  reposIDs = get_Repos_Entities( "", "STPROC", "", FALSE$, |
                                 TRUE$, FALSE$, "" )
  
  // Load them into the combobox LIST property
  call set_Property_Only( ctrlEntID, "LIST", reposIDs )

  // Now show the list
  call set_Property_Only( ctrlEntID, "DROPDOWN", TRUE$ )

Comboboxes as options buttons?

Another area where AUTODROPDOWN may help is for systems that use comboboxes as a combined edit and options control, where clicking the dropdown button loads a dialog box or popup instead of dropping a list.  Again, in previous versions this tends to look a little crude because the combobox shows a very shallow list even though it is empty, but with AUTODROPDOWN set to FALSE$ the list is never shown, and consequently looks much better.

(Disclaimer: This article is based on preliminary information and may be subject to change in the final release version of OpenInsight 10).