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