BUG: Wrong value of LastKey() after aChoice()

200 views
Skip to first unread message

Yakano

unread,
Sep 21, 2021, 12:56:26 AM9/21/21
to Harbour Users
Hi guys

When analyzing how the user chooses a value from aChoice (keyboard or mouse), to take one action or another, the returned value is always K_ENTER (13) and never K_LDBLCLK (1006).

The objective is to make the selection with a single click, instead of two, by injecting double click after detecting a single click. Then, the user is informed of the action carried out, showing the option he has chosen, which will not be necessary if he uses the keyboard.

I don't know if I'm doing something wrong. How can I know then which system the user used to select? Please can you try the code below and confirm? I use HB 3.2 in Console mode.

I have tested this with MENU TO and in that case it works fine, that is, the return value for mouse is K_LDBLCLK (1006).

Any ideas will be well received.

Thanks and best regards


// testing LastKey after aChoice
Private cScreen, nChoice, aOptions
   AltD()
   Clear Screen
   aOptions:={'1. One','2. Two','3. Three','4. Four','5. Five','0. Exit'}
   SET( _SET_EVENTMASK, INKEY_ALL-INKEY_MOVE )
   SET KEY K_LBUTTONDOWN TO DClick
   do while nChoice<>6
      nChoice := aChoice(10,10, 20,30, aOptions)
      if nChoice <> 0
      ///if LastKey() == K_LDBLCLK
            Save Screen to cScreen
            @ 10+nChoice-1,10 SAY PadR(aOptions[nChoice],20) COLOR 'W/R'
            MilliSec(250)
            Restore Screen from cScreen
            Alert('LastKey:'+Str(LastKey()))  // (?) LastKey()=13 (allways!!!)
      ///endif
      endif
   enddo

PROCEDURE DClick
   hb_KeyPut(K_LDBLCLK)
RETURN

Klas Engwall

unread,
Sep 21, 2021, 10:52:28 AM9/21/21
to harbou...@googlegroups.com
Hi Yakano,

> When analyzing how the user chooses a value from aChoice (keyboard or
> mouse), to take one action or another, the returned value is always
> K_ENTER (13) and never K_LDBLCLK (1006).
>
> The objective is to make the selection with a single click, instead of
> two, by injecting double click after detecting a single click. Then, the
> user is informed of the action carried out, showing the option he has
> chosen, which will not be necessary if he uses the keyboard.
>
> I don't know if I'm doing something wrong. How can I know then which
> system the user used to select? Please can you try the code below and
> confirm? I use HB 3.2 in Console mode.
>
> I have tested this with MENU TO and in that case it works fine, that is,
> the return value for mouse is K_LDBLCLK (1006).
>
> Any ideas will be well received.

K_ENTER replaces K_LDBLCLK inside Achoice(). Try instead with a user
function that returns AC_EXCEPT when it sees K_LBUTTONUP (after a
completed mouse click)-

Regards,
Klas

//----------------------------------

#include "set.ch"
#include "inkey.ch"
#include "achoice.ch"

procedure main()

// testing LastKey after aChoice
Private cScreen, nChoice, aOptions
setmode( 25, 80 )

AltD()
Clear Screen
aOptions:={'1. One','2. Two','3. Three','4. Four','5. Five','0. Exit'}
SET( _SET_EVENTMASK, INKEY_ALL-INKEY_MOVE )
// SET KEY K_LBUTTONDOWN TO DClick
do while nChoice<>6
nChoice := aChoice( 10, 10, 20, 30, aOptions,, 'MyUserFunc' )
if nChoice <> 0
///if LastKey() == K_LDBLCLK
Save Screen to cScreen
@ 10+nChoice-1,10 SAY PadR(aOptions[nChoice],20) COLOR 'W/R'
MilliSec(250)
Restore Screen from cScreen
Alert('LastKey:'+Str(LastKey())) // (?) LastKey()=13
(allways!!!)
///endif
endif
enddo

return

// PROCEDURE DClick
// hb_KeyPut(K_LDBLCLK)
// RETURN

function MyUserFunc( nMode )

local nRetVal := AC_CONT
local nKey := lastkey()

