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

 

 

 

EditTables – Deleting and Inserting Rows

Support for deleting and inserting rows in the EditTable control has always been somewhat basic, exposing only minimum functionality that allows you to control how a user inserts or deletes a row in the grid.  We’ve enhanced this for version 10 by providing some fine-grain control over the row insertion and deletion process that we’ll describe below.

Imposing limits

Firstly, a couple of new properties have been implemented that allow you to set limits on the number of rows a user can add or remove from the EditTable via the keyboard: These are the MAXROWLIMIT and MINROWLIMIT properties.

MAXROWLIMIT property

When MAXROWLIMIT is set the user cannot use the Insert key to insert more rows than the number specified by this property.  The default value is “0”, which means there is no maximum limit.   Note that this property does not apply to programmatic INSERT method operations or data set by the LIST or ARRAY properties.

MINROWLIMIT property

When MINROWLIMIT is set the user cannot use the Delete key to delete EditTable rows once the minimum limit has been reached. The default value is “0”, which means there is no minimum row limit.   Note that this property does not apply to programmatic DELETE method operations or data set by the LIST or ARRAY properties.

Blocking the “Insert” and “Delete” keys

Previous versions of the EditTable allowed you to set a “Protected” property at design time that stopped all row insert and delete operations by the user.  Unfortunately, this was not actually exposed run-time (unless you adjusted a bit-flag the control’s STYLE property), so in version 10 we’ve expanded the old “Protected” property into two new properties called ALLOWROWDELETE and ALLOWROWINSERT.  These can used at both run-time and design-time.

ALLOWROWDELETE property

When set to False the user cannot use the Delete key to delete rows within an edit table.  The default value of this property is True.

ALLOWROWINSERT property

When set to False the user cannot use the Insert key to insert new rows within an edit table.  The default value of this property is True.

Managing the “Insert” and “Delete” keys

Finally, we made some changes to the way the actual inserts and deletes take place to give you an opportunity to intercept them for even more control.  In previous versions of OpenInsight the EditTable notified you of an insert or delete operation after it had taken place, via the INSERTROW and DELETEROW events respectively.  This means that if you wanted to ‘prevent’ the modification based on some run-time criteria you had to effectively undo it, which usually included some sort of unpleasant visual effect as the insert or delete was rolled back (it also resulted in a loss of formatting information or other cell-specific data that you would have to reapply).

In version 10 we’ve added a new Boolean property called ROWEVENTMODE. When set to False (the default) row insertion and deletion via the keyboard behaves in exactly the same way as previous versions of OpenInsight.  When set to True however, the INSERTROW or DELETEROW event happens before the actual operation takes place, and at this point you can do the following:

  1. If you’re using an EventScript you can return FALSE$ and the operation will be cancelled.
  2. If you’re using a code called from a QuickEvent you can set the EventStatus flag in your code to prevent the operation from proceeding further, just as you would currently do in a WINDOW CLOSE event to cancel it.

 

E.g. Stopping a row being deleted in a DELETEROW EventScript handler.

// Assumes "RowEventMode" is "True" 
//
// Check to see if there is anything in column 2 of the deleted data,
// and if so stop the delete via the event return value. 

$insert logical
if bLen( rowData<2> ) then
   // Stop the delete
   retVal = FALSE$
end else
   retVal = TRUE$
end

return retVal

 

E.g. Stopping a row being deleted in a DELETEROW QuickEvent handler

// Assumes "RowEventMode" is "True" 
//
// Check to see if there is anything in column 2 of the deleted data,
// and if so stop the delete via the Event Status 

$insert logical 
if bLen( rowData<2> ) then
   // Stop the delete
   call set_EventStatus( TRUE$ )
end

return

Of course, once you’ve allowed the Insert or Delete operation to take place you may also want to do some post-processing, and for this we’ve provided two new events: INSERTEDROW and DELETEDROW.  These have the same signature as their INSERTROW and DELETEROW counterparts and are executed after the row modification has taken place (Note that these are only fired if the ROWEVENTMODE is True).

