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.

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