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