Application Development


Chapter 47. Processing the Mouse Interface

This chapter shows how to evaluate and process mouse events, mouse data, and contextual information. Topics include:


Trapping Mouse Events

You can intercept single and double mouse clicks on an application-wide basis through Panther's key change hook function. You can also intercept double clicking on an individual widget through its Double Click property. Both techniques are discussed in the sections that follow.

Using Key Change Functions

With Panther's key change hook function, you can intercept single and double mouse clicks throughout the program. Panther's key file (smkeys.h) defines these two events through the logical keys MOUS for single mouse clicks, and MDBL for double clicks. A key change function that tests for these logical keys can use Panther library functions to examine the state of the mouse cursor and mouse buttons, and perform special processing accordingly.

For example, the following code shows in skeletal format a key change function that tests for a single click mouse event outside a field, and then determines which button, if any, is down. It also conditionally tests for different combinations of mouse events with keyboard modifiers, such as Shift+click versus Ctrl+click. Most of the processing relies on sm_ms_inquire to test the mouse's state. For detailed information on using this function, refer to page 47-4. For more information on key change functions, refer to page 44-36.

int keychg ( int which_key )
{
   int ms_btn;
   switch ( which_key )
   {
   case MOUS:
   
      /*is mouse click outside field? */
      if ( sm_ms_inquire( MOUSE_FIELD ) < 0
      { 
         ms_btn = ( sm_ms_inquire( MOUSE_BUTTONS ) & 0x49;
         /*is any button down?*/
         if ( ms_btn > 0 )
         {
            /*test which button is down*/
            switch ( mouse_button )
            {
               ...
            }
            /*is any keyboard modifier also down */
            if ( sm_ms_inquire( MOUSE_SHIFT ))
            {
               ms_kbd = sm_ms_inquire( MOUSE_SHIFT );
               switch ( ms_kbd ) /*which key is down? */
               {
                  ...
               }
 ...
}

Trapping Double Clicks on a Widget

Several widget types have the double_click property, which lets you specify an action that is triggered by double clicking on a widget. double_click gets a control string as its value. This control string can specify to call a function, invoke an operating system command, or open another screen.

The following widget types have the double_click property:


Getting Mouse Data

Panther provides library functions and application properties that get information about the mouse's current state:

Determining Mouse Click Location

The library function sm_ms_inquire lets you test the last mouse click's line and column location on a Panther screen or on the physical display. Several runtime properties also offer access to the field and screen on which the last mouse click occurred.

Identifying Mouse Coordinates

To determine the line and column location of the last mouse click, supply sm_ms_inquire with arguments of MOUSE_LINE and MOUSE_COLM, respectively. To get the mouse click's line and column within a Panther screen, supply MOUSE_FORM_LINE and MOUSE_FORM_COLM. For example, the following routine gets the mouse click coordinates on a map that is displayed on a static label:

void get_mouse_coords( void )
{
   int longitude, latitude;
   /* make sure the user clicked somewhere on the map */
   if ( sm_ms_inquire( MOUSE_FIELD ) > 0 && 
        sm_prop_get_str
        ( PR_APPLICATION, PR_MOUSE_FIELD_NAME ) == "mapLbl" )
   {
      longitude = sm_ms_inquire( MOUSE_FORM_COLM );
      latitude = sm_ms_inquire( MOUSE_FORM_LINE );
      return get_map_location( longitude, latitude );
   }
}

Mouse and Widgets

The previous example also uses sm_ms_inquire and sm_prop_get_str to test whether a mouse click occurred inside a field and the field's identity:

if ( sm_ms_inquire( MOUSE_FIELD ) > 0 && 
     sm_prop_get_str
     ( PR_APPLICATION, PR_MOUSE_FIELD_NAME ) == "mapLbl" )

When supplied an argument of MOUSE_FIELD, sm_ms_inquire returns either the field number in which the mouse click occurred, or -1 if the mouse click occurred outside the field.

You can also use these runtime properties to get the name of the field and occurrence in which a mouse click occurred:

All mouse properties are application-level properties, accessible through the @app() modifier. For example, this all-purpose code obtains the data in the last clicked-on occurrence of any field:

vars data
data = @widget(@app()->mouse_field_name)[@app()->mouse_field_occ]

Mouse and Screen

mouse_form_name is an application runtime property that gets the name of the screen on which a mouse click occurred. Like other mouse properties, it is accessible through the @app() modifier as in this example:

vars mouse_screen
mouse_screen = @app()->mouse_form_name

Determining Mouse Button State

You can get the state of each mouse button—up, down, just pressed, or just released—by supplying sm_ms_inquire an argument of MOUSE_BUTTONS. If successful, the function returns an integer bit mask. The function puts the requested data in three segments of three bits each, where each segment represents one of three mouse buttons—left, middle, and right. The three lowest-order bits contain left button data; if the mouse has only one button, only these bit settings are significant. The middle three bits contain right button data. The three highest-order bits contain data for the middle button, if any.

Each bit within a three-bit segment can be set as follows, from lowest- to highest-order bit:

For example, the bit settings returned for a just-initiated point and click operation—left button is down and just pressed—can be represented as follows:

A click and drag operation that is in progress—right button is down—can be represented like this:

Only four combinations of bit settings are meaningful to Panther and recognized as representing valid button states:

For example, the following routine tests whether any mouse buttons are down: it bitwise AND's sm_ms_inquire's return value with 0x49, thereby masking off all but the first, fourth, and seventh-order bits:

/*find out whether any button is down */
int is_any_button_down( void )
{
   return sm_ms_inquire ( MOUSE_BUTTONS ) & 0x49;
}

Identifying Keyboard Modifiers

By supplying sm_ms_inquire with an argument of MOUSE_SHIFT, you can find out whether a mouse click occurred with one or more of these keys pressed down: Shift, Ctrl, and Alt. The function returns an integer bit mask whose three lowest-order bits are set to indicate which of the three keys, if any, were pressed. These bits are set as follows, from lowest- to highest-order bit:

For example, a return value of 2 (0 1 0) indicates that the Ctrl key is down, while a return value of 5 (1 0 1) indicates that the Alt and Shift keys are both down. The second of these returns can be represented as follows:

In the following example, the return value of sm_ms_inquire(MOUSE_FIELD) is bitwise AND's with 0x06 in order to mask off the lowest-order bit (Shift). This lets the program determine whether Alt or Ctrl, or both, were pressed down during the last mouse click:

if ( sm_ms_inquire( MOUSE_SHIFT ))
{
   /*test for Alt and Ctrl keys only */
   ms_kbd = sm_ms_inquire( MOUSE_SHIFT ) & 0x06;
   switch ( ms_kbd )
   {
      case 0x02: /*Ctrl key is down */
       ...
      break;
      case 0x04: /*Alt key is down*/
       ...
      break;
      case 0x06: /*Alt+Ctrl keys are down */
       ...
      break;
   }

Reporting Elapsed Time between Mouse Clicks

sm_mus_time reports the number of milliseconds that elapsed since an unspecified time. You can compare this value to the value reported on previous or subsequent mouse clicks—for example, to determine whether two successive mouse clicks should be interpreted as a double mouse click.

Notes: Ordinarily, you can use the key change function to intercept double mouse click events. For more information, refer to page 47-1.


Changing the Mouse Pointer State

sm_delay_cursor sets the mouse pointer to be either the default cursor or the delay cursor, or gets the mouse pointer's current state, according to the supplied argument. It can also specify to change the cursor's state automatically, depending on whether the application is awaiting input or not.

For GUI platforms, you can set a screen's default cursor through its Pointer property. In Windows and Motif, the default cursor is an arrow. The delay cursor in Windows is an hourglass; in Motif, the delay cursor is usually a wristwatch icon. You can change Motif's default cursor through the pointerShape resource.

Because character-mode Panther does not change the mouse pointer shape, sm_delay_cursor resets the background status line message to the value of SM_WAIT or SM_READY. Note that you can turn background status messages on and off through sm_setstatus. sm_delay_cursor takes a single integer argument, one of these constants:

SM_AUTO_BUSY_CURSOR
Sets the mouse pointer to toggle automatically between the default cursor and the delay cursor, depending on whether the application is awaiting input or not. The default cursor appears whenever Panther is awaiting input.

SM_BUSY_CURSOR
Changes the mouse pointer into the delay cursor.

SM_DEFAULT_CURSOR
Restores the default cursor.

SM_SAME_CURSOR
Leaves the mouse pointer unchanged. Use this argument to get the pointer's current state.

SM_TEMP_BUSY_CURSOR
Temporarily changes the mouse pointer to the delay cursor. Panther restores the mouse pointer to the default cursor after Panther refreshes the screen.