|
|
/*
* @doc INTERNAL * * @module magellan.cpp -- Handle magellan mouse. | * * For REC 2, Magellan mouse can roll scroll and mButtonDown drag scroll. * * Owner: <nl> * Jon Matousek - 1996 * * Copyright (c) 1995-1996 Microsoft Corporation. All rights reserved. */
#include "_common.h"
#if !defined(NOMAGELLAN)
#include "_edit.h"
#include "_disp.h"
#include "_magelln.h"
ASSERTDATA
/*
* CMagellan::MagellanStartMButtonScroll * * @mfunc * Called when we get an mButtonDown message. Initiates tracking * of the mouse which in turn will scroll at various speeds based * on how far the user moves the mouse from the mDownPt. * * @rdesc * TRUE if the caller should capture the mouse. * */ BOOL CMagellan::MagellanStartMButtonScroll( CTxtEdit &ed, POINT mDownPt ) { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::MagellanStartMButtonScroll");
RECT rc; BOOL fCapture = FALSE; CDisplay *pdp;
pdp = ed._pdp; if ( pdp) { pdp->GetViewRect(rc); // skip scroll bars, etc.
if ( PtInRect(&rc, mDownPt) && !_fMButtonScroll ) { fCapture = TRUE; _ID_currMDownBMP = 0; _fMButtonScroll = TRUE; // Now tracking...
_zMouseScrollStartPt = mDownPt; _fLastScrollWasRoll = FALSE; // Differentiate type.
CheckInstallMagellanTrackTimer ( ed ); // Fire up timer...
} } return fCapture; }
/*
* CMagellan::MagellanEndMButtonScroll * * @mfunc * Finished tracking mButtonDown magellan scroll, finish up state. * */ VOID CMagellan::MagellanEndMButtonScroll( CTxtEdit &ed ) { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::MagellanEndMButtonScroll");
CDisplay *pdp;
_fMButtonScroll = FALSE; CheckRemoveMagellanUpdaterTimer ( ed ); // Remove timer...
pdp = ed._pdp; if ( pdp ) { pdp->FinishSmoothVScroll(); // So smooth scroll stops.
InvertMagellanDownBMP(pdp, FALSE, NULL); // Turn it off.
}
if ( _MagellanMDownBMP ) // Release bitmap.
{ DeleteObject( _MagellanMDownBMP ); _MagellanMDownBMP = NULL; _ID_currMDownBMP = 0; } }
/*
* CMagellan::MagellanRollScroll * * @mfunc * Handle the Magellan WM_MOUSEROLLER message. This routine has global, internal * state that allows the number of lines scrolled to increase if the user continues * to roll the wheel in rapid succession. * */ VOID CMagellan::MagellanRollScroll ( CDisplay *pdp, int direction, WORD cLines, int speedNum, int speedDenom, BOOL fAdditive ) { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::MagellanRollScroll");
static DWORD lastFastRollTime; static int lastDirection; static INT cFastRolls; DWORD tickCount = GetTickCount();
if ( !_fMButtonScroll && pdp ) { // start/continue fast
if ( tickCount - lastFastRollTime < FAST_ROLL_SCROLL_TRANSITION_TICKS || ((lastDirection ^ (direction < 0 ? -1 : 1)) == 0 // or, same sign
&& _fLastScrollWasRoll // and in slow.
&& pdp->IsSmoothVScolling() )) { cFastRolls++; if ( cFastRolls > FASTER_ROLL2_COUNT ) // make faster.
cLines <<= 1; else if ( cFastRolls > FASTER_ROLL1_COUNT ) // make fast
cLines += 1; speedNum = cLines; // Cancel smooth
// effect.
lastFastRollTime = tickCount; } else { cFastRolls = 0; } // Do the scroll.
pdp->SmoothVScroll( direction, cLines, speedNum, speedDenom, TRUE);
_fLastScrollWasRoll = TRUE; lastDirection = (direction < 0) ? -1 : 1; } }
/*
* CMagellan::CheckInstallMagellanTrackTimer * * @mfunc * Install a timing task that will allow TrackUpdateMagellanMButtonDown * To be periodically called. * * @devnote * The CTxtEdit class handles all WM_TIMER dispatches, so there's glue there * to call our magellan routine. * */ VOID CMagellan::CheckInstallMagellanTrackTimer ( CTxtEdit &ed ) { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::CheckInstallMagellanTrackTimer");
ed.TxSetTimer(RETID_MAGELLANTRACK, cmsecScrollInterval); }
/*
* CMagellan::CheckRemoveMagellanUpdaterTimer * * @mfunc * Remove the timing task that dispatches to TrackUpdateMagellanMButtonDown. * */ VOID CMagellan::CheckRemoveMagellanUpdaterTimer ( CTxtEdit &ed ) { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::CheckRemoveMagellanUpdaterTimer");
ed.TxKillTimer(RETID_MAGELLANTRACK); }
/*
* CMagellan::TrackUpdateMagellanMButtonDown * * @mfunc * After mButtonDown capture, a periodic WM_TIMER calls this from OnTxTimer(). The cursor * is tracked to determine direction, speed, and in dead zone (not moving). * Movement is dispacted to CDisplay. The cursor is set to the appropriate * direction cusor, and the mButtonDown point BMP is drawn. */ VOID CMagellan::TrackUpdateMagellanMButtonDown ( CTxtEdit &ed, POINT mousePt ) { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::TrackUpdateMagellanMButtonDown");
RECT deadZone, rcClient; WORD wide, tall, xInset, yInset; POINT pt, center;
LONG xDiff, yDiff, inflate, target;
SHORT IDC_mScrollCursor, IDC_mDeadScrollCursor;
BOOL fDoHScroll, fDoVScroll; BOOL fFastScroll = FALSE;
CDisplay *pdp;
pdp = ed._pdp;
Assert ( _fMButtonScroll ); Assert ( pdp ); // Calc dead zone rect.
deadZone.top = deadZone.bottom = _zMouseScrollStartPt.y; deadZone.left = deadZone.right = _zMouseScrollStartPt.x; inflate = pdp->LYtoDY(DEAD_ZONE_TWIPS); InflateRect(&deadZone, inflate, inflate);
//
// Calculate direction to scroll and what cusor to display.
//
// By numbering a compass like the following, we can easily calc the index into
// the scrollCursors array to get the proper cursor:
//
// North = 1
// NW = 7 NE = 4
// West = 6 East = 3
// SW = 8 SE = 5
// South = 2
//
IDC_mScrollCursor = 0; IDC_mDeadScrollCursor = 0; fDoVScroll = FALSE; fDoHScroll = FALSE; if ( pdp->IsVScrollEnabled() ) // Can scroll vertically?
{ IDC_mDeadScrollCursor = 1; if ( mousePt.y < deadZone.top || mousePt.y > deadZone.bottom ) { fDoVScroll = TRUE; IDC_mScrollCursor = ( mousePt.y < _zMouseScrollStartPt.y ) ? 1 : 2; } }
// FUTURE (alexgo): allow magellan scrolling even for single line
// controls with no scrollbar. For now, however, that change is too
// risky, so we look explicity for a scrollbar.
if( pdp->IsHScrollEnabled() && ed.TxGetScrollBars() & WS_HSCROLL ) // Can scroll horizontally?
{ IDC_mDeadScrollCursor |= 2; if ( mousePt.x < deadZone.left || mousePt.x > deadZone.right ) { fDoHScroll = TRUE; IDC_mScrollCursor += ( mousePt.x < _zMouseScrollStartPt.x ) ? 6 : 3; } }
SHORT scrollCursors[] = { // Cursor for various
0, // directions.
IDC_SCROLLNORTH, IDC_SCROLLSOUTH, IDC_SCROLLEAST, IDC_SCROLLNE, IDC_SCROLLSE, IDC_SCROLLWEST, IDC_SCROLLNW, IDC_SCROLLSW }; IDC_mScrollCursor = scrollCursors[IDC_mScrollCursor];
SHORT mDownBMPs[] = { // mButtonDown origin BMPs.
0,
IDB_1DVSCROL, IDB_1DHSCROL, IDB_2DSCROL };
// BMAP-mButtonDown for UI
if ( mDownBMPs[IDC_mDeadScrollCursor] != _ID_currMDownBMP ) { if ( _MagellanMDownBMP ) // Undraw old BMP.
{ InvertMagellanDownBMP( pdp, FALSE, NULL );
DeleteObject ( _MagellanMDownBMP ); _MagellanMDownBMP = NULL; } // Draw new BMP.
_ID_currMDownBMP = mDownBMPs[IDC_mDeadScrollCursor]; _MagellanMDownBMP = LoadBitmap ( hinstRE, MAKEINTRESOURCE ( _ID_currMDownBMP ) ); InvertMagellanDownBMP( pdp, TRUE, NULL ); }
// Moved out of dead zone?
if ( fDoVScroll || fDoHScroll ) // time to scroll...
{
// Prepare data for
// scrolling routines.
ed.TxGetClientRect(&rcClient); // Get our client rect.
wide = rcClient.right - rcClient.left; tall = rcClient.bottom - rcClient.top;
// Calc center of rcClient.
center.x = rcClient.left + (wide >> 1); center.y = rcClient.top + (tall >> 1);
xInset = (wide >> 1) - 2; // Get inset to center
yInset = (tall >> 1) - 2; // about rcClient.
// Map origin to rcClient.
xDiff = mousePt.x - _zMouseScrollStartPt.x; yDiff = mousePt.y - _zMouseScrollStartPt.y; pt.x = center.x + xDiff; pt.y = center.y + yDiff; // Determine scroll speed.
target = (tall * 2) / 5; // target is 40% of screen
// height. Past that, we
// scroll page at a time.
yDiff = abs(yDiff);
if ( yDiff >= target ) // Fast scroll?
{ fFastScroll = TRUE; // Stop mutually exclusive
pdp->CheckRemoveSmoothVScroll(); // scroll type.
// Fast line scroll.
if ( fDoVScroll ) // Vertically a page at a time.
{ pdp->VScroll( ( _zMouseScrollStartPt.y - mousePt.y < 0 ) ? SB_PAGEDOWN : SB_PAGEUP, 0 ); }
if ( fDoHScroll ) { pt.y = center.y; // Prevent y dir scrolling.
// Do x dir scroll.
pdp->AutoScroll( pt, xInset, 0 ); } } else // Smooth scroll.
{ // Start, or continue
// smooth vertical scrolling.
// This formula is a bit magical, but here goes. What
// we want is the sub-linear part of an exponential function.
// In other words, smallish distances should produce pixel
// by pixel scrolling. At 40% of the screen height, however,
// we should be srolling by a page at a time (tall # of pixels).
//
// So the formula we use is (x^2)/tall, where x is yDiff scaled
// to be in units of tall (i.e. 5yDiff/2). The final 10*
// multiplier is to shift all the values leftward so we can
// do this in integer arithmetic.
LONG num = MulDiv(10*25*yDiff/4, yDiff, tall);
if( !num ) { num = 1; }
if ( fDoVScroll ) { pdp->SmoothVScroll ( _zMouseScrollStartPt.y - mousePt.y, 0, num, 10*tall, FALSE ); } // x direction scrolling?
if ( fDoHScroll ) { pt.y = center.y; // Prevent y dir scrolling.
// Do x dir scroll.
pdp->AutoScroll( pt, xInset, 0 ); } }
// notify through the messagefilter that we scrolled
if ((ed._dwEventMask & ENM_SCROLLEVENTS) && (fDoHScroll || fDoVScroll)) { MSGFILTER msgfltr; ZeroMemory(&msgfltr, sizeof(MSGFILTER)); if (fDoHScroll) { msgfltr.msg = WM_HSCROLL; msgfltr.wParam = fFastScroll ? (xDiff > 0 ? SB_PAGERIGHT: SB_PAGELEFT): (xDiff > 0 ? SB_LINERIGHT: SB_LINELEFT); } else { msgfltr.msg = WM_VSCROLL; msgfltr.wParam = fFastScroll ? (yDiff > 0 ? SB_PAGEDOWN: SB_PAGEUP): (yDiff > 0 ? SB_LINEDOWN: SB_LINEUP); }
msgfltr.lParam = NULL; // we don't check the result of this call --
// it's not a message we received and we're not going to
// process it any further
ed._phost->TxNotify(EN_MSGFILTER, &msgfltr); }
} else { // No scroll in dead zone.
SHORT noScrollCursors[] = { 0, IDC_NOSCROLLV, IDC_NOSCROLLH, IDC_NOSCROLLVH }; // Set dead-zone cursor.
IDC_mScrollCursor = noScrollCursors[IDC_mDeadScrollCursor];
pdp->FinishSmoothVScroll(); // Finish up last line.
} // Set magellan cursor.
ed._phost->TxSetCursor(IDC_mScrollCursor ? LoadCursor(hinstRE, MAKEINTRESOURCE(IDC_mScrollCursor)) : ed._hcurArrow, FALSE); }
/*
* BOOL CMagellan::InvertMagellanDownBMP * * @mfunc * Magellan mouse UI requires that the mouse down point draw * and maintain a bitmap in order to help the user control scroll speed. * * @devnote * This routine is designed to be nested. It also handles WM_PAINT updates * when the repaintDC is passed in. Because there is no support for multiple * cursors in the operating system, all WM_PAINT and ScrollWindow redraws * must temporarily turn off the BMP and then redraw it. This gives the * BMAP a flicker. * * @rdesc * TRUE if the bitmap was previously drawn. */ BOOL CMagellan::InvertMagellanDownBMP( CDisplay *pdp, BOOL fTurnOn, HDC repaintDC ) { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::InvertMagellanDownBMP");
BOOL fOldState = _fMagellanBitMapOn;
Assert (pdp);
if ( fOldState != fTurnOn ) { if ( _MagellanMDownBMP ) { BITMAP bm; HDC hdcMem, screenDC; POINT ptSize, ptOrg;
screenDC = (repaintDC != NULL) ? repaintDC : pdp->GetDC(); if ( screenDC ) { hdcMem = CreateCompatibleDC ( screenDC ); if ( hdcMem ) { SelectObject ( hdcMem, _MagellanMDownBMP ); SetMapMode ( hdcMem, GetMapMode (screenDC) );
if ( GetObjectA( _MagellanMDownBMP, sizeof(BITMAP), (LPVOID) &bm) ) { ptSize.x = bm.bmWidth; ptSize.y = bm.bmHeight; DPtoLP ( screenDC, &ptSize, 1 ); ptOrg.x = 0; ptOrg.y = 0; DPtoLP( hdcMem, &ptOrg, 1 );
BitBlt( screenDC, _zMouseScrollStartPt.x - (ptSize.x >> 1) - 1, _zMouseScrollStartPt.y - (ptSize.y >> 1) + 1, ptSize.x, ptSize.y, hdcMem, ptOrg.x, ptOrg.y, 0x00990066 /* NOTXOR */ );
_fMagellanBitMapOn = !fOldState; } DeleteDC( hdcMem ); } if ( repaintDC == NULL ) pdp->ReleaseDC( screenDC ); } } }
return fOldState; }
////////////////////////// CMagellanBMPStateWrap class.
/*
* CMagellanBMPStateWrap:: CMagellanBMPStateWrap * * @mfunc * Handles the state of whether to redraw the Magellan BMP as well as * repaints due to WM_PAINT. * * @devnote * This class is akin to smart pointer wrapper class idioms, in that * no matter how a routine exits the correct state of whether the * BMP is drawn will be maintined. */ CMagellanBMPStateWrap:: CMagellanBMPStateWrap(CTxtEdit &ed, HDC repaintDC) : _ed(ed), _repaintDC(repaintDC) { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellanBMPStateWrap:: CMagellanBMPStateWrap");
BOOL fRepaint;
fRepaint = repaintDC != NULL && _ed.mouse._fMagellanBitMapOn != 0; _fMagellanState = fRepaint || _ed.mouse.InvertMagellanDownBMP(_ed._pdp, FALSE, NULL); _ed.mouse._fMagellanBitMapOn = FALSE; }
CMagellanBMPStateWrap::~CMagellanBMPStateWrap() { TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellanBMPStateWrap::~CMagellanBMPStateWrap");
_ed.mouse.InvertMagellanDownBMP(_ed._pdp, _fMagellanState, _repaintDC); }
#endif // !defined(NOMAGELLAN)
|