ipaddress.c 14.7 KB
Newer Older
1 2
/*
 * IP Address control
3
 *
4 5 6
 * Copyright 2002 Dimitrie O. Paun
 * Copyright 1999 Chris Morgan<cmorgan@wpi.edu>
 * Copyright 1999 James Abbatiello<abbeyj@wpi.edu>
7
 * Copyright 1998, 1999 Eric Kohl
8
 * Copyright 1998 Alex Priem <alexp@sci.kun.nl>
9
 *
10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
24 25
 */

26 27
#include <ctype.h>
#include <stdlib.h>
28
#include <stdio.h>
29
#include <string.h>
30

31
#include "winbase.h"
32
#include "commctrl.h"
33
#include "wine/unicode.h"
34
#include "wine/debug.h"
35

36
WINE_DEFAULT_DEBUG_CHANNEL(ipaddress);
37

38 39
typedef struct
{
40 41 42 43 44
    HWND     EditHwnd;
    INT      LowerLimit;
    INT      UpperLimit;
    WNDPROC  OrigProc;
} IPPART_INFO;
45 46 47

typedef struct
{
48 49 50
    HWND	Self;
    IPPART_INFO	Part[4];
} IPADDRESS_INFO;
51

52 53 54 55
#define POS_DEFAULT	0
#define POS_LEFT	1
#define POS_RIGHT	2
#define POS_SELALL	3
56 57

#define IP_SUBCLASS_PROP "CCIP32SubclassInfo"
58
#define IPADDRESS_GetInfoPtr(hwnd) ((IPADDRESS_INFO *)GetWindowLongW (hwnd, 0))
59 60 61


static LRESULT CALLBACK
62
IPADDRESS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
63

64
static LRESULT IPADDRESS_Notify (IPADDRESS_INFO *infoPtr, UINT command)
65
{
66
    HWND hwnd = infoPtr->Self;
67

68
    TRACE("(command=%x)\n", command);
69

70 71
    return SendMessageW (GetParent (hwnd), WM_COMMAND,
             MAKEWPARAM (GetWindowLongW (hwnd, GWL_ID), command), (LPARAM)hwnd);
72 73
}

74
static INT IPADDRESS_IPNotify (IPADDRESS_INFO *infoPtr, INT field, INT value)
75
{
76
    NMIPADDRESS nmip;
77

78
    TRACE("(field=%x, value=%d)\n", field, value);
79

80 81 82
    nmip.hdr.hwndFrom = infoPtr->Self;
    nmip.hdr.idFrom   = GetWindowLongW (infoPtr->Self, GWL_ID);
    nmip.hdr.code     = IPN_FIELDCHANGED;
83

84 85
    nmip.iField = field;
    nmip.iValue = value;
86

87 88
    SendMessageW (GetParent (infoPtr->Self), WM_NOTIFY,
                  (WPARAM)nmip.hdr.idFrom, (LPARAM)&nmip);
89

90
    TRACE("<-- %d\n", nmip.iValue);
91

92
    return nmip.iValue;
93
}
94 95


96
static int IPADDRESS_GetPartIndex(IPADDRESS_INFO *infoPtr, HWND hwnd)
97
{
98
    int i;
99

100
    TRACE("(hwnd=%x)\n", hwnd);
101 102

    for (i = 0; i < 4; i++)
103
        if (infoPtr->Part[i].EditHwnd == hwnd) return i;
104

105 106
    ERR("We subclassed the wrong window! (hwnd=%x)\n", hwnd);
    return -1;
107 108 109
}


110
static LRESULT IPADDRESS_Draw (IPADDRESS_INFO *infoPtr, HDC hdc)
111
{
112 113 114
    RECT rect, rcPart;
    POINT pt;
    int i;
115

116
    TRACE("\n");
117

118 119 120 121 122 123 124
    GetClientRect (infoPtr->Self, &rect);
    DrawEdge (hdc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);

    for (i = 0; i < 3; i++) {
        GetWindowRect (infoPtr->Part[i].EditHwnd, &rcPart);
	pt.x = rcPart.right;
	ScreenToClient(infoPtr->Self, &pt);
125
	rect.left = pt.x;
126 127 128 129 130 131
	GetWindowRect (infoPtr->Part[i+1].EditHwnd, &rcPart);
	pt.x = rcPart.left;
	ScreenToClient(infoPtr->Self, &pt);
	rect.right = pt.x;
	DrawTextA(hdc, ".", 1, &rect, DT_SINGLELINE | DT_CENTER | DT_BOTTOM);
    }
132

133
    return 0;
134
}
135 136


