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.
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