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 } 1767