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.

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