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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 1768 VOID SYSLINK_Unregister (void) 1769 { 1770 UnregisterClassW (WC_LINK, NULL); 1771 } 1772