/*! When dealing with mouse coordinates, we need to convert them to image coordinates, and so must consider the following things: 1) The mouse has the origin at top-left, but in all calculations we assume an origin at bottom-left, so when reading the mouse, we do mousePt.Set(Fl::event_x(), h() - Fl::event_y()). 2) In general, the relation between the (bottom-left-origin) mouse point $p_m$ and its corresponding image point $p_i$ is given by: \[ p_m = p_i * S + T \], where S is the scaling and T is the translation. Then, given a mouse point we get the image point by \[ p_i = (p_m - T) / S \] 3) When zooming, we want to end up at the same image point,. Then, given a requested new scaling factor, $S'$, we must find find the appropriate translation $T'$ for it that will make the mouse point, $p_m$, correspond to the same image point $p_i$. \[ p_m = p_i * S' + T' \], which means that from (2), we can get \[ T' = p_m - p_i * S' \]. We could replace $p_i$ and simplify things a bit, but that's not really necessary. 4) In the case of the y coordinate, we have it reflexed (multiplied by -1) and translated by the window hight, h, to make it agree with the top-left origin of the displayed images. So, thes coordinate needs some special treatment. Then, the image y-coordinate is given by \[ p_i = (p_m - T - h) / -S \], and the new translation dy is given by \[ dy = p_m - p_i * -S' - h \]. Agaim, some simplifycation could me made here, but that's not necessary, and the point realtion are seen clearer this way. */ int ImageView::handle(int eventId) { switch (eventId) { // case FL_KEYDOWN: // does not work case FL_KEYUP: OnKeyboard(Fl::event_key()); return 1; case FL_MOUSEWHEEL: OnZoom(Fl::event_dy()); return 1; case FL_PUSH: OnMousePush(); return 1; case FL_DRAG: OnMouseDrag(); return 1; //case FL_ENTER: // m_savedTitle = parent()->label(); // return 1; case FL_LEAVE: if (m_curImgInfo != parent()->label()) { parent()->label(m_curImgInfo.c_str()); parent()->redraw(); } return 1; case FL_MOVE: OnMouseMove(); return 1; } return BaseImageView::handle(eventId); }
LRESULT CALLBACK Widget::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { MsgProcResult result; switch(msg) { case WM_PAINT: { PAINTSTRUCT ps; HDC hDC; if(wParam == 0) hDC = BeginPaint(hWnd, &ps); else hDC = (HDC)wParam; Gdiplus::Graphics g(hDC); OnPaint(&g, ps.rcPaint); if(wParam == 0) EndPaint(hWnd, &ps); } break; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: { mIsMouseDown = true; SetCapture(hWnd); mMouseLastPos = POINT{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; MouseEvent ev(this, mMouseLastPos, 1, wParam, msg == WM_LBUTTONDOWN ? MouseButton::Left : msg == WM_RBUTTONDOWN ? MouseButton::Right : msg == WM_MBUTTONDOWN ? MouseButton::Middle : MouseButton::None); SignalMouseDown.emit(ev); OnMouseDown(ev); } break; case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: { mIsMouseDown = false; ReleaseCapture(); mMouseLastPos = POINT{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; MouseEvent ev(this, mMouseLastPos, 1, wParam, msg == WM_LBUTTONUP ? MouseButton::Left : msg == WM_RBUTTONUP ? MouseButton::Right : msg == WM_MBUTTONUP ? MouseButton::Middle : MouseButton::None); SignalMouseUp.emit(ev); OnMouseUp(ev); if(!mHasDragged) { SignalMouseClick.emit(ev); OnMouseClick(ev); } else mHasDragged = false; } break; case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: { MouseEvent ev(this, POINT{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}, 2, wParam, msg == WM_LBUTTONDBLCLK ? MouseButton::Left : msg == WM_MBUTTONDBLCLK ? MouseButton::Right : msg == WM_RBUTTONDBLCLK ? MouseButton::Middle : MouseButton::None); SignalMouseDoubleClick.emit(ev); OnMouseDoubleClick(ev); } break; case WM_MOUSEMOVE: { POINT mouseNewPos = POINT{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; MouseEvent ev(this, mouseNewPos, 0, wParam, MouseButton::None, mMouseLastPos); mMouseLastPos = mouseNewPos; if(mIsMouseDown) { mHasDragged = true; SignalMouseDrag.emit(ev); OnMouseDrag(ev); } else { SignalMouseMove.emit(ev); OnMouseMove(ev); } } break; case WM_MOUSEWHEEL: { POINT pt = POINT{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; ScreenToClient(mhWnd, &pt); MouseEvent ev(this, pt, 0, GET_KEYSTATE_WPARAM(wParam), MouseButton::None, mMouseLastPos, GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA); SignalMouseWheel.emit(ev); OnMouseWheel(ev); } break; case WM_SIZE: { int width = LOWORD(lParam); int height = HIWORD(lParam); SignalResize.emit(width, height); if(mLayout) mLayout->Apply(this); OnResize(width, height); } break; case WM_COMMAND: { HWND hCtrl = reinterpret_cast<HWND>(lParam); if(hCtrl != nullptr) { Widget* child = Widget::FromHandle(hCtrl); if(child != nullptr) child->OnCommand(HIWORD(wParam)); } } break; case WM_NOTIFY: { LPNMHDR lpnmhdr = reinterpret_cast<LPNMHDR>(lParam); Widget* child = Widget::FromHandle(lpnmhdr->hwndFrom); if(child != nullptr) result = child->OnNotify(lpnmhdr); } break; } if(result.ReturnDefault()) return CallDefaultProc(hWnd, msg, wParam, lParam); else return result.Value(); }