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.

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