Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

514 lines
14 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module magellan.cpp -- Handle magellan mouse. |
  5. *
  6. * Magellan mouse can roll scroll and mButtonDown drag scroll.
  7. *
  8. * History: <nl>
  9. * Jon Matousek - 1996
  10. * 4/1/2000 KeithCu - Cleanup, coding convention, support for textflows
  11. *
  12. * Copyright (c) 1995-1996 Microsoft Corporation. All rights reserved.
  13. */
  14. #include "_common.h"
  15. #if !defined(NOMAGELLAN)
  16. #include "_edit.h"
  17. #include "_disp.h"
  18. #include "_magelln.h"
  19. ASSERTDATA
  20. const SHORT scrollCursors[] =
  21. { // Cursor for various
  22. 0, // directions.
  23. IDC_SCROLLNORTH,
  24. IDC_SCROLLSOUTH,
  25. IDC_SCROLLEAST,
  26. IDC_SCROLLNE,
  27. IDC_SCROLLSE,
  28. IDC_SCROLLWEST,
  29. IDC_SCROLLNW,
  30. IDC_SCROLLSW
  31. };
  32. const SHORT mDownBMPs[] =
  33. { // mButtonDown origin BMPs.
  34. 0,
  35. IDB_1DVSCROL,
  36. IDB_1DHSCROL,
  37. IDB_2DSCROL
  38. };
  39. const SHORT noScrollCursors[] =
  40. {
  41. 0,
  42. IDC_NOSCROLLV,
  43. IDC_NOSCROLLH,
  44. IDC_NOSCROLLVH
  45. };
  46. //Convert the compass from logical to visual
  47. const BYTE mapCursorTflowSW[] =
  48. {
  49. 0, 3, 6, 2, 5, 8, 1, 4, 7
  50. };
  51. const BYTE mapCursorTflowWN[] =
  52. {
  53. 0, 2, 1, 6, 8, 7, 3, 5, 4
  54. };
  55. const BYTE mapCursorTflowNE[] =
  56. {
  57. 0, 6, 3, 1, 7, 4, 2, 8, 5
  58. };
  59. BOOL CMagellan::ContinueMButtonScroll(CTxtEdit *ped, INT x, INT y)
  60. {
  61. POINTUV ptuv;
  62. POINT ptxy = {x,y};
  63. ped->_pdp->PointuvFromPoint(ptuv, ptxy);
  64. return (_ptStart.u == ptuv.u && _ptStart.v == ptuv.v);
  65. }
  66. /*
  67. * CMagellan::MagellanStartMButtonScroll
  68. *
  69. * @mfunc
  70. * Called when we get an mButtonDown message. Initiates tracking
  71. * of the mouse which in turn will scroll at various speeds based
  72. * on how far the user moves the mouse from the mDownPt.
  73. *
  74. * @rdesc
  75. * TRUE if the caller should capture the mouse.
  76. *
  77. */
  78. BOOL CMagellan::MagellanStartMButtonScroll(CTxtEdit &ed, POINT ptxy)
  79. {
  80. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::MagellanStartMButtonScroll");
  81. RECTUV rc;
  82. BOOL fCapture = FALSE;
  83. CDisplay *pdp = ed._pdp;
  84. POINTUV pt;
  85. pdp->PointuvFromPoint(pt, ptxy);
  86. pdp->GetViewRect(rc); // skip scroll bars, etc.
  87. if (PtInRect(&rc, pt) && !_fMButtonScroll)
  88. {
  89. fCapture = TRUE;
  90. _ID_currMDownBMP = 0;
  91. _fMButtonScroll = TRUE; // Now tracking...
  92. _ptStart = pt;
  93. _fLastScrollWasRoll = FALSE; // Differentiate type.
  94. CheckInstallMagellanTrackTimer(ed); // Fire up timer...
  95. }
  96. return fCapture;
  97. }
  98. /*
  99. * CMagellan::MagellanEndMButtonScroll
  100. *
  101. * @mfunc
  102. * Finished tracking mButtonDown magellan scroll, finish up state.
  103. *
  104. */
  105. VOID CMagellan::MagellanEndMButtonScroll(CTxtEdit &ed)
  106. {
  107. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::MagellanEndMButtonScroll");
  108. CDisplay *pdp = ed._pdp;
  109. _fMButtonScroll = FALSE;
  110. CheckRemoveMagellanUpdaterTimer(ed); // Remove timer...
  111. pdp->FinishSmoothVScroll(); // So smooth scroll stops.
  112. InvertMagellanDownBMP(pdp, FALSE, NULL); // Turn it off.
  113. if (_MagellanMDownBMP) // Release bitmap.
  114. {
  115. DeleteObject(_MagellanMDownBMP);
  116. _MagellanMDownBMP = NULL;
  117. _ID_currMDownBMP = 0;
  118. }
  119. }
  120. /*
  121. * CMagellan::MagellanRollScroll
  122. *
  123. * @mfunc
  124. * Handle the Magellan WM_MOUSEROLLER message. This routine has global, internal
  125. * state that allows the number of lines scrolled to increase if the user continues
  126. * to roll the wheel in rapid succession.
  127. *
  128. */
  129. VOID CMagellan::MagellanRollScroll (CDisplay *pdp, int direction, WORD cLines,
  130. int speedNum, int speedDenom, BOOL fAdditive)
  131. {
  132. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::MagellanRollScroll");
  133. static DWORD lastFastRollTime;
  134. static int lastDirection;
  135. static INT cFastRolls;
  136. DWORD tickCount = GetTickCount();
  137. if (!_fMButtonScroll)
  138. {
  139. // start/continue fast
  140. if (tickCount - lastFastRollTime < FAST_ROLL_SCROLL_TRANSITION_TICKS
  141. || ((lastDirection ^ (direction < 0 ? -1 : 1)) == 0 // or, same sign
  142. && _fLastScrollWasRoll // and in slow.
  143. && pdp->IsSmoothVScolling()))
  144. {
  145. cFastRolls++;
  146. if (cFastRolls > FASTER_ROLL2_COUNT) // make faster.
  147. cLines <<= 1;
  148. else if (cFastRolls > FASTER_ROLL1_COUNT) // make fast
  149. cLines += 1;
  150. speedNum = cLines; // Cancel smooth
  151. // effect.
  152. lastFastRollTime = tickCount;
  153. }
  154. else
  155. cFastRolls = 0;
  156. pdp->SmoothVScroll(direction, cLines, speedNum, speedDenom, TRUE);
  157. _fLastScrollWasRoll = TRUE;
  158. lastDirection = (direction < 0) ? -1 : 1;
  159. }
  160. }
  161. /*
  162. * CMagellan::CheckInstallMagellanTrackTimer
  163. *
  164. * @mfunc
  165. * Install a timing task that will allow TrackUpdateMagellanMButtonDown
  166. * To be periodically called.
  167. *
  168. * @devnote
  169. * The CTxtEdit class handles all WM_TIMER dispatches, so there's glue there
  170. * to call our magellan routine.
  171. *
  172. */
  173. VOID CMagellan::CheckInstallMagellanTrackTimer (CTxtEdit &ed)
  174. {
  175. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::CheckInstallMagellanTrackTimer");
  176. ed.TxSetTimer(RETID_MAGELLANTRACK, cmsecScrollInterval);
  177. }
  178. /*
  179. * CMagellan::CheckRemoveMagellanUpdaterTimer
  180. *
  181. * @mfunc
  182. * Remove the timing task that dispatches to TrackUpdateMagellanMButtonDown.
  183. *
  184. */
  185. VOID CMagellan::CheckRemoveMagellanUpdaterTimer (CTxtEdit &ed)
  186. {
  187. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::CheckRemoveMagellanUpdaterTimer");
  188. ed.TxKillTimer(RETID_MAGELLANTRACK);
  189. }
  190. /*
  191. * CMagellan::TrackUpdateMagellanMButtonDown
  192. *
  193. * @mfunc
  194. * After mButtonDown capture, a periodic WM_TIMER calls this from OnTxTimer(). The cursor
  195. * is tracked to determine direction, speed, and in dead zone (not moving).
  196. * Movement is dispacted to CDisplay. The cursor is set to the appropriate
  197. * direction cusor, and the mButtonDown point BMP is drawn.
  198. */
  199. VOID CMagellan::TrackUpdateMagellanMButtonDown (CTxtEdit &ed, POINT ptxy)
  200. {
  201. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::TrackUpdateMagellanMButtonDown");
  202. RECTUV deadZone;
  203. POINTUV pt;
  204. LONG inflate;
  205. SHORT IDC_mScrollCursor = 0, IDC_mDeadScrollCursor = 0;
  206. BOOL fDoUScroll = FALSE, fDoVScroll = FALSE;
  207. CDisplay *pdp = ed._pdp;
  208. pdp->PointuvFromPoint(pt, ptxy);
  209. Assert (_fMButtonScroll);
  210. deadZone.top = deadZone.bottom = _ptStart.v;
  211. deadZone.left = deadZone.right = _ptStart.u;
  212. inflate = pdp->LYtoDY(DEAD_ZONE_TWIPS);
  213. InflateRect(&deadZone, inflate, inflate);
  214. //
  215. // Calculate direction to scroll and what cusor to display.
  216. //
  217. // By numbering a compass like the following, we can easily calc the index into
  218. // the scrollCursors array to get the proper cursor:
  219. //
  220. // North = 1
  221. // NW = 7 NE = 4
  222. // West = 6 East = 3
  223. // SW = 8 SE = 5
  224. // South = 2
  225. //
  226. if (pdp->IsVScrollEnabled()) // Can scroll vertically?
  227. {
  228. IDC_mDeadScrollCursor = 1;
  229. if (pt.v < deadZone.top || pt.v > deadZone.bottom)
  230. {
  231. fDoVScroll = TRUE;
  232. IDC_mScrollCursor = (pt.v < _ptStart.v) ? 1 : 2;
  233. }
  234. }
  235. // FUTURE (alexgo): allow magellan scrolling when no scrollbar?
  236. if(pdp->IsUScrollEnabled() && ed.TxGetScrollBars() & WS_HSCROLL) // Can scroll horizontally?
  237. {
  238. IDC_mDeadScrollCursor |= 2;
  239. if (pt.u < deadZone.left || pt.u > deadZone.right)
  240. {
  241. fDoUScroll = TRUE;
  242. IDC_mScrollCursor += (pt.u < _ptStart.u) ? 6 : 3;
  243. }
  244. }
  245. //Convert cursors from logical to visual
  246. switch(pdp->GetTflow())
  247. {
  248. case tflowSW:
  249. IDC_mScrollCursor = mapCursorTflowSW[IDC_mScrollCursor];
  250. break;
  251. case tflowWN:
  252. IDC_mScrollCursor = mapCursorTflowWN[IDC_mScrollCursor];
  253. break;
  254. case tflowNE:
  255. IDC_mScrollCursor = mapCursorTflowNE[IDC_mScrollCursor];
  256. break;
  257. }
  258. if (IsUVerticalTflow(pdp->GetTflow()))
  259. {
  260. if (IDC_mDeadScrollCursor == 2)
  261. IDC_mDeadScrollCursor = 1;
  262. else if (IDC_mDeadScrollCursor == 1)
  263. IDC_mDeadScrollCursor = 2;
  264. }
  265. //Review (keithcu) A little goofy...
  266. IDC_mScrollCursor = scrollCursors[IDC_mScrollCursor];
  267. if (mDownBMPs[IDC_mDeadScrollCursor] != _ID_currMDownBMP)
  268. {
  269. if (_MagellanMDownBMP) // Undraw old BMP.
  270. {
  271. InvertMagellanDownBMP(pdp, FALSE, NULL);
  272. DeleteObject (_MagellanMDownBMP);
  273. _MagellanMDownBMP = NULL;
  274. }
  275. // Draw new BMP.
  276. _ID_currMDownBMP = mDownBMPs[IDC_mDeadScrollCursor];
  277. _MagellanMDownBMP = LoadBitmap (hinstRE, MAKEINTRESOURCE (_ID_currMDownBMP));
  278. InvertMagellanDownBMP(pdp, TRUE, NULL);
  279. }
  280. // Moved out of dead zone?
  281. if (fDoVScroll || fDoUScroll) // time to scroll...
  282. {
  283. RECTUV rcClient;
  284. POINTUV ptCenter, ptAuto;
  285. ed.TxGetClientRect(&rcClient); // Get our client rect.
  286. LONG dupClient = rcClient.right - rcClient.left;
  287. LONG dvpClient = rcClient.bottom - rcClient.top;
  288. ptCenter.u = rcClient.left + (dupClient >> 1);
  289. ptCenter.v = rcClient.top + (dvpClient >> 1);
  290. LONG uInset = (dupClient >> 1) - 2; // Get inset to center
  291. // Map origin to rcClient.
  292. LONG dup = pt.u - _ptStart.u;
  293. LONG dvp = pt.v - _ptStart.v;
  294. ptAuto.u = ptCenter.u + dup;
  295. ptAuto.v = ptCenter.v + dvp;
  296. // This formula is a bit magical, but here goes. What
  297. // we want is the sub-linear part of an exponential function.
  298. // In other words, smallish distances should produce pixel
  299. // by pixel scrolling.
  300. //
  301. // So the formula we use is (x^2) / dvpClient, where x is dvpClient scaled
  302. // to be in units of dvpClient (i.e. 5yDiff/2). The final 10*
  303. // multiplier is to shift all the values leftward so we can
  304. // do this in integer arithmetic.
  305. LONG num = MulDiv(10 * 25 * dvp, dvp, dvpClient * 4);
  306. if(!num)
  307. num = 1;
  308. if (fDoVScroll)
  309. pdp->SmoothVScroll(_ptStart.v - pt.v, 0, num, 10 * dvpClient, FALSE);
  310. if (fDoUScroll) // u direction scrolling?
  311. {
  312. ptAuto.v = ptCenter.v; // Prevent v dir scrolling.
  313. // Do u dir scroll.
  314. pdp->AutoScroll(ptAuto, uInset, 0);
  315. }
  316. // notify through the messagefilter that we scrolled
  317. if ((ed._dwEventMask & ENM_SCROLLEVENTS) && (fDoUScroll || fDoVScroll))
  318. {
  319. MSGFILTER msgfltr;
  320. ZeroMemory(&msgfltr, sizeof(MSGFILTER));
  321. if (fDoUScroll)
  322. {
  323. msgfltr.msg = WM_HSCROLL;
  324. msgfltr.wParam = (dup > 0 ? SB_LINERIGHT : SB_LINELEFT);
  325. ed._phost->TxNotify(EN_MSGFILTER, &msgfltr);
  326. }
  327. if (fDoVScroll)
  328. {
  329. msgfltr.msg = WM_VSCROLL;
  330. msgfltr.wParam = (dvp > 0 ? SB_LINEDOWN : SB_LINEUP);
  331. ed._phost->TxNotify(EN_MSGFILTER, &msgfltr);
  332. }
  333. }
  334. }
  335. else // No scroll in dead zone.
  336. {
  337. IDC_mScrollCursor = noScrollCursors[IDC_mDeadScrollCursor];
  338. pdp->FinishSmoothVScroll(); // Finish up last line.
  339. }
  340. ed._phost->TxSetCursor(IDC_mScrollCursor ?
  341. LoadCursor(hinstRE, MAKEINTRESOURCE(IDC_mScrollCursor)) : ed._hcurArrow, FALSE);
  342. }
  343. /*
  344. * BOOL CMagellan::InvertMagellanDownBMP
  345. *
  346. * @mfunc
  347. * Magellan mouse UI requires that the mouse down point draw
  348. * and maintain a bitmap in order to help the user control scroll speed.
  349. *
  350. * @devnote
  351. * This routine is designed to be nested. It also handles WM_PAINT updates
  352. * when the repaintDC is passed in. Because there is no support for multiple
  353. * cursors in the operating system, all WM_PAINT and ScrollWindow redraws
  354. * must temporarily turn off the BMP and then redraw it. This gives the
  355. * BMAP a flicker.
  356. *
  357. * @rdesc
  358. * TRUE if the bitmap was previously drawn.
  359. */
  360. BOOL CMagellan::InvertMagellanDownBMP(CDisplay *pdp, BOOL fTurnOn, HDC repaintDC)
  361. {
  362. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellan::InvertMagellanDownBMP");
  363. BOOL fOldState = _fMagellanBitMapOn;
  364. Assert (pdp);
  365. if (fOldState != fTurnOn)
  366. {
  367. if (_MagellanMDownBMP)
  368. {
  369. BITMAP bm;
  370. HDC hdcMem, screenDC;
  371. POINT ptSize, ptOrg;
  372. screenDC = (repaintDC != NULL) ? repaintDC : pdp->GetDC();
  373. if (screenDC)
  374. {
  375. hdcMem = CreateCompatibleDC(screenDC);
  376. if (hdcMem)
  377. {
  378. SelectObject(hdcMem, _MagellanMDownBMP);
  379. if (W32->GetObject(_MagellanMDownBMP, sizeof(BITMAP), &bm))
  380. {
  381. ptSize.x = bm.bmWidth;
  382. ptSize.y = bm.bmHeight;
  383. DPtoLP(screenDC, &ptSize, 1);
  384. ptOrg.x = 0;
  385. ptOrg.y = 0;
  386. DPtoLP(hdcMem, &ptOrg, 1);
  387. POINT ptBitmap;
  388. pdp->PointFromPointuv(ptBitmap, _ptStart);
  389. BitBlt(screenDC,
  390. ptBitmap.x - (ptSize.x >> 1) - 1,
  391. ptBitmap.y - (ptSize.y >> 1) + 1,
  392. ptSize.x, ptSize.y,
  393. hdcMem, ptOrg.x, ptOrg.y, 0x00990066 /* NOTXOR */);
  394. _fMagellanBitMapOn = !fOldState;
  395. }
  396. DeleteDC(hdcMem);
  397. }
  398. if (repaintDC == NULL)
  399. pdp->ReleaseDC(screenDC);
  400. }
  401. }
  402. }
  403. return fOldState;
  404. }
  405. ////////////////////////// CMagellanBMPStateWrap class.
  406. /*
  407. * CMagellanBMPStateWrap:: CMagellanBMPStateWrap
  408. *
  409. * @mfunc
  410. * Handles the state of whether to redraw the Magellan BMP as well as
  411. * repaints due to WM_PAINT.
  412. *
  413. * @devnote
  414. * This class is akin to smart pointer wrapper class idioms, in that
  415. * no matter how a routine exits the correct state of whether the
  416. * BMP is drawn will be maintined.
  417. */
  418. CMagellanBMPStateWrap:: CMagellanBMPStateWrap(CTxtEdit &ed, HDC repaintDC)
  419. : _ed(ed), _repaintDC(repaintDC)
  420. {
  421. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellanBMPStateWrap:: CMagellanBMPStateWrap");
  422. BOOL fRepaint;
  423. fRepaint = repaintDC != NULL && _ed.mouse._fMagellanBitMapOn != 0;
  424. _fMagellanState = fRepaint || _ed.mouse.InvertMagellanDownBMP(_ed._pdp, FALSE, NULL);
  425. _ed.mouse._fMagellanBitMapOn = FALSE;
  426. }
  427. CMagellanBMPStateWrap::~CMagellanBMPStateWrap()
  428. {
  429. TRACEBEGIN(TRCSUBSYSEDIT, TRCSCOPEINTERN, "CMagellanBMPStateWrap::~CMagellanBMPStateWrap");
  430. _ed.mouse.InvertMagellanDownBMP(_ed._pdp, _fMagellanState, _repaintDC);
  431. }
  432. #endif // !defined(NOMAGELLAN)