|
|||||||
One issue that recently reared it's head again at Sprezz Towers Support Central was the problem of using the Basic+ Rnd() function to generate a random number sequence - apparently the same series of numbers was being produced every time.
On first investigation it appeared that the customer was not using the InitRnd() function to seed the Rnd() generator before asking for the random numbers. This is a documented requirement - Calling InitRnd() is an absolute necessity before using the Rnd() function. Even so, using InitRnd() as specified in the online help failed to produce sufficiently random results so we decided to take a closer look at the process to see what the problem was and how we could improve it. Under the hood the Basic+ InitRnd() and Rnd() functions are little more than thin wrappers around the srand() and rand() functions in the Windows C-runtime library. The rand() function actually produces a pseudorandom sequence of numbers, so it is not truly random at all - instead the srand() function is used to seed the starting point for the pseudorandom sequence so it begins in a different place after each initialisation. Also, if srand() is called again with the same seed the same sequence of numbers is generated by rand()! Going back to the problem, the OpenInsight online help gives an example of using InitRnd() with a seed based on the current time and date (which is what our customer tried next): 0001 /* 0002 The present time and date set the random number 0003 generator with a unique value. 0004 */ 0005 0006 InitRnd Time(): Date() Unfortunately for this example using the date results in a seed value that doesn't show much variance in it's low order value (it only increments by one per day), and this appears to have the effect of generating a identical Rnd() sequence between each initialisation (especially if called multiple times on the same day which is something that should be avoided). We then thought about swapping the date and time around so that the low order value would hopefully be more varied, but here we find ourselves in danger of generating predictable sequences if we started our application at the same time every day (like at 9:15am for example). What we needed was some way to fulfil the following criteria:
We quickly put together the following function to do this that gave much better results: 0001 subroutine zz_InitRnd( bReset ) 0002 /* 0003 Author : Mr C, Sprezzatura Ltd 0004 Date : 29 July 2010 0005 Purpose : Simple function to call initRnd with with a more varied 0006 seed value, and to also ensure we only call it once per 0007 session. 0008 0009 Parameters 0010 ========== 0011 0012 bReset : If TRUE$ then call initRnd regardless of whether or 0013 not it's been called before. 0014 0015 */ 0016 declare function getTickCount, getEngineWindow 0017 $insert logical 0018 0019 if assigned( bReset ) else bReset = "" 0020 0021 common /%%_ZZ_INITRND_%%/ zzInitRndSeed@ 0022 0023 * // Check to see if we've already been called this session. 0024 if zzInitRndSeed@ then 0025 * // We've called it already - are we asking for a reset? 0026 if bReset else 0027 * // Nope! 0028 return 0029 end 0030 end 0031 0032 * // Start off with the tickcount (ms) and the 0033 * // OE hwnd ... 0034 seed = getTickCount() + getEngineWindow() 0035 0036 * // Now adjust this slightly to help avoid the time 0037 * // factor 0038 if mod( seed, 2 ) then 0039 b = seed[-1,1] 0040 if b then 0041 seed = int( seed / b ) 0042 end else 0043 seed = int( seed / 3 ) 0044 end 0045 end 0046 0047 * // Right - run it through some more mods to keep it 0048 * // within range of a short int if it's not already... 0049 loop 0050 while ( seed > 0x7FFF ) 0051 if mod( seed, 2 ) then 0052 seed -= 0x7FFF 0053 end else 0054 b = seed[-1,1] 0055 if b then 0056 seed = int( seed / b ) 0057 end else 0058 seed = int( seed / 3 ) 0059 end 0060 end 0061 repeat 0062 0063 initRnd seed 0064 0065 * // Flag that we've already called this... 0066 zzInitRndSeed@ = seed 0067 0068 return (Note that we have a global variable in place to ensure that initRnd is only called once per session, unless we explicitly override this) You can download a text version of zz_InitRnd 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: Basic+, 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 |
|||||||
| |||||||