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.

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