if nMode == AC_EXCEPT
do case
case nKey == K_ESC
nRetVal := AC_ABORT
case nKey == K_ENTER .or. nKey == K_LBUTTONUP
nRetVal := AC_SELECT
endcase
endif

return nRetVal

//----------------------------------

Mel Smith

unread,
Sep 21, 2021, 1:27:07 PM9/21/21
to Harbour Users
Hi Yakano:

   I think the problem is in the achoice.prg itself (i.e., in \harbour\src\rtl\achoice.prg lines 174 thru 181.
   See below:
   -Mel

   ***** lines 174-181 from achoice.prg *****
      CASE nKey == K_LDBLCLK .OR. nKey == K_LBUTTONDOWN
         nAux := HitTest( nTop, nLeft, nBottom, nRight, MRow(), MCol() )
         IF nAux != 0 .AND. ( nNewPos := nAtTop + nAux - 1 ) <= nItems
            IF Ach_Select( alSelect, nNewPos )
               DispLine( acItems[ nPos ], nTop + nPos - nAtTop, nLeft, Ach_Select( alSelect, nPos ), .F., nNumCols )
               nPos := nNewPos
               DispLine( acItems[ nPos ], nTop + nPos - nAtTop, nLeft, Ach_Select( alSelect, nPos ), .T., nNumCols )
               IF nKey == K_LDBLCLK

       ***** this next statement inserts a chr(13) into the input stream after detecting a K_LDBLCLK *****
                  hb_keyIns( K_ENTER )
               ENDIF
            ENDIF

         ENDIF
***** end of achoice fragment *****

Klas Engwall

unread,
Sep 21, 2021, 4:59:30 PM9/21/21
to harbou...@googlegroups.com
Hi Mel,

>    I think the problem is in the achoice.prg itself (i.e., in
> \harbour\src\rtl\achoice.prg lines 174 thru 181.
>    See below:
>    -Mel
>
>    ***** lines 174-181 from achoice.prg *****
>       CASE nKey == K_LDBLCLK .OR. nKey == K_LBUTTONDOWN
>          nAux := HitTest( nTop, nLeft, nBottom, nRight, MRow(), MCol() )
>          IF nAux != 0 .AND. ( nNewPos := nAtTop + nAux - 1 ) <= nItems
>             IF Ach_Select( alSelect, nNewPos )
>                DispLine( acItems[ nPos ], nTop + nPos - nAtTop, nLeft,
> Ach_Select( alSelect, nPos ), .F., nNumCols )
>                nPos := nNewPos
>                DispLine( acItems[ nPos ], nTop + nPos - nAtTop, nLeft,
> Ach_Select( alSelect, nPos ), .T., nNumCols )
>                IF nKey == K_LDBLCLK
>
>        ***** this next statement inserts a chr(13) into the input
> stream after detecting a K_LDBLCLK *****
>                   hb_keyIns( K_ENTER )
>                ENDIF
>             ENDIF
>
>          ENDIF
> ***** end of achoice fragment *****

Yes, that is exactly what I was referring to. K_ENTER replaces
K_LDBLCLK. But the problem, if it actually is a problem, can easily be
solved with the user function I posted. Actually, there are a zillion
useful things that a properly designed user function can accomplish.

Regards,
Klas

Mel Smith

unread,
Sep 21, 2021, 7:58:20 PM9/21/21
to Harbour Users
Hi Klas:
   Yes, I understand.
   I guess, also, that Yakano couldget  his own copy of achoice.prg, and make any changes he wished -- to develop a  'signal' that a mouse click was activating his menu rather than a key Press. Hmmmm . But that is probably a bad programming practice.
-Mel

Yakano

unread,
Sep 21, 2021, 9:55:53 PM9/21/21
to Harbour Users
Hi klass

MyUserFunc does not work quite well and it will take more control and work that I do not know if I will know how to carry out. The first character of each option does not move Achoice and when the mouse is used in Alert the click is also retrieved by MyUserFunc. Those tasks were performed by Acoice by default and I don't know if there will be more. 

 Greetings!

Yakano

unread,
Sep 21, 2021, 10:02:40 PM9/21/21
to Harbour Users
Hi Mel

I will try to advance in that way, although I have never modified any function of the Harbor itself, but perhaps it would be interesting to include that change for future versions, because I still think that the correct thing would be to receive a double click instead of enter when using LastKey.

Greetings!

Klas Engwall

unread,
Sep 22, 2021, 2:11:58 AM9/22/21
to harbou...@googlegroups.com
Hi Yakano,

> MyUserFunc does not work quite well and it will take more control and
> work that I do not know if I will know how to carry out. The first
> character of each option does not move Achoice and when the mouse is
> used in Alert the click is also retrieved by MyUserFunc. Those tasks
> were performed by Acoice by default and I don't know if there will be more.

So maybe you should show us what your user function does. And I don't
understand your comment about the Alert() keypress. In the test code we
have seen so far you do not call Alert() until Achoice() and the user
function have finished. How can the Alert() keypress jump back in time
and show up in the user function?

Or have you moved Alert() inside the user function now? In that case,
save the Lastkey() that exists before calling Alert() and put it back
with hb_SetLastKey() after the alert. BTW, what is the purpose of the
Alert() now? So far you have used it to display the Lastkey() value,
clearly for debugging only. What do you use it for now?

Regards,
Klas

Yakano

unread,
Sep 22, 2021, 3:28:14 AM9/22/21
to Harbour Users
Hi Klas 

 I didn't do anything with the MyUserFun function, I just copied, compiled and tested. With keyboard there are no problems, but with mouse I will try to explain ...

 
So maybe you should show us what your user function does. And I don't
understand your comment about the Alert() keypress. In the test code we
have seen so far you do not call Alert() until Achoice() and the user
function have finished. How can the Alert() keypress jump back in time
and show up in the user function?

1) Click Down on option 5 (Achoice moves to that option) 2) Click Up (Exit Achoice and enter Alert) 3) Click Down (Alert accepts Ok and exits ... {!} And Achoice moves to position 1.) 4) Click Up (Achoice takes Click Up in option 1 and loops if we continue using the mouse) I don't know how he gets it, but Alert did "Back to the Future" ... hahaha 
 
Or have you moved Alert() inside the user function now? In that case,
save the Lastkey() that exists before calling Alert() and put it back
with hb_SetLastKey() after the alert. BTW, what is the purpose of the
Alert() now? So far you have used it to display the Lastkey() value,
clearly for debugging only. What do you use it for now?

Yes, in this case I use Alert to show LastKey, but in many situations after choosing an "unusual or dangerous" option, I request confirmation with Alert. And this is even more "dangerous", because (according to the above example) after confirming option 5, without knowing it is returning to 1. Also, by giving options to control with the mouse, we are losing the selection with the first character (1..5) using the keyboard. At least, until that task (so far by default) is implemented in MyUserFun. 

Thanks, Klas. 

Appliserver

unread,
Sep 22, 2021, 5:54:52 AM9/22/21
to harbou...@googlegroups.com
You need to intercept the double click before everything else, I think.

Try this approach with HB_GTI_INKEYFILTER, it does exactly that.

Then, you can add an user function to manage achoice.

Dan

testdblclk.prg

Yakano

unread,
Sep 22, 2021, 11:16:50 AM9/22/21
to Harbour Users
Hello Dan

I have tested your code, but it doesn't do what I expected. It is not a question of knowing when a double click has been used to select that option, but of converting a simple click into a double click. 

That is why I use hb_KeyPut, to add it to the end of the buffer,  I respect that the first click of the user moves Choice to its position and then the double click injected makes the selection. 

The problem, I think, comes from where Mel points, aChoice uses hb_KeyIns to put a dummy Enter at the beginning of the keyboard buffer  for its internal functioning, which is finally shown outside when consulting LastKey (this is the Bug: to allow dummy Enter scape from aChoice).

I have used this same method successfully in "Menu To" (well, menu to has another small bug that makes double clicking anywhere on the screen be accepted even if it is not done on a prompt element, but that's another story ... XD). 

Thanks and best regards!

Appliserver

unread,
Sep 22, 2021, 12:15:11 PM9/22/21
to harbou...@googlegroups.com

Well, it seems that my understanding of the OP was wrong. Oops.

Anyway, the technique used in my prg is still a good example to control achoice. I don't think that rely on lastkey and putkey (or similar functions) is a good solution. I consider more robust and "elegant" not to touch the keyboard buffers. And lastkey after a mouse click is always 1002 (button UP), so you lose dblclick or click, when instead the block nAfterInkey is executed for every single event. Moreover it does not interfere with subsequent input (alert or other).

The example can be adapted to do what you need with small changes, I suppose.

Regards,

Dan

--
--
You received this message because you are subscribed to the Google
Groups "Harbour Users" group.
Unsubscribe: harbour-user...@googlegroups.com
Web: http://groups.google.com/group/harbour-users

---
You received this message because you are subscribed to the Google Groups "Harbour Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to harbour-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/harbour-users/f512583e-e277-43ee-9f86-71bcb3d32e82n%40googlegroups.com.

Klas Engwall

unread,
Sep 22, 2021, 2:02:28 PM9/22/21
to harbou...@googlegroups.com
Hi Yakano,

> 1) Click Down on option 5 (Achoice moves to that option) 2) Click Up
> (Exit Achoice and enter Alert) 3) Click Down (Alert accepts Ok and exits
> ... {!} And Achoice moves to position 1.) 4) Click Up (Achoice takes
> Click Up in option 1 and loops if we continue using the mouse) I don't
> know how he gets it, but Alert did "Back to the Future" ... hahaha

You say:
2) Exit Achoice
3) Achoice moves to option 1

No, Achoice cannot move anywhere after exiting. In fact, you are
starting a new Achoice() after exiting. And by default Achoice() starts
on the first item. If you want to use Achoice() in a loop that calls it
over and over again, and you expect it to start on your selection from
the last time you called it, then you have to provide the number of that
selection in the 8th argument.

> Yes, in this case I use Alert to show LastKey, but in many situations
> after choosing an "unusual or dangerous" option, I request confirmation
> with Alert.

Again, did you move Alert() into the user function? Then you must save
the Lastkey() value from before calling Alert() and put it back before
returning to Achoice() from the user function.

> And this is even more "dangerous", because (according to the
> above example) after confirming option 5, without knowing it is
> returning to 1.

Again, this is your loop that starts on option 1 the next time you call
Achoice(), not anything that Achoice() or the user function does.

> Also, by giving options to control with the mouse, we
> are losing the selection with the first character (1..5) using the
> keyboard. At least, until that task (so far by default) is implemented
> in MyUserFun.

Yes, the use of the first character is not supported when using a user
function. The supported keys are listed in the Clipper docs.

Regards,
Klas

Yakano

unread,
Sep 22, 2021, 4:10:46 PM9/22/21
to Harbour Users

Hello Dan

Undoubtedly your Prg provides a more robust and elegant vision to take into account, but I don't have much experience in Harbor and I keep looking for solutions that I think I understand better and that will maintain compatibility with the functions already developed. 

 Thanks!

Yakano

unread,
Sep 22, 2021, 4:16:24 PM9/22/21
to Harbour Users
Hi Klas 

 You're right, not keeping the last selected value misled me. 

 Thanks!

Yakano

unread,
Oct 13, 2021, 2:06:07 AM10/13/21
to Harbour Users
Hi Mel

I have made some minimal changes on aChoice thats works for me and I think it can be included in harbour core for having a right LastKey() after aChoice(), but I have not experience to made it.

I included MyChoice.prg (modified aChoice.prg), and it can be used if someone agree and know how to update for new harbour versions.

I just change hb_KeyIns(K_ENTER) for the code in case K_ENTER option ...

               ///hb_keyIns( K_ENTER )
                  IF nPos != 0
                     DispLine( acItems[ nPos ], nTop + nPos - nAtTop, nLeft, .T., .F., nNumCols )
                  ENDIF
                  nMode     := AC_IDLE
                  lFinished := .T.
               ENDIF

Best regards

El martes, 21 de septiembre de 2021 a las 19:27:07 UTC+2, Mel Smith escribió:
MYCHOICE.prg
Reply all
Reply to author
Forward
0 new messages