![]() |
|||||||||||||||||||||||
![]() |
|
||||||||||||||||||||||
![]() |
|||||||||||||||||||||||
|
A recent question on the Revelation Forum asked how to detect an ENTER keypress when using an EditTable as this is not exposed by the standard OpenInsight CHAR event (ENTER is one of the keys that the EditTable normally considers to be "reserved" like TAB and the Arrow keys).
Despite this limitation it is actually possible to detect an ENTER keypress with WM_KEYDOWN window message and the WINMSG event but ONLY if the EditTable is set to "Protected", i.e.
To trap the ENTER key we first need to tell OpenInsight that we want to be notified of any WM_KEYDOWN window messages that are sent to the EditTable by using the OpenInsight QUALIFY_EVENT message. This is normally done in the form's CREATE event: 0001 $insert winAPI_WindowMessage_Equates
0002 $insert logical 0003 0004 call send_Message( @window : ".TABLE_1", "QUALIFY_EVENT", | 0005 WM_KEYDOWN$, TRUE$ ) 0006 Once we've done this we can write a WINMSG event handler for the EditTable that responds to the VK_RETURN keycode: 0001 /*
0002 Example WINMSG event handler to trap the Enter key (VK_RETURN$) 0003 while processing the WM_KEYDOWN$ window message. 0004 0005 hwnd -> handle of the EditTable 0006 message -> Message number - should be WM_KEYDOWN$ 0007 wParam -> Virtual Keycode - we are looking for VK_RETURN$ 0008 lParam -> Keydown flags - see MSDN for more info! 0009 0010 */ 0011 0012 $insert winAPI_WindowMessage_Equates 0013 $insert winAPI_VirtualKey_Equates 0014 $insert logical 0015 0016 begin case 0017 case ( message = WM_KEYDOWN$ ) 0018 0019 begin case 0020 case ( wParam = VK_RETURN$ ) 0021 // Write your code to handle the event here 0022 call msg( @window, "Enter key pressed!" ) 0023 0024 end case 0025 0026 end case 0027 0028 return TRUE$ (Note in these examples we are using some insert records from our WinAPI Library that you can find here) Labels: EditTable, EditTable Cookbook, OpenInsight
Following on from our recent post on how to remove the row-selection from an EditTable we came across a related issue last week that we think you might like to know about.
Basically we designed a form with several EditTables, all row-select enabled, but during testing it became difficult to know where the actual input focus was as so many controls had a selection highlighted. We could have used the technique in the aforementioned blog post to clear the row-selection during the LOSTFOCUS event, but in this case we still needed to see which row was selected even though the focus was on another control. The solution we used was to change the highlight color of the EditTable during the LOSTFOCUS event, toning it down to a lighter shade than normal. We then reset it during the GOTFOCUS event. This is actually quite easy to achieve with the COLOR_BY_POS message, the only real challenge is to devise an algorithm to fade the highlight color. As usual we've saved you the trouble - here's small function that you can use as a starting point for your own application should you wish to use a similar technique: 0001 subroutine edt_FadeSelection( edtID, bFade ) 0002 /* 0003 Author : Darth C, Sprezzatura Actual 0004 Date : 12 Jul 2010 0005 Purpose : Function to adjust the row selection color of an edit table 0006 0007 Parameters 0008 ========== 0009 0010 edtID -> Fully qualified name of the edit table 0011 0012 bFade -> If TRUE then fade the selection color, otherwise 0013 reset it to normal 0014 0015 Requirements 0016 ============ 0017 0018 */ 0019 declare function rgb 0020 $insert winAPI_EditTable_Equates 0021 $insert winAPI_SysColor_Equates 0022 $insert logical 0023 0024 if assigned( edtID ) else edtID = "" 0025 if assigned( bFade ) else bFade = FALSE$ 0026 0027 if len( edtID ) then 0028 if ( bFade ) then 0029 goSub fadeSelectionColor 0030 end else 0031 goSub resetSelectionColor 0032 end 0033 end 0034 0035 return 0036 0037 /////////////////////////////////////////////////////////////////////////////// 0038 /////////////////////////////////////////////////////////////////////////////// 0039 0040 fadeSelectionColor: 0041 0042 origColor = winAPI_GetSysColor( SYSCOLOR_HIGHLIGHT$ ) 0043 0044 origColor = fmt( oconv( origColor, "MB" ), "R(0)#32" ) 0045 bleach = 185 ; * // this is how much white we want to add...(0..255) 0046 0047 selColor = iconv( origColor[25,8], "MB" ) 0048 selColor := @fm : iconv( origColor[17,8], "MB" ) 0049 selColor := @fm : iconv( origColor[9,8], "MB" ) 0050 0051 * // Now add the bleach to each component 0052 for x = 1 to 3 0053 pct = ( 1 - ( selColor<x>/255 ) ) 0054 selColor<x> = selColor<x> + int( pct * bleach ) 0055 next 0056 0057 selColor = rgb( selColor<1>, selColor<2>, selColor<3> ) 0058 txtColor = winAPI_GetSysColor( SYSCOLOR_WINDOWTEXT$ ) 0059 if txtColor else 0060 * // 0 (BLACK) means "default color" in COLOR_BY_POS processing!!! 0061 txtColor += 1 0062 end 0063 0064 dtcs = DT_DEFAULTCOLOR$ | 0065 : @fm : DT_DEFAULTCOLOR$ | 0066 : @fm : selColor | 0067 : @fm : txtColor 0068 0069 call send_Message( edtID, "COLOR_BY_POS", 0, 0, dtcs ) 0070 0071 return 0072 0073 /////////////////////////////////////////////////////////////////////////////// 0074 0075 resetSelectionColor: 0076 0077 dtcs = DT_DEFAULTCOLOR$ | 0078 : @fm : DT_DEFAULTCOLOR$ | 0079 : @fm : DT_DEFAULTCOLOR$ | 0080 : @fm : DT_DEFAULTCOLOR$ 0081 0082 call send_Message( edtID, "COLOR_BY_POS", 0, 0, dtcs ) 0083 0084 return 0085 0086 /////////////////////////////////////////////////////////////////////////////// 0087 /////////////////////////////////////////////////////////////////////////////// In your EditTable LOSTFOCUS event you call this: call edt_FadeSelection( @window : ".TABLE_1", TRUE$ ) And to reset the colors in your EditTable GOTFOCUS event you call this: call edt_FadeSelection( @window : ".TABLE_1", FALSE$ ) (You'll notice that the function uses two "WinAPI" $insert records, both of which can be found the WinAPI Library that we recently posted.) You can download a text version of edt_FadeSelection here. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Labels: EditTable, EditTable Cookbook, OpenInsight
There's a quick way to select or deselect all rows in a multi-select edit table, and that's via the DTM_SELALLROWS message. It takes a single wParam argument which is TRUE$ to select all rows, or FALSE$ to deselect them.
Here's an example: $insert logical equ DTM_SELALLROWS$ to 1085 ; * // ( WM_USER + 61 ) hwndEdt = get_Property( @window : ".TABLE_1", "HANDLE" ) * // Select all rows.... call sendMessage( hwndEdt, DTM_SELALLROWS$, TRUE$, 0 ) * // Deselect all rows.... call sendMessage( hwndEdt, DTM_SELALLROWS$, FALSE$, 0 ) Labels: EditTable, EditTable Cookbook, OpenInsight
One of the more annoying visual traits we find with the EditTable is with single row selection: There seems to be no way to unselect a row and remove the highlight once it's there, and this can lead to a confusing state when it appears that more than one control has the focus.
We've often been asked how to rectify this as simply setting the SELPOS property doesn't suffice, so here's the solution - we use the Windows API SendMessage function to clear the selection like so... $insert logical equ DTM_SELROW$ to 1083 ; * // ( WM_USER + 59 ) equ DTPOS_INVALID$ to -3 hwndEdt = get_Property( @window : ".TABLE_1", "HANDLE" ) call sendMessage( hwndEdt, DTM_SELROW$, FALSE$, DTPOS_INVALID$ ) Labels: EditTable, EditTable Cookbook, OpenInsight
Way back in the days of OpenInsight 7.2.1 the EditTable was modified so that the speed of setting large amounts of data via the LIST or ARRAY properties was significantly increased.
What was not changed was the speed of getting data via LIST or ARRAY and this can have a impact on the setting speed if you're not careful, because each Set_Property operation performs an implicit Get_Property regardless of whether or not you actually want the original data. For example, if you already have 10000 lines of data in your EditTable and you want to use the LIST or ARRAY property to set new data you may still see a speed drop as the system uses the slow Get_Property to retrieve the original data before the update. To overcome this problem you can tell the EditTable to clear the existing data before you use Set_Property, that way the implicit Get_Property has nothing to process. This can be done with SendMessage() and the DTM_RESETDATA message like so: 0001 equ DTM_RESETDATA$ to 1025 0002 0003 hwndEdt = get_Property( @window : ".TABLE_1", "HANDLE" ) 0004 call sendMessage( hwndEdt, DTM_RESETDATA$, 0, 0 ) 0005 0006 call set_Property( @window : ".TABLE_1", "LIST", lotsOfStuff ) Labels: EditTable, EditTable Cookbook, OpenInsight
Getting notification of mouse messages has always been something of a problem with OpenInsight EditTable controls. With other controls this process is quite easy - you simply qualify the WINMSG event with the relevent mouse message number and you can easily respond to it, but with an EditTable this process fails.
This is mainly due to the architecture of the EditTable itself - it's not really a single control, it's actually two: The visible control that you interact with (known as the "DataTbl" control) and a very thin parent wrapper around it (called the "Editable" control). When you use an EditTable control in your application OpenInsight creates the wrapper control which in turn creates the visible "DataTbl" control. When you interact with an EditTable in Basic+ you are interacting directly with the wrapper - it simply passes on your request to the "DataTbl" as appropriate. If you qualify the WINMSG event on an EditTable you are qualifying against the wrapper - you are NOT qualifying against the visible "DataTbl" control! When the user interacts with the EditTable control they communicate with the visible "DataTbl", so it is this part that receives the mouse messages. These messages are interpreted and the relevant notifications passed up to the wrapper and then onto OpenInsight - they are not passed directly, so you will never be able use a WINMSG event with the usual mouse messages. However, since OpenInsight 9.1 it has been possible to trap mouse messages by another means. In this version, when the "DataTbl" receives a mouse message, it actually sends a notification message to the wrapper that you can pick up with a WINMSG event. The mouse message itself is simply offset by the value 3124 (WM_USER + 2100). For example to detect a "Right Mouse Button Down" message you would qualify against WM_RBUTTONDOWN + 3124 like so: $insert logical * // From the Windows SDK headers: equ WM_USER$ to 0x0400 equ WM_LBUTTONDOWN$ to 0x0201 equ WM_LBUTTONUP$ to 0x0202 equ WM_LBUTTONDBLCLK$ to 0x0203 equ WM_RBUTTONDOWN$ to 0x0204 equ WM_RBUTTONUP$ to 0x0205 equ WM_RBUTTONDBLCLK$ to 0x0206 equ WM_MBUTTONDOWN$ to 0x0207 equ WM_MBUTTONUP$ to 0x0208 equ WM_MBUTTONDBLCLK$ to 0x0209 * // Offset value equ ETM_MOUSEMSGOFFSET to (WM_USER$ + 2100) ; * // 3124 * // EditTable Mouse message notifications equ ETM_LBUTTONDOWN$ to (ETM_MOUSEMSGOFFSET$ + WM_LBUTTONDOWN$) equ ETM_LBUTTONUP$ to (ETM_MOUSEMSGOFFSET$ + WM_LBUTTONUP$) equ ETM_LBUTTONDBLCLK$ to (ETM_MOUSEMSGOFFSET$ + WM_LBUTTONDBLCLK$) equ ETM_MBUTTONDOWN$ to (ETM_MOUSEMSGOFFSET$ + WM_MBUTTONDOWN$) equ ETM_MBUTTONUP$ to (ETM_MOUSEMSGOFFSET$ + WM_MBUTTONUP$) equ ETM_MBUTTONDBLCLK$ to (ETM_MOUSEMSGOFFSET$ + WM_MBUTTONDBLCLK$) equ ETM_RBUTTONDOWN$ to (ETM_MOUSEMSGOFFSET$ + WM_RBUTTONDOWN$) equ ETM_RBUTTONUP$ to (ETM_MOUSEMSGOFFSET$ + WM_RBUTTONUP$) equ ETM_RBUTTONDBLCLK$ to (ETM_MOUSEMSGOFFSET$ + WM_RBUTTONDBLCLK$) * // To trap a right button down message: call Send_Message( @window : ".TABLE_1", "QUALIFY_EVENT", ETM_RBUTTONDOWN$, TRUE$ ) Labels: EditTable, EditTable Cookbook, OpenInsight
A common requirement when dealing with EditTables is to prevent a user from deleting a row on a case by case basis at runtime. In many applications we've seen this implemented by trapping the standard DELETEROW event and then sending an INSERT message with the deleted row contents, but this looks messy and unprofessional because the data disappears and reappears again.
A better way to do this is to trap the low-level ETM_DELETEROW notification sent by the EditTable prior to the actual DELETEROW event occuring. However, this notification has to be handled in a synchronous fashion (via a WINMSG event), and we also have to tell OpenInsight to return a special value from its own low-level internal message handler so that the EditTable stops the deletion (This last requirement is why the event has to be handled synchronously, because we need to return a value at the point in time that the message is sent). We do this in two stages: First we tell OpenInsight to trap the WINMSG event for the EditTable and listen specifically for the ETM_DELETEROW message. This is normally done in a form's CREATE event like so: 0001 $insert logical 0002 0003 equ WM_USER$ to 1024 0004 equ ETM_INSERTROW$ to (WM_USER$ + 2004) 0005 equ ETM_DELETEROW$ to (WM_USER$ + 2005) 0006 0007 eventOp = TRUE$ ; * // Turn tracking on 0008 eventOp<4> = TRUE$ ; * // Track Synchronously 0009 0010 call send_Message( @window : ".TABLE_1", | 0011 "QUALIFY_EVENT", | 0012 ETM_DELETEROW$, | 0013 eventOp ) Next we have to add a WINMSG event handler to the EditTable to catch the ETM_DELETEROW message: 0001 $insert logical 0002 0003 equ WM_USER$ to 1024 0004 equ ETM_INSERTROW$ to (WM_USER$ + 2004) 0005 equ ETM_DELETEROW$ to (WM_USER$ + 2005) 0006 0007 begin case 0008 case ( message = ETM_DELETEROW$ ) 0009 * // Stop the delete here... 0010 call set_WinMsgVal( TRUE$, 0 ) ; * // Force PS to return 0 0011 ; * // to Windows 0012 0013 end case Set_WinMsgVal Notice the use of the Set_WinMsgVal function. This function only works from within a synchronous WINMSG event and it allows us to set the actual low-level value that OpenInsight returns internally from handling the ETM_DELETEROW message. Returning 0 here tells the EditTable not to allow the row deletion. Preventing Row Insertion We can also prevent users from inserting rows in a similar fashion, by trapping the ETM_INSERTROW message instead (which we've defined in the examples above). However, implementing this is an exercise left for the reader. Labels: EditTable, EditTable Cookbook, OpenInsight
Still on the topic of undocumented EditTable features here's the details of the MOVE_ROW message that you can use to move a row within an EditTable. The nice thing about this message is that it takes all the colour, style and formatting information when the row is moved, which makes it easier to use than deleting and inserting the row "manually" yourself.
MOVE_ROW message
E.g. 0001 * // EditTable MOVE_ROW message example to move the 0002 * // row at position 2 to position 4 0003 0004 edtID = @window : ".TABLE_1" 0005 fromRow = 2 0006 toRow = 4 0007 0008 call send_Message( edtID, "MOVE_ROW", fromRow, toRow ) Labels: EditTable, EditTable Cookbook, OpenInsight
As documented the TEXT_BY_POS message may be used to retrieve the contents of a nominated cell. What is not documented is the fact that you can use it to update the contents of a cell as well. It's simply a matter of adding an extra parameter containing the data you wish to set.
E.g. 0001 * // Example to show setting cell contents with the 0002 * // TEXT_BY_POS message. 0003 0004 * // Set the contents of cell [3,4] 0005 colNo = 3 0006 rowNo = 4 0007 cellText = "New Cell Data" 0008 0009 call send_Message( @window : ".TABLE_1", | 0010 "TEXT_BY_POS", | 0011 colNo, | 0012 rowNo, | 0013 cellText ) OpenInsight trivia bonus: The EditTable CELLPOS property is a simple wrapper around the TEXT_BY_POS message. Labels: EditTable, EditTable Cookbook, OpenInsight
While recently adding a new window into our internal admin system we ran into a subtle problem with the CHANGED event and the NOTIFYPOS property.
NOTIFYPOS is an EditTable property that is always updated to contain the coordinates of the last cell to raise an event, but this is not restricted to the CHANGED event: Any EditTable event that is cell-oriented will also update NOTIFYPOS when triggered, common examples being DBLCLK and POSCHANGED. In our case the sequence of events ran like so:
Now you'd think that step (6) would actually have taken place straight after step (3) but unfortunately that's not the case due to the way that OpenInsight communicates with OpenEngine to run Basic+ event handlers. Normal Basic+ event handlers are executed in an asynchronous fashion, i.e. they are not executed directly when the notification is received, but are placed into a queue and executed when the queue is processed by the application's "message pump" (For those of you familiar with the Windows API they are dispatched via the PostMessage function). The solution to the problem was to ensure that our Basic+ event handler ran in a synchronous fashion instead - i.e. it should be executed as soon as OpenInsight is notified by the EditTable that the CHANGED event has taken place. That way we know that NOTIFYPOS will still contain the correct coordinates when our handler runs. Doing this was simply a matter of qualifying the CHANGED event with the synchronous flag in the window CREATE event handler like so: 0001 * // This is from the CREATE event handler for the window 0002 0003 * // Set the synchronous flag for the CHANGED event 0004 * // of the EDT_DETAILS edit table. 0005 0006 tmp = TRUE$ ; * // .. to ensure event is registered. 0007 tmp<4> = TRUE$ ; * // Sync flag -> TRUE$ == Synchronous 0008 ; * // FALSE$ == Asynchronous 0009 0010 call send_Message( @window : ".EDT_DETAILS", | 0011 "QUALIFY_EVENT", | 0012 "CHANGED", | 0013 tmp ) The new sequence of events now ran like this:
Labels: EditTable, EditTable Cookbook, OpenInsight
Many of the applications we write need to display things like option dialog boxes near a specific control. In most cases this is quite easy to handle as we can easily obtain the SIZE property of a control and work out our positioning from that. A slightly more difficult task is to position something relative to an EditTable cell because OpenInsight doesn't expose this information as a property or a method.
We've seen many attempts to calculate cell coordinates in Basic+ - we've even done it a few times ourselves and it's quite a pain, having to take into account all the different styles of the edit table, the width of columns, which columns are hidden and so forth. Well, there's a really easy way to do this, and that's by asking the EditTable itself what the coordinates are via the standard Windows API SendMessage function. We just need to know what message to send to the EditTable. DTM_READCELLRECT The message we need is called DTM_READCELLRECT, and it returns the coordinates of the cell identified via the ACCESSPOS property. All we need to do is pass it the address of a RECT structure to fill in, which we then translate into a dynamic array which we can use further. Here's a simple function to demonstrate this: 0001 compile function edt_GetCellRect( edtID, colNo, rowNo ) 0002 /* 0003 Author : Darth C, Sprezzatura Actual 0004 Date : Sep 09 0005 Purpose : Function to return edit table cell coordinates 0006 0007 Parameters 0008 ========== 0009 0010 edtID -> Fully qualified name of the edit table 0011 0012 colNo -> Column number of the target cell. Defaults 0013 to currentPos 0014 0015 rowNo -> Row number of the target cell. Defaults to 0016 currentPos 0017 0018 Returns 0019 ======= 0020 0021 Returns the edit table cell coordinates as per the RECT 0022 structure layout, i.e. 0023 0024 <1> Left 0025 <2> Top 0026 <3> Right 0027 <4> Bottom 0028 0029 Note these coordinates are relative to the Edit Table 0030 CLIENT area, NOT the desktop/screen! 0031 0032 */ 0033 declare function sendMessage, blank_Struct, struct_To_Var 0034 declare function get_Property 0035 0036 equ DTM_READCELLRECT$ to 1079 ; * // (WM_USER + 55) 0037 equ DTA_ACCESS$ to 0x0000 0038 0039 if assigned( edtID ) else edtID = "" 0040 if assigned( colNo ) else colNo = "" 0041 if assigned( rowNo ) else rowNo = "" 0042 0043 if len( edtID ) else 0044 return "" 0045 end 0046 0047 if len( colNo ) and len( rowNo ) then 0048 call set_Property( edtID, "ACCESSPOS", colNo : @fm : rowNo ) 0049 end else 0050 * // Use the current "caret" position - ensure ACCESSPOS 0051 * // is sync'd with CARETPOS 0052 call set_Property( edtID, "ACCESSPOS", | 0053 get_Property( edtID, "CARETPOS" ) ) 0054 end 0055 0056 * // Create a blank RECT structure for the edit table 0057 * // to fill for us and lock it 0058 rc = blank_Struct( "RECT" ) 0059 lockVariable rc as BINARY 0060 0061 * // Send the DTM_READCELLRECT message. 0062 * // 0063 * // The third parameter (wParam) contains a value that 0064 * // tells the edittable which cell we want. DTA_ACCESS 0065 * // means "use the ACCESSPOS property". 0066 * // 0067 * // We send the address of the RECT structure to fill in 0068 * // as the last parameter (lParam). 0069 call sendMessage( get_Property( edtID, "HANDLE" ), | 0070 DTM_READCELLRECT$, | 0071 DTA_ACCESS$, | 0072 getPointer( rc ) ) 0073 0074 * // Unlock and translate the structure to a 0075 * // dynamic array 0076 unlockVariable rc 0077 rc = struct_To_Var( rc, "RECT" ) 0078 0079 return rc (The more pedantic amongst you may notice that we didn't reset ACCESSPOS after we updated it. The reason for this is simple - every low-level function in the EditTable updates ACCESSPOS to the required coordinates before executing, and you should never assume ACCESSPOS is at the correct coordinates - always set it yourself before use!) You can download a text version of edt_GetCellRect here Labels: EditTable, EditTable Cookbook, OpenInsight
Previous Posts
Archives
BlogRoll
Subscribe
Subscribe via RSS
(For those who still appreciate civilised technology.)
Add to your reader
Scan to subscribe
Subscribe to
|
|||||||||||||||||||||||
![]() |
|||||||||||||||||||||||
| |||||||||||||||||||||||