|
Broadly speaking, there are two categories of mouse input. Basic mouse events are sent using wxMouseEvent and are passed uninterpreted to your handler function. Commands associated with controls, on the other hand, are often the result of interpreting a mouse (or other) event as a particular command. For example, when you add EVT_BUTTON to an event table, you are intercepting a wxCommandEvent that was generated by the wxButton. Internally, the button is intercepting EVT_LEFT_DOWN and generating the command event as a result. (Of course, on most platforms, wxButton is implemented natively and doesn't use low-level wxWidgets event handling, but it's true of custom classes.) Because we've already seen examples of handling command events, we will concentrate on basic mouse events. You can intercept button up, button down, and double-click events for left, middle, and right mouse buttons. You can intercept motion events, whether the mouse is moving with or without buttons pressed. You can intercept events telling you that the mouse is entering or leaving the window. Finally, you can intercept scroll wheel events if the hardware provides a scroll wheel. When you receive a mouse event, you can also check the state of the mouse buttons, and the pressed state of the modifier keys (Shift, Alt, Control, and Meta). You can also retrieve the current mouse position relative to the top-left corner of the window's client area. Table 6-1 lists the event table macros you can use. wxMouseEvent does not propagate to parents of the originating window, so to handle these events, you must derive from a window class or derive from wxEvtHandler and plug the object into the window with SetEventHandler or PushEventHandler. Alternatively, you can use dynamic event handling with Connect.
Handling Button and Motion EventsThese are the main mouse event functions that you can use within your event handler when handling mouse button and motion events. To test whether a modifier key is down at the time of generating the event, use AltDown, MetaDown, ControlDown, or ShiftDown. Use CmdDown if you want to test for either the Meta key (on Mac OS X) or the Control key (other platforms). See "Modifier Key Variations" later in the chapter for more on this. To determine which mouse button is currently pressed, use LeftIsDown, MiddleIsDown, and RightIsDown. You can also test whether a button is pressed by passing wxMOUSE_BTN_LEFT, wxMOUSE_BTN_MIDDLE, wxMOUSE_BTN_RIGHT or wxMOUSE_ BTN_ANY to Button. Note that these test whether a button is down at the time of the mouse event, rather than whether the button changed state. On Mac OS X, the Command key translates to Meta, and the Option key is Alt. Because the Mac is often configured with only one mouse button, the user holds down the Control key while clicking to generate a right-click event. This means that there is no such thing as Control-Right Click on Mac unless you have an external mouse with two or three buttons. You can test for the type of mouse event with Dragging (the mouse is moving with a button pressed down), Moving (no button is currently pressed), Entering, Leaving, ButtonDown, ButtonUp, ButtonDClick, LeftClick, LeftDClick, LeftUp, RightClick, RightDClick, RightUp, ButtonUp, and IsButton. Retrieve the mouse position in device units (relative to the client window's top-left corner) with GetPosition or GetX and GetY. You can get the position in logical units by passing a device context to GetLogicalPosition. Here's an example of mouse handling for a simple doodling application. BEGIN_EVENT_TABLE(DoodleCanvas, wxWindow) EVT_MOUSE_EVENTS(DoodleCanvas::OnMouseEvent) END_EVENT_TABLE() void DoodleCanvas::OnMouseEvent(wxMouseEvent& event) { static DoodleSegment *s_currentSegment = NULL; wxPoint pt(event.GetPosition()); if (s_currentSegment && event.LeftUp()) { // End the segment on mouse left up if (s_currentSegment->GetLines().GetCount() == 0) { // Empty segment: delete it delete s_currentSegment; s_currentSegment = (DoodleSegment *) NULL; } else { // We've got a valid segment, so store it DrawingDocument *doc = GetDocument(); doc->GetCommandProcessor()->Submit( new DrawingCommand(wxT("Add Segment"), DOODLE_ADD, doc, s_currentSegment)); doc->Modify(true); s_currentSegment = NULL; } } else if (m_lastX > -1 && m_lastY > -1 && event.Dragging()) { // We're dragging: append a line to the current segment if (!s_currentSegment) s_currentSegment = new DoodleSegment; DoodleLine *newLine = new DoodleLine(m_lastX, m_lastY, pt.x, pt.y); s_currentSegment->GetLines().Append(newLine); wxClientDC dc(this); DoPrepareDC(dc); dc.SetPen(*wxBLACK_PEN); dc.DrawLine( m_lastX, m_lastY, pt.x, pt.y); } m_lastX = pt.x; m_lastY = pt.y; } In this application, line segments are stored in a document. While the user drags using the left button, the function adds lines to the current segment and also draws the lines. When the user releases the left mouse button, the current segment is submitted to the document using the command processor (part of the document-view framework), which allows undo/redo to be implemented. In the application's OnPaint handler (not shown), all the document's line segments are drawn. For a complete doodling program with undo/redo, see Chapter 19, "Working with Documents and Views." A more realistic application would capture the mouse on left button down and release it on left button up so that when dragging outside the window, the window would still receive events. Handling Mouse Wheel EventsWhen you get a mouse wheel event, you retrieve the positive or negative rotation amount with GetWheelRotation. Divide this value by the value returned by GetWheelDelta to get the number of lines that this rotation represents. Most devices generate one event per delta, but future devices may send events more frequently, so you need to be able to accumulate the amount of rotation and only take action when rotation equivalent to a full line has been received. Alternatively, you may be able to scroll by a fraction of a line. You should take into account the value returned by GetLinesPerAction, as configured by the user via the system control panel, and multiply by this amount to scroll the desired number of actual lines. In fact, the mouse may be configured to scroll by a page at a time. In this case, you need to call IsPageScroll, and if this returns true, scroll by a page. To illustrate, here's how wxScrolledWindow implements default scroll wheel processing. The variable m_wheelRotation accumulates the rotation, and action is only taken if the number of lines is non-zero. void wxScrollHelper::HandleOnMouseWheel(wxMouseEvent& event) { m_wheelRotation += event.GetWheelRotation(); int lines = m_wheelRotation / event.GetWheelDelta(); m_wheelRotation -= lines * event.GetWheelDelta(); if (lines != 0) { wxScrollWinEvent newEvent; newEvent.SetPosition(0); newEvent.SetOrientation(wxVERTICAL); newEvent.m_eventObject = m_win; if (event.IsPageScroll()) { if (lines > 0) newEvent.m_eventType = wxEVT_SCROLLWIN_PAGEUP; else newEvent.m_eventType = wxEVT_SCROLLWIN_PAGEDOWN; m_win->GetEventHandler()->ProcessEvent(newEvent); } else { lines *= event.GetLinesPerAction(); if (lines > 0) newEvent.m_eventType = wxEVT_SCROLLWIN_LINEUP; else newEvent.m_eventType = wxEVT_SCROLLWIN_LINEDOWN; int times = abs(lines); for (; times > 0; times) m_win->GetEventHandler()->ProcessEvent(newEvent); } } } |
|