137
static LRESULT IPADDRESS_Create (HWND hwnd)
138
{
139 140 141 142 143 144
    IPADDRESS_INFO *infoPtr;
    RECT rcClient, edit;
    int i, fieldsize;
    WCHAR EDIT[] = { 'E', 'd', 'i', 't', 0 };

    TRACE("\n");
145

146 147
    SetWindowLongW (hwnd, GWL_STYLE,
		    GetWindowLongW(hwnd, GWL_STYLE) & ~WS_BORDER);
148

149 150 151
    infoPtr = (IPADDRESS_INFO *)COMCTL32_Alloc (sizeof(IPADDRESS_INFO));
    if (!infoPtr) return -1;
    SetWindowLongW (hwnd, 0, (DWORD)infoPtr);
152

153
    GetClientRect (hwnd, &rcClient);
154

155
    fieldsize = (rcClient.right - rcClient.left) / 4;
156

157 158
    edit.top    = rcClient.top + 2;
    edit.bottom = rcClient.bottom - 2;
159

160
    infoPtr->Self = hwnd;
161

162 163
    for (i = 0; i < 4; i++) {
	IPPART_INFO* part = &infoPtr->Part[i];
164

165 166 167 168 169 170
	part->LowerLimit = 0;
	part->UpperLimit = 255;
        edit.left = rcClient.left + i*fieldsize + 6;
        edit.right = rcClient.left + (i+1)*fieldsize - 2;
        part->EditHwnd =
		CreateWindowW (EDIT, NULL, WS_CHILD | WS_VISIBLE | ES_CENTER,
171 172
                               edit.left, edit.top, edit.right - edit.left,
			       edit.bottom - edit.top, hwnd, (HMENU) 1,
173 174 175
			       GetWindowLongW (hwnd, GWL_HINSTANCE), NULL);
	SetPropA(part->EditHwnd, IP_SUBCLASS_PROP, hwnd);
        part->OrigProc = (WNDPROC)
176
		SetWindowLongW (part->EditHwnd, GWL_WNDPROC,
177 178
				(LONG)IPADDRESS_SubclassProc);
    }
179

180
    return 0;
181
}
182 183


184
static LRESULT IPADDRESS_Destroy (IPADDRESS_INFO *infoPtr)
185
{
186
    int i;
187

188
    TRACE("\n");
189

190 191 192 193
    for (i = 0; i < 4; i++) {
	IPPART_INFO* part = &infoPtr->Part[i];
        SetWindowLongW (part->EditHwnd, GWL_WNDPROC, (LONG)part->OrigProc);
    }
194

195 196 197
    SetWindowLongW (infoPtr->Self, 0, 0);
    COMCTL32_Free (infoPtr);
    return 0;
198 199 200
}


201
static LRESULT IPADDRESS_Paint (IPADDRESS_INFO *infoPtr, HDC hdc)
202
{
203
    PAINTSTRUCT ps;
204

205
    TRACE("\n");
206

207
    if (hdc) return IPADDRESS_Draw (infoPtr, hdc);
208

209 210 211 212
    hdc = BeginPaint (infoPtr->Self, &ps);
    IPADDRESS_Draw (infoPtr, hdc);
    EndPaint (infoPtr->Self, &ps);
    return 0;
213 214 215
}


216
static BOOL IPADDRESS_IsBlank (IPADDRESS_INFO *infoPtr)
217
{
218
    int i;
219

220
    TRACE("\n");
221

222 223 224 225
    for (i = 0; i < 4; i++)
        if (GetWindowTextLengthW (infoPtr->Part[i].EditHwnd)) return FALSE;

    return TRUE;
226 227
}

228

229
static int IPADDRESS_GetAddress (IPADDRESS_INFO *infoPtr, LPDWORD ip_address)
230
{
231 232 233
    WCHAR field[5];
    int i, invalid = 0;
    DWORD ip_addr = 0;
234

235
    TRACE("\n");
236

237 238
    for (i = 0; i < 4; i++) {
        ip_addr *= 256;
239
        if (GetWindowTextW (infoPtr->Part[i].EditHwnd, field, 4))
240
  	    ip_addr += atolW(field);
241 242 243 244
	else
	    invalid++;
    }
    *ip_address = ip_addr;
245

246
    return 4 - invalid;
247 248
}

