wined3d: Add an ARB_sync implementation of event queries.
[wine] / dlls / comctl32 / syslink.c
1 /*
2  * SysLink control
3  *
4  * Copyright 2004 - 2006 Thomas Weidenmueller <w3seek@reactos.com>
5  *
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.
10  *
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.
15  *
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
19  *
20  * NOTES
21  *
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.
24  * 
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.
28  */
29
30 #include <stdarg.h>
31 #include <string.h>
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36 #include "winnls.h"
37 #include "commctrl.h"
38 #include "comctl32.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(progress);
43
44 INT WINAPI StrCmpNIW(LPCWSTR,LPCWSTR,INT);
45
46 typedef struct
47 {
48     int nChars;
49     int nSkip;
50     RECT rc;
51 } DOC_TEXTBLOCK, *PDOC_TEXTBLOCK;
52
53 #define LIF_FLAGSMASK   (LIF_STATE | LIF_ITEMID | LIF_URL)
54 #define LIS_MASK        (LIS_FOCUSED | LIS_ENABLED | LIS_VISITED)
55
56 typedef enum
57 {
58     slText = 0,
59     slLink
60 } SL_ITEM_TYPE;
61
62 typedef struct _DOC_ITEM
63 {
64     struct _DOC_ITEM *Next; /* Address to the next item */
65     UINT nText;             /* Number of characters of the text */
66     SL_ITEM_TYPE Type;      /* type of the item */
67     PDOC_TEXTBLOCK Blocks;  /* Array of text blocks */
68     union
69     {
70         struct
71         {
72             UINT state;     /* Link state */
73             WCHAR *szID;    /* Link ID string */
74             WCHAR *szUrl;   /* Link URL string */
75         } Link;
76         struct
77         {
78             UINT Dummy;
79         } Text;
80     } u;
81     WCHAR Text[1];          /* Text of the document item */
82 } DOC_ITEM, *PDOC_ITEM;
83
84 typedef struct
85 {
86     HWND      Self;         /* The window handle for this control */
87     HWND      Notify;       /* The parent handle to receive notifications */
88     DWORD     Style;        /* Styles 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     COLORREF  BackColor;    /* Background color, set on creation */
98     WCHAR     BreakChar;    /* Break Character for the current font */
99     BOOL      IgnoreReturn; /* (infoPtr->Style & LWS_IGNORERETURN) on creation */
100 } SYSLINK_INFO;
101
102 static const WCHAR SL_LINKOPEN[] =  { '<','a', 0 };
103 static const WCHAR SL_HREF[] =      { 'h','r','e','f','=','\"',0 };
104 static const WCHAR SL_ID[] =        { 'i','d','=','\"',0 };
105 static const WCHAR SL_LINKCLOSE[] = { '<','/','a','>',0 };
106
107 /* Control configuration constants */
108
109 #define SL_LEFTMARGIN   (0)
110 #define SL_TOPMARGIN    (0)
111 #define SL_RIGHTMARGIN  (0)
112 #define SL_BOTTOMMARGIN (0)
113
114 /***********************************************************************
115  * SYSLINK_FreeDocItem
116  * Frees all data and gdi objects associated with a document item
117  */
118 static VOID SYSLINK_FreeDocItem (PDOC_ITEM DocItem)
119 {
120     if(DocItem->Type == slLink)
121     {
122         Free(DocItem->u.Link.szID);
123         Free(DocItem->u.Link.szUrl);
124     }
125
126     /* we don't free Text because it's just a pointer to a character in the
127        entire window text string */
128
129     Free(DocItem);
130 }
131
132 /***********************************************************************
133  * SYSLINK_AppendDocItem
134  * Create and append a new document item.
135  */
136 static PDOC_ITEM SYSLINK_AppendDocItem (SYSLINK_INFO *infoPtr, LPCWSTR Text, UINT textlen,
137                                         SL_ITEM_TYPE type, PDOC_ITEM LastItem)
138 {
139     PDOC_ITEM Item;
140
141     textlen = min(textlen, strlenW(Text));
142     Item = Alloc(FIELD_OFFSET(DOC_ITEM, Text[textlen + 1]));
143     if(Item == NULL)
144     {
145         ERR("Failed to alloc DOC_ITEM structure!\n");
146         return NULL;
147     }
148
149     Item->Next = NULL;
150     Item->nText = textlen;
151     Item->Type = type;
152     Item->Blocks = NULL;
153     
154     if(LastItem != NULL)
155     {
156         LastItem->Next = Item;
157     }
158     else
159     {
160         infoPtr->Items = Item;
161     }
162     
163     lstrcpynW(Item->Text, Text, textlen + 1);
164     
165     return Item;
166 }
167
168 /***********************************************************************
169  * SYSLINK_ClearDoc
170  * Clears the document tree
171  */
172 static VOID SYSLINK_ClearDoc (SYSLINK_INFO *infoPtr)
173 {
174     PDOC_ITEM Item, Next;
175     
176     Item = infoPtr->Items;
177     while(Item != NULL)
178     {
179         Next = Item->Next;
180         SYSLINK_FreeDocItem(Item);
181         Item = Next;
182     }
183     
184     infoPtr->Items = NULL;
185 }
186
187 /***********************************************************************
188  * SYSLINK_ParseText
189  * Parses the window text string and creates a document. Returns the
190  * number of document items created.
191  */
192 static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
193 {
194     LPCWSTR current, textstart = NULL, linktext = NULL, firsttag = NULL;
195     int taglen = 0, textlen = 0, linklen = 0, docitems = 0;
196     PDOC_ITEM Last = NULL;
197     SL_ITEM_TYPE CurrentType = slText;
198     LPCWSTR lpID, lpUrl;
199     UINT lenId, lenUrl;
200
201     for(current = Text; *current != 0;)
202     {
203         if(*current == '<')
204         {
205             if(!StrCmpNIW(current, SL_LINKOPEN, 2) && (CurrentType == slText))
206             {
207                 BOOL ValidParam = FALSE, ValidLink = FALSE;
208
209                 if(*(current + 2) == '>')
210                 {
211                     /* we just have to deal with a <a> tag */
212                     taglen = 3;
213                     ValidLink = TRUE;
214                     ValidParam = TRUE;
215                     firsttag = current;
216                     linklen = 0;
217                     lpID = NULL;
218                     lpUrl = NULL;
219                 }
220                 else if(*(current + 2) == infoPtr->BreakChar)
221                 {
222                     /* we expect parameters, parse them */
223                     LPCWSTR *CurrentParameter = NULL, tmp;
224                     UINT *CurrentParameterLen = NULL;
225
226                     taglen = 3;
227                     tmp = current + taglen;
228                     lpID = NULL;
229                     lpUrl = NULL;
230                     
231 CheckParameter:
232                     /* compare the current position with all known parameters */
233                     if(!StrCmpNIW(tmp, SL_HREF, 6))
234                     {
235                         taglen += 6;
236                         ValidParam = TRUE;
237                         CurrentParameter = &lpUrl;
238                         CurrentParameterLen = &lenUrl;
239                     }
240                     else if(!StrCmpNIW(tmp, SL_ID, 4))
241                     {
242                         taglen += 4;
243                         ValidParam = TRUE;
244                         CurrentParameter = &lpID;
245                         CurrentParameterLen = &lenId;
246                     }
247                     else
248                     {
249                         ValidParam = FALSE;
250                     }
251                     
252                     if(ValidParam)
253                     {
254                         /* we got a known parameter, now search until the next " character.
255                            If we can't find a " character, there's a syntax error and we just assume it's text */
256                         ValidParam = FALSE;
257                         *CurrentParameter = current + taglen;
258                         *CurrentParameterLen = 0;
259
260                         for(tmp = *CurrentParameter; *tmp != 0; tmp++)
261                         {
262                             taglen++;
263                             if(*tmp == '\"')
264                             {
265                                 ValidParam = TRUE;
266                                 tmp++;
267                                 break;
268                             }
269                             (*CurrentParameterLen)++;
270                         }
271                     }
272                     if(ValidParam)
273                     {
274                         /* we're done with this parameter, now there are only 2 possibilities:
275                          * 1. another parameter is coming, so expect a ' ' (space) character
276                          * 2. the tag is being closed, so expect a '<' character
277                          */
278                         if(*tmp == infoPtr->BreakChar)
279                         {
280                             /* we expect another parameter, do the whole thing again */
281                             taglen++;
282                             tmp++;
283                             goto CheckParameter;
284                         }
285                         else if(*tmp == '>')
286                         {
287                             /* the tag is being closed, we're done */
288                             ValidLink = TRUE;
289                             taglen++;
290                         }
291                     }
292                 }
293                 
294                 if(ValidLink && ValidParam)
295                 {
296                     /* the <a ...> tag appears to be valid. save all information
297                        so we can add the link if we find a valid </a> tag later */
298                     CurrentType = slLink;
299                     linktext = current + taglen;
300                     linklen = 0;
301                     firsttag = current;
302                 }
303                 else
304                 {
305                     taglen = 1;
306                     lpID = NULL;
307                     lpUrl = NULL;
308                     if(textstart == NULL)
309                     {
310                         textstart = current;
311                     }
312                 }
313             }
314             else if(!StrCmpNIW(current, SL_LINKCLOSE, 4) && (CurrentType == slLink) && firsttag)
315             {
316                 /* there's a <a...> tag opened, first add the previous text, if present */
317                 if(textstart != NULL && textlen > 0 && firsttag > textstart)
318                 {
319                     Last = SYSLINK_AppendDocItem(infoPtr, textstart, firsttag - textstart, slText, Last);
320                     if(Last == NULL)
321                     {
322                         ERR("Unable to create new document item!\n");
323                         return docitems;
324                     }
325                     docitems++;
326                     textstart = NULL;
327                     textlen = 0;
328                 }
329                 
330                 /* now it's time to add the link to the document */
331                 current += 4;
332                 if(linktext != NULL && linklen > 0)
333                 {
334                     Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slLink, Last);
335                     if(Last == NULL)
336                     {
337                         ERR("Unable to create new document item!\n");
338                         return docitems;
339                     }
340                     docitems++;
341                     if(CurrentType == slLink)
342                     {
343                         int nc;
344
345                         if(!(infoPtr->Style & WS_DISABLED))
346                         {
347                             Last->u.Link.state |= LIS_ENABLED;
348                         }
349                         /* Copy the tag parameters */
350                         if(lpID != NULL)
351                         {
352                             nc = min(lenId, strlenW(lpID));
353                             nc = min(nc, MAX_LINKID_TEXT - 1);
354                             Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
355                             if(Last->u.Link.szID != NULL)
356                             {
357                                 lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
358                             }
359                         }
360                         else
361                             Last->u.Link.szID = NULL;
362                         if(lpUrl != NULL)
363                         {
364                             nc = min(lenUrl, strlenW(lpUrl));
365                             nc = min(nc, L_MAX_URL_LENGTH - 1);
366                             Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
367                             if(Last->u.Link.szUrl != NULL)
368                             {
369                                 lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
370                             }
371                         }
372                         else
373                             Last->u.Link.szUrl = NULL;
374                     }
375                     linktext = NULL;
376                 }
377                 CurrentType = slText;
378                 firsttag = NULL;
379                 textstart = NULL;
380                 continue;
381             }
382             else
383             {
384                 /* we don't know what tag it is, so just continue */
385                 taglen = 1;
386                 linklen++;
387                 if(CurrentType == slText && textstart == NULL)
388                 {
389                     textstart = current;
390                 }
391             }
392             
393             textlen += taglen;
394             current += taglen;
395         }
396         else
397         {
398             textlen++;
399             linklen++;
400
401             /* save the pointer of the current text item if we couldn't find a tag */
402             if(textstart == NULL && CurrentType == slText)
403             {
404                 textstart = current;
405             }
406             
407             current++;
408         }
409     }
410     
411     if(textstart != NULL && textlen > 0)
412     {
413         Last = SYSLINK_AppendDocItem(infoPtr, textstart, textlen, CurrentType, Last);
414         if(Last == NULL)
415         {
416             ERR("Unable to create new document item!\n");
417             return docitems;
418         }
419         if(CurrentType == slLink)
420         {
421             int nc;
422
423             if(!(infoPtr->Style & WS_DISABLED))
424             {
425                 Last->u.Link.state |= LIS_ENABLED;
426             }
427             /* Copy the tag parameters */
428             if(lpID != NULL)
429             {
430                 nc = min(lenId, strlenW(lpID));
431                 nc = min(nc, MAX_LINKID_TEXT - 1);
432                 Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
433                 if(Last->u.Link.szID != NULL)
434                 {
435                     lstrcpynW(Last->u.Link.szID, lpID, nc + 1);
436                 }
437             }
438             else
439                 Last->u.Link.szID = NULL;
440             if(lpUrl != NULL)
441             {
442                 nc = min(lenUrl, strlenW(lpUrl));
443                 nc = min(nc, L_MAX_URL_LENGTH - 1);
444                 Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
445                 if(Last->u.Link.szUrl != NULL)
446                 {
447                     lstrcpynW(Last->u.Link.szUrl, lpUrl, nc + 1);
448                 }
449             }
450             else
451                 Last->u.Link.szUrl = NULL;
452         }
453         docitems++;
454     }
455
456     if(linktext != NULL && linklen > 0)
457     {
458         /* we got an unclosed link, just display the text */
459         Last = SYSLINK_AppendDocItem(infoPtr, linktext, linklen, slText, Last);
460         if(Last == NULL)
461         {
462             ERR("Unable to create new document item!\n");
463             return docitems;
464         }
465         docitems++;
466     }
467
468     return docitems;
469 }
470
471 /***********************************************************************
472  * SYSLINK_RepaintLink
473  * Repaints a link.
474  */
475 static VOID SYSLINK_RepaintLink (const SYSLINK_INFO *infoPtr, const DOC_ITEM *DocItem)
476 {
477     PDOC_TEXTBLOCK bl;
478     int n;
479
480     if(DocItem->Type != slLink)
481     {
482         ERR("DocItem not a link!\n");
483         return;
484     }
485     
486     bl = DocItem->Blocks;
487     if (bl != NULL)
488     {
489         n = DocItem->nText;
490         
491         while(n > 0)
492         {
493             InvalidateRect(infoPtr->Self, &bl->rc, TRUE);
494             n -= bl->nChars + bl->nSkip;
495             bl++;
496         }
497     }
498 }
499
500 /***********************************************************************
501  * SYSLINK_GetLinkItemByIndex
502  * Retrieves a document link by its index
503  */
504 static PDOC_ITEM SYSLINK_GetLinkItemByIndex (const SYSLINK_INFO *infoPtr, int iLink)
505 {
506     PDOC_ITEM Current = infoPtr->Items;
507
508     while(Current != NULL)
509     {
510         if((Current->Type == slLink) && (iLink-- <= 0))
511         {
512             return Current;
513         }
514         Current = Current->Next;
515     }
516     return NULL;
517 }
518
519 /***********************************************************************
520  * SYSLINK_GetFocusLink
521  * Retrieves the link that has the LIS_FOCUSED bit
522  */
523 static PDOC_ITEM SYSLINK_GetFocusLink (const SYSLINK_INFO *infoPtr, int *LinkId)
524 {
525     PDOC_ITEM Current = infoPtr->Items;
526     int id = 0;
527
528     while(Current != NULL)
529     {
530         if((Current->Type == slLink))
531         {
532             if(Current->u.Link.state & LIS_FOCUSED)
533             {
534                 if(LinkId != NULL)
535                     *LinkId = id;
536                 return Current;
537             }
538             id++;
539         }
540         Current = Current->Next;
541     }
542     return NULL;
543 }
544
545 /***********************************************************************
546  * SYSLINK_GetNextLink
547  * Gets the next link
548  */
549 static PDOC_ITEM SYSLINK_GetNextLink (const SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
550 {
551     for(Current = (Current != NULL ? Current->Next : infoPtr->Items);
552         Current != NULL;
553         Current = Current->Next)
554     {
555         if(Current->Type == slLink)
556         {
557             return Current;
558         }
559     }
560     return NULL;
561 }
562
563 /***********************************************************************
564  * SYSLINK_GetPrevLink
565  * Gets the previous link
566  */
567 static PDOC_ITEM SYSLINK_GetPrevLink (const SYSLINK_INFO *infoPtr, PDOC_ITEM Current)
568 {
569     if(Current == NULL)
570     {
571         /* returns the last link */
572         PDOC_ITEM Last = NULL;
573         
574         for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
575         {
576             if(Current->Type == slLink)
577             {
578                 Last = Current;
579             }
580         }
581         return Last;
582     }
583     else
584     {
585         /* returns the previous link */
586         PDOC_ITEM Cur, Prev = NULL;
587         
588         for(Cur = infoPtr->Items; Cur != NULL; Cur = Cur->Next)
589         {
590             if(Cur == Current)
591             {
592                 break;
593             }
594             if(Cur->Type == slLink)
595             {
596                 Prev = Cur;
597             }
598         }
599         return Prev;
600     }
601 }
602
603 /***********************************************************************
604  * SYSLINK_WrapLine
605  * Tries to wrap a line.
606  */
607 static BOOL SYSLINK_WrapLine (LPWSTR Text, WCHAR BreakChar, int *LineLen,
608                              int nFit, LPSIZE Extent)
609 {
610     WCHAR *Current;
611
612     if(nFit == *LineLen)
613     {
614         return FALSE;
615     }
616
617     *LineLen = nFit;
618
619     Current = Text + nFit;
620     
621     /* check if we're in the middle of a word */
622     if((*Current) != BreakChar)
623     {
624         /* search for the beginning of the word */
625         while(Current > Text && (*(Current - 1)) != BreakChar)
626         {
627             Current--;
628             (*LineLen)--;
629         }
630         
631         if((*LineLen) == 0)
632         {
633             Extent->cx = 0;
634             Extent->cy = 0;
635         }
636         return TRUE;
637     }
638
639     return TRUE;
640 }
641
642 /***********************************************************************
643  * SYSLINK_Render
644  * Renders the document in memory
645  */
646 static VOID SYSLINK_Render (const SYSLINK_INFO *infoPtr, HDC hdc, PRECT pRect)
647 {
648     RECT rc;
649     PDOC_ITEM Current;
650     HGDIOBJ hOldFont;
651     int x, y, LineHeight;
652     SIZE szDoc;
653
654     szDoc.cx = szDoc.cy = 0;
655
656     rc = *pRect;
657     rc.right -= SL_RIGHTMARGIN;
658     rc.bottom -= SL_BOTTOMMARGIN;
659
660     if(rc.right - SL_LEFTMARGIN < 0)
661         rc.right = MAXLONG;
662     if (rc.bottom - SL_TOPMARGIN < 0)
663         rc.bottom = MAXLONG;
664     
665     hOldFont = SelectObject(hdc, infoPtr->Font);
666     
667     x = SL_LEFTMARGIN;
668     y = SL_TOPMARGIN;
669     LineHeight = 0;
670     
671     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
672     {
673         int n, nBlocks;
674         LPWSTR tx;
675         PDOC_TEXTBLOCK bl, cbl;
676         INT nFit;
677         SIZE szDim;
678
679         if(Current->nText == 0)
680         {
681             continue;
682         }
683
684         tx = Current->Text;
685         n = Current->nText;
686
687         Free(Current->Blocks);
688         Current->Blocks = NULL;
689         bl = NULL;
690         nBlocks = 0;
691
692         if(Current->Type == slText)
693         {
694             SelectObject(hdc, infoPtr->Font);
695         }
696         else if(Current->Type == slLink)
697         {
698             SelectObject(hdc, infoPtr->LinkFont);
699         }
700         
701         while(n > 0)
702         {
703             int SkipChars = 0;
704
705             /* skip break characters unless they're the first of the doc item */
706             if(tx != Current->Text || x == SL_LEFTMARGIN)
707             {
708                 while(n > 0 && (*tx) == infoPtr->BreakChar)
709                 {
710                     tx++;
711                     SkipChars++;
712                     n--;
713                 }
714             }
715
716             if((n == 0 && SkipChars != 0) ||
717                GetTextExtentExPointW(hdc, tx, n, rc.right - x, &nFit, NULL, &szDim))
718             {
719                 int LineLen = n;
720                 BOOL Wrap = FALSE;
721                 PDOC_TEXTBLOCK nbl;
722                 
723                 if(n != 0)
724                 {
725                     Wrap = SYSLINK_WrapLine(tx, infoPtr->BreakChar, &LineLen, nFit, &szDim);
726
727                     if(LineLen == 0)
728                     {
729                         if(x > SL_LEFTMARGIN)
730                         {
731                             /* move one line down, the word didn't fit into the line */
732                             x = SL_LEFTMARGIN;
733                             y += LineHeight;
734                             LineHeight = 0;
735                             continue;
736                         }
737                         else
738                         {
739                             /* the word starts at the beginning of the line and doesn't
740                                fit into the line, so break it at the last character that fits */
741                             LineLen = max(nFit, 1);
742                         }
743                     }
744
745                     if(LineLen != n)
746                     {
747                         if(!GetTextExtentExPointW(hdc, tx, LineLen, rc.right - x, NULL, NULL, &szDim))
748                         {
749                             if(bl != NULL)
750                             {
751                                 Free(bl);
752                                 bl = NULL;
753                                 nBlocks = 0;
754                             }
755                             break;
756                         }
757                     }
758                 }
759                 
760                 nbl = ReAlloc(bl, (nBlocks + 1) * sizeof(DOC_TEXTBLOCK));
761                 if (nbl != NULL)
762                 {
763                     bl = nbl;
764                     nBlocks++;
765
766                     cbl = bl + nBlocks - 1;
767                     
768                     cbl->nChars = LineLen;
769                     cbl->nSkip = SkipChars;
770                     cbl->rc.left = x;
771                     cbl->rc.top = y;
772                     cbl->rc.right = x + szDim.cx;
773                     cbl->rc.bottom = y + szDim.cy;
774
775                     if (cbl->rc.right > szDoc.cx)
776                         szDoc.cx = cbl->rc.right;
777                     if (cbl->rc.bottom > szDoc.cy)
778                         szDoc.cy = cbl->rc.bottom;
779
780                     if(LineLen != 0)
781                     {
782                         x += szDim.cx;
783                         LineHeight = max(LineHeight, szDim.cy);
784
785                         if(Wrap)
786                         {
787                             x = SL_LEFTMARGIN;
788                             y += LineHeight;
789                             LineHeight = 0;
790                         }
791                     }
792                 }
793                 else
794                 {
795                     Free(bl);
796                     bl = NULL;
797                     nBlocks = 0;
798
799                     ERR("Failed to alloc DOC_TEXTBLOCK structure!\n");
800                     break;
801                 }
802                 n -= LineLen;
803                 tx += LineLen;
804             }
805             else
806             {
807                 n--;
808             }
809         }
810
811         if(nBlocks != 0)
812         {
813             Current->Blocks = bl;
814         }
815     }
816     
817     SelectObject(hdc, hOldFont);
818
819     pRect->right = pRect->left + szDoc.cx;
820     pRect->bottom = pRect->top + szDoc.cy;
821 }
822
823 /***********************************************************************
824  * SYSLINK_Draw
825  * Draws the SysLink control.
826  */
827 static LRESULT SYSLINK_Draw (const SYSLINK_INFO *infoPtr, HDC hdc)
828 {
829     RECT rc;
830     PDOC_ITEM Current;
831     HFONT hOldFont;
832     COLORREF OldTextColor, OldBkColor;
833
834     hOldFont = SelectObject(hdc, infoPtr->Font);
835     OldTextColor = SetTextColor(hdc, infoPtr->TextColor);
836     OldBkColor = SetBkColor(hdc, infoPtr->BackColor);
837     
838     GetClientRect(infoPtr->Self, &rc);
839     rc.right -= SL_RIGHTMARGIN + SL_LEFTMARGIN;
840     rc.bottom -= SL_BOTTOMMARGIN + SL_TOPMARGIN;
841
842     if(rc.right < 0 || rc.bottom < 0) return 0;
843
844     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
845     {
846         int n;
847         LPWSTR tx;
848         PDOC_TEXTBLOCK bl;
849         
850         bl = Current->Blocks;
851         if(bl != NULL)
852         {
853             tx = Current->Text;
854             n = Current->nText;
855
856             if(Current->Type == slText)
857             {
858                  SelectObject(hdc, infoPtr->Font);
859                  SetTextColor(hdc, infoPtr->TextColor);
860             }
861             else
862             {
863                  SelectObject(hdc, infoPtr->LinkFont);
864                  SetTextColor(hdc, (!(Current->u.Link.state & LIS_VISITED) ? infoPtr->LinkColor : infoPtr->VisitedColor));
865             }
866
867             while(n > 0)
868             {
869                 tx += bl->nSkip;
870                 ExtTextOutW(hdc, bl->rc.left, bl->rc.top, ETO_OPAQUE | ETO_CLIPPED, &bl->rc, tx, bl->nChars, NULL);
871                 if((Current->Type == slLink) && (Current->u.Link.state & LIS_FOCUSED) && infoPtr->HasFocus)
872                 {
873                     COLORREF PrevTextColor;
874                     PrevTextColor = SetTextColor(hdc, infoPtr->TextColor);
875                     DrawFocusRect(hdc, &bl->rc);
876                     SetTextColor(hdc, PrevTextColor);
877                 }
878                 tx += bl->nChars;
879                 n -= bl->nChars + bl->nSkip;
880                 bl++;
881             }
882         }
883     }
884
885     SetBkColor(hdc, OldBkColor);
886     SetTextColor(hdc, OldTextColor);
887     SelectObject(hdc, hOldFont);
888     
889     return 0;
890 }
891
892
893 /***********************************************************************
894  * SYSLINK_Paint
895  * Handles the WM_PAINT message.
896  */
897 static LRESULT SYSLINK_Paint (const SYSLINK_INFO *infoPtr, HDC hdcParam)
898 {
899     HDC hdc;
900     PAINTSTRUCT ps;
901
902     hdc = hdcParam ? hdcParam : BeginPaint (infoPtr->Self, &ps);
903     if (hdc)
904     {
905         SYSLINK_Draw (infoPtr, hdc);
906         if (!hdcParam) EndPaint (infoPtr->Self, &ps);
907     }
908     return 0;
909 }
910
911 /***********************************************************************
912  * SYSLINK_EraseBkgnd
913  * Handles the WM_ERASEBKGND message.
914  */
915 static LRESULT SYSLINK_EraseBkgnd (const SYSLINK_INFO *infoPtr, HDC hdc)
916 {
917    HBRUSH hbr;
918    RECT r;
919
920    GetClientRect(infoPtr->Self, &r);
921    hbr = CreateSolidBrush(infoPtr->BackColor);
922    FillRect(hdc, &r, hbr);
923    DeleteObject(hbr);
924
925    return 1;
926 }
927
928 /***********************************************************************
929  *           SYSLINK_SetFont
930  * Set new Font for the SysLink control.
931  */
932 static HFONT SYSLINK_SetFont (SYSLINK_INFO *infoPtr, HFONT hFont, BOOL bRedraw)
933 {
934     HDC hdc;
935     LOGFONTW lf;
936     TEXTMETRICW tm;
937     RECT rcClient;
938     HFONT hOldFont = infoPtr->Font;
939     infoPtr->Font = hFont;
940     
941     /* free the underline font */
942     if(infoPtr->LinkFont != NULL)
943     {
944         DeleteObject(infoPtr->LinkFont);
945         infoPtr->LinkFont = NULL;
946     }
947
948     /* Render text position and word wrapping in memory */
949     if (GetClientRect(infoPtr->Self, &rcClient))
950     {
951         hdc = GetDC(infoPtr->Self);
952         if(hdc != NULL)
953         {
954             /* create a new underline font */
955             if(GetTextMetricsW(hdc, &tm) &&
956                GetObjectW(infoPtr->Font, sizeof(LOGFONTW), &lf))
957             {
958                 lf.lfUnderline = TRUE;
959                 infoPtr->LinkFont = CreateFontIndirectW(&lf);
960                 infoPtr->BreakChar = tm.tmBreakChar;
961             }
962             else
963             {
964                 ERR("Failed to create link font!\n");
965             }
966
967             SYSLINK_Render(infoPtr, hdc, &rcClient);
968             ReleaseDC(infoPtr->Self, hdc);
969         }
970     }
971     
972     if(bRedraw)
973     {
974         RedrawWindow(infoPtr->Self, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
975     }
976     
977     return hOldFont;
978 }
979
980 /***********************************************************************
981  *           SYSLINK_SetText
982  * Set new text for the SysLink control.
983  */
984 static LRESULT SYSLINK_SetText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
985 {
986     /* clear the document */
987     SYSLINK_ClearDoc(infoPtr);
988
989     if(Text == NULL || *Text == 0)
990     {
991         return TRUE;
992     }
993
994     /* let's parse the string and create a document */
995     if(SYSLINK_ParseText(infoPtr, Text) > 0)
996     {
997         RECT rcClient;
998
999         /* Render text position and word wrapping in memory */
1000         if (GetClientRect(infoPtr->Self, &rcClient))
1001         {
1002             HDC hdc = GetDC(infoPtr->Self);
1003             if (hdc != NULL)
1004             {
1005                 SYSLINK_Render(infoPtr, hdc, &rcClient);
1006                 ReleaseDC(infoPtr->Self, hdc);
1007
1008                 InvalidateRect(infoPtr->Self, NULL, TRUE);
1009             }
1010         }
1011     }
1012     
1013     return TRUE;
1014 }
1015
1016 /***********************************************************************
1017  *           SYSLINK_SetFocusLink
1018  * Updates the focus status bits and focusses the specified link.
1019  * If no document item is specified, the focus bit will be removed from all links.
1020  * Returns the previous focused item.
1021  */
1022 static PDOC_ITEM SYSLINK_SetFocusLink (const SYSLINK_INFO *infoPtr, const DOC_ITEM *DocItem)
1023 {
1024     PDOC_ITEM Current, PrevFocus = NULL;
1025     
1026     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
1027     {
1028         if(Current->Type == slLink)
1029         {
1030             if((PrevFocus == NULL) && (Current->u.Link.state & LIS_FOCUSED))
1031             {
1032                 PrevFocus = Current;
1033             }
1034             
1035             if(Current == DocItem)
1036             {
1037                 Current->u.Link.state |= LIS_FOCUSED;
1038             }
1039             else
1040             {
1041                 Current->u.Link.state &= ~LIS_FOCUSED;
1042             }
1043         }
1044     }
1045     
1046     return PrevFocus;
1047 }
1048
1049 /***********************************************************************
1050  *           SYSLINK_SetItem
1051  * Sets the states and attributes of a link item.
1052  */
1053 static LRESULT SYSLINK_SetItem (const SYSLINK_INFO *infoPtr, const LITEM *Item)
1054 {
1055     PDOC_ITEM di;
1056     int nc;
1057     PWSTR szId = NULL;
1058     PWSTR szUrl = NULL;
1059     BOOL Repaint = FALSE;
1060
1061     if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
1062     {
1063         ERR("Invalid Flags!\n");
1064         return FALSE;
1065     }
1066
1067     di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
1068     if(di == NULL)
1069     {
1070         ERR("Link %d couldn't be found\n", Item->iLink);
1071         return FALSE;
1072     }
1073
1074     if(Item->mask & LIF_ITEMID)
1075     {
1076         nc = min(lstrlenW(Item->szID), MAX_LINKID_TEXT - 1);
1077         szId = Alloc((nc + 1) * sizeof(WCHAR));
1078         if(szId)
1079         {
1080             lstrcpynW(szId, Item->szID, nc + 1);
1081         }
1082         else
1083         {
1084             ERR("Unable to allocate memory for link id\n");
1085             return FALSE;
1086         }
1087     }
1088
1089     if(Item->mask & LIF_URL)
1090     {
1091         nc = min(lstrlenW(Item->szUrl), L_MAX_URL_LENGTH - 1);
1092         szUrl = Alloc((nc + 1) * sizeof(WCHAR));
1093         if(szUrl)
1094         {
1095             lstrcpynW(szUrl, Item->szUrl, nc + 1);
1096         }
1097         else
1098         {
1099             Free(szId);
1100
1101             ERR("Unable to allocate memory for link url\n");
1102             return FALSE;
1103         }
1104     }
1105
1106     if(Item->mask & LIF_ITEMID)
1107     {
1108         Free(di->u.Link.szID);
1109         di->u.Link.szID = szId;
1110     }
1111
1112     if(Item->mask & LIF_URL)
1113     {
1114         Free(di->u.Link.szUrl);
1115         di->u.Link.szUrl = szUrl;
1116     }
1117
1118     if(Item->mask & LIF_STATE)
1119     {
1120         UINT oldstate = di->u.Link.state;
1121         /* clear the masked bits */
1122         di->u.Link.state &= ~(Item->stateMask & LIS_MASK);
1123         /* copy the bits */
1124         di->u.Link.state |= (Item->state & Item->stateMask) & LIS_MASK;
1125         Repaint = (oldstate != di->u.Link.state);
1126         
1127         /* update the focus */
1128         SYSLINK_SetFocusLink(infoPtr, ((di->u.Link.state & LIS_FOCUSED) ? di : NULL));
1129     }
1130     
1131     if(Repaint)
1132     {
1133         SYSLINK_RepaintLink(infoPtr, di);
1134     }
1135     
1136     return TRUE;
1137 }
1138
1139 /***********************************************************************
1140  *           SYSLINK_GetItem
1141  * Retrieves the states and attributes of a link item.
1142  */
1143 static LRESULT SYSLINK_GetItem (const SYSLINK_INFO *infoPtr, PLITEM Item)
1144 {
1145     PDOC_ITEM di;
1146     
1147     if(!(Item->mask & LIF_ITEMINDEX) || !(Item->mask & (LIF_FLAGSMASK)))
1148     {
1149         ERR("Invalid Flags!\n");
1150         return FALSE;
1151     }
1152     
1153     di = SYSLINK_GetLinkItemByIndex(infoPtr, Item->iLink);
1154     if(di == NULL)
1155     {
1156         ERR("Link %d couldn't be found\n", Item->iLink);
1157         return FALSE;
1158     }
1159     
1160     if(Item->mask & LIF_STATE)
1161     {
1162         Item->state = (di->u.Link.state & Item->stateMask);
1163         if(!infoPtr->HasFocus)
1164         {
1165             /* remove the LIS_FOCUSED bit if the control doesn't have focus */
1166             Item->state &= ~LIS_FOCUSED;
1167         }
1168     }
1169     
1170     if(Item->mask & LIF_ITEMID)
1171     {
1172         if(di->u.Link.szID)
1173         {
1174             lstrcpyW(Item->szID, di->u.Link.szID);
1175         }
1176         else
1177         {
1178             Item->szID[0] = 0;
1179         }
1180     }
1181     
1182     if(Item->mask & LIF_URL)
1183     {
1184         if(di->u.Link.szUrl)
1185         {
1186             lstrcpyW(Item->szUrl, di->u.Link.szUrl);
1187         }
1188         else
1189         {
1190             Item->szUrl[0] = 0;
1191         }
1192     }
1193     
1194     return TRUE;
1195 }
1196
1197 /***********************************************************************
1198  *           SYSLINK_PtInDocItem
1199  * Determines if a point is in the region of a document item
1200  */
1201 static BOOL SYSLINK_PtInDocItem (const DOC_ITEM *DocItem, POINT pt)
1202 {
1203     PDOC_TEXTBLOCK bl;
1204     int n;
1205
1206     bl = DocItem->Blocks;
1207     if (bl != NULL)
1208     {
1209         n = DocItem->nText;
1210
1211         while(n > 0)
1212         {
1213             if (PtInRect(&bl->rc, pt))
1214             {
1215                 return TRUE;
1216             }
1217             n -= bl->nChars + bl->nSkip;
1218             bl++;
1219         }
1220     }
1221     
1222     return FALSE;
1223 }
1224
1225 /***********************************************************************
1226  *           SYSLINK_HitTest
1227  * Determines the link the user clicked on.
1228  */
1229 static LRESULT SYSLINK_HitTest (const SYSLINK_INFO *infoPtr, PLHITTESTINFO HitTest)
1230 {
1231     PDOC_ITEM Current;
1232     int id = 0;
1233
1234     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
1235     {
1236         if(Current->Type == slLink)
1237         {
1238             if(SYSLINK_PtInDocItem(Current, HitTest->pt))
1239             {
1240                 HitTest->item.mask = 0;
1241                 HitTest->item.iLink = id;
1242                 HitTest->item.state = 0;
1243                 HitTest->item.stateMask = 0;
1244                 if(Current->u.Link.szID)
1245                 {
1246                     lstrcpyW(HitTest->item.szID, Current->u.Link.szID);
1247                 }
1248                 else
1249                 {
1250                     HitTest->item.szID[0] = 0;
1251                 }
1252                 if(Current->u.Link.szUrl)
1253                 {
1254                     lstrcpyW(HitTest->item.szUrl, Current->u.Link.szUrl);
1255                 }
1256                 else
1257                 {
1258                     HitTest->item.szUrl[0] = 0;
1259                 }
1260                 return TRUE;
1261             }
1262             id++;
1263         }
1264     }
1265     
1266     return FALSE;
1267 }
1268
1269 /***********************************************************************
1270  *           SYSLINK_GetIdealHeight
1271  * Returns the preferred height of a link at the current control's width.
1272  */
1273 static LRESULT SYSLINK_GetIdealHeight (const SYSLINK_INFO *infoPtr)
1274 {
1275     HDC hdc = GetDC(infoPtr->Self);
1276     if(hdc != NULL)
1277     {
1278         LRESULT height;
1279         TEXTMETRICW tm;
1280         HGDIOBJ hOldFont = SelectObject(hdc, infoPtr->Font);
1281         
1282         if(GetTextMetricsW(hdc, &tm))
1283         {
1284             height = tm.tmHeight;
1285         }
1286         else
1287         {
1288             height = 0;
1289         }
1290         SelectObject(hdc, hOldFont);
1291         ReleaseDC(infoPtr->Self, hdc);
1292         
1293         return height;
1294     }
1295     return 0;
1296 }
1297
1298 /***********************************************************************
1299  *           SYSLINK_SendParentNotify
1300  * Sends a WM_NOTIFY message to the parent window.
1301  */
1302 static LRESULT SYSLINK_SendParentNotify (const SYSLINK_INFO *infoPtr, UINT code, const DOC_ITEM *Link, int iLink)
1303 {
1304     NMLINK nml;
1305
1306     nml.hdr.hwndFrom = infoPtr->Self;
1307     nml.hdr.idFrom = GetWindowLongPtrW(infoPtr->Self, GWLP_ID);
1308     nml.hdr.code = code;
1309
1310     nml.item.mask = 0;
1311     nml.item.iLink = iLink;
1312     nml.item.state = 0;
1313     nml.item.stateMask = 0;
1314     if(Link->u.Link.szID)
1315     {
1316         lstrcpyW(nml.item.szID, Link->u.Link.szID);
1317     }
1318     else
1319     {
1320         nml.item.szID[0] = 0;
1321     }
1322     if(Link->u.Link.szUrl)
1323     {
1324         lstrcpyW(nml.item.szUrl, Link->u.Link.szUrl);
1325     }
1326     else
1327     {
1328         nml.item.szUrl[0] = 0;
1329     }
1330
1331     return SendMessageW(infoPtr->Notify, WM_NOTIFY, nml.hdr.idFrom, (LPARAM)&nml);
1332 }
1333
1334 /***********************************************************************
1335  *           SYSLINK_SetFocus
1336  * Handles receiving the input focus.
1337  */
1338 static LRESULT SYSLINK_SetFocus (SYSLINK_INFO *infoPtr)
1339 {
1340     PDOC_ITEM Focus;
1341     
1342     infoPtr->HasFocus = TRUE;
1343
1344     /* We always select the first link, even if we activated the control using
1345        SHIFT+TAB. This is the default behavior */
1346     Focus = SYSLINK_GetNextLink(infoPtr, NULL);
1347     if(Focus != NULL)
1348     {
1349         SYSLINK_SetFocusLink(infoPtr, Focus);
1350         SYSLINK_RepaintLink(infoPtr, Focus);
1351     }
1352     return 0;
1353 }
1354
1355 /***********************************************************************
1356  *           SYSLINK_KillFocus
1357  * Handles losing the input focus.
1358  */
1359 static LRESULT SYSLINK_KillFocus (SYSLINK_INFO *infoPtr)
1360 {
1361     PDOC_ITEM Focus;
1362     
1363     infoPtr->HasFocus = FALSE;
1364     Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1365     
1366     if(Focus != NULL)
1367     {
1368         SYSLINK_RepaintLink(infoPtr, Focus);
1369     }
1370
1371     return 0;
1372 }
1373
1374 /***********************************************************************
1375  *           SYSLINK_LinkAtPt
1376  * Returns a link at the specified position
1377  */
1378 static PDOC_ITEM SYSLINK_LinkAtPt (const SYSLINK_INFO *infoPtr, const POINT *pt, int *LinkId, BOOL MustBeEnabled)
1379 {
1380     PDOC_ITEM Current;
1381     int id = 0;
1382
1383     for(Current = infoPtr->Items; Current != NULL; Current = Current->Next)
1384     {
1385         if((Current->Type == slLink) && SYSLINK_PtInDocItem(Current, *pt) &&
1386            (!MustBeEnabled || (MustBeEnabled && (Current->u.Link.state & LIS_ENABLED))))
1387         {
1388             if(LinkId != NULL)
1389             {
1390                 *LinkId = id;
1391             }
1392             return Current;
1393         }
1394         id++;
1395     }
1396
1397     return NULL;
1398 }
1399
1400 /***********************************************************************
1401  *           SYSLINK_LButtonDown
1402  * Handles mouse clicks
1403  */
1404 static LRESULT SYSLINK_LButtonDown (SYSLINK_INFO *infoPtr, const POINT *pt)
1405 {
1406     PDOC_ITEM Current, Old;
1407     int id;
1408
1409     Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
1410     if(Current != NULL)
1411     {
1412       SetFocus(infoPtr->Self);
1413
1414       Old = SYSLINK_SetFocusLink(infoPtr, Current);
1415       if(Old != NULL && Old != Current)
1416       {
1417           SYSLINK_RepaintLink(infoPtr, Old);
1418       }
1419       infoPtr->MouseDownID = id;
1420       SYSLINK_RepaintLink(infoPtr, Current);
1421     }
1422
1423     return 0;
1424 }
1425
1426 /***********************************************************************
1427  *           SYSLINK_LButtonUp
1428  * Handles mouse clicks
1429  */
1430 static LRESULT SYSLINK_LButtonUp (SYSLINK_INFO *infoPtr, const POINT *pt)
1431 {
1432     if(infoPtr->MouseDownID > -1)
1433     {
1434         PDOC_ITEM Current;
1435         int id;
1436         
1437         Current = SYSLINK_LinkAtPt(infoPtr, pt, &id, TRUE);
1438         if((Current != NULL) && (Current->u.Link.state & LIS_FOCUSED) && (infoPtr->MouseDownID == id))
1439         {
1440             SYSLINK_SendParentNotify(infoPtr, NM_CLICK, Current, id);
1441         }
1442     }
1443
1444     infoPtr->MouseDownID = -1;
1445
1446     return 0;
1447 }
1448
1449 /***********************************************************************
1450  *           SYSLINK_OnEnter
1451  * Handles ENTER key events
1452  */
1453 static BOOL SYSLINK_OnEnter (const SYSLINK_INFO *infoPtr)
1454 {
1455     if(infoPtr->HasFocus && !infoPtr->IgnoreReturn)
1456     {
1457         PDOC_ITEM Focus;
1458         int id;
1459         
1460         Focus = SYSLINK_GetFocusLink(infoPtr, &id);
1461         if(Focus)
1462         {
1463             SYSLINK_SendParentNotify(infoPtr, NM_RETURN, Focus, id);
1464             return TRUE;
1465         }
1466     }
1467     return FALSE;
1468 }
1469
1470 /***********************************************************************
1471  *           SYSKEY_SelectNextPrevLink
1472  * Changes the currently focused link
1473  */
1474 static BOOL SYSKEY_SelectNextPrevLink (const SYSLINK_INFO *infoPtr, BOOL Prev)
1475 {
1476     if(infoPtr->HasFocus)
1477     {
1478         PDOC_ITEM Focus;
1479         int id;
1480
1481         Focus = SYSLINK_GetFocusLink(infoPtr, &id);
1482         if(Focus != NULL)
1483         {
1484             PDOC_ITEM NewFocus, OldFocus;
1485
1486             if(Prev)
1487                 NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
1488             else
1489                 NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
1490
1491             if(NewFocus != NULL)
1492             {
1493                 OldFocus = SYSLINK_SetFocusLink(infoPtr, NewFocus);
1494
1495                 if(OldFocus && OldFocus != NewFocus)
1496                 {
1497                     SYSLINK_RepaintLink(infoPtr, OldFocus);
1498                 }
1499                 SYSLINK_RepaintLink(infoPtr, NewFocus);
1500                 return TRUE;
1501             }
1502         }
1503     }
1504     return FALSE;
1505 }
1506
1507 /***********************************************************************
1508  *           SYSKEY_SelectNextPrevLink
1509  * Determines if there's a next or previous link to decide whether the control
1510  * should capture the tab key message
1511  */
1512 static BOOL SYSLINK_NoNextLink (const SYSLINK_INFO *infoPtr, BOOL Prev)
1513 {
1514     PDOC_ITEM Focus, NewFocus;
1515
1516     Focus = SYSLINK_GetFocusLink(infoPtr, NULL);
1517     if(Prev)
1518         NewFocus = SYSLINK_GetPrevLink(infoPtr, Focus);
1519     else
1520         NewFocus = SYSLINK_GetNextLink(infoPtr, Focus);
1521
1522     return NewFocus == NULL;
1523 }
1524
1525 /***********************************************************************
1526  *           SYSLINK_GetIdealSize
1527  * Calculates the ideal size of a link control at a given maximum width.
1528  */
1529 static VOID SYSLINK_GetIdealSize (const SYSLINK_INFO *infoPtr, int cxMaxWidth, LPSIZE lpSize)
1530 {
1531     RECT rc;
1532     HDC hdc;
1533
1534     rc.left = rc.top = rc.bottom = 0;
1535     rc.right = cxMaxWidth;
1536
1537     hdc = GetDC(infoPtr->Self);
1538     if (hdc != NULL)
1539     {
1540         HGDIOBJ hOldFont = SelectObject(hdc, infoPtr->Font);
1541
1542         SYSLINK_Render(infoPtr, hdc, &rc);
1543
1544         SelectObject(hdc, hOldFont);
1545         ReleaseDC(infoPtr->Self, hdc);
1546
1547         lpSize->cx = rc.right;
1548         lpSize->cy = rc.bottom;
1549     }
1550 }
1551
1552 /***********************************************************************
1553  *           SysLinkWindowProc
1554  */
1555 static LRESULT WINAPI SysLinkWindowProc(HWND hwnd, UINT message,
1556                                         WPARAM wParam, LPARAM lParam)
1557 {
1558     SYSLINK_INFO *infoPtr;
1559
1560     TRACE("hwnd=%p msg=%04x wparam=%lx lParam=%lx\n", hwnd, message, wParam, lParam);
1561
1562     infoPtr = (SYSLINK_INFO *)GetWindowLongPtrW(hwnd, 0);
1563
1564     if (!infoPtr && message != WM_CREATE)
1565         goto HandleDefaultMessage;
1566
1567     switch(message) {
1568     case WM_PRINTCLIENT:
1569     case WM_PAINT:
1570         return SYSLINK_Paint (infoPtr, (HDC)wParam);
1571
1572     case WM_ERASEBKGND:
1573         return SYSLINK_EraseBkgnd(infoPtr, (HDC)wParam);
1574
1575     case WM_SETCURSOR:
1576     {
1577         LHITTESTINFO ht;
1578         DWORD mp = GetMessagePos();
1579         
1580         ht.pt.x = (short)LOWORD(mp);
1581         ht.pt.y = (short)HIWORD(mp);
1582         
1583         ScreenToClient(infoPtr->Self, &ht.pt);
1584         if(SYSLINK_HitTest (infoPtr, &ht))
1585         {
1586             SetCursor(LoadCursorW(0, (LPCWSTR)IDC_HAND));
1587             return TRUE;
1588         }
1589         /* let the default window proc handle this message */
1590         goto HandleDefaultMessage;
1591     }
1592
1593     case WM_SIZE:
1594     {
1595         RECT rcClient;
1596         if (GetClientRect(infoPtr->Self, &rcClient))
1597         {
1598             HDC hdc = GetDC(infoPtr->Self);
1599             if(hdc != NULL)
1600             {
1601                 SYSLINK_Render(infoPtr, hdc, &rcClient);
1602                 ReleaseDC(infoPtr->Self, hdc);
1603             }
1604         }
1605         return 0;
1606     }
1607
1608     case WM_GETFONT:
1609         return (LRESULT)infoPtr->Font;
1610
1611     case WM_SETFONT:
1612         return (LRESULT)SYSLINK_SetFont(infoPtr, (HFONT)wParam, (BOOL)lParam);
1613
1614     case WM_SETTEXT:
1615         SYSLINK_SetText(infoPtr, (LPWSTR)lParam);
1616         goto HandleDefaultMessage;
1617
1618     case WM_LBUTTONDOWN:
1619     {
1620         POINT pt;
1621         pt.x = (short)LOWORD(lParam);
1622         pt.y = (short)HIWORD(lParam);
1623         return SYSLINK_LButtonDown(infoPtr, &pt);
1624     }
1625     case WM_LBUTTONUP:
1626     {
1627         POINT pt;
1628         pt.x = (short)LOWORD(lParam);
1629         pt.y = (short)HIWORD(lParam);
1630         return SYSLINK_LButtonUp(infoPtr, &pt);
1631     }
1632     
1633     case WM_KEYDOWN:
1634     {
1635         switch(wParam)
1636         {
1637         case VK_RETURN:
1638             SYSLINK_OnEnter(infoPtr);
1639             return 0;
1640         case VK_TAB:
1641         {
1642             BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
1643             SYSKEY_SelectNextPrevLink(infoPtr, shift);
1644             return 0;
1645         }
1646         }
1647         goto HandleDefaultMessage;
1648     }
1649     
1650     case WM_GETDLGCODE:
1651     {
1652         LRESULT Ret = DLGC_HASSETSEL;
1653         int vk = (lParam != 0 ? (int)((LPMSG)lParam)->wParam : 0);
1654         switch(vk)
1655         {
1656         case VK_RETURN:
1657             Ret |= DLGC_WANTMESSAGE;
1658             break;
1659         case VK_TAB:
1660         {
1661             BOOL shift = GetKeyState(VK_SHIFT) & 0x8000;
1662             if(!SYSLINK_NoNextLink(infoPtr, shift))
1663             {
1664                 Ret |= DLGC_WANTTAB;
1665             }
1666             else
1667             {
1668                 Ret |= DLGC_WANTCHARS;
1669             }
1670             break;
1671         }
1672         }
1673         return Ret;
1674     }
1675     
1676     case WM_NCHITTEST:
1677     {
1678         POINT pt;
1679         RECT rc;
1680         pt.x = (short)LOWORD(lParam);
1681         pt.y = (short)HIWORD(lParam);
1682         
1683         GetClientRect(infoPtr->Self, &rc);
1684         ScreenToClient(infoPtr->Self, &pt);
1685         if(pt.x < 0 || pt.y < 0 || pt.x > rc.right || pt.y > rc.bottom)
1686         {
1687             return HTNOWHERE;
1688         }
1689
1690         if(SYSLINK_LinkAtPt(infoPtr, &pt, NULL, FALSE))
1691         {
1692             return HTCLIENT;
1693         }
1694         
1695         return HTTRANSPARENT;
1696     }
1697
1698     case LM_HITTEST:
1699         return SYSLINK_HitTest(infoPtr, (PLHITTESTINFO)lParam);
1700
1701     case LM_SETITEM:
1702         return SYSLINK_SetItem(infoPtr, (PLITEM)lParam);
1703
1704     case LM_GETITEM:
1705         return SYSLINK_GetItem(infoPtr, (PLITEM)lParam);
1706
1707     case LM_GETIDEALHEIGHT:
1708         if (lParam)
1709         {
1710             /* LM_GETIDEALSIZE */
1711             SYSLINK_GetIdealSize(infoPtr, (int)wParam, (LPSIZE)lParam);
1712         }
1713         return SYSLINK_GetIdealHeight(infoPtr);
1714
1715     case WM_SETFOCUS:
1716         return SYSLINK_SetFocus(infoPtr);
1717
1718     case WM_KILLFOCUS:
1719         return SYSLINK_KillFocus(infoPtr);
1720
1721     case WM_ENABLE:
1722         infoPtr->Style &= ~WS_DISABLED;
1723         infoPtr->Style |= (wParam ? 0 : WS_DISABLED);
1724         InvalidateRect (infoPtr->Self, NULL, FALSE);
1725         return 0;
1726
1727     case WM_STYLECHANGED:
1728         if (wParam == GWL_STYLE)
1729         {
1730             infoPtr->Style = ((LPSTYLESTRUCT)lParam)->styleNew;
1731
1732             InvalidateRect(infoPtr->Self, NULL, TRUE);
1733         }
1734         return 0;
1735
1736     case WM_CREATE:
1737         /* allocate memory for info struct */
1738         infoPtr = Alloc (sizeof(SYSLINK_INFO));
1739         if (!infoPtr) return -1;
1740         SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
1741
1742         /* initialize the info struct */
1743         infoPtr->Self = hwnd;
1744         infoPtr->Notify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1745         infoPtr->Style = ((LPCREATESTRUCTW)lParam)->style;
1746         infoPtr->Font = 0;
1747         infoPtr->LinkFont = 0;
1748         infoPtr->Items = NULL;
1749         infoPtr->HasFocus = FALSE;
1750         infoPtr->MouseDownID = -1;
1751         infoPtr->TextColor = comctl32_color.clrWindowText;
1752         infoPtr->LinkColor = comctl32_color.clrHighlight;
1753         infoPtr->VisitedColor = comctl32_color.clrHighlight;
1754         infoPtr->BackColor = infoPtr->Style & LWS_TRANSPARENT ?
1755                              comctl32_color.clrWindow : comctl32_color.clrBtnFace;
1756         infoPtr->BreakChar = ' ';
1757         infoPtr->IgnoreReturn = infoPtr->Style & LWS_IGNORERETURN;
1758         TRACE("SysLink Ctrl creation, hwnd=%p\n", hwnd);
1759         SYSLINK_SetText(infoPtr, ((LPCREATESTRUCTW)lParam)->lpszName);
1760         return 0;
1761
1762     case WM_DESTROY:
1763         TRACE("SysLink Ctrl destruction, hwnd=%p\n", hwnd);
1764         SYSLINK_ClearDoc(infoPtr);
1765         if(infoPtr->Font != 0) DeleteObject(infoPtr->Font);
1766         if(infoPtr->LinkFont != 0) DeleteObject(infoPtr->LinkFont);
1767         SetWindowLongPtrW(hwnd, 0, 0);
1768         Free (infoPtr);
1769         return 0;
1770
1771     case WM_SYSCOLORCHANGE:
1772         COMCTL32_RefreshSysColors();
1773         if (infoPtr->Style & LWS_TRANSPARENT)
1774             infoPtr->BackColor = comctl32_color.clrWindow;
1775         return 0;
1776
1777     default:
1778 HandleDefaultMessage:
1779         if ((message >= WM_USER) && (message < WM_APP) && !COMCTL32_IsReflectedMessage(message))
1780         {
1781             ERR("unknown msg %04x wp=%04lx lp=%08lx\n", message, wParam, lParam );
1782         }
1783         return DefWindowProcW(hwnd, message, wParam, lParam);
1784     }
1785 }
1786
1787
1788 /***********************************************************************
1789  * SYSLINK_Register [Internal]
1790  *
1791  * Registers the SysLink window class.
1792  */
1793 VOID SYSLINK_Register (void)
1794 {
1795     WNDCLASSW wndClass;
1796
1797     ZeroMemory (&wndClass, sizeof(wndClass));
1798     wndClass.style         = CS_GLOBALCLASS | CS_VREDRAW | CS_HREDRAW;
1799     wndClass.lpfnWndProc   = SysLinkWindowProc;
1800     wndClass.cbClsExtra    = 0;
1801     wndClass.cbWndExtra    = sizeof (SYSLINK_INFO *);
1802     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1803     wndClass.lpszClassName = WC_LINK;
1804
1805     RegisterClassW (&wndClass);
1806 }
1807
1808
1809 /***********************************************************************
1810  * SYSLINK_Unregister [Internal]
1811  *
1812  * Unregisters the SysLink window class.
1813  */
1814 VOID SYSLINK_Unregister (void)
1815 {
1816     UnregisterClassW (WC_LINK, NULL);
1817 }