api-ms-win-core-comm-l1-1-0: Add dll.
[wine.git] / dlls / comctl32 / syslink.c
blobcd9d5311fd3d3962511d92a60ab07b7ffb218a84
1 /*
2 * SysLink control
4 * Copyright 2004 - 2006 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <string.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "commctrl.h"
29 #include "comctl32.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
32 #include "wine/list.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(syslink);
36 typedef struct
38 int nChars;
39 int nSkip;
40 RECT rc;
41 } DOC_TEXTBLOCK, *PDOC_TEXTBLOCK;
43 #define LIF_FLAGSMASK (LIF_STATE | LIF_ITEMID | LIF_URL)
44 #define LIS_MASK (LIS_FOCUSED | LIS_ENABLED | LIS_VISITED)
46 typedef enum
48 slText = 0,
49 slLink
50 } SL_ITEM_TYPE;
52 typedef struct _DOC_ITEM
54 struct list entry;
55 UINT nText; /* Number of characters of the text */
56 SL_ITEM_TYPE Type; /* type of the item */
57 PDOC_TEXTBLOCK Blocks; /* Array of text blocks */
58 union
60 struct
62 UINT state; /* Link state */
63 WCHAR *szID; /* Link ID string */
64 WCHAR *szUrl; /* Link URL string */
65 } Link;
66 struct
68 UINT Dummy;
69 } Text;
70 } u;
71 WCHAR Text[1]; /* Text of the document item */
72 } DOC_ITEM, *PDOC_ITEM;
74 typedef struct
76 HWND Self; /* The window handle for this control */
77 HWND Notify; /* The parent handle to receive notifications */
78 DWORD Style; /* Styles for this control */
79 struct list Items; /* Document items list */
80 BOOL HasFocus; /* Whether the control has the input focus */
81 int MouseDownID; /* ID of the link that the mouse button first selected */
82 HFONT Font; /* Handle to the font for text */
83 HFONT LinkFont; /* Handle to the font for links */
84 COLORREF TextColor; /* Color of the text */
85 COLORREF LinkColor; /* Color of links */
86 COLORREF VisitedColor; /* Color of visited links */
87 WCHAR BreakChar; /* Break Character for the current font */
88 BOOL IgnoreReturn; /* (infoPtr->Style & LWS_IGNORERETURN) on creation */
89 } SYSLINK_INFO;
91 /* Control configuration constants */
93 #define SL_LEFTMARGIN (0)
94 #define SL_TOPMARGIN (0)
95 #define SL_RIGHTMARGIN (0)
96 #define SL_BOTTOMMARGIN (0)
98 /***********************************************************************
99 * SYSLINK_FreeDocItem
100 * Frees all data and gdi objects associated with a document item
102 static VOID SYSLINK_FreeDocItem (PDOC_ITEM DocItem)
104 if(DocItem->Type == slLink)
106 Free(DocItem->u.Link.szID);
107 Free(DocItem->u.Link.szUrl);
110 Free(DocItem->Blocks);
112 /* we don't free Text because it's just a pointer to a character in the
113 entire window text string */
115 Free(DocItem);
118 /***********************************************************************
119 * SYSLINK_AppendDocItem
120 * Create and append a new document item.
122 static PDOC_ITEM SYSLINK_AppendDocItem (SYSLINK_INFO *infoPtr, LPCWSTR Text, UINT textlen,
123 SL_ITEM_TYPE type, PDOC_ITEM LastItem)
125 PDOC_ITEM Item;
127 textlen = min(textlen, strlenW(Text));
128 Item = Alloc(FIELD_OFFSET(DOC_ITEM, Text[textlen + 1]));
129 if(Item == NULL)
131 ERR("Failed to alloc DOC_ITEM structure!\n");
132 return NULL;
135 Item->nText = textlen;
136 Item->Type = type;
137 Item->Blocks = NULL;
138 lstrcpynW(Item->Text, Text, textlen + 1);
139 if (LastItem)
140 list_add_after(&LastItem->entry, &Item->entry);
141 else
142 list_add_tail(&infoPtr->Items, &Item->entry);
144 return Item;
147 /***********************************************************************
148 * SYSLINK_ClearDoc
149 * Clears the document tree
151 static VOID SYSLINK_ClearDoc (SYSLINK_INFO *infoPtr)
153 DOC_ITEM *Item, *Item2;
155 LIST_FOR_EACH_ENTRY_SAFE(Item, Item2, &infoPtr->Items, DOC_ITEM, entry)
157 list_remove(&Item->entry);
158 SYSLINK_FreeDocItem(Item);
162 /***********************************************************************
163 * SYSLINK_ParseText
164 * Parses the window text string and creates a document. Returns the
165 * number of document items created.
167 static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
169 static const WCHAR SL_LINKOPEN[] = { '<','a' };
170 static const WCHAR SL_HREF[] = { 'h','r','e','f','=','\"' };
171 static const WCHAR SL_ID[] = { 'i','d','=','\"' };
172 static const WCHAR SL_LINKCLOSE[] = { '<','/','a','>' };
173 LPCWSTR current, textstart = NULL, linktext = NULL, firsttag = NULL;
174 int taglen = 0, textlen = 0, linklen = 0, docitems = 0;
175 PDOC_ITEM Last = NULL;
176 SL_ITEM_TYPE CurrentType = slText;
177 LPCWSTR lpID, lpUrl;
178 UINT lenId, lenUrl;
180 TRACE("(%p %s)\n", infoPtr, debugstr_w(Text));
182 for(current = Text; *current != 0;)
184 if(*current == '<')
186 if(!strncmpiW(current, SL_LINKOPEN, ARRAY_SIZE(SL_LINKOPEN)) && (CurrentType == slText))
188 BOOL ValidParam = FALSE, ValidLink = FALSE;
190 if(*(current + 2) == '>')
192 /* we just have to deal with a <a> tag */
193 taglen = 3;
194 ValidLink = TRUE;
195 ValidParam = TRUE;
196 firsttag = current;
197 linklen = 0;
198 lpID = NULL;
199 lpUrl = NULL;
201 else if(*(current + 2) == infoPtr->BreakChar)
203 /* we expect parameters, parse them */
204 LPCWSTR *CurrentParameter = NULL, tmp;
205 UINT *CurrentParameterLen = NULL;
207 taglen = 3;
208 tmp = current + taglen;
209 lpID = NULL;
210 lpUrl = NULL;
212 CheckParameter:
213 /* compare the current position with all known parameters */
214 if(!strncmpiW(tmp, SL_HREF, ARRAY_SIZE(SL_HREF)))
216 taglen += 6;
217 ValidParam = TRUE;
218 CurrentParameter = &lpUrl;
219 CurrentParameterLen = &lenUrl;
221 else if(!strncmpiW(tmp, SL_ID, ARRAY_SIZE(SL_ID)))
223 taglen += 4;
224 ValidParam = TRUE;
225 CurrentParameter = &lpID;
226 CurrentParameterLen = &lenId;
228 else
230 ValidParam = FALSE;
233 if(ValidParam)
235 /* we got a known parameter, now search until the next " character.
236 If we can't find a " character, there's a syntax error and we just assume it's text */
237 ValidParam = FALSE;
238 *CurrentParameter = current + taglen;
239 *CurrentParameterLen = 0;
241 for(tmp = *CurrentParameter; *tmp != 0; tmp++)
243 taglen++;
244 if(*tmp == '\"')
246 ValidParam = TRUE;
247 tmp++;
248 break;
250 (*CurrentParameterLen)++;
253 if(ValidParam)
255 /* we're done with this parameter, now there are only 2 possibilities:
256 * 1. another parameter is coming, so expect a ' ' (space) character
257 * 2. the tag is being closed, so expect a '<' character
259 if(*tmp == infoPtr->BreakChar)
261 /* we expect another parameter, do the whole thing again */
262 taglen++;
263 tmp++;
264 goto CheckParameter;
266 else if(*tmp == '>')
268 /* the tag is being closed, we're done */
269 ValidLink = TRUE;
270 taglen++;
275 if(ValidLink && ValidParam)
277 /* the <a ...> tag appears to be valid. save all information
278 so we can add the link if we find a valid </a> tag later */
279 CurrentType = slLink;
280 linktext = current + taglen;
281 linklen = 0;
282 firsttag = current;
284 else
286 taglen = 1;
287 lpID = NULL;
288 lpUrl = NULL;
289 if(textstart == NULL)
291 textstart = current;
295 else if(!strncmpiW(current, SL_LINKCLOSE, ARRAY_SIZE(SL_LINKCLOSE)) && (CurrentType == slLink) && firsttag)
297 /* there's a <a...> tag opened, first add the previous text, if present */
298 if(textstart != NULL && textlen > 0 && firsttag > textstart)
300 Last = SYSLINK_AppendDocItem(infoPtr, textstart, firsttag - textstart, slText, Last);
301 if(Last == NULL)
303 ERR("Unable to create new document item!\n");
304 return docitems;
306 docitems++;
307 textstart = NULL;
308 textlen = 0;
311 /* now it's time to add the link to the document */
312 current += 4;
313 if(linktext != NULL && linklen > 0)
315 Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slLink, Last);
316 if(Last == NULL)
318 ERR("Unable to create new document item!\n");
319 return docitems;
321 docitems++;
322 if(CurrentType == slLink)
324 int nc;
326 if(!(infoPtr->Style & WS_DISABLED))
328 Last->u.Link.state |= LIS_ENABLED;
330 /* Copy the tag parameters */
331 if(lpID != NULL)
333 nc = min(lenId, strlenW(lpID));
334 nc = min(nc, MAX_LINKID_TEXT - 1);
335 Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
336 if(Last->u.Link.szID != NULL)
338 lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
341 else
342 Last->u.Link.szID = NULL;
343 if(lpUrl != NULL)
345 nc = min(lenUrl, strlenW(lpUrl));
346 nc = min(nc, L_MAX_URL_LENGTH - 1);
347 Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
348 if(Last->u.Link.szUrl != NULL)
350 lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
353 else
354 Last->u.Link.szUrl = NULL;
356 linktext = NULL;
358 CurrentType = slText;
359 firsttag = NULL;
360 textstart = NULL;
361 continue;
363 else
365 /* we don't know what tag it is, so just continue */
366 taglen = 1;
367 linklen++;
368 if(CurrentType == slText && textstart == NULL)
370 textstart = current;
374 textlen += taglen;
375 current += taglen;
377 else
379 textlen++;
380 linklen++;
382 /* save the pointer of the current text item if we couldn't find a tag */
383 if(textstart == NULL && CurrentType == slText)
385 textstart = current;
388 current++;
392 if(textstart != NULL && textlen > 0)
394 Last = SYSLINK_AppendDocItem(infoPtr, textstart, textlen, CurrentType, Last);
395 if(Last == NULL)
397 ERR("Unable to create new document item!\n");
398 return docitems;
400 if(CurrentType == slLink)
402 int nc;
404 if(!(infoPtr->Style & WS_DISABLED))
406 Last->u.Link.state |= LIS_ENABLED;
408 /* Copy the tag parameters */
409 if(lpID != NULL)
411 nc = min(lenId, strlenW(lpID));
412 nc = min(nc, MAX_LINKID_TEXT - 1);
413 Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
414 if(Last->u.Link.szID != NULL)
416 lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
419 else
420 Last->u.Link.szID = NULL;
421 if(lpUrl != NULL)
423 nc = min(lenUrl, strlenW(lpUrl));
424 nc = min(nc, L_MAX_URL_LENGTH - 1);
425 Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
426 if(Last->u.Link.szUrl != NULL)
428 lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
431 else
432 Last->u.Link.szUrl = NULL;
434 docitems++;
437 if(linktext != NULL && linklen > 0)
439 /* we got an unclosed link, just display the text */
440 Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slText, Last);
441 if(Last == NULL)
443 ERR("Unable to create new document item!\n");
444 return docitems;
446 docitems++;
449 return docitems;
452 /***********************************************************************
453 * SYSLINK_RepaintLink
454 * Repaints a link.
456 static VOID SYSLINK_RepaintLink (const SYSLINK_INFO *infoPtr, const DOC_ITEM *DocItem)
458 PDOC_TEXTBLOCK bl;
459 int n;
461 if(DocItem->Type != slLink)
463 ERR("DocItem not a link!\n");
464 return;
467 bl = DocItem->Blocks;
468 if (bl != NULL)
470 n = DocItem->nText;
472 while(n > 0)
474 InvalidateRect(infoPtr->Self, &bl->rc, TRUE);
475 n -= bl->nChars + bl->nSkip;
476 bl++;
481 /***********************************************************************
482 * SYSLINK_GetLinkItemByIndex
483 * Retrieves a document link by its index
485 static PDOC_ITEM SYSLINK_GetLinkItemByIndex (const SYSLINK_INFO *infoPtr, int iLink)
487 DOC_ITEM *Current;
489 LIST_FOR_EACH_ENTRY(Current, &infoPtr->Items, DOC_ITEM, entry)
491 if ((Current->Type == slLink) && (iLink-- <= 0))
492 return Current;
494 return NULL;
497 /***********************************************************************
498 * SYSLINK_GetFocusLink
499 * Retrieves the link that has the LIS_FOCUSED bit
501 static PDOC_ITEM SYSLINK_GetFocusLink (const SYSLINK_INFO *infoPtr, int *LinkId)
503 DOC_ITEM *Current;
504 int id = 0;
506 LIST_FOR_EACH_ENTRY(Current, &infoPtr->Items, DOC_ITEM, entry)
508 if(Current->Type == slLink)
510 if(Current->u.Link.state & LIS_FOCUSED)
512 if(LinkId != NULL)
513 *LinkId = id;
514 return Current;
516 id++;
520 return NULL;
523 /***********************************************************************
524 * SYSLINK_GetNextLink
525 * Gets the next link
527 static PDOC_ITEM SYSLINK_GetNextLink (const SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
529 DOC_ITEM *Next;
531 LIST_FOR_EACH_ENTRY(Next, Current ? &Current->entry : &infoPtr->Items, DOC_ITEM, entry)
533 if (Next->Type == slLink)
535 return Next;
538 return NULL;
541 /***********************************************************************
542 * SYSLINK_GetPrevLink
543 * Gets the previous link
545 static PDOC_ITEM SYSLINK_GetPrevLink (const SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
547 DOC_ITEM *Prev;
549 LIST_FOR_EACH_ENTRY_REV(Prev, Current ? &Current->entry : list_tail(&infoPtr->Items), DOC_ITEM, entry)
551 if (Prev->Type == slLink)
553 return Prev;
557 return NULL;
560 /***********************************************************************
561 * SYSLINK_WrapLine
562 * Tries to wrap a line.
564 static BOOL SYSLINK_WrapLine (LPWSTR Text, WCHAR BreakChar, int x, int *LineLen,
565 int nFit, LPSIZE Extent)
567 int i;
569 for (i = 0; i < nFit; i++) if (Text[i] == '\n') break;
571 if (i == *LineLen) return FALSE;
573 /* check if we're in the middle of a word */
574 if (Text[i] != '\n' && Text[i] != BreakChar)
576 /* search for the beginning of the word */
577 while (i && Text[i - 1] != BreakChar) i--;
579 if (i == 0)
581 Extent->cx = 0;
582 Extent->cy = 0;
583 if (x == SL_LEFTMARGIN) i = max( nFit, 1 );
586 *LineLen = i;
587 return TRUE;
590 /***********************************************************************
591 * SYSLINK_Render
592 * Renders the document in memory
594 static VOID SYSLINK_Render (const SYSLINK_INFO *infoPtr, HDC hdc, PRECT pRect)
596 RECT rc;
597 PDOC_ITEM Current;
598 HGDIOBJ hOldFont;
599 int x, y, LineHeight;
600 SIZE szDoc;
601 TEXTMETRICW tm;
603 szDoc.cx = szDoc.cy = 0;
605 rc = *pRect;
606 rc.right -= SL_RIGHTMARGIN;
607 rc.bottom -= SL_BOTTOMMARGIN;
609 if(rc.right - SL_LEFTMARGIN < 0)
610 rc.right = MAXLONG;
611 if (rc.bottom - SL_TOPMARGIN < 0)
612 rc.bottom = MAXLONG;
614 hOldFont = SelectObject(hdc, infoPtr->Font);
616 x = SL_LEFTMARGIN;
617 y = SL_TOPMARGIN;
618 GetTextMetricsW( hdc, &tm );
619 LineHeight = tm.tmHeight + tm.tmExternalLeading;
621 LIST_FOR_EACH_ENTRY(Current, &infoPtr->Items, DOC_ITEM, entry)
623 int n, nBlocks;
624 LPWSTR tx;
625 PDOC_TEXTBLOCK bl, cbl;
626 INT nFit;
627 SIZE szDim;
628 int SkipChars = 0;
630 if(Current->nText == 0)
632 continue;
635 tx = Current->Text;
636 n = Current->nText;
638 Free(Current->Blocks);
639 Current->Blocks = NULL;
640 bl = NULL;
641 nBlocks = 0;
643 if(Current->Type == slText)
645 SelectObject(hdc, infoPtr->Font);
647 else if(Current->Type == slLink)
649 SelectObject(hdc, infoPtr->LinkFont);
652 while(n > 0)
654 /* skip break characters unless they're the first of the doc item */
655 if(tx != Current->Text || x == SL_LEFTMARGIN)
657 if (n && *tx == '\n')
659 tx++;
660 SkipChars++;
661 n--;
663 while(n > 0 && (*tx) == infoPtr->BreakChar)
665 tx++;
666 SkipChars++;
667 n--;
671 if((n == 0 && SkipChars != 0) ||
672 GetTextExtentExPointW(hdc, tx, n, rc.right - x, &nFit, NULL, &szDim))
674 int LineLen = n;
675 BOOL Wrap = FALSE;
676 PDOC_TEXTBLOCK nbl;
678 if(n != 0)
680 Wrap = SYSLINK_WrapLine(tx, infoPtr->BreakChar, x, &LineLen, nFit, &szDim);
682 if(LineLen == 0)
684 /* move one line down, the word didn't fit into the line */
685 x = SL_LEFTMARGIN;
686 y += LineHeight;
687 continue;
690 if(LineLen != n)
692 if(!GetTextExtentExPointW(hdc, tx, LineLen, rc.right - x, NULL, NULL, &szDim))
694 if(bl != NULL)
696 Free(bl);
697 bl = NULL;
698 nBlocks = 0;
700 break;
705 nbl = ReAlloc(bl, (nBlocks + 1) * sizeof(DOC_TEXTBLOCK));
706 if (nbl != NULL)
708 bl = nbl;
709 nBlocks++;
711 cbl = bl + nBlocks - 1;
713 cbl->nChars = LineLen;
714 cbl->nSkip = SkipChars;
715 SetRect(&cbl->rc, x, y, x + szDim.cx, y + szDim.cy);
717 if (cbl->rc.right > szDoc.cx)
718 szDoc.cx = cbl->rc.right;
719 if (cbl->rc.bottom > szDoc.cy)
720 szDoc.cy = cbl->rc.bottom;
722 if(LineLen != 0)
724 x += szDim.cx;
725 if(Wrap)
727 x = SL_LEFTMARGIN;
728 y += LineHeight;
732 else
734 Free(bl);
735 bl = NULL;
736 nBlocks = 0;
738 ERR("Failed to alloc DOC_TEXTBLOCK structure!\n");
739 break;
741 n -= LineLen;
742 tx += LineLen;
743 SkipChars = 0;
745 else
747 n--;
751 if(nBlocks != 0)
753 Current->Blocks = bl;
757 SelectObject(hdc, hOldFont);
759 pRect->right = pRect->left + szDoc.cx;
760 pRect->bottom = pRect->top + szDoc.cy;
763 /***********************************************************************
764 * SYSLINK_Draw
765 * Draws the SysLink control.
767 static LRESULT SYSLINK_Draw (const SYSLINK_INFO *infoPtr, HDC hdc)
769 RECT rc;
770 PDOC_ITEM Current;
771 HFONT hOldFont;
772 COLORREF OldTextColor, OldBkColor;
773 HBRUSH hBrush;
774 UINT text_flags = ETO_CLIPPED;
775 UINT mode = GetBkMode( hdc );
777 hOldFont = SelectObject(hdc, infoPtr->Font);
778 OldTextColor = SetTextColor(hdc, infoPtr->TextColor);
779 OldBkColor = SetBkColor(hdc, comctl32_color.clrWindow);
781 GetClientRect(infoPtr->Self, &rc);
782 rc.right -= SL_RIGHTMARGIN + SL_LEFTMARGIN;
783 rc.bottom -= SL_BOTTOMMARGIN + SL_TOPMARGIN;
785 if(rc.right < 0 || rc.bottom < 0) return 0;
787 hBrush = (HBRUSH)SendMessageW(infoPtr->Notify, WM_CTLCOLORSTATIC,
788 (WPARAM)hdc, (LPARAM)infoPtr->Self);
789 if (!(infoPtr->Style & LWS_TRANSPARENT))
791 FillRect(hdc, &rc, hBrush);
792 if (GetBkMode( hdc ) == OPAQUE) text_flags |= ETO_OPAQUE;
794 else SetBkMode( hdc, TRANSPARENT );
796 DeleteObject(hBrush);
798 LIST_FOR_EACH_ENTRY(Current, &infoPtr->Items, DOC_ITEM, entry)
800 int n;
801 LPWSTR tx;
802 PDOC_TEXTBLOCK bl;
804 bl = Current->Blocks;
805 if(bl != NULL)
807 tx = Current->Text;
808 n = Current->nText;
810 if(Current->Type == slText)
812 SelectObject(hdc, infoPtr->Font);
813 SetTextColor(hdc, infoPtr->TextColor);
815 else
817 SelectObject(hdc, infoPtr->LinkFont);
818 SetTextColor(hdc, (!(Current->u.Link.state & LIS_VISITED) ? infoPtr->LinkColor : infoPtr->VisitedColor));
821 while(n > 0)
823 tx += bl->nSkip;
824 ExtTextOutW(hdc, bl->rc.left, bl->rc.top, text_flags, &bl->rc, tx, bl->nChars, NULL);
825 if((Current->Type == slLink) && (Current->u.Link.state & LIS_FOCUSED) && infoPtr->HasFocus)
827 COLORREF PrevTextColor;
828 PrevTextColor = SetTextColor(hdc, infoPtr->TextColor);
829 DrawFocusRect(hdc, &bl->rc);
830 SetTextColor(hdc, PrevTextColor);
832 tx += bl->nChars;
833 n -= bl->nChars + bl->nSkip;
834 bl++;
839 SetBkColor(hdc, OldBkColor);
840 SetTextColor(hdc, OldTextColor);
841 SelectObject(hdc, hOldFont);
842 SetBkMode(hdc, mode);
843 return 0;
847 /***********************************************************************
848 * SYSLINK_Paint
849 * Handles the WM_PAINT message.
851 static LRESULT SYSLINK_Paint (const SYSLINK_INFO *infoPtr, HDC hdcParam)
853 HDC hdc;
854 PAINTSTRUCT ps;
856 hdc = hdcParam ? hdcParam : BeginPaint (infoPtr->Self, &ps);
857 if (hdc)
859 SYSLINK_Draw (infoPtr, hdc);
860 if (!hdcParam) EndPaint (infoPtr->Self, &ps);
862 return 0;
865 /***********************************************************************
866 * SYSLINK_SetFont
867 * Set new Font for the SysLink control.
869 static HFONT SYSLINK_SetFont (SYSLINK_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
871 HDC hdc;
872 LOGFONTW lf;
873 TEXTMETRICW tm;
874 RECT rcClient;
875 HFONT hOldFont = infoPtr->Font;
876 infoPtr->Font = hFont;
878 /* free the underline font */
879 if(infoPtr->LinkFont != NULL)
881 DeleteObject(infoPtr->LinkFont);
882 infoPtr->LinkFont = NULL;
885 /* Render text position and word wrapping in memory */
886 if (GetClientRect(infoPtr->Self, &rcClient))
888 hdc = GetDC(infoPtr->Self);
889 if(hdc != NULL)
891 /* create a new underline font */
892 if(GetTextMetricsW(hdc, &tm) &&
893 GetObjectW(infoPtr->Font, sizeof(LOGFONTW), &lf))
895 lf.lfUnderline = TRUE;
896 infoPtr->LinkFont = CreateFontIndirectW(&lf);
897 infoPtr->BreakChar = tm.tmBreakChar;
899 else
901 ERR("Failed to create link font!\n");
904 SYSLINK_Render(infoPtr, hdc, &rcClient);
905 ReleaseDC(infoPtr->Self, hdc);
909 if(bRedraw)
911 RedrawWindow(infoPtr->Self, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
914 return hOldFont;
917 /***********************************************************************
918 * SYSLINK_SetText
919 * Set new text for the SysLink control.
921 static LRESULT SYSLINK_SetText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
923 /* clear the document */
924 SYSLINK_ClearDoc(infoPtr);
926 if(Text == NULL || *Text == 0)
928 return TRUE;
931 /* let's parse the string and create a document */
932 if(SYSLINK_ParseText(infoPtr, Text) > 0)
934 RECT rcClient;
936 /* Render text position and word wrapping in memory */
937 if (GetClientRect(infoPtr->Self, &rcClient))
939 HDC hdc = GetDC(infoPtr->Self);
940 if (hdc != NULL)
942 SYSLINK_Render(infoPtr, hdc, &rcClient);
943 ReleaseDC(infoPtr->Self, hdc);
945 InvalidateRect(infoPtr->Self, NULL, TRUE);
950 return TRUE;
953 /***********************************************************************
954 * SYSLINK_SetFocusLink
955 * Updates the focus status bits and focusses the specified link.
956 * If no document item is specified, the focus bit will be removed from all links.
957 * Returns the previous focused item.
959 static PDOC_ITEM SYSLINK_SetFocusLink (const SYSLINK_INFO *infoPtr, const DOC_ITEM *DocItem)
961 PDOC_ITEM Current, PrevFocus = NULL;
963 LIST_FOR_EACH_ENTRY(Current, &infoPtr->Items, DOC_ITEM, entry)
965 if(Current->Type == slLink)
967 if((PrevFocus == NULL) && (Current->u.Link.state & LIS_FOCUSED))
969 PrevFocus = Current;
972 if(Current == DocItem)
974 Current->u.Link.state |= LIS_FOCUSED;
976 else
978 Current->u.Link.state &= ~LIS_FOCUSED;
983 return PrevFocus;
986 /***********************************************************************
987 * SYSLINK_SetItem
988 * Sets the states and attributes of a link item.
990 static LRESULT SYSLINK_SetItem (const SYSLINK_INFO *infoPtr, const LITEM *Item)
992 PDOC_ITEM di;
993 int nc;
994 PWSTR szId = NULL;
995 PWSTR szUrl = NULL;
996 BOOL Repaint = FALSE;
998 if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
1000 ERR("Invalid Flags!\n");
1001 return FALSE;
1004 di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
1005 if(di == NULL)
1007 ERR("Link %d couldn't be found\n", Item->iLink);
1008 return FALSE;
1011 if(Item->mask & LIF_ITEMID)
1013 nc = min(lstrlenW(Item->szID), MAX_LINKID_TEXT - 1);
1014 szId = Alloc((nc + 1) * sizeof(WCHAR));
1015 if(szId)
1017 lstrcpynW(szId, Item->szID, nc + 1);
1019 else
1021 ERR("Unable to allocate memory for link id\n");
1022 return FALSE;
1026 if(Item->mask & LIF_URL)
1028 nc = min(lstrlenW(Item->szUrl), L_MAX_URL_LENGTH - 1);
1029 szUrl = Alloc((nc + 1) * sizeof(WCHAR));
1030 if(szUrl)
1032 lstrcpynW(szUrl, Item->szUrl, nc + 1);
1034 else
1036 Free(szId);
1038 ERR("Unable to allocate memory for link url\n");
1039 return FALSE;
1043 if(Item->mask & LIF_ITEMID)
1045 Free(di->u.Link.szID);
1046 di->u.Link.szID = szId;
1049 if(Item->mask & LIF_URL)
1051 Free(di->u.Link.szUrl);
1052 di->u.Link.szUrl = szUrl;
1055 if(Item->mask & LIF_STATE)
1057 UINT oldstate = di->u.Link.state;
1058 /* clear the masked bits */
1059 di->u.Link.state &= ~(Item->stateMask & LIS_MASK);
1060 /* copy the bits */
1061 di->u.Link.state |= (Item->state & Item->stateMask) & LIS_MASK;
1062 Repaint = (oldstate != di->u.Link.state);
1064 /* update the focus */
1065 SYSLINK_SetFocusLink(infoPtr, ((di->u.Link.state & LIS_FOCUSED) ? di : NULL));
1068 if(Repaint)
1070 SYSLINK_RepaintLink(infoPtr, di);
1073 return TRUE;
1076 /***********************************************************************
1077 * SYSLINK_GetItem
1078 * Retrieves the states and attributes of a link item.
1080 static LRESULT SYSLINK_GetItem (const SYSLINK_INFO *infoPtr, PLITEM Item)
1082 PDOC_ITEM di;
1084 if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
1086 ERR("Invalid Flags!\n");
1087 return FALSE;
1090 di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
1091 if(di == NULL)
1093 ERR("Link %d couldn't be found\n", Item->iLink);
1094 return FALSE;
1097 if(Item->mask & LIF_STATE)
1099 Item->state = (di->u.Link.state & Item->stateMask);
1100 if(!infoPtr->HasFocus)
1102 /* remove the LIS_FOCUSED bit if the control doesn't have focus */
1103 Item->state &= ~LIS_FOCUSED;
1107 if(Item->mask & LIF_ITEMID)
1109 if(di->u.Link.szID)
1111 lstrcpyW(Item->szID, di->u.Link.szID);
1113 else
1115 Item->szID[0] = 0;
1119 if(Item->mask & LIF_URL)
1121 if(di->u.Link.szUrl)
1123 lstrcpyW(Item->szUrl, di->u.Link.szUrl);
1125 else
1127 Item->szUrl[0] = 0;
1131 return TRUE;
1134 /***********************************************************************
1135 * SYSLINK_PtInDocItem
1136 * Determines if a point is in the region of a document item
1138 static BOOL SYSLINK_PtInDocItem (const DOC_ITEM *DocItem, POINT pt)
1140 PDOC_TEXTBLOCK bl;
1141 int n;
1143 bl = DocItem->Blocks;
1144 if (bl != NULL)
1146 n = DocItem->nText;
1148 while(n > 0)
1150 if (PtInRect(&bl->rc, pt))
1152 return TRUE;
1154 n -= bl->nChars + bl->nSkip;
1155 bl++;
1159 return FALSE;
1162 /***********************************************************************
1163 * SYSLINK_HitTest
1164 * Determines the link the user clicked on.
1166 static LRESULT SYSLINK_HitTest (const SYSLINK_INFO *infoPtr, PLHITTESTINFO HitTest)
1168 PDOC_ITEM Current;
1169 int id = 0;
1171 LIST_FOR_EACH_ENTRY(Current, &infoPtr->Items, DOC_ITEM, entry)
1173 if(Current->Type == slLink)
1175 if(SYSLINK_PtInDocItem(Current, HitTest->pt))
1177 HitTest->item.mask = 0;
1178 HitTest->item.iLink = id;
1179 HitTest->item.state = 0;
1180 HitTest->item.stateMask = 0;
1181 if(Current->u.Link.szID)
1183 lstrcpyW(HitTest->item.szID, Current->u.Link.szID);
1185 else
1187 HitTest->item.szID[0] = 0;
1189 if(Current->u.Link.szUrl)
1191 lstrcpyW(HitTest->item.szUrl, Current->u.Link.szUrl);
1193 else
1195 HitTest->item.szUrl[0] = 0;
1197 return TRUE;
1199 id++;
1203 return FALSE;
1206 /***********************************************************************
1207 * SYSLINK_GetIdealHeight
1208 * Returns the preferred height of a link at the current control's width.
1210 static LRESULT SYSLINK_GetIdealHeight (const SYSLINK_INFO *infoPtr)
1212 HDC hdc = GetDC(infoPtr->Self);
1213 if(hdc != NULL)
1215 LRESULT height;
1216 TEXTMETRICW tm;
1217 HGDIOBJ hOldFont = SelectObject(hdc, infoPtr->Font);
1219 if(GetTextMetricsW(hdc, &tm))
1221 height = tm.tmHeight;
1223 else
1225 height = 0;
1227 SelectObject(hdc, hOldFont);
1228 ReleaseDC(infoPtr->Self, hdc);
1230 return height;
1232 return 0;
1235 /***********************************************************************
1236 * SYSLINK_SendParentNotify
1237 * Sends a WM_NOTIFY message to the parent window.
1239 static LRESULT SYSLINK_SendParentNotify (const SYSLINK_INFO *infoPtr, UINT code, const DOC_ITEM *Link, int iLink)
1241 NMLINK nml;
1243 nml.hdr.hwndFrom = infoPtr->Self;
1244 nml.hdr.idFrom = GetWindowLongPtrW(infoPtr->Self, GWLP_ID);
1245 nml.hdr.code = code;
1247 nml.item.mask = 0;
1248 nml.item.iLink = iLink;
1249 nml.item.state = 0;
1250 nml.item.stateMask = 0;
1251 if(Link->u.Link.szID)
1253 lstrcpyW(nml.item.szID, Link->u.Link.szID);
1255 else
1257 nml.item.szID[0] = 0;
1259 if(Link->u.Link.szUrl)
1261 lstrcpyW(nml.item.szUrl, Link->u.Link.szUrl);
1263 else
1265 nml.item.szUrl[0] = 0;
1268 return SendMessageW(infoPtr->Notify, WM_NOTIFY, nml.hdr.idFrom, (LPARAM)&nml);
1271 /***********************************************************************
1272 * SYSLINK_SetFocus
1273 * Handles receiving the input focus.
1275 static LRESULT SYSLINK_SetFocus (SYSLINK_INFO *infoPtr)
1277 PDOC_ITEM Focus;
1279 infoPtr->HasFocus = TRUE;
1281 /* We always select the first link, even if we activated the control using
1282 SHIFT+TAB. This is the default behavior */
1283 Focus = SYSLINK_GetNextLink(infoPtr, NULL);
1284 if(Focus != NULL)
1286 SYSLINK_SetFocusLink(infoPtr, Focus);
1287 SYSLINK_RepaintLink(infoPtr, Focus);
1289 return 0;
1292 /***********************************************************************
1293 * SYSLINK_KillFocus
1294 * Handles losing the input focus.
1296 static LRESULT SYSLINK_KillFocus (SYSLINK_INFO *infoPtr)
1298 PDOC_ITEM Focus;
1300 infoPtr->HasFocus = FALSE;
1301 Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1303 if(Focus != NULL)
1305 SYSLINK_RepaintLink(infoPtr, Focus);
1308 return 0;
1311 /***********************************************************************
1312 * SYSLINK_LinkAtPt
1313 * Returns a link at the specified position
1315 static PDOC_ITEM SYSLINK_LinkAtPt (const SYSLINK_INFO *infoPtr, const POINT *pt, int *LinkId, BOOL MustBeEnabled)
1317 PDOC_ITEM Current;
1318 int id = 0;
1320 LIST_FOR_EACH_ENTRY(Current, &infoPtr->Items, DOC_ITEM, entry)
1322 if((Current->Type == slLink) && SYSLINK_PtInDocItem(Current, *pt) &&
1323 (!MustBeEnabled || (Current->u.Link.state & LIS_ENABLED)))
1325 if(LinkId != NULL)
1327 *LinkId = id;
1329 return Current;
1331 id++;
1334 return NULL;
1337 /***********************************************************************
1338 * SYSLINK_LButtonDown
1339 * Handles mouse clicks
1341 static LRESULT SYSLINK_LButtonDown (SYSLINK_INFO *infoPtr, const POINT *pt)
1343 PDOC_ITEM Current, Old;
1344 int id;
1346 Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
1347 if(Current != NULL)
1349 SetFocus(infoPtr->Self);
1351 Old = SYSLINK_SetFocusLink(infoPtr, Current);
1352 if(Old != NULL && Old != Current)
1354 SYSLINK_RepaintLink(infoPtr, Old);
1356 infoPtr->MouseDownID = id;
1357 SYSLINK_RepaintLink(infoPtr, Current);
1360 return 0;
1363 /***********************************************************************
1364 * SYSLINK_LButtonUp
1365 * Handles mouse clicks
1367 static LRESULT SYSLINK_LButtonUp (SYSLINK_INFO *infoPtr, const POINT *pt)
1369 if(infoPtr->MouseDownID > -1)
1371 PDOC_ITEM Current;
1372 int id;
1374 Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
1375 if((Current != NULL) && (Current->u.Link.state & LIS_FOCUSED) && (infoPtr->MouseDownID == id))
1377 SYSLINK_SendParentNotify(infoPtr, NM_CLICK, Current, id);
1381 infoPtr->MouseDownID = -1;
1383 return 0;
1386 /***********************************************************************
1387 * SYSLINK_OnEnter
1388 * Handles ENTER key events
1390 static BOOL SYSLINK_OnEnter (const SYSLINK_INFO *infoPtr)
1392 if(infoPtr->HasFocus && !infoPtr->IgnoreReturn)
1394 PDOC_ITEM Focus;
1395 int id;
1397 Focus = SYSLINK_GetFocusLink(infoPtr, &id);
1398 if(Focus)
1400 SYSLINK_SendParentNotify(infoPtr, NM_RETURN, Focus, id);
1401 return TRUE;
1404 return FALSE;
1407 /***********************************************************************
1408 * SYSKEY_SelectNextPrevLink
1409 * Changes the currently focused link
1411 static BOOL SYSKEY_SelectNextPrevLink (const SYSLINK_INFO *infoPtr, BOOL Prev)
1413 if(infoPtr->HasFocus)
1415 PDOC_ITEM Focus;
1416 int id;
1418 Focus = SYSLINK_GetFocusLink(infoPtr, &id);
1419 if(Focus != NULL)
1421 PDOC_ITEM NewFocus, OldFocus;
1423 if(Prev)
1424 NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
1425 else
1426 NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
1428 if(NewFocus != NULL)
1430 OldFocus = SYSLINK_SetFocusLink(infoPtr, NewFocus);
1432 if(OldFocus && OldFocus != NewFocus)
1434 SYSLINK_RepaintLink(infoPtr, OldFocus);
1436 SYSLINK_RepaintLink(infoPtr, NewFocus);
1437 return TRUE;
1441 return FALSE;
1444 /***********************************************************************
1445 * SYSKEY_SelectNextPrevLink
1446 * Determines if there's a next or previous link to decide whether the control
1447 * should capture the tab key message
1449 static BOOL SYSLINK_NoNextLink (const SYSLINK_INFO *infoPtr, BOOL Prev)
1451 PDOC_ITEM Focus, NewFocus;
1453 Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1454 if(Prev)
1455 NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
1456 else
1457 NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
1459 return NewFocus == NULL;
1462 /***********************************************************************
1463 * SYSLINK_GetIdealSize
1464 * Calculates the ideal size of a link control at a given maximum width.
1466 static LONG SYSLINK_GetIdealSize (const SYSLINK_INFO *infoPtr, int cxMaxWidth, SIZE *lpSize)
1468 RECT rc;
1469 HDC hdc;
1471 rc.left = rc.top = rc.bottom = 0;
1472 rc.right = cxMaxWidth;
1474 hdc = GetDC(infoPtr->Self);
1475 if (hdc != NULL)
1477 HGDIOBJ hOldFont = SelectObject(hdc, infoPtr->Font);
1479 SYSLINK_Render(infoPtr, hdc, &rc);
1481 SelectObject(hdc, hOldFont);
1482 ReleaseDC(infoPtr->Self, hdc);
1484 lpSize->cx = rc.right;
1485 lpSize->cy = rc.bottom;
1488 return rc.bottom;
1491 /***********************************************************************
1492 * SysLinkWindowProc
1494 static LRESULT WINAPI SysLinkWindowProc(HWND hwnd, UINT message,
1495 WPARAM wParam, LPARAM lParam)
1497 SYSLINK_INFO *infoPtr;
1499 TRACE("hwnd=%p msg=%04x wparam=%lx lParam=%lx\n", hwnd, message, wParam, lParam);
1501 infoPtr = (SYSLINK_INFO *)GetWindowLongPtrW(hwnd, 0);
1503 if (!infoPtr && message != WM_CREATE)
1504 return DefWindowProcW(hwnd, message, wParam, lParam);
1506 switch(message) {
1507 case WM_PRINTCLIENT:
1508 case WM_PAINT:
1509 return SYSLINK_Paint (infoPtr, (HDC)wParam);
1511 case WM_ERASEBKGND:
1512 if (!(infoPtr->Style & LWS_TRANSPARENT))
1514 HDC hdc = (HDC)wParam;
1515 HBRUSH brush = CreateSolidBrush( comctl32_color.clrWindow );
1516 RECT rect;
1518 GetClipBox( hdc, &rect );
1519 FillRect( hdc, &rect, brush );
1520 DeleteObject( brush );
1521 return 1;
1523 return 0;
1525 case WM_SETCURSOR:
1527 LHITTESTINFO ht;
1528 DWORD mp = GetMessagePos();
1530 ht.pt.x = (short)LOWORD(mp);
1531 ht.pt.y = (short)HIWORD(mp);
1533 ScreenToClient(infoPtr->Self, &ht.pt);
1534 if(SYSLINK_HitTest (infoPtr, &ht))
1536 SetCursor(LoadCursorW(0, (LPCWSTR)IDC_HAND));
1537 return TRUE;
1540 return DefWindowProcW(hwnd, message, wParam, lParam);
1543 case WM_SIZE:
1545 RECT rcClient;
1546 if (GetClientRect(infoPtr->Self, &rcClient))
1548 HDC hdc = GetDC(infoPtr->Self);
1549 if(hdc != NULL)
1551 SYSLINK_Render(infoPtr, hdc, &rcClient);
1552 ReleaseDC(infoPtr->Self, hdc);
1555 return 0;
1558 case WM_GETFONT:
1559 return (LRESULT)infoPtr->Font;
1561 case WM_SETFONT:
1562 return (LRESULT)SYSLINK_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
1564 case WM_SETTEXT:
1565 SYSLINK_SetText(infoPtr, (LPWSTR)lParam);
1566 return DefWindowProcW(hwnd, message, wParam, lParam);
1568 case WM_LBUTTONDOWN:
1570 POINT pt;
1571 pt.x = (short)LOWORD(lParam);
1572 pt.y = (short)HIWORD(lParam);
1573 return SYSLINK_LButtonDown(infoPtr, &pt);
1575 case WM_LBUTTONUP:
1577 POINT pt;
1578 pt.x = (short)LOWORD(lParam);
1579 pt.y = (short)HIWORD(lParam);
1580 return SYSLINK_LButtonUp(infoPtr, &pt);
1583 case WM_KEYDOWN:
1585 switch(wParam)
1587 case VK_RETURN:
1588 SYSLINK_OnEnter(infoPtr);
1589 return 0;
1590 case VK_TAB:
1592 BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
1593 SYSKEY_SelectNextPrevLink(infoPtr, shift);
1594 return 0;
1596 default:
1597 return DefWindowProcW(hwnd, message, wParam, lParam);
1601 case WM_GETDLGCODE:
1603 LRESULT Ret = DLGC_HASSETSEL;
1604 int vk = (lParam != 0 ? (int)((LPMSG)lParam)->wParam : 0);
1605 switch(vk)
1607 case VK_RETURN:
1608 Ret |= DLGC_WANTMESSAGE;
1609 break;
1610 case VK_TAB:
1612 BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
1613 if(!SYSLINK_NoNextLink(infoPtr, shift))
1615 Ret |= DLGC_WANTTAB;
1617 else
1619 Ret |= DLGC_WANTCHARS;
1621 break;
1624 return Ret;
1627 case WM_NCHITTEST:
1629 POINT pt;
1630 RECT rc;
1631 pt.x = (short)LOWORD(lParam);
1632 pt.y = (short)HIWORD(lParam);
1634 GetClientRect(infoPtr->Self, &rc);
1635 ScreenToClient(infoPtr->Self, &pt);
1636 if(pt.x < 0 || pt.y < 0 || pt.x > rc.right || pt.y > rc.bottom)
1638 return HTNOWHERE;
1641 if(SYSLINK_LinkAtPt(infoPtr, &pt, NULL, FALSE))
1643 return HTCLIENT;
1646 return HTTRANSPARENT;
1649 case LM_HITTEST:
1650 return SYSLINK_HitTest(infoPtr, (PLHITTESTINFO)lParam);
1652 case LM_SETITEM:
1653 return SYSLINK_SetItem(infoPtr, (PLITEM)lParam);
1655 case LM_GETITEM:
1656 return SYSLINK_GetItem(infoPtr, (PLITEM)lParam);
1658 case LM_GETIDEALHEIGHT:
1659 if (lParam)
1660 return SYSLINK_GetIdealSize(infoPtr, (int)wParam, (SIZE *)lParam);
1661 else
1662 return SYSLINK_GetIdealHeight(infoPtr);
1664 case WM_SETFOCUS:
1665 return SYSLINK_SetFocus(infoPtr);
1667 case WM_KILLFOCUS:
1668 return SYSLINK_KillFocus(infoPtr);
1670 case WM_ENABLE:
1671 infoPtr->Style &= ~WS_DISABLED;
1672 infoPtr->Style |= (wParam ? 0 : WS_DISABLED);
1673 InvalidateRect (infoPtr->Self, NULL, FALSE);
1674 return 0;
1676 case WM_STYLECHANGED:
1677 if (wParam == GWL_STYLE)
1679 infoPtr->Style = ((LPSTYLESTRUCT)lParam)->styleNew;
1681 InvalidateRect(infoPtr->Self, NULL, TRUE);
1683 return 0;
1685 case WM_CREATE:
1687 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
1689 /* allocate memory for info struct */
1690 infoPtr = Alloc (sizeof(SYSLINK_INFO));
1691 if (!infoPtr) return -1;
1692 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
1694 /* initialize the info struct */
1695 infoPtr->Self = hwnd;
1696 infoPtr->Notify = cs->hwndParent;
1697 infoPtr->Style = cs->style;
1698 infoPtr->Font = 0;
1699 infoPtr->LinkFont = 0;
1700 list_init(&infoPtr->Items);
1701 infoPtr->HasFocus = FALSE;
1702 infoPtr->MouseDownID = -1;
1703 infoPtr->TextColor = comctl32_color.clrWindowText;
1704 infoPtr->LinkColor = comctl32_color.clrHighlight;
1705 infoPtr->VisitedColor = comctl32_color.clrHighlight;
1706 infoPtr->BreakChar = ' ';
1707 infoPtr->IgnoreReturn = infoPtr->Style & LWS_IGNORERETURN;
1708 TRACE("SysLink Ctrl creation, hwnd=%p\n", hwnd);
1709 SYSLINK_SetText(infoPtr, cs->lpszName);
1710 return 0;
1712 case WM_DESTROY:
1713 TRACE("SysLink Ctrl destruction, hwnd=%p\n", hwnd);
1714 SYSLINK_ClearDoc(infoPtr);
1715 if(infoPtr->Font != 0) DeleteObject(infoPtr->Font);
1716 if(infoPtr->LinkFont != 0) DeleteObject(infoPtr->LinkFont);
1717 SetWindowLongPtrW(hwnd, 0, 0);
1718 Free (infoPtr);
1719 return 0;
1721 case WM_SYSCOLORCHANGE:
1722 COMCTL32_RefreshSysColors();
1723 return 0;
1725 default:
1726 if ((message >= WM_USER) && (message < WM_APP) && !COMCTL32_IsReflectedMessage(message))
1728 ERR("unknown msg %04x wp=%04lx lp=%08lx\n", message, wParam, lParam );
1730 return DefWindowProcW(hwnd, message, wParam, lParam);
1735 /***********************************************************************
1736 * SYSLINK_Register [Internal]
1738 * Registers the SysLink window class.
1740 VOID SYSLINK_Register (void)
1742 WNDCLASSW wndClass;
1744 ZeroMemory (&wndClass, sizeof(wndClass));
1745 wndClass.style = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
1746 wndClass.lpfnWndProc = SysLinkWindowProc;
1747 wndClass.cbClsExtra = 0;
1748 wndClass.cbWndExtra = sizeof (SYSLINK_INFO *);
1749 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1750 wndClass.lpszClassName = WC_LINK;
1752 RegisterClassW (&wndClass);
1756 /***********************************************************************
1757 * SYSLINK_Unregister [Internal]
1759 * Unregisters the SysLink window class.
1761 VOID SYSLINK_Unregister (void)
1763 UnregisterClassW (WC_LINK, NULL);