249

250
static BOOL IPADDRESS_SetRange (IPADDRESS_INFO *infoPtr, int index, WORD range)
251
{
252
    TRACE("\n");
253

254
    if ( (index < 0) || (index > 3) ) return FALSE;
255

256 257 258 259
    infoPtr->Part[index].LowerLimit = range & 0xFF;
    infoPtr->Part[index].UpperLimit = (range >> 8)  & 0xFF;

    return TRUE;
260 261 262
}


263
static void IPADDRESS_ClearAddress (IPADDRESS_INFO *infoPtr)
264
{
Alexandre Julliard's avatar
Alexandre Julliard committed
265
    WCHAR nil[1] = { 0 };
266
    int i;
267

268
    TRACE("\n");
269

270
    for (i = 0; i < 4; i++)
271
        SetWindowTextW (infoPtr->Part[i].EditHwnd, nil);
272 273
}

274

275
static LRESULT IPADDRESS_SetAddress (IPADDRESS_INFO *infoPtr, DWORD ip_address)
276
{
277 278 279 280
    WCHAR buf[20], fmt[] = { '%', 'd', 0 };
    int i;

    TRACE("\n");
281

282 283 284 285
    for (i = 3; i >= 0; i--) {
	IPPART_INFO* part = &infoPtr->Part[i];
        int value = ip_address & 0xff;
	if ( (value >= part->LowerLimit) && (value <= part->UpperLimit) ) {
286
	    wsprintfW (buf, fmt, value);
287 288 289 290 291
	    SetWindowTextW (part->EditHwnd, buf);
	    IPADDRESS_Notify (infoPtr, EN_CHANGE);
        }
        ip_address >>= 8;
    }
292

293
    return TRUE;
294
}
295 296


297
static void IPADDRESS_SetFocusToField (IPADDRESS_INFO *infoPtr, INT index)
298
{
299
    TRACE("(index=%d)\n", index);
300

301 302 303 304 305
    if (index > 3) {
	for (index = 0; index < 4; index++)
	    if (!GetWindowTextLengthW(infoPtr->Part[index].EditHwnd)) break;
    }
    if (index < 9 || index > 3) index = 0;
306

307
    SetFocus (infoPtr->Part[index].EditHwnd);
308 309 310
}


311
static BOOL IPADDRESS_ConstrainField (IPADDRESS_INFO *infoPtr, int currentfield)
312
{
313 314 315
    IPPART_INFO *part = &infoPtr->Part[currentfield];
    WCHAR field[10], fmt[] = { '%', 'd', 0 };
    int curValue, newValue;
316

317
    TRACE("(currentfield=%d)\n", currentfield);
318

319
    if (currentfield < 0 || currentfield > 3) return FALSE;
320

321
    if (!GetWindowTextW (part->EditHwnd, field, 4)) return FALSE;
322

323
    curValue = atoiW(field);
324
    TRACE("  curValue=%d\n", curValue);
325

326 327 328 329 330
    newValue = IPADDRESS_IPNotify(infoPtr, currentfield, curValue);
    TRACE("  newValue=%d\n", newValue);

    if (newValue < part->LowerLimit) newValue = part->LowerLimit;
    if (newValue > part->UpperLimit) newValue = part->UpperLimit;
331

332
    if (newValue == curValue) return FALSE;
333

334
    wsprintfW (field, fmt, newValue);
335 336
    TRACE("  field='%s'\n", debugstr_w(field));
    return SetWindowTextW (part->EditHwnd, field);
337
}
338 339


