# Creating Mouse Event Functions

You can create custom functions to handle mouse events in a graphics WINDOW or a WIDGET_WINDOW. These functions must use the syntax described below.

Each function must return a value of 0 to turn off default event handling or 1 to perform default event handling.

See the WINDOW example and WIDGET_WINDOW example, below.

Tip: Instead of creating a separate function for each event handler, you may find it convenient to use a custom event handling class along with the EVENT_HANDLER property. See Creating an Event Handler Class to Control Events for details on how to write this object class.

## Syntax

The syntax of the various event handler functions are similar, as the following list shows.

Handler Type | Syntax
--- | ---
Mouse_Down | Result = FUNCTION_NAME(Window, X, Y, Button, KeyMods, Clicks)
Mouse_Motion | Result = FUNCTION_NAME(Window, X, Y, KeyMods)
Mouse_Up | Result = FUNCTION_NAME(Window, X, Y, Button)
Mouse_Wheel | Result = FUNCTION_NAME(Window, X, Y, Delta, KeyMods)

Where FUNCTION_NAME is a unique name for the function, which will be called from one of the mouse event handling keywords of WINDOW or WIDGET_WINDOW. Avoid using generic names such as "MouseDownHandler" or "MouseUpHandler" to prevent name collisions with other applications using event handlers.

## Arguments

### Window

The object reference of the window in which the mouse event occurred.

### X

The x-coordinate location of the mouse cursor (in device coordinates) at the time of the event.

### Y

The y-coordinate of the mouse cursor (in device coordinates) at the time of the event.

### Button

The value of the clicked button. Possible values are:

Value | Mouse Button
--- | ---
1 | Left
2 | Middle
4 | Right

### Clicks

The value indicating how many button clicks occurred. The value is 1 for a single click and 2 for a double click.

### Delta

The value indicating the direction and number of movements of the mouse wheel.

Pushing the wheel generates positive values, pulling the wheel generates negative values. The magnitude of the value depends on the device setting for the individual mouse, but is usually limited to small integer values such as +1, -1, +2, -2, etc.

### KeyMods

The value containing a bitwise mask indicating which modifier keys are active at the time the mouse event happens. Possible values are:

Value | Modifier Key
1 | Shift
2 | Control
4 | Caps lock
8 | Alt

## Example Using WINDOW

In the following example code, the `RBBoxMouseDown` function creates a polygon and caches the start point of a drag operation. In the `RBBoxMouseMotion` function the polygon is updated with the current location of the cursor. In the `RBBoxMouseUp` function, the polygon is again updated and the color of the polygon is modified. Copy this code into a new IDL file and run it. To test the code, drag and drop the mouse to draw a box in the graphic window.

FUNCTION RBBoxMouseDown, oWin, $

x, y, iButton, KeyMods, nClicks

state = oWin.UVALUE

state.x0 = x

state.y0 = y

state.buttonDown = 1

state.poly.HIDE = 0

state.poly.SetData, [0,0,0], [0,0,0]

state.poly.LINESTYLE='--'

oWin.UVALUE=state

RETURN, 0 ; Skip default event handling

END

FUNCTION RBBoxMouseMotion, oWin, x, y, KeyMods

state = oWin.uvalue

IF state.buttonDown then begin

x0 = state.x0

y0 = state.y0

xVector=[x0,x0,x,x,x0]

yVector=[y0,y,y,y0,y0]

xy = state.poly.ConvertCoord(xVector, yVector, /DEVICE, /TO_NORMAL)

state.poly.SetData, REFORM(xy[0,*]), REFORM(xy[1,*])

ENDIF

RETURN, 0 ; Skip default event handling

END

FUNCTION RBBoxMouseUp, oWin, x, y, iButton

state = oWin.uvalue

IF (~state.buttonDown) THEN RETURN, 0

x0 = state.x0

y0 = state.y0

xVector=[x0,x0,x,x,x0]

yVector=[y0,y,y,y0,y0]

xy = state.poly.ConvertCoord(xVector, yVector, /DEVICE, /TO_NORMAL)

state.poly.SetData, REFORM(xy[0,*]), REFORM(xy[1,*])

state.poly.LINESTYLE='-'

state.buttonDown=0

oWin.uvalue=state

; Clear the current selections

oSelect = oWin.GetSelect()

FOREACH oVis, oSelect do oVis.Select, /UNSELECT

; Do a hit test and select new items.

oVisList = oWin.HitTest(x0+(x-x0)/2, y0+(y-y0)/2, $

DIMENSIONS=ABS([x-x0, y-y0]) > 10)

FOREACH vis, oVisList do begin

if vis ne state.poly then vis.Select, /ADD

ENDFOREACH

RETURN, 0 ; Skip default event handling

END

PRO RBBoxEventsTest

x = Findgen(200)

y = Sin(x*2*!PI/25.0)*Exp(-0.01*x)

p = PLOT(x, y, TITLE='Click, hold mouse down and drag, release to draw box')

; Add a hidden polygon for the rubber-band box.

poly = POLYGON([0,0,0],[0,0,0], /DEVICE, $

LINESTYLE='--', /HIDE, $

FILL_TRANSPARENCY=90, FILL_BACKGROUND = 1, FILL_COLOR='red')

p.window.UVALUE={x0:0, y0:0, buttonDown:0L, $

poly: poly}

p.window.MOUSE_DOWN_HANDLER='RBBoxMouseDown'

p.window.MOUSE_UP_HANDLER='RBBoxMouseUp'

p.window.MOUSE_MOTION_HANDLER='RBBoxMouseMotion'

END

## Example Using WIDGET_WINDOW

In this example, only the MOUSE_MOTION_HANDLER event handler is used. The event coordinates (in device coordinates) are converted to data coordinates and displayed in a label widget below the draw widget. A crosshair is also drawn at the nearest plot data point.

### Mouse Event Handler Function

FUNCTION ExWidWinMouseMotionHandler, oWin, x, y, KeyMods

state = oWin.uValue

IF (~ISA(state.plot)) then return, 1

; Convert from screen coordinates to data coordinates.

xy = state.plot.ConvertCoord(x, y, /DEVICE, /TO_DATA)

; "Snap" the location to the nearest plot point.

xy = state.plot.GetValueAtLocation(xy[0])

; Update the crosshair location and the label

state.crosshair.LOCATION = xy

probe = STRING(xy[0:1], FORMAT='(2F9.2)')

WIDGET_CONTROL, state.label, SET_VALUE=probe

return, 1 ; Perform default event handling

END

### Mouse Motion Procedure

Run the following code example to test the event handling functions defined in the previous code example.

PRO ExWidgetWindowEvents_event, event

; Be sure to process the internal window event first.

; This handles selection, translation, rotation, etc.

w = WIDGET_EVENT(/NOWAIT)

CASE TAG_NAMES(event, /STRUCTURE_NAME) of

'WIDGET_BASE': BEGIN

; Handle base resize events. Retrieve our cached padding,

; and our new size.

WIDGET_CONTROL, event.id, GET_UVALUE=pad, TLB_GET_SIZE=newSize

wDraw = WIDGET_INFO(event.top, FIND_BY_UNAME='ex_widwin_window')

; Change the draw widget to match the new size, minus padding.

xy = newSize - pad

WIDGET_CONTROL, wDraw, $

DRAW_XSIZE=xy[0], DRAW_YSIZE=xy[1], $

SCR_XSIZE=xy[0], SCR_YSIZE=xy[1]

END

ELSE: ; do nothing

ENDCASE

END

PRO ExWidgetWindowEvents

; Create the widgets (start unmapped)

wBase = WIDGET_BASE(/COLUMN, /TLB_SIZE_EVENTS, MAP=0)

wDraw = WIDGET_WINDOW(wBase, $

UNAME='ex_widwin_window', $

MOUSE_MOTION_HANDLER='ExWidWinMouseMotionHandler', $

X_SCROLL_SIZE=640, Y_SCROLL_SIZE=512)

w1 = WIDGET_LABEL(wBase, /ALIGN_LEFT, /DYNAMIC)

WIDGET_CONTROL, wBase, /REALIZE

; Cache the padding between the base and the draw

WIDGET_CONTROL, wBase, TLB_GET_SIZE=basesize

padding = basesize - [640, 512]

WIDGET_CONTROL, wBase, SET_UVALUE=padding

; Retrieve the newly-created Window object.

WIDGET_CONTROL, wDraw, GET_VALUE=win

; Make sure this is the current window

win.Select

p = PLOT(/TEST, /CURRENT)

; Change crosshair to "manual" mode and set some properties.

c = p.CROSSHAIR

c.STYLE = 'Manual'

c.COLOR = 'Red'

c.LINESTYLE = '-'

; Cache the graphics references for the event handlers

win.uValue = {plot:p, label:w1, crosshair: c}

; Draw the widgets and start the event processing

WIDGET_CONTROL, wBase, /MAP

XMANAGER, 'ExWidgetWindowEvents', wBase, /NO_BLOCK

END