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