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