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