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.

980 lines
28 KiB

  1. /* Copyright (c) 1991, Microsoft Corporation, all rights reserved
  2. ipaddr.c - TCP/IP Address custom control
  3. November 9, 1992 Greg Strange
  4. */
  5. #include "ctlspriv.h"
  6. // The character that is displayed between address fields.
  7. #define FILLER TEXT('.')
  8. #define SZFILLER TEXT(".")
  9. #define SPACE TEXT(' ')
  10. #define BACK_SPACE 8
  11. /* Min, max values */
  12. #define NUM_FIELDS 4
  13. #define CHARS_PER_FIELD 3
  14. #define HEAD_ROOM 1 // space at top of control
  15. #define LEAD_ROOM 3 // space at front of control
  16. #define MIN_FIELD_VALUE 0 // default minimum allowable field value
  17. #define MAX_FIELD_VALUE 255 // default maximum allowable field value
  18. // All the information unique to one control is stuffed in one of these
  19. // structures in global memory and the handle to the memory is stored in the
  20. // Windows extra space.
  21. typedef struct tagFIELD {
  22. HANDLE hWnd;
  23. WNDPROC lpfnWndProc;
  24. BYTE byLow; // lowest allowed value for this field.
  25. BYTE byHigh; // Highest allowed value for this field.
  26. } FIELD;
  27. typedef struct tagIPADDR
  28. {
  29. HWND hwndParent;
  30. HWND hwnd;
  31. UINT uiFieldWidth;
  32. UINT uiFillerWidth;
  33. BOOL fEnabled : 1;
  34. BOOL fPainted : 1;
  35. BOOL bControlInFocus : 1; // TRUE if the control is already in focus, dont't send another focus command
  36. BOOL bCancelParentNotify : 1; // Don't allow the edit controls to notify parent if TRUE
  37. BOOL fInMessageBox : 1; // Set when a message box is displayed so that
  38. BOOL fFontCreated :1;
  39. HFONT hfont;
  40. // we don't send a EN_KILLFOCUS message when
  41. // we receive the EN_KILLFOCUS message for the
  42. // current field.
  43. FIELD Children[NUM_FIELDS];
  44. HTHEME hTheme;
  45. } IPADDR;
  46. // The following macros extract and store the CONTROL structure for a control.
  47. #define IPADDRESS_EXTRA sizeof(DWORD)
  48. #define GET_IPADDR_HANDLE(hWnd) ((HGLOBAL)(GetWindowLongPtr((hWnd), GWLP_USERDATA)))
  49. #define SAVE_IPADDR_HANDLE(hWnd,x) (SetWindowLongPtr((hWnd), GWLP_USERDATA, (LONG_PTR)(x)))
  50. /* internal IPAddress function prototypes */
  51. LRESULT IPAddressWndFn( HWND, UINT, WPARAM, LPARAM );
  52. LRESULT IPAddressFieldProc(HWND, UINT, WPARAM, LPARAM);
  53. BOOL SwitchFields(IPADDR *, int, int, WORD, WORD);
  54. void EnterField(FIELD *, WORD, WORD);
  55. BOOL ExitField(IPADDR *, int iField);
  56. int GetFieldValue(FIELD *);
  57. void SetFieldValue(IPADDR *pipa, int iField, int iValue);
  58. BOOL IsDBCS();
  59. /*
  60. IPAddrInit() - IPAddress custom control initialization
  61. call
  62. hInstance = library or application instance
  63. return
  64. TRUE on success, FALSE on failure.
  65. This function does all the one time initialization of IPAddress custom
  66. controls. Specifically it creates the IPAddress window class.
  67. */
  68. int InitIPAddr(HANDLE hInstance)
  69. {
  70. WNDCLASS wc;
  71. wc.lpszClassName = WC_IPADDRESS;
  72. wc.hCursor = LoadCursor(NULL,IDC_IBEAM);
  73. wc.hIcon = NULL;
  74. wc.lpszMenuName = (LPCTSTR)NULL;
  75. wc.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS|CS_GLOBALCLASS;
  76. wc.lpfnWndProc = IPAddressWndFn;
  77. wc.hInstance = hInstance;
  78. wc.hIcon = NULL;
  79. wc.cbWndExtra = IPADDRESS_EXTRA;
  80. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1 );
  81. wc.cbClsExtra = 0;
  82. if (!RegisterClass(&wc) && !GetClassInfo(hInstance, WC_IPADDRESS, &wc))
  83. return FALSE;
  84. return TRUE;
  85. }
  86. /*
  87. IPAddressWndFn() - Main window function for an IPAddress control.
  88. call
  89. hWnd handle to IPAddress window
  90. wMsg message number
  91. wParam word parameter
  92. lParam long parameter
  93. */
  94. void FormatIPAddress(LPTSTR pszString, DWORD* dwValue)
  95. {
  96. int nField, nPos;
  97. BOOL fFinish = FALSE;
  98. dwValue[0] = 0; dwValue[1] = 0; dwValue[2] = 0; dwValue[3] = 0;
  99. if (pszString[0] == 0)
  100. return;
  101. for( nField = 0, nPos = 0; !fFinish; nPos++)
  102. {
  103. if (( pszString[nPos]<TEXT('0')) || (pszString[nPos]>TEXT('9')))
  104. {
  105. // not a number
  106. nField++;
  107. fFinish = (nField == 4);
  108. }
  109. else
  110. {
  111. dwValue[nField] *= 10;
  112. dwValue[nField] += (pszString[nPos]-TEXT('0'));
  113. }
  114. }
  115. }
  116. void IP_OnSetFont(IPADDR* pipa, HFONT hfont, BOOL fRedraw)
  117. {
  118. int i;
  119. RECT rect;
  120. HFONT OldFont;
  121. BOOL fNewFont = FALSE;
  122. UINT uiFieldStart;
  123. HDC hdc;
  124. if (hfont) {
  125. fNewFont = TRUE;
  126. } else {
  127. hfont = (HFONT)SendMessage(pipa->hwnd, WM_GETFONT, 0, 0);
  128. }
  129. hdc = GetDC(pipa->hwnd);
  130. OldFont = SelectObject(hdc, hfont);
  131. GetCharWidth(hdc, FILLER, FILLER,
  132. (int *)(&pipa->uiFillerWidth));
  133. SelectObject(hdc, OldFont);
  134. ReleaseDC(pipa->hwnd, hdc);
  135. GetClientRect(pipa->hwnd, &rect);
  136. pipa->hfont = hfont;
  137. pipa->uiFieldWidth = (RECTWIDTH(rect)
  138. - LEAD_ROOM
  139. - pipa->uiFillerWidth
  140. *(NUM_FIELDS-1))
  141. / NUM_FIELDS;
  142. uiFieldStart = LEAD_ROOM;
  143. for (i = 0; i < NUM_FIELDS; i++) {
  144. HWND hwnd = pipa->Children[i].hWnd;
  145. if (fNewFont)
  146. SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)fRedraw);
  147. SetWindowPos(hwnd, NULL,
  148. uiFieldStart,
  149. HEAD_ROOM,
  150. pipa->uiFieldWidth,
  151. (rect.bottom-rect.top),
  152. SWP_NOACTIVATE);
  153. uiFieldStart += pipa->uiFieldWidth
  154. + pipa->uiFillerWidth;
  155. }
  156. }
  157. LRESULT IPAddressWndFn( hWnd, wMsg, wParam, lParam )
  158. HWND hWnd;
  159. UINT wMsg;
  160. WPARAM wParam;
  161. LPARAM lParam;
  162. {
  163. LRESULT lResult;
  164. IPADDR *pipa;
  165. int i;
  166. pipa = (IPADDR *)GET_IPADDR_HANDLE(hWnd);
  167. lResult = TRUE;
  168. switch( wMsg )
  169. {
  170. // use empty string (not NULL) to set to blank
  171. case WM_SETTEXT:
  172. {
  173. TCHAR szBuf[CHARS_PER_FIELD+1];
  174. DWORD dwValue[4];
  175. LPTSTR pszString = (LPTSTR)lParam;
  176. FormatIPAddress(pszString, &dwValue[0]);
  177. pipa->bCancelParentNotify = TRUE;
  178. for (i = 0; i < NUM_FIELDS; ++i)
  179. {
  180. if (pszString[0] == 0)
  181. {
  182. szBuf[0] = 0;
  183. }
  184. else
  185. {
  186. wsprintf(szBuf, TEXT("%d"), dwValue[i]);
  187. }
  188. SendMessage(pipa->Children[i].hWnd, WM_SETTEXT,
  189. 0, (LPARAM) (LPSTR) szBuf);
  190. }
  191. pipa->bCancelParentNotify = FALSE;
  192. SendMessage(pipa->hwndParent, WM_COMMAND,
  193. MAKEWPARAM(GetDlgCtrlID(hWnd), EN_CHANGE), (LPARAM)hWnd);
  194. }
  195. break;
  196. case WM_GETTEXTLENGTH:
  197. case WM_GETTEXT:
  198. {
  199. int iFieldValue;
  200. DWORD dwValue[4];
  201. TCHAR pszResult[30];
  202. TCHAR *pszDest = (TCHAR *)lParam;
  203. lResult = 0;
  204. dwValue[0] = 0;
  205. dwValue[1] = 0;
  206. dwValue[2] = 0;
  207. dwValue[3] = 0;
  208. for (i = 0; i < NUM_FIELDS; ++i)
  209. {
  210. iFieldValue = GetFieldValue(&(pipa->Children[i]));
  211. if (iFieldValue == -1)
  212. iFieldValue = 0;
  213. else
  214. ++lResult;
  215. dwValue[i] = iFieldValue;
  216. }
  217. wsprintf( pszResult, TEXT("%d.%d.%d.%d"), dwValue[0], dwValue[1], dwValue[2], dwValue[3] );
  218. if (wMsg == WM_GETTEXT)
  219. {
  220. StrCpyN(pszDest, pszResult, (int) wParam);
  221. lResult = lstrlen( pszDest );
  222. }
  223. else
  224. {
  225. lResult = lstrlen( pszResult );
  226. }
  227. }
  228. break;
  229. case WM_GETDLGCODE :
  230. lResult = DLGC_WANTCHARS;
  231. break;
  232. case WM_NCCREATE:
  233. SetWindowBits(hWnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE, WS_EX_CLIENTEDGE);
  234. SetWindowBits(hWnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0);
  235. lResult = TRUE;
  236. break;
  237. case WM_THEMECHANGED:
  238. {
  239. if (pipa->hTheme)
  240. CloseThemeData(pipa->hTheme);
  241. pipa->hTheme = OpenThemeData(hWnd, L"Combobox");
  242. InvalidateRect(hWnd, NULL, TRUE);
  243. }
  244. break;
  245. case WM_CREATE : /* create pallette window */
  246. {
  247. LONG id;
  248. pipa = (IPADDR*)LocalAlloc(LPTR, sizeof(IPADDR));
  249. if (pipa)
  250. {
  251. CREATESTRUCT* pcs = ((CREATESTRUCT *)lParam);
  252. SAVE_IPADDR_HANDLE(hWnd, pipa);
  253. pipa->fEnabled = TRUE;
  254. pipa->hwndParent = pcs->hwndParent;
  255. pipa->hwnd = hWnd;
  256. pipa->hTheme = OpenThemeData(hWnd, L"Combobox");
  257. id = GetDlgCtrlID(hWnd);
  258. for (i = 0; i < NUM_FIELDS; ++i)
  259. {
  260. pipa->Children[i].byLow = MIN_FIELD_VALUE;
  261. pipa->Children[i].byHigh = MAX_FIELD_VALUE;
  262. pipa->Children[i].hWnd = CreateWindowEx(0,
  263. TEXT("Edit"),
  264. NULL,
  265. WS_CHILD |
  266. ES_CENTER,
  267. 0, 10, 100, 100,
  268. hWnd,
  269. (HMENU)(LONG_PTR)id,
  270. pcs->hInstance,
  271. (LPVOID)NULL);
  272. SAVE_IPADDR_HANDLE(pipa->Children[i].hWnd, i);
  273. SendMessage(pipa->Children[i].hWnd, EM_LIMITTEXT,
  274. CHARS_PER_FIELD, 0L);
  275. pipa->Children[i].lpfnWndProc =
  276. (WNDPROC) GetWindowLongPtr(pipa->Children[i].hWnd,
  277. GWLP_WNDPROC);
  278. SetWindowLongPtr(pipa->Children[i].hWnd,
  279. GWLP_WNDPROC, (LONG_PTR)IPAddressFieldProc);
  280. }
  281. IP_OnSetFont(pipa, NULL, FALSE);
  282. for (i = 0; i < NUM_FIELDS; ++i)
  283. ShowWindow(pipa->Children[i].hWnd, SW_SHOW);
  284. #undef pcs
  285. }
  286. else
  287. DestroyWindow(hWnd);
  288. }
  289. lResult = 0;
  290. break;
  291. case WM_PAINT: /* paint IPADDR window */
  292. {
  293. PAINTSTRUCT Ps;
  294. RECT rect;
  295. COLORREF TextColor;
  296. COLORREF cRef;
  297. HFONT OldFont;
  298. BeginPaint(hWnd, (LPPAINTSTRUCT)&Ps);
  299. OldFont = SelectObject( Ps.hdc, pipa->hfont);
  300. GetClientRect(hWnd, &rect);
  301. if (pipa->fEnabled)
  302. {
  303. TextColor = GetSysColor(COLOR_WINDOWTEXT);
  304. cRef = GetSysColor(COLOR_WINDOW);
  305. }
  306. else
  307. {
  308. TextColor = GetSysColor(COLOR_GRAYTEXT);
  309. cRef = GetSysColor(COLOR_3DFACE);
  310. }
  311. FillRectClr(Ps.hdc, &rect, cRef);
  312. SetRect(&rect, 0, HEAD_ROOM, pipa->uiFillerWidth, (rect.bottom-rect.top));
  313. SetBkColor(Ps.hdc, cRef);
  314. SetTextColor(Ps.hdc, TextColor);
  315. for (i = 0; i < NUM_FIELDS-1; ++i)
  316. {
  317. rect.left += pipa->uiFieldWidth + pipa->uiFillerWidth;
  318. rect.right += rect.left + pipa->uiFillerWidth;
  319. ExtTextOut(Ps.hdc, rect.left, HEAD_ROOM, ETO_OPAQUE, &rect, SZFILLER, 1, NULL);
  320. }
  321. pipa->fPainted = TRUE;
  322. SelectObject(Ps.hdc, OldFont);
  323. EndPaint(hWnd, &Ps);
  324. }
  325. break;
  326. case WM_SETFOCUS : /* get focus - display caret */
  327. EnterField(&(pipa->Children[0]), 0, CHARS_PER_FIELD);
  328. break;
  329. HANDLE_MSG(pipa, WM_SETFONT, IP_OnSetFont);
  330. case WM_LBUTTONDOWN : /* left button depressed - fall through */
  331. SetFocus(hWnd);
  332. break;
  333. case WM_ENABLE:
  334. {
  335. pipa->fEnabled = (BOOL)wParam;
  336. for (i = 0; i < NUM_FIELDS; ++i)
  337. {
  338. EnableWindow(pipa->Children[i].hWnd, (BOOL)wParam);
  339. }
  340. if (pipa->fPainted)
  341. InvalidateRect(hWnd, NULL, FALSE);
  342. }
  343. break;
  344. case WM_NCPAINT:
  345. {
  346. if (pipa->hTheme)
  347. {
  348. HRGN hrgn = (wParam != 1) ? (HRGN)wParam : NULL;
  349. HBRUSH hbr = (HBRUSH)GetClassLongPtr(hWnd, GCLP_HBRBACKGROUND);
  350. if (CCDrawNonClientTheme(pipa->hTheme, hWnd, hrgn, hbr, 0, CBXS_NORMAL))
  351. {
  352. return TRUE;
  353. }
  354. }
  355. }
  356. goto DoDefault;
  357. case WM_DESTROY :
  358. // Restore all the child window procedures before we delete our memory block.
  359. for (i = 0; i < NUM_FIELDS; ++i)
  360. {
  361. SendMessage(pipa->Children[i].hWnd, WM_DESTROY, 0, 0);
  362. SetWindowLongPtr(pipa->Children[i].hWnd, GWLP_WNDPROC,
  363. (LONG_PTR)pipa->Children[i].lpfnWndProc);
  364. }
  365. if (pipa->hTheme)
  366. CloseThemeData(pipa->hTheme);
  367. LocalFree(pipa);
  368. break;
  369. case WM_COMMAND:
  370. switch (HIWORD(wParam))
  371. {
  372. // One of the fields lost the focus, see if it lost the focus to another field
  373. // of if we've lost the focus altogether. If its lost altogether, we must send
  374. // an EN_KILLFOCUS notification on up the ladder.
  375. case EN_KILLFOCUS:
  376. {
  377. HWND hFocus;
  378. if (!pipa->fInMessageBox)
  379. {
  380. hFocus = GetFocus();
  381. for (i = 0; i < NUM_FIELDS; ++i)
  382. if (pipa->Children[i].hWnd == hFocus)
  383. break;
  384. if (i >= NUM_FIELDS)
  385. {
  386. SendMessage(pipa->hwndParent, WM_COMMAND,
  387. MAKEWPARAM(GetDlgCtrlID(hWnd),
  388. EN_KILLFOCUS), (LPARAM)hWnd);
  389. pipa->bControlInFocus = FALSE;
  390. }
  391. }
  392. }
  393. break;
  394. case EN_SETFOCUS:
  395. {
  396. HWND hFocus;
  397. if (!pipa->fInMessageBox)
  398. {
  399. hFocus = (HWND)lParam;
  400. for (i = 0; i < NUM_FIELDS; ++i)
  401. if (pipa->Children[i].hWnd == hFocus)
  402. break;
  403. // send a focus message when the
  404. if (i < NUM_FIELDS && pipa->bControlInFocus == FALSE)
  405. {
  406. SendMessage(pipa->hwndParent, WM_COMMAND,
  407. MAKEWPARAM(GetDlgCtrlID(hWnd),
  408. EN_SETFOCUS), (LPARAM)hWnd);
  409. pipa->bControlInFocus = TRUE; // only set the focus once
  410. }
  411. }
  412. }
  413. break;
  414. case EN_CHANGE:
  415. if (pipa->bCancelParentNotify == FALSE)
  416. {
  417. SendMessage(pipa->hwndParent, WM_COMMAND,
  418. MAKEWPARAM(GetDlgCtrlID(hWnd), EN_CHANGE), (LPARAM)hWnd);
  419. }
  420. break;
  421. }
  422. break;
  423. // Get the value of the IP Address. The address is placed in the DWORD pointed
  424. // to by lParam and the number of non-blank fields is returned.
  425. case IPM_GETADDRESS:
  426. {
  427. int iFieldValue;
  428. DWORD dwValue;
  429. lResult = 0;
  430. dwValue = 0;
  431. for (i = 0; i < NUM_FIELDS; ++i)
  432. {
  433. iFieldValue = GetFieldValue(&(pipa->Children[i]));
  434. if (iFieldValue == -1)
  435. iFieldValue = 0;
  436. else
  437. ++lResult;
  438. dwValue = (dwValue << 8) + iFieldValue;
  439. }
  440. *((DWORD *)lParam) = dwValue;
  441. }
  442. break;
  443. // Clear all fields to blanks.
  444. case IPM_CLEARADDRESS:
  445. {
  446. pipa->bCancelParentNotify = TRUE;
  447. for (i = 0; i < NUM_FIELDS; ++i)
  448. {
  449. SendMessage(pipa->Children[i].hWnd, WM_SETTEXT,
  450. 0, (LPARAM) (LPSTR) TEXT(""));
  451. }
  452. pipa->bCancelParentNotify = FALSE;
  453. SendMessage(pipa->hwndParent, WM_COMMAND,
  454. MAKEWPARAM(GetDlgCtrlID(hWnd), EN_CHANGE), (LPARAM)hWnd);
  455. }
  456. break;
  457. // Set the value of the IP Address. The address is in the lParam with the
  458. // first address byte being the high byte, the second being the second byte,
  459. // and so on. A lParam value of -1 removes the address.
  460. case IPM_SETADDRESS:
  461. {
  462. pipa->bCancelParentNotify = TRUE;
  463. for (i = 0; i < NUM_FIELDS; ++i)
  464. {
  465. BYTE bVal = HIBYTE(HIWORD(lParam));
  466. if (pipa->Children[i].byLow <= bVal &&
  467. bVal <= pipa->Children[i].byHigh) {
  468. SetFieldValue(pipa, i, bVal);
  469. } else {
  470. lResult = FALSE;
  471. }
  472. lParam <<= 8;
  473. }
  474. pipa->bCancelParentNotify = FALSE;
  475. SendMessage(pipa->hwndParent, WM_COMMAND,
  476. MAKEWPARAM(GetDlgCtrlID(hWnd), EN_CHANGE), (LPARAM)hWnd);
  477. }
  478. break;
  479. case IPM_SETRANGE:
  480. if (wParam < NUM_FIELDS && LOBYTE(LOWORD(lParam)) <= HIBYTE(LOWORD(lParam)))
  481. {
  482. lResult = MAKEIPRANGE(pipa->Children[wParam].byLow, pipa->Children[wParam].byHigh);
  483. pipa->Children[wParam].byLow = LOBYTE(LOWORD(lParam));
  484. pipa->Children[wParam].byHigh = HIBYTE(LOWORD(lParam));
  485. break;
  486. }
  487. lResult = 0;
  488. break;
  489. // Set the focus to this IPADDR.
  490. // wParam = the field number to set focus to, or -1 to set the focus to the
  491. // first non-blank field.
  492. case IPM_SETFOCUS:
  493. if (wParam >= NUM_FIELDS)
  494. {
  495. for (wParam = 0; wParam < NUM_FIELDS; ++wParam)
  496. if (GetFieldValue(&(pipa->Children[wParam])) == -1) break;
  497. if (wParam >= NUM_FIELDS) wParam = 0;
  498. }
  499. EnterField(&(pipa->Children[wParam]), 0, CHARS_PER_FIELD);
  500. break;
  501. // Determine whether all four subfields are blank
  502. case IPM_ISBLANK:
  503. lResult = TRUE;
  504. for (i = 0; i < NUM_FIELDS; ++i)
  505. {
  506. if (GetFieldValue(&(pipa->Children[i])) != -1)
  507. {
  508. lResult = FALSE;
  509. break;
  510. }
  511. }
  512. break;
  513. default:
  514. DoDefault:
  515. lResult = DefWindowProc( hWnd, wMsg, wParam, lParam );
  516. break;
  517. }
  518. return( lResult );
  519. }
  520. /*
  521. IPAddressFieldProc() - Edit field window procedure
  522. This function sub-classes each edit field.
  523. */
  524. LRESULT IPAddressFieldProc(HWND hWnd,
  525. UINT wMsg,
  526. WPARAM wParam,
  527. LPARAM lParam)
  528. {
  529. IPADDR *pipa;
  530. FIELD *pField;
  531. HWND hIPADDRWindow;
  532. WORD wChildID;
  533. LRESULT lresult;
  534. if (!(hIPADDRWindow = GetParent(hWnd)))
  535. return 0;
  536. pipa = (IPADDR *)GET_IPADDR_HANDLE(hIPADDRWindow);
  537. if (!pipa)
  538. return 0;
  539. wChildID = (WORD)GET_IPADDR_HANDLE(hWnd);
  540. pField = &(pipa->Children[wChildID]);
  541. if (pField->hWnd != hWnd)
  542. return 0;
  543. switch (wMsg)
  544. {
  545. case WM_DESTROY:
  546. DeleteObject((HGDIOBJ)SendMessage(hWnd, WM_GETFONT, 0, 0));
  547. return 0;
  548. case WM_CHAR:
  549. // Typing in the last digit in a field, skips to the next field.
  550. if (wParam >= TEXT('0') && wParam <= TEXT('9'))
  551. {
  552. LRESULT lResult;
  553. lResult = CallWindowProc(pipa->Children[wChildID].lpfnWndProc,
  554. hWnd, wMsg, wParam, lParam);
  555. lResult = SendMessage(hWnd, EM_GETSEL, 0, 0L);
  556. if (lResult == MAKELPARAM(CHARS_PER_FIELD, CHARS_PER_FIELD)
  557. && ExitField(pipa, wChildID)
  558. && wChildID < NUM_FIELDS-1)
  559. {
  560. EnterField(&(pipa->Children[wChildID+1]),
  561. 0, CHARS_PER_FIELD);
  562. }
  563. return lResult;
  564. }
  565. // spaces and periods fills out the current field and then if possible,
  566. // goes to the next field.
  567. else if (wParam == FILLER || wParam == SPACE )
  568. {
  569. LRESULT lResult;
  570. lResult = SendMessage(hWnd, EM_GETSEL, 0, 0L);
  571. if (lResult != 0L && HIWORD(lResult) == LOWORD(lResult)
  572. && ExitField(pipa, wChildID))
  573. {
  574. if (wChildID >= NUM_FIELDS-1)
  575. MessageBeep((UINT)-1);
  576. else
  577. {
  578. EnterField(&(pipa->Children[wChildID+1]),
  579. 0, CHARS_PER_FIELD);
  580. }
  581. }
  582. return 0;
  583. }
  584. // Backspaces go to the previous field if at the beginning of the current field.
  585. // Also, if the focus shifts to the previous field, the backspace must be
  586. // processed by that field.
  587. else if (wParam == BACK_SPACE)
  588. {
  589. if (wChildID > 0 && SendMessage(hWnd, EM_GETSEL, 0, 0L) == 0L)
  590. {
  591. if (SwitchFields(pipa, wChildID, wChildID-1,
  592. CHARS_PER_FIELD, CHARS_PER_FIELD)
  593. && SendMessage(pipa->Children[wChildID-1].hWnd,
  594. EM_LINELENGTH, 0, 0L) != 0L)
  595. {
  596. SendMessage(pipa->Children[wChildID-1].hWnd,
  597. wMsg, wParam, lParam);
  598. }
  599. return 0;
  600. }
  601. }
  602. // Any other printable characters are not allowed.
  603. else if (wParam > SPACE)
  604. {
  605. MessageBeep((UINT)-1);
  606. return 0;
  607. }
  608. break;
  609. case WM_KEYDOWN:
  610. switch (wParam)
  611. {
  612. // Arrow keys move between fields when the end of a field is reached.
  613. case VK_LEFT:
  614. case VK_RIGHT:
  615. case VK_UP:
  616. case VK_DOWN:
  617. if (GetKeyState(VK_CONTROL) < 0)
  618. {
  619. if ((wParam == VK_LEFT || wParam == VK_UP) && wChildID > 0)
  620. {
  621. SwitchFields(pipa, wChildID, wChildID-1,
  622. 0, CHARS_PER_FIELD);
  623. return 0;
  624. }
  625. else if ((wParam == VK_RIGHT || wParam == VK_DOWN)
  626. && wChildID < NUM_FIELDS-1)
  627. {
  628. SwitchFields(pipa, wChildID, wChildID+1,
  629. 0, CHARS_PER_FIELD);
  630. return 0;
  631. }
  632. }
  633. else
  634. {
  635. DWORD dwResult;
  636. WORD wStart, wEnd;
  637. dwResult = (DWORD)SendMessage(hWnd, EM_GETSEL, 0, 0L);
  638. wStart = LOWORD(dwResult);
  639. wEnd = HIWORD(dwResult);
  640. if (wStart == wEnd)
  641. {
  642. if ((wParam == VK_LEFT || wParam == VK_UP)
  643. && wStart == 0
  644. && wChildID > 0)
  645. {
  646. SwitchFields(pipa, wChildID, wChildID-1,
  647. CHARS_PER_FIELD, CHARS_PER_FIELD);
  648. return 0;
  649. }
  650. else if ((wParam == VK_RIGHT || wParam == VK_DOWN)
  651. && wChildID < NUM_FIELDS-1)
  652. {
  653. dwResult = (DWORD)SendMessage(hWnd, EM_LINELENGTH, 0, 0L);
  654. if (wStart >= dwResult)
  655. {
  656. SwitchFields(pipa, wChildID, wChildID+1, 0, 0);
  657. return 0;
  658. }
  659. }
  660. }
  661. }
  662. break;
  663. // Home jumps back to the beginning of the first field.
  664. case VK_HOME:
  665. if (wChildID > 0)
  666. {
  667. SwitchFields(pipa, wChildID, 0, 0, 0);
  668. return 0;
  669. }
  670. break;
  671. // End scoots to the end of the last field.
  672. case VK_END:
  673. if (wChildID < NUM_FIELDS-1)
  674. {
  675. SwitchFields(pipa, wChildID, NUM_FIELDS-1,
  676. CHARS_PER_FIELD, CHARS_PER_FIELD);
  677. return 0;
  678. }
  679. break;
  680. } // switch (wParam)
  681. break;
  682. case WM_KILLFOCUS:
  683. if ( !ExitField( pipa, wChildID ))
  684. {
  685. return 0;
  686. }
  687. } // switch (wMsg)
  688. lresult = CallWindowProc( pipa->Children[wChildID].lpfnWndProc,
  689. hWnd, wMsg, wParam, lParam);
  690. return lresult;
  691. }
  692. /*
  693. Switch the focus from one field to another.
  694. call
  695. pipa = Pointer to the IPADDR structure.
  696. iOld = Field we're leaving.
  697. iNew = Field we're entering.
  698. hNew = Window of field to goto
  699. wStart = First character selected
  700. wEnd = Last character selected + 1
  701. returns
  702. TRUE on success, FALSE on failure.
  703. Only switches fields if the current field can be validated.
  704. */
  705. BOOL SwitchFields(IPADDR *pipa, int iOld, int iNew, WORD wStart, WORD wEnd)
  706. {
  707. if (!ExitField(pipa, iOld)) return FALSE;
  708. EnterField(&(pipa->Children[iNew]), wStart, wEnd);
  709. return TRUE;
  710. }
  711. /*
  712. Set the focus to a specific field's window.
  713. call
  714. pField = pointer to field structure for the field.
  715. wStart = First character selected
  716. wEnd = Last character selected + 1
  717. */
  718. void EnterField(FIELD *pField, WORD wStart, WORD wEnd)
  719. {
  720. SetFocus(pField->hWnd);
  721. SendMessage(pField->hWnd, EM_SETSEL, wStart, wEnd);
  722. }
  723. void SetFieldValue(IPADDR *pipa, int iField, int iValue)
  724. {
  725. TCHAR szBuf[CHARS_PER_FIELD+1];
  726. FIELD* pField = &(pipa->Children[iField]);
  727. wsprintf(szBuf, TEXT("%d"), iValue);
  728. SendMessage(pField->hWnd, WM_SETTEXT, 0, (LPARAM) (LPSTR) szBuf);
  729. }
  730. /*
  731. Exit a field.
  732. call
  733. pipa = pointer to IPADDR structure.
  734. iField = field number being exited.
  735. returns
  736. TRUE if the user may exit the field.
  737. FALSE if he may not.
  738. */
  739. BOOL ExitField(IPADDR *pipa, int iField)
  740. {
  741. FIELD *pField;
  742. int i;
  743. NMIPADDRESS nm;
  744. int iOldValue;
  745. pField = &(pipa->Children[iField]);
  746. i = GetFieldValue(pField);
  747. iOldValue = i;
  748. nm.iField = iField;
  749. nm.iValue = i;
  750. SendNotifyEx(pipa->hwndParent, pipa->hwnd, IPN_FIELDCHANGED, &nm.hdr, FALSE);
  751. i = nm.iValue;
  752. if (i != -1) {
  753. if (i < (int)(UINT)pField->byLow || i > (int)(UINT)pField->byHigh)
  754. {
  755. if ( i < (int)(UINT) pField->byLow )
  756. {
  757. /* too small */
  758. i = (int)(UINT)pField->byLow;
  759. }
  760. else
  761. {
  762. /* must be bigger */
  763. i = (int)(UINT)pField->byHigh;
  764. }
  765. SetFieldValue(pipa, iField, i);
  766. // FEATURE: send notify up
  767. return FALSE;
  768. }
  769. }
  770. if (iOldValue != i) {
  771. SetFieldValue(pipa, iField, i);
  772. }
  773. return TRUE;
  774. }
  775. /*
  776. Get the value stored in a field.
  777. call
  778. pField = pointer to the FIELD structure for the field.
  779. returns
  780. The value (0..255) or -1 if the field has not value.
  781. */
  782. int GetFieldValue(FIELD *pField)
  783. {
  784. WORD wLength;
  785. TCHAR szBuf[CHARS_PER_FIELD+1];
  786. INT i;
  787. *(WORD *)szBuf = (sizeof(szBuf)/sizeof(TCHAR)) - 1;
  788. wLength = (WORD)SendMessage(pField->hWnd,EM_GETLINE,0,(LPARAM)(LPSTR)szBuf);
  789. if (wLength != 0)
  790. {
  791. szBuf[wLength] = TEXT('\0');
  792. i = StrToInt(szBuf);
  793. return i;
  794. }
  795. else
  796. return -1;
  797. }
  798. BOOL IsDBCS()
  799. {
  800. LANGID langid;
  801. langid = PRIMARYLANGID(GetThreadLocale());
  802. if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
  803. {
  804. return (TRUE);
  805. }
  806. else
  807. {
  808. return (FALSE);
  809. }
  810. }