FORM Objects + EVENTS
Updated: July 11, 2009

The system may report a number of events with a simple mouse click.
Therefore, internal variable nCode (notify code) may be examined in event
subroutines to filter desired events (HBguidef.inc).  For example, COMBOBOX
and LISTBOX events may be filtered as shown in hotwin.bas source code.

Note: Based on available information, a HotBasic application will most often
set hWnd to the handle of the message sender upon calling event subroutines.

All event procedures have up to five useful arguments, in HotBasic internal
variables hWnd, uMsg, wParam, lParam and nCode.

hWnd is the FORM object handle.

uMsg is the message type.  E.g., uMsg indicates the mouse button used:

             Left  Right Middle
OnMouseDown  &H201 &H204 &H207
OnMouseUp    &H202 &H205 &H208
OnDblClick   &H203 &H206 &H209

wParam and lParam provide additional data specific to the message type.

nCode is the specific event evoking the message, where applicable, described
in HBguidef.inc in your "inc" subdirectory.

An event procedure cannot assume that these values will not be overwritten
by startup of other event procedures, since even a single GUI application
may have multiple threads running.  Thus, if one or more of the event
arguments are needed, they should be saved immediately upon entry into your
procedure:

  DEFINT vKey
  MyKeyDownProc:
  vKey = wParam  'save wParam

As above, event procedures may start with a LABEL and end with RETURN.  Or

  DECLARE SUB MyKeyDownProc  'no arguments listed here
  'code
  MyForm.OnKeyDown = MyKeyDownProc
  'code
  SUB MyKeyDownProc
  vKey = wParam  'save wParam
  'code
  END SUB


EVENTS     Defines subroutine called on event
~~~~~~     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OnClick    MyButton.OnClick = RunMyCode

  .OnClick is a generic user-select event, which might be called ".OnSelect"
  instead.  For example, a user LISTBOX selection occurs with doubleclick.
  Keyboard inputs may also cause a user-select event.  If you want mouse only,
  try .OnMouseDown.

  Most .OnClick events arise from WM_COMMAND (uMsg = &H111) messages.  nCode
  is the "notify code" -- the HIWORD of wParam.  The LOWORD of wParam is the
  .ID of the object originating the message.  MyID = LOWORD(wParam)

  lParam is the handle of the object which is most often that of the main form,
  since child objects send messages through the parent.  For SCROLLBAR and
  TRACKBAR, lParam is .Position for appropriate nCodes.

  Some .OnClick events arise from WM_NOTIFY (uMsg = &4E) messages where lParam
  is not a handle, but rather a pointer to a NMHDR structure. 

  Generally, the .OnClick event (WM_COMMAND) is processed only by FORM or
  SPLASH parents.  That is, if a BUTTON is set as child of something else,
  such as a TABCONTROL, PANEL, GROUPBOX, etc, .OnClick will not produce any
  response for the simple reason that these objects *do not* process
  WM_COMMAND messages.  For such "child of child" objects, use .OnMouseDown
  instead (please see example in hotchild.bas in the HotToys download).

OnClose   

  For forms, the close button in the title bar normally closes the form
  displayed with a .ShowModal statements allowing subsequent code in the
  application to run.  If you prefer that additional FORMs are "closed" without
  closing the main form, try using .OnClose:

  form2.OnClose = form2_close
  'code
  form2_close:
  form2.visible=false  'hide form2 instead of exiting application
  return

  For the main form, a "blank" .OnClose procedure prevents the user from
  closing the main form:

  form_close:
  return

OnDblClick  wParam and lParam as in .OnMouseDown

OnKeyDown 

OnKeyUp 

  .OnKeyDown and .OnKeyUp trap WM_KEYDOWN (uMsg = &H100) and WM_KEYUP
  (uMsg = &H101) messages where wParam = the virtual-key code of the non-
  system key (alt not pressed) and lParam contains details:

  Bits  Description
  0-15  repeat count = LOWORD(lParam)
  16-23 scan code = HIWORD(lParam) AND &HFF
  24    1 if the key is an extended key else 0
  30    previous key state:
           1 if the key is down before the message is sent for WM_KEYDOWN
           1 for WM_KEYUP
  31    transition state: 0 for WM_KEYDOWN, 1 for WM_KEYUP