340
static BOOL IPADDRESS_GotoNextField (IPADDRESS_INFO *infoPtr, int cur, int sel)
341
{
342 343 344 345
    TRACE("\n");

    if(cur >= -1 && cur < 4) {
	IPADDRESS_ConstrainField(infoPtr, cur);
346

347 348 349 350 351 352 353 354 355 356 357 358 359
	if(cur < 3) {
	    IPPART_INFO *next = &infoPtr->Part[cur + 1];
	    int start = 0, end = 0;
            SetFocus (next->EditHwnd);
	    if (sel != POS_DEFAULT) {
		if (sel == POS_RIGHT)
		    start = end = GetWindowTextLengthW(next->EditHwnd);
		else if (sel == POS_SELALL)
		    end = -1;
	        SendMessageW(next->EditHwnd, EM_SETSEL, start, end);
	    }
	    return TRUE;
	}
360

361
    }
362
    return FALSE;
363 364 365
}


366 367 368 369 370 371 372 373 374
/*
 * period: move and select the text in the next field to the right if
 *         the current field is not empty(l!=0), we are not in the
 *         left most position, and nothing is selected(startsel==endsel)
 *
 * spacebar: same behavior as period
 *
 * alpha characters: completely ignored
 *
375 376 377 378 379
 * digits: accepted when field text length < 2 ignored otherwise.
 *         when 3 numbers have been entered into the field the value
 *         of the field is checked, if the field value exceeds the
 *         maximum value and is changed the field remains the current
 *         field, otherwise focus moves to the field to the right
380
 *
381 382
 * tab: change focus from the current ipaddress control to the next
 *      control in the tab order
383
 *
384 385 386 387
 * right arrow: move to the field on the right to the left most
 *              position in that field if no text is selected,
 *              we are in the right most position in the field,
 *              we are not in the right most field
388
 *
389 390 391 392
 * left arrow: move to the field on the left to the right most
 *             position in that field if no text is selected,
 *             we are in the left most position in the current field
 *             and we are not in the left most field
393
 *
394 395 396 397 398
 * backspace: delete the character to the left of the cursor position,
 *            if none are present move to the field on the left if
 *            we are not in the left most field and delete the right
 *            most digit in that field while keeping the cursor
 *            on the right side of the field
399
 */
400
LRESULT CALLBACK
401
IPADDRESS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
402
{
403 404 405 406 407
    HWND Self = (HWND)GetPropA (hwnd, IP_SUBCLASS_PROP);
    IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (Self);
    CHAR c = (CHAR)wParam;
    INT index, len = 0, startsel, endsel;
    IPPART_INFO *part;
408

409
    TRACE("(hwnd=0x%x msg=0x%x wparam=0x%x lparam=0x%lx)\n", hwnd, uMsg, wParam, lParam);
410

411 412
    if ( (index = IPADDRESS_GetPartIndex(infoPtr, hwnd)) < 0) return 0;
    part = &infoPtr->Part[index];
413

414 415 416
    if (uMsg == WM_CHAR || uMsg == WM_KEYDOWN) {
	len = GetWindowTextLengthW (hwnd);
	SendMessageW(hwnd, EM_GETSEL, (WPARAM)&startsel, (LPARAM)&endsel);
417
    }
418
    switch (uMsg) {
419
 	case WM_CHAR:
420 421 422 423
 	    if(isdigit(c)) {
		if(len == 2 && startsel==endsel && endsel==len) {
		    /* process the digit press before we check the field */
		    int return_val = CallWindowProcW (part->OrigProc, hwnd, uMsg, wParam, lParam);
424

425 426 427 428 429 430 431
		    /* if the field value was changed stay at the current field */
		    if(!IPADDRESS_ConstrainField(infoPtr, index))
			IPADDRESS_GotoNextField (infoPtr, index, POS_DEFAULT);

		    return return_val;
		} else if (len == 3 && startsel==endsel && endsel==len)
		    IPADDRESS_GotoNextField (infoPtr, index, POS_SELALL);
432
		else if (len < 3) break;
433 434
	    } else if(c == '.' || c == ' ') {
		if(len && startsel==endsel && startsel != 0) {
435
		    IPADDRESS_GotoNextField(infoPtr, index, POS_SELALL);
436 437 438
		}
 	    } else if (c == VK_BACK) break;
	    return 0;
439

440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
	case WM_KEYDOWN:
	    switch(c) {
		case VK_RIGHT:
		    if(startsel==endsel && startsel==len) {
			IPADDRESS_GotoNextField(infoPtr, index, POS_LEFT);
			return 0;
		    }
		    break;
		case VK_LEFT:
		    if(startsel==0 && startsel==endsel && index > 0) {
			IPADDRESS_GotoNextField(infoPtr, index - 2, POS_RIGHT);
			return 0;
		    }
		    break;
		case VK_BACK:
		    if(startsel==endsel && startsel==0 && index > 0) {
			IPPART_INFO *prev = &infoPtr->Part[index-1];
			WCHAR val[10];
458

459 460 461 462
			if(GetWindowTextW(prev->EditHwnd, val, 5)) {
			    val[lstrlenW(val) - 1] = 0;
			    SetWindowTextW(prev->EditHwnd, val);
			}
463

464 465 466 467
			IPADDRESS_GotoNextField(infoPtr, index - 2, POS_RIGHT);
			return 0;
		    }
		    break;
468
	    }
469 470 471 472 473 474 475 476 477 478 479
	    break;
	case WM_KILLFOCUS:
	    if (IPADDRESS_GetPartIndex(infoPtr, (HWND)wParam) < 0)
		IPADDRESS_Notify(infoPtr, EN_KILLFOCUS);
	    break;
	case WM_SETFOCUS:
	    if (IPADDRESS_GetPartIndex(infoPtr, (HWND)wParam) < 0)
		IPADDRESS_Notify(infoPtr, EN_SETFOCUS);
	    break;
    }
    return CallWindowProcW (part->OrigProc, hwnd, uMsg, wParam, lParam);
480
}
481

482

483
static LRESULT WINAPI
484
IPADDRESS_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
485
{
486
    IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd);
487

488
    TRACE("(hwnd=0x%x msg=0x%x wparam=0x%x lparam=0x%lx)\n", hwnd, uMsg, wParam, lParam);
489

490 491
    if (!infoPtr && (uMsg != WM_CREATE))
        return DefWindowProcW (hwnd, uMsg, wParam, lParam);
492

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
    switch (uMsg)
    {
	case WM_CREATE:
	    return IPADDRESS_Create (hwnd);

	case WM_DESTROY:
	    return IPADDRESS_Destroy (infoPtr);

	case WM_PAINT:
	    return IPADDRESS_Paint (infoPtr, (HDC)wParam);

	case WM_COMMAND:
	    switch(wParam >> 16) {
		case EN_CHANGE:
		    IPADDRESS_Notify(infoPtr, EN_CHANGE);
		    break;
		case EN_KILLFOCUS:
		    IPADDRESS_ConstrainField(infoPtr, IPADDRESS_GetPartIndex(infoPtr, (HWND)lParam));
		    break;
	    }
	    break;

        case IPM_CLEARADDRESS:
            IPADDRESS_ClearAddress (infoPtr);
	    break;

        case IPM_SETADDRESS:
            return IPADDRESS_SetAddress (infoPtr, (DWORD)lParam);

        case IPM_GETADDRESS:
 	    return IPADDRESS_GetAddress (infoPtr, (LPDWORD)lParam);

	case IPM_SETRANGE:
	    return IPADDRESS_SetRange (infoPtr, (int)wParam, (WORD)lParam);

	case IPM_SETFOCUS:
	    IPADDRESS_SetFocusToField (infoPtr, (int)wParam);
	    break;

	case IPM_ISBLANK:
	    return IPADDRESS_IsBlank (infoPtr);

	default:
	    if (uMsg >= WM_USER)
		ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
	    return DefWindowProcW (hwnd, uMsg, wParam, lParam);
539 540 541 542 543
    }
    return 0;
}


544
void IPADDRESS_Register (void)
545
{
546 547 548 549 550 551 552 553 554 555
    WNDCLASSW wndClass;

    ZeroMemory (&wndClass, sizeof(WNDCLASSW));
    wndClass.style         = CS_GLOBALCLASS;
    wndClass.lpfnWndProc   = (WNDPROC)IPADDRESS_WindowProc;
    wndClass.cbClsExtra    = 0;
    wndClass.cbWndExtra    = sizeof(IPADDRESS_INFO *);
    wndClass.hCursor       = LoadCursorW (0, IDC_IBEAMW);
    wndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
    wndClass.lpszClassName = WC_IPADDRESSW;
556

557
    RegisterClassW (&wndClass);
558 559
}

560

561
void IPADDRESS_Unregister (void)
562
{
563
    UnregisterClassW (WC_IPADDRESSW, (HINSTANCE)NULL);
564
}