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