#pragma pack(?) changed to #include "*pack*.h"
[wine] / dlls / comctl32 / ipaddress.c
1 /*
2  * IP Address control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 1998 Alex Priem <alexp@sci.kun.nl>
6  *
7  * NOTES
8
9  *
10  * TODO:
11  *    -Check ranges when changing field-focus.
12  *        -Check all notifications/behavior.
13  *    -Optimization: include lpipsi in IPADDRESS_INFO.
14  *        -CurrentFocus: field that has focus at moment of processing.
15  *        -connect Rect32 rcClient.
16  *        -handle right and left arrows correctly. Boring.
17  *        -split GotoNextField in CheckField and GotoNextField.
18  *        -check ipaddress.cpp for missing features.
19  *    -refresh: draw '.' instead of setpixel.
20  *        -handle VK_ESCAPE.
21  */
22
23 #include <ctype.h>
24 #include <stdlib.h>
25
26 #include "winbase.h"
27 #include "commctrl.h"
28 #include "ipaddress.h"
29 #include "heap.h"
30 #include "debug.h"
31
32 DEFAULT_DEBUG_CHANNEL(ipaddress)
33
34
35 #define IPADDRESS_GetInfoPtr(hwnd) ((IPADDRESS_INFO *)GetWindowLongA (hwnd, 0))
36
37
38 static BOOL 
39 IPADDRESS_SendNotify (HWND hwnd, UINT command);
40 static BOOL 
41 IPADDRESS_SendIPAddressNotify (HWND hwnd, UINT field, BYTE newValue);
42
43
44 /* property name of tooltip window handle */
45 #define IP_SUBCLASS_PROP "CCIP32SubclassInfo"
46
47
48 static LRESULT CALLBACK
49 IPADDRESS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
50
51
52
53
54 static VOID
55 IPADDRESS_Refresh (HWND hwnd, HDC hdc)
56 {
57         RECT rcClient;
58         HBRUSH hbr;
59         COLORREF clr=GetSysColor (COLOR_3DDKSHADOW);
60     int i,x,fieldsize;
61
62     GetClientRect (hwnd, &rcClient);
63         hbr =  CreateSolidBrush (RGB(255,255,255));
64     DrawEdge (hdc, &rcClient, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
65         FillRect (hdc, &rcClient, hbr);
66     DeleteObject (hbr);
67
68         x=rcClient.left;
69         fieldsize=(rcClient.right-rcClient.left) /4;
70
71         for (i=0; i<3; i++) {           /* Should draw text "." here */
72                 x+=fieldsize;
73                 SetPixel (hdc, x,   13, clr);
74                 SetPixel (hdc, x,   14, clr);
75                 SetPixel (hdc, x+1, 13, clr);
76                 SetPixel (hdc, x+1, 14, clr);
77
78         }
79
80 }
81
82
83
84
85
86 static LRESULT
87 IPADDRESS_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
88 {
89     IPADDRESS_INFO *infoPtr;
90         RECT rcClient, edit;
91         int i,fieldsize;
92         LPIP_SUBCLASS_INFO lpipsi;
93         
94
95     infoPtr = (IPADDRESS_INFO *)COMCTL32_Alloc (sizeof(IPADDRESS_INFO));
96     SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
97
98     GetClientRect (hwnd, &rcClient);
99
100         fieldsize=(rcClient.right-rcClient.left) /4;
101
102         edit.top   =rcClient.top+2;
103         edit.bottom=rcClient.bottom-2;
104
105         lpipsi=(LPIP_SUBCLASS_INFO)
106                         GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP);
107         if (lpipsi == NULL)  {
108                 lpipsi= (LPIP_SUBCLASS_INFO) COMCTL32_Alloc (sizeof(IP_SUBCLASS_INFO));
109                 lpipsi->hwnd = hwnd;
110                 lpipsi->uRefCount++;
111                 SetPropA ((HWND)hwnd, IP_SUBCLASS_PROP,
112                                         (HANDLE)lpipsi);
113 /*              infoPtr->lpipsi= lpipsi; */
114         } else 
115                 WARN (ipaddress,"IP-create called twice\n");
116         
117         for (i=0; i<=3; i++) {
118                 infoPtr->LowerLimit[i]=0;
119                 infoPtr->UpperLimit[i]=255;
120                 edit.left=rcClient.left+i*fieldsize+3;
121                 edit.right=rcClient.left+(i+1)*fieldsize-2;
122                 lpipsi->hwndIP[i]= CreateWindowA ("edit", NULL, 
123                                 WS_CHILD | WS_VISIBLE | ES_LEFT,
124                                 edit.left, edit.top, edit.right-edit.left, edit.bottom-edit.top,
125                                 hwnd, (HMENU) 1, GetWindowLongA (hwnd, GWL_HINSTANCE), NULL);
126                 lpipsi->wpOrigProc[i]= (WNDPROC)
127                                         SetWindowLongA (lpipsi->hwndIP[i],GWL_WNDPROC, (LONG)
128                                         IPADDRESS_SubclassProc);
129                 SetPropA ((HWND)lpipsi->hwndIP[i], IP_SUBCLASS_PROP,
130                                         (HANDLE)lpipsi);
131         }
132
133         lpipsi->infoPtr= infoPtr;
134
135     return 0;
136 }
137
138
139 static LRESULT
140 IPADDRESS_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
141 {
142         int i;
143     IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd);
144         LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO)
145             GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP);
146
147         for (i=0; i<=3; i++) {
148                 SetWindowLongA ((HWND)lpipsi->hwndIP[i], GWL_WNDPROC,
149                   (LONG)lpipsi->wpOrigProc[i]);
150         }
151
152     COMCTL32_Free (infoPtr);
153     return 0;
154 }
155
156
157 static LRESULT
158 IPADDRESS_KillFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
159 {
160     HDC hdc;
161
162         TRACE (ipaddress,"\n");
163     hdc = GetDC (hwnd);
164     IPADDRESS_Refresh (hwnd, hdc);
165     ReleaseDC (hwnd, hdc);
166
167     IPADDRESS_SendIPAddressNotify (hwnd, 0, 0);  /* FIXME: should use -1 */
168     IPADDRESS_SendNotify (hwnd, EN_KILLFOCUS);       
169     InvalidateRect (hwnd, NULL, TRUE);
170
171     return 0;
172 }
173
174
175 static LRESULT
176 IPADDRESS_Paint (HWND hwnd, WPARAM wParam)
177 {
178     HDC hdc;
179     PAINTSTRUCT ps;
180
181     hdc = wParam==0 ? BeginPaint (hwnd, &ps) : (HDC)wParam;
182     IPADDRESS_Refresh (hwnd, hdc);
183     if(!wParam)
184         EndPaint (hwnd, &ps);
185     return 0;
186 }
187
188
189 static LRESULT
190 IPADDRESS_SetFocus (HWND hwnd, WPARAM wParam, LPARAM lParam)
191 {
192     HDC hdc;
193
194         TRACE (ipaddress,"\n");
195
196     hdc = GetDC (hwnd);
197     IPADDRESS_Refresh (hwnd, hdc);
198     ReleaseDC (hwnd, hdc);
199
200     return 0;
201 }
202
203
204 static LRESULT
205 IPADDRESS_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
206 {
207     /* IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd); */
208         TRACE (ipaddress,"\n");
209     return 0;
210 }
211
212
213 static BOOL
214 IPADDRESS_SendNotify (HWND hwnd, UINT command)
215
216 {
217     TRACE (ipaddress, "%x\n",command);
218     return (BOOL)SendMessageA (GetParent (hwnd), WM_COMMAND,
219               MAKEWPARAM (GetWindowLongA (hwnd, GWL_ID),command), (LPARAM)hwnd);
220 }
221
222
223 static BOOL
224 IPADDRESS_SendIPAddressNotify (HWND hwnd, UINT field, BYTE newValue)
225 {
226         NMIPADDRESS nmip;
227
228     TRACE (ipaddress, "%x %x\n",field,newValue);
229     nmip.hdr.hwndFrom = hwnd;
230     nmip.hdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
231     nmip.hdr.code     = IPN_FIELDCHANGED;
232
233         nmip.iField=field;
234         nmip.iValue=newValue;
235
236     return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
237                                (WPARAM)nmip.hdr.idFrom, (LPARAM)&nmip);
238 }
239
240
241
242
243 static LRESULT
244 IPADDRESS_ClearAddress (HWND hwnd, WPARAM wParam, LPARAM lParam)
245 {
246         int i;
247         HDC hdc;
248         char buf[1];
249         LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO)
250             GetPropA ((HWND)hwnd,IP_SUBCLASS_PROP);
251
252         TRACE (ipaddress,"\n");
253
254         buf[0]=0;
255         for (i=0; i<=3; i++) 
256                 SetWindowTextA (lpipsi->hwndIP[i],buf);
257         
258     hdc = GetDC (hwnd);
259     IPADDRESS_Refresh (hwnd, hdc);
260     ReleaseDC (hwnd, hdc);
261         
262         return 0;
263 }
264
265 static LRESULT
266 IPADDRESS_IsBlank (HWND hwnd, WPARAM wParam, LPARAM lParam)
267 {
268  int i;
269  char buf[20];
270  LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO)
271             GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP);
272
273  TRACE (ipaddress,"\n");
274
275  for (i=0; i<=3; i++) {
276                 GetWindowTextA (lpipsi->hwndIP[i],buf,5);
277         if (buf[0])
278             return 0;
279         }
280
281  return 1;
282 }
283
284 static LRESULT
285 IPADDRESS_GetAddress (HWND hwnd, WPARAM wParam, LPARAM lParam)
286 {
287  char field[20];
288  int i,valid,fieldvalue;
289  DWORD ip_addr;
290  IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd);
291  LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO)
292             GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP);
293
294  TRACE (ipaddress,"\n");
295
296  valid=0;
297  ip_addr=0;
298  for (i=0; i<=3; i++) {
299                 GetWindowTextA (lpipsi->hwndIP[i],field,4);
300                 ip_addr*=256;
301                 if (field[0]) {
302                         field[3]=0;
303                         fieldvalue=atoi(field);
304                         if (fieldvalue<infoPtr->LowerLimit[i]) 
305                                 fieldvalue=infoPtr->LowerLimit[i];
306                         if (fieldvalue>infoPtr->UpperLimit[i]) 
307                                 fieldvalue=infoPtr->UpperLimit[i];
308                         ip_addr+=fieldvalue;
309                         valid++;
310                 }
311  }
312
313  *(LPDWORD) lParam=ip_addr;
314
315  return valid;
316 }
317
318
319 static LRESULT
320 IPADDRESS_SetRange (HWND hwnd, WPARAM wParam, LPARAM lParam)
321 {
322     IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd);
323         INT index;
324         
325         TRACE (ipaddress,"\n");
326
327         index=(INT) wParam;
328         if ((index<0) || (index>3)) return 0;
329
330         infoPtr->LowerLimit[index]=lParam & 0xff;
331         infoPtr->UpperLimit[index]=(lParam >>8)  & 0xff;
332         return 1;
333 }
334
335 static LRESULT
336 IPADDRESS_SetAddress (HWND hwnd, WPARAM wParam, LPARAM lParam)
337 {
338     IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd);
339         HDC hdc;
340         LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO)
341             GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP);
342         int i,ip_address,value;
343     char buf[20];
344
345         TRACE (ipaddress,"\n");
346         ip_address=(DWORD) lParam;
347
348         for (i=3; i>=0; i--) {
349                 value=ip_address & 0xff;
350                 if ((value>=infoPtr->LowerLimit[i]) && (value<=infoPtr->UpperLimit[i])) 
351                         {
352                          sprintf (buf,"%d",value);
353                          SetWindowTextA (lpipsi->hwndIP[i],buf);
354                          IPADDRESS_SendNotify (hwnd, EN_CHANGE);
355                 }
356                 ip_address/=256;
357         }
358
359     hdc = GetDC (hwnd);         /* & send notifications */
360     IPADDRESS_Refresh (hwnd, hdc);
361     ReleaseDC (hwnd, hdc);
362
363  return TRUE;
364 }
365
366
367
368
369 static LRESULT
370 IPADDRESS_SetFocusToField (HWND hwnd, WPARAM wParam, LPARAM lParam)
371 {
372     /* IPADDRESS_INFO *infoPtr = IPADDRESS_GetInfoPtr (hwnd); */
373         LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO)
374             GetPropA ((HWND)hwnd, IP_SUBCLASS_PROP);
375         INT index;
376
377         index=(INT) wParam;
378         TRACE (ipaddress," %d\n", index);
379         if ((index<0) || (index>3)) return 0;
380         
381         SetFocus (lpipsi->hwndIP[index]);
382         
383     return 1;
384 }
385
386
387 static LRESULT
388 IPADDRESS_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
389 {
390     TRACE (ipaddress, "\n");
391
392         SetFocus (hwnd);
393         IPADDRESS_SendNotify (hwnd, EN_SETFOCUS);
394         IPADDRESS_SetFocusToField (hwnd, 0, 0);
395
396         return TRUE;
397 }
398
399
400
401 /* tab/shift-tab: IPN_FIELDCHANGED, lose focus.
402    dot, space,right arrow:      set focus to next child edit.
403    numerics (0..9), control characters: forward to default edit control 
404    other characters: dropped
405 */
406    
407
408
409
410 static int
411 IPADDRESS_GotoNextField (LPIP_SUBCLASS_INFO lpipsi, int currentfield)
412 {
413  int newField,fieldvalue;
414  char field[20];
415  IPADDRESS_INFO *infoPtr=lpipsi->infoPtr;
416
417  TRACE (ipaddress,"\n");
418  GetWindowTextA (lpipsi->hwndIP[currentfield],field,4);
419  if (field[0]) {
420         field[3]=0;     
421         newField=-1;
422         fieldvalue=atoi(field);
423         if (fieldvalue<infoPtr->LowerLimit[currentfield]) 
424                 newField=infoPtr->LowerLimit[currentfield];
425         if (fieldvalue>infoPtr->UpperLimit[currentfield])
426                 newField=infoPtr->UpperLimit[currentfield];
427         if (newField>=0) {
428                 sprintf (field,"%d",newField);
429                 SetWindowTextA (lpipsi->hwndIP[currentfield], field);
430                 return 1;
431         }
432  }
433
434  if (currentfield<3) 
435                 SetFocus (lpipsi->hwndIP[currentfield+1]);
436  return 0;
437 }
438
439
440 LRESULT CALLBACK
441 IPADDRESS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
442 {
443  int i,l,index;
444  IPADDRESS_INFO *infoPtr;
445  LPIP_SUBCLASS_INFO lpipsi=(LPIP_SUBCLASS_INFO)
446             GetPropA ((HWND)hwnd,IP_SUBCLASS_PROP); 
447
448  infoPtr = lpipsi->infoPtr;
449  index=0;             /* FIXME */
450  for (i=0; i<=3; i++) 
451                 if (lpipsi->hwndIP[i]==hwnd) index=i;
452
453  switch (uMsg) {
454         case WM_CHAR: break;
455         case WM_KEYDOWN: {
456                         char c=(char) wParam;
457                         if (c==VK_TAB) {
458                                 HWND pwnd;
459                                 int shift;
460                                 shift = GetKeyState(VK_SHIFT) & 0x8000;
461                                 if (shift)
462                                         pwnd=GetNextDlgTabItem (GetParent (hwnd), 0, TRUE);
463                                 else
464                                         pwnd=GetNextDlgTabItem (GetParent (hwnd), 0, FALSE);
465                                 if (pwnd) SetFocus (pwnd);
466                                 break;
467                         }
468                         
469                         if ((c==' ') || (c=='.') || (c==VK_RIGHT)) {
470                                 IPADDRESS_GotoNextField (lpipsi,index);
471                                 wParam=0;
472                                 lParam=0;
473                                 break;
474                         }
475                         if (c==VK_LEFT) {
476                                 
477                         }
478                         if (c==VK_RETURN) {
479                         }
480                         if (((c>='0') && (c<='9')) || (iscntrl(c))) {
481                                 l=GetWindowTextLengthA (lpipsi->hwndIP[index]);
482                                 if (l==3) 
483                                         if (IPADDRESS_GotoNextField (lpipsi,index)) {
484                                                 wParam=0;
485                                                 lParam=0;
486                                         }
487                                 break;
488                         }
489         
490                         wParam=0;
491                         lParam=0;
492                         break;
493                 }
494  }
495
496  return CallWindowProcA (lpipsi->wpOrigProc[index], hwnd, uMsg, wParam, lParam);
497 }
498
499 LRESULT WINAPI
500 IPADDRESS_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
501 {
502     switch (uMsg)
503     {
504         case IPM_CLEARADDRESS:
505                 return IPADDRESS_ClearAddress (hwnd, wParam, lParam);
506
507         case IPM_SETADDRESS:
508             return IPADDRESS_SetAddress (hwnd, wParam, lParam);
509
510         case IPM_GETADDRESS:
511             return IPADDRESS_GetAddress (hwnd, wParam, lParam);
512
513         case IPM_SETRANGE:
514             return IPADDRESS_SetRange (hwnd, wParam, lParam);
515
516         case IPM_SETFOCUS:
517             return IPADDRESS_SetFocusToField (hwnd, wParam, lParam);
518
519         case IPM_ISBLANK:
520                 return IPADDRESS_IsBlank (hwnd, wParam, lParam);
521
522         case WM_CREATE:
523             return IPADDRESS_Create (hwnd, wParam, lParam);
524
525         case WM_DESTROY:
526             return IPADDRESS_Destroy (hwnd, wParam, lParam);
527
528         case WM_GETDLGCODE:
529             return DLGC_WANTARROWS | DLGC_WANTCHARS;
530
531         case WM_KILLFOCUS:
532             return IPADDRESS_KillFocus (hwnd, wParam, lParam);
533
534         case WM_LBUTTONDOWN:
535         return IPADDRESS_LButtonDown (hwnd, wParam, lParam);
536
537         case WM_PAINT:
538             return IPADDRESS_Paint (hwnd, wParam);
539
540         case WM_SETFOCUS:
541             return IPADDRESS_SetFocus (hwnd, wParam, lParam);
542
543         case WM_SIZE:
544             return IPADDRESS_Size (hwnd, wParam, lParam);
545
546         default:
547             if (uMsg >= WM_USER)
548                 ERR (ipaddress, "unknown msg %04x wp=%08x lp=%08lx\n",
549                      uMsg, wParam, lParam);
550             return DefWindowProcA (hwnd, uMsg, wParam, lParam);
551     }
552     return 0;
553 }
554
555
556 void
557 IPADDRESS_Register (void)
558 {
559     WNDCLASSA wndClass;
560
561     if (GlobalFindAtomA (WC_IPADDRESSA)) return;
562
563     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
564     wndClass.style         = CS_GLOBALCLASS;
565     wndClass.lpfnWndProc   = (WNDPROC)IPADDRESS_WindowProc;
566     wndClass.cbClsExtra    = 0;
567     wndClass.cbWndExtra    = sizeof(IPADDRESS_INFO *);
568     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
569     wndClass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
570     wndClass.lpszClassName = WC_IPADDRESSA;
571  
572     RegisterClassA (&wndClass);
573 }
574
575 VOID
576 IPADDRESS_Unregister (VOID)
577 {
578     if (GlobalFindAtomA (WC_IPADDRESSA))
579     UnregisterClassA (WC_IPADDRESSA, (HINSTANCE)NULL);
580 }
581