It is also worth noting that, as with previous versions of OpenInsight, using the DELETE and INSERT methods to programatically modify the EditTable contents will not trigger the INSERTROW or DELETEROW events (or any subsequent INSERTEDROW and DELETEDROW events either).

Region Blocking

One small (but useful!) feature we added to the Basic+ editor was the use of “region blocks” to help with code organization in large programs. The blocks group together related sections of code under a descriptive name so they may be navigated and handled more easily (those of you who have programmed in other languages such a C# and C++ might be familiar with this concept already).

Essentially region blocks are simply a pair of statements ( “#region” and “#endregion”) that you insert before and after a block of code to define it, along with a name that describes the region.  Once you have done this the entire region becomes a “fold point”, so it can be folded to hide it, and it also appears as a “jump point” in the editor navigation dropdown so you can get to it quickly.

E.g.

#region ScrollMode

// Here's some code for handling the ScrollMode property in the FormDes etc...
onParseStruct_HandleScrollMode:
   if bitAnd( psPSStyleEx, PSSX_VIEW_SCROLLMODEPAGING$ ) then
      psWinStyle = bitOr( psWinStyle, WS_VSCROLL$ )
   end
return

// More stuff ....
#endregion ScrollMode

This now becomes a fold point in the editor:

Region Folding

Region Folding

And can be jumped to in the navigation dropdown like so:

Region Dropdown

Region Dropdown

So, if you do have some programs with large amounts of code hopefully this feature might help find your way around it quicker.

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 “NewEditState“, 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 =        newEditState<3>

   objxArray := @rm : @window : ".BTN_COPY"
   propArray := @rm : "ENABLED"
   dataArray := @rm : newEditState<4>
   
   objxArray := @rm : @window : ".BTN_PASTE"
   propArray := @rm : "ENABLED"
   dataArray := @rm : newEditState<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).

 

The COMMUTERMODULE Property

As most of you will probably know, “commuter module” is the term given to a stored procedure whose main purpose is to handle event processing for a specific form.  Rather than have each individual event processed in a separate event script, quick-events are used instead to call the commuter module directly, passing it various parameters such as the name of the object firing the event, the name of the event itself, and any relevant arguments.  The commuter module then branches off to different internal subroutines to handle the event.

Following this methodology offers several important advantages:

  • Simplified code management
  • Simplified deployment
  • Improved code sharing via internal subroutines
  • Lower memory overhead (single procedure vs. multiple event scripts)

While using a commuter module like this is the recommended way of developing applications in OpenInsight, there has never been any sort of formal link between the commuter module and the form itself, which means that it is usually necessary to adopt a naming convention for this approach to work.

For example, it is common to have a commuter module with the same name as the form, or with the same name as the form and suffixed with the string “_EVENTS”.  By doing this it was easy to define quick-events to call the commuter module in the old v9 Form Designer like so:

v9 Commuter Module Quick Event

v9 Commuter Module Quick Event

The OBJ_CALL_EVENT program looks for a stored procedure with the same name as the form, or one with the same name as the form and suffixed with “_EVENTS”.  It was also possible to avoid the overhead of a lookup and call the commuter module directly by using the “@WINDOW” placeholder like this:

v9 Commuter Module Quick Event (using @WINDOW)

v9 Commuter Module Quick Event (using @WINDOW)

Or the “@WINDOW_EVENTS” placeholder like this:

v9 Commuter Module Quick Event (using @WINDOW_EVENTS)

Of course, even with this approach the form still wasn’t actually linked to it’s commuter module, rather it was only linked to “OBJ_CALL_EVENTS” or a fictitious entity called “@WINDOW”.

In version 10 we’ve added a new WINDOW property called COMMUTERMODULE, which simply contains the name of the stored procedure to use (this can be any valid name – you are no longer limited to one based on the name of the form, though we do suggest you keep this convention to help organize your application):

CommuterModule Property

CommuterModule Property

When the form is saved and compiled this stored procedure is linked to the form with a “used-by” relationship, thereby making deployment easier as you will see the link when you create your RDK definition records.

Another benefit of this is that you can quickly open your commuter module from the form by using the “View Commuter Module” button on the Form Designer toolbar:

View Commuter Module button

View Commuter Module button

Finally you can easily set your quick events to call your commuter module in the Event Designer like so:

Commuter Module QuickEvent

Commuter Module QuickEvent

Note that it uses an “@COMMUTER” placeholder – this is replaced with the contents of the COMMUTERMODULE property at runtime.

(You can still use the previous @WINDOW/_EVENTS or Obj_Call_Events methods if you wish – those options still exist.)

So, this is the first step in tightening the relationship between a form and it’s commuter module.  There is certainly scope for more integration between the two and this is something that we hope to pursue during subsequent releases.

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

Context Menus in OpenInsight 10 – Part III

To finish off this short series on context menus we’re going to take a look a couple of new control methods, and also some new functionality in the ContextMenu stored procedure.

The ATTACHMENU method

successFlag = exec_Method( ctrlEntID, "ATTACHMENU", menuID )

As mentioned in the previous post, context menus are not usually created until they are needed, after which they are cached and made ready for subsequent use.  If the context menu supports accelerator keys (like the one used with the group selection control in the Form Designer) this is a problem, as there is nothing for the system to scan when looking for a keystroke handler.

Instead of waiting for the user to right click on a control the menu may be created without being displayed using the ATTACHMENU method.  This task is normally performed in the CREATE event for specific controls that need it.

Parameters

  • MenuID – This is the fully qualified repository ID of a context menu to attach.  It defaults to the contents of the control’s CONTEXTMENU property.

Returns

  • TRUE$ if the menu is attached successfully, or FALSE$ otherwise.

Even though the menu is not displayed it still progresses through the standard INITCONTEXTMENU and CONTEXTMENU events as described in Part II.  This is when the aforementioned AttachOnly parameter in the CONTEXTMENU event will be set to TRUE$, and this is why you shouldn’t modify it in your event handler.

The SHOWMENU method

successFlag = exec_Method( ctrlEntID, "SHOWMENU", xPos, yPos )

This method displays the control’s context menu at the specified coordinates. No attempt is made by the system to provide default coordinates, so you must decide where you want the menu to appear.

Parameters

  • XPos – Horizontal screen position to display the menu at.
  • YPos – Vertical screen position to display the menu at

Returns

  • TRUE$ if the menu is displayed successfully, or FALSE$ otherwise.

 

The ContextMenu stored procedure

This program has been part of OpenInsight for a while, but has been updated with some new methods to help with managing context menus in v10.  The available methods are:

  • ADDQUICKEVENTS
  • CREATEMENU
  • GETVALUE
  • SETVALUE

ContextMenu ADDQUICKEVENTS method

If you dynamically add menu items during the CONTEXTMENU event you will also need to add some way of responding the MENU events raised when a user selects them.  As usual you have two choices – an event script or a QuickEvent.  For the former you can pass a normal script qualifier ID directly in the item’s menu structure, but for latter this is not so: QuickEvents are held within the cached menu structure and therefore need special treatment.

This is the purpose of the ADDQUICKEVENTS method – it takes a list of menu item IDs, along with their QuickEvent specifications, and adds them to the internal structure.

successFlag = contextMenu( ctrlEntID,        |
                           "ADDQUICKEVENTS", |
                           menuIDs,          |
                           eventHandlers )

 Parameters

  • MenuIDs – This is an @vm-delimited list of menu IDs to set the QuickEvent handlers for.  Note these are not fully qualified – you don’t need to pass the name of the control and the “.CONTEXTMENU.” prefix.
  • EventHandlers – This is an @vm-delimited list of QuickEvent handler specifications for each item passed in MenuIDs.  Each handler is an @svm-delimited array with the following structure:
<0,0,1>  Event Type
<0,0,2>  Message
<0,0,3>  Target
<0,0,4>  Parameters (@tm-delimited)
<0,0,5>  Return Target
<0,0,6>  Return Message
<0,0,7>  Return Type
<0,0,8>  Return Parameters
<0,0,9>  Return Flags (@tm-delimited)
<0,0,10> Final Type
<0,0,11> Final Message
<0,0,12> Final Target
<0,0,13> Final Parameters (@tm-delimited)
<0,0,14> Final Flags

(This is a standard QuickEvent structure and is documented more fully in the NPHANDLER_EQUATES insert record)

Returns

  • TRUE$ if the events are added successfully, or FALSE$ otherwise.

Example

// This is a simple example of adding an item in the CONTEXTMENU
// event and setting a QuickEvent handler for it.
//
// We are inserting an item just after a separator called "TEST_SEP"
// and we will add a quick event for it to call the window's commuter
// module when it gets selected.

declare function contextMenu, rti_Convert
$insert oiWin_Equates
$insert npHandler_Equates
$insert logical

sepID     = ctrlEntID : ".CONTEXTMENU.TEST_SEP"
insertPos = 0
xCount = fieldCount( menuStruct, @vm )
for x = 5 to xCount ; // ignore the header fields
   if ( menuStruct<0,x>[1,1] == "@" ) else
      if ( menuStruct<0,x,MENUPOS_NAME$> == sepID ) then
         insertPos = x + 1
         x = xCount; // break
      end
   end
next

if insertPos then
   itemID = ctrlEntID : ".CONTEXTMENU.TEST_ITEM"
   menuItem = ""
   menuItem<0,0,MENUPOS_TYPE$> = "ITEM"
   menuItem<0,0,MENUPOS_NAME$> = itemID
   menuItem<0,0,MENUPOS_TEXT$> = "Test Item"

   // Add the item to the menu structure
   menuStruct = insert( menuStruct, 0, insertPos, 0, |
                        menuItem )

   // And give it a quick event
   qeID      = "TEST_ITEM"
   qeHandler = ""
   qeHandler<0,0,NP_MSGTYPE$> = "R"
   qeHandler<0,0,NP_MSG$>     = "EXECUTE"
   qeHandler<0,0,NP_TARGET$>  = @appID<1> : "*STPROCEXE**@COMMUTER"
   qeHandler<0,0,NP_ARGS$>    = rti_Convert( "@SELF,@EVENT", ",", @tm )

   call contextMenu( ctrlEntID, "ADDQUICKEVENTS", |
                     qeID,                        |
                     qeHandler )
end

ContextMenu CREATEMENU method

successFlag = CreateMenu( ctrlEntID, "CREATEMENU", menuStruct, |
                          "", "", "", altMenuID )

This method is the same as it was in previous versions of OpenInsight.  It attempts to load a context menu entity with the same name as the parent control, optionally overriding it with a passed structure.

Parameters

  • ctrlEntID – Fully qualified name of the control to attach the menu to.
  • menuStruct – (optional) A dynamic array containing a menu structure that overrides the stored one.  This structure can be in either the old v9 format (as documented in the v9 online help), or the new v10 format as discussed in Part II.
  • altMenuID – (optional) Specifies the name of the menu to load, overriding the default behaviour that assumes we are creating a menu with the same name as the parent control.

Returns

  • TRUE$ if the menu was created and attached successfully, or FALSE$ otherwise.  Errors are returned via the Set_Status stored procedure.

When you use this method the menu structure is parsed and cached by the control using the INITCONTEXTMENU event; it is not displayed until the user right-clicks on it (in a similar fashion to the ATTACHMENU method)

ContextMenu GETVALUE method

This is a simple helper function for inspecting values in a menu item as discussed in Part II of this series.

itemValue = contextMenu( itemID, "GETVALUE", menuStruct, itemValueIdx )

Parameters

  • ItemID – Fully qualified name of the item to query.
  • MenuStruct – Menu structure to parse.
  • ItemValueIdx – Index of the item value to return (see OIWIN_EQUATES).

Returns

  • The requested value.

ContextMenu SETVALUE method

This is a simple helper function for updating values in a menu item as discussed in Part II of this series.

origValue = contextMenu( itemID, "SETVALUE", menuStruct, itemValueIdx, |
                         newValue )

Parameters

  • ItemID – Fully qualified name of the item to query.
  • MenuStruct – Menu structure to parse.
  • ItemValueIdx – Index of the item value to return (see OIWIN_EQUATES).
  • NewValue – New value to set.

Returns

  • The original value that was replaced by the new value.

 

That concludes this small trilogy on context menus – hopefully you will find them much easier to use in OpenInsight 10 and be able to make more use of them in your own applications.

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

 

 

 

Context Menus in OpenInsight 10 – Part II

In our last post we looked at the CONTEXTMENU property and the way in which a context menu can be linked to a form or control at design time.  This time we’ll take a look at how to alter those menus at runtime before they are displayed, which is sometimes necessary depending on the state of the parent control and/or its environment.

When a context menu is about to be displayed the system goes through three distinct phases:

  1. The INITCONTEXTMENU event
  2. The CONTEXTMENU event
  3. A call to the TRACKPOPUPMENU method.

Generally speaking a context menu is not created until needed, after which it is cached for subsequent access.

The INITCONTEXTMENU event

This event is fired by the Presentation Server in response to a right click (actually a WM_CONTEXTMENU message from Windows) and is responsible for the following tasks:

  • Calling the Yield() stored procedure to clear any pending events
  • Calling an INITCONTEXTMENU quick event, if defined.
  • Reading the context menu definition from the repository (if it’s not cached)
  • Converting the structure into v10 format if needed
  • Compiling it into an “executable” format
  • Caching it
  • Firing the subsequent CONTEXTMENU event

The intent of INITCONTEXTMENU is as a tool for the Presentation Server to kick off the context menu process, so as such it is a system tool – it is not really intended that developers have to interact with this event, although there’s nothing to stop you should you wish to do so.

The CONTEXTMENU event

This is the point where the context menu is about to be displayed, and offers you a chance to modify it.  The CONTEXTMENU event is passed five parameters:

  • MenuID – the identifer of the context menu to display
  • MenuStructure – a dynamic array containing the executable structure of the menu – this is the same format as used for standard OpenInsight Window menus.
  • XPos – the horizontal position of the cursor, in screen coordinates, at the time of the mouse click.
  • YPos – the vertical position of the cursor, in screen coordinates, at the time of the mouse click.
  • AttachOnly flag – if this flag is TRUE$ then the menu will only be “stored” ready to be displayed.  This is a more advanced feature for use with context menus that have their own accelerator keys, because the menu needs to be created to trap the keystokes, even if it has not been displayed yet.  We’ll cover this in a later post, but you should leave this parameter unmodified.

You can intercept this event from a script, or from a QuickEvent.

  • If you prefer to use a script then you must call the Forward_Event stored procedure to display the menu and return FALSE$ from your script (otherwise you will see the menu twice).
  • If you use a QuickEvent you need to return TRUE$ from your event handler so that the menu is executed.  You can also use the Set_EventStatus stored procedure to stop the menu from being displayed and return information as to why it was cancelled.

Moving the menu

If you adjust the XPos or YPos parameters you can alter the position at which the menu is displayed.  These coordinates are normally the point at which the right mouse-button was clicked, but if the context menu was triggered by the keyboard the Presentation Server attempts to pick a suitable location – for many controls this would be left-aligned underneath the parent object, or underneath the current cell for an EditTable control and so on.

Modifying the menu structure

The format of the context menu structure has changed in v10 to use the same format as normal window menus, so they now support nested sub-menus.  The layout of this structure is described the OIWIN_EQUATES insert record, and if you’re familiar with this structure you’ll notice it’s been expanded to include more image information:

   equ MENUPOS_TYPE$              to 1
   equ MENUPOS_END$               to 2
   equ MENUPOS_NAME$              to 3
   equ MENUPOS_TEXT$              to 4
   equ MENUPOS_GREY$              to 5
   equ MENUPOS_CHECK$             to 6
   equ MENUPOS_HIDDEN$            to 7
   equ MENUPOS_ACCEL$             to 8
   equ MENUPOS_HELP_TEXT$         to 9
   equ MENUPOS_HANDLER$           to 10
   equ MENUPOS_STYLE$             to 11
   equ MENUPOS_BITMAP$            to 12
   equ MENUPOS_COLORKEY$          to 13
   equ MENUPOS_IMAGELISTINDEX$    to 14
   equ MENUPOS_IMAGEAUTOSCALE$    to 15
   equ MENUPOS_IMAGEFRAMEINDEX$   to 16
   equ MENUPOS_IMAGEOFFSET$       to 17
   equ MENUPOS_IMAGEORIGIN$       to 18
   equ MENUPOS_IMAGETRANSLUCENCY$ to 19
   equ MENUPOS_MISC$              to 20
   equ MENUPOS_RESERVED_1$        to 21
   equ MENUPOS_RESERVED_2$        to 22

As you have access to the raw structure you may modify it in any way you please, but we have included a pair of helper methods in the ContextMenu() stored procedure (GETVALUE and SETVALUE) to deal with setting simple values like so:

// Set hide a menu item
call contextMenu( itemID, "SETVALUE", menuStruct, MENUPOS_HIDDEN$, TRUE$ )

// Disable a menu item
call contextMenu( itemID, "SETVALUE", menuStruct, MENUPOS_GREY$, TRUE$ )

…. and so on.

These functions themselves are very simple and just iterate over the structure until they find the passed ID.  You can do this yourself quite easily, but these make your code look a little neater.

Of course if you have a lot of modifications to make then parsing the structure yourself will be faster, so here’s a bare bones example to get you started:

 // MenuID - ID of the item to modify. Has the format:
 //
 // <controlName> ".CONTEXTMENU." <itemName>
 //
 // e.g.
 //
 // CUSTOMERS.EDL_FORENAME.CONTEXTMENU.PASTE

 xCount = fieldCount( menuStruct, @vm )
 for x = 5 to xCount
    if ( menuStruct<0,x>[1,1] == "@" ) then
       null ; // ImageList header field - ignore
    end else
       if ( menuStruct<0,x,MENUPOS_NAME$> == menuID ) then
          // Found it - disable it
          menuStruct<0,x,MENUPOS_GREY$> = TRUE$
          x = xCount; // break;
       end
    end
 next

Inserting and removing items is a little more involved, due to the need to preserve the end flags in the correct location, but it is quite possible with a bit of care and attention. We won’t be covering that here however, so for the present this is left as an exercise for the reader.

Calling the TRACKMENUPOPUP method

Once you’ve finished with your modifications you can simply let the CONTEXTMENU event complete which calls the TRACKPOPUPMENU method to actually display the menu.

TRACKPOPUPMENU is a new method that displays a context menu at the specified coordinates.  In the unlikely event that you need to call it yourself here are the details:

   bSuccess = exec_Method( ctrlEntID, "TRACKPOPUPMENU", |
                           menuStruct,                  |
                           xPos,                        |
                           yPos,                        |
                           uFlags )

As you can see most of the parameters are the same as passed for the CONTEXTMENU event – the only difference being the “uFlags” argument.

The TRACKPOPUPMENU method is actually a thin wrapper around the Windows API TrackPopupMenu() function and the “uFlags”  argument in the method maps onto the “uFlags” argument in the Windows function.  You can check the Microsoft documentation for more details on that if you wish.

In the next post we’ll wrap up this short series on context menus and take a look at the ContextMenu stored procedure.

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