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.

1659 lines
46 KiB

  1. //---------------------------------------------------------------------------------------
  2. // File : Pager.cpp
  3. // Description :
  4. // This file implements the pager control
  5. //---------------------------------------------------------------------------------------
  6. #include "ctlspriv.h"
  7. #include "pager.h"
  8. #define MINBUTTONSIZE 12
  9. //Timer Flags
  10. #define PGT_SCROLL 1
  11. void NEAR DrawScrollArrow(HDC hdc, LPRECT lprc, WORD wControlState);
  12. //Public Functions
  13. //---------------------------------------------------------------------------------------
  14. extern "C" {
  15. //This function registers the pager window class
  16. BOOL InitPager(HINSTANCE hinst)
  17. {
  18. WNDCLASS wc;
  19. TraceMsg(TF_PAGER, "Init Pager");
  20. wc.lpfnWndProc = CPager::PagerWndProc;
  21. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  22. wc.hIcon = NULL;
  23. wc.lpszMenuName = NULL;
  24. wc.hInstance = hinst;
  25. wc.lpszClassName = WC_PAGESCROLLER;
  26. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); // NULL;
  27. wc.style = CS_GLOBALCLASS;
  28. wc.cbWndExtra = sizeof(LPVOID);
  29. wc.cbClsExtra = 0;
  30. RegisterClass(&wc);
  31. return TRUE;
  32. }
  33. }; // extern "C"
  34. //---------------------------------------------------------------------------------------
  35. CPager::CPager()
  36. {
  37. _clrBk = g_clrBtnFace;
  38. //Initialize Static Members
  39. _iButtonSize = (int) g_cxScrollbar * 3 / 4;
  40. if (_iButtonSize < MINBUTTONSIZE) {
  41. _iButtonSize = MINBUTTONSIZE;
  42. }
  43. _ptLastMove.x = -1;
  44. _ptLastMove.y = -1;
  45. _cLinesPerTimeout = 0;
  46. _cPixelsPerLine = 0;
  47. _cTimeout = GetDoubleClickTime() / 8;
  48. }
  49. //---------------------------------------------------------------------------------------
  50. // Static Pager Window Procedure
  51. LRESULT CPager::PagerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  52. {
  53. CPager *pp = (CPager*)GetWindowPtr(hwnd, 0);
  54. if (uMsg == WM_CREATE) {
  55. ASSERT(!pp);
  56. pp = new CPager();
  57. if (!pp)
  58. return 0L;
  59. }
  60. if (pp) {
  61. return pp->v_WndProc(hwnd, uMsg, wParam, lParam);
  62. }
  63. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  64. }
  65. //---------------------------------------------------------------------------------------
  66. LRESULT CPager::PagerDragCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  67. {
  68. CPager *pp = (CPager*)GetWindowPtr(hwnd, 0);
  69. if (pp) {
  70. return pp->_DragCallback(hwnd, uMsg, wParam, lParam);
  71. }
  72. return -1;
  73. }
  74. //---------------------------------------------------------------------------------------
  75. // CControl Class Implementation
  76. //---------------------------------------------------------------------------------------
  77. LRESULT CControl::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  78. {
  79. LRESULT lres = 0;
  80. switch (uMsg) {
  81. case WM_CREATE:
  82. CCCreateWindow();
  83. SetWindowPtr(hwnd, 0, this);
  84. CIInitialize(&ci, hwnd, (CREATESTRUCT*)lParam);
  85. return v_OnCreate();
  86. case WM_NCCALCSIZE:
  87. if (v_OnNCCalcSize(wParam, lParam, &lres))
  88. break;
  89. goto DoDefault;
  90. case WM_SIZE:
  91. v_OnSize(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  92. break;
  93. case WM_NOTIFYFORMAT:
  94. return CIHandleNotifyFormat(&ci, lParam);
  95. case WM_NOTIFY:
  96. return v_OnNotify(wParam, lParam);
  97. case WM_STYLECHANGED:
  98. v_OnStyleChanged(wParam, lParam);
  99. break;
  100. case WM_COMMAND:
  101. return v_OnCommand(wParam, lParam);
  102. case WM_NCPAINT:
  103. v_OnNCPaint();
  104. goto DoDefault;
  105. case WM_PRINTCLIENT:
  106. case WM_PAINT:
  107. _OnPaint((HDC)wParam);
  108. break;
  109. case WM_DESTROY:
  110. CCDestroyWindow();
  111. SetWindowLongPtr(hwnd, 0, 0);
  112. delete this;
  113. break;
  114. case TB_SETPARENT:
  115. {
  116. HWND hwndOld = ci.hwndParent;
  117. ci.hwndParent = (HWND)wParam;
  118. return (LRESULT)hwndOld;
  119. }
  120. default:
  121. if (CCWndProc(&ci, uMsg, wParam, lParam, &lres))
  122. return lres;
  123. DoDefault:
  124. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  125. }
  126. return lres;
  127. }
  128. //---------------------------------------------------------------------------------------
  129. BOOL CControl::v_OnNCCalcSize(WPARAM wParam, LPARAM lParam, LRESULT *plres)
  130. {
  131. return FALSE;
  132. }
  133. //---------------------------------------------------------------------------------------
  134. DWORD CControl::v_OnStyleChanged(WPARAM wParam, LPARAM lParam)
  135. {
  136. LPSTYLESTRUCT lpss = (LPSTYLESTRUCT) lParam;
  137. DWORD dwChanged = 0;
  138. if (wParam == GWL_STYLE) {
  139. ci.style = lpss->styleNew;
  140. dwChanged = (lpss->styleOld ^ lpss->styleNew);
  141. } else if (wParam == GWL_EXSTYLE) {
  142. //
  143. // Save the new ex-style bits
  144. //
  145. dwChanged = (lpss->styleOld ^ lpss->styleNew);
  146. ci.dwExStyle = lpss->styleNew;
  147. }
  148. TraceMsg(TF_PAGER, "cctl.v_osc: style=%x ret dwChged=%x", ci.style, dwChanged);
  149. return dwChanged;
  150. }
  151. //---------------------------------------------------------------------------------------
  152. void CControl::_OnPaint(HDC hdc)
  153. {
  154. if (hdc) {
  155. v_OnPaint(hdc);
  156. } else {
  157. PAINTSTRUCT ps;
  158. hdc = BeginPaint(ci.hwnd, &ps);
  159. v_OnPaint(hdc);
  160. EndPaint(ci.hwnd, &ps);
  161. }
  162. }
  163. //---------------------------------------------------------------------------------------
  164. // CPager Class Implementation
  165. //---------------------------------------------------------------------------------------
  166. inline int CPager::_GetButtonSize()
  167. {
  168. return _iButtonSize;
  169. }
  170. //---------------------------------------------------------------------------------------
  171. LRESULT CPager::_DragCallback(HWND hwnd, UINT code, WPARAM wp, LPARAM lp)
  172. {
  173. LRESULT lres = -1;
  174. switch (code)
  175. {
  176. case DPX_DRAGHIT:
  177. if (lp)
  178. {
  179. POINT pt;
  180. int iButton;
  181. pt.x = ((POINTL *)lp)->x;
  182. pt.y = ((POINTL *)lp)->y;
  183. MapWindowPoints(NULL, ci.hwnd, &pt, 1);
  184. iButton = _HitTest(pt.x, pt.y);
  185. if (iButton >= 0)
  186. {
  187. if(!_fTimerSet)
  188. {
  189. _fTimerSet = TRUE;
  190. _iButtonTrack = iButton;
  191. SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
  192. }
  193. } else {
  194. _KillTimer();
  195. _iButtonTrack = -1;
  196. }
  197. }
  198. else
  199. lres = -1;
  200. break;
  201. case DPX_LEAVE:
  202. _KillTimer();
  203. _iButtonTrack = -1;
  204. break;
  205. default:
  206. lres = -1;
  207. break;
  208. }
  209. return lres;
  210. }
  211. //---------------------------------------------------------------------------------------
  212. void CPager::_NeedScrollbars(RECT rc)
  213. {
  214. int parentheight;
  215. int childheight;
  216. POINT ptPos = _ptPos;
  217. if( ci.style & PGS_HORZ ) {
  218. FlipRect(&rc);
  219. FlipPoint(&ptPos);
  220. }
  221. //Get Parent Window height
  222. parentheight = RECTHEIGHT(rc);
  223. //Get Child Window height
  224. rc = _rcChildIdeal;
  225. if (ci.style & PGS_HORZ ) {
  226. FlipRect(&rc);
  227. }
  228. childheight = RECTHEIGHT(rc);
  229. TraceMsg(TF_PAGER, "cps.nsb: cyChild=%d cyParent=%d _yPos=%d", childheight, parentheight, ptPos.y);
  230. if (childheight < parentheight )
  231. {
  232. ptPos.y = 0;
  233. }
  234. int iButton = _HitTestCursor();
  235. //See if we need top scrollbar
  236. if (ptPos.y > 0 ) {
  237. // if this button is the one that is hot tracked and the style is not PGS_AUTOSCROLL
  238. // then we set the state to PGF_HOT otherwise the state is set to PGF_NORMAL
  239. _dwState[PGB_TOPORLEFT] |= PGF_NORMAL;
  240. _dwState[PGB_TOPORLEFT] &= ~PGF_GRAYED;
  241. } else {
  242. if (!(ci.style & PGS_AUTOSCROLL) && (iButton == PGB_TOPORLEFT || _iButtonTrack == PGB_TOPORLEFT)) {
  243. _dwState[PGB_TOPORLEFT] |= PGF_GRAYED;
  244. } else {
  245. _dwState[PGB_TOPORLEFT] = PGF_INVISIBLE;
  246. }
  247. }
  248. if (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE)
  249. {
  250. parentheight -= _GetButtonSize();
  251. }
  252. //See if we need botton scrollbar
  253. if ((childheight - ptPos.y) > parentheight ) {
  254. //We need botton scroll bar
  255. // if this button is the one that is hot tracked and the style is not PGS_AUTOSCROLL
  256. // then we set the state to PGF_HOT otherwise the state is set to PGF_NORMAL
  257. _dwState[PGB_BOTTOMORRIGHT] |= PGF_NORMAL;
  258. _dwState[PGB_BOTTOMORRIGHT] &= ~PGF_GRAYED;
  259. } else {
  260. if (!(ci.style & PGS_AUTOSCROLL) && (iButton == PGB_BOTTOMORRIGHT || _iButtonTrack == PGB_BOTTOMORRIGHT)) {
  261. _dwState[PGB_BOTTOMORRIGHT] |= PGF_GRAYED;
  262. } else {
  263. _dwState[PGB_BOTTOMORRIGHT] = PGF_INVISIBLE;
  264. }
  265. }
  266. }
  267. //---------------------------------------------------------------------------------------
  268. BOOL CPager::v_OnNCCalcSize(WPARAM wParam, LPARAM lParam, LRESULT *plres)
  269. {
  270. *plres = DefWindowProc(ci.hwnd, WM_NCCALCSIZE, wParam, lParam ) ;
  271. if (wParam) {
  272. BOOL bHorzMirror = ((ci.dwExStyle & RTL_MIRRORED_WINDOW) && (ci.style & PGS_HORZ));
  273. DWORD dwStateOld[2];
  274. NCCALCSIZE_PARAMS* pnp = (NCCALCSIZE_PARAMS*)lParam;
  275. _rcDefClient = pnp->rgrc[0];
  276. InflateRect(&_rcDefClient, -_iBorder, -_iBorder);
  277. _GetChildSize();
  278. dwStateOld[0] = _dwState[0];
  279. dwStateOld[1] = _dwState[1];
  280. _NeedScrollbars(pnp->rgrc[0]);
  281. // invalidate only if something has changed to force a new size
  282. if ((dwStateOld[0] != _dwState[0] && (dwStateOld[0] == PGF_INVISIBLE || _dwState[0] == PGF_INVISIBLE)) ||
  283. (dwStateOld[1] != _dwState[1] && (dwStateOld[1] == PGF_INVISIBLE || _dwState[1] == PGF_INVISIBLE))
  284. ) {
  285. RedrawWindow(ci.hwnd, NULL,NULL,RDW_INVALIDATE|RDW_ERASE);
  286. }
  287. // Check and change for horizontal mode
  288. if( ci.style & PGS_HORZ ) {
  289. FlipRect(&(pnp->rgrc[0]));
  290. }
  291. if( _dwState[PGB_TOPORLEFT] != PGF_INVISIBLE ) {
  292. //
  293. // Check for RTL mirrored window
  294. //
  295. if (bHorzMirror)
  296. pnp->rgrc[0].bottom -= _GetButtonSize();
  297. else
  298. pnp->rgrc[0].top += _GetButtonSize();
  299. } else
  300. pnp->rgrc[0].top += _iBorder;
  301. if( _dwState[PGB_BOTTOMORRIGHT] != PGF_INVISIBLE ) {
  302. //
  303. // Check for RTL mirrored window
  304. //
  305. if (bHorzMirror)
  306. pnp->rgrc[0].top += _GetButtonSize();
  307. else
  308. pnp->rgrc[0].bottom -= _GetButtonSize();
  309. } else
  310. pnp->rgrc[0].bottom -= _iBorder;
  311. if (pnp->rgrc[0].bottom < pnp->rgrc[0].top)
  312. pnp->rgrc[0].bottom = pnp->rgrc[0].top;
  313. //Change back
  314. if( ci.style & PGS_HORZ ) {
  315. FlipRect(&(pnp->rgrc[0]));
  316. }
  317. }
  318. return TRUE;
  319. }
  320. int CPager::_HitTestCursor()
  321. {
  322. POINT pt;
  323. GetCursorPos(&pt);
  324. return _HitTestScreen(&pt);
  325. }
  326. int CPager::_HitTestScreen(POINT* ppt)
  327. {
  328. RECT rc, rc1;
  329. GetWindowRect(ci.hwnd, &rc);
  330. if (!PtInRect(&rc, *ppt)) {
  331. return -1;
  332. }
  333. //Get the button Rects;
  334. rc = _GetButtonRect(PGB_TOPORLEFT);
  335. rc1 = _GetButtonRect(PGB_BOTTOMORRIGHT);
  336. if (PtInRect(&rc, *ppt)) {
  337. return (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE ? PGB_TOPORLEFT : -1);
  338. }else if (PtInRect(&rc1, *ppt)) {
  339. return (_dwState[PGB_BOTTOMORRIGHT] != PGF_INVISIBLE ? PGB_BOTTOMORRIGHT : -1);
  340. }
  341. return -1;
  342. }
  343. //---------------------------------------------------------------------------------------
  344. int CPager::_HitTest(int x, int y)
  345. {
  346. POINT pt;
  347. pt.x = x;
  348. pt.y = y;
  349. ClientToScreen(ci.hwnd, &pt);
  350. return _HitTestScreen(&pt);
  351. }
  352. //---------------------------------------------------------------------------------------
  353. void CPager::_DrawBlank(HDC hdc, int button)
  354. {
  355. RECT rc;
  356. UINT uFlags = 0;
  357. int iHeight;
  358. BOOL fRelDC = FALSE;
  359. if (hdc == NULL) {
  360. hdc = GetWindowDC(ci.hwnd);
  361. fRelDC = TRUE;
  362. }
  363. GetWindowRect(ci.hwnd, &rc);
  364. MapWindowRect(NULL, ci.hwnd, &rc);
  365. // client to window coordinates
  366. OffsetRect(&rc, -rc.left, -rc.top);
  367. //Check for horizontal mode
  368. if( ci.style & PGS_HORZ ) {
  369. FlipRect(&rc);
  370. }
  371. iHeight = _dwState[button] == PGF_INVISIBLE ? _iBorder : _GetButtonSize();
  372. switch(button) {
  373. case PGB_TOPORLEFT:
  374. rc.bottom = rc.top + iHeight;
  375. break;
  376. case PGB_BOTTOMORRIGHT:
  377. rc.top = rc.bottom - iHeight;
  378. break;
  379. }
  380. if( ci.style & PGS_HORZ ) {
  381. FlipRect(&rc);
  382. }
  383. FillRectClr(hdc, &rc, _clrBk);
  384. if (fRelDC)
  385. ReleaseDC(ci.hwnd, hdc);
  386. }
  387. //---------------------------------------------------------------------------------------
  388. void CPager::_DrawButton(HDC hdc, int button)
  389. {
  390. RECT rc;
  391. UINT uFlags = 0;
  392. BOOL fRelDC = FALSE;
  393. GetWindowRect(ci.hwnd, &rc);
  394. MapWindowRect(NULL, ci.hwnd, &rc);
  395. int state = _dwState[button];
  396. if (state == PGF_INVISIBLE)
  397. return;
  398. if (hdc == NULL) {
  399. hdc = GetWindowDC(ci.hwnd);
  400. fRelDC = TRUE;
  401. }
  402. if (state & PGF_GRAYED ) {
  403. uFlags |= DCHF_INACTIVE;
  404. } else if (state & PGF_DEPRESSED ) {
  405. uFlags |= DCHF_PUSHED;
  406. } else if (state & PGF_HOT ) {
  407. uFlags |= DCHF_HOT;
  408. }
  409. // screen to window coordinates
  410. OffsetRect(&rc, -rc.left, -rc.top);
  411. //Check for horizontal mode
  412. if( ci.style & PGS_HORZ ) {
  413. FlipRect(&rc);
  414. }
  415. if( ci.style & PGS_HORZ )
  416. uFlags |= DCHF_HORIZONTAL;
  417. if (button == PGB_BOTTOMORRIGHT)
  418. uFlags |= DCHF_FLIPPED;
  419. switch(button) {
  420. case PGB_TOPORLEFT:
  421. rc.bottom = rc.top + _GetButtonSize();
  422. rc.left += _iBorder;
  423. rc.right -= _iBorder;
  424. break;
  425. case PGB_BOTTOMORRIGHT:
  426. rc.top = rc.bottom - _GetButtonSize();
  427. rc.left += _iBorder;
  428. rc.right -= _iBorder;
  429. break;
  430. default:
  431. ASSERT(FALSE);
  432. }
  433. if( ci.style & PGS_HORZ ) {
  434. FlipRect(&rc);
  435. }
  436. SetBkColor(hdc, _clrBk);
  437. DrawScrollArrow(hdc, &rc, uFlags);
  438. if (fRelDC)
  439. ReleaseDC(ci.hwnd, hdc);
  440. }
  441. //---------------------------------------------------------------------------------------
  442. void CPager::v_OnNCPaint()
  443. {
  444. HDC hdc = GetWindowDC(ci.hwnd);
  445. _DrawBlank(hdc, PGB_TOPORLEFT);
  446. _DrawButton(hdc, PGB_TOPORLEFT);
  447. _DrawBlank(hdc, PGB_BOTTOMORRIGHT);
  448. _DrawButton(hdc, PGB_BOTTOMORRIGHT);
  449. ReleaseDC(ci.hwnd, hdc);
  450. }
  451. //---------------------------------------------------------------------------------------
  452. void CPager::v_OnPaint(HDC hdc)
  453. {
  454. //There's nothing to paint in the client area.
  455. }
  456. //---------------------------------------------------------------------------------------
  457. BOOL CPager::_OnPrint(HDC hdc, UINT uFlags)
  458. {
  459. //We'll be partying with the hdc in this function so save it.
  460. int iDC = SaveDC(hdc);
  461. //Print only the Non Client Area.
  462. if (uFlags & PRF_NONCLIENT) {
  463. int cx = 0;
  464. int cy = 0;
  465. RECT rc;
  466. //Draw the top/left button
  467. _DrawBlank(hdc, PGB_TOPORLEFT);
  468. _DrawButton(hdc, PGB_TOPORLEFT);
  469. //Draw the bottom/left button
  470. _DrawBlank(hdc, PGB_BOTTOMORRIGHT);
  471. _DrawButton(hdc, PGB_BOTTOMORRIGHT);
  472. //Is the top button visible
  473. if (_dwState[PGB_TOPORLEFT] != PGF_INVISIBLE) {
  474. //yes, find the space taken
  475. if ( ci.style & PGS_HORZ ) {
  476. cx = _GetButtonSize();
  477. }else {
  478. cy = _GetButtonSize();
  479. }
  480. }
  481. //Restrict the child draw area to our client area
  482. GetClientRect(ci.hwnd, &rc);
  483. IntersectClipRect(hdc, cx, cy, cx + RECTWIDTH(rc), cy + RECTHEIGHT(rc));
  484. //Since We have drawn the non client area, Nuke the PRF_NONCLIENT flag
  485. uFlags &= ~PRF_NONCLIENT;
  486. }
  487. //Pass it to the def window proc for default processing
  488. DefWindowProc(ci.hwnd, WM_PRINT, (WPARAM)hdc, (LPARAM)uFlags);
  489. //Restore the saved DC
  490. RestoreDC(hdc, iDC);
  491. return TRUE;
  492. }
  493. //---------------------------------------------------------------------------------------
  494. LRESULT CPager::v_OnCommand(WPARAM wParam, LPARAM lParam)
  495. {
  496. // forward to parent
  497. return SendMessage(ci.hwndParent, WM_COMMAND, wParam, lParam);
  498. }
  499. //---------------------------------------------------------------------------------------
  500. LRESULT CPager::v_OnNotify(WPARAM wParam, LPARAM lParam)
  501. {
  502. // forward to parent
  503. LPNMHDR lpNmhdr = (LPNMHDR)lParam;
  504. return SendNotifyEx(ci.hwndParent, (HWND) -1,
  505. lpNmhdr->code, lpNmhdr, ci.bUnicode);
  506. }
  507. //---------------------------------------------------------------------------------------
  508. DWORD CPager::v_OnStyleChanged(WPARAM wParam, LPARAM lParam)
  509. {
  510. DWORD dwChanged = CControl::v_OnStyleChanged(wParam, lParam);
  511. if (dwChanged & PGS_DRAGNDROP) {
  512. if ((ci.style & PGS_DRAGNDROP) && !_hDragProxy) {
  513. _hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, TRUE);
  514. } else if (! (ci.style & PGS_DRAGNDROP) && _hDragProxy) {
  515. DestroyDragProxy(_hDragProxy);
  516. }
  517. }
  518. if (dwChanged)
  519. CCInvalidateFrame(ci.hwnd); // SWP_FRAMECHANGED etc.
  520. return dwChanged;
  521. }
  522. //---------------------------------------------------------------------------------------
  523. LRESULT CPager::v_OnCreate()
  524. {
  525. if (ci.style & PGS_DRAGNDROP)
  526. _hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, TRUE);
  527. return TRUE;
  528. }
  529. //---------------------------------------------------------------------------------------
  530. void CPager::_GetChildSize()
  531. {
  532. if (_hwndChild) {
  533. RECT rc;
  534. NMPGCALCSIZE nmpgcalcsize;
  535. int width , height;
  536. rc = _rcDefClient;
  537. if( ci.style & PGS_HORZ ) {
  538. nmpgcalcsize.dwFlag = PGF_CALCWIDTH;
  539. } else {
  540. nmpgcalcsize.dwFlag = PGF_CALCHEIGHT;
  541. }
  542. nmpgcalcsize.iWidth = RECTWIDTH(rc); // pager width
  543. nmpgcalcsize.iHeight = RECTHEIGHT(rc); // best-guess for child
  544. CCSendNotify(&ci, PGN_CALCSIZE, &nmpgcalcsize.hdr);
  545. if( ci.style & PGS_HORZ ) {
  546. width = nmpgcalcsize.iWidth;
  547. height = RECTHEIGHT(rc);
  548. } else {
  549. width = RECTWIDTH(rc);
  550. height = nmpgcalcsize.iHeight;
  551. }
  552. GetWindowRect(_hwndChild, &rc);
  553. MapWindowRect(NULL, ci.hwnd, &rc);
  554. if( ci.style & PGS_HORZ ) {
  555. rc.top = _iBorder;
  556. } else {
  557. rc.left = _iBorder;
  558. }
  559. rc.right = rc.left + width;
  560. rc.bottom = rc.top + height;
  561. _rcChildIdeal = rc;
  562. }
  563. }
  564. //---------------------------------------------------------------------------------------
  565. void CPager::v_OnSize(int x, int y)
  566. {
  567. if (_hwndChild) {
  568. RECT rc = _rcChildIdeal;
  569. _SetChildPos(&rc, 0); // SetWindowPos
  570. }
  571. }
  572. //---------------------------------------------------------------------------------------
  573. //*** _SetChildPos -- SetWindowPos of child, w/ validation
  574. // NOTES
  575. // 'validation' means in sane state -- min size, and not off end.
  576. // WARNING: we don't update *prcChild.
  577. // BUGBUG what happens if we're called w/ NOMOVE or NOSIZE?
  578. void CPager::_SetChildPos(IN RECT * prcChild, UINT uFlags)
  579. {
  580. POINT ptPos = _ptPos;
  581. RECT rcChild = *prcChild;
  582. RECT rcPager;
  583. ASSERT(!(uFlags & SWP_NOMOVE)); // won't work
  584. // BUGBUG (scotth): is it okay that _hwndChild is NULL sometimes?
  585. // If so, should this whole function be wrapped with if (_hwndChild)
  586. // or just the call to SetWindowPos below?
  587. ASSERT(IS_VALID_HANDLE(_hwndChild, WND));
  588. rcPager = _rcDefClient;
  589. if ( ci.style & PGS_HORZ ) {
  590. FlipPoint(&ptPos);
  591. FlipRect(&rcChild);
  592. FlipRect(&rcPager);
  593. }
  594. int yNew = ptPos.y;
  595. if (RECTHEIGHT(rcChild) < RECTHEIGHT(rcPager)) {
  596. // force to min height
  597. // this handles the case where: i have an ISFBand that fills up the
  598. // whole pager, i stretch the pager width, and the ISFBand reformats
  599. // to take less height, so it shrinks its height and ends up shorter
  600. // than the pager.
  601. TraceMsg(TF_PAGER, "cps.s: h=%d h'=%d", RECTHEIGHT(rcChild), RECTHEIGHT(rcPager));
  602. ASSERT(!(uFlags & SWP_NOSIZE)); // won't work
  603. rcChild.bottom = rcChild.top + RECTHEIGHT(rcPager);
  604. yNew = 0;
  605. }
  606. // Maximum we can scroll is child height minus pager height.
  607. // Here rcPager also includes scrollbutton so we need to add that also
  608. /*
  609. ___________ Button Width
  610. |
  611. V ---------------- Max we can scroll (yMax)
  612. __ |
  613. / \V
  614. - ---------pager-----------
  615. | |-------------------------|--------------------------------
  616. | || | |
  617. | || child | |
  618. | |-------------------------|--------------------------------
  619. - -------------------------
  620. \/\/
  621. Border | |
  622. <----- -------------->We need to take care of this gap.
  623. \-----------------------------/
  624. ^
  625. |______ RECTHEIGHT(rcChild) - RECTHEIGHT(rcPager)
  626. rcPager
  627. We need to add the difference between the button size and border to
  628. */
  629. int yMax = RECTHEIGHT(rcChild) - RECTHEIGHT(rcPager) + (_GetButtonSize() - _iBorder);
  630. // make sure we don't end up off the top/end, and we always show
  631. // at least 1 page worth (if we have that much)
  632. // n.b. pager can override client's policy (BUGBUG?)
  633. if (yNew < 0) {
  634. // 1st page
  635. yNew = 0;
  636. } else if (yNew > yMax) {
  637. // last page
  638. yNew = yMax;
  639. }
  640. int yOffset = yNew;
  641. // When the top button is grayed we do not want to display our child away from the button .
  642. // it should be drawn right below the button. For this we tweak the position of the child window.
  643. //Check for the condition of grayed top button in which case we need to set position even behind
  644. // so that the child window falls below the grayed button
  645. if( _dwState[PGB_TOPORLEFT] & PGF_GRAYED )
  646. {
  647. yOffset += (_GetButtonSize() - _iBorder);
  648. }
  649. //yOffset is the tweaked value. Its just for making the child window to appear below the grayed button
  650. OffsetRect(&rcChild, 0, -yOffset - rcChild.top);
  651. //yNew is the actual logical positon of the window .
  652. ptPos.y = yNew;
  653. if (ci.style & PGS_HORZ) {
  654. // restore for copy and SWP
  655. FlipPoint(&ptPos);
  656. FlipRect(&rcChild);
  657. }
  658. _ptPos = ptPos;
  659. SetWindowPos(_hwndChild, NULL, rcChild.left, rcChild.top, RECTWIDTH(rcChild), RECTHEIGHT(rcChild), uFlags);
  660. return;
  661. }
  662. //---------------------------------------------------------------------------------------
  663. //*** PGFToPGNDirection -- convert PGB_TOPORLEFT/btmorright to up/down/left/right
  664. // NOTES
  665. // BUGBUG maybe PGN_* should we just take the PGF flags?
  666. // BUGBUG should make a macro (including some ordering magic)
  667. DWORD CPager::_PGFToPGNDirection(DWORD dwDir)
  668. {
  669. ASSERT(dwDir == PGB_TOPORLEFT || dwDir == PGB_BOTTOMORRIGHT);
  670. if (ci.style & PGS_HORZ) {
  671. return (dwDir == PGB_TOPORLEFT) ? PGF_SCROLLLEFT : PGF_SCROLLRIGHT;
  672. }
  673. else {
  674. return (dwDir == PGB_TOPORLEFT) ? PGF_SCROLLUP : PGF_SCROLLDOWN;
  675. }
  676. }
  677. //---------------------------------------------------------------------------------------
  678. void CPager::_Scroll(DWORD dwDirection)
  679. {
  680. RECT rc;
  681. NMPGSCROLL nmpgscroll;
  682. int iXoffset =0, iYoffset=0;
  683. WORD fwKeys = 0;
  684. int iNewPos ;
  685. // if grayed, you can't scroll.
  686. if (_dwState[dwDirection] & PGF_GRAYED)
  687. return;
  688. if (GetKeyState(VK_CONTROL) < 0 )
  689. fwKeys |= PGK_CONTROL;
  690. if (GetKeyState(VK_SHIFT) < 0 )
  691. fwKeys |= PGK_SHIFT;
  692. if (GetKeyState(VK_MENU) < 0 )
  693. fwKeys |= PGK_MENU;
  694. dwDirection = _PGFToPGNDirection(dwDirection);
  695. // set some defaults
  696. GetClientRect(ci.hwnd, &rc);
  697. nmpgscroll.fwKeys = fwKeys;
  698. nmpgscroll.rcParent = rc;
  699. nmpgscroll.iXpos = _ptPos.x;
  700. nmpgscroll.iYpos = _ptPos.y;
  701. nmpgscroll.iDir = dwDirection;
  702. int iScroll = (ci.style & PGS_HORZ) ? RECTWIDTH(rc) : RECTHEIGHT(rc);
  703. if (_cLinesPerTimeout)
  704. iScroll = _cLinesPerTimeout * _cPixelsPerLine;
  705. nmpgscroll.iScroll = iScroll;
  706. // let client override
  707. CCSendNotify(&ci, PGN_SCROLL, &nmpgscroll.hdr);
  708. // do it
  709. switch (dwDirection)
  710. {
  711. case PGF_SCROLLDOWN:
  712. iNewPos = _ptPos.y + nmpgscroll.iScroll;
  713. break;
  714. case PGF_SCROLLUP:
  715. iNewPos = _ptPos.y - nmpgscroll.iScroll;
  716. break;
  717. case PGF_SCROLLRIGHT:
  718. iNewPos = _ptPos.x + nmpgscroll.iScroll;
  719. break;
  720. case PGF_SCROLLLEFT:
  721. iNewPos = _ptPos.x - nmpgscroll.iScroll;
  722. break;
  723. }
  724. _OnSetPos(iNewPos);
  725. }
  726. //---------------------------------------------------------------------------------------
  727. void CPager::_OnLButtonChange(UINT uMsg,LPARAM lParam)
  728. {
  729. POINT pt;
  730. int iButton;
  731. pt.x = GET_X_LPARAM(lParam);
  732. pt.y = GET_Y_LPARAM(lParam);
  733. iButton = _HitTest(pt.x, pt.y);
  734. if( uMsg == WM_LBUTTONDOWN ) {
  735. // Check the button is valid and is not grayed
  736. // if it is grayed then dont do anything
  737. if (iButton >= 0) {
  738. SetCapture(ci.hwnd);
  739. _fOwnsButtonDown = TRUE;
  740. _iButtonTrack = iButton;
  741. _dwState[iButton] |= PGF_DEPRESSED;
  742. _DrawButton(NULL, iButton);
  743. _Scroll(iButton);
  744. SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout * 4, NULL);
  745. }
  746. } else {
  747. if (_iButtonTrack >= 0) {
  748. _dwState[_iButtonTrack] &= ~PGF_DEPRESSED;
  749. _DrawButton(NULL, _iButtonTrack);
  750. _iButtonTrack = -1;
  751. }
  752. _KillTimer();
  753. if (iButton < 0)
  754. _OnMouseLeave();
  755. }
  756. }
  757. //---------------------------------------------------------------------------------------
  758. RECT CPager :: _GetButtonRect(int iButton)
  759. {
  760. RECT rc;
  761. GetWindowRect(ci.hwnd, &rc);
  762. if( ci.style & PGS_HORZ ) {
  763. FlipRect(&rc);
  764. }
  765. //
  766. // Mirror the rects if the parent is mirrored
  767. //
  768. if (((ci.dwExStyle & RTL_MIRRORED_WINDOW) && (ci.style & PGS_HORZ))) {
  769. switch (iButton) {
  770. case PGB_TOPORLEFT:
  771. iButton = PGB_BOTTOMORRIGHT;
  772. break;
  773. case PGB_BOTTOMORRIGHT:
  774. iButton = PGB_TOPORLEFT;
  775. break;
  776. }
  777. }
  778. switch(iButton) {
  779. case PGB_TOPORLEFT:
  780. rc.bottom = rc.top + _GetButtonSize();
  781. rc.left += _iBorder;
  782. rc.right -= _iBorder;
  783. break;
  784. case PGB_BOTTOMORRIGHT:
  785. rc.top = rc.bottom - _GetButtonSize();
  786. rc.left += _iBorder;
  787. rc.right -= _iBorder;
  788. break;
  789. }
  790. if( ci.style & PGS_HORZ ) {
  791. FlipRect(&rc);
  792. }
  793. return rc;
  794. }
  795. //---------------------------------------------------------------------------------------
  796. void CPager :: _OnMouseLeave()
  797. {
  798. //Whether we leave the window (WM_MOUSELEAVE) or Leave one of the scroll buttons (WM_MOUSEMOVE)
  799. // We do the same thing.
  800. // We are leaving the pager window.
  801. if (GetCapture() == ci.hwnd) {
  802. CCReleaseCapture(&ci);
  803. }
  804. // if we are tracking some button then release that mouse and that button
  805. if (_iButtonTrack >= 0) {
  806. _iButtonTrack = -1;
  807. }
  808. if (_dwState[PGB_TOPORLEFT] & (PGF_HOT | PGF_DEPRESSED)) {
  809. _dwState[PGB_TOPORLEFT] &= ~(PGF_HOT | PGF_DEPRESSED);
  810. _DrawButton(NULL, PGB_TOPORLEFT);
  811. }
  812. if (_dwState[PGB_BOTTOMORRIGHT] & (PGF_HOT | PGF_DEPRESSED)) {
  813. _dwState[PGB_BOTTOMORRIGHT] &= ~(PGF_HOT | PGF_DEPRESSED);
  814. _DrawButton(NULL, PGB_BOTTOMORRIGHT);
  815. }
  816. _KillTimer();
  817. _fOwnsButtonDown = FALSE;
  818. //If any of the button is in gray state then it needs to be removed.
  819. if ((_dwState[PGB_TOPORLEFT] & PGF_GRAYED) || (_dwState[PGB_BOTTOMORRIGHT] & PGF_GRAYED)) {
  820. //This forces a recalc for scrollbars and removes those that are not needed
  821. CCInvalidateFrame(ci.hwnd);
  822. }
  823. }
  824. //---------------------------------------------------------------------------------------
  825. void CPager::_OnMouseMove(WPARAM wParam, LPARAM lparam)
  826. {
  827. RECT rc;
  828. POINT pt;
  829. int iButton;
  830. pt.x = GET_X_LPARAM(lparam);
  831. pt.y = GET_Y_LPARAM(lparam);
  832. // Ignore zero-mouse moves
  833. if (pt.x == _ptLastMove.x && pt.y == _ptLastMove.y)
  834. return;
  835. _ptLastMove = pt;
  836. iButton = _HitTest(pt.x, pt.y);
  837. if (_iButtonTrack >= 0 )
  838. {
  839. if (_dwState[_iButtonTrack] != PGF_INVISIBLE)
  840. {
  841. //Some Button is pressed right now
  842. ClientToScreen(ci.hwnd, &pt);
  843. rc = _GetButtonRect(_iButtonTrack);
  844. DWORD dwOldState = _dwState[_iButtonTrack];
  845. if (PtInRect(&rc, pt))
  846. {
  847. _dwState[_iButtonTrack] |= PGF_DEPRESSED;
  848. }
  849. else
  850. {
  851. _dwState[_iButtonTrack] &= ~PGF_DEPRESSED;
  852. }
  853. if (dwOldState != _dwState[_iButtonTrack])
  854. _DrawButton(NULL, _iButtonTrack);
  855. }
  856. // if we were tracking it, but the mouse is up and gone
  857. if (GetCapture() == ci.hwnd && !((wParam & MK_LBUTTON) || (ci.style & PGS_AUTOSCROLL)) && iButton != _iButtonTrack)
  858. _OnMouseLeave();
  859. }
  860. else
  861. {
  862. // No button is pressed .
  863. if( iButton >= 0 )
  864. {
  865. //Capture the mouse so that we can keep track of when the mouse is leaving our button
  866. SetCapture(ci.hwnd);
  867. // if the style is PGS_AUTOSCROLL then we dont make the button hot when hovering
  868. // over button.
  869. //Is PGS_AUTOSCROLL set
  870. _dwState[iButton] |= PGF_HOT;
  871. if (ci.style & PGS_AUTOSCROLL)
  872. {
  873. _dwState[iButton] |= PGF_DEPRESSED;
  874. }
  875. //If the lbutton is down and the mouse is over one of the button then
  876. // someone is trying to do drag and drop so autoscroll to help them.
  877. // Make sure the lbutton down did not happen in the button before scrolling
  878. if ( ((wParam & MK_LBUTTON) &&
  879. (_iButtonTrack < 0)) ||
  880. (ci.style & PGS_AUTOSCROLL) )
  881. {
  882. _iButtonTrack = iButton;
  883. SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
  884. }
  885. _DrawButton(NULL, iButton);
  886. }
  887. else
  888. {
  889. //Mouse is not over any button or it has left one of the scroll buttons.
  890. //In either case call _OnMouseLeave
  891. _OnMouseLeave();
  892. }
  893. }
  894. }
  895. //---------------------------------------------------------------------------------------
  896. void CPager::_OnSetChild(HWND hwnd, HWND hwndChild)
  897. {
  898. ASSERT(IS_VALID_HANDLE(hwndChild, WND));
  899. RECT rc;
  900. _hwndChild = hwndChild;
  901. _ptPos.x = 0;
  902. _ptPos.y = 0;
  903. _fReCalcSend = FALSE;
  904. if (GetCapture() == ci.hwnd)
  905. {
  906. CCReleaseCapture(&ci);
  907. }
  908. _iButtonTrack = -1;
  909. GetClientRect(hwnd, &rc);
  910. _OnReCalcSize();
  911. }
  912. //---------------------------------------------------------------------------------------
  913. void CPager::_OnReCalcSize()
  914. {
  915. RECT rc;
  916. CCInvalidateFrame(ci.hwnd); // SWP_FRAMECHANGED etc.
  917. _fReCalcSend = FALSE;
  918. rc = _rcChildIdeal;
  919. _SetChildPos(&rc, 0); // SetWindowPos
  920. }
  921. //---------------------------------------------------------------------------------------
  922. void CPager::_OnSetPos(int iPos)
  923. {
  924. RECT rc = _rcChildIdeal;
  925. if( ci.style & PGS_HORZ ) {
  926. FlipRect(&rc);
  927. FlipPoint(&_ptPos);
  928. }
  929. int height;
  930. if (iPos < 0)
  931. iPos = 0;
  932. height = RECTHEIGHT(rc);
  933. if( iPos < 0 || iPos > height || _ptPos.y == iPos ) {
  934. //Invalid Position specified or no change . Igonore it.
  935. return;
  936. }
  937. _ptPos.y = iPos;
  938. if( ci.style & PGS_HORZ ) {
  939. FlipRect(&rc);
  940. FlipPoint(&_ptPos);
  941. }
  942. CCInvalidateFrame(ci.hwnd);
  943. _SetChildPos(&rc , 0);
  944. }
  945. //---------------------------------------------------------------------------------------
  946. int CPager::_OnGetPos()
  947. {
  948. if( ci.style & PGS_HORZ ) {
  949. return _ptPos.x;
  950. }else{
  951. return _ptPos.y;
  952. }
  953. }
  954. //---------------------------------------------------------------------------------------
  955. DWORD CPager::_GetButtonState(int iButton)
  956. {
  957. DWORD dwState = 0;
  958. // Is the button id valid ?
  959. if ((iButton == PGB_TOPORLEFT) || (iButton == PGB_BOTTOMORRIGHT))
  960. {
  961. //yes , Get the current state of the button
  962. dwState = _dwState[iButton];
  963. }
  964. return dwState;
  965. }
  966. //---------------------------------------------------------------------------------------
  967. void CPager::_OnTimer(UINT id)
  968. {
  969. switch (id)
  970. {
  971. case PGT_SCROLL:
  972. if (_iButtonTrack >= 0)
  973. {
  974. // set it again because we do it faster every subsequent time
  975. SetTimer(ci.hwnd, PGT_SCROLL, _cTimeout, NULL);
  976. if (_HitTestCursor() == _iButtonTrack)
  977. {
  978. _Scroll(_iButtonTrack);
  979. }
  980. else if (!_fOwnsButtonDown)
  981. {
  982. // if we don't own the mouse tracking (ie, the user didn't button down on us to begin with,
  983. // then we're done once we leave the button
  984. _OnMouseLeave();
  985. }
  986. }
  987. break;
  988. }
  989. }
  990. void CPager::_KillTimer()
  991. {
  992. KillTimer(ci.hwnd, PGT_SCROLL);
  993. _fTimerSet = FALSE;
  994. }
  995. //---------------------------------------------------------------------------------------
  996. int CPager::_OnSetBorder(int iBorder)
  997. {
  998. int iOld = _iBorder;
  999. int iNew = iBorder;
  1000. //Border can't be negative
  1001. if (iNew < 0 )
  1002. {
  1003. iNew = 0;
  1004. }
  1005. //Border can't be bigger than the button size
  1006. if (iNew > _GetButtonSize())
  1007. {
  1008. iNew = _GetButtonSize();
  1009. }
  1010. _iBorder = iNew;
  1011. CCInvalidateFrame(ci.hwnd);
  1012. RECT rc = _rcChildIdeal;
  1013. _SetChildPos(&rc, 0); // SetWindowPos
  1014. return iOld;
  1015. }
  1016. //---------------------------------------------------------------------------------------
  1017. int CPager::_OnSetButtonSize(int iSize)
  1018. {
  1019. int iOldSize = _iButtonSize;
  1020. _iButtonSize = iSize;
  1021. if (_iButtonSize < MINBUTTONSIZE)
  1022. {
  1023. _iButtonSize = MINBUTTONSIZE;
  1024. }
  1025. // Border can't be bigger than button size
  1026. if (_iBorder > _iButtonSize)
  1027. {
  1028. _iBorder = _iButtonSize;
  1029. }
  1030. CCInvalidateFrame(ci.hwnd);
  1031. RECT rc = _rcChildIdeal;
  1032. _SetChildPos(&rc, 0); // SetWindowPos
  1033. return iOldSize;
  1034. }
  1035. //---------------------------------------------------------------------------------------
  1036. LRESULT CPager::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1037. {
  1038. switch (uMsg) {
  1039. case PGM_GETDROPTARGET:
  1040. if (!_hDragProxy)
  1041. _hDragProxy = CreateDragProxy(ci.hwnd, PagerDragCallback, FALSE);
  1042. GetDragProxyTarget(_hDragProxy, (IDropTarget**)lParam);
  1043. break;
  1044. case PGM_SETSCROLLINFO:
  1045. _cLinesPerTimeout = LOWORD(lParam);
  1046. _cPixelsPerLine = HIWORD(lParam);
  1047. _cTimeout = (UINT)wParam;
  1048. break;
  1049. case PGM_SETCHILD:
  1050. _OnSetChild(hwnd, (HWND)lParam);
  1051. break;
  1052. case PGM_RECALCSIZE:
  1053. if (!_fReCalcSend )
  1054. {
  1055. _fReCalcSend = TRUE;
  1056. PostMessage(hwnd, PGMP_RECALCSIZE, wParam, lParam);
  1057. }
  1058. break;
  1059. case PGMP_RECALCSIZE:
  1060. _OnReCalcSize();
  1061. break;
  1062. case PGM_FORWARDMOUSE:
  1063. // forward mouse messages
  1064. _fForwardMouseMsgs = BOOLIFY(wParam);
  1065. break;
  1066. case PGM_SETBKCOLOR:
  1067. {
  1068. COLORREF clr = _clrBk;
  1069. if ((COLORREF) lParam == CLR_DEFAULT)
  1070. _clrBk = g_clrBtnFace;
  1071. else
  1072. _clrBk = (COLORREF)lParam;
  1073. _fBkColorSet = TRUE;
  1074. CCInvalidateFrame(ci.hwnd);
  1075. //Force a paint
  1076. RedrawWindow(ci.hwnd, NULL,NULL,RDW_INVALIDATE|RDW_ERASE);
  1077. return clr;
  1078. }
  1079. case PGM_GETBKCOLOR:
  1080. return (LRESULT)_clrBk;
  1081. case PGM_SETBORDER:
  1082. return _OnSetBorder((int)lParam);
  1083. case PGM_GETBORDER:
  1084. return (LRESULT)_iBorder;
  1085. case PGM_SETPOS:
  1086. _OnSetPos((int)lParam);
  1087. break;
  1088. case PGM_GETPOS:
  1089. return _OnGetPos();
  1090. case PGM_SETBUTTONSIZE:
  1091. return _OnSetButtonSize((int)lParam);
  1092. case PGM_GETBUTTONSIZE:
  1093. return _GetButtonSize();
  1094. case PGM_GETBUTTONSTATE:
  1095. return _GetButtonState((int)lParam);
  1096. case WM_PRINT:
  1097. return _OnPrint((HDC)wParam, (UINT)lParam);
  1098. case WM_NCHITTEST:
  1099. {
  1100. POINT pt;
  1101. pt.x = GET_X_LPARAM(lParam);
  1102. pt.y = GET_Y_LPARAM(lParam);
  1103. if (_HitTestScreen(&pt) == -1)
  1104. return HTTRANSPARENT;
  1105. return HTCLIENT;
  1106. }
  1107. case WM_SYSCOLORCHANGE:
  1108. if (!_fBkColorSet)
  1109. {
  1110. InitGlobalColors();
  1111. _clrBk = g_clrBtnFace;
  1112. CCInvalidateFrame(ci.hwnd);
  1113. }
  1114. break;
  1115. case WM_SETFOCUS:
  1116. SetFocus(_hwndChild);
  1117. return 0;
  1118. case WM_LBUTTONDOWN:
  1119. //Fall Through
  1120. case WM_LBUTTONUP:
  1121. if(!(ci.style & PGS_AUTOSCROLL)) {
  1122. _OnLButtonChange(uMsg,lParam);
  1123. }
  1124. break;
  1125. case WM_MOUSEMOVE:
  1126. // Only forward if the point is within the client rect of pager.
  1127. if (_fForwardMouseMsgs && _hwndChild)
  1128. {
  1129. POINT pt;
  1130. RECT rcClient;
  1131. // BUGBUG (scotth): cache this
  1132. GetClientRect(ci.hwnd, &rcClient);
  1133. pt.x = GET_X_LPARAM(lParam);
  1134. pt.y = GET_Y_LPARAM(lParam);
  1135. // Is this point in our client rect?
  1136. if (PtInRect(&rcClient, pt))
  1137. {
  1138. // Yes; then convert coords and forward it
  1139. pt.x += _ptPos.x;
  1140. pt.y += _ptPos.y;
  1141. SendMessage(_hwndChild, WM_MOUSEMOVE, wParam, MAKELPARAM(pt.x, pt.y));
  1142. }
  1143. }
  1144. _OnMouseMove(wParam,lParam);
  1145. break;
  1146. case WM_MOUSELEAVE :
  1147. _OnMouseLeave();
  1148. break;
  1149. case WM_ERASEBKGND:
  1150. {
  1151. LRESULT lres = CCForwardEraseBackground(ci.hwnd, (HDC) wParam);
  1152. if (_iBorder) {
  1153. // paint the borders
  1154. RECT rc;
  1155. RECT rc2;
  1156. GetClientRect(ci.hwnd, &rc);
  1157. rc2 = rc;
  1158. if( ci.style & PGS_HORZ ) {
  1159. FlipRect(&rc2);
  1160. }
  1161. rc2.right = rc2.left + _iBorder + 1;
  1162. if( ci.style & PGS_HORZ ) {
  1163. FlipRect(&rc2);
  1164. }
  1165. FillRectClr((HDC)wParam, &rc2, _clrBk);
  1166. rc2 = rc;
  1167. if( ci.style & PGS_HORZ ) {
  1168. FlipRect(&rc2);
  1169. }
  1170. rc2.left = rc2.right - _iBorder - 1;
  1171. if( ci.style & PGS_HORZ ) {
  1172. FlipRect(&rc2);
  1173. }
  1174. FillRectClr((HDC)wParam, &rc2, _clrBk);
  1175. }
  1176. return TRUE;
  1177. }
  1178. case WM_TIMER:
  1179. _OnTimer((UINT)wParam);
  1180. return 0;
  1181. case WM_SETTINGCHANGE:
  1182. InitGlobalMetrics(wParam);
  1183. _iButtonSize = (int) g_cxScrollbar * 3 / 4;
  1184. if (_iButtonSize < MINBUTTONSIZE) {
  1185. _iButtonSize = MINBUTTONSIZE;
  1186. }
  1187. break;
  1188. case WM_DESTROY:
  1189. if (_hDragProxy)
  1190. DestroyDragProxy(_hDragProxy);
  1191. break;
  1192. }
  1193. return CControl::v_WndProc(hwnd, uMsg, wParam, lParam);
  1194. }
  1195. //---------------------------------------------------------------------------------------
  1196. // call with cyCh == 0 to specify auto vsizing
  1197. BOOL DrawChar(HDC hdc, LPRECT lprc, UINT wState, TCHAR ch, UINT cyCh, BOOL fAlwaysGrayed, BOOL fTopAlign)
  1198. {
  1199. COLORREF rgb;
  1200. BOOL fDrawDisabled = !fAlwaysGrayed && (wState & DCHF_INACTIVE);
  1201. BOOL fDrawPushed = wState & DCHF_PUSHED;
  1202. // Bad UI to have a pushed disabled button
  1203. ASSERT (!fDrawDisabled || !fDrawPushed);
  1204. RECT rc = *lprc;
  1205. UINT uFormat = DT_CENTER | DT_SINGLELINE;
  1206. if (fAlwaysGrayed)
  1207. rgb = g_clrBtnShadow;
  1208. else if (fDrawDisabled)
  1209. rgb = g_clrBtnHighlight;
  1210. else
  1211. rgb = g_clrBtnText;
  1212. rgb = SetTextColor(hdc, rgb);
  1213. if (cyCh)
  1214. {
  1215. if (fTopAlign)
  1216. rc.bottom = rc.top + cyCh;
  1217. else
  1218. {
  1219. rc.top += ((RECTHEIGHT(rc) - cyCh) / 2);
  1220. rc.bottom = rc.top + cyCh;
  1221. }
  1222. uFormat |= DT_BOTTOM;
  1223. }
  1224. else
  1225. uFormat |= DT_VCENTER;
  1226. if (fDrawDisabled || fDrawPushed)
  1227. OffsetRect(&rc, 1, 1);
  1228. DrawText(hdc, &ch, 1, &rc, uFormat);
  1229. if (fDrawDisabled)
  1230. {
  1231. OffsetRect(&rc, -1, -1);
  1232. SetTextColor(hdc, g_clrBtnShadow);
  1233. DrawText(hdc, &ch, 1, &rc, uFormat);
  1234. }
  1235. SetTextColor(hdc, rgb);
  1236. return(TRUE);
  1237. }
  1238. void DrawBlankButton(HDC hdc, LPRECT lprc, DWORD wControlState)
  1239. {
  1240. BOOL fAdjusted;
  1241. if (wControlState & (DCHF_HOT | DCHF_PUSHED) &&
  1242. !(wControlState & DCHF_NOBORDER)) {
  1243. COLORSCHEME clrsc;
  1244. clrsc.dwSize = 1;
  1245. if (GetBkColor(hdc) == g_clrBtnShadow) {
  1246. clrsc.clrBtnHighlight = g_clrBtnHighlight;
  1247. clrsc.clrBtnShadow = g_clrBtnText;
  1248. } else
  1249. clrsc.clrBtnHighlight = clrsc.clrBtnShadow = CLR_DEFAULT;
  1250. // if button is both DCHF_HOT and DCHF_PUSHED, DCHF_HOT wins here
  1251. CCDrawEdge(hdc, lprc, (wControlState & DCHF_HOT) ? BDR_RAISEDINNER : BDR_SUNKENOUTER,
  1252. (UINT) (BF_ADJUST | BF_RECT), &clrsc);
  1253. fAdjusted = TRUE;
  1254. } else {
  1255. fAdjusted = FALSE;
  1256. }
  1257. if (!(wControlState & DCHF_TRANSPARENT))
  1258. FillRectClr(hdc, lprc, GetBkColor(hdc));
  1259. if (!fAdjusted)
  1260. InflateRect(lprc, -g_cxBorder, -g_cyBorder);
  1261. }
  1262. //---------------------------------------------------------------------------------------
  1263. void DrawCharButton(HDC hdc, LPRECT lprc, UINT wControlState, TCHAR ch, UINT cyCh, BOOL fAlwaysGrayed, BOOL fTopAlign)
  1264. {
  1265. RECT rc;
  1266. CopyRect(&rc, lprc);
  1267. DrawBlankButton(hdc, &rc, wControlState);
  1268. if ((RECTWIDTH(rc) <= 0) || (RECTHEIGHT(rc) <= 0))
  1269. return;
  1270. int iOldBk = SetBkMode(hdc, TRANSPARENT);
  1271. DrawChar(hdc, &rc, wControlState, ch, cyCh, fAlwaysGrayed, fTopAlign);
  1272. SetBkMode(hdc, iOldBk);
  1273. }
  1274. // --------------------------------------------------------------------------------------
  1275. //
  1276. // DrawScrollArrow
  1277. //
  1278. // --------------------------------------------------------------------------------------
  1279. void DrawScrollArrow(HDC hdc, LPRECT lprc, UINT wControlState)
  1280. {
  1281. #define szfnMarlett TEXT("MARLETT")
  1282. TCHAR ch = (wControlState & DCHF_HORIZONTAL) ? TEXT('3') : TEXT('5');
  1283. //
  1284. // Flip the direction arrow in case of a RTL mirrored DC,
  1285. // since it won't be flipped automatically (textout!)
  1286. //
  1287. if (IS_DC_RTL_MIRRORED(hdc) && (wControlState & DCHF_HORIZONTAL))
  1288. wControlState ^= DCHF_FLIPPED;
  1289. LONG lMin = min(RECTWIDTH(*lprc), RECTHEIGHT(*lprc)) - (2 * g_cxBorder); // g_cxBorder fudge notches font size down
  1290. HFONT hFont = CreateFont(lMin, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, szfnMarlett);
  1291. HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
  1292. if (wControlState & DCHF_FLIPPED)
  1293. ch++;
  1294. DrawCharButton(hdc, lprc, wControlState, ch, 0, FALSE, FALSE);
  1295. SelectObject(hdc, hOldFont);
  1296. DeleteObject(hFont);
  1297. }
  1298. //---------------------------------------------------------------------------------------
  1299. #define CX_EDGE 2
  1300. #define CX_LGEDGE 4
  1301. #define CX_INCREMENT 1
  1302. #define CX_DECREMENT (-CX_INCREMENT)
  1303. #define MIDPOINT(x1, x2) ((x1 + x2) / 2)
  1304. #define CHEVRON_WIDTH(dSeg) (4 * dSeg)
  1305. void DrawChevron(HDC hdc, LPRECT lprc, DWORD dwFlags)
  1306. {
  1307. RECT rc;
  1308. CopyRect(&rc, lprc);
  1309. // draw the border and background
  1310. DrawBlankButton(hdc, &rc, dwFlags);
  1311. // offset the arrow if pushed
  1312. if (dwFlags & DCHF_PUSHED)
  1313. OffsetRect(&rc, CX_INCREMENT, CX_INCREMENT);
  1314. // draw the arrow
  1315. HBRUSH hbrSave = SelectBrush(hdc, GetSysColorBrush(COLOR_BTNTEXT));
  1316. int dSeg = (dwFlags & DCHF_LARGE) ? CX_LGEDGE : CX_EDGE;
  1317. if (dwFlags & DCHF_HORIZONTAL)
  1318. {
  1319. // horizontal arrow
  1320. int x = MIDPOINT(rc.left, rc.right - CHEVRON_WIDTH(dSeg));
  1321. int yBase;
  1322. if (dwFlags & DCHF_TOPALIGN)
  1323. yBase = rc.top + dSeg + (2 * CX_EDGE);
  1324. else
  1325. yBase = MIDPOINT(rc.top, rc.bottom);
  1326. for (int y = -dSeg; y <= dSeg; y++)
  1327. {
  1328. PatBlt(hdc, x, yBase + y, dSeg, CX_INCREMENT, PATCOPY);
  1329. PatBlt(hdc, x + (dSeg * 2), yBase + y, dSeg, CX_INCREMENT, PATCOPY);
  1330. x += (y < 0) ? CX_INCREMENT : CX_DECREMENT;
  1331. }
  1332. }
  1333. else
  1334. {
  1335. // vertical arrow
  1336. int y = rc.top + CX_INCREMENT;
  1337. int xBase = MIDPOINT(rc.left, rc.right);
  1338. for (int x = -dSeg; x <= dSeg; x++)
  1339. {
  1340. PatBlt(hdc, xBase + x, y, CX_INCREMENT, dSeg, PATCOPY);
  1341. PatBlt(hdc, xBase + x, y + (dSeg * 2), CX_INCREMENT, dSeg, PATCOPY);
  1342. y += (x < 0) ? CX_INCREMENT : CX_DECREMENT;
  1343. }
  1344. }
  1345. // clean up
  1346. SelectBrush(hdc, hbrSave);
  1347. }