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