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