Added descriptions for a few more machine types.
[wine/multimedia.git] / dlls / comctl32 / syslink.c
blob12a060556481f02463f12a235a70b0071444d59a
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 * NOTES
22 * This code was audited for completeness against the documented features
23 * of Comctl32.dll version 6.0 on Apr. 4, 2005, by Dimitrie O. Paun.
25 * Unless otherwise noted, we believe this code to be complete, as per
26 * the specification mentioned above.
27 * If you discover missing features, or bugs, please note them below.
29 * TODO:
30 * - Fix SHIFT+TAB and TAB issue (wrong link is selected when control gets the focus)
31 * - Better string parsing
32 * - Improve word wrapping
36 #include <stdarg.h>
37 #include <string.h>
38 #include "windef.h"
39 #include "winbase.h"
40 #include "wingdi.h"
41 #include "winuser.h"
42 #include "winnls.h"
43 #include "commctrl.h"
44 #include "comctl32.h"
45 #include "wine/unicode.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(progress);
50 INT WINAPI StrCmpNIW(LPCWSTR,LPCWSTR,INT);
52 typedef struct
54 int nChars;
55 RECT rc;
56 } DOC_TEXTBLOCK, *PDOC_TEXTBLOCK;
58 #define LIF_FLAGSMASK (LIF_STATE | LIF_ITEMID | LIF_URL)
59 #define LIS_MASK (LIS_FOCUSED | LIS_ENABLED | LIS_VISITED)
61 typedef enum
63 slText = 0,
64 slLink
65 } SL_ITEM_TYPE;
67 typedef struct _DOC_ITEM
69 struct _DOC_ITEM *Next; /* Address to the next item */
70 LPWSTR Text; /* Text of the document item */
71 UINT nText; /* Number of characters of the text */
72 SL_ITEM_TYPE Type; /* type of the item */
73 PDOC_TEXTBLOCK Blocks; /* Array of text blocks */
74 union
76 struct
78 UINT state; /* Link state */
79 WCHAR *szID; /* Link ID string */
80 WCHAR *szUrl; /* Link URL string */
81 HRGN hRgn; /* Region of the link */
82 } Link;
83 struct
85 UINT Dummy;
86 } Text;
87 } u;
88 } DOC_ITEM, *PDOC_ITEM;
90 typedef struct
92 HWND Self; /* The window handle for this control */
93 HWND Notify; /* The parent handle to receive notifications */
94 DWORD Style; /* Styles for this control */
95 PDOC_ITEM Items; /* Address to the first document item */
96 BOOL HasFocus; /* Whether the control has the input focus */
97 int MouseDownID; /* ID of the link that the mouse button first selected */
98 HFONT Font; /* Handle to the font for text */
99 HFONT LinkFont; /* Handle to the font for links */
100 COLORREF TextColor; /* Color of the text */
101 COLORREF LinkColor; /* Color of links */
102 COLORREF VisitedColor; /* Color of visited links */
103 } SYSLINK_INFO;
105 static const WCHAR SL_LINKOPEN[] = { '<','a', 0 };
106 static const WCHAR SL_HREF[] = { 'h','r','e','f','=','\"',0 };
107 static const WCHAR SL_ID[] = { 'i','d','=','\"',0 };
108 static const WCHAR SL_LINKCLOSE[] = { '<','/','a','>',0 };
110 /* Control configuration constants */
112 #define SL_LEFTMARGIN (0)
113 #define SL_TOPMARGIN (0)
114 #define SL_RIGHTMARGIN (0)
115 #define SL_BOTTOMMARGIN (0)
117 /***********************************************************************
118 * SYSLINK_FreeDocItem
119 * Frees all data and gdi objects associated with a document item
121 static VOID SYSLINK_FreeDocItem (PDOC_ITEM DocItem)
123 if(DocItem->Type == slLink)
125 Free(DocItem->u.Link.szID);
126 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 Free(DocItem);
140 /***********************************************************************
141 * SYSLINK_AppendDocItem
142 * Create and append a new document item.
144 static PDOC_ITEM SYSLINK_AppendDocItem (SYSLINK_INFO *infoPtr, LPCWSTR Text, UINT textlen,
145 SL_ITEM_TYPE type, PDOC_ITEM LastItem)
147 PDOC_ITEM Item;
148 Item = 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, LPCWSTR Text)
203 LPCWSTR current, textstart = NULL, linktext = NULL, firsttag = NULL;
204 int taglen = 0, textlen = 0, linklen = 0, docitems = 0;
205 PDOC_ITEM Last = NULL;
206 SL_ITEM_TYPE CurrentType = slText;
207 LPCWSTR lpID, lpUrl;
208 UINT lenId, lenUrl;
210 for(current = Text; *current != 0;)
212 if(*current == '<')
214 if(!StrCmpNIW(current, SL_LINKOPEN, 2) && (CurrentType == slText))
216 BOOL ValidParam = FALSE, ValidLink = FALSE;
218 switch (*(current + 2))
220 case '>':
221 /* we just have to deal with a <a> tag */
222 taglen = 3;
223 ValidLink = TRUE;
224 ValidParam = TRUE;
225 firsttag = current;
226 linklen = 0;
227 lpID = NULL;
228 lpUrl = NULL;
229 break;
230 case ' ':
232 /* we expect parameters, parse them */
233 LPCWSTR *CurrentParameter = NULL, tmp;
234 UINT *CurrentParameterLen = NULL;
236 taglen = 3;
237 tmp = current + taglen;
238 lpID = NULL;
239 lpUrl = NULL;
241 CheckParameter:
242 /* compare the current position with all known parameters */
243 if(!StrCmpNIW(tmp, SL_HREF, 6))
245 taglen += 6;
246 ValidParam = TRUE;
247 CurrentParameter = &lpUrl;
248 CurrentParameterLen = &lenUrl;
250 else if(!StrCmpNIW(tmp, SL_ID, 4))
252 taglen += 4;
253 ValidParam = TRUE;
254 CurrentParameter = &lpID;
255 CurrentParameterLen = &lenId;
257 else
259 ValidParam = FALSE;
262 if(ValidParam)
264 /* we got a known parameter, now search until the next " character.
265 If we can't find a " character, there's a syntax error and we just assume it's text */
266 ValidParam = FALSE;
267 *CurrentParameter = current + taglen;
268 *CurrentParameterLen = 0;
270 for(tmp = *CurrentParameter; *tmp != 0; tmp++)
272 taglen++;
273 if(*tmp == '\"')
275 ValidParam = TRUE;
276 tmp++;
277 break;
279 (*CurrentParameterLen)++;
282 if(ValidParam)
284 /* we're done with this parameter, now there are only 2 possibilities:
285 * 1. another parameter is coming, so expect a ' ' (space) character
286 * 2. the tag is being closed, so expect a '<' character
288 switch(*tmp)
290 case ' ':
291 /* we expect another parameter, do the whole thing again */
292 taglen++;
293 tmp++;
294 goto CheckParameter;
296 case '>':
297 /* the tag is being closed, we're done */
298 ValidLink = TRUE;
299 taglen++;
300 break;
301 default:
302 tmp++;
303 break;
307 break;
311 if(ValidLink && ValidParam)
313 /* the <a ...> tag appears to be valid. save all information
314 so we can add the link if we find a valid </a> tag later */
315 CurrentType = slLink;
316 linktext = current + taglen;
317 linklen = 0;
318 firsttag = current;
320 else
322 taglen = 1;
323 lpID = NULL;
324 lpUrl = NULL;
325 if(textstart == NULL)
327 textstart = current;
331 else if(!StrCmpNIW(current, SL_LINKCLOSE, 4) && (CurrentType == slLink) && firsttag)
333 /* there's a <a...> tag opened, first add the previous text, if present */
334 if(textstart != NULL && textlen > 0 && firsttag > textstart)
336 Last = SYSLINK_AppendDocItem(infoPtr, textstart, firsttag - textstart, slText, Last);
337 if(Last == NULL)
339 ERR("Unable to create new document item!\n");
340 return docitems;
342 docitems++;
343 textstart = NULL;
344 textlen = 0;
347 /* now it's time to add the link to the document */
348 current += 4;
349 if(linktext != NULL && linklen > 0)
351 Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slLink, Last);
352 if(Last == NULL)
354 ERR("Unable to create new document item!\n");
355 return docitems;
357 docitems++;
358 if(CurrentType == slLink)
360 int nc;
362 if(!(infoPtr->Style & WS_DISABLED))
364 Last->u.Link.state |= LIS_ENABLED;
366 /* Copy the tag parameters */
367 if(lpID != NULL)
369 nc = min(lenId, strlenW(lpID));
370 nc = min(nc, MAX_LINKID_TEXT);
371 Last->u.Link.szID = Alloc((MAX_LINKID_TEXT + 1) * sizeof(WCHAR));
372 if(Last->u.Link.szID != NULL)
374 lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
375 Last->u.Link.szID[nc] = 0;
378 else
379 Last->u.Link.szID = NULL;
380 if(lpUrl != NULL)
382 nc = min(lenUrl, strlenW(lpUrl));
383 nc = min(nc, L_MAX_URL_LENGTH);
384 Last->u.Link.szUrl = Alloc((L_MAX_URL_LENGTH + 1) * sizeof(WCHAR));
385 if(Last->u.Link.szUrl != NULL)
387 lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
388 Last->u.Link.szUrl[nc] = 0;
391 else
392 Last->u.Link.szUrl = NULL;
394 linktext = NULL;
396 CurrentType = slText;
397 firsttag = NULL;
398 textstart = NULL;
399 continue;
401 else
403 /* we don't know what tag it is, so just continue */
404 taglen = 1;
405 linklen++;
406 if(CurrentType == slText && textstart == NULL)
408 textstart = current;
412 textlen += taglen;
413 current += taglen;
415 else
417 textlen++;
418 linklen++;
420 /* save the pointer of the current text item if we couldn't find a tag */
421 if(textstart == NULL && CurrentType == slText)
423 textstart = current;
426 current++;
430 if(textstart != NULL && textlen > 0)
432 Last = SYSLINK_AppendDocItem(infoPtr, textstart, textlen, CurrentType, Last);
433 if(Last == NULL)
435 ERR("Unable to create new document item!\n");
436 return docitems;
438 if(CurrentType == slLink)
440 int nc;
442 if(!(infoPtr->Style & WS_DISABLED))
444 Last->u.Link.state |= LIS_ENABLED;
446 /* Copy the tag parameters */
447 if(lpID != NULL)
449 nc = min(lenId, strlenW(lpID));
450 nc = min(nc, MAX_LINKID_TEXT);
451 Last->u.Link.szID = Alloc((MAX_LINKID_TEXT + 1) * sizeof(WCHAR));
452 if(Last->u.Link.szID != NULL)
454 lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
455 Last->u.Link.szID[nc] = 0;
458 else
459 Last->u.Link.szID = NULL;
460 if(lpUrl != NULL)
462 nc = min(lenUrl, strlenW(lpUrl));
463 nc = min(nc, L_MAX_URL_LENGTH);
464 Last->u.Link.szUrl = Alloc((L_MAX_URL_LENGTH + 1) * sizeof(WCHAR));
465 if(Last->u.Link.szUrl != NULL)
467 lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
468 Last->u.Link.szUrl[nc] = 0;
471 else
472 Last->u.Link.szUrl = NULL;
474 docitems++;
477 if(linktext != NULL && linklen > 0)
479 /* we got an unclosed link, just display the text */
480 Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slText, Last);
481 if(Last == NULL)
483 ERR("Unable to create new document item!\n");
484 return docitems;
486 docitems++;
489 return docitems;
492 /***********************************************************************
493 * SYSLINK_RepaintLink
494 * Repaints a link.
496 static VOID SYSLINK_RepaintLink (SYSLINK_INFO *infoPtr, PDOC_ITEM DocItem)
498 if(DocItem->Type != slLink)
500 ERR("DocItem not a link!\n");
501 return;
504 if(DocItem->u.Link.hRgn != NULL)
506 /* repaint the region */
507 RedrawWindow(infoPtr->Self, NULL, DocItem->u.Link.hRgn, RDW_INVALIDATE | RDW_UPDATENOW);
511 /***********************************************************************
512 * SYSLINK_GetLinkItemByIndex
513 * Retrieves a document link by its index
515 static PDOC_ITEM SYSLINK_GetLinkItemByIndex (SYSLINK_INFO *infoPtr, int iLink)
517 PDOC_ITEM Current = infoPtr->Items;
519 while(Current != NULL)
521 if((Current->Type == slLink) && (iLink-- <= 0))
523 return Current;
525 Current = Current->Next;
527 return NULL;
530 /***********************************************************************
531 * SYSLINK_GetFocusLink
532 * Retrieves the link that has the LIS_FOCUSED bit
534 static PDOC_ITEM SYSLINK_GetFocusLink (SYSLINK_INFO *infoPtr, int *LinkId)
536 PDOC_ITEM Current = infoPtr->Items;
537 int id = 0;
539 while(Current != NULL)
541 if((Current->Type == slLink))
543 if(Current->u.Link.state & LIS_FOCUSED)
545 if(LinkId != NULL)
546 *LinkId = id;
547 return Current;
549 id++;
551 Current = Current->Next;
553 return NULL;
556 /***********************************************************************
557 * SYSLINK_GetNextLink
558 * Gets the next link
560 static PDOC_ITEM SYSLINK_GetNextLink (SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
562 for(Current = (Current != NULL ? Current->Next : infoPtr->Items);
563 Current != NULL;
564 Current = Current->Next)
566 if(Current->Type == slLink)
568 return Current;
571 return NULL;
574 /***********************************************************************
575 * SYSLINK_GetPrevLink
576 * Gets the previous link
578 static PDOC_ITEM SYSLINK_GetPrevLink (SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
580 if(Current == NULL)
582 /* returns the last link */
583 PDOC_ITEM Last = NULL;
585 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
587 if(Current->Type == slLink)
589 Last = Current;
592 return Last;
594 else
596 /* returns the previous link */
597 PDOC_ITEM Cur, Prev = NULL;
599 for(Cur = infoPtr->Items; Cur != NULL; Cur = Cur->Next)
601 if(Cur == Current)
603 break;
605 if(Cur->Type == slLink)
607 Prev = Cur;
610 return Prev;
614 /***********************************************************************
615 * SYSLINK_WrapLine
616 * Tries to wrap a line.
618 static BOOL SYSLINK_WrapLine (HDC hdc, LPWSTR Text, WCHAR BreakChar, int *LineLen, int nFit, LPSIZE Extent, int Width)
620 WCHAR *Current;
622 if(nFit == *LineLen)
624 return FALSE;
627 *LineLen = nFit;
629 Current = Text + nFit;
631 /* check if we're in the middle of a word */
632 if((*Current) != BreakChar)
634 /* search for the beginning of the word */
635 while(Current > Text && (*(Current - 1)) != BreakChar)
637 Current--;
638 (*LineLen)--;
641 if((*LineLen) == 0)
643 Extent->cx = 0;
644 Extent->cy = 0;
646 return TRUE;
649 return TRUE;
652 /***********************************************************************
653 * SYSLINK_Render
654 * Renders the document in memory
656 static VOID SYSLINK_Render (SYSLINK_INFO *infoPtr, HDC hdc)
658 RECT rc;
659 PDOC_ITEM Current;
660 HGDIOBJ hOldFont;
661 int x, y, LineHeight;
662 TEXTMETRICW tm;
664 GetClientRect(infoPtr->Self, &rc);
665 rc.right -= SL_RIGHTMARGIN;
666 rc.bottom -= SL_BOTTOMMARGIN;
668 if(rc.right - SL_LEFTMARGIN < 0 || rc.bottom - SL_TOPMARGIN < 0) return;
670 hOldFont = SelectObject(hdc, infoPtr->Font);
671 GetTextMetricsW(hdc, &tm);
673 x = SL_LEFTMARGIN;
674 y = SL_TOPMARGIN;
675 LineHeight = 0;
677 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
679 int n, nBlocks;
680 LPWSTR tx;
681 PDOC_TEXTBLOCK bl, cbl;
682 INT nFit;
683 SIZE szDim;
685 if(Current->nText == 0)
687 ERR("DOC_ITEM with no text?!\n");
688 continue;
691 tx = Current->Text;
692 n = Current->nText;
693 bl = Current->Blocks;
694 nBlocks = 0;
696 if(Current->Type == slText)
698 SelectObject(hdc, infoPtr->Font);
700 else if(Current->Type == slLink)
702 SelectObject(hdc, infoPtr->LinkFont);
705 while(n > 0)
707 if(GetTextExtentExPointW(hdc, tx, n, rc.right - x, &nFit, NULL, &szDim))
709 int LineLen = n;
710 BOOL Wrap = SYSLINK_WrapLine(hdc, tx, tm.tmBreakChar, &LineLen, nFit, &szDim, rc.right - x);
712 if(LineLen == 0)
714 if(x > SL_LEFTMARGIN)
716 /* move one line down, the word didn't fit into the line */
717 x = SL_LEFTMARGIN;
718 y += LineHeight;
719 LineHeight = 0;
720 continue;
722 else
724 /* the word starts at the beginning of the line and doesn't
725 fit into the line, so break it at the last character that fits */
726 LineLen = max(nFit, 1);
730 if(LineLen != n)
732 GetTextExtentExPointW(hdc, tx, LineLen, rc.right - x, NULL, NULL, &szDim);
735 if(bl != NULL)
737 bl = ReAlloc(bl, ++nBlocks * sizeof(DOC_TEXTBLOCK));
739 else
741 bl = Alloc(++nBlocks * sizeof(DOC_TEXTBLOCK));
744 if(bl != NULL)
746 cbl = bl + nBlocks - 1;
748 cbl->nChars = LineLen;
749 cbl->rc.left = x;
750 cbl->rc.top = y;
751 cbl->rc.right = x + szDim.cx;
752 cbl->rc.bottom = y + szDim.cy;
754 x += szDim.cx;
755 LineHeight = max(LineHeight, szDim.cy);
757 /* (re)calculate the link's region */
758 if(Current->Type == slLink)
760 if(nBlocks <= 1)
762 if(Current->u.Link.hRgn != NULL)
764 DeleteObject(Current->u.Link.hRgn);
766 /* initialize the link's hRgn */
767 Current->u.Link.hRgn = CreateRectRgnIndirect(&cbl->rc);
769 else if(Current->u.Link.hRgn != NULL)
771 HRGN hrgn;
772 hrgn = CreateRectRgnIndirect(&cbl->rc);
773 /* add the rectangle */
774 CombineRgn(Current->u.Link.hRgn, Current->u.Link.hRgn, hrgn, RGN_OR);
775 DeleteObject(hrgn);
779 if(Wrap)
781 x = SL_LEFTMARGIN;
782 y += LineHeight;
783 LineHeight = 0;
786 else
788 ERR("Failed to alloc DOC_TEXTBLOCK structure!\n");
789 break;
791 n -= LineLen;
792 tx += LineLen;
794 else
796 ERR("GetTextExtentExPoint() failed?!\n");
797 n--;
800 Current->Blocks = bl;
803 SelectObject(hdc, hOldFont);
806 /***********************************************************************
807 * SYSLINK_Draw
808 * Draws the SysLink control.
810 static LRESULT SYSLINK_Draw (SYSLINK_INFO *infoPtr, HDC hdc)
812 RECT rc;
813 PDOC_ITEM Current;
814 HFONT hOldFont;
815 COLORREF OldTextColor, OldBkColor;
817 hOldFont = SelectObject(hdc, infoPtr->Font);
818 OldTextColor = SetTextColor(hdc, infoPtr->TextColor);
819 OldBkColor = SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
821 GetClientRect(infoPtr->Self, &rc);
822 rc.right -= SL_RIGHTMARGIN + SL_LEFTMARGIN;
823 rc.bottom -= SL_BOTTOMMARGIN + SL_TOPMARGIN;
825 if(rc.right < 0 || rc.bottom < 0) return 0;
827 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
829 int n;
830 LPWSTR tx;
831 PDOC_TEXTBLOCK bl;
833 bl = Current->Blocks;
834 if(bl != NULL)
836 tx = Current->Text;
837 n = Current->nText;
839 if(Current->Type == slText)
841 SelectObject(hdc, infoPtr->Font);
842 SetTextColor(hdc, infoPtr->TextColor);
844 else
846 SelectObject(hdc, infoPtr->LinkFont);
847 SetTextColor(hdc, (!(Current->u.Link.state & LIS_VISITED) ? infoPtr->LinkColor : infoPtr->VisitedColor));
850 while(n > 0)
852 ExtTextOutW(hdc, bl->rc.left, bl->rc.top, ETO_OPAQUE | ETO_CLIPPED, &bl->rc, tx, bl->nChars, NULL);
853 if((Current->Type == slLink) && (Current->u.Link.state & LIS_FOCUSED) && infoPtr->HasFocus)
855 COLORREF PrevColor;
856 PrevColor = SetBkColor(hdc, OldBkColor);
857 DrawFocusRect(hdc, &bl->rc);
858 SetBkColor(hdc, PrevColor);
860 tx += bl->nChars;
861 n -= bl->nChars;
862 bl++;
867 SetBkColor(hdc, OldBkColor);
868 SetTextColor(hdc, OldTextColor);
869 SelectObject(hdc, hOldFont);
871 return 0;
875 /***********************************************************************
876 * SYSLINK_Paint
877 * Handles the WM_PAINT message.
879 static LRESULT SYSLINK_Paint (SYSLINK_INFO *infoPtr, HDC hdcParam)
881 HDC hdc;
882 PAINTSTRUCT ps;
884 hdc = hdcParam ? hdcParam : BeginPaint (infoPtr->Self, &ps);
885 SYSLINK_Draw (infoPtr, hdc);
886 if (!hdcParam) EndPaint (infoPtr->Self, &ps);
887 return 0;
891 /***********************************************************************
892 * SYSLINK_SetFont
893 * Set new Font for the SysLink control.
895 static HFONT SYSLINK_SetFont (SYSLINK_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
897 HDC hdc;
898 LOGFONTW lf;
899 HFONT hOldFont = infoPtr->Font;
900 infoPtr->Font = hFont;
902 /* free the underline font */
903 if(infoPtr->LinkFont != NULL)
905 DeleteObject(infoPtr->LinkFont);
906 infoPtr->LinkFont = NULL;
909 /* Render text position and word wrapping in memory */
910 hdc = GetDC(infoPtr->Self);
911 if(hdc != NULL)
913 /* create a new underline font */
914 if(GetObjectW(infoPtr->Font, sizeof(LOGFONTW), &lf))
916 lf.lfUnderline = TRUE;
917 infoPtr->LinkFont = CreateFontIndirectW(&lf);
919 else
921 ERR("Failed to create link font!\n");
924 SYSLINK_Render(infoPtr, hdc);
925 ReleaseDC(infoPtr->Self, hdc);
928 if(bRedraw)
930 RedrawWindow(infoPtr->Self, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
933 return hOldFont;
936 /***********************************************************************
937 * SYSLINK_SetText
938 * Set new text for the SysLink control.
940 static LRESULT SYSLINK_SetText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
942 int textlen;
944 /* clear the document */
945 SYSLINK_ClearDoc(infoPtr);
947 textlen = lstrlenW(Text);
948 if(Text == NULL || textlen == 0)
950 return TRUE;
953 /* let's parse the string and create a document */
954 if(SYSLINK_ParseText(infoPtr, Text) > 0)
956 /* Render text position and word wrapping in memory */
957 HDC hdc = GetDC(infoPtr->Self);
958 SYSLINK_Render(infoPtr, hdc);
959 SYSLINK_Draw(infoPtr, hdc);
960 ReleaseDC(infoPtr->Self, hdc);
963 return TRUE;
966 /***********************************************************************
967 * SYSLINK_SetFocusLink
968 * Updates the focus status bits and focusses the specified link.
969 * If no document item is specified, the focus bit will be removed from all links.
970 * Returns the previous focused item.
972 static PDOC_ITEM SYSLINK_SetFocusLink (SYSLINK_INFO *infoPtr, PDOC_ITEM DocItem)
974 PDOC_ITEM Current, PrevFocus = NULL;
976 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
978 if(Current->Type == slLink)
980 if((PrevFocus == NULL) && (Current->u.Link.state & LIS_FOCUSED))
982 PrevFocus = Current;
985 if(Current == DocItem)
987 Current->u.Link.state |= LIS_FOCUSED;
989 else
991 Current->u.Link.state &= ~LIS_FOCUSED;
996 return PrevFocus;
999 /***********************************************************************
1000 * SYSLINK_SetItem
1001 * Sets the states and attributes of a link item.
1003 static LRESULT SYSLINK_SetItem (SYSLINK_INFO *infoPtr, PLITEM Item)
1005 PDOC_ITEM di;
1006 BOOL Repaint = FALSE;
1007 BOOL Ret = TRUE;
1009 if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
1011 ERR("Invalid Flags!\n");
1012 return FALSE;
1015 di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
1016 if(di == NULL)
1018 ERR("Link %d couldn't be found\n", Item->iLink);
1019 return FALSE;
1022 if(Item->mask & LIF_STATE)
1024 UINT oldstate = di->u.Link.state;
1025 /* clear the masked bits */
1026 di->u.Link.state &= ~(Item->stateMask & LIS_MASK);
1027 /* copy the bits */
1028 di->u.Link.state |= (Item->state & Item->stateMask) & LIS_MASK;
1029 Repaint = (oldstate != di->u.Link.state);
1031 /* update the focus */
1032 SYSLINK_SetFocusLink(infoPtr, ((di->u.Link.state & LIS_FOCUSED) ? di : NULL));
1035 if(Item->mask & LIF_ITEMID)
1037 if(!di->u.Link.szID)
1039 di->u.Link.szID = Alloc((MAX_LINKID_TEXT + 1) * sizeof(WCHAR));
1040 if(!Item->szID)
1042 ERR("Unable to allocate memory for link id\n");
1043 Ret = FALSE;
1046 if(di->u.Link.szID)
1048 lstrcpynW(di->u.Link.szID, Item->szID, MAX_LINKID_TEXT + 1);
1052 if(Item->mask & LIF_URL)
1054 if(!di->u.Link.szUrl)
1056 di->u.Link.szUrl = Alloc((MAX_LINKID_TEXT + 1) * sizeof(WCHAR));
1057 if(!Item->szUrl)
1059 ERR("Unable to allocate memory for link url\n");
1060 Ret = FALSE;
1063 if(di->u.Link.szUrl)
1065 lstrcpynW(di->u.Link.szUrl, Item->szUrl, MAX_LINKID_TEXT + 1);
1069 if(Repaint)
1071 SYSLINK_RepaintLink(infoPtr, di);
1074 return Ret;
1077 /***********************************************************************
1078 * SYSLINK_GetItem
1079 * Retrieves the states and attributes of a link item.
1081 static LRESULT SYSLINK_GetItem (SYSLINK_INFO *infoPtr, PLITEM Item)
1083 PDOC_ITEM di;
1085 if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
1087 ERR("Invalid Flags!\n");
1088 return FALSE;
1091 di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
1092 if(di == NULL)
1094 ERR("Link %d couldn't be found\n", Item->iLink);
1095 return FALSE;
1098 if(Item->mask & LIF_STATE)
1100 Item->state = (di->u.Link.state & Item->stateMask);
1101 if(!infoPtr->HasFocus)
1103 /* remove the LIS_FOCUSED bit if the control doesn't have focus */
1104 Item->state &= ~LIS_FOCUSED;
1108 if(Item->mask & LIF_ITEMID)
1110 if(di->u.Link.szID)
1112 lstrcpynW(Item->szID, di->u.Link.szID, MAX_LINKID_TEXT + 1);
1114 else
1116 Item->szID[0] = 0;
1120 if(Item->mask & LIF_URL)
1122 if(di->u.Link.szUrl)
1124 lstrcpynW(Item->szUrl, di->u.Link.szUrl, L_MAX_URL_LENGTH + 1);
1126 else
1128 Item->szUrl[0] = 0;
1132 return TRUE;
1135 /***********************************************************************
1136 * SYSLINK_HitTest
1137 * Determines the link the user clicked on.
1139 static LRESULT SYSLINK_HitTest (SYSLINK_INFO *infoPtr, PLHITTESTINFO HitTest)
1141 PDOC_ITEM Current;
1142 int id = 0;
1144 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
1146 if(Current->Type == slLink)
1148 if((Current->u.Link.hRgn != NULL) &&
1149 PtInRegion(Current->u.Link.hRgn, HitTest->pt.x, HitTest->pt.y))
1151 HitTest->item.mask = 0;
1152 HitTest->item.iLink = id;
1153 HitTest->item.state = 0;
1154 HitTest->item.stateMask = 0;
1155 if(Current->u.Link.szID)
1157 lstrcpynW(HitTest->item.szID, Current->u.Link.szID, MAX_LINKID_TEXT + 1);
1159 else
1161 HitTest->item.szID[0] = 0;
1163 if(Current->u.Link.szUrl)
1165 lstrcpynW(HitTest->item.szUrl, Current->u.Link.szUrl, L_MAX_URL_LENGTH + 1);
1167 else
1169 HitTest->item.szUrl[0] = 0;
1171 return TRUE;
1173 id++;
1177 return FALSE;
1180 /***********************************************************************
1181 * SYSLINK_GetIdealHeight
1182 * Returns the preferred height of a link at the current control's width.
1184 static LRESULT SYSLINK_GetIdealHeight (SYSLINK_INFO *infoPtr)
1186 HDC hdc = GetDC(infoPtr->Self);
1187 if(hdc != NULL)
1189 LRESULT height;
1190 TEXTMETRICW tm;
1191 HGDIOBJ hOldFont = SelectObject(hdc, infoPtr->Font);
1193 if(GetTextMetricsW(hdc, &tm))
1195 height = tm.tmHeight;
1197 else
1199 height = 0;
1201 SelectObject(hdc, hOldFont);
1202 ReleaseDC(infoPtr->Self, hdc);
1204 return height;
1206 return 0;
1209 /***********************************************************************
1210 * SYSLINK_SendParentNotify
1211 * Sends a WM_NOTIFY message to the parent window.
1213 static LRESULT SYSLINK_SendParentNotify (SYSLINK_INFO *infoPtr, UINT code, PDOC_ITEM Link, int iLink)
1215 NMLINK nml;
1217 nml.hdr.hwndFrom = infoPtr->Self;
1218 nml.hdr.idFrom = GetWindowLongPtrW(infoPtr->Self, GWLP_ID);
1219 nml.hdr.code = code;
1221 nml.item.mask = 0;
1222 nml.item.iLink = iLink;
1223 nml.item.state = 0;
1224 nml.item.stateMask = 0;
1225 if(Link->u.Link.szID)
1227 lstrcpynW(nml.item.szID, Link->u.Link.szID, MAX_LINKID_TEXT + 1);
1229 else
1231 nml.item.szID[0] = 0;
1233 if(Link->u.Link.szUrl)
1235 lstrcpynW(nml.item.szUrl, Link->u.Link.szUrl, L_MAX_URL_LENGTH + 1);
1237 else
1239 nml.item.szUrl[0] = 0;
1242 return SendMessageW(infoPtr->Notify, WM_NOTIFY, (WPARAM)nml.hdr.idFrom, (LPARAM)&nml);
1245 /***********************************************************************
1246 * SYSLINK_SetFocus
1247 * Handles receiving the input focus.
1249 static LRESULT SYSLINK_SetFocus (SYSLINK_INFO *infoPtr, HWND PrevFocusWindow)
1251 PDOC_ITEM Focus;
1253 infoPtr->HasFocus = TRUE;
1255 #if 1
1256 /* FIXME - How to detect whether SHIFT+TAB or just TAB has been pressed?
1257 * The problem is we could get this message without keyboard input, too
1259 Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1261 if(Focus == NULL && (Focus = SYSLINK_GetNextLink(infoPtr, NULL)))
1263 SYSLINK_SetFocusLink(infoPtr, Focus);
1265 #else
1266 /* This is a temporary hack since I'm not really sure how to detect which link to select.
1267 See message above! */
1268 Focus = SYSLINK_GetNextLink(infoPtr, NULL);
1269 if(Focus != NULL)
1271 SYSLINK_SetFocusLink(infoPtr, Focus);
1273 #endif
1275 SYSLINK_RepaintLink(infoPtr, Focus);
1277 return 0;
1280 /***********************************************************************
1281 * SYSLINK_KillFocus
1282 * Handles losing the input focus.
1284 static LRESULT SYSLINK_KillFocus (SYSLINK_INFO *infoPtr, HWND NewFocusWindow)
1286 PDOC_ITEM Focus;
1288 infoPtr->HasFocus = FALSE;
1289 Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1291 if(Focus != NULL)
1293 SYSLINK_RepaintLink(infoPtr, Focus);
1296 return 0;
1299 /***********************************************************************
1300 * SYSLINK_LinkAtPt
1301 * Returns a link at the specified position
1303 static PDOC_ITEM SYSLINK_LinkAtPt (SYSLINK_INFO *infoPtr, POINT *pt, int *LinkId, BOOL MustBeEnabled)
1305 PDOC_ITEM Current;
1306 int id = 0;
1308 for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
1310 if((Current->Type == slLink) && (Current->u.Link.hRgn != NULL) &&
1311 PtInRegion(Current->u.Link.hRgn, pt->x, pt->y) &&
1312 (!MustBeEnabled || (MustBeEnabled && (Current->u.Link.state & LIS_ENABLED))))
1314 if(LinkId != NULL)
1316 *LinkId = id;
1318 return Current;
1320 id++;
1323 return NULL;
1326 /***********************************************************************
1327 * SYSLINK_LButtonDown
1328 * Handles mouse clicks
1330 static LRESULT SYSLINK_LButtonDown (SYSLINK_INFO *infoPtr, DWORD Buttons, POINT *pt)
1332 PDOC_ITEM Current, Old;
1333 int id;
1335 Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
1336 if(Current != NULL)
1338 Old = SYSLINK_SetFocusLink(infoPtr, Current);
1339 if(Old != NULL && Old != Current)
1341 SYSLINK_RepaintLink(infoPtr, Old);
1343 infoPtr->MouseDownID = id;
1344 SYSLINK_RepaintLink(infoPtr, Current);
1345 SetFocus(infoPtr->Self);
1348 return 0;
1351 /***********************************************************************
1352 * SYSLINK_LButtonUp
1353 * Handles mouse clicks
1355 static LRESULT SYSLINK_LButtonUp (SYSLINK_INFO *infoPtr, DWORD Buttons, POINT *pt)
1357 if(infoPtr->MouseDownID > -1)
1359 PDOC_ITEM Current;
1360 int id;
1362 Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
1363 if((Current != NULL) && (Current->u.Link.state & LIS_FOCUSED) && (infoPtr->MouseDownID == id))
1365 SYSLINK_SendParentNotify(infoPtr, NM_CLICK, Current, id);
1369 infoPtr->MouseDownID = -1;
1371 return 0;
1374 /***********************************************************************
1375 * SYSLINK_OnEnter
1376 * Handles ENTER key events
1378 static BOOL SYSLINK_OnEnter (SYSLINK_INFO *infoPtr)
1380 if(infoPtr->HasFocus)
1382 PDOC_ITEM Focus;
1383 int id;
1385 Focus = SYSLINK_GetFocusLink(infoPtr, &id);
1386 if(Focus != NULL)
1388 SYSLINK_SendParentNotify(infoPtr, NM_RETURN, Focus, id);
1389 return TRUE;
1392 return FALSE;
1395 /***********************************************************************
1396 * SYSKEY_SelectNextPrevLink
1397 * Changes the currently focused link
1399 static BOOL SYSKEY_SelectNextPrevLink (SYSLINK_INFO *infoPtr, BOOL Prev)
1401 if(infoPtr->HasFocus)
1403 PDOC_ITEM Focus;
1404 int id;
1406 Focus = SYSLINK_GetFocusLink(infoPtr, &id);
1407 if(Focus != NULL)
1409 PDOC_ITEM NewFocus, OldFocus;
1411 if(Prev)
1412 NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
1413 else
1414 NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
1416 if(NewFocus != NULL)
1418 OldFocus = SYSLINK_SetFocusLink(infoPtr, NewFocus);
1420 if(OldFocus != NewFocus)
1422 SYSLINK_RepaintLink(infoPtr, OldFocus);
1424 SYSLINK_RepaintLink(infoPtr, NewFocus);
1425 return TRUE;
1429 return FALSE;
1432 /***********************************************************************
1433 * SYSKEY_SelectNextPrevLink
1434 * Determines if there's a next or previous link to decide whether the control
1435 * should capture the tab key message
1437 static BOOL SYSLINK_NoNextLink (SYSLINK_INFO *infoPtr, BOOL Prev)
1439 PDOC_ITEM Focus, NewFocus;
1441 Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1442 if(Prev)
1443 NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
1444 else
1445 NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
1447 return NewFocus == NULL;
1450 /***********************************************************************
1451 * SysLinkWindowProc
1453 static LRESULT WINAPI SysLinkWindowProc(HWND hwnd, UINT message,
1454 WPARAM wParam, LPARAM lParam)
1456 SYSLINK_INFO *infoPtr;
1458 TRACE("hwnd=%p msg=%04x wparam=%x lParam=%lx\n", hwnd, message, wParam, lParam);
1460 infoPtr = (SYSLINK_INFO *)GetWindowLongPtrW(hwnd, 0);
1462 if (!infoPtr && message != WM_CREATE)
1463 return DefWindowProcW( hwnd, message, wParam, lParam );
1465 switch(message) {
1466 case WM_PAINT:
1467 return SYSLINK_Paint (infoPtr, (HDC)wParam);
1469 case WM_SETCURSOR:
1471 LHITTESTINFO ht;
1472 DWORD mp = GetMessagePos();
1474 ht.pt.x = (short)LOWORD(mp);
1475 ht.pt.y = (short)HIWORD(mp);
1477 ScreenToClient(infoPtr->Self, &ht.pt);
1478 if(SYSLINK_HitTest (infoPtr, &ht))
1480 SetCursor(LoadCursorW(0, (LPCWSTR)IDC_HAND));
1481 return TRUE;
1483 /* let the default window proc handle this message */
1484 return DefWindowProcW(hwnd, message, wParam, lParam);
1488 case WM_SIZE:
1490 HDC hdc = GetDC(infoPtr->Self);
1491 if(hdc != NULL)
1493 SYSLINK_Render(infoPtr, hdc);
1494 ReleaseDC(infoPtr->Self, hdc);
1496 return 0;
1499 case WM_GETFONT:
1500 return (LRESULT)infoPtr->Font;
1502 case WM_SETFONT:
1503 return (LRESULT)SYSLINK_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
1505 case WM_SETTEXT:
1506 SYSLINK_SetText(infoPtr, (LPWSTR)lParam);
1507 return DefWindowProcW(hwnd, message, wParam, lParam);
1509 case WM_LBUTTONDOWN:
1511 POINT pt;
1512 pt.x = LOWORD(lParam);
1513 pt.y = HIWORD(lParam);
1514 return SYSLINK_LButtonDown(infoPtr, wParam, &pt);
1516 case WM_LBUTTONUP:
1518 POINT pt;
1519 pt.x = LOWORD(lParam);
1520 pt.y = HIWORD(lParam);
1521 return SYSLINK_LButtonUp(infoPtr, wParam, &pt);
1524 case WM_KEYDOWN:
1526 switch(wParam)
1528 case VK_RETURN:
1529 SYSLINK_OnEnter(infoPtr);
1530 return 0;
1531 case VK_TAB:
1533 BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
1534 SYSKEY_SelectNextPrevLink(infoPtr, shift);
1535 return 0;
1538 return DefWindowProcW(hwnd, message, wParam, lParam);
1541 case WM_GETDLGCODE:
1543 LRESULT Ret = DLGC_HASSETSEL;
1544 int vk = (lParam != 0 ? (int)((LPMSG)lParam)->wParam : 0);
1545 switch(vk)
1547 case VK_RETURN:
1548 Ret |= DLGC_WANTMESSAGE;
1549 break;
1550 case VK_TAB:
1552 BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
1553 if(!SYSLINK_NoNextLink(infoPtr, shift))
1555 Ret |= DLGC_WANTTAB;
1557 else
1559 Ret |= DLGC_WANTCHARS;
1561 break;
1564 return Ret;
1567 case WM_NCHITTEST:
1569 POINT pt;
1570 RECT rc;
1571 pt.x = LOWORD(lParam);
1572 pt.y = HIWORD(lParam);
1574 GetClientRect(infoPtr->Self, &rc);
1575 ScreenToClient(infoPtr->Self, &pt);
1576 if(pt.x < 0 || pt.y < 0 || pt.x > rc.right || pt.y > rc.bottom)
1578 return HTNOWHERE;
1581 if(SYSLINK_LinkAtPt(infoPtr, &pt, NULL, FALSE))
1583 return HTCLIENT;
1586 return HTTRANSPARENT;
1589 case LM_HITTEST:
1590 return SYSLINK_HitTest(infoPtr, (PLHITTESTINFO)lParam);
1592 case LM_SETITEM:
1593 return SYSLINK_SetItem(infoPtr, (PLITEM)lParam);
1595 case LM_GETITEM:
1596 return SYSLINK_GetItem(infoPtr, (PLITEM)lParam);
1598 case LM_GETIDEALHEIGHT:
1599 return SYSLINK_GetIdealHeight(infoPtr);
1601 case WM_SETFOCUS:
1602 return SYSLINK_SetFocus(infoPtr, (HWND)wParam);
1604 case WM_KILLFOCUS:
1605 return SYSLINK_KillFocus(infoPtr, (HWND)wParam);
1607 case WM_ENABLE:
1608 infoPtr->Style &= ~WS_DISABLED;
1609 infoPtr->Style |= (wParam ? 0 : WS_DISABLED);
1610 InvalidateRect (infoPtr->Self, NULL, FALSE);
1611 return 0;
1613 case WM_STYLECHANGED:
1614 if (wParam == GWL_STYLE)
1616 infoPtr->Style = ((LPSTYLESTRUCT)lParam)->styleNew;
1618 InvalidateRect(infoPtr->Self, NULL, TRUE);
1620 return 0;
1622 case WM_CREATE:
1623 /* allocate memory for info struct */
1624 infoPtr = Alloc (sizeof(SYSLINK_INFO));
1625 if (!infoPtr) return -1;
1626 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
1628 /* initialize the info struct */
1629 infoPtr->Self = hwnd;
1630 infoPtr->Notify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1631 infoPtr->Style = ((LPCREATESTRUCTW)lParam)->style;
1632 infoPtr->Font = 0;
1633 infoPtr->LinkFont = 0;
1634 infoPtr->Items = NULL;
1635 infoPtr->HasFocus = FALSE;
1636 infoPtr->MouseDownID = -1;
1637 infoPtr->TextColor = GetSysColor(COLOR_WINDOWTEXT);
1638 infoPtr->LinkColor = GetSysColor(COLOR_HIGHLIGHT);
1639 infoPtr->VisitedColor = GetSysColor(COLOR_HIGHLIGHT);
1640 TRACE("SysLink Ctrl creation, hwnd=%p\n", hwnd);
1641 SYSLINK_SetText(infoPtr, ((LPCREATESTRUCTW)lParam)->lpszName);
1642 return 0;
1644 case WM_DESTROY:
1645 TRACE("SysLink Ctrl destruction, hwnd=%p\n", hwnd);
1646 SYSLINK_ClearDoc(infoPtr);
1647 if(infoPtr->Font != 0) DeleteObject(infoPtr->Font);
1648 if(infoPtr->LinkFont != 0) DeleteObject(infoPtr->LinkFont);
1649 SetWindowLongPtrW(hwnd, 0, 0);
1650 Free (infoPtr);
1651 return 0;
1653 default:
1654 if ((message >= WM_USER) && (message < WM_APP))
1655 ERR("unknown msg %04x wp=%04x lp=%08lx\n", message, wParam, lParam );
1656 return DefWindowProcW(hwnd, message, wParam, lParam);
1661 /***********************************************************************
1662 * SYSLINK_Register [Internal]
1664 * Registers the SysLink window class.
1666 VOID SYSLINK_Register (void)
1668 WNDCLASSW wndClass;
1670 ZeroMemory (&wndClass, sizeof(wndClass));
1671 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
1672 wndClass.lpfnWndProc = SysLinkWindowProc;
1673 wndClass.cbClsExtra = 0;
1674 wndClass.cbWndExtra = sizeof (SYSLINK_INFO *);
1675 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1676 wndClass.lpszClassName = WC_LINK;
1678 RegisterClassW (&wndClass);
1682 /***********************************************************************
1683 * SYSLINK_Unregister [Internal]
1685 * Unregisters the SysLink window class.
1687 VOID SYSLINK_Unregister (void)
1689 UnregisterClassW (WC_LINK, NULL);