OnMessage 

  (1) Usage with FORM events

  Traps Windows messages not processed by other HotBasic .On... events.
  This powerful event allows coders to process such messages to customize
  and expand HotBasic coding capabilities.  Used with main FORM.

  =====onmsg.bas
  $APPTYPE GUI: $TYPECHECK ON
  SHOWCONSOLE
  DIM form As FORM
  form.onmessage = PrintThis
  form.ShowModal
  END

  PrintThis:
  'normally you select your message here
  PRINT HEX$(hWnd);space;HEX$(uMsg);space;HEX$(wParam);space;HEX$(lParam)
  RETVAL zero  'Default Windows Procedure called as usual
  RETURN
  =====onmsg.bas

  To use .OnMessage, "RETVAL zero" or "RETVAL one" are essential to control
  whether the Default Windows Procedure is or is not called.  With 
  "RETVAL zero", window object behavior might remain the same, because
  its Default procedure is called as normal (without .OnMessage).
  With "RETVAL one", you have essentially bypassed the Default procedure
  and "replaced" it with yours, which in effect is a sort of "sub-classing"
  done in the HotBasic way -- simple and clean.
  
  In sum, your .OnMessage procedure (1) does what it wants for particular
  messages (uMsg) and (2) decides whether the Default Windows Procedure
  for the object and message is also run (RETVAL zero) or not (RETVAL one).

  (2) Usage with FORM child Objects (e.g., GRID, TABCONTROL, TREE, etc)

  An .OnMessage routine for a FORM child object is equivalent to .OnNotify
  and thus, the uMsg parameter is always WM_NOTIFY.  "Something happened to
  me," is the event for a child object .OnMessage procedure.

  On entry, the wParam variable should equal the .ID property of the child
  object.  lParam points to a NMHDR structure, where BYREF(lParam) equals
  the object's .Handle property and BYREF(lParam+8) is the notification code
  which tells you what happened.  Coders can look up the codes that apply
  for the Object type.

  For error checking, note that wParam should equal BYREF(lParam+4); both
  values are the object's .ID property.

OnMouseDown 

OnMouseMove

OnMouseUp 

  .OnMouseDown, .OnMouseMove and .OnMouseUp trap button events. 

  wParam key flags are:

  $DEFINE MK_LBUTTON  &H1 'left mouse button
  $DEFINE MK_RBUTTON  &H2 'right mouse button
  $DEFINE MK_SHIFT    &H4 'SHIFT key is down
  $DEFINE MK_CONTROL  &H8 'CTRL key is down
  $DEFINE MK_MBUTTON &H10 'middle mouse button

  LOWORD(lParam) is mouse x and HIWORD(lParam) is mouse y position.

OnPaint    No message parameters.

  MyForm.OnPaint = form_paint
  'code
  form_paint:
  MyForm.Circle 40,40,30,&HFFFFFF 'white
  return

OnReSize 

  wParam = a SIZE_... value; client width = LOWORD(lParam) and client height
  = HIWORD(lParam).

OnShow     wParam = 1 (show) or = 0 (hide)

  To run GUI programs from the command-line or from a .bat file, there may
  be no user key or mouse inputs.  Thus, the main form .OnShow procedure
  will be called with FORM .Show (not .ShowModal):

  (1) Parse the command-line and set up your job, by setting EDIT text,
  CHECKBOX states, etc, anything a real-time user might otherwise do with
  key and mouse inputs in a "regular" run of the program.

  (2) Call the procedure that starts the job.  For example, this may be an
  .OnClick procedure that would be called if a user had clicked a button. 

  (3) Exit the program.

  If you watch the program run, it will "magically" appear with all the
  "right" settings, start running and close itself when the job is done.

  You never get out of the .OnShow procedure, unless it detects that certain
  command-line parameters are absent.  If so, the .OnShow procedure is done
  and the next main program statement may be FORM .ShowModal for a regular
  user-controlled run.

  Since .Show does not start the GUI message loop, be sure to add DOEVENTS
  as needed in your .OnShow and procedure code.

hbLoop
~~~~~~
hbLoop is an internal variable created in GUI apps.  If hbLoop is non-zero,
its value is assumed to be the entry address for a routine, e.g.,

  hbLoop = codeptr(MyRoutine)

which will process *all* messages before any further normal processing occurs.

Here is a template for a hbLoop routine:

  MyRoutine:
  'your code
  RETVAL zero
  ret

The above code does nothing.  Upon a message, before anything is done, hbLoop
is checked and if non-zero, MyRoutine is called with hbMsg containing the
address of the Windows' message structure which would contain all the info you
need.  E.g., BYREF(hbMsg) is the handle of the Window's object sending the
message.

Finally, with RETVAL X in MyRoutine, if X is not zero, then any further default
processing of the message is aborted.  In the above template, RETVAL zero
assures that "normal" default processing of the message occurs.

In sum, hbLoop provides an easy way to "preprocess" all app messages in a
custom manner with the option to then run the default event processing
(RETVAL zero) or not (RETVAL one).  With the latter return value, you have the
option to completely replace HotBasic's and Windows' default message processing
with your own code.

~~~~~~~~~~~
Note:  The ZERO keyword may be used to "turn off" any of the above events:

  MyObject.OnClick = zero

At run-time, an application can change the procedure that handles an event:

  MyObject.OnClick = MyObjProc2  'was MyObjProc1 at startup


+ Penthouse (registered) version

Copyright 2003-2009 James J Keene PhD
Original Publication: Nov 18, 2003
