Source code of Windows XP (NT5)
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.

812 lines
25 KiB

  1. /** FILE: arrow.c ********** Module Header ********************************
  2. *
  3. * Control panel utility library routines for managing "cpArrow" window
  4. * class/spinner controls used in applet dialogs.
  5. *
  6. * History:
  7. * 15:30 on Thur 25 Apr 1991 -by- Steve Cathcart [stevecat]
  8. * Took base code from Win 3.1 source
  9. * 10:30 on Tues 04 Feb 1992 -by- Steve Cathcart [stevecat]
  10. * Updated code to latest Win 3.1 sources
  11. * 12:00 on Fri 07 Aug 1992 -by- Steve Cathcart [stevecat]
  12. * Implemented new drawing scheme for spinner/arrow control
  13. *
  14. * Copyright (C) 1990-1992 Microsoft Corporation
  15. *
  16. *************************************************************************/
  17. //==========================================================================
  18. // Include files
  19. //==========================================================================
  20. // C Runtime
  21. #include <stddef.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <windows.h>
  25. #include <windowsx.h>
  26. // Application specific
  27. #include "ups.h"
  28. //==========================================================================
  29. // Local Definitions
  30. //==========================================================================
  31. // Offsets to use with GetWindowLong
  32. #define GWL_SPINNERSTATE 0
  33. // Control state flags.
  34. #define SPINNERSTATE_GRAYED 0x0001
  35. #define SPINNERSTATE_HIDDEN 0x0002
  36. #define SPINNERSTATE_MOUSEOUT 0x0004
  37. #define SPINNERSTATE_UPCLICK 0x0008
  38. #define SPINNERSTATE_DOWNCLICK 0x0010
  39. // Combination of click states.
  40. #define SPINNERSTATE_CLICKED (SPINNERSTATE_UPCLICK | SPINNERSTATE_DOWNCLICK)
  41. // Combination of state flags.
  42. #define SPINNERSTATE_ALL 0x001F
  43. // Sinner Control color indices
  44. #define SPINNERCOLOR_FACE 0
  45. #define SPINNERCOLOR_ARROW 1
  46. #define SPINNERCOLOR_SHADOW 2
  47. #define SPINNERCOLOR_HIGHLIGHT 3
  48. #define SPINNERCOLOR_FRAME 4
  49. #define CCOLORS 5
  50. //==========================================================================
  51. // External Declarations
  52. //==========================================================================
  53. //==========================================================================
  54. // Local Data Declarations
  55. //==========================================================================
  56. /*
  57. * Macros to change the control state given the state flag(s)
  58. */
  59. #define StateSet(dwState, wFlags) (dwState |= (wFlags))
  60. #define StateClear(dwState, wFlags) (dwState &= ~(wFlags))
  61. #define StateTest(dwState, wFlags) (dwState & (wFlags))
  62. //Array of default colors, matching the order of SPINNERCOLOR_* values.
  63. DWORD rgColorDef[CCOLORS]={
  64. COLOR_BTNFACE, // SPINNERCOLOR_FACE
  65. COLOR_BTNTEXT, // SPINNERCOLOR_ARROW
  66. COLOR_BTNSHADOW, // SPINNERCOLOR_SHADOW
  67. COLOR_BTNHIGHLIGHT, // SPINNERCOLOR_HIGHLIGHT
  68. COLOR_WINDOWFRAME // SPINNERCOLOR_FRAME
  69. };
  70. BOOL bArrowTimed = FALSE;
  71. BOOL bRight;
  72. HANDLE hParent;
  73. //==========================================================================
  74. // Local Function Prototypes
  75. //==========================================================================
  76. void Draw3DButtonRect (HDC hDC, HPEN hPenHigh, HPEN hPenShadow, int x1,
  77. int y1, int x2, int y2, BOOL fClicked);
  78. LONG SpinnerPaint (HWND hWnd, DWORD dwSpinnerState);
  79. //==========================================================================
  80. // Functions
  81. //==========================================================================
  82. /*BOOL OddArrowWindow(HWND hArrowWnd)
  83. {
  84. HWND hParent;
  85. RECT rResize;
  86. BOOL bResize;
  87. // [stevecat] NULL this out for testing new drawing scheme 8/7/92
  88. return(TRUE);
  89. #ifdef OLD_CODE
  90. GetWindowRect(hArrowWnd, (LPRECT) &rResize);
  91. if (!(bResize = (rResize.right - rResize.left) % 2))
  92. {
  93. rResize.right++;
  94. ScreenToClient(hParent = GetParent(hArrowWnd), (LPPOINT) & rResize.left);
  95. ScreenToClient(hParent, (LPPOINT) & rResize.right);
  96. MoveWindow(hArrowWnd, rResize.left, rResize.top,
  97. (rResize.right - rResize.left),
  98. (rResize.bottom - rResize.top), FALSE);
  99. }
  100. return(bResize);
  101. #endif // OLD_CODE
  102. }
  103. */
  104. VOID ArrowTimerProc(HWND hWnd, UINT wMsg, UINT_PTR nID, DWORD dwTime)
  105. {
  106. WORD wScroll;
  107. DWORD dwSpinnerState;
  108. dwSpinnerState = (DWORD) GetWindowLong (hWnd, GWL_SPINNERSTATE);
  109. if (StateTest(dwSpinnerState, SPINNERSTATE_CLICKED))
  110. {
  111. wScroll = (StateTest(dwSpinnerState, SPINNERSTATE_DOWNCLICK)) ?
  112. SB_LINEDOWN : SB_LINEUP;
  113. if (bRight == WM_RBUTTONDOWN)
  114. wScroll += SB_PAGEUP - SB_LINEUP;
  115. SendMessage(hParent, WM_VSCROLL,
  116. MAKELONG(wScroll, GetWindowLong(hWnd, GWL_ID)),
  117. (LPARAM) hWnd);
  118. }
  119. // Don't need to call KillTimer(), because SetTimer will
  120. // reset the right one
  121. SetTimer(hWnd, nID, 50, ArrowTimerProc);
  122. return ;
  123. wMsg = wMsg;
  124. dwTime = dwTime;
  125. }
  126. /*
  127. * ClickedRectCalc
  128. *
  129. * Description:
  130. * Calculates the rectangle of the clicked region based on the
  131. * state flags SPINNERSTATE_UPCLICK and SPINNERSTATE_DOWNCLICK.
  132. *
  133. * Parameter:
  134. * hWnd HWND handle to the control window.
  135. * lpRect LPRECT rectangle structure to fill.
  136. *
  137. * Return Value:
  138. * void
  139. *
  140. */
  141. void ClickedRectCalc(HWND hWnd, DWORD dwState, LPRECT lpRect)
  142. {
  143. int cx, cy;
  144. GetClientRect (hWnd, lpRect);
  145. cx = lpRect->right >> 1;
  146. cy = lpRect->bottom >> 1;
  147. if (StateTest(dwState, SPINNERSTATE_DOWNCLICK))
  148. lpRect->top = cy;
  149. else
  150. lpRect->bottom = 1+cy;
  151. return;
  152. }
  153. /*
  154. * ArrowControlProc
  155. *
  156. * Description:
  157. *
  158. * Window Procedure for the Spinner/Arrow custom control. Handles all
  159. * messages like WM_PAINT just as a normal application window would.
  160. * State information about the control is maintained ALL drawing is
  161. * handled during WM_PAINT message processing.
  162. *
  163. */
  164. LRESULT APIENTRY ArrowControlProc(HWND hArrow, UINT message, WPARAM wParam, LPARAM lParam)
  165. {
  166. WORD wScroll;
  167. POINT pt;
  168. RECT rect;
  169. int x, y;
  170. int cy;
  171. DWORD dwSpinnerState, dwState;
  172. dwSpinnerState = (DWORD) GetWindowLong (hArrow, GWL_SPINNERSTATE);
  173. switch (message)
  174. {
  175. case WM_CREATE:
  176. dwSpinnerState = 0;
  177. SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  178. break;
  179. case WM_ENABLE:
  180. // Handles disabling/enabling case. Example of a
  181. // change-state-and-repaint strategy since we let the
  182. // painting code take care of the visuals.
  183. if (wParam)
  184. StateClear(dwSpinnerState, SPINNERSTATE_GRAYED);
  185. else
  186. StateSet(dwSpinnerState, SPINNERSTATE_GRAYED);
  187. SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  188. // Force a repaint since the control will look different.
  189. InvalidateRect (hArrow, NULL, TRUE);
  190. UpdateWindow (hArrow);
  191. break;
  192. case WM_SHOWWINDOW:
  193. // Set or clear the hidden flag. Windows will
  194. // automatically force a repaint if we become visible.
  195. if (wParam)
  196. StateClear(dwSpinnerState, SPINNERSTATE_HIDDEN);
  197. else
  198. StateSet(dwSpinnerState, SPINNERSTATE_HIDDEN);
  199. SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  200. break;
  201. case WM_CANCELMODE:
  202. // IMPORTANT MESSAGE! WM_CANCELMODE means that a
  203. // dialog or some other modal process has started.
  204. // we must make sure that we cancel any clicked state
  205. // we are in, kill the timers, and release the capture.
  206. StateClear(dwSpinnerState, SPINNERSTATE_CLICKED);
  207. if (bArrowTimed)
  208. {
  209. SendMessage (hParent, WM_VSCROLL, MAKELONG(SB_ENDSCROLL,
  210. GetWindowLong (hArrow, GWL_ID)), (LPARAM) hArrow);
  211. KillTimer (hArrow, GetWindowLong (hArrow, GWL_ID));
  212. bArrowTimed = FALSE;
  213. }
  214. ReleaseCapture();
  215. break;
  216. case WM_RBUTTONDOWN:
  217. case WM_LBUTTONDOWN:
  218. // When we get a mouse down message, we know that the mouse
  219. // is over the control. We then do the following steps
  220. // to set up the new state:
  221. // 1. Hit-test the coordinates of the click to
  222. // determine in which half the click occurred.
  223. // 2. Set the appropriate SPINNERSTATE_*CLICK state
  224. // and repaint that clicked half. This is another
  225. // example of a change-state-and-repaint strategy.
  226. // 3. Send an initial scroll message.
  227. // 4. Set the mouse capture.
  228. // 5. Set the initial delay timer before repeating
  229. // the scroll message.
  230. if (bRight)
  231. break;
  232. bRight = message;
  233. hParent = GetParent (hArrow);
  234. // Get the mouse coordinates.
  235. x = GET_X_LPARAM(lParam);
  236. y = GET_Y_LPARAM(lParam);
  237. // Only need to hit-test the upper half
  238. // Then change-state-and-repaint
  239. GetClientRect (hArrow, &rect);
  240. cy = rect.bottom >> 1;
  241. if (y > cy)
  242. {
  243. StateSet(dwSpinnerState, SPINNERSTATE_DOWNCLICK);
  244. rect.top = cy;
  245. wScroll = SB_LINEDOWN;
  246. }
  247. else
  248. {
  249. StateSet(dwSpinnerState, SPINNERSTATE_UPCLICK);
  250. rect.bottom = cy + 1;
  251. wScroll = SB_LINEUP;
  252. }
  253. SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  254. InvalidateRect (hArrow, &rect, TRUE);
  255. UpdateWindow (hArrow);
  256. SetCapture (hArrow);
  257. // Process SHIFT key state along with button message
  258. if (wParam & MK_SHIFT)
  259. {
  260. if (message != WM_RBUTTONDOWN)
  261. wScroll += (WORD) (SB_TOP - SB_LINEUP);
  262. else
  263. wScroll += (WORD) (SB_THUMBPOSITION - SB_LINEUP);
  264. }
  265. else
  266. {
  267. if (message == WM_RBUTTONDOWN)
  268. wScroll += SB_PAGEUP - SB_LINEUP;
  269. bArrowTimed = SetTimer (hArrow, GetWindowLong (hArrow, GWL_ID),
  270. 200, ArrowTimerProc) != 0;
  271. }
  272. SendMessage (hParent, WM_VSCROLL, MAKELONG(wScroll,
  273. GetWindowLong (hArrow, GWL_ID)), (LONG_PTR) hArrow);
  274. break;
  275. case WM_MOUSEMOVE:
  276. // On WM_MOUSEMOVE messages we want to know if the mouse
  277. // has moved out of the control when the control is in
  278. // a clicked state. If the control has not been clicked,
  279. // then we have nothing to do. Otherwise we want to set
  280. // the SPINNERSTATE_MOUSEOUT flag and repaint so the button
  281. // visually comes up.
  282. if (!StateTest(dwSpinnerState, SPINNERSTATE_CLICKED))
  283. break;
  284. // Save copy of original state
  285. dwState = dwSpinnerState;
  286. // Get the mouse coordinates.
  287. pt.x = GET_X_LPARAM(lParam);
  288. pt.y = GET_Y_LPARAM(lParam);
  289. // Get the area we originally clicked and the new POINT
  290. ClickedRectCalc (hArrow, dwSpinnerState, &rect);
  291. // Hit-Test the rectange and change the state if necessary.
  292. if (PtInRect(&rect, pt))
  293. StateClear(dwSpinnerState, SPINNERSTATE_MOUSEOUT);
  294. else
  295. StateSet(dwSpinnerState, SPINNERSTATE_MOUSEOUT);
  296. SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  297. // If the state changed, repaint the appropriate part of
  298. // the control.
  299. if (dwState != dwSpinnerState)
  300. {
  301. InvalidateRect (hArrow, &rect, TRUE);
  302. UpdateWindow (hArrow);
  303. }
  304. break;
  305. case WM_LBUTTONUP:
  306. case WM_RBUTTONUP:
  307. // A mouse button up event is much like WM_CANCELMODE since
  308. // we have to clean out whatever state the control is in:
  309. // 1. Kill any repeat timers we might have created.
  310. // 2. Release the mouse capture.
  311. // 3. Clear the clicked states and repaint, another example
  312. // of a change-state-and-repaint strategy.
  313. if ((UINT) (bRight - WM_LBUTTONDOWN + WM_LBUTTONUP) == message)
  314. {
  315. bRight = 0;
  316. ReleaseCapture();
  317. if (bArrowTimed)
  318. {
  319. SendMessage (hParent, WM_VSCROLL, MAKELONG(SB_ENDSCROLL,
  320. GetWindowLong (hArrow, GWL_ID)), (LPARAM) hArrow);
  321. KillTimer (hArrow, GetWindowLong (hArrow, GWL_ID));
  322. bArrowTimed = FALSE;
  323. }
  324. // Repaint if necessary, only if we are clicked AND the mouse
  325. // is still in the boundaries of the control.
  326. if (StateTest(dwSpinnerState, SPINNERSTATE_CLICKED) &&
  327. StateTest(dwSpinnerState, ~SPINNERSTATE_MOUSEOUT))
  328. {
  329. // Calculate the rectangle before clearing states.
  330. ClickedRectCalc (hArrow, dwSpinnerState, &rect);
  331. // Clear the states so we repaint properly.
  332. StateClear(dwSpinnerState, SPINNERSTATE_CLICKED | SPINNERSTATE_MOUSEOUT);
  333. SetWindowLong (hArrow, GWL_SPINNERSTATE, (LONG) dwSpinnerState);
  334. InvalidateRect (hArrow, &rect, TRUE);
  335. UpdateWindow (hArrow);
  336. }
  337. }
  338. break;
  339. case WM_PAINT:
  340. return SpinnerPaint (hArrow, dwSpinnerState);
  341. default:
  342. return (DefWindowProc (hArrow, message, wParam, lParam));
  343. break;
  344. }
  345. return(0L);
  346. }
  347. /*
  348. * SpinnerPaint
  349. *
  350. * Description:
  351. *
  352. * Handles all WM_PAINT messages for the control and paints
  353. * the control for the current state, whether it be clicked
  354. * or disabled.
  355. *
  356. * Parameters:
  357. * hWnd HWND Handle to the control.
  358. * dwSpinnerState DWORD Spinner control status flags
  359. *
  360. * Return Value:
  361. * LONG 0L.
  362. */
  363. LONG SpinnerPaint (HWND hWnd, DWORD dwSpinnerState)
  364. {
  365. PAINTSTRUCT ps;
  366. LPRECT lpRect;
  367. RECT rect;
  368. HDC hDC;
  369. COLORREF rgCr[CCOLORS];
  370. HPEN rgHPen[CCOLORS];
  371. int iColor;
  372. HBRUSH hBrushArrow;
  373. HBRUSH hBrushFace;
  374. HBRUSH hBrushBlack;
  375. POINT rgpt1[3];
  376. POINT rgpt2[3];
  377. int xAdd1=0, yAdd1=0;
  378. int xAdd2=0, yAdd2=0;
  379. int cx, cy; // Whole dimensions
  380. int cx2, cy2; // Half dimensions
  381. int cx4, cy4; // Quarter dimensions
  382. lpRect = &rect;
  383. hDC = BeginPaint (hWnd, &ps);
  384. GetClientRect (hWnd, lpRect);
  385. // Get colors that we'll need. We do not want to cache these
  386. // items since we may our top-level parent window may have
  387. // received a WM_WININICHANGE message at which time the control
  388. // is repainted. Since this control never sees that message,
  389. // we cannot assume that colors will remain the same throughout
  390. // the life of the control.
  391. for (iColor = 0; iColor < CCOLORS; iColor++)
  392. {
  393. rgCr[iColor] = GetSysColor (rgColorDef[iColor]);
  394. rgHPen[iColor] = CreatePen (PS_SOLID, 1, rgCr[iColor]);
  395. }
  396. hBrushFace = CreateSolidBrush (rgCr[SPINNERCOLOR_FACE]);
  397. if (hBrushFace)
  398. {
  399. hBrushArrow = CreateSolidBrush (rgCr[SPINNERCOLOR_ARROW]);
  400. if (hBrushArrow)
  401. {
  402. hBrushBlack = GetStockObject (BLACK_BRUSH);
  403. if (hBrushBlack)
  404. {
  405. // These values are extremely cheap to calculate for the amount
  406. // we are going to use them.
  407. cx = lpRect->right - lpRect->left;
  408. cy = lpRect->bottom - lpRect->top;
  409. cx2 = cx >> 1;
  410. cy2 = cy >> 1;
  411. cx4 = cx2 >> 1;
  412. cy4 = cy2 >> 1;
  413. // If one half is depressed, set the x/yAdd varaibles that we use
  414. // to shift the small arrow image down and right.
  415. if (!StateTest(dwSpinnerState, SPINNERSTATE_MOUSEOUT))
  416. {
  417. if (StateTest(dwSpinnerState, SPINNERSTATE_UPCLICK))
  418. {
  419. xAdd1 = 1;
  420. yAdd1 = 1;
  421. }
  422. else if (StateTest(dwSpinnerState, SPINNERSTATE_DOWNCLICK))
  423. {
  424. xAdd2 = 1;
  425. yAdd2 = 1;
  426. }
  427. }
  428. // Draw the face color and the outer frame
  429. SelectObject (hDC, hBrushFace);
  430. SelectObject (hDC, rgHPen[SPINNERCOLOR_FRAME]);
  431. Rectangle (hDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
  432. // Draw the horizontal center line.
  433. MoveToEx (hDC, 0, cy2, NULL);
  434. LineTo (hDC, cx, cy2);
  435. // We do one of three modifications for drawing the borders:
  436. // 1) Both halves un-clicked.
  437. // 2) Top clicked, bottom unclicked.
  438. // 3) Top unclicked, bottom clicked.
  439. //
  440. // Case 1 is xAdd1==xAdd2==0
  441. // Case 2 is xAdd1==1, xAdd2=0
  442. // Case 3 is xAdd1==0, xAdd2==1
  443. // Draw top and bottom buttons borders.
  444. Draw3DButtonRect (hDC, rgHPen[SPINNERCOLOR_HIGHLIGHT],
  445. rgHPen[SPINNERCOLOR_SHADOW],
  446. 0, 0, cx-1, cy2, (BOOL) xAdd1);
  447. Draw3DButtonRect (hDC, rgHPen[SPINNERCOLOR_HIGHLIGHT],
  448. rgHPen[SPINNERCOLOR_SHADOW],
  449. 0, cy2, cx-1, cy-1, (BOOL) xAdd2);
  450. // Select default line color.
  451. SelectObject (hDC, rgHPen[SPINNERCOLOR_ARROW]);
  452. // Draw the arrows depending on the enable state.
  453. if (StateTest (dwSpinnerState, SPINNERSTATE_GRAYED))
  454. {
  455. // Draw arrow color lines in the upper left of the
  456. // top arrow and on the top of the bottom arrow.
  457. // Pen was already selected as a default.
  458. MoveToEx (hDC, cx2, cy4-2, NULL); //Top arrow
  459. LineTo (hDC, cx2-3, cy4+1);
  460. MoveToEx (hDC, cx2-3, cy2+cy4-2, NULL); //Bottom arrow
  461. LineTo (hDC, cx2+3, cy2+cy4-2);
  462. // Draw highlight color lines in the bottom of the
  463. // top arrow and on the lower right of the bottom arrow.
  464. SelectObject (hDC, rgHPen[SPINNERCOLOR_HIGHLIGHT]);
  465. MoveToEx (hDC, cx2-3, cy4+1, NULL); //Top arrow
  466. LineTo (hDC, cx2+3, cy4+1);
  467. MoveToEx (hDC, cx2+3, cy2+cy4-2, NULL); //Bottom arrow
  468. LineTo (hDC, cx2, cy2+cy4+1);
  469. SetPixel (hDC, cx2, cy2+cy4+1, rgCr[SPINNERCOLOR_HIGHLIGHT]);
  470. }
  471. else
  472. {
  473. // Top arrow polygon
  474. rgpt1[0].x = xAdd1 + cx2;
  475. rgpt1[0].y = yAdd1 + cy4 - 2;
  476. rgpt1[1].x = xAdd1 + cx2 - 3;
  477. rgpt1[1].y = yAdd1 + cy4 + 1;
  478. rgpt1[2].x = xAdd1 + cx2 + 3;
  479. rgpt1[2].y = yAdd1 + cy4 + 1;
  480. // Bottom arrow polygon
  481. rgpt2[0].x = xAdd2 + cx2;
  482. rgpt2[0].y = yAdd2 + cy2 + cy4 + 1;
  483. rgpt2[1].x = xAdd2 + cx2 - 3;
  484. rgpt2[1].y = yAdd2 + cy2 + cy4 - 2;
  485. rgpt2[2].x = xAdd2 + cx2 + 3;
  486. rgpt2[2].y = yAdd2 + cy2 + cy4 - 2;
  487. // Draw the arrows
  488. SelectObject (hDC, hBrushArrow);
  489. Polygon (hDC, (LPPOINT)rgpt1, 3);
  490. Polygon (hDC, (LPPOINT)rgpt2, 3);
  491. }
  492. // Clean up
  493. EndPaint(hWnd, &ps);
  494. }
  495. DeleteObject (hBrushArrow);
  496. }
  497. DeleteObject (hBrushFace);
  498. }
  499. for (iColor = 0; iColor < CCOLORS; iColor++)
  500. {
  501. if (rgHPen[iColor])
  502. DeleteObject (rgHPen[iColor]);
  503. }
  504. return 0L;
  505. }
  506. /*
  507. * Draw3DButtonRect
  508. *
  509. * Description:
  510. * Draws the 3D button look within a given rectangle. This rectangle
  511. * is assumed to be bounded by a one pixel black border, so everything
  512. * is bumped in by one.
  513. *
  514. * Parameters:
  515. * hDC DC to draw to.
  516. * hPenHigh HPEN highlight color pen.
  517. * hPenShadow HPEN shadow color pen.
  518. * x1 int Upper left corner x.
  519. * y1 int Upper left corner y.
  520. * x2 int Lower right corner x.
  521. * y2 int Lower right corner y.
  522. * fClicked BOOL specifies if the button is down or not (TRUE==DOWN)
  523. *
  524. * Return Value:
  525. * void
  526. *
  527. */
  528. void Draw3DButtonRect (HDC hDC, HPEN hPenHigh, HPEN hPenShadow, int x1,
  529. int y1, int x2, int y2, BOOL fClicked)
  530. {
  531. HPEN hPenOrg;
  532. // Shrink the rectangle to account for borders.
  533. x1+=1;
  534. x2-=1;
  535. y1+=1;
  536. y2-=1;
  537. hPenOrg = SelectObject (hDC, hPenShadow);
  538. if (fClicked)
  539. {
  540. // Shadow on left and top edge when clicked.
  541. MoveToEx (hDC, x1, y2, NULL);
  542. LineTo (hDC, x1, y1);
  543. LineTo (hDC, x2+1, y1);
  544. }
  545. else
  546. {
  547. // Lowest shadow line.
  548. MoveToEx (hDC, x1, y2, NULL);
  549. LineTo (hDC, x2, y2);
  550. LineTo (hDC, x2, y1-1);
  551. // Upper shadow line.
  552. MoveToEx (hDC, x1+1, y2-1, NULL);
  553. LineTo (hDC, x2-1, y2-1);
  554. LineTo (hDC, x2-1, y1);
  555. SelectObject (hDC, hPenHigh);
  556. // Upper highlight line.
  557. MoveToEx (hDC, x1, y2-1, NULL);
  558. LineTo (hDC, x1, y1);
  559. LineTo (hDC, x2, y1);
  560. }
  561. if (hPenOrg)
  562. SelectObject (hDC, hPenOrg);
  563. return;
  564. }
  565. BOOL RegisterArrowClass (HANDLE hModule)
  566. {
  567. WNDCLASS wcArrow;
  568. wcArrow.lpszClassName = "upsArrow";
  569. wcArrow.hInstance = hModule;
  570. wcArrow.lpfnWndProc = ArrowControlProc;
  571. wcArrow.hCursor = LoadCursor(NULL, IDC_ARROW);
  572. wcArrow.hIcon = NULL;
  573. wcArrow.lpszMenuName = NULL;
  574. wcArrow.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  575. wcArrow.style = CS_HREDRAW | CS_VREDRAW;
  576. wcArrow.cbClsExtra = 0;
  577. wcArrow.cbWndExtra = sizeof(DWORD);
  578. return(RegisterClass((LPWNDCLASS) &wcArrow));
  579. }
  580. VOID UnRegisterArrowClass (HANDLE hModule)
  581. {
  582. UnregisterClass("upsArrow", hModule);
  583. }
  584. /*
  585. short ArrowVScrollProc(wScroll, nCurrent, lpAVS)
  586. wScroll is an SB_* message
  587. nCurrent is the base value to change
  588. lpAVS is a far pointer to the structure containing change amounts
  589. and limits to be used, along with a flags location for errors
  590. returns a short value of the final amount
  591. the flags element in the lpAVS struct is
  592. 0 if no problems found
  593. OVERFLOW set if the change exceeded upper limit (limit is returned)
  594. UNDERFLOW set if the change exceeded lower limit (limit is returned)
  595. UNKNOWNCOMMAND set if wScroll is not a known SB_* message
  596. NOTE: Only one of OVERFLOW or UNDERFLOW may be set. If you send in values
  597. that would allow both to be set, that's your problem. Either can
  598. be set in combination with UNKNOWNCOMMAND (when the command is not
  599. known and the input value is out of bounds).
  600. */
  601. short ArrowVScrollProc(short wScroll, short nCurrent, LPARROWVSCROLL lpAVS)
  602. {
  603. short nDelta;
  604. /* Find the message and put the relative change in nDelta. If the
  605. message is an absolute change, put 0 in nDelta and set nCurrent
  606. to the value specified. If the command is unknown, set error
  607. flag, set nDelta to 0, and proceed through checks.
  608. */
  609. switch (wScroll)
  610. {
  611. case SB_LINEUP:
  612. nDelta = lpAVS->lineup;
  613. break;
  614. case SB_LINEDOWN:
  615. nDelta = lpAVS->linedown;
  616. break;
  617. case SB_PAGEUP:
  618. nDelta = lpAVS->pageup;
  619. break;
  620. case SB_PAGEDOWN:
  621. nDelta = lpAVS->pagedown;
  622. break;
  623. case SB_TOP:
  624. nCurrent = lpAVS->top;
  625. nDelta = 0;
  626. break;
  627. case SB_BOTTOM:
  628. nCurrent = lpAVS->bottom;
  629. nDelta = 0;
  630. break;
  631. case SB_THUMBTRACK:
  632. nCurrent = lpAVS->thumbtrack;
  633. nDelta = 0;
  634. break;
  635. case SB_THUMBPOSITION:
  636. nCurrent = lpAVS->thumbpos;
  637. nDelta = 0;
  638. break;
  639. case SB_ENDSCROLL:
  640. nDelta = 0;
  641. break;
  642. default:
  643. lpAVS->flags = UNKNOWNCOMMAND;
  644. nDelta = 0;
  645. break;
  646. }
  647. if (nCurrent + nDelta > lpAVS->top)
  648. {
  649. nCurrent = lpAVS->top;
  650. nDelta = 0;
  651. lpAVS->flags = OVERFLOW;
  652. }
  653. else if (nCurrent + nDelta < lpAVS->bottom)
  654. {
  655. nCurrent = lpAVS->bottom;
  656. nDelta = 0;
  657. lpAVS->flags = UNDERFLOW;
  658. }
  659. else
  660. lpAVS->flags = 0;
  661. return(nCurrent + nDelta);
  662. }