You can create a custom event handler to handle events in a graphics WINDOW or a WIDGET_WINDOW. The event handler class must be a subclass of GraphicsEventAdapter. Your subclass can then override any of the following methods: ::MouseDown, ::MouseUp, ::MouseMotion, ::MouseWheel, ::KeyHandler, and ::SelectChange.

Each function method 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.

Syntax


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

Result = obj.MouseDown(Window, X, Y, Button, KeyMods, Clicks)

Result = obj.MouseMotion(Window, X, Y, KeyMods)

Result = obj.MouseUp(Window, X, Y, Button)

Result = obj.MouseWheel(Window, X, Y, Delta, KeyMods)

Result = obj.KeyHandler(Window, IsASCII, Character, KeyValue, X, Y, Press, Release, KeyMods)

Result = obj.SelectChange(Window, Graphic, Mode, WasSelected)

Arguments


Window

The object reference of the window in which the 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

IsASCII

A scalar byte value that indicates whether the keyboard event corresponds to an ASCII character. If IsASCII is non-zero, the Character argument will be set to a byte value corresponding to the character of the pressed key. If IsASCII is zero, the KeySymbol argument will be set to a numeric value indicating the key that was pressed.

Character

If IsASCII is non-zero, this argument is set to a byte value corresponding to the ASCII character of the key that was pressed. Otherwise, this argument is set to zero.

KeyValue

If IsASCII is zero, this argument is set to a value that indicates the key that was pressed. Otherwise, this argument is set to zero. Possible values are:

Value

Key

1

Shift

2

Control

3

Caps lock

4

Alt

5

Left

6

Right

7

Up

8

Down

9

Page up

10

Page down

11

Home

12

End

Note: On East Asian (Chinese, Japanese, Korean) localized Windows operating systems with an Asian language pack installed, characters entered in the Windows Input Method Manager (IMM)composition window are returned in the the KeyValue argument as unsigned integers representing a Wide character (Unicode value). The I18N_WIDECHARTOMULTIBYTE routine can convert these characters to multibyte strings. For more information, see Internationalizing Code.

Press

The value indicating that this event represents a key press. This argument is non-zero if the event is the result of pressing the key.

Release

The value indicating that this event represents a key release. This argument is non-zero if the event is the result of releasing the key.

Graphic

The graphic to be selected or unselected.

Mode

A value representing the mode that was used for the current selection. Possible values are:

Value

Selection Type

0

Unselect

1

Select

2

Toggled selection

3

Additive

WasSelected

A value indicating if the graphic item was already selected prior to this event. A value of 1 indicates an item was previously selected; 0 if not selected.

GraphicsEventAdapter Class


The GraphicsEventAdapter class provides a default implementation for all of the event handler methods. The default implementation returns a value of 1 for each of the methods. For convenience, you should create your event handler as a subclass of GraphicsEventAdapter and only override the methods that you need.

The GraphicsEventAdapter class is written in the IDL language. Its source code can be found in the file graphicseventadapter__define.pro in the lib/graphics subdirectory of the IDL distribution.

Example Using WINDOW


In the following example code, the RBBox class create a "rubber-band" box. The RBBox::MouseDown method creates a polygon and caches the start point of a drag operation. In the RBBox::MouseMotion method the polygon is updated with the current location of the cursor. In the RBBox::MouseUp method, 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 RBBox::MouseDown, oWin, $
  x, y, iButton, KeyMods, nClicks
  if (~ISA(self.poly)) then begin
  ; Add a hidden polygon for the rubber-band box.
  self.poly = POLYGON([0,0,0],[0,0,0], /DEVICE, $
  LINESTYLE='--', /HIDE, $
  FILL_TRANSPARENCY=90, FILL_BACKGROUND = 1, FILL_COLOR='red')
  endif
  self.x0 = x
  self.y0 = y
  self.buttonDown = 1
  self.poly.HIDE = 0
  self.poly.SetData, [0,0,0], [0,0,0]
  self.poly.LINESTYLE='--'
  RETURN, 0 ; Skip default event handling
END
 
FUNCTION RBBox::MouseMotion, oWin, x, y, KeyMods
  IF self.buttonDown then begin
     x0 = self.x0
     y0 = self.y0
     xVector=[x0,x0,x,x,x0]
     yVector=[y0,y,y,y0,y0]
     xy = self.poly.ConvertCoord(xVector, yVector, /DEVICE, /TO_NORMAL)
     self.poly.SetData, REFORM(xy[0,*]), REFORM(xy[1,*])
  ENDIF
  RETURN, 0 ; Skip default event handling
END
 
FUNCTION RBBox::MouseUp, oWin, x, y, iButton
  IF (~self.buttonDown) THEN RETURN, 0
  x0 = self.x0
  y0 = self.y0
  xVector=[x0,x0,x,x,x0]
  yVector=[y0,y,y,y0,y0]
  xy = self.poly.ConvertCoord(xVector, yVector, /DEVICE, /TO_NORMAL)
  self.poly.SetData, REFORM(xy[0,*]), REFORM(xy[1,*])
  self.poly.LINESTYLE='-'
  self.buttonDown=0
  ; 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 self.poly then vis.Select, /ADD
  ENDFOREACH
  RETURN, 0 ; Skip default event handling
END
 
PRO RBBox__define
  ; We must subclass from GraphicsEventAdapter.
  void = {RBBox, inherits GraphicsEventAdapter, $
  X0: 0, Y0:0, BUTTONDOWN:0, POLY:OBJ_NEW()}
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')
  p.window.EVENT_HANDLER=Obj_New('RBBox')
END

Example Using WIDGET_WINDOW


In this example, only the MouseMotion event method 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. Save all of the code in a file, exwidgetwindowevents.pro, and then run the ExWidgetWindowEvents procedure.

 
FUNCTION ExWidWin::Init, plot, label, crosshair
  self.plot = plot
  self.label = label
  self.crosshair = crosshair
  return, 1
END
 
FUNCTION ExWidWin::MouseMotion, oWin, x, y, KeyMods
  ; Convert from screen coordinates to data coordinates.
  xy = self.plot.ConvertCoord(x, y, /DEVICE, /TO_DATA)
  ; "Snap" the location to the nearest plot point.
  xy = self.plot.GetValueAtLocation(xy[0])
  ; Update the crosshair location and the label
  self.crosshair.LOCATION = xy
  probe = STRING(xy[0:1], FORMAT='(2F9.2)')
  WIDGET_CONTROL, self.label, SET_VALUE=probe
  return, 1 ; Perform default event handling
END
 
PRO ExWidWin__define
  void = {ExWidWin, inherits GraphicsEventAdapter, $
  plot: OBJ_NEW(), label: 0L, crosshair: OBJ_NEW()}
END
 
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', $
     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
  handler = OBJ_NEW('ExWidWin', p, w1, c)
  win.EVENT_HANDLER = handler
   
  ; Draw the widgets and start the event processing
  WIDGET_CONTROL, wBase, /MAP
  XMANAGER, 'ExWidgetWindowEvents', wBase, /NO_BLOCK
END