Mouse

.


Apply focus to a widget

Sometimes you may need to force focus to a certain widget.
There must be several solutions but I found this one to be fairly reliable: make Windows think the user clicked on the widget using his left mouse button. 'Click' is a combination of 'buttondown' and 'buttonup' so it involves two messages, but I assume you might be allowed to skip one of them.
So if you want to apply focus to FILL-IN-1 you might say:

  run MouseClick(FILL-IN-1:HANDLE).
{windows.i}
  
PROCEDURE MouseClick :
 DEFINE INPUT PARAMETER hWidget AS HANDLE NO-UNDO.
 
 DEFINE VARIABLE ReturnValue AS INTEGER NO-UNDO.
 RUN PostMessage{&A} IN hpApi(hWidget:HWND,
                              {&WM_LBUTTONDOWN},
                              {&MK_LBUTTON},
                              0,
                              OUTPUT ReturnValue).
 RUN PostMessage{&A} IN hpApi(hWidget:HWND,
                              {&WM_LBUTTONUP},
                              0,
                              0,
                              OUTPUT ReturnValue).
END PROCEDURE.

Notes

You might have noticed the use of PostMessage instead of SendMessage like in apply the right mouse button. I have no idea why SendMessage doesn't work for the left mouse button.


Apply the right mouse button

Or: programatic dropping a popup-menu

Sometimes you will want to "apply" a mouse click to a widget, for example to give it focus (with the standard left button) or to force a popup-menu to pop up (with the alternate button).
In this example we have an image widget; the user can perform several actions on that image widget (like loading a new bmp-file or whatever). Those actions are listed in a popup-menu and now you want that popup-menu to appear whenever the user clicks the widget, even when he uses the left mouse button. Unfortunately the P4GL statement 'APPLY "MOUSE-MENU-CLICK" doesn't work but you can fool Progress by sending the appropriate mouse messages.

ON "MOUSE-SELECT-CLICK" OF IMAGE-1
DO:
  RUN CenterMouseCursor(SELF:HANDLE).
  RUN Apply-mouse-menu-click(SELF:HANDLE).
END.

The first statement, CenterMouseCursor, moves the mouse to the middle of the widget so this will be the exact location where the upper-left corner of the popup-menu will appear. This is by itself not very important and you may want to skip or change it. The real work is done by the Apply-mouse-menu-click.
Here is the api stuff:

{windows.i}  
 
PROCEDURE Apply-mouse-menu-click :
/*------------------------------------------------------------------------------
Purpose:     Programatic click the right mouse button on a widget
Parameters:  Widget-handle on which you want to click
------------------------------------------------------------------------------*/
   DEFINE INPUT PARAMETER  p-wh   AS WIDGET-HANDLE  NO-UNDO.
 
   DEFINE VARIABLE ReturnValue AS INTEGER NO-UNDO.
   RUN SendMessage{&A} IN hpApi (INPUT p-wh:HWND, 
                                 INPUT {&WM_RBUTTONDOWN},
                                 INPUT {&MK_RBUTTON},
                                 INPUT 0,
                                 OUTPUT ReturnValue).
   RUN SendMessage{&A} IN hpApi (INPUT p-wh:HWND, 
                                 INPUT {&WM_RBUTTONUP},
                                 INPUT 0, 
                                 INPUT 0,
                                 OUTPUT ReturnValue).
END PROCEDURE.
 
PROCEDURE CenterMouseCursor :
/*------------------------------------------------------------------------------
Purpose:     Move the mouse cursor to the middle of a widget
Parameters:  the widget-handle
------------------------------------------------------------------------------*/
   DEFINE INPUT PARAMETER  p-wh   AS WIDGET-HANDLE  NO-UNDO.
 
   DEFINE VARIABLE lppoint     AS MEMPTR  NO-UNDO.  /* POINT FAR*  */
   DEFINE VARIABLE ReturnValue AS INTEGER NO-UNDO.
   SET-SIZE(lppoint)= 2 * {&INTSIZE}.
 
   PUT-{&INT}(lppoint,1 + 0 * {&INTSIZE})=INTEGER(p-wh:WIDTH-PIXELS / 2).
   PUT-{&INT}(lppoint,1 + 1 * {&INTSIZE})=INTEGER(p-wh:HEIGHT-PIXELS / 2).
   RUN ClientToScreen IN hpApi (INPUT p-wh:HWND, 
                                INPUT GET-POINTER-VALUE(lppoint),
                                OUTPUT ReturnValue).
   RUN SetCursorPos IN hpApi   (INPUT GET-{&INT}(lppoint,1 + 0 * {&INTSIZE}), 
                                INPUT GET-{&INT}(lppoint,1 + 1 * {&INTSIZE}),
                                OUTPUT ReturnValue).
   SET-SIZE(lppoint)= 0.
END PROCEDURE.
 

Explanation:

Procedure SetCursorPos works with absolute screen coordinates where the upper left corner of your screen has coordinates (0,0).
But the widget coordinates p-wh:X and p-wh:Y are relative to p-wh:parent.
The conversion is done by function ClientToScreen (there is also a ScreenToClient function) which has two params: a handle and a memorypointer to a POINT structure.

Function ClientToScreen changes the contents of the POINT structure.

Based on a procedure by Rod Gaither


mouse-over event

based on an example by Chris Braddock

There is no real MOUSE-OVER event but this example shows one possible way to achieve the same effect.
The general idea is: use the PSTimer.OCX. Read the mouse coordinates on every tick of the timer using API-function GetCursorPos. The resulting coordinates are screen-coordinates. Then use API-function ScreenToClient to convert the screen-coordinates to client-coordinates, where "client" is the window you are interested in. Finally, test if those client coordinates are inside the client rectangle.
Attached example (in mousexy.zip) continuously shows the screen-coordinates and the client-coordinates. It also colors the frame green when the mouse moves over the frame, and colors the frame grey when the mouse moves away from the frame.
The example is based on these two API functions: GetCursorPos and ScreenToClient.

Attachments

mousexy.zip