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