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.

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