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.

1785 lines
46 KiB

  1. #include "ctlspriv.h"
  2. /////////////////////////////////////////////////////////////////////////////
  3. //
  4. // updown.c : A micro-scrollbar control; useful for increment/decrement.
  5. //
  6. /////////////////////////////////////////////////////////////////////////////
  7. #define NUM_UDACCELS 3
  8. #define DONTCARE 0
  9. #define SIGNED 1
  10. #define UNSIGNED 2
  11. #define UD_HITNOWHERE 0
  12. #define UD_HITDOWN 1
  13. #define UD_HITUP 2
  14. typedef struct {
  15. CCONTROLINFO ci;
  16. HWND hwndBuddy;
  17. unsigned fUp : 1;
  18. unsigned fDown : 1;
  19. unsigned fUnsigned : 1;
  20. unsigned fSharedBorder : 1;
  21. unsigned fSunkenBorder : 1;
  22. unsigned fUpDownDestroyed : 1; // This tells the buddy that updown destoryed.
  23. BOOL fTrackSet: 1;
  24. unsigned fSubclassed:1; // did we subclass the buddy?
  25. UINT nBase;
  26. int nUpper;
  27. int nLower;
  28. int nPos;
  29. UINT uClass;
  30. BOOL bDown;
  31. DWORD dwStart;
  32. UINT nAccel;
  33. UDACCEL *udAccel;
  34. UINT uHot;
  35. int cReenterSetint; // To avoid recursion death in setint()
  36. HTHEME hTheme;
  37. HTHEME hThemeBuddy;
  38. } UDSTATE, *PUDSTATE;
  39. // Constants:
  40. //
  41. #define CLASS_UNKNOWN 0
  42. #define CLASS_EDIT 1
  43. #define CLASS_LISTBOX 2
  44. #define MAX_INTLENGTH 18 // big enough for all intl stuff, too
  45. // this is the space to the left and right of the arrow (in pixels)
  46. #define XBORDER 0
  47. #define BASE_DECIMAL 10
  48. #define BASE_HEX 16
  49. #define CURSORMAX 1300
  50. // Declarations:
  51. //
  52. LRESULT CALLBACK ArrowKeyProc(HWND hWnd, UINT uMsg, WPARAM wParam,
  53. LPARAM lParam, UINT_PTR uIdSubclass, ULONG_PTR dwRefData);
  54. /////////////////////////////////////////////////////////////////////////////
  55. //
  56. // ***** Internal workhorses *****
  57. //
  58. // Validates the buddy.
  59. //
  60. void isgoodbuddy(PUDSTATE np)
  61. {
  62. if (!np->hwndBuddy)
  63. return;
  64. if (!IsWindow(np->hwndBuddy))
  65. {
  66. np->hwndBuddy = NULL;
  67. np->uClass = CLASS_UNKNOWN;
  68. }
  69. if (GetParent(np->hwndBuddy) != np->ci.hwndParent)
  70. {
  71. np->hwndBuddy = NULL;
  72. np->uClass = CLASS_UNKNOWN;
  73. }
  74. }
  75. // Picks a good buddy.
  76. //
  77. void pickbuddy(PUDSTATE np)
  78. {
  79. if (np->ci.style & UDS_AUTOBUDDY)
  80. np->hwndBuddy = GetWindow(np->ci.hwnd, GW_HWNDPREV);
  81. }
  82. void unachor(PUDSTATE np)
  83. {
  84. RECT rc;
  85. RECT rcBuddy;
  86. RECT rcUD;
  87. if ( np->hwndBuddy && (np->ci.style & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT))) {
  88. GetWindowRect(np->hwndBuddy, &rcBuddy);
  89. GetWindowRect(np->ci.hwnd, &rcUD);
  90. UnionRect(&rc, &rcUD, &rcBuddy);
  91. MapWindowRect(NULL, np->ci.hwndParent, &rc);
  92. MoveWindow(np->hwndBuddy, rc.left, rc.top,
  93. rc.right - rc.left, rc.bottom - rc.top, FALSE);
  94. }
  95. }
  96. // Anchor this control to the buddy's edge, if appropriate.
  97. //
  98. void anchor(PUDSTATE np)
  99. {
  100. BOOL bAlignToBuddy;
  101. int nOver = 0, nHasBorder;
  102. RECT rc, rcBuddy;
  103. int nHeight, nWidth;
  104. np->fSharedBorder = FALSE;
  105. isgoodbuddy(np);
  106. nHasBorder = (np->ci.style & WS_BORDER) == WS_BORDER;
  107. bAlignToBuddy = np->hwndBuddy && (np->ci.style & (UDS_ALIGNLEFT | UDS_ALIGNRIGHT));
  108. if (bAlignToBuddy)
  109. {
  110. if ((np->uClass == CLASS_EDIT) ||
  111. (GetWindowLong(np->hwndBuddy, GWL_EXSTYLE) & WS_EX_CLIENTEDGE))
  112. {
  113. np->fSunkenBorder = TRUE;
  114. }
  115. GetWindowRect(np->hwndBuddy, &rc);
  116. if ((np->uClass == CLASS_EDIT) || (GetWindowLong(np->hwndBuddy, GWL_STYLE) & WS_BORDER))
  117. {
  118. // FEATURE: for full generalization, should handle border AND clientedge
  119. nOver = g_cxBorder * (np->fSunkenBorder ? 2 : 1);
  120. np->fSharedBorder = TRUE;
  121. // turn off border styles...
  122. np->ci.style &= ~WS_BORDER;
  123. SetWindowLong(np->ci.hwnd, GWL_STYLE, np->ci.style);
  124. SetWindowLong(np->ci.hwnd, GWL_EXSTYLE, GetWindowLong(np->ci.hwnd, GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE));
  125. }
  126. }
  127. else
  128. {
  129. GetWindowRect(np->ci.hwnd, &rc);
  130. }
  131. nHeight = rc.bottom - rc.top;
  132. nWidth = rc.right - rc.left;
  133. //
  134. // If the parent is RTL mirrored, then placement of the
  135. // child (i.e. anchor point) should be relative to the visual
  136. // right edge (near edge). [samera]
  137. //
  138. if (IS_WINDOW_RTL_MIRRORED(np->ci.hwndParent))
  139. {
  140. rc.left = rc.right;
  141. }
  142. ScreenToClient(np->ci.hwndParent, (LPPOINT)&rc.left);
  143. rc.right = rc.left + nWidth;
  144. if (bAlignToBuddy)
  145. {
  146. nWidth = g_cxVScroll - g_cxBorder;
  147. if (nWidth > nHeight) { // don't let the aspect ratio
  148. nWidth = nHeight; // get worse than square
  149. }
  150. nWidth += nOver;
  151. rcBuddy = rc;
  152. if (np->ci.style & UDS_ALIGNLEFT)
  153. {
  154. // size buddy to right
  155. rcBuddy.left += nWidth - nOver;
  156. rc.right = rc.left + nWidth;
  157. }
  158. else
  159. {
  160. // size buddy to left
  161. rcBuddy.right -= nWidth - nOver;
  162. rc.left = rc.right - nWidth;
  163. }
  164. // size the buddy to fit the updown on the appropriate side
  165. MoveWindow(np->hwndBuddy, rcBuddy.left, rcBuddy.top,
  166. rcBuddy.right - rcBuddy.left, nHeight, TRUE);
  167. }
  168. else if (!(np->ci.style & UDS_HORZ))
  169. {
  170. nWidth = g_cxVScroll + 2 * nHasBorder;
  171. }
  172. SetWindowPos(np->ci.hwnd, NULL, rc.left, rc.top, nWidth, nHeight,
  173. SWP_DRAWFRAME | SWP_NOZORDER | SWP_NOACTIVATE);
  174. }
  175. // Use this to make any and all comparisons involving the nPos,
  176. // nUpper or nLower fields of the PUDSTATE. It determines
  177. // whether to do a signed or unsigned comparison and returns
  178. // > 0 for (x > y)
  179. // < 0 for (x < y)
  180. // == 0 for (x == y).
  181. //
  182. // fCompareType is SIGNED to force a signed comparison,
  183. // fCompareType is UNSIGNED to force an unsigned comparison,
  184. // fCompareType is DONTCARE to use the np->fUnsigned flag to decide.
  185. //
  186. // In comments, comparison operators are suffixed with "D", "U" or "S"
  187. // to emphasize whether the comparison is DONTCARE, UNSIGNED, or SIGNED.
  188. // For example "x <U y" means "x < y as UNSIGNED".
  189. int compare(PUDSTATE np, int x, int y, UINT fCompareType)
  190. {
  191. if ((fCompareType == UNSIGNED) || ((np->fUnsigned) && !(fCompareType == SIGNED)) )
  192. {
  193. // Do unsigned comparisons
  194. if ((UINT)x > (UINT)y)
  195. return 1;
  196. else if ((UINT)x < (UINT)y)
  197. return -1;
  198. }
  199. else
  200. {
  201. // Do signed comparisons
  202. if (x > y)
  203. return 1;
  204. else if (x < y)
  205. return -1;
  206. }
  207. return 0;
  208. }
  209. // Use this after any pos change to make sure pos stays in range.
  210. // Wraps as necessary.
  211. // returns nonzero if the current value was out of range (and therefore
  212. // got changed so it fit into range again)
  213. //
  214. BOOL nudge(PUDSTATE np)
  215. {
  216. BOOL bOutOfRange = TRUE;
  217. int min = np->nUpper;
  218. int max = np->nLower;
  219. // if (max <D min) swap(min, max)
  220. if (compare(np,max,min, DONTCARE) < 0)
  221. {
  222. int t;
  223. t = min;
  224. min = max;
  225. max = t;
  226. }
  227. if (np->ci.style & UDS_WRAP)
  228. {
  229. // if (nPos <D min) nPos = max -- wrap from below to above
  230. // else if (nPos >D max) nPos = min -- wrap from above to below
  231. if ((compare(np, np->nPos, min, DONTCARE) < 0))
  232. np->nPos = max;
  233. else if ((compare(np, np->nPos, max, DONTCARE) > 0))
  234. np->nPos = min;
  235. else bOutOfRange = FALSE;
  236. }
  237. else
  238. {
  239. // if (nPos <D min) nPos = min -- pin at min
  240. // else if (nPos >D max) nPos = max -- pin at max
  241. if (compare(np,np->nPos,min, DONTCARE) < 0)
  242. np->nPos = min;
  243. else if (compare(np,np->nPos,max, DONTCARE) > 0)
  244. np->nPos = max;
  245. else
  246. bOutOfRange = FALSE;
  247. }
  248. return(bOutOfRange);
  249. }
  250. // Sets the state of the buttons (pushed, released).
  251. //
  252. void squish(PUDSTATE np, UINT bTop, UINT bBottom)
  253. {
  254. BOOL bInvalidate = FALSE;
  255. if (np->nUpper == np->nLower || !IsWindowEnabled(np->ci.hwnd))
  256. {
  257. bTop = FALSE;
  258. bBottom = FALSE;
  259. }
  260. else
  261. {
  262. bTop = !!bTop;
  263. bBottom = !!bBottom;
  264. }
  265. if (np->fUp != bTop)
  266. {
  267. np->fUp = bTop;
  268. bInvalidate = TRUE;
  269. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, np->ci.hwnd, OBJID_CLIENT, 1);
  270. }
  271. if (np->fDown != bBottom)
  272. {
  273. np->fDown = bBottom;
  274. bInvalidate = TRUE;
  275. NotifyWinEvent(EVENT_OBJECT_STATECHANGE, np->ci.hwnd, OBJID_CLIENT, 2);
  276. }
  277. if (bInvalidate)
  278. {
  279. np->dwStart = GetTickCount();
  280. InvalidateRect(np->ci.hwnd, NULL, FALSE);
  281. }
  282. }
  283. // Gets the intl 1000 separator
  284. //
  285. void getthousands(LPTSTR pszThousand)
  286. {
  287. if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, pszThousand, 2))
  288. {
  289. pszThousand[0] = TEXT(',');
  290. pszThousand[1] = TEXT('\0');
  291. }
  292. }
  293. //
  294. // Obtain NLS info about how numbers should be grouped.
  295. //
  296. // The annoying thing is that LOCALE_SGROUPING and NUMBERFORMAT
  297. // have different ways of specifying number grouping.
  298. //
  299. // LOCALE NUMBERFMT Sample Country
  300. //
  301. // 3;0 3 1,234,567 United States
  302. // 3;2;0 32 12,34,567 India
  303. // 3 30 1234,567 ??
  304. //
  305. // Not my idea. That's the way it works.
  306. //
  307. // Bonus treat - Win9x doesn't support complex number formats,
  308. // so we return only the first number.
  309. //
  310. UINT getgrouping(void)
  311. {
  312. UINT grouping;
  313. LPTSTR psz;
  314. TCHAR szGrouping[32];
  315. // If no locale info, then assume Western style thousands
  316. if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szGrouping, ARRAYSIZE(szGrouping)))
  317. return 3;
  318. grouping = 0;
  319. psz = szGrouping;
  320. for (;;)
  321. {
  322. if (*psz == '0') break; // zero - stop
  323. else if ((UINT)(*psz - '0') < 10) // digit - accumulate it
  324. grouping = grouping * 10 + (UINT)(*psz - '0');
  325. else if (*psz) // punctuation - ignore it
  326. { }
  327. else // end of string, no "0" found
  328. {
  329. grouping = grouping * 10; // put zero on end (see examples)
  330. break; // and finished
  331. }
  332. psz++;
  333. }
  334. return grouping;
  335. }
  336. // Gets the caption of the buddy
  337. // Returns the current position of the updown control
  338. // and sets *pfError on error.
  339. //
  340. LRESULT getint(PUDSTATE np, BOOL *pfError)
  341. {
  342. TCHAR szInt[MAX_INTLENGTH]; // big enough for all intl stuff, too
  343. TCHAR szThousand[2];
  344. TCHAR cTemp;
  345. int nPos;
  346. int sign = 1;
  347. LPTSTR p = szInt;
  348. BOOL bInValid = TRUE;
  349. isgoodbuddy(np);
  350. if (np->hwndBuddy && np->ci.style & UDS_SETBUDDYINT)
  351. {
  352. if (np->uClass == CLASS_LISTBOX)
  353. {
  354. np->nPos = (int)SendMessage(np->hwndBuddy, LB_GETCURSEL, 0, 0L);
  355. bInValid = nudge(np);
  356. }
  357. else
  358. {
  359. GetWindowText(np->hwndBuddy, szInt, ARRAYSIZE(szInt));
  360. switch (np->nBase)
  361. {
  362. case BASE_HEX:
  363. if ((*p == TEXT('x')) || (*p == TEXT('X')))
  364. // ignore first character
  365. p++;
  366. else if ((*p == TEXT('0')) && ((*(p + 1) == TEXT('x')) || (*(p + 1) == TEXT('X'))))
  367. // ignore first two characters (TEXT("0x") or "0X")
  368. p += 2;
  369. for (nPos = 0; *p; p++)
  370. {
  371. if ((*p >= TEXT('A')) && (*p <= TEXT('F')))
  372. cTemp = (TCHAR)(*p - TEXT('A') + 10);
  373. else if ((*p >= TEXT('a')) && (*p <= TEXT('f')))
  374. cTemp = (TCHAR)(*p - TEXT('a') + 10);
  375. else if ((*p >= TEXT('0')) && (*p <= TEXT('9')))
  376. cTemp = (TCHAR)(*p - TEXT('0'));
  377. else
  378. goto BadValue;
  379. nPos = (nPos * 16) + cTemp;
  380. }
  381. np->nPos = nPos;
  382. break;
  383. case BASE_DECIMAL:
  384. default:
  385. getthousands(szThousand);
  386. if (*p == TEXT('-') && !np->fUnsigned)
  387. {
  388. sign = -1;
  389. ++p;
  390. }
  391. for (nPos=0; *p; p++)
  392. {
  393. cTemp = *p;
  394. // If there is a thousand separator, just ignore it.
  395. // Do not validate that it's in the right place,
  396. // because it prevents the user from editing the
  397. // middle of a number.
  398. if (cTemp == szThousand[0])
  399. {
  400. continue;
  401. }
  402. cTemp -= TEXT('0');
  403. if ((UINT)cTemp > 9)
  404. {
  405. goto BadValue;
  406. }
  407. nPos = (nPos*10) + cTemp;
  408. }
  409. np->nPos = nPos*sign;
  410. break;
  411. }
  412. bInValid = nudge(np);
  413. }
  414. }
  415. BadValue:
  416. if (pfError)
  417. *pfError = bInValid;
  418. return np->nPos;
  419. }
  420. // Sets the caption of the buddy if appropriate.
  421. //
  422. void setint(PUDSTATE np)
  423. {
  424. TCHAR szInt[MAX_INTLENGTH];
  425. TCHAR szThousand[2];
  426. int pos = np->nPos;
  427. isgoodbuddy(np);
  428. if (np->hwndBuddy && np->ci.style & UDS_SETBUDDYINT)
  429. {
  430. BOOL fError;
  431. /*
  432. * If we have reentered, then maybe the app has set up a loop.
  433. * Check to see if the value has actually changed. If not,
  434. * then there's no need to set it again. This breaks the
  435. * recursion.
  436. */
  437. if (np->cReenterSetint && (LRESULT)pos==getint(np, &fError) && !fError)
  438. {
  439. return;
  440. }
  441. np->nPos = pos;
  442. np->cReenterSetint++;
  443. if (np->uClass == CLASS_LISTBOX)
  444. {
  445. SendMessage(np->hwndBuddy, LB_SETCURSEL, pos, 0L);
  446. FORWARD_WM_COMMAND(GetParent(np->hwndBuddy),
  447. GetDlgCtrlID(np->hwndBuddy),
  448. np->hwndBuddy, LBN_SELCHANGE, SendMessage);
  449. }
  450. else
  451. {
  452. switch (np->nBase)
  453. {
  454. case BASE_HEX:
  455. if ((np->nUpper | np->nLower) >= 0x00010000)
  456. {
  457. StringCchPrintf(szInt, ARRAYSIZE(szInt), TEXT("0x%08X"), pos);
  458. }
  459. else
  460. {
  461. StringCchPrintf(szInt, ARRAYSIZE(szInt), TEXT("0x%04X"), pos);
  462. }
  463. break;
  464. case BASE_DECIMAL:
  465. default:
  466. {
  467. LPTSTR pszInt = szInt;
  468. if (pos < 0 && !np->fUnsigned)
  469. {
  470. *pszInt++ = TEXT('-');
  471. pos = -pos;
  472. }
  473. if (pos >= 1000 && !(np->ci.style & UDS_NOTHOUSANDS))
  474. {
  475. TCHAR szFmt[MAX_INTLENGTH];
  476. NUMBERFMT nf;
  477. nf.NumDigits = 0; // no digits after decimal point
  478. nf.LeadingZero = 0; // no leading zeros
  479. nf.Grouping = getgrouping();
  480. nf.lpDecimalSep = TEXT(""); // no decimal point
  481. nf.lpThousandSep = szThousand;
  482. nf.NegativeOrder = 0; // (not used - we always pass positive numbers)
  483. getthousands(szThousand);
  484. StringCchPrintf(szFmt, ARRAYSIZE(szFmt), TEXT("%u"), pos);
  485. GetNumberFormat(LOCALE_USER_DEFAULT,
  486. 0,
  487. szFmt,
  488. &nf,
  489. pszInt,
  490. ARRAYSIZE(szInt) - ((pszInt == szInt) ? 0 : 1));
  491. }
  492. else
  493. {
  494. StringCchPrintf(pszInt,
  495. ARRAYSIZE(szInt) - ((pszInt == szInt) ? 0 : 1),
  496. TEXT("%u"),
  497. pos);
  498. }
  499. break;
  500. }
  501. }
  502. SetWindowText(np->hwndBuddy, szInt);
  503. }
  504. np->cReenterSetint;
  505. }
  506. }
  507. // Use this to click the pos up or down by one.
  508. //
  509. void bump(PUDSTATE np)
  510. {
  511. BOOL bChanged = FALSE;
  512. UINT uElapsed, increment;
  513. int direction, i;
  514. /* So I'm not really getting seconds here; it's close enough, and
  515. * dividing by 1024 keeps __aFuldiv from being needed.
  516. */
  517. uElapsed = (UINT)((GetTickCount() - np->dwStart) / 1024);
  518. if (np->udAccel != NULL)
  519. {
  520. increment = np->udAccel[0].nInc;
  521. for (i=np->nAccel-1; i>=0; --i)
  522. {
  523. if (np->udAccel[i].nSec <= uElapsed)
  524. {
  525. increment = np->udAccel[i].nInc;
  526. break;
  527. }
  528. }
  529. }
  530. else
  531. {
  532. increment = 1;
  533. }
  534. if (increment == 0)
  535. {
  536. DebugMsg(DM_ERROR, TEXT("bad accelerator value"));
  537. return;
  538. }
  539. direction = compare(np,np->nUpper,np->nLower, DONTCARE) < 0 ? -1 : 1;
  540. if (np->fUp)
  541. {
  542. bChanged = TRUE;
  543. }
  544. if (np->fDown)
  545. {
  546. direction = -direction;
  547. bChanged = TRUE;
  548. }
  549. if (bChanged)
  550. {
  551. /* Make sure we have a multiple of the increment
  552. * Note that we should loop only when the increment changes
  553. */
  554. NM_UPDOWN nm;
  555. nm.iPos = np->nPos;
  556. nm.iDelta = increment*direction;
  557. if (CCSendNotify(&np->ci, UDN_DELTAPOS, &nm.hdr))
  558. return;
  559. np->nPos += nm.iDelta;
  560. for ( ; ; )
  561. {
  562. if (!((int)np->nPos % (int)increment))
  563. {
  564. break;
  565. }
  566. np->nPos += direction;
  567. }
  568. nudge(np);
  569. setint(np);
  570. if (np->ci.style & UDS_HORZ)
  571. FORWARD_WM_HSCROLL(np->ci.hwndParent, np->ci.hwnd, SB_THUMBPOSITION, np->nPos, SendMessage);
  572. else
  573. FORWARD_WM_VSCROLL(np->ci.hwndParent, np->ci.hwnd, SB_THUMBPOSITION, np->nPos, SendMessage);
  574. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, np->ci.hwnd, OBJID_CLIENT, 0);
  575. }
  576. }
  577. //#pragma data_seg(DATASEG_READONLY)
  578. const TCHAR c_szListbox[] = TEXT("listbox");
  579. //#pragma data_seg()
  580. // Sets the new buddy
  581. //
  582. LRESULT setbuddy(PUDSTATE np, HWND hwndBuddy)
  583. {
  584. HWND hOldBuddy;
  585. TCHAR szClName[10];
  586. hOldBuddy = np->hwndBuddy;
  587. if (np->hThemeBuddy)
  588. {
  589. CloseThemeData(np->hThemeBuddy);
  590. np->hThemeBuddy = NULL;
  591. }
  592. if ((np->hwndBuddy = hwndBuddy) == NULL)
  593. {
  594. pickbuddy(np);
  595. hwndBuddy = np->hwndBuddy;
  596. }
  597. if ((hOldBuddy != hwndBuddy) && np->fSubclassed)
  598. {
  599. ASSERT(hOldBuddy);
  600. RemoveWindowSubclass(hOldBuddy, ArrowKeyProc, 0);
  601. np->fSubclassed = FALSE;
  602. }
  603. np->uClass = CLASS_UNKNOWN;
  604. if (hwndBuddy)
  605. {
  606. if (np->ci.style & UDS_ARROWKEYS)
  607. {
  608. np->fSubclassed = TRUE;
  609. SetWindowSubclass(hwndBuddy, ArrowKeyProc, 0, (ULONG_PTR)np);
  610. }
  611. GetClassName(hwndBuddy, szClName, ARRAYSIZE(szClName));
  612. if (!lstrcmpi(szClName, c_szEdit))
  613. {
  614. np->uClass = CLASS_EDIT;
  615. np->hThemeBuddy = OpenThemeData(hwndBuddy, WC_EDIT);
  616. }
  617. else if (!lstrcmpi(szClName, c_szListbox))
  618. {
  619. np->uClass = CLASS_LISTBOX;
  620. }
  621. }
  622. anchor(np);
  623. return (LRESULT)hOldBuddy;
  624. }
  625. //
  626. // This is how CCThemeDrawEdge should be implemented once DrawThemeLine supports part and
  627. // state ids
  628. //
  629. //
  630. BOOL UpDown_ThemeDrawEdge(HTHEME hTheme, HDC hdc, PRECT prc, int iPartId, int iStateId, UINT uFlags)
  631. {
  632. BOOL fRet = FALSE;
  633. RECT rc;
  634. int cxBorder, cyBorder;
  635. if (SUCCEEDED(GetThemeInt(hTheme, iPartId, iStateId, TMT_SIZINGBORDERWIDTH, &cxBorder)))
  636. {
  637. cyBorder = cxBorder;
  638. }
  639. else
  640. {
  641. cxBorder = g_cxBorder;
  642. cyBorder = g_cyBorder;
  643. }
  644. rc = *prc;
  645. if (uFlags & BF_LEFT)
  646. {
  647. rc.left += cxBorder;
  648. }
  649. if (uFlags & BF_TOP)
  650. {
  651. rc.top += cyBorder;
  652. }
  653. if (uFlags & BF_RIGHT)
  654. {
  655. rc.right -= cxBorder;
  656. }
  657. if (uFlags & BF_BOTTOM)
  658. {
  659. rc.bottom -= cyBorder;
  660. }
  661. ExcludeClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  662. if (SUCCEEDED(DrawThemeBackground(hTheme, hdc, iPartId, iStateId, prc, 0)))
  663. {
  664. fRet = TRUE;
  665. if (uFlags & BF_ADJUST)
  666. {
  667. *prc = rc;
  668. }
  669. }
  670. SelectClipRgn(hdc, NULL);
  671. return fRet;
  672. }
  673. // Paint the whole control
  674. //
  675. // PaintUpDownControl is theme aware
  676. void PaintUpDownControl(PUDSTATE np, HDC hdc)
  677. {
  678. UINT uFlags;
  679. PAINTSTRUCT ps;
  680. RECT rcBtn;
  681. RECT rc;
  682. int iPartId;
  683. int iStateId;
  684. BOOL bEnabled = (np->nUpper != np->nLower) && IsWindowEnabled(np->ci.hwnd);
  685. if (np->hwndBuddy)
  686. bEnabled = bEnabled && IsWindowEnabled(np->hwndBuddy);
  687. if (hdc)
  688. ps.hdc = hdc;
  689. else
  690. BeginPaint(np->ci.hwnd, &ps);
  691. GetClientRect(np->ci.hwnd, &rcBtn);
  692. // if we are autobuddy'd and anchored to a sunken-edge control, we draw the
  693. // "nonclient" area of ourselves to blend in with our buddy.
  694. if (!np->hTheme || (np->hThemeBuddy && (np->uClass == CLASS_EDIT)))
  695. {
  696. if (np->fSharedBorder && np->fSunkenBorder)
  697. {
  698. UINT bf = BF_TOP | BF_BOTTOM | BF_ADJUST |
  699. (np->ci.style & UDS_ALIGNLEFT ? BF_LEFT : 0) |
  700. (np->ci.style & UDS_ALIGNRIGHT ? BF_RIGHT : 0);
  701. if (!np->hThemeBuddy)
  702. {
  703. DrawEdge(ps.hdc, &rcBtn, EDGE_SUNKEN, bf);
  704. }
  705. else
  706. {
  707. UpDown_ThemeDrawEdge(np->hThemeBuddy,
  708. ps.hdc,
  709. &rcBtn,
  710. EP_EDITTEXT,
  711. bEnabled ? ETS_NORMAL : ETS_DISABLED,
  712. bf);
  713. }
  714. }
  715. }
  716. // with remaining space, draw appropriate scrollbar arrow controls in
  717. // upper and lower halves
  718. rc = rcBtn;
  719. if (np->ci.style & UDS_HORZ)
  720. {
  721. iPartId = SPNP_DOWNHORZ; // Down horizontal
  722. iStateId = DNHZS_NORMAL;
  723. uFlags = DFCS_SCROLLLEFT;
  724. if (np->fDown)
  725. {
  726. uFlags |= DFCS_PUSHED;
  727. iStateId = DNHZS_PRESSED;
  728. }
  729. if (!bEnabled)
  730. {
  731. uFlags |= DFCS_INACTIVE;
  732. iStateId = DNHZS_DISABLED;
  733. }
  734. if (np->uHot == UD_HITDOWN)
  735. {
  736. uFlags |= DFCS_HOT;
  737. if (iStateId == DNHZS_NORMAL)
  738. iStateId = DNHZS_HOT;
  739. }
  740. // Horizontal ones
  741. rc.right = (rcBtn.right + rcBtn.left) / 2;
  742. if (np->hTheme)
  743. {
  744. DrawThemeBackground(np->hTheme, ps.hdc, iPartId, iStateId, &rc, 0);
  745. }
  746. else
  747. {
  748. DrawFrameControl(ps.hdc, &rc, DFC_SCROLL,
  749. uFlags);
  750. }
  751. iPartId = SPNP_UPHORZ; // Up horizontal
  752. iStateId = UPHZS_NORMAL;
  753. uFlags = DFCS_SCROLLRIGHT;
  754. if (np->fUp)
  755. {
  756. uFlags |= DFCS_PUSHED;
  757. iStateId = UPHZS_PRESSED;
  758. }
  759. if (!bEnabled)
  760. {
  761. uFlags |= DFCS_INACTIVE;
  762. iStateId = UPHZS_DISABLED;
  763. }
  764. if (np->uHot == UD_HITUP)
  765. {
  766. uFlags |= DFCS_HOT;
  767. if (iStateId == UPHZS_NORMAL)
  768. iStateId = UPHZS_HOT;
  769. }
  770. rc.left = rcBtn.right - (rc.right - rc.left); // handles odd-x case, too
  771. rc.right = rcBtn.right;
  772. if (np->hTheme)
  773. {
  774. DrawThemeBackground(np->hTheme, ps.hdc, iPartId, iStateId, &rc, 0);
  775. }
  776. else
  777. {
  778. DrawFrameControl(ps.hdc, &rc, DFC_SCROLL, uFlags);
  779. }
  780. }
  781. else
  782. {
  783. iPartId = SPNP_UP; // Up vertical
  784. iStateId = UPS_NORMAL;
  785. uFlags = DFCS_SCROLLUP;
  786. if (np->fUp)
  787. {
  788. uFlags |= DFCS_PUSHED;
  789. iStateId = UPS_PRESSED;
  790. }
  791. if (!bEnabled)
  792. {
  793. uFlags |= DFCS_INACTIVE;
  794. iStateId = UPS_DISABLED;
  795. }
  796. if (np->uHot == UD_HITUP)
  797. {
  798. uFlags |= DFCS_HOT;
  799. if (iStateId == UPS_NORMAL)
  800. iStateId = UPS_HOT;
  801. }
  802. rc.bottom = (rcBtn.bottom + rcBtn.top) / 2;
  803. if (np->hTheme)
  804. {
  805. DrawThemeBackground(np->hTheme, ps.hdc, iPartId, iStateId, &rc, 0);
  806. }
  807. else
  808. {
  809. DrawFrameControl(ps.hdc, &rc, DFC_SCROLL, uFlags);
  810. }
  811. iPartId = SPNP_DOWN; // Down vertical
  812. iStateId = DNS_NORMAL;
  813. uFlags = DFCS_SCROLLDOWN;
  814. if (np->fDown)
  815. {
  816. uFlags |= DFCS_PUSHED;
  817. iStateId = DNS_PRESSED;
  818. }
  819. if (!bEnabled)
  820. {
  821. uFlags |= DFCS_INACTIVE;
  822. iStateId = DNS_DISABLED;
  823. }
  824. if (np->uHot == UD_HITDOWN)
  825. {
  826. uFlags |= DFCS_HOT;
  827. if (iStateId == DNS_NORMAL)
  828. iStateId = DNS_HOT;
  829. }
  830. rc.top = rcBtn.bottom - (rc.bottom - rc.top); // handles odd-y case, too
  831. rc.bottom = rcBtn.bottom;
  832. if (np->hTheme)
  833. {
  834. DrawThemeBackground(np->hTheme, ps.hdc, iPartId, iStateId, &rc, 0);
  835. }
  836. else
  837. {
  838. DrawFrameControl(ps.hdc, &rc, DFC_SCROLL,
  839. uFlags);
  840. }
  841. }
  842. if (hdc == NULL)
  843. EndPaint(np->ci.hwnd, &ps);
  844. }
  845. LRESULT CALLBACK ArrowKeyProc(HWND hWnd, UINT uMsg, WPARAM wParam,
  846. LPARAM lParam, UINT_PTR uIdSubclass, ULONG_PTR dwRefData)
  847. {
  848. PUDSTATE np = (PUDSTATE)dwRefData;
  849. int cDetants;
  850. HRGN hrgnEdit = NULL;
  851. LRESULT lResult;
  852. switch (uMsg)
  853. {
  854. case WM_NCDESTROY:
  855. RemoveWindowSubclass(hWnd, ArrowKeyProc, 0);
  856. np->fSubclassed = FALSE;
  857. np->hwndBuddy = NULL;
  858. if (np->fUpDownDestroyed)
  859. {
  860. // The buddy was destroyed after updown so free the memory now
  861. // And pass off to the message to who we subclassed...
  862. LocalFree((HLOCAL)np);
  863. }
  864. break;
  865. case WM_GETDLGCODE:
  866. return (DefSubclassProc(hWnd, uMsg, wParam, lParam) | DLGC_WANTARROWS);
  867. case WM_KEYDOWN:
  868. switch (wParam)
  869. {
  870. case VK_UP:
  871. case VK_DOWN:
  872. if (GetCapture() != np->ci.hwnd)
  873. {
  874. /* Get the value from the buddy if this is the first key down
  875. */
  876. if (!(lParam&(1L<<30)))
  877. {
  878. getint(np, NULL);
  879. }
  880. /* Update the visuals and bump the value
  881. */
  882. np->bDown = (wParam == VK_DOWN);
  883. squish(np, !np->bDown, np->bDown);
  884. bump(np);
  885. //notify of navigation key usage
  886. CCNotifyNavigationKeyUsage(&(np->ci), UISF_HIDEFOCUS);
  887. }
  888. return(0L);
  889. default:
  890. break;
  891. }
  892. break;
  893. case WM_KEYUP:
  894. switch (wParam)
  895. {
  896. case VK_UP:
  897. case VK_DOWN:
  898. if (GetCapture() != np->ci.hwnd)
  899. {
  900. squish(np, FALSE, FALSE);
  901. }
  902. return(0L);
  903. default:
  904. break;
  905. }
  906. break;
  907. case WM_KILLFOCUS:
  908. // Reset wheel scroll amount
  909. gcWheelDelta = 0;
  910. break;
  911. case WM_NCPAINT:
  912. if (np->hTheme && (np->uClass == CLASS_EDIT))
  913. {
  914. RECT rc;
  915. HRGN hrgnSpin;
  916. //
  917. // exclude the updown window rect from the edit painting region
  918. //
  919. GetWindowRect(np->ci.hwnd, &rc);
  920. hrgnSpin = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
  921. if (hrgnSpin)
  922. {
  923. switch (wParam)
  924. {
  925. case 0:
  926. case 1:
  927. //
  928. // update the entire edit nc area
  929. //
  930. GetWindowRect(hWnd, &rc);
  931. hrgnEdit = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
  932. if (!hrgnEdit)
  933. {
  934. break;
  935. }
  936. wParam = (WPARAM)hrgnEdit;
  937. // fall through
  938. default:
  939. //
  940. // exclude spin rgn from edit rgn
  941. //
  942. CombineRgn((HRGN)wParam, (HRGN)wParam, hrgnSpin, RGN_DIFF);
  943. }
  944. DeleteObject(hrgnSpin);
  945. }
  946. }
  947. break;
  948. default:
  949. if ((uMsg == g_msgMSWheel) && (GetCapture() != np->ci.hwnd))
  950. {
  951. int iWheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
  952. // Update count of scroll amount
  953. gcWheelDelta -= iWheelDelta;
  954. cDetants = gcWheelDelta / WHEEL_DELTA;
  955. if (cDetants != 0)
  956. {
  957. gcWheelDelta %= WHEEL_DELTA;
  958. if (GET_KEYSTATE_WPARAM(wParam) & (MK_SHIFT | MK_CONTROL))
  959. {
  960. break;
  961. }
  962. getint(np, NULL);
  963. np->bDown = (cDetants > 0);
  964. cDetants = abs(cDetants);
  965. while (cDetants-- > 0)
  966. {
  967. squish(np, !np->bDown, np->bDown);
  968. bump(np);
  969. }
  970. squish(np, FALSE, FALSE);
  971. }
  972. return 1;
  973. }
  974. break;
  975. }
  976. lResult = DefSubclassProc(hWnd, uMsg, wParam, lParam);
  977. if (hrgnEdit)
  978. {
  979. DeleteObject(hrgnEdit);
  980. }
  981. return lResult;
  982. }
  983. UINT setbase(PUDSTATE np, UINT wNewBase)
  984. {
  985. UINT wOldBase;
  986. switch (wNewBase)
  987. {
  988. case BASE_DECIMAL:
  989. case BASE_HEX:
  990. np->fUnsigned = (wNewBase != BASE_DECIMAL);
  991. wOldBase = np->nBase;
  992. np->nBase = wNewBase;
  993. setint(np);
  994. return wOldBase;
  995. }
  996. return 0;
  997. }
  998. /////////////////////////////////////////////////////////////////////////////
  999. HWND WINAPI CreateUpDownControl(DWORD dwStyle, int x, int y, int cx, int cy,
  1000. HWND hParent, int nID, HINSTANCE hInst,
  1001. HWND hwndBuddy, int nUpper, int nLower, int nPos)
  1002. {
  1003. HWND hWnd = CreateWindow(s_szUpdownClass, NULL, dwStyle, x, y, cx, cy,
  1004. hParent, IntToPtr_(HMENU, nID), hInst, 0L);
  1005. if (hWnd)
  1006. {
  1007. SendMessage(hWnd, UDM_SETBUDDY, (WPARAM)hwndBuddy, 0L);
  1008. SendMessage(hWnd, UDM_SETRANGE, 0, MAKELONG(nUpper, nLower));
  1009. SendMessage(hWnd, UDM_SETPOS, 0, MAKELONG(nPos, 0));
  1010. }
  1011. return hWnd;
  1012. }
  1013. UINT UD_HitTest(PUDSTATE np, int x, int y)
  1014. {
  1015. RECT rc;
  1016. GetClientRect(np->ci.hwnd, &rc);
  1017. if (np->ci.style & UDS_HORZ)
  1018. {
  1019. // Horizontal placement
  1020. if (x < (rc.right / 2))
  1021. {
  1022. return UD_HITDOWN;
  1023. }
  1024. else if (x > (rc.right / 2))
  1025. {
  1026. return UD_HITUP;
  1027. }
  1028. }
  1029. else
  1030. {
  1031. if (y > (rc.bottom / 2))
  1032. {
  1033. return UD_HITDOWN;
  1034. }
  1035. else if (y < (rc.bottom / 2))
  1036. {
  1037. return UD_HITUP;
  1038. }
  1039. }
  1040. return UD_HITNOWHERE;
  1041. }
  1042. void UD_Invalidate(PUDSTATE np, UINT uWhich, BOOL fErase)
  1043. {
  1044. int iMid;
  1045. RECT rc;
  1046. GetClientRect(np->ci.hwnd, &rc);
  1047. if (np->ci.style & UDS_HORZ)
  1048. {
  1049. iMid = rc.right / 2;
  1050. if (uWhich == UD_HITDOWN) {
  1051. rc.right = iMid;
  1052. } else if (uWhich == UD_HITUP) {
  1053. rc.left = iMid;
  1054. } else
  1055. return;
  1056. }
  1057. else
  1058. {
  1059. iMid = rc.bottom /2;
  1060. if (uWhich == UD_HITDOWN) {
  1061. rc.top = iMid;
  1062. } else if (uWhich == UD_HITUP){
  1063. rc.bottom = iMid;
  1064. } else
  1065. return;
  1066. }
  1067. InvalidateRect(np->ci.hwnd, &rc, fErase);
  1068. }
  1069. void UD_OnMouseMove(PUDSTATE np, DWORD dwPos)
  1070. {
  1071. if (np->ci.style & UDS_HOTTRACK) {
  1072. UINT uHot = UD_HitTest(np, GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos));
  1073. if (uHot != np->uHot) {
  1074. UD_Invalidate(np, np->uHot, FALSE);
  1075. UD_Invalidate(np, uHot, FALSE);
  1076. np->uHot = uHot;
  1077. }
  1078. }
  1079. }
  1080. /////////////////////////////////////////////////////////////////////////////
  1081. // UpDownWndProc:
  1082. //
  1083. // UpDownWndProc is theme aware
  1084. LRESULT CALLBACK UpDownWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1085. {
  1086. RECT rc;
  1087. int i;
  1088. BOOL f;
  1089. LRESULT lres;
  1090. PUDSTATE np = GetWindowPtr(hwnd, 0);
  1091. if (np) {
  1092. if ((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST) &&
  1093. (np->ci.style & UDS_HOTTRACK) && !np->fTrackSet) {
  1094. TRACKMOUSEEVENT tme;
  1095. np->fTrackSet = TRUE;
  1096. tme.cbSize = sizeof(tme);
  1097. tme.hwndTrack = np->ci.hwnd;
  1098. tme.dwFlags = TME_LEAVE;
  1099. TrackMouseEvent(&tme);
  1100. }
  1101. else if (uMsg == WM_THEMECHANGED) // Check for theme changes
  1102. {
  1103. if (np->hTheme)
  1104. CloseThemeData(np->hTheme);
  1105. np->hTheme = OpenThemeData(np->ci.hwnd, L"Spin");
  1106. if (np->hTheme)
  1107. {
  1108. // Ensure style is applied
  1109. np->ci.style |= UDS_HOTTRACK;
  1110. }
  1111. if (np->hThemeBuddy)
  1112. {
  1113. CloseThemeData(np->hThemeBuddy);
  1114. np->hThemeBuddy = NULL;
  1115. }
  1116. if (np->hwndBuddy && (np->uClass == CLASS_EDIT))
  1117. {
  1118. np->hThemeBuddy = OpenThemeData(np->hwndBuddy, WC_EDIT);
  1119. }
  1120. InvalidateRect(np->ci.hwnd, NULL, TRUE);
  1121. }
  1122. } else if (uMsg != WM_CREATE)
  1123. goto DoDefault;
  1124. switch (uMsg)
  1125. {
  1126. case WM_MOUSEMOVE:
  1127. UD_OnMouseMove(np, (DWORD) lParam);
  1128. break;
  1129. case WM_MOUSELEAVE:
  1130. np->fTrackSet = FALSE;
  1131. UD_Invalidate(np, np->uHot, FALSE);
  1132. np->uHot = UD_HITNOWHERE;
  1133. break;
  1134. case WM_LBUTTONDOWN:
  1135. {
  1136. // Don't set a timer if on the middle border
  1137. BOOL bTimeIt = TRUE;
  1138. if (np->hwndBuddy && !IsWindowEnabled(np->hwndBuddy))
  1139. break;
  1140. SetCapture(hwnd);
  1141. getint(np, NULL);
  1142. switch (np->uClass)
  1143. {
  1144. case CLASS_EDIT:
  1145. case CLASS_LISTBOX:
  1146. SetFocus(np->hwndBuddy);
  1147. break;
  1148. }
  1149. switch(UD_HitTest(np, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)))
  1150. {
  1151. case UD_HITDOWN:
  1152. np->bDown = TRUE;
  1153. squish(np, FALSE, TRUE);
  1154. break;
  1155. case UD_HITUP:
  1156. np->bDown = FALSE;
  1157. squish(np, TRUE, FALSE);
  1158. break;
  1159. case UD_HITNOWHERE:
  1160. bTimeIt = FALSE;
  1161. break;
  1162. }
  1163. if (bTimeIt)
  1164. {
  1165. UINT uElapse = min(GetCaretBlinkTime(), CURSORMAX);
  1166. SetTimer(hwnd, 1, uElapse, NULL);
  1167. bump(np);
  1168. }
  1169. break;
  1170. }
  1171. case WM_TIMER:
  1172. {
  1173. POINT pt;
  1174. if (GetCapture() != hwnd)
  1175. {
  1176. goto EndScroll;
  1177. }
  1178. SetTimer(hwnd, 1, 100, NULL);
  1179. GetWindowRect(hwnd, &rc);
  1180. if (np->ci.style & UDS_HORZ) {
  1181. i = (rc.left + rc.right) / 2;
  1182. if (np->bDown)
  1183. {
  1184. rc.right = i;
  1185. }
  1186. else
  1187. {
  1188. rc.left = i;
  1189. }
  1190. } else {
  1191. i = (rc.top + rc.bottom) / 2;
  1192. if (np->bDown)
  1193. {
  1194. rc.top = i;
  1195. }
  1196. else
  1197. {
  1198. rc.bottom = i;
  1199. }
  1200. }
  1201. InflateRect(&rc, (g_cxFrame+1)/2, (g_cyFrame+1)/2);
  1202. GetCursorPos(&pt);
  1203. if (PtInRect(&rc, pt))
  1204. {
  1205. squish(np, !np->bDown, np->bDown);
  1206. bump(np);
  1207. }
  1208. else
  1209. {
  1210. squish(np, FALSE, FALSE);
  1211. }
  1212. break;
  1213. }
  1214. case WM_LBUTTONUP:
  1215. if (np->hwndBuddy && !IsWindowEnabled(np->hwndBuddy))
  1216. break;
  1217. if (GetCapture() == hwnd)
  1218. {
  1219. EndScroll:
  1220. squish(np, FALSE, FALSE);
  1221. // We cannot call CCReleaseCapture() here, because it busts a lot of apps.
  1222. ReleaseCapture();
  1223. KillTimer(hwnd, 1);
  1224. if (np->uClass == CLASS_EDIT)
  1225. Edit_SetSel(np->hwndBuddy, 0, -1);
  1226. if (np->ci.style & UDS_HORZ)
  1227. FORWARD_WM_HSCROLL(np->ci.hwndParent, np->ci.hwnd,
  1228. SB_ENDSCROLL, np->nPos, SendMessage);
  1229. else
  1230. FORWARD_WM_VSCROLL(np->ci.hwndParent, np->ci.hwnd,
  1231. SB_ENDSCROLL, np->nPos, SendMessage);
  1232. }
  1233. break;
  1234. case WM_ENABLE:
  1235. InvalidateRect(hwnd, NULL, TRUE);
  1236. break;
  1237. case WM_WININICHANGE:
  1238. if (np && (!wParam ||
  1239. (wParam == SPI_SETNONCLIENTMETRICS) ||
  1240. (wParam == SPI_SETICONTITLELOGFONT))) {
  1241. InitGlobalMetrics(wParam);
  1242. unachor(np);
  1243. anchor(np);
  1244. }
  1245. break;
  1246. case WM_PRINTCLIENT:
  1247. case WM_PAINT:
  1248. PaintUpDownControl(np, (HDC)wParam);
  1249. break;
  1250. case WM_UPDATEUISTATE:
  1251. //not sure need to set bit, will probably not use it, on the other hand this
  1252. // is consistent with remaining of common controls and not very expensive
  1253. CCOnUIState(&(np->ci), WM_UPDATEUISTATE, wParam, lParam);
  1254. goto DoDefault;
  1255. case UDM_SETRANGE:
  1256. np->nUpper = GET_X_LPARAM(lParam);
  1257. np->nLower = GET_Y_LPARAM(lParam);
  1258. nudge(np);
  1259. break;
  1260. case UDM_SETRANGE32:
  1261. np->nUpper = (int)lParam;
  1262. np->nLower = (int)wParam;
  1263. break;
  1264. case UDM_GETRANGE32:
  1265. if (lParam) {
  1266. *((LPINT)lParam) = np->nUpper;
  1267. }
  1268. if (wParam) {
  1269. *((LPINT)wParam) = np->nLower;
  1270. }
  1271. break;
  1272. case UDM_GETRANGE:
  1273. return MAKELONG(np->nUpper, np->nLower);
  1274. case UDM_SETBASE:
  1275. // wParam: new base
  1276. // lParam: not used
  1277. // return: 0 if invalid base is specified,
  1278. // previous base otherwise
  1279. return (LRESULT)setbase(np, (UINT)wParam);
  1280. case UDM_GETBASE:
  1281. return np->nBase;
  1282. case UDM_SETPOS:
  1283. lParam = GET_X_LPARAM(lParam);
  1284. // FALL THROUGH
  1285. case UDM_SETPOS32:
  1286. {
  1287. int iNewPos = (int)lParam;
  1288. if (compare(np, np->nLower, np->nUpper, DONTCARE) < 0) {
  1289. if (compare(np, iNewPos, np->nUpper, DONTCARE) > 0) {
  1290. iNewPos = np->nUpper;
  1291. }
  1292. if (compare(np, iNewPos, np->nLower, DONTCARE) < 0) {
  1293. iNewPos = np->nLower;
  1294. }
  1295. } else {
  1296. if (compare(np, iNewPos, np->nUpper, DONTCARE) < 0) {
  1297. iNewPos = np->nUpper;
  1298. }
  1299. if (compare(np, iNewPos, np->nLower, DONTCARE) > 0) {
  1300. iNewPos = np->nLower;
  1301. }
  1302. }
  1303. i = np->nPos;
  1304. np->nPos = iNewPos;
  1305. setint(np);
  1306. NotifyWinEvent(EVENT_OBJECT_VALUECHANGE, np->ci.hwnd, OBJID_CLIENT, 0);
  1307. return (LRESULT)i;
  1308. }
  1309. case UDM_GETPOS:
  1310. lres = getint(np, &f);
  1311. return MAKELRESULT(lres, f);
  1312. case UDM_GETPOS32:
  1313. return getint(np, (BOOL *)lParam);
  1314. case UDM_SETBUDDY:
  1315. return setbuddy(np, (HWND)wParam);
  1316. case UDM_GETBUDDY:
  1317. return (LRESULT)np->hwndBuddy;
  1318. case UDM_SETACCEL:
  1319. if (wParam == 0)
  1320. {
  1321. return FALSE;
  1322. }
  1323. if (wParam >= NUM_UDACCELS)
  1324. {
  1325. UDACCEL *puda;
  1326. puda = (UDACCEL *)LocalReAlloc((HLOCAL)np->udAccel, sizeof(UDACCEL)*wParam, LMEM_MOVEABLE);
  1327. if (!puda)
  1328. {
  1329. return FALSE;
  1330. }
  1331. else
  1332. {
  1333. np->udAccel = puda;
  1334. }
  1335. }
  1336. if (np->udAccel != NULL)
  1337. {
  1338. np->nAccel = (UINT)wParam;
  1339. for (i = 0; i < (int)wParam; i++)
  1340. {
  1341. np->udAccel[i] = ((LPUDACCEL)lParam)[i];
  1342. }
  1343. }
  1344. return TRUE;
  1345. case UDM_GETACCEL:
  1346. if (wParam > np->nAccel)
  1347. {
  1348. wParam = np->nAccel;
  1349. }
  1350. if (np->udAccel)
  1351. {
  1352. for (i=0; i<(int)wParam; ++i)
  1353. {
  1354. ((LPUDACCEL)lParam)[i] = np->udAccel[i];
  1355. }
  1356. }
  1357. return np->nAccel;
  1358. case WM_NOTIFYFORMAT:
  1359. return CIHandleNotifyFormat(&np->ci, lParam);
  1360. case WM_CREATE:
  1361. // Allocate the instance data space.
  1362. np = (PUDSTATE)LocalAlloc(LPTR, sizeof(UDSTATE));
  1363. if (!np)
  1364. return -1;
  1365. SetWindowPtr(hwnd, 0, np);
  1366. #define lpCreate ((CREATESTRUCT *)lParam)
  1367. CIInitialize(&np->ci, hwnd, lpCreate);
  1368. np->hTheme = OpenThemeData(np->ci.hwnd, L"Spin");
  1369. // np->fUp =
  1370. // np->fDown =
  1371. // np->fUnsigned =
  1372. // np->fSharedBorder =
  1373. // np->fSunkenBorder =
  1374. // FALSE;
  1375. if (lpCreate->style & UDS_UNSIGNED)
  1376. np->fUnsigned = TRUE;
  1377. if (lpCreate->dwExStyle & WS_EX_CLIENTEDGE)
  1378. np->fSunkenBorder = TRUE;
  1379. np->nBase = BASE_DECIMAL;
  1380. np->nUpper = 0;
  1381. np->nLower = 100;
  1382. np->nPos = 0;
  1383. np->hwndBuddy = NULL;
  1384. np->uClass = CLASS_UNKNOWN;
  1385. ASSERT(np->cReenterSetint == 0);
  1386. np->udAccel = (UDACCEL *)LocalAlloc(LPTR, sizeof(UDACCEL) * NUM_UDACCELS);
  1387. if (np->udAccel)
  1388. {
  1389. np->nAccel = NUM_UDACCELS;
  1390. np->udAccel[0].nSec = 0;
  1391. np->udAccel[0].nInc = 1;
  1392. np->udAccel[1].nSec = 2;
  1393. np->udAccel[1].nInc = 5;
  1394. np->udAccel[2].nSec = 5;
  1395. np->udAccel[2].nInc = 20;
  1396. }
  1397. else
  1398. {
  1399. np->nAccel = 0;
  1400. }
  1401. /* This does the pickbuddy and anchor
  1402. */
  1403. setbuddy(np, NULL);
  1404. setint(np);
  1405. // Automatically enable hot tracking if themes are being used
  1406. if (np->hTheme)
  1407. np->ci.style |= UDS_HOTTRACK;
  1408. break;
  1409. case WM_DESTROY:
  1410. if (np)
  1411. {
  1412. if (np->hTheme)
  1413. {
  1414. CloseThemeData(np->hTheme);
  1415. np->hTheme = NULL;
  1416. }
  1417. if (np->hThemeBuddy)
  1418. {
  1419. CloseThemeData(np->hThemeBuddy);
  1420. np->hThemeBuddy = NULL;
  1421. }
  1422. if (np->udAccel)
  1423. {
  1424. LocalFree((HLOCAL)np->udAccel);
  1425. }
  1426. if (np->hwndBuddy)
  1427. {
  1428. // Our buddy needs to be unsubclassed, which we'll do
  1429. // in response to WM_NCDESTROY; doing so now would
  1430. // bust any subsequent call to the suclass proc.
  1431. DebugMsg(DM_TRACE, TEXT("UpDown Destroyed while buddy subclassed"));
  1432. np->fUpDownDestroyed = TRUE;
  1433. }
  1434. else
  1435. {
  1436. LocalFree((HLOCAL)np);
  1437. }
  1438. SetWindowPtr(hwnd, 0, 0);
  1439. }
  1440. break;
  1441. case WM_GETOBJECT:
  1442. if( lParam == OBJID_QUERYCLASSNAMEIDX )
  1443. return MSAA_CLASSNAMEIDX_UPDOWN;
  1444. goto DoDefault;
  1445. default:
  1446. {
  1447. if (CCWndProc(&np->ci, uMsg, wParam, lParam, &lres))
  1448. return lres;
  1449. }
  1450. DoDefault:
  1451. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  1452. }
  1453. return 0L;
  1454. }
  1455. /////////////////////////////////////////////////////////////////////////////
  1456. // InitUpDownClass:
  1457. // Adds our WNDCLASS to the system.
  1458. //
  1459. #pragma code_seg(CODESEG_INIT)
  1460. BOOL InitUpDownClass(HINSTANCE hInst)
  1461. {
  1462. WNDCLASS wc;
  1463. wc.lpfnWndProc = UpDownWndProc;
  1464. wc.lpszClassName = s_szUpdownClass;
  1465. wc.hInstance = hInst;
  1466. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  1467. wc.hIcon = NULL;
  1468. wc.lpszMenuName = NULL;
  1469. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  1470. wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
  1471. wc.cbClsExtra = 0;
  1472. wc.cbWndExtra = sizeof(PUDSTATE);
  1473. return (RegisterClass(&wc) || (GetLastError() == ERROR_CLASS_ALREADY_EXISTS));
  1474. }
  1475. #pragma code_seg()