Free allocated font handles when control is destroyed.
[wine/multimedia.git] / dlls / comctl32 / syslink.c
blob437c597520474d891b46b8fb0bffd5e30b133749
1 /*
2 * SysLink control
4 * Copyright 2004 Thomas Weidenmueller <w3seek@reactos.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * TODO:
21 * - Fix SHIFT+TAB and TAB issue (wrong link is selected when control gets the focus)
22 * - Better string parsing
23 * - Improve word wrapping
24 * - Control styles?!
28 #include <stdarg.h>
29 #include <string.h>
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "winnls.h"
35 #include "commctrl.h"
36 #include "comctl32.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(progress);
42 INT WINAPI StrCmpNIW(LPCWSTR,LPCWSTR,INT);
44 #define SYSLINK_Alloc(size) HeapAlloc(GetProcessHeap(), 0, (size))
45 #define SYSLINK_Free(ptr) HeapFree(GetProcessHeap(), 0, (ptr))
46 #define SYSLINK_ReAlloc(ptr, size) HeapReAlloc(GetProcessHeap(), 0, ptr, (size))
48 typedef struct
50 int nChars;
51 RECT rc;
52 } DOC_TEXTBLOCK, *PDOC_TEXTBLOCK;
54 #define LIF_FLAGSMASK (LIF_STATE | LIF_ITEMID | LIF_URL)
55 #define LIS_MASK (LIS_FOCUSED | LIS_ENABLED | LIS_VISITED)
57 typedef enum
59 slText = 0,
60 slLink
61 } SL_ITEM_TYPE;
63 typedef struct _DOC_ITEM
65 struct _DOC_ITEM *Next; /* Address to the next item */
66 LPWSTR Text; /* Text of the document item */
67 UINT nText; /* Number of characters of the text */
68 SL_ITEM_TYPE Type; /* type of the item */
69 PDOC_TEXTBLOCK Blocks; /* Array of text blocks */
70 union
72 struct
74 UINT state; /* Link state */
75 WCHAR *szID; /* Link ID string */
76 WCHAR *szUrl; /* Link URL string */
77 HRGN hRgn; /* Region of the link */
78 } Link;
79 struct
81 UINT Dummy;
82 } Text;
83 } u;
84 } DOC_ITEM, *PDOC_ITEM;
86 typedef struct
88 HWND Self; /* The window handle for this control */
89 PDOC_ITEM Items; /* Address to the first document item */
90 BOOL HasFocus; /* Whether the control has the input focus */
91 int MouseDownID; /* ID of the link that the mouse button first selected */
92 HFONT Font; /* Handle to the font for text */
93 HFONT LinkFont; /* Handle to the font for links */
94 COLORREF TextColor; /* Color of the text */
95 COLORREF LinkColor; /* Color of links */
96 COLORREF VisitedColor; /* Color of visited links */
97 } SYSLINK_INFO;
99 static const WCHAR SL_LINKOPEN[] = { '<','a', 0 };
100 static const WCHAR SL_HREF[] = { 'h','r','e','f','=','\"',0 };
101 static const WCHAR SL_ID[] = { 'i','d','=','\"',0 };
102 static const WCHAR SL_LINKCLOSE[] = { '<','/','a','>',0 };
104 /* Control configuration constants */
106 #define SL_LEFTMARGIN (0)
107 #define SL_TOPMARGIN (0)
108 #define SL_RIGHTMARGIN (0)
109 #define SL_BOTTOMMARGIN (0)
111 /***********************************************************************
112 * SYSLINK_FreeDocItem
113 * Frees all data and gdi objects associated with a document item
115 static VOID SYSLINK_FreeDocItem (PDOC_ITEM DocItem)
117 if(DocItem->Type == slLink)
119 if(DocItem->u.Link.szID != NULL)
121 SYSLINK_Free(DocItem->u.Link.szID);
123 if(DocItem->u.Link.szUrl != NULL)
125 SYSLINK_Free(DocItem->u.Link.szUrl);
129 if(DocItem->Type == slLink && DocItem->u.Link.hRgn != NULL)
131 DeleteObject(DocItem->u.Link.hRgn);
134 /* we don't free Text because it's just a pointer to a character in the
135 entire window text string */
137 SYSLINK_Free(DocItem);
140 /***********************************************************************
141 * SYSLINK_AppendDocItem
142 * Create and append a new document item.
144 static PDOC_ITEM SYSLINK_AppendDocItem (SYSLINK_INFO *infoPtr, LPWSTR Text, UINT textlen,
145 SL_ITEM_TYPE type, PDOC_ITEM LastItem)
147 PDOC_ITEM Item;
148 Item = SYSLINK_Alloc(sizeof(DOC_ITEM) + ((textlen + 1) * sizeof(WCHAR)));
149 if(Item == NULL)
151 ERR("Failed to alloc DOC_ITEM structure!\n");
152 return NULL;
154 textlen = min(textlen, lstrlenW(Text));
156 Item->Next = NULL;
157 Item->Text = (LPWSTR)(Item + 1);
158 Item->nText = textlen;
159 Item->Type = type;
160 Item->Blocks = NULL;
162 if(LastItem != NULL)
164 LastItem->Next = Item;
166 else
168 infoPtr->Items = Item;
171 lstrcpynW(Item->Text, Text, textlen + 1);
172 Item->Text[textlen] = 0;
174 return Item;
177 /***********************************************************************
178 * SYSLINK_ClearDoc
179 * Clears the document tree
181 static VOID SYSLINK_ClearDoc (SYSLINK_INFO *infoPtr)
183 PDOC_ITEM Item, Next;
185 Item = infoPtr->Items;
186 while(Item != NULL)
188 Next = Item->Next;
189 SYSLINK_FreeDocItem(Item);
190 Item = Next;
193 infoPtr->Items = NULL;
196 /***********************************************************************
197 * SYSLINK_ParseText
198 * Parses the window text string and creates a document. Returns the
199 * number of document items created.
201 static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPWSTR Text)
203 WCHAR *current, *textstart, *linktext, *firsttag;
204 int taglen = 0, textlen, linklen, docitems = 0;
205 PDOC_ITEM Last = NULL;
206 SL_ITEM_TYPE CurrentType = slText;
207 DWORD Style;
208 LPWSTR lpID, lpUrl;
209 UINT lenId, lenUrl;
211 Style = GetWindowLongW(infoPtr->Self, GWL_STYLE);
213 firsttag = NULL;
214 textstart = NULL;
215 linktext = NULL;
216 textlen = 0;
217 linklen = 0;
219 for(current = (WCHAR*)Text; *current != 0;)
221 if(*current == '<')
223 if(!StrCmpNIW(current, SL_LINKOPEN, 2) && (CurrentType == slText))
225 BOOL ValidParam = FALSE, ValidLink = FALSE;
227 switch (*(current + 2))
229 case '>':
230 /* we just have to deal with a <a> tag */
231 taglen = 3;
232 ValidLink = TRUE;
233 ValidParam = TRUE;
234 firsttag = current;
235 linklen = 0;
236 lpID = NULL;
237 lpUrl = NULL;
238 break;
239 case ' ':
241 /* we expect parameters, parse them */
242 LPWSTR *CurrentParameter = NULL;
243 UINT *CurrentParameterLen = NULL;
244 WCHAR *tmp;
246 taglen = 3;
247 tmp = current + taglen;
248 lpID = NULL;
249 lpUrl = NULL;
251 CheckParameter:
252 /* compare the current position with all known parameters */
253 if(!StrCmpNIW(tmp, SL_HREF, 6))
255 taglen += 6;
256 ValidParam = TRUE;
257 CurrentParameter = &lpUrl;
258 CurrentParameterLen = &lenUrl;
260 else if(!StrCmpNIW(tmp, SL_ID, 4))
262 taglen += 4;
263 ValidParam = TRUE;
264 CurrentParameter = &lpID;
265 CurrentParameterLen = &lenId;
267 else
269 ValidParam = FALSE;
272 if(ValidParam)
274 /* we got a known parameter, now search until the next " character.
275 If we can't find a " character, there's a syntax error and we just assume it's text */
276 ValidParam = FALSE;
277 *CurrentParameter = current + taglen;
278 *CurrentParameterLen = 0;
280 for(tmp = *CurrentParameter; *tmp != 0; tmp++)
282 taglen++;
283 if(*tmp == '\"')
285 ValidParam = TRUE;
286 tmp++;
287 break;
289 (*CurrentParameterLen)++;
292 if(ValidParam)
294 /* we're done with this parameter, now there are only 2 possibilities:
295 * 1. another parameter is coming, so expect a ' ' (space) character
296 * 2. the tag is being closed, so expect a '<' character
298 switch(*tmp)
300 case ' ':
301 /* we expect another parameter, do the whole thing again */
302 taglen++;
303 tmp++;
304 goto CheckParameter;
306 case '>':
307 /* the tag is being closed, we're done */
308 ValidLink = TRUE;
309 taglen++;
310 break;
311 default:
312 tmp++;
313 break;
317 break;
321 if(ValidLink && ValidParam)
323 /* the <a ...> tag appears to be valid. save all information
324 so we can add the link if we find a valid </a> tag later */
325 CurrentType = slLink;
326 linktext = current + taglen;
327 linklen = 0;
328 firsttag = current;
330 else
332 taglen = 1;
333 lpID = NULL;
334 lpUrl = NULL;
335 if(textstart == NULL)
337 textstart = current;
341 else if(!StrCmpNIW(current, SL_LINKCLOSE, 4) && (CurrentType == slLink) && firsttag)
343 /* there's a <a...> tag opened, first add the previous text, if present */
344 if(textstart != NULL && textlen > 0 && firsttag > textstart)
346 Last = SYSLINK_AppendDocItem(infoPtr, textstart, firsttag - textstart, slText, Last);
347 if(Last == NULL)
349 ERR("Unable to create new document item!\n");
350 return docitems;
352 docitems++;
353 textstart = NULL;
354 textlen = 0;
357 /* now it's time to add the link to the document */
358 current += 4;
359 if(linktext != NULL && linklen > 0)
361 Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slLink, Last);
362 if(Last == NULL)
364 ERR("Unable to create new document item!\n");
365 return docitems;
367 docitems++;
368 if(CurrentType == slLink)
370 int nc;
372 if(!(Style & WS_DISABLED))
374 Last->u.Link.state |= LIS_ENABLED;
376 /* Copy the tag parameters */
377 if(lpID != NULL)
379 nc = min(lenId, strlenW(lpID));
380 nc = min(nc, MAX_LINKID_TEXT);
381 Last->u.Link.szID = SYSLINK_Alloc((MAX_LINKID_TEXT + 1) * sizeof(WCHAR));
382 if(Last->u.Link.szID != NULL)
384 lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
385 Last->u.Link.szID[nc] = 0;
388 else
389 Last->u.Link.szID = NULL;
390 if(lpUrl != NULL)
392 nc = min(lenUrl, strlenW(lpUrl));
393 nc = min(nc, L_MAX_URL_LENGTH);
394 Last->u.Link.szUrl = SYSLINK_Alloc((L_MAX_URL_LENGTH + 1) * sizeof(WCHAR));
395 if(Last->u.Link.szUrl != NULL)
397 lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
398 Last->u.Link.szUrl[nc] = 0;
401 else
402 Last->u.Link.szUrl = NULL;
404 linktext = NULL;
406 CurrentType = slText;
407 firsttag = NULL;
408 textstart = NULL;
409 continue;
411 else
413 /* we don't know what tag it is, so just continue */
414 taglen = 1;
415 linklen++;
416 if(CurrentType == slText && textstart == NULL)
418 textstart = current;
422 textlen += taglen;
423 current += taglen;
425 else
427 textlen++;
428 linklen++;
430 /* save the pointer of the current text item if we couldn't find a tag */
431 if(textstart == NULL && CurrentType == slText)
433 textstart = current;
436 current++;
440 if(textstart != NULL && textlen > 0)
442 Last = SYSLINK_AppendDocItem(infoPtr, textstart, textlen, CurrentType, Last);
443 if(Last == NULL)
445 ERR("Unable to create new document item!\n");
446 return docitems;
448 if(CurrentType == slLink)
450 int nc;
452 if(!(Style & WS_DISABLED))
454 Last->u.Link.state |= LIS_ENABLED;
456 /* Copy the tag parameters */
457 if(lpID != NULL)
459 nc = min(lenId, strlenW(lpID));
460 nc = min(nc, MAX_LINKID_TEXT);
461 Last->u.Link.szID = SYSLINK_Alloc((MAX_LINKID_TEXT + 1) * sizeof(WCHAR));
462 if(Last->u.Link.szID != NULL)
464 lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
465 Last->u.Link.szID[nc] = 0;
468 else
469 Last->u.Link.szID = NULL;
470 if(lpUrl != NULL)
472 nc = min(lenUrl, strlenW(lpUrl));
473 nc = min(nc, L_MAX_URL_LENGTH);
474 Last->u.Link.szUrl = SYSLINK_Alloc((L_MAX_URL_LENGTH + 1) * sizeof(WCHAR));
475 if(Last->u.Link.szUrl != NULL)
477 lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
478 Last->u.Link.szUrl[nc] = 0;
481 else
482 Last->u.Link.szUrl = NULL;
484 docitems++;
487 if(linktext != NULL && linklen > 0)
489 /* we got a unclosed link, just display the text */
490 Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slText, Last);
491 if(Last == NULL)
493 ERR("Unable to create new document item!\n");
494 return docitems;
496 docitems++;
499 return docitems;
502 /***********************************************************************
503 * SYSLINK_RepaintLink
504 * Repaints a link.
506 static VOID SYSLINK_RepaintLink (SYSLINK_INFO *infoPtr, PDOC_ITEM DocItem)
508 if(DocItem->Type != slLink)
510 ERR("DocItem not a link!\n");
511 return;
514 if(DocItem->u.Link.hRgn != NULL)
516 /* repaint the region */
517 RedrawWindow(infoPtr->Self, NULL, DocItem->u.Link.hRgn, RDW_INVALIDATE | RDW_UPDATENOW);
521 /***********************************************************************
522 * SYSLINK_GetLinkItemByIndex
523 * Retreives a document link by it's index
525 static PDOC_ITEM SYSLINK_GetLinkItemByIndex (SYSLINK_INFO *infoPtr, int iLink)
527 PDOC_ITEM Current = infoPtr->Items;
529 while(Current != NULL)
531 if((Current->Type == slLink) && (iLink-- <= 0))
533 return Current;
535 Current = Current->Next;
537 return NULL;
540 /***********************************************************************
541 * SYSLINK_GetFocusLink
542 * Retreives the link that has the LIS_FOCUSED bit
544 static PDOC_ITEM SYSLINK_GetFocusLink (SYSLINK_INFO *infoPtr, int *LinkId)
546 PDOC_ITEM Current = infoPtr->Items;
547 int id = 0;
549 while(Current != NULL)
551 if((Current->Type == slLink))
553 if(Current->u.Link.state & LIS_FOCUSED)
555 if(LinkId != NULL)
556 *LinkId = id;
557 return Current;
559 id++;
561 Current = Current->Next;
563 return NULL;
566 /***********************************************************************
567 * SYSLINK_GetNextLink
568 * Gets the next link
570 static PDOC_ITEM SYSLINK_GetNextLink (SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
572 for(Current = (Current != NULL ? Current->Next : infoPtr->Items);
573 Current != NULL;
574 Current = Current->Next)
576 if(Current->Type == slLink)
578 return Current;
581 return NULL;
584 /***********************************************************************
585 * SYSLINK_GetPrevLink
586 * Gets the previous link
588 static PDOC_ITEM SYSLINK_GetPrevLink (SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
590 if(Current == NULL)
592 /* returns the last link */
593 PDOC_ITEM Last = NULL;
595 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
597 if(Current->Type == slLink)
599 Last = Current;
602 return Last;
604 else
606 /* returns the previous link */
607 PDOC_ITEM Cur, Prev = NULL;
609 for(Cur = infoPtr->Items; Cur != NULL; Cur = Cur->Next)
611 if(Cur == Current)
613 break;
615 if(Cur->Type == slLink)
617 Prev = Cur;
620 return Prev;
624 /***********************************************************************
625 * SYSLINK_WrapLine
626 * Tries to wrap a line.
628 static BOOL SYSLINK_WrapLine (HDC hdc, LPWSTR Text, WCHAR BreakChar, int *LineLen, int nFit, LPSIZE Extent, int Width)
630 WCHAR *Current;
632 if(nFit == *LineLen)
634 return FALSE;
637 *LineLen = nFit;
639 Current = Text + nFit;
641 /* check if we're in the middle of a word */
642 if((*Current) != BreakChar)
644 /* search for the beginning of the word */
645 while(Current > Text && (*(Current - 1)) != BreakChar)
647 Current--;
648 (*LineLen)--;
651 if((*LineLen) == 0)
653 Extent->cx = 0;
654 Extent->cy = 0;
656 return TRUE;
659 return TRUE;
662 /***********************************************************************
663 * SYSLINK_Render
664 * Renders the document in memory
666 static VOID SYSLINK_Render (SYSLINK_INFO *infoPtr, HDC hdc)
668 RECT rc;
669 PDOC_ITEM Current;
670 HGDIOBJ hOldFont;
671 int x, y, LineHeight;
672 TEXTMETRICW tm;
674 GetClientRect(infoPtr->Self, &rc);
675 rc.right -= SL_RIGHTMARGIN;
676 rc.bottom -= SL_BOTTOMMARGIN;
678 if(rc.right - SL_LEFTMARGIN < 0 || rc.bottom - SL_TOPMARGIN < 0) return;
680 hOldFont = SelectObject(hdc, infoPtr->Font);
681 GetTextMetricsW(hdc, &tm);
683 x = SL_LEFTMARGIN;
684 y = SL_TOPMARGIN;
685 LineHeight = 0;
687 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
689 int n, nBlocks;
690 LPWSTR tx;
691 PDOC_TEXTBLOCK bl, cbl;
692 INT nFit;
693 SIZE szDim;
695 if(Current->nText == 0)
697 ERR("DOC_ITEM with no text?!\n");
698 continue;
701 tx = Current->Text;
702 n = Current->nText;
703 bl = Current->Blocks;
704 nBlocks = 0;
706 if(Current->Type == slText)
708 SelectObject(hdc, infoPtr->Font);
710 else if(Current->Type == slLink)
712 SelectObject(hdc, infoPtr->LinkFont);
715 while(n > 0)
717 if(GetTextExtentExPointW(hdc, tx, n, rc.right - x, &nFit, NULL, &szDim))
719 int LineLen = n;
720 BOOL Wrap = SYSLINK_WrapLine(hdc, tx, tm.tmBreakChar, &LineLen, nFit, &szDim, rc.right - x);
722 if(LineLen == 0)
724 if(x > SL_LEFTMARGIN)
726 /* move one line down, the word didn't fit into the line */
727 x = SL_LEFTMARGIN;
728 y += LineHeight;
729 LineHeight = 0;
730 continue;
732 else
734 /* the word starts at the beginning of the line and doesn't
735 fit into the line, so break it at the last character that fits */
736 LineLen = max(nFit, 1);
740 if(LineLen != n)
742 GetTextExtentExPointW(hdc, tx, LineLen, rc.right - x, NULL, NULL, &szDim);
745 if(bl != NULL)
747 bl = SYSLINK_ReAlloc(bl, ++nBlocks * sizeof(DOC_TEXTBLOCK));
749 else
751 bl = SYSLINK_Alloc(++nBlocks * sizeof(DOC_TEXTBLOCK));
754 if(bl != NULL)
756 cbl = bl + nBlocks - 1;
758 cbl->nChars = LineLen;
759 cbl->rc.left = x;
760 cbl->rc.top = y;
761 cbl->rc.right = x + szDim.cx;
762 cbl->rc.bottom = y + szDim.cy;
764 x += szDim.cx;
765 LineHeight = max(LineHeight, szDim.cy);
767 /* (re)calculate the link's region */
768 if(Current->Type == slLink)
770 if(nBlocks <= 1)
772 if(Current->u.Link.hRgn != NULL)
774 DeleteObject(Current->u.Link.hRgn);
776 /* initialize the link's hRgn */
777 Current->u.Link.hRgn = CreateRectRgnIndirect(&cbl->rc);
779 else if(Current->u.Link.hRgn != NULL)
781 HRGN hrgn;
782 hrgn = CreateRectRgnIndirect(&cbl->rc);
783 /* add the rectangle */
784 CombineRgn(Current->u.Link.hRgn, Current->u.Link.hRgn, hrgn, RGN_OR);
785 DeleteObject(hrgn);
789 if(Wrap)
791 x = SL_LEFTMARGIN;
792 y += LineHeight;
793 LineHeight = 0;
796 else
798 ERR("Failed to alloc DOC_TEXTBLOCK structure!\n");
799 break;
801 n -= LineLen;
802 tx += LineLen;
804 else
806 ERR("GetTextExtentExPoint() failed?!\n");
807 n--;
810 Current->Blocks = bl;
813 SelectObject(hdc, hOldFont);
816 /***********************************************************************
817 * SYSLINK_Draw
818 * Draws the SysLink control.
820 static LRESULT SYSLINK_Draw (SYSLINK_INFO *infoPtr, HDC hdc)
822 RECT rc;
823 PDOC_ITEM Current;
824 HFONT hOldFont;
825 COLORREF OldTextColor, OldBkColor;
827 hOldFont = SelectObject(hdc, infoPtr->Font);
828 OldTextColor = SetTextColor(hdc, infoPtr->TextColor);
829 OldBkColor = SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
831 GetClientRect(infoPtr->Self, &rc);
832 rc.right -= SL_RIGHTMARGIN + SL_LEFTMARGIN;
833 rc.bottom -= SL_BOTTOMMARGIN + SL_TOPMARGIN;
835 if(rc.right < 0 || rc.bottom < 0) return 0;
837 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
839 int n;
840 LPWSTR tx;
841 PDOC_TEXTBLOCK bl;
843 bl = Current->Blocks;
844 if(bl != NULL)
846 tx = Current->Text;
847 n = Current->nText;
849 if(Current->Type == slText)
851 SelectObject(hdc, infoPtr->Font);
852 SetTextColor(hdc, infoPtr->TextColor);
854 else
856 SelectObject(hdc, infoPtr->LinkFont);
857 SetTextColor(hdc, (!(Current->u.Link.state & LIS_VISITED) ? infoPtr->LinkColor : infoPtr->VisitedColor));
860 while(n > 0)
862 ExtTextOutW(hdc, bl->rc.left, bl->rc.top, ETO_OPAQUE | ETO_CLIPPED, &bl->rc, tx, bl->nChars, NULL);
863 if((Current->Type == slLink) && (Current->u.Link.state & LIS_FOCUSED) && infoPtr->HasFocus)
865 COLORREF PrevColor;
866 PrevColor = SetBkColor(hdc, OldBkColor);
867 DrawFocusRect(hdc, &bl->rc);
868 SetBkColor(hdc, PrevColor);
870 tx += bl->nChars;
871 n -= bl->nChars;
872 bl++;
877 SetBkColor(hdc, OldBkColor);
878 SetTextColor(hdc, OldTextColor);
879 SelectObject(hdc, hOldFont);
881 return 0;
885 /***********************************************************************
886 * SYSLINK_Paint
887 * Handles the WM_PAINT message.
889 static LRESULT SYSLINK_Paint (SYSLINK_INFO *infoPtr)
891 HDC hdc;
892 PAINTSTRUCT ps;
893 hdc = BeginPaint (infoPtr->Self, &ps);
894 SYSLINK_Draw (infoPtr, hdc);
895 EndPaint (infoPtr->Self, &ps);
896 return 0;
900 /***********************************************************************
901 * SYSLINK_SetFont
902 * Set new Font for the SysLink control.
904 static HFONT SYSLINK_SetFont (SYSLINK_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
906 HDC hdc;
907 LOGFONTW lf;
908 HFONT hOldFont = infoPtr->Font;
909 infoPtr->Font = hFont;
911 /* free the underline font */
912 if(infoPtr->LinkFont != NULL)
914 DeleteObject(infoPtr->LinkFont);
915 infoPtr->LinkFont = NULL;
918 /* Render text position and word wrapping in memory */
919 hdc = GetDC(infoPtr->Self);
920 if(hdc != NULL)
922 /* create a new underline font */
923 if(GetObjectW(infoPtr->Font, sizeof(LOGFONTW), &lf))
925 lf.lfUnderline = TRUE;
926 infoPtr->LinkFont = CreateFontIndirectW(&lf);
928 else
930 ERR("Failed to create link font!\n");
933 SYSLINK_Render(infoPtr, hdc);
934 ReleaseDC(infoPtr->Self, hdc);
937 if(bRedraw)
939 RedrawWindow(infoPtr->Self, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
942 return hOldFont;
945 /***********************************************************************
946 * SYSLINK_SetText
947 * Set new text for the SysLink control.
949 static LRESULT SYSLINK_SetText (SYSLINK_INFO *infoPtr, LPWSTR Text)
951 int textlen;
953 /* clear the document */
954 SYSLINK_ClearDoc(infoPtr);
956 textlen = lstrlenW(Text);
957 if(Text == NULL || textlen == 0)
959 return TRUE;
962 /* let's parse the string and create a document */
963 if(SYSLINK_ParseText(infoPtr, Text) > 0)
965 /* Render text position and word wrapping in memory */
966 HDC hdc = GetDC(infoPtr->Self);
967 SYSLINK_Render(infoPtr, hdc);
968 SYSLINK_Draw(infoPtr, hdc);
969 ReleaseDC(infoPtr->Self, hdc);
972 return TRUE;
975 /***********************************************************************
976 * SYSLINK_SetFocusLink
977 * Updates the focus status bits and focusses the specified link.
978 * If no document item is specified, the focus bit will be removed from all links.
979 * Returns the previous focused item.
981 static PDOC_ITEM SYSLINK_SetFocusLink (SYSLINK_INFO *infoPtr, PDOC_ITEM DocItem)
983 PDOC_ITEM Current, PrevFocus = NULL;
985 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
987 if(Current->Type == slLink)
989 if((PrevFocus == NULL) && (Current->u.Link.state & LIS_FOCUSED))
991 PrevFocus = Current;
994 if(Current == DocItem)
996 Current->u.Link.state |= LIS_FOCUSED;
998 else
1000 Current->u.Link.state &= ~LIS_FOCUSED;
1005 return PrevFocus;
1008 /***********************************************************************
1009 * SYSLINK_SetItem
1010 * Sets the states and attributes of a link item.
1012 static LRESULT SYSLINK_SetItem (SYSLINK_INFO *infoPtr, PLITEM Item)
1014 PDOC_ITEM di;
1015 BOOL Repaint = FALSE;
1016 BOOL Ret = TRUE;
1018 if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
1020 ERR("Invalid Flags!\n");
1021 return FALSE;
1024 di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
1025 if(di == NULL)
1027 ERR("Link %d couldn't be found\n", Item->iLink);
1028 return FALSE;
1031 if(Item->mask & LIF_STATE)
1033 UINT oldstate = di->u.Link.state;
1034 /* clear the masked bits */
1035 di->u.Link.state &= ~(Item->stateMask & LIS_MASK);
1036 /* copy the bits */
1037 di->u.Link.state |= (Item->state & Item->stateMask) & LIS_MASK;
1038 Repaint = (oldstate != di->u.Link.state);
1040 /* update the focus */
1041 SYSLINK_SetFocusLink(infoPtr, ((di->u.Link.state & LIS_FOCUSED) ? di : NULL));
1044 if(Item->mask & LIF_ITEMID)
1046 if(!di->u.Link.szID)
1048 di->u.Link.szID = SYSLINK_Alloc((MAX_LINKID_TEXT + 1) * sizeof(WCHAR));
1049 if(!Item->szID)
1051 ERR("Unable to allocate memory for link id\n");
1052 Ret = FALSE;
1055 if(di->u.Link.szID)
1057 lstrcpynW(di->u.Link.szID, Item->szID, MAX_LINKID_TEXT + 1);
1061 if(Item->mask & LIF_URL)
1063 if(!di->u.Link.szUrl)
1065 di->u.Link.szUrl = SYSLINK_Alloc((MAX_LINKID_TEXT + 1) * sizeof(WCHAR));
1066 if(!Item->szUrl)
1068 ERR("Unable to allocate memory for link url\n");
1069 Ret = FALSE;
1072 if(di->u.Link.szUrl)
1074 lstrcpynW(di->u.Link.szUrl, Item->szUrl, MAX_LINKID_TEXT + 1);
1078 if(Repaint)
1080 SYSLINK_RepaintLink(infoPtr, di);
1083 return Ret;
1086 /***********************************************************************
1087 * SYSLINK_GetItem
1088 * Retrieves the states and attributes of a link item.
1090 static LRESULT SYSLINK_GetItem (SYSLINK_INFO *infoPtr, PLITEM Item)
1092 PDOC_ITEM di;
1094 if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
1096 ERR("Invalid Flags!\n");
1097 return FALSE;
1100 di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
1101 if(di == NULL)
1103 ERR("Link %d couldn't be found\n", Item->iLink);
1104 return FALSE;
1107 if(Item->mask & LIF_STATE)
1109 Item->state = (di->u.Link.state & Item->stateMask);
1110 if(!infoPtr->HasFocus)
1112 /* remove the LIS_FOCUSED bit if the control doesn't have focus */
1113 Item->state &= ~LIS_FOCUSED;
1117 if(Item->mask & LIF_ITEMID)
1119 if(di->u.Link.szID)
1121 lstrcpynW(Item->szID, di->u.Link.szID, MAX_LINKID_TEXT + 1);
1123 else
1125 Item->szID[0] = 0;
1129 if(Item->mask & LIF_URL)
1131 if(di->u.Link.szUrl)
1133 lstrcpynW(Item->szUrl, di->u.Link.szUrl, L_MAX_URL_LENGTH + 1);
1135 else
1137 Item->szUrl[0] = 0;
1141 return TRUE;
1144 /***********************************************************************
1145 * SYSLINK_HitTest
1146 * Determines the link the user clicked on.
1148 static LRESULT SYSLINK_HitTest (SYSLINK_INFO *infoPtr, PLHITTESTINFO HitTest)
1150 PDOC_ITEM Current;
1151 int id = 0;
1153 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
1155 if(Current->Type == slLink)
1157 if((Current->u.Link.hRgn != NULL) &&
1158 PtInRegion(Current->u.Link.hRgn, HitTest->pt.x, HitTest->pt.y))
1160 HitTest->item.mask = 0;
1161 HitTest->item.iLink = id;
1162 HitTest->item.state = 0;
1163 HitTest->item.stateMask = 0;
1164 if(Current->u.Link.szID)
1166 lstrcpynW(HitTest->item.szID, Current->u.Link.szID, MAX_LINKID_TEXT + 1);
1168 else
1170 HitTest->item.szID[0] = 0;
1172 if(Current->u.Link.szUrl)
1174 lstrcpynW(HitTest->item.szUrl, Current->u.Link.szUrl, L_MAX_URL_LENGTH + 1);
1176 else
1178 HitTest->item.szUrl[0] = 0;
1180 return TRUE;
1182 id++;
1186 return FALSE;
1189 /***********************************************************************
1190 * SYSLINK_GetIdealHeight
1191 * Returns the preferred height of a link at the current control's width.
1193 static LRESULT SYSLINK_GetIdealHeight (SYSLINK_INFO *infoPtr)
1195 HDC hdc = GetDC(infoPtr->Self);
1196 if(hdc != NULL)
1198 LRESULT height;
1199 TEXTMETRICW tm;
1200 HGDIOBJ hOldFont = SelectObject(hdc, infoPtr->Font);
1202 if(GetTextMetricsW(hdc, &tm))
1204 height = tm.tmHeight;
1206 else
1208 height = 0;
1210 SelectObject(hdc, hOldFont);
1211 ReleaseDC(infoPtr->Self, hdc);
1213 return height;
1215 return 0;
1218 /***********************************************************************
1219 * SYSLINK_SendParentNotify
1220 * Sends a WM_NOTIFY message to the parent window.
1222 static LRESULT SYSLINK_SendParentNotify (SYSLINK_INFO *infoPtr, UINT code, PDOC_ITEM Link, int iLink)
1224 NMLINK nml;
1226 nml.hdr.hwndFrom = infoPtr->Self;
1227 nml.hdr.idFrom = GetWindowLongPtrW(infoPtr->Self, GWLP_ID);
1228 nml.hdr.code = code;
1230 nml.item.mask = 0;
1231 nml.item.iLink = iLink;
1232 nml.item.state = 0;
1233 nml.item.stateMask = 0;
1234 if(Link->u.Link.szID)
1236 lstrcpynW(nml.item.szID, Link->u.Link.szID, MAX_LINKID_TEXT + 1);
1238 else
1240 nml.item.szID[0] = 0;
1242 if(Link->u.Link.szUrl)
1244 lstrcpynW(nml.item.szUrl, Link->u.Link.szUrl, L_MAX_URL_LENGTH + 1);
1246 else
1248 nml.item.szUrl[0] = 0;
1251 return SendMessageW(GetParent(infoPtr->Self), WM_NOTIFY, (WPARAM)nml.hdr.idFrom, (LPARAM)&nml);
1254 /***********************************************************************
1255 * SYSLINK_SetFocus
1256 * Handles receiving the input focus.
1258 static LRESULT SYSLINK_SetFocus (SYSLINK_INFO *infoPtr, HWND PrevFocusWindow)
1260 PDOC_ITEM Focus;
1262 infoPtr->HasFocus = TRUE;
1264 #if 1
1265 /* FIXME - How to detect whether SHIFT+TAB or just TAB has been pressed?
1266 * The problem is we could get this message without keyboard input, too
1268 Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1270 if(Focus == NULL && (Focus = SYSLINK_GetNextLink(infoPtr, NULL)))
1272 SYSLINK_SetFocusLink(infoPtr, Focus);
1274 #else
1275 /* This is a temporary hack since I'm not really sure how to detect which link to select.
1276 See message above! */
1277 Focus = SYSLINK_GetNextLink(infoPtr, NULL);
1278 if(Focus != NULL)
1280 SYSLINK_SetFocusLink(infoPtr, Focus);
1282 #endif
1284 SYSLINK_RepaintLink(infoPtr, Focus);
1286 return 0;
1289 /***********************************************************************
1290 * SYSLINK_KillFocus
1291 * Handles losing the input focus.
1293 static LRESULT SYSLINK_KillFocus (SYSLINK_INFO *infoPtr, HWND NewFocusWindow)
1295 PDOC_ITEM Focus;
1297 infoPtr->HasFocus = FALSE;
1298 Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1300 if(Focus != NULL)
1302 SYSLINK_RepaintLink(infoPtr, Focus);
1305 return 0;
1308 /***********************************************************************
1309 * SYSLINK_LinkAtPt
1310 * Returns a link at the specified position
1312 static PDOC_ITEM SYSLINK_LinkAtPt (SYSLINK_INFO *infoPtr, POINT *pt, int *LinkId, BOOL MustBeEnabled)
1314 PDOC_ITEM Current;
1315 int id = 0;
1317 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
1319 if((Current->Type == slLink) && (Current->u.Link.hRgn != NULL) &&
1320 PtInRegion(Current->u.Link.hRgn, pt->x, pt->y) &&
1321 (!MustBeEnabled || (MustBeEnabled && (Current->u.Link.state & LIS_ENABLED))))
1323 if(LinkId != NULL)
1325 *LinkId = id;
1327 return Current;
1329 id++;
1332 return NULL;
1335 /***********************************************************************
1336 * SYSLINK_LButtonDown
1337 * Handles mouse clicks
1339 static LRESULT SYSLINK_LButtonDown (SYSLINK_INFO *infoPtr, DWORD Buttons, POINT *pt)
1341 PDOC_ITEM Current, Old;
1342 int id;
1344 Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
1345 if(Current != NULL)
1347 Old = SYSLINK_SetFocusLink(infoPtr, Current);
1348 if(Old != NULL && Old != Current)
1350 SYSLINK_RepaintLink(infoPtr, Old);
1352 infoPtr->MouseDownID = id;
1353 SYSLINK_RepaintLink(infoPtr, Current);
1354 SetFocus(infoPtr->Self);
1357 return 0;
1360 /***********************************************************************
1361 * SYSLINK_LButtonUp
1362 * Handles mouse clicks
1364 static LRESULT SYSLINK_LButtonUp (SYSLINK_INFO *infoPtr, DWORD Buttons, POINT *pt)
1366 if(infoPtr->MouseDownID > -1)
1368 PDOC_ITEM Current;
1369 int id;
1371 Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
1372 if((Current != NULL) && (Current->u.Link.state & LIS_FOCUSED) && (infoPtr->MouseDownID == id))
1374 SYSLINK_SendParentNotify(infoPtr, NM_CLICK, Current, id);
1378 infoPtr->MouseDownID = -1;
1380 return 0;
1383 /***********************************************************************
1384 * SYSLINK_OnEnter
1385 * Handles ENTER key events
1387 static BOOL SYSLINK_OnEnter (SYSLINK_INFO *infoPtr)
1389 if(infoPtr->HasFocus)
1391 PDOC_ITEM Focus;
1392 int id;
1394 Focus = SYSLINK_GetFocusLink(infoPtr, &id);
1395 if(Focus != NULL)
1397 SYSLINK_SendParentNotify(infoPtr, NM_RETURN, Focus, id);
1398 return TRUE;
1401 return FALSE;
1404 /***********************************************************************
1405 * SYSKEY_SelectNextPrevLink
1406 * Changes the currently focused link
1408 static BOOL SYSKEY_SelectNextPrevLink (SYSLINK_INFO *infoPtr, BOOL Prev)
1410 if(infoPtr->HasFocus)
1412 PDOC_ITEM Focus;
1413 int id;
1415 Focus = SYSLINK_GetFocusLink(infoPtr, &id);
1416 if(Focus != NULL)
1418 PDOC_ITEM NewFocus, OldFocus;
1420 if(Prev)
1421 NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
1422 else
1423 NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
1425 if(NewFocus != NULL)
1427 OldFocus = SYSLINK_SetFocusLink(infoPtr, NewFocus);
1429 if(OldFocus != NewFocus)
1431 SYSLINK_RepaintLink(infoPtr, OldFocus);
1433 SYSLINK_RepaintLink(infoPtr, NewFocus);
1434 return TRUE;
1438 return FALSE;
1441 /***********************************************************************
1442 * SYSKEY_SelectNextPrevLink
1443 * Determines if there's a next or previous link to decide whether the control
1444 * should capture the tab key message
1446 static BOOL SYSLINK_NoNextLink (SYSLINK_INFO *infoPtr, BOOL Prev)
1448 PDOC_ITEM Focus, NewFocus;
1450 Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1451 if(Prev)
1452 NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
1453 else
1454 NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
1456 return NewFocus == NULL;
1459 /***********************************************************************
1460 * SysLinkWindowProc
1462 static LRESULT WINAPI SysLinkWindowProc(HWND hwnd, UINT message,
1463 WPARAM wParam, LPARAM lParam)
1465 SYSLINK_INFO *infoPtr;
1467 TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
1469 infoPtr = (SYSLINK_INFO *)GetWindowLongPtrW(hwnd, 0);
1471 if (!infoPtr && message != WM_CREATE)
1472 return DefWindowProcW( hwnd, message, wParam, lParam );
1474 switch(message) {
1475 case WM_PAINT:
1476 return SYSLINK_Paint (infoPtr);
1478 case WM_SETCURSOR:
1480 LHITTESTINFO ht;
1481 DWORD mp = GetMessagePos();
1483 ht.pt.x = (short)LOWORD(mp);
1484 ht.pt.y = (short)HIWORD(mp);
1486 ScreenToClient(infoPtr->Self, &ht.pt);
1487 if(SYSLINK_HitTest (infoPtr, &ht))
1489 SetCursor(LoadCursorW(0, (LPCWSTR)IDC_HAND));
1490 return TRUE;
1492 /* let the default window proc handle this message */
1493 return DefWindowProcW(hwnd, message, wParam, lParam);
1497 case WM_SIZE:
1499 HDC hdc = GetDC(infoPtr->Self);
1500 if(hdc != NULL)
1502 SYSLINK_Render(infoPtr, hdc);
1503 ReleaseDC(infoPtr->Self, hdc);
1505 return 0;
1508 case WM_GETFONT:
1509 return (LRESULT)infoPtr->Font;
1511 case WM_SETFONT:
1512 return (LRESULT)SYSLINK_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
1514 case WM_SETTEXT:
1515 SYSLINK_SetText(infoPtr, (LPWSTR)lParam);
1516 return DefWindowProcW(hwnd, message, wParam, lParam);
1518 case WM_LBUTTONDOWN:
1520 POINT pt;
1521 pt.x = LOWORD(lParam);
1522 pt.y = HIWORD(lParam);
1523 return SYSLINK_LButtonDown(infoPtr, wParam, &pt);
1525 case WM_LBUTTONUP:
1527 POINT pt;
1528 pt.x = LOWORD(lParam);
1529 pt.y = HIWORD(lParam);
1530 return SYSLINK_LButtonUp(infoPtr, wParam, &pt);
1533 case WM_KEYDOWN:
1535 switch(wParam)
1537 case VK_RETURN:
1538 SYSLINK_OnEnter(infoPtr);
1539 return 0;
1540 case VK_TAB:
1542 BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
1543 SYSKEY_SelectNextPrevLink(infoPtr, shift);
1544 return 0;
1547 return DefWindowProcW(hwnd, message, wParam, lParam);
1550 case WM_GETDLGCODE:
1552 LRESULT Ret = DLGC_HASSETSEL;
1553 int vk = (lParam != 0 ? (int)((LPMSG)lParam)->wParam : 0);
1554 switch(vk)
1556 case VK_RETURN:
1557 Ret |= DLGC_WANTMESSAGE;
1558 break;
1559 case VK_TAB:
1561 BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
1562 if(!SYSLINK_NoNextLink(infoPtr, shift))
1564 Ret |= DLGC_WANTTAB;
1566 else
1568 Ret |= DLGC_WANTCHARS;
1570 break;
1573 return Ret;
1576 case WM_NCHITTEST:
1578 POINT pt;
1579 RECT rc;
1580 pt.x = LOWORD(lParam);
1581 pt.y = HIWORD(lParam);
1583 GetClientRect(infoPtr->Self, &rc);
1584 ScreenToClient(infoPtr->Self, &pt);
1585 if(pt.x < 0 || pt.y < 0 || pt.x > rc.right || pt.y > rc.bottom)
1587 return HTNOWHERE;
1590 if(SYSLINK_LinkAtPt(infoPtr, &pt, NULL, FALSE))
1592 return HTCLIENT;
1595 return HTTRANSPARENT;
1598 case LM_HITTEST:
1599 return SYSLINK_HitTest(infoPtr, (PLHITTESTINFO)lParam);
1601 case LM_SETITEM:
1602 return SYSLINK_SetItem(infoPtr, (PLITEM)lParam);
1604 case LM_GETITEM:
1605 return SYSLINK_GetItem(infoPtr, (PLITEM)lParam);
1607 case LM_GETIDEALHEIGHT:
1608 return SYSLINK_GetIdealHeight(infoPtr);
1610 case WM_SETFOCUS:
1611 return SYSLINK_SetFocus(infoPtr, (HWND)wParam);
1613 case WM_KILLFOCUS:
1614 return SYSLINK_KillFocus(infoPtr, (HWND)wParam);
1616 case WM_CREATE:
1617 /* allocate memory for info struct */
1618 infoPtr = (SYSLINK_INFO *)SYSLINK_Alloc (sizeof(SYSLINK_INFO));
1619 if (!infoPtr) return -1;
1620 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
1622 /* initialize the info struct */
1623 infoPtr->Self = hwnd;
1624 infoPtr->Font = 0;
1625 infoPtr->LinkFont = 0;
1626 infoPtr->Items = NULL;
1627 infoPtr->HasFocus = FALSE;
1628 infoPtr->MouseDownID = -1;
1629 infoPtr->TextColor = GetSysColor(COLOR_WINDOWTEXT);
1630 infoPtr->LinkColor = GetSysColor(COLOR_HIGHLIGHT);
1631 infoPtr->VisitedColor = GetSysColor(COLOR_HIGHLIGHT);
1632 TRACE("SysLink Ctrl creation, hwnd=%p\n", hwnd);
1633 lParam = (LPARAM)(((LPCREATESTRUCTW)lParam)->lpszName);
1634 SYSLINK_SetText(infoPtr, (LPWSTR)lParam);
1635 return 0;
1637 case WM_DESTROY:
1638 TRACE("SysLink Ctrl destruction, hwnd=%p\n", hwnd);
1639 SYSLINK_ClearDoc(infoPtr);
1640 if(infoPtr->Font != 0) DeleteObject(infoPtr->Font);
1641 if(infoPtr->LinkFont != 0) DeleteObject(infoPtr->LinkFont);
1642 SYSLINK_Free (infoPtr);
1643 SetWindowLongPtrW(hwnd, 0, 0);
1644 return 0;
1646 default:
1647 if ((message >= WM_USER) && (message < WM_APP))
1648 ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
1649 return DefWindowProcW(hwnd, message, wParam, lParam);
1654 /***********************************************************************
1655 * SYSLINK_Register [Internal]
1657 * Registers the SysLink window class.
1659 VOID SYSLINK_Register (void)
1661 WNDCLASSW wndClass;
1663 ZeroMemory (&wndClass, sizeof(wndClass));
1664 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
1665 wndClass.lpfnWndProc = SysLinkWindowProc;
1666 wndClass.cbClsExtra = 0;
1667 wndClass.cbWndExtra = sizeof (SYSLINK_INFO *);
1668 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1669 wndClass.lpszClassName = WC_LINK;
1671 RegisterClassW (&wndClass);
1675 /***********************************************************************
1676 * SYSLINK_Unregister [Internal]
1678 * Unregisters the SysLink window class.
1680 VOID SYSLINK_Unregister (void)
1682 UnregisterClassW (WC_LINK, NULL);