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