1 /* 2 * Tool tip control 3 * 4 * Copyright 1998, 1999 Eric Kohl 5 * Copyright 2004 Robert Shearman 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 * 21 * NOTES 22 * 23 * This code was audited for completeness against the documented features 24 * of Comctl32.dll version 6.0 on Sep. 08, 2004, by Robert Shearman. 25 * 26 * Unless otherwise noted, we believe this code to be complete, as per 27 * the specification mentioned above. 28 * If you discover missing features or bugs please note them below. 29 * 30 * TODO: 31 * - Custom draw support. 32 * - Animation. 33 * - Links. 34 * - Messages: 35 * o TTM_ADJUSTRECT 36 * o TTM_GETTITLEA 37 * o TTM_GETTTILEW 38 * o TTM_POPUP 39 * - Styles: 40 * o TTS_NOANIMATE 41 * o TTS_NOFADE 42 * o TTS_CLOSE 43 * 44 * Testing: 45 * - Run tests using Waite Group Windows95 API Bible Volume 2. 46 * The second cdrom (chapter 3) contains executables activate.exe, 47 * curtool.exe, deltool.exe, enumtools.exe, getinfo.exe, getiptxt.exe, 48 * hittest.exe, needtext.exe, newrect.exe, updtext.exe and winfrpt.exe. 49 * 50 * Timer logic. 51 * 52 * One important point to remember is that tools don't necessarily get 53 * a WM_MOUSEMOVE once the cursor leaves the tool, an example is when 54 * a tool sets TTF_IDISHWND (i.e. an entire window is a tool) because 55 * here WM_MOUSEMOVEs only get sent when the cursor is inside the 56 * client area. Therefore the only reliable way to know that the 57 * cursor has left a tool is to keep a timer running and check the 58 * position every time it expires. This is the role of timer 59 * ID_TIMERLEAVE. 60 * 61 * 62 * On entering a tool (detected in a relayed WM_MOUSEMOVE) we start 63 * ID_TIMERSHOW, if this times out and we're still in the tool we show 64 * the tip. On showing a tip we start both ID_TIMERPOP and 65 * ID_TIMERLEAVE. On hiding a tooltip we kill ID_TIMERPOP. 66 * ID_TIMERPOP is restarted on every relayed WM_MOUSEMOVE. If 67 * ID_TIMERPOP expires the tool is hidden and ID_TIMERPOP is killed. 68 * ID_TIMERLEAVE remains running - this is important as we need to 69 * determine when the cursor leaves the tool. 70 * 71 * When ID_TIMERLEAVE expires or on a relayed WM_MOUSEMOVE if we're 72 * still in the tool do nothing (apart from restart ID_TIMERPOP if 73 * this is a WM_MOUSEMOVE) (ID_TIMERLEAVE remains running). If we've 74 * left the tool and entered another one then hide the tip and start 75 * ID_TIMERSHOW with time ReshowTime and kill ID_TIMERLEAVE. If we're 76 * outside all tools hide the tip and kill ID_TIMERLEAVE. On Relayed 77 * mouse button messages hide the tip but leave ID_TIMERLEAVE running, 78 * this again will let us keep track of when the cursor leaves the 79 * tool. 80 * 81 * 82 * infoPtr->nTool is the tool the mouse was on on the last relayed MM 83 * or timer expiry or -1 if the mouse was not on a tool. 84 * 85 * infoPtr->nCurrentTool is the tool for which the tip is currently 86 * displaying text for or -1 if the tip is not shown. Actually this 87 * will only ever be infoPtr-nTool or -1, so it could be changed to a 88 * BOOL. 89 * 90 */ 91 92 #include "comctl32.h" 93 94 #include <wine/exception.h> 95 96 WINE_DEFAULT_DEBUG_CHANNEL(tooltips); 97 98 static HICON hTooltipIcons[TTI_ERROR+1]; 99 100 typedef struct 101 { 102 UINT uFlags; 103 UINT uInternalFlags; 104 HWND hwnd; 105 BOOL bNotifyUnicode; 106 UINT_PTR uId; 107 RECT rect; 108 HINSTANCE hinst; 109 LPWSTR lpszText; 110 LPARAM lParam; 111 } TTTOOL_INFO; 112 113 114 typedef struct 115 { 116 HWND hwndSelf; 117 WCHAR szTipText[INFOTIPSIZE]; 118 BOOL bActive; 119 BOOL bTrackActive; 120 UINT uNumTools; 121 COLORREF clrBk; 122 COLORREF clrText; 123 HFONT hFont; 124 HFONT hTitleFont; 125 INT xTrackPos; 126 INT yTrackPos; 127 INT nMaxTipWidth; 128 INT nTool; /* tool that mouse was on on last relayed mouse move */ 129 INT nCurrentTool; 130 INT nTrackTool; 131 INT nReshowTime; 132 INT nAutoPopTime; 133 INT nInitialTime; 134 RECT rcMargin; 135 BOOL bToolBelow; 136 LPWSTR pszTitle; 137 HICON hTitleIcon; 138 139 TTTOOL_INFO *tools; 140 } TOOLTIPS_INFO; 141 142 #define ID_TIMERSHOW 1 /* show delay timer */ 143 #define ID_TIMERPOP 2 /* auto pop timer */ 144 #define ID_TIMERLEAVE 3 /* tool leave timer */ 145 146 147 #define TOOLTIPS_GetInfoPtr(hWindow) ((TOOLTIPS_INFO *)GetWindowLongPtrW (hWindow, 0)) 148 149 /* offsets from window edge to start of text */ 150 #define NORMAL_TEXT_MARGIN 2 151 #define BALLOON_TEXT_MARGIN (NORMAL_TEXT_MARGIN+8) 152 /* value used for CreateRoundRectRgn that specifies how much 153 * each corner is curved */ 154 #define BALLOON_ROUNDEDNESS 20 155 #define BALLOON_STEMHEIGHT 13 156 #define BALLOON_STEMWIDTH 10 157 #define BALLOON_STEMINDENT 20 158 159 #define BALLOON_ICON_TITLE_SPACING 8 /* horizontal spacing between icon and title */ 160 #define BALLOON_TITLE_TEXT_SPACING 8 /* vertical spacing between icon/title and main text */ 161 #define ICON_HEIGHT 16 162 #define ICON_WIDTH 16 163 164 #define MAX_TEXT_SIZE_A 80 /* maximum retrieving text size by ANSI message */ 165 166 static LRESULT CALLBACK 167 TOOLTIPS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRef); 168 169 170 static inline BOOL TOOLTIPS_IsCallbackString(LPCWSTR str, BOOL isW) 171 { 172 if (isW) 173 return str == LPSTR_TEXTCALLBACKW; 174 else 175 return (LPCSTR)str == LPSTR_TEXTCALLBACKA; 176 } 177 178 static inline UINT_PTR 179 TOOLTIPS_GetTitleIconIndex(HICON hIcon) 180 { 181 UINT i; 182 for (i = 0; i <= TTI_ERROR; i++) 183 if (hTooltipIcons[i] == hIcon) 184 return i; 185 return (UINT_PTR)hIcon; 186 } 187 188 static void 189 TOOLTIPS_InitSystemSettings (TOOLTIPS_INFO *infoPtr) 190 { 191 NONCLIENTMETRICSW nclm; 192 193 infoPtr->clrBk = comctl32_color.clrInfoBk; 194 infoPtr->clrText = comctl32_color.clrInfoText; 195 196 DeleteObject (infoPtr->hFont); 197 nclm.cbSize = sizeof(nclm); 198 SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0); 199 infoPtr->hFont = CreateFontIndirectW (&nclm.lfStatusFont); 200 201 DeleteObject (infoPtr->hTitleFont); 202 nclm.lfStatusFont.lfWeight = FW_BOLD; 203 infoPtr->hTitleFont = CreateFontIndirectW (&nclm.lfStatusFont); 204 } 205 206 /* Custom draw routines */ 207 static void 208 TOOLTIPS_customdraw_fill(const TOOLTIPS_INFO *infoPtr, NMTTCUSTOMDRAW *lpnmttcd, 209 HDC hdc, const RECT *rcBounds, UINT uFlags) 210 { 211 ZeroMemory(lpnmttcd, sizeof(NMTTCUSTOMDRAW)); 212 lpnmttcd->uDrawFlags = uFlags; 213 lpnmttcd->nmcd.hdr.hwndFrom = infoPtr->hwndSelf; 214 lpnmttcd->nmcd.hdr.code = NM_CUSTOMDRAW; 215 if (infoPtr->nCurrentTool != -1) { 216 TTTOOL_INFO *toolPtr = &infoPtr->tools[infoPtr->nCurrentTool]; 217 lpnmttcd->nmcd.hdr.idFrom = toolPtr->uId; 218 } 219 lpnmttcd->nmcd.hdc = hdc; 220 lpnmttcd->nmcd.rc = *rcBounds; 221 /* FIXME - dwItemSpec, uItemState, lItemlParam */ 222 } 223 224 static inline DWORD 225 TOOLTIPS_notify_customdraw (DWORD dwDrawStage, NMTTCUSTOMDRAW *lpnmttcd) 226 { 227 LRESULT result; 228 lpnmttcd->nmcd.dwDrawStage = dwDrawStage; 229 230 TRACE("Notifying stage %d, flags %x, id %x\n", lpnmttcd->nmcd.dwDrawStage, 231 lpnmttcd->uDrawFlags, lpnmttcd->nmcd.hdr.code); 232 233 result = SendMessageW(GetParent(lpnmttcd->nmcd.hdr.hwndFrom), WM_NOTIFY, 234 0, (LPARAM)lpnmttcd); 235 236 TRACE("Notify result %x\n", (unsigned int)result); 237 238 return result; 239 } 240 241 static void 242 TOOLTIPS_Refresh (const TOOLTIPS_INFO *infoPtr, HDC hdc) 243 { 244 RECT rc; 245 INT oldBkMode; 246 HFONT hOldFont; 247 HBRUSH hBrush; 248 UINT uFlags = DT_EXTERNALLEADING; 249 HRGN hRgn = NULL; 250 DWORD dwStyle = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE); 251 NMTTCUSTOMDRAW nmttcd; 252 DWORD cdmode; 253 254 if (infoPtr->nMaxTipWidth > -1) 255 uFlags |= DT_WORDBREAK; 256 if (GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TTS_NOPREFIX) 257 uFlags |= DT_NOPREFIX; 258 GetClientRect (infoPtr->hwndSelf, &rc); 259 260 hBrush = CreateSolidBrush(infoPtr->clrBk); 261 262 oldBkMode = SetBkMode (hdc, TRANSPARENT); 263 SetTextColor (hdc, infoPtr->clrText); 264 hOldFont = SelectObject (hdc, infoPtr->hFont); 265 266 /* Custom draw - Call PrePaint once initial properties set up */ 267 /* Note: Contrary to MSDN, CDRF_SKIPDEFAULT still draws a tooltip */ 268 TOOLTIPS_customdraw_fill(infoPtr, &nmttcd, hdc, &rc, uFlags); 269 cdmode = TOOLTIPS_notify_customdraw(CDDS_PREPAINT, &nmttcd); 270 uFlags = nmttcd.uDrawFlags; 271 272 if (dwStyle & TTS_BALLOON) 273 { 274 /* create a region to store result into */ 275 hRgn = CreateRectRgn(0, 0, 0, 0); 276 277 GetWindowRgn(infoPtr->hwndSelf, hRgn); 278 279 /* fill the background */ 280 FillRgn(hdc, hRgn, hBrush); 281 DeleteObject(hBrush); 282 hBrush = NULL; 283 } 284 else 285 { 286 /* fill the background */ 287 FillRect(hdc, &rc, hBrush); 288 DeleteObject(hBrush); 289 hBrush = NULL; 290 } 291 292 if ((dwStyle & TTS_BALLOON) || infoPtr->pszTitle) 293 { 294 /* calculate text rectangle */ 295 rc.left += (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.left); 296 rc.top += (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.top); 297 rc.right -= (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.right); 298 rc.bottom -= (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.bottom); 299 if(infoPtr->bToolBelow) rc.top += BALLOON_STEMHEIGHT; 300 301 if (infoPtr->pszTitle) 302 { 303 RECT rcTitle = {rc.left, rc.top, rc.right, rc.bottom}; 304 int height; 305 BOOL icon_present; 306 HFONT prevFont; 307 308 /* draw icon */ 309 icon_present = infoPtr->hTitleIcon && 310 DrawIconEx(hdc, rc.left, rc.top, infoPtr->hTitleIcon, 311 ICON_WIDTH, ICON_HEIGHT, 0, NULL, DI_NORMAL); 312 if (icon_present) 313 rcTitle.left += ICON_WIDTH + BALLOON_ICON_TITLE_SPACING; 314 315 rcTitle.bottom = rc.top + ICON_HEIGHT; 316 317 /* draw title text */ 318 prevFont = SelectObject (hdc, infoPtr->hTitleFont); 319 height = DrawTextW(hdc, infoPtr->pszTitle, -1, &rcTitle, DT_BOTTOM | DT_SINGLELINE | DT_NOPREFIX); 320 SelectObject (hdc, prevFont); 321 rc.top += height + BALLOON_TITLE_TEXT_SPACING; 322 } 323 } 324 else 325 { 326 /* calculate text rectangle */ 327 rc.left += (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.left); 328 rc.top += (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.top); 329 rc.right -= (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.right); 330 rc.bottom -= (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.bottom); 331 } 332 333 /* draw text */ 334 DrawTextW (hdc, infoPtr->szTipText, -1, &rc, uFlags); 335 336 /* Custom draw - Call PostPaint after drawing */ 337 if (cdmode & CDRF_NOTIFYPOSTPAINT) { 338 TOOLTIPS_notify_customdraw(CDDS_POSTPAINT, &nmttcd); 339 } 340 341 /* be polite and reset the things we changed in the dc */ 342 SelectObject (hdc, hOldFont); 343 SetBkMode (hdc, oldBkMode); 344 345 if (dwStyle & TTS_BALLOON) 346 { 347 /* frame region because default window proc doesn't do it */ 348 INT width = GetSystemMetrics(SM_CXDLGFRAME) - GetSystemMetrics(SM_CXEDGE); 349 INT height = GetSystemMetrics(SM_CYDLGFRAME) - GetSystemMetrics(SM_CYEDGE); 350 351 hBrush = GetSysColorBrush(COLOR_WINDOWFRAME); 352 FrameRgn(hdc, hRgn, hBrush, width, height); 353 } 354 355 if (hRgn) 356 DeleteObject(hRgn); 357 } 358 359 static void TOOLTIPS_GetDispInfoA(const TOOLTIPS_INFO *infoPtr, TTTOOL_INFO *toolPtr, WCHAR *buffer) 360 { 361 NMTTDISPINFOA ttnmdi; 362 363 /* fill NMHDR struct */ 364 ZeroMemory (&ttnmdi, sizeof(NMTTDISPINFOA)); 365 ttnmdi.hdr.hwndFrom = infoPtr->hwndSelf; 366 ttnmdi.hdr.idFrom = toolPtr->uId; 367 ttnmdi.hdr.code = TTN_GETDISPINFOA; /* == TTN_NEEDTEXTA */ 368 ttnmdi.lpszText = ttnmdi.szText; 369 ttnmdi.uFlags = toolPtr->uFlags; 370 ttnmdi.lParam = toolPtr->lParam; 371 372 TRACE("hdr.idFrom = %lx\n", ttnmdi.hdr.idFrom); 373 SendMessageW(toolPtr->hwnd, WM_NOTIFY, toolPtr->uId, (LPARAM)&ttnmdi); 374 375 if (IS_INTRESOURCE(ttnmdi.lpszText)) { 376 LoadStringW(ttnmdi.hinst, LOWORD(ttnmdi.lpszText), 377 buffer, INFOTIPSIZE); 378 if (ttnmdi.uFlags & TTF_DI_SETITEM) { 379 toolPtr->hinst = ttnmdi.hinst; 380 toolPtr->lpszText = (LPWSTR)ttnmdi.lpszText; 381 } 382 } 383 else if (ttnmdi.lpszText == 0) { 384 buffer[0] = '\0'; 385 } 386 else if (ttnmdi.lpszText != LPSTR_TEXTCALLBACKA) { 387 Str_GetPtrAtoW(ttnmdi.lpszText, buffer, INFOTIPSIZE); 388 if (ttnmdi.uFlags & TTF_DI_SETITEM) { 389 toolPtr->hinst = 0; 390 toolPtr->lpszText = NULL; 391 Str_SetPtrW(&toolPtr->lpszText, buffer); 392 } 393 } 394 else { 395 ERR("recursive text callback\n"); 396 buffer[0] = '\0'; 397 } 398 399 /* no text available - try calling parent instead as per native */ 400 /* FIXME: Unsure if SETITEM should save the value or not */ 401 if (buffer[0] == 0x00) { 402 403 SendMessageW(GetParent(toolPtr->hwnd), WM_NOTIFY, toolPtr->uId, (LPARAM)&ttnmdi); 404 405 if (IS_INTRESOURCE(ttnmdi.lpszText)) { 406 LoadStringW(ttnmdi.hinst, LOWORD(ttnmdi.lpszText), 407 buffer, INFOTIPSIZE); 408 } else if (ttnmdi.lpszText && 409 ttnmdi.lpszText != LPSTR_TEXTCALLBACKA) { 410 Str_GetPtrAtoW(ttnmdi.lpszText, buffer, INFOTIPSIZE); 411 } 412 } 413 } 414 415 static void TOOLTIPS_GetDispInfoW(const TOOLTIPS_INFO *infoPtr, TTTOOL_INFO *toolPtr, WCHAR *buffer) 416 { 417 NMTTDISPINFOW ttnmdi; 418 419 /* fill NMHDR struct */ 420 ZeroMemory (&ttnmdi, sizeof(NMTTDISPINFOW)); 421 ttnmdi.hdr.hwndFrom = infoPtr->hwndSelf; 422 ttnmdi.hdr.idFrom = toolPtr->uId; 423 ttnmdi.hdr.code = TTN_GETDISPINFOW; /* == TTN_NEEDTEXTW */ 424 ttnmdi.lpszText = ttnmdi.szText; 425 ttnmdi.uFlags = toolPtr->uFlags; 426 ttnmdi.lParam = toolPtr->lParam; 427 428 TRACE("hdr.idFrom = %lx\n", ttnmdi.hdr.idFrom); 429 SendMessageW(toolPtr->hwnd, WM_NOTIFY, toolPtr->uId, (LPARAM)&ttnmdi); 430 431 if (IS_INTRESOURCE(ttnmdi.lpszText)) { 432 LoadStringW(ttnmdi.hinst, LOWORD(ttnmdi.lpszText), 433 buffer, INFOTIPSIZE); 434 if (ttnmdi.uFlags & TTF_DI_SETITEM) { 435 toolPtr->hinst = ttnmdi.hinst; 436 toolPtr->lpszText = ttnmdi.lpszText; 437 } 438 } 439 else if (ttnmdi.lpszText == 0) { 440 buffer[0] = '\0'; 441 } 442 else if (ttnmdi.lpszText != LPSTR_TEXTCALLBACKW) { 443 Str_GetPtrW(ttnmdi.lpszText, buffer, INFOTIPSIZE); 444 if (ttnmdi.uFlags & TTF_DI_SETITEM) { 445 toolPtr->hinst = 0; 446 toolPtr->lpszText = NULL; 447 Str_SetPtrW(&toolPtr->lpszText, buffer); 448 } 449 } 450 else { 451 ERR("recursive text callback\n"); 452 buffer[0] = '\0'; 453 } 454 455 /* no text available - try calling parent instead as per native */ 456 /* FIXME: Unsure if SETITEM should save the value or not */ 457 if (buffer[0] == 0x00) { 458 459 SendMessageW(GetParent(toolPtr->hwnd), WM_NOTIFY, toolPtr->uId, (LPARAM)&ttnmdi); 460 461 if (IS_INTRESOURCE(ttnmdi.lpszText)) { 462 LoadStringW(ttnmdi.hinst, LOWORD(ttnmdi.lpszText), 463 buffer, INFOTIPSIZE); 464 } else if (ttnmdi.lpszText && 465 ttnmdi.lpszText != LPSTR_TEXTCALLBACKW) { 466 Str_GetPtrW(ttnmdi.lpszText, buffer, INFOTIPSIZE); 467 } 468 } 469 470 } 471 472 static void 473 TOOLTIPS_GetTipText (const TOOLTIPS_INFO *infoPtr, INT nTool, WCHAR *buffer) 474 { 475 TTTOOL_INFO *toolPtr = &infoPtr->tools[nTool]; 476 477 if (IS_INTRESOURCE(toolPtr->lpszText)) { 478 /* load a resource */ 479 TRACE("load res string %p %x\n", 480 toolPtr->hinst, LOWORD(toolPtr->lpszText)); 481 if (!LoadStringW (toolPtr->hinst, LOWORD(toolPtr->lpszText), buffer, INFOTIPSIZE)) 482 buffer[0] = '\0'; 483 } 484 else if (toolPtr->lpszText) { 485 if (toolPtr->lpszText == LPSTR_TEXTCALLBACKW) { 486 if (toolPtr->bNotifyUnicode) 487 TOOLTIPS_GetDispInfoW(infoPtr, toolPtr, buffer); 488 else 489 TOOLTIPS_GetDispInfoA(infoPtr, toolPtr, buffer); 490 } 491 else { 492 /* the item is a usual (unicode) text */ 493 lstrcpynW (buffer, toolPtr->lpszText, INFOTIPSIZE); 494 } 495 } 496 else { 497 /* no text available */ 498 buffer[0] = '\0'; 499 } 500 501 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & TTS_NOPREFIX)) { 502 WCHAR *ptrW; 503 if ((ptrW = strchrW(buffer, '\t'))) 504 *ptrW = 0; 505 } 506 507 TRACE("%s\n", debugstr_w(buffer)); 508 } 509 510 511 static void 512 TOOLTIPS_CalcTipSize (const TOOLTIPS_INFO *infoPtr, LPSIZE lpSize) 513 { 514 HDC hdc; 515 HFONT hOldFont; 516 DWORD style = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE); 517 UINT uFlags = DT_EXTERNALLEADING | DT_CALCRECT; 518 RECT rc = {0, 0, 0, 0}; 519 SIZE title = {0, 0}; 520 521 if (infoPtr->nMaxTipWidth > -1) { 522 rc.right = infoPtr->nMaxTipWidth; 523 uFlags |= DT_WORDBREAK; 524 } 525 if (style & TTS_NOPREFIX) 526 uFlags |= DT_NOPREFIX; 527 TRACE("%s\n", debugstr_w(infoPtr->szTipText)); 528 529 hdc = GetDC (infoPtr->hwndSelf); 530 if (infoPtr->pszTitle) 531 { 532 RECT rcTitle = {0, 0, 0, 0}; 533 TRACE("title %s\n", debugstr_w(infoPtr->pszTitle)); 534 if (infoPtr->hTitleIcon) 535 { 536 title.cx = ICON_WIDTH; 537 title.cy = ICON_HEIGHT; 538 } 539 if (title.cx != 0) title.cx += BALLOON_ICON_TITLE_SPACING; 540 hOldFont = SelectObject (hdc, infoPtr->hTitleFont); 541 DrawTextW(hdc, infoPtr->pszTitle, -1, &rcTitle, DT_SINGLELINE | DT_NOPREFIX | DT_CALCRECT); 542 SelectObject (hdc, hOldFont); 543 title.cy = max(title.cy, rcTitle.bottom - rcTitle.top) + BALLOON_TITLE_TEXT_SPACING; 544 title.cx += (rcTitle.right - rcTitle.left); 545 } 546 hOldFont = SelectObject (hdc, infoPtr->hFont); 547 DrawTextW (hdc, infoPtr->szTipText, -1, &rc, uFlags); 548 SelectObject (hdc, hOldFont); 549 ReleaseDC (infoPtr->hwndSelf, hdc); 550 551 if ((style & TTS_BALLOON) || infoPtr->pszTitle) 552 { 553 lpSize->cx = max(rc.right - rc.left, title.cx) + 2*BALLOON_TEXT_MARGIN + 554 infoPtr->rcMargin.left + infoPtr->rcMargin.right; 555 lpSize->cy = title.cy + rc.bottom - rc.top + 2*BALLOON_TEXT_MARGIN + 556 infoPtr->rcMargin.bottom + infoPtr->rcMargin.top + 557 BALLOON_STEMHEIGHT; 558 } 559 else 560 { 561 lpSize->cx = rc.right - rc.left + 2*NORMAL_TEXT_MARGIN + 562 infoPtr->rcMargin.left + infoPtr->rcMargin.right; 563 lpSize->cy = rc.bottom - rc.top + 2*NORMAL_TEXT_MARGIN + 564 infoPtr->rcMargin.bottom + infoPtr->rcMargin.top; 565 } 566 } 567 568 569 static void 570 TOOLTIPS_Show (TOOLTIPS_INFO *infoPtr, BOOL track_activate) 571 { 572 TTTOOL_INFO *toolPtr; 573 HMONITOR monitor; 574 MONITORINFO mon_info; 575 RECT rect; 576 SIZE size; 577 NMHDR hdr; 578 int ptfx = 0; 579 DWORD style = GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE); 580 INT nTool, current; 581 582 if (track_activate) 583 { 584 if (infoPtr->nTrackTool == -1) 585 { 586 TRACE("invalid tracking tool %d\n", infoPtr->nTrackTool); 587 return; 588 } 589 nTool = infoPtr->nTrackTool; 590 } 591 else 592 { 593 if (infoPtr->nTool == -1) 594 { 595 TRACE("invalid tool %d\n", infoPtr->nTool); 596 return; 597 } 598 nTool = infoPtr->nTool; 599 } 600 601 TRACE("Show tooltip pre %d, %p\n", nTool, infoPtr->hwndSelf); 602 603 current = infoPtr->nCurrentTool; 604 if (!track_activate) 605 infoPtr->nCurrentTool = infoPtr->nTool; 606 607 TOOLTIPS_GetTipText (infoPtr, nTool, infoPtr->szTipText); 608 609 if (infoPtr->szTipText[0] == '\0') 610 { 611 infoPtr->nCurrentTool = current; 612 return; 613 } 614 615 toolPtr = &infoPtr->tools[nTool]; 616 617 TRACE("Show tooltip %d\n", nTool); 618 619 hdr.hwndFrom = infoPtr->hwndSelf; 620 hdr.idFrom = toolPtr->uId; 621 hdr.code = TTN_SHOW; 622 SendMessageW (toolPtr->hwnd, WM_NOTIFY, toolPtr->uId, (LPARAM)&hdr); 623 624 TRACE("%s\n", debugstr_w(infoPtr->szTipText)); 625 626 TOOLTIPS_CalcTipSize (infoPtr, &size); 627 TRACE("size %d x %d\n", size.cx, size.cy); 628 629 if (track_activate && (toolPtr->uFlags & TTF_TRACK)) 630 { 631 rect.left = infoPtr->xTrackPos; 632 rect.top = infoPtr->yTrackPos; 633 ptfx = rect.left; 634 635 if (toolPtr->uFlags & TTF_CENTERTIP) 636 { 637 rect.left -= (size.cx / 2); 638 if (!(style & TTS_BALLOON)) 639 rect.top -= (size.cy / 2); 640 } 641 if (!(infoPtr->bToolBelow = (infoPtr->yTrackPos + size.cy <= GetSystemMetrics(SM_CYSCREEN)))) 642 rect.top -= size.cy; 643 644 if (!(toolPtr->uFlags & TTF_ABSOLUTE)) 645 { 646 if (style & TTS_BALLOON) 647 rect.left -= BALLOON_STEMINDENT; 648 else 649 { 650 RECT rcTool; 651 652 if (toolPtr->uFlags & TTF_IDISHWND) 653 GetWindowRect ((HWND)toolPtr->uId, &rcTool); 654 else 655 { 656 rcTool = toolPtr->rect; 657 MapWindowPoints (toolPtr->hwnd, NULL, (LPPOINT)&rcTool, 2); 658 } 659 660 /* smart placement */ 661 if ((rect.left + size.cx > rcTool.left) && (rect.left < rcTool.right) && 662 (rect.top + size.cy > rcTool.top) && (rect.top < rcTool.bottom)) 663 rect.left = rcTool.right; 664 } 665 } 666 } 667 else 668 { 669 if (toolPtr->uFlags & TTF_CENTERTIP) 670 { 671 RECT rc; 672 673 if (toolPtr->uFlags & TTF_IDISHWND) 674 GetWindowRect ((HWND)toolPtr->uId, &rc); 675 else { 676 rc = toolPtr->rect; 677 MapWindowPoints (toolPtr->hwnd, NULL, (LPPOINT)&rc, 2); 678 } 679 rect.left = (rc.left + rc.right - size.cx) / 2; 680 if (style & TTS_BALLOON) 681 { 682 ptfx = rc.left + ((rc.right - rc.left) / 2); 683 684 /* CENTERTIP ballon tooltips default to below the field 685 * if they fit on the screen */ 686 if (rc.bottom + size.cy > GetSystemMetrics(SM_CYSCREEN)) 687 { 688 rect.top = rc.top - size.cy; 689 infoPtr->bToolBelow = FALSE; 690 } 691 else 692 { 693 infoPtr->bToolBelow = TRUE; 694 rect.top = rc.bottom; 695 } 696 rect.left = max(0, rect.left - BALLOON_STEMINDENT); 697 } 698 else 699 { 700 rect.top = rc.bottom + 2; 701 infoPtr->bToolBelow = TRUE; 702 } 703 } 704 else 705 { 706 GetCursorPos ((LPPOINT)&rect); 707 if (style & TTS_BALLOON) 708 { 709 ptfx = rect.left; 710 if(rect.top - size.cy >= 0) 711 { 712 rect.top -= size.cy; 713 infoPtr->bToolBelow = FALSE; 714 } 715 else 716 { 717 infoPtr->bToolBelow = TRUE; 718 rect.top += 20; 719 } 720 rect.left = max(0, rect.left - BALLOON_STEMINDENT); 721 } 722 else 723 { 724 rect.top += 20; 725 infoPtr->bToolBelow = TRUE; 726 } 727 } 728 } 729 730 TRACE("pos %d - %d\n", rect.left, rect.top); 731 732 rect.right = rect.left + size.cx; 733 rect.bottom = rect.top + size.cy; 734 735 /* check position */ 736 737 monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY ); 738 mon_info.cbSize = sizeof(mon_info); 739 GetMonitorInfoW( monitor, &mon_info ); 740 741 if( rect.right > mon_info.rcWork.right ) { 742 rect.left -= rect.right - mon_info.rcWork.right + 2; 743 rect.right = mon_info.rcWork.right - 2; 744 } 745 if (rect.left < mon_info.rcWork.left) rect.left = mon_info.rcWork.left; 746 747 if( rect.bottom > mon_info.rcWork.bottom ) { 748 RECT rc; 749 750 if (toolPtr->uFlags & TTF_IDISHWND) 751 GetWindowRect ((HWND)toolPtr->uId, &rc); 752 else { 753 rc = toolPtr->rect; 754 MapWindowPoints (toolPtr->hwnd, NULL, (LPPOINT)&rc, 2); 755 } 756 rect.bottom = rc.top - 2; 757 rect.top = rect.bottom - size.cy; 758 } 759 760 AdjustWindowRectEx (&rect, GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE), 761 FALSE, GetWindowLongW (infoPtr->hwndSelf, GWL_EXSTYLE)); 762 763 if (style & TTS_BALLOON) 764 { 765 HRGN hRgn; 766 HRGN hrStem; 767 POINT pts[3]; 768 769 ptfx -= rect.left; 770 771 if(infoPtr->bToolBelow) 772 { 773 pts[0].x = ptfx; 774 pts[0].y = 0; 775 pts[1].x = max(BALLOON_STEMINDENT, ptfx - (BALLOON_STEMWIDTH / 2)); 776 pts[1].y = BALLOON_STEMHEIGHT; 777 pts[2].x = pts[1].x + BALLOON_STEMWIDTH; 778 pts[2].y = pts[1].y; 779 if(pts[2].x > (rect.right - rect.left) - BALLOON_STEMINDENT) 780 { 781 pts[2].x = (rect.right - rect.left) - BALLOON_STEMINDENT; 782 pts[1].x = pts[2].x - BALLOON_STEMWIDTH; 783 } 784 } 785 else 786 { 787 pts[0].x = max(BALLOON_STEMINDENT, ptfx - (BALLOON_STEMWIDTH / 2)); 788 pts[0].y = (rect.bottom - rect.top) - BALLOON_STEMHEIGHT; 789 pts[1].x = pts[0].x + BALLOON_STEMWIDTH; 790 pts[1].y = pts[0].y; 791 pts[2].x = ptfx; 792 pts[2].y = (rect.bottom - rect.top); 793 if(pts[1].x > (rect.right - rect.left) - BALLOON_STEMINDENT) 794 { 795 pts[1].x = (rect.right - rect.left) - BALLOON_STEMINDENT; 796 pts[0].x = pts[1].x - BALLOON_STEMWIDTH; 797 } 798 } 799 800 hrStem = CreatePolygonRgn(pts, sizeof(pts) / sizeof(pts[0]), ALTERNATE); 801 802 hRgn = CreateRoundRectRgn(0, 803 (infoPtr->bToolBelow ? BALLOON_STEMHEIGHT : 0), 804 rect.right - rect.left, 805 (infoPtr->bToolBelow ? rect.bottom - rect.top : rect.bottom - rect.top - BALLOON_STEMHEIGHT), 806 BALLOON_ROUNDEDNESS, BALLOON_ROUNDEDNESS); 807 808 CombineRgn(hRgn, hRgn, hrStem, RGN_OR); 809 DeleteObject(hrStem); 810 811 SetWindowRgn(infoPtr->hwndSelf, hRgn, FALSE); 812 /* we don't free the region handle as the system deletes it when 813 * it is no longer needed */ 814 } 815 816 SetWindowPos (infoPtr->hwndSelf, HWND_TOPMOST, rect.left, rect.top, 817 rect.right - rect.left, rect.bottom - rect.top, 818 SWP_SHOWWINDOW | SWP_NOACTIVATE); 819 820 /* repaint the tooltip */ 821 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); 822 UpdateWindow(infoPtr->hwndSelf); 823 824 if (!track_activate) 825 { 826 SetTimer (infoPtr->hwndSelf, ID_TIMERPOP, infoPtr->nAutoPopTime, 0); 827 TRACE("timer 2 started\n"); 828 SetTimer (infoPtr->hwndSelf, ID_TIMERLEAVE, infoPtr->nReshowTime, 0); 829 TRACE("timer 3 started\n"); 830 } 831 } 832 833 834 static void 835 TOOLTIPS_Hide (TOOLTIPS_INFO *infoPtr) 836 { 837 TTTOOL_INFO *toolPtr; 838 NMHDR hdr; 839 840 TRACE("Hide tooltip %d, %p.\n", infoPtr->nCurrentTool, infoPtr->hwndSelf); 841 842 if (infoPtr->nCurrentTool == -1) 843 return; 844 845 toolPtr = &infoPtr->tools[infoPtr->nCurrentTool]; 846 KillTimer (infoPtr->hwndSelf, ID_TIMERPOP); 847 848 hdr.hwndFrom = infoPtr->hwndSelf; 849 hdr.idFrom = toolPtr->uId; 850 hdr.code = TTN_POP; 851 SendMessageW (toolPtr->hwnd, WM_NOTIFY, toolPtr->uId, (LPARAM)&hdr); 852 853 infoPtr->nCurrentTool = -1; 854 855 SetWindowPos (infoPtr->hwndSelf, HWND_TOP, 0, 0, 0, 0, 856 SWP_NOZORDER | SWP_HIDEWINDOW | SWP_NOACTIVATE); 857 } 858 859 860 static void 861 TOOLTIPS_TrackShow (TOOLTIPS_INFO *infoPtr) 862 { 863 TOOLTIPS_Show(infoPtr, TRUE); 864 } 865 866 867 static void 868 TOOLTIPS_TrackHide (const TOOLTIPS_INFO *infoPtr) 869 { 870 TTTOOL_INFO *toolPtr; 871 NMHDR hdr; 872 873 TRACE("hide tracking tooltip %d\n", infoPtr->nTrackTool); 874 875 if (infoPtr->nTrackTool == -1) 876 return; 877 878 toolPtr = &infoPtr->tools[infoPtr->nTrackTool]; 879 880 hdr.hwndFrom = infoPtr->hwndSelf; 881 hdr.idFrom = toolPtr->uId; 882 hdr.code = TTN_POP; 883 SendMessageW (toolPtr->hwnd, WM_NOTIFY, toolPtr->uId, (LPARAM)&hdr); 884 885 SetWindowPos (infoPtr->hwndSelf, HWND_TOP, 0, 0, 0, 0, 886 SWP_NOZORDER | SWP_HIDEWINDOW | SWP_NOACTIVATE); 887 } 888 889 /* Structure layout is the same for TTTOOLINFOW and TTTOOLINFOA, 890 this helper is used in both cases. */ 891 static INT 892 TOOLTIPS_GetToolFromInfoT (const TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *lpToolInfo) 893 { 894 TTTOOL_INFO *toolPtr; 895 UINT nTool; 896 897 for (nTool = 0; nTool < infoPtr->uNumTools; nTool++) { 898 toolPtr = &infoPtr->tools[nTool]; 899 900 if (!(toolPtr->uFlags & TTF_IDISHWND) && 901 (lpToolInfo->hwnd == toolPtr->hwnd) && 902 (lpToolInfo->uId == toolPtr->uId)) 903 return nTool; 904 } 905 906 for (nTool = 0; nTool < infoPtr->uNumTools; nTool++) { 907 toolPtr = &infoPtr->tools[nTool]; 908 909 if ((toolPtr->uFlags & TTF_IDISHWND) && 910 (lpToolInfo->uId == toolPtr->uId)) 911 return nTool; 912 } 913 914 return -1; 915 } 916 917 918 static INT 919 TOOLTIPS_GetToolFromPoint (const TOOLTIPS_INFO *infoPtr, HWND hwnd, const POINT *lpPt) 920 { 921 TTTOOL_INFO *toolPtr; 922 UINT nTool; 923 924 for (nTool = 0; nTool < infoPtr->uNumTools; nTool++) { 925 toolPtr = &infoPtr->tools[nTool]; 926 927 if (!(toolPtr->uFlags & TTF_IDISHWND)) { 928 if (hwnd != toolPtr->hwnd) 929 continue; 930 if (!PtInRect (&toolPtr->rect, *lpPt)) 931 continue; 932 return nTool; 933 } 934 } 935 936 for (nTool = 0; nTool < infoPtr->uNumTools; nTool++) { 937 toolPtr = &infoPtr->tools[nTool]; 938 939 if (toolPtr->uFlags & TTF_IDISHWND) { 940 if ((HWND)toolPtr->uId == hwnd) 941 return nTool; 942 } 943 } 944 945 return -1; 946 } 947 948 static inline void 949 TOOLTIPS_CopyInfoT (const TOOLTIPS_INFO *infoPtr, INT index, TTTOOLINFOW *ti, BOOL isW) 950 { 951 const TTTOOL_INFO *toolPtr = &infoPtr->tools[index]; 952 953 ti->uFlags = toolPtr->uFlags; 954 ti->hwnd = toolPtr->hwnd; 955 ti->uId = toolPtr->uId; 956 ti->rect = toolPtr->rect; 957 ti->hinst = toolPtr->hinst; 958 959 if (ti->lpszText) { 960 if (toolPtr->lpszText == NULL || 961 IS_INTRESOURCE(toolPtr->lpszText) || 962 toolPtr->lpszText == LPSTR_TEXTCALLBACKW) 963 ti->lpszText = toolPtr->lpszText; 964 else if (isW) 965 strcpyW (ti->lpszText, toolPtr->lpszText); 966 else 967 /* ANSI version, the buffer is maximum 80 bytes without null. */ 968 WideCharToMultiByte(CP_ACP, 0, toolPtr->lpszText, -1, 969 (LPSTR)ti->lpszText, MAX_TEXT_SIZE_A, NULL, NULL); 970 } 971 972 if (ti->cbSize >= TTTOOLINFOW_V2_SIZE) 973 ti->lParam = toolPtr->lParam; 974 975 /* lpReserved is intentionally not set. */ 976 } 977 978 static BOOL 979 TOOLTIPS_IsWindowActive (HWND hwnd) 980 { 981 HWND hwndActive = GetActiveWindow (); 982 if (!hwndActive) 983 return FALSE; 984 if (hwndActive == hwnd) 985 return TRUE; 986 return IsChild (hwndActive, hwnd); 987 } 988 989 990 static INT 991 TOOLTIPS_CheckTool (const TOOLTIPS_INFO *infoPtr, BOOL bShowTest) 992 { 993 POINT pt; 994 HWND hwndTool; 995 INT nTool; 996 997 GetCursorPos (&pt); 998 hwndTool = (HWND)SendMessageW (infoPtr->hwndSelf, TTM_WINDOWFROMPOINT, 0, (LPARAM)&pt); 999 if (hwndTool == 0) 1000 return -1; 1001 1002 ScreenToClient (hwndTool, &pt); 1003 nTool = TOOLTIPS_GetToolFromPoint (infoPtr, hwndTool, &pt); 1004 if (nTool == -1) 1005 return -1; 1006 1007 if (!(GetWindowLongW (infoPtr->hwndSelf, GWL_STYLE) & TTS_ALWAYSTIP) && bShowTest) 1008 { 1009 TTTOOL_INFO *ti = &infoPtr->tools[nTool]; 1010 HWND hwnd = (ti->uFlags & TTF_IDISHWND) ? (HWND)ti->uId : ti->hwnd; 1011 1012 if (!TOOLTIPS_IsWindowActive(hwnd)) 1013 { 1014 TRACE("not active: hwnd %p, parent %p, active %p\n", 1015 hwnd, GetParent(hwnd), GetActiveWindow()); 1016 return -1; 1017 } 1018 } 1019 1020 TRACE("tool %d\n", nTool); 1021 1022 return nTool; 1023 } 1024 1025 1026 static LRESULT 1027 TOOLTIPS_Activate (TOOLTIPS_INFO *infoPtr, BOOL activate) 1028 { 1029 infoPtr->bActive = activate; 1030 1031 TRACE("activate %d\n", activate); 1032 1033 if (!(infoPtr->bActive) && (infoPtr->nCurrentTool != -1)) 1034 TOOLTIPS_Hide (infoPtr); 1035 1036 return 0; 1037 } 1038 1039 1040 static LRESULT 1041 TOOLTIPS_AddToolT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW) 1042 { 1043 TTTOOL_INFO *toolPtr; 1044 INT nResult; 1045 1046 if (!ti) return FALSE; 1047 1048 TRACE("add tool (%p) %p %ld%s\n", infoPtr->hwndSelf, ti->hwnd, ti->uId, 1049 (ti->uFlags & TTF_IDISHWND) ? " TTF_IDISHWND" : ""); 1050 1051 if (ti->cbSize >= TTTOOLINFOW_V2_SIZE && !ti->lpszText && isW) 1052 return FALSE; 1053 1054 if (infoPtr->uNumTools == 0) { 1055 infoPtr->tools = Alloc (sizeof(TTTOOL_INFO)); 1056 toolPtr = infoPtr->tools; 1057 } 1058 else { 1059 TTTOOL_INFO *oldTools = infoPtr->tools; 1060 infoPtr->tools = 1061 Alloc (sizeof(TTTOOL_INFO) * (infoPtr->uNumTools + 1)); 1062 memcpy (infoPtr->tools, oldTools, 1063 infoPtr->uNumTools * sizeof(TTTOOL_INFO)); 1064 Free (oldTools); 1065 toolPtr = &infoPtr->tools[infoPtr->uNumTools]; 1066 } 1067 1068 infoPtr->uNumTools++; 1069 1070 /* copy tool data */ 1071 toolPtr->uFlags = ti->uFlags; 1072 toolPtr->uInternalFlags = (ti->uFlags & (TTF_SUBCLASS | TTF_IDISHWND)); 1073 toolPtr->hwnd = ti->hwnd; 1074 toolPtr->uId = ti->uId; 1075 toolPtr->rect = ti->rect; 1076 toolPtr->hinst = ti->hinst; 1077 1078 if (ti->cbSize >= TTTOOLINFOW_V1_SIZE) { 1079 if (IS_INTRESOURCE(ti->lpszText)) { 1080 TRACE("add string id %x\n", LOWORD(ti->lpszText)); 1081 toolPtr->lpszText = ti->lpszText; 1082 } 1083 else if (ti->lpszText) { 1084 if (TOOLTIPS_IsCallbackString(ti->lpszText, isW)) { 1085 TRACE("add CALLBACK\n"); 1086 toolPtr->lpszText = LPSTR_TEXTCALLBACKW; 1087 } 1088 else if (isW) { 1089 INT len = lstrlenW (ti->lpszText); 1090 TRACE("add text %s\n", debugstr_w(ti->lpszText)); 1091 toolPtr->lpszText = Alloc ((len + 1)*sizeof(WCHAR)); 1092 strcpyW (toolPtr->lpszText, ti->lpszText); 1093 } 1094 else { 1095 INT len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, -1, NULL, 0); 1096 TRACE("add text \"%s\"\n", debugstr_a((char *)ti->lpszText)); 1097 toolPtr->lpszText = Alloc (len * sizeof(WCHAR)); 1098 MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, -1, toolPtr->lpszText, len); 1099 } 1100 } 1101 } 1102 1103 if (ti->cbSize >= TTTOOLINFOW_V2_SIZE) 1104 toolPtr->lParam = ti->lParam; 1105 1106 /* install subclassing hook */ 1107 if (toolPtr->uInternalFlags & TTF_SUBCLASS) { 1108 if (toolPtr->uInternalFlags & TTF_IDISHWND) { 1109 SetWindowSubclass((HWND)toolPtr->uId, TOOLTIPS_SubclassProc, 1, 1110 (DWORD_PTR)infoPtr->hwndSelf); 1111 } 1112 else { 1113 SetWindowSubclass(toolPtr->hwnd, TOOLTIPS_SubclassProc, 1, 1114 (DWORD_PTR)infoPtr->hwndSelf); 1115 } 1116 TRACE("subclassing installed\n"); 1117 } 1118 1119 nResult = SendMessageW (toolPtr->hwnd, WM_NOTIFYFORMAT, 1120 (WPARAM)infoPtr->hwndSelf, NF_QUERY); 1121 if (nResult == NFR_ANSI) { 1122 toolPtr->bNotifyUnicode = FALSE; 1123 TRACE(" -- WM_NOTIFYFORMAT returns: NFR_ANSI\n"); 1124 } else if (nResult == NFR_UNICODE) { 1125 toolPtr->bNotifyUnicode = TRUE; 1126 TRACE(" -- WM_NOTIFYFORMAT returns: NFR_UNICODE\n"); 1127 } else { 1128 TRACE (" -- WM_NOTIFYFORMAT returns: %d\n", nResult); 1129 } 1130 1131 return TRUE; 1132 } 1133 1134 static void TOOLTIPS_ResetSubclass (const TTTOOL_INFO *toolPtr) 1135 { 1136 /* Reset subclassing data. */ 1137 if (toolPtr->uInternalFlags & TTF_SUBCLASS) 1138 SetWindowSubclass(toolPtr->uInternalFlags & TTF_IDISHWND ? (HWND)toolPtr->uId : toolPtr->hwnd, 1139 TOOLTIPS_SubclassProc, 1, 0); 1140 } 1141 1142 static LRESULT 1143 TOOLTIPS_DelToolT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW) 1144 { 1145 TTTOOL_INFO *toolPtr; 1146 INT nTool; 1147 1148 if (!ti) return 0; 1149 if (isW && ti->cbSize > TTTOOLINFOW_V2_SIZE && 1150 ti->cbSize != TTTOOLINFOW_V3_SIZE) 1151 return 0; 1152 if (infoPtr->uNumTools == 0) 1153 return 0; 1154 1155 nTool = TOOLTIPS_GetToolFromInfoT (infoPtr, ti); 1156 1157 TRACE("tool %d\n", nTool); 1158 1159 if (nTool == -1) 1160 return 0; 1161 1162 /* make sure the tooltip has disappeared before deleting it */ 1163 TOOLTIPS_Hide(infoPtr); 1164 1165 /* delete text string */ 1166 toolPtr = &infoPtr->tools[nTool]; 1167 if (toolPtr->lpszText) { 1168 if ( (toolPtr->lpszText != LPSTR_TEXTCALLBACKW) && 1169 !IS_INTRESOURCE(toolPtr->lpszText) ) 1170 Free (toolPtr->lpszText); 1171 } 1172 1173 TOOLTIPS_ResetSubclass (toolPtr); 1174 1175 /* delete tool from tool list */ 1176 if (infoPtr->uNumTools == 1) { 1177 Free (infoPtr->tools); 1178 infoPtr->tools = NULL; 1179 } 1180 else { 1181 TTTOOL_INFO *oldTools = infoPtr->tools; 1182 infoPtr->tools = 1183 Alloc (sizeof(TTTOOL_INFO) * (infoPtr->uNumTools - 1)); 1184 1185 if (nTool > 0) 1186 memcpy (&infoPtr->tools[0], &oldTools[0], 1187 nTool * sizeof(TTTOOL_INFO)); 1188 1189 if (nTool < infoPtr->uNumTools - 1) 1190 memcpy (&infoPtr->tools[nTool], &oldTools[nTool + 1], 1191 (infoPtr->uNumTools - nTool - 1) * sizeof(TTTOOL_INFO)); 1192 1193 Free (oldTools); 1194 } 1195 1196 /* update any indices affected by delete */ 1197 1198 /* destroying tool that mouse was on on last relayed mouse move */ 1199 if (infoPtr->nTool == nTool) 1200 /* -1 means no current tool (0 means first tool) */ 1201 infoPtr->nTool = -1; 1202 else if (infoPtr->nTool > nTool) 1203 infoPtr->nTool--; 1204 1205 if (infoPtr->nTrackTool == nTool) 1206 /* -1 means no current tool (0 means first tool) */ 1207 infoPtr->nTrackTool = -1; 1208 else if (infoPtr->nTrackTool > nTool) 1209 infoPtr->nTrackTool--; 1210 1211 if (infoPtr->nCurrentTool == nTool) 1212 /* -1 means no current tool (0 means first tool) */ 1213 infoPtr->nCurrentTool = -1; 1214 else if (infoPtr->nCurrentTool > nTool) 1215 infoPtr->nCurrentTool--; 1216 1217 infoPtr->uNumTools--; 1218 1219 return 0; 1220 } 1221 1222 static LRESULT 1223 TOOLTIPS_EnumToolsT (const TOOLTIPS_INFO *infoPtr, UINT uIndex, TTTOOLINFOW *ti, 1224 BOOL isW) 1225 { 1226 if (!ti || ti->cbSize < TTTOOLINFOW_V1_SIZE) 1227 return FALSE; 1228 if (uIndex >= infoPtr->uNumTools) 1229 return FALSE; 1230 1231 TRACE("index=%u\n", uIndex); 1232 1233 TOOLTIPS_CopyInfoT (infoPtr, uIndex, ti, isW); 1234 1235 return TRUE; 1236 } 1237 1238 static LRESULT 1239 TOOLTIPS_GetBubbleSize (const TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *lpToolInfo) 1240 { 1241 INT nTool; 1242 SIZE size; 1243 1244 if (lpToolInfo == NULL) 1245 return FALSE; 1246 if (lpToolInfo->cbSize < TTTOOLINFOW_V1_SIZE) 1247 return FALSE; 1248 1249 nTool = TOOLTIPS_GetToolFromInfoT (infoPtr, lpToolInfo); 1250 if (nTool == -1) return 0; 1251 1252 TRACE("tool %d\n", nTool); 1253 1254 TOOLTIPS_CalcTipSize (infoPtr, &size); 1255 TRACE("size %d x %d\n", size.cx, size.cy); 1256 1257 return MAKELRESULT(size.cx, size.cy); 1258 } 1259 1260 static LRESULT 1261 TOOLTIPS_GetCurrentToolT (const TOOLTIPS_INFO *infoPtr, TTTOOLINFOW *ti, BOOL isW) 1262 { 1263 if (ti) { 1264 if (ti->cbSize < TTTOOLINFOW_V1_SIZE) 1265 return FALSE; 1266 1267 if (infoPtr->nCurrentTool != -1) 1268 TOOLTIPS_CopyInfoT (infoPtr, infoPtr->nCurrentTool, ti, isW); 1269 } 1270 1271 return infoPtr->nCurrentTool != -1; 1272 } 1273 1274 1275 static LRESULT 1276 TOOLTIPS_GetDelayTime (const TOOLTIPS_INFO *infoPtr, DWORD duration) 1277 { 1278 switch (duration) { 1279 case TTDT_RESHOW: 1280 return infoPtr->nReshowTime; 1281 1282 case TTDT_AUTOPOP: 1283 return infoPtr->nAutoPopTime; 1284 1285 case TTDT_INITIAL: 1286 case TTDT_AUTOMATIC: /* Apparently TTDT_AUTOMATIC returns TTDT_INITIAL */ 1287 return infoPtr->nInitialTime; 1288 1289 default: 1290 WARN("Invalid duration flag %x\n", duration); 1291 break; 1292 } 1293 1294 return -1; 1295 } 1296 1297 1298 static LRESULT 1299 TOOLTIPS_GetMargin (const TOOLTIPS_INFO *infoPtr, RECT *rect) 1300 { 1301 if (rect) 1302 *rect = infoPtr->rcMargin; 1303 1304 return 0; 1305 } 1306 1307 1308 static inline LRESULT 1309 TOOLTIPS_GetMaxTipWidth (const TOOLTIPS_INFO *infoPtr) 1310 { 1311 return infoPtr->nMaxTipWidth; 1312 } 1313 1314 1315 static LRESULT 1316 TOOLTIPS_GetTextT (const TOOLTIPS_INFO *infoPtr, TTTOOLINFOW *ti, BOOL isW) 1317 { 1318 INT nTool; 1319 1320 if (!ti) return 0; 1321 if (ti->cbSize < TTTOOLINFOW_V1_SIZE) 1322 return 0; 1323 1324 nTool = TOOLTIPS_GetToolFromInfoT (infoPtr, ti); 1325 if (nTool == -1) return 0; 1326 1327 if (infoPtr->tools[nTool].lpszText == NULL) 1328 return 0; 1329 1330 if (isW) { 1331 ti->lpszText[0] = '\0'; 1332 TOOLTIPS_GetTipText(infoPtr, nTool, ti->lpszText); 1333 } 1334 else { 1335 WCHAR buffer[INFOTIPSIZE]; 1336 1337 /* NB this API is broken, there is no way for the app to determine 1338 what size buffer it requires nor a way to specify how long the 1339 one it supplies is. According to the test result, it's up to 1340 80 bytes by the ANSI version. */ 1341 1342 buffer[0] = '\0'; 1343 TOOLTIPS_GetTipText(infoPtr, nTool, buffer); 1344 WideCharToMultiByte(CP_ACP, 0, buffer, -1, (LPSTR)ti->lpszText, 1345 MAX_TEXT_SIZE_A, NULL, NULL); 1346 } 1347 1348 return 0; 1349 } 1350 1351 1352 static inline LRESULT 1353 TOOLTIPS_GetTipBkColor (const TOOLTIPS_INFO *infoPtr) 1354 { 1355 return infoPtr->clrBk; 1356 } 1357 1358 1359 static inline LRESULT 1360 TOOLTIPS_GetTipTextColor (const TOOLTIPS_INFO *infoPtr) 1361 { 1362 return infoPtr->clrText; 1363 } 1364 1365 1366 static inline LRESULT 1367 TOOLTIPS_GetToolCount (const TOOLTIPS_INFO *infoPtr) 1368 { 1369 return infoPtr->uNumTools; 1370 } 1371 1372 1373 static LRESULT 1374 TOOLTIPS_GetToolInfoT (const TOOLTIPS_INFO *infoPtr, TTTOOLINFOW *ti, BOOL isW) 1375 { 1376 INT nTool; 1377 HWND hwnd; 1378 1379 if (!ti) return FALSE; 1380 if (ti->cbSize < TTTOOLINFOW_V1_SIZE) 1381 return FALSE; 1382 if (infoPtr->uNumTools == 0) 1383 return FALSE; 1384 1385 nTool = TOOLTIPS_GetToolFromInfoT (infoPtr, ti); 1386 if (nTool == -1) 1387 return FALSE; 1388 1389 TRACE("tool %d\n", nTool); 1390 1391 hwnd = ti->hwnd; 1392 TOOLTIPS_CopyInfoT (infoPtr, nTool, ti, isW); 1393 ti->hwnd = hwnd; 1394 1395 return TRUE; 1396 } 1397 1398 1399 static LRESULT 1400 TOOLTIPS_HitTestT (const TOOLTIPS_INFO *infoPtr, LPTTHITTESTINFOW lptthit, 1401 BOOL isW) 1402 { 1403 INT nTool; 1404 1405 if (lptthit == 0) 1406 return FALSE; 1407 1408 nTool = TOOLTIPS_GetToolFromPoint (infoPtr, lptthit->hwnd, &lptthit->pt); 1409 if (nTool == -1) 1410 return FALSE; 1411 1412 TRACE("tool %d\n", nTool); 1413 1414 /* copy tool data */ 1415 if (lptthit->ti.cbSize >= TTTOOLINFOW_V1_SIZE) 1416 TOOLTIPS_CopyInfoT (infoPtr, nTool, &lptthit->ti, isW); 1417 1418 return TRUE; 1419 } 1420 1421 1422 static LRESULT 1423 TOOLTIPS_NewToolRectT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti) 1424 { 1425 INT nTool; 1426 1427 if (!ti) return 0; 1428 if (ti->cbSize < TTTOOLINFOW_V1_SIZE) 1429 return FALSE; 1430 1431 nTool = TOOLTIPS_GetToolFromInfoT (infoPtr, ti); 1432 1433 TRACE("nTool = %d, rect = %s\n", nTool, wine_dbgstr_rect(&ti->rect)); 1434 1435 if (nTool == -1) return 0; 1436 1437 infoPtr->tools[nTool].rect = ti->rect; 1438 1439 return 0; 1440 } 1441 1442 1443 static inline LRESULT 1444 TOOLTIPS_Pop (TOOLTIPS_INFO *infoPtr) 1445 { 1446 TOOLTIPS_Hide (infoPtr); 1447 1448 return 0; 1449 } 1450 1451 1452 static LRESULT 1453 TOOLTIPS_RelayEvent (TOOLTIPS_INFO *infoPtr, LPMSG lpMsg) 1454 { 1455 POINT pt; 1456 INT nOldTool; 1457 1458 if (!lpMsg) { 1459 ERR("lpMsg == NULL\n"); 1460 return 0; 1461 } 1462 1463 switch (lpMsg->message) { 1464 case WM_LBUTTONDOWN: 1465 case WM_LBUTTONUP: 1466 case WM_MBUTTONDOWN: 1467 case WM_MBUTTONUP: 1468 case WM_RBUTTONDOWN: 1469 case WM_RBUTTONUP: 1470 TOOLTIPS_Hide (infoPtr); 1471 break; 1472 1473 case WM_MOUSEMOVE: 1474 pt.x = (short)LOWORD(lpMsg->lParam); 1475 pt.y = (short)HIWORD(lpMsg->lParam); 1476 nOldTool = infoPtr->nTool; 1477 infoPtr->nTool = TOOLTIPS_GetToolFromPoint(infoPtr, lpMsg->hwnd, 1478 &pt); 1479 TRACE("tool (%p) %d %d %d\n", infoPtr->hwndSelf, nOldTool, 1480 infoPtr->nTool, infoPtr->nCurrentTool); 1481 TRACE("WM_MOUSEMOVE (%p %s)\n", infoPtr->hwndSelf, wine_dbgstr_point(&pt)); 1482 1483 if (infoPtr->nTool != nOldTool) { 1484 if(infoPtr->nTool == -1) { /* Moved out of all tools */ 1485 TOOLTIPS_Hide(infoPtr); 1486 KillTimer(infoPtr->hwndSelf, ID_TIMERLEAVE); 1487 } else if (nOldTool == -1) { /* Moved from outside */ 1488 if(infoPtr->bActive) { 1489 SetTimer(infoPtr->hwndSelf, ID_TIMERSHOW, infoPtr->nInitialTime, 0); 1490 TRACE("timer 1 started\n"); 1491 } 1492 } else { /* Moved from one to another */ 1493 TOOLTIPS_Hide (infoPtr); 1494 KillTimer(infoPtr->hwndSelf, ID_TIMERLEAVE); 1495 if(infoPtr->bActive) { 1496 SetTimer (infoPtr->hwndSelf, ID_TIMERSHOW, infoPtr->nReshowTime, 0); 1497 TRACE("timer 1 started\n"); 1498 } 1499 } 1500 } else if(infoPtr->nCurrentTool != -1) { /* restart autopop */ 1501 KillTimer(infoPtr->hwndSelf, ID_TIMERPOP); 1502 SetTimer(infoPtr->hwndSelf, ID_TIMERPOP, infoPtr->nAutoPopTime, 0); 1503 TRACE("timer 2 restarted\n"); 1504 } else if(infoPtr->nTool != -1 && infoPtr->bActive) { 1505 /* previous show attempt didn't result in tooltip so try again */ 1506 SetTimer(infoPtr->hwndSelf, ID_TIMERSHOW, infoPtr->nInitialTime, 0); 1507 TRACE("timer 1 started\n"); 1508 } 1509 break; 1510 } 1511 1512 return 0; 1513 } 1514 1515 1516 static LRESULT 1517 TOOLTIPS_SetDelayTime (TOOLTIPS_INFO *infoPtr, DWORD duration, INT nTime) 1518 { 1519 switch (duration) { 1520 case TTDT_AUTOMATIC: 1521 if (nTime <= 0) 1522 nTime = GetDoubleClickTime(); 1523 infoPtr->nReshowTime = nTime / 5; 1524 infoPtr->nAutoPopTime = nTime * 10; 1525 infoPtr->nInitialTime = nTime; 1526 break; 1527 1528 case TTDT_RESHOW: 1529 if(nTime < 0) 1530 nTime = GetDoubleClickTime() / 5; 1531 infoPtr->nReshowTime = nTime; 1532 break; 1533 1534 case TTDT_AUTOPOP: 1535 if(nTime < 0) 1536 nTime = GetDoubleClickTime() * 10; 1537 infoPtr->nAutoPopTime = nTime; 1538 break; 1539 1540 case TTDT_INITIAL: 1541 if(nTime < 0) 1542 nTime = GetDoubleClickTime(); 1543 infoPtr->nInitialTime = nTime; 1544 break; 1545 1546 default: 1547 WARN("Invalid duration flag %x\n", duration); 1548 break; 1549 } 1550 1551 return 0; 1552 } 1553 1554 1555 static LRESULT 1556 TOOLTIPS_SetMargin (TOOLTIPS_INFO *infoPtr, const RECT *rect) 1557 { 1558 if (rect) 1559 infoPtr->rcMargin = *rect; 1560 1561 return 0; 1562 } 1563 1564 1565 static inline LRESULT 1566 TOOLTIPS_SetMaxTipWidth (TOOLTIPS_INFO *infoPtr, INT MaxWidth) 1567 { 1568 INT nTemp = infoPtr->nMaxTipWidth; 1569 1570 infoPtr->nMaxTipWidth = MaxWidth; 1571 1572 return nTemp; 1573 } 1574 1575 1576 static inline LRESULT 1577 TOOLTIPS_SetTipBkColor (TOOLTIPS_INFO *infoPtr, COLORREF clrBk) 1578 { 1579 infoPtr->clrBk = clrBk; 1580 1581 return 0; 1582 } 1583 1584 1585 static inline LRESULT 1586 TOOLTIPS_SetTipTextColor (TOOLTIPS_INFO *infoPtr, COLORREF clrText) 1587 { 1588 infoPtr->clrText = clrText; 1589 1590 return 0; 1591 } 1592 1593 1594 static LRESULT 1595 TOOLTIPS_SetTitleT (TOOLTIPS_INFO *infoPtr, UINT_PTR uTitleIcon, LPCWSTR pszTitle, 1596 BOOL isW) 1597 { 1598 UINT size; 1599 1600 TRACE("hwnd = %p, title = %s, icon = %p\n", infoPtr->hwndSelf, debugstr_w(pszTitle), 1601 (void*)uTitleIcon); 1602 1603 Free(infoPtr->pszTitle); 1604 1605 if (pszTitle) 1606 { 1607 if (isW) 1608 { 1609 size = (strlenW(pszTitle)+1)*sizeof(WCHAR); 1610 infoPtr->pszTitle = Alloc(size); 1611 if (!infoPtr->pszTitle) 1612 return FALSE; 1613 memcpy(infoPtr->pszTitle, pszTitle, size); 1614 } 1615 else 1616 { 1617 size = sizeof(WCHAR)*MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pszTitle, -1, NULL, 0); 1618 infoPtr->pszTitle = Alloc(size); 1619 if (!infoPtr->pszTitle) 1620 return FALSE; 1621 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pszTitle, -1, infoPtr->pszTitle, size/sizeof(WCHAR)); 1622 } 1623 } 1624 else 1625 infoPtr->pszTitle = NULL; 1626 1627 if (uTitleIcon <= TTI_ERROR) 1628 infoPtr->hTitleIcon = hTooltipIcons[uTitleIcon]; 1629 else 1630 infoPtr->hTitleIcon = CopyIcon((HICON)uTitleIcon); 1631 1632 TRACE("icon = %p\n", infoPtr->hTitleIcon); 1633 1634 return TRUE; 1635 } 1636 1637 1638 static LRESULT 1639 TOOLTIPS_SetToolInfoT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW) 1640 { 1641 TTTOOL_INFO *toolPtr; 1642 INT nTool; 1643 1644 if (!ti) return 0; 1645 if (ti->cbSize < TTTOOLINFOW_V1_SIZE) 1646 return 0; 1647 1648 nTool = TOOLTIPS_GetToolFromInfoT (infoPtr, ti); 1649 if (nTool == -1) return 0; 1650 1651 TRACE("tool %d\n", nTool); 1652 1653 toolPtr = &infoPtr->tools[nTool]; 1654 1655 /* copy tool data */ 1656 toolPtr->uFlags = ti->uFlags; 1657 toolPtr->hwnd = ti->hwnd; 1658 toolPtr->uId = ti->uId; 1659 toolPtr->rect = ti->rect; 1660 toolPtr->hinst = ti->hinst; 1661 1662 if (IS_INTRESOURCE(ti->lpszText)) { 1663 TRACE("set string id %x\n", LOWORD(ti->lpszText)); 1664 toolPtr->lpszText = ti->lpszText; 1665 } 1666 else { 1667 if (TOOLTIPS_IsCallbackString(ti->lpszText, isW)) 1668 toolPtr->lpszText = LPSTR_TEXTCALLBACKW; 1669 else { 1670 if ( (toolPtr->lpszText) && 1671 !IS_INTRESOURCE(toolPtr->lpszText) ) { 1672 if( toolPtr->lpszText != LPSTR_TEXTCALLBACKW) 1673 Free (toolPtr->lpszText); 1674 toolPtr->lpszText = NULL; 1675 } 1676 if (ti->lpszText) { 1677 if (isW) { 1678 INT len = lstrlenW (ti->lpszText); 1679 toolPtr->lpszText = Alloc ((len+1)*sizeof(WCHAR)); 1680 strcpyW (toolPtr->lpszText, ti->lpszText); 1681 } 1682 else { 1683 INT len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, 1684 -1, NULL, 0); 1685 toolPtr->lpszText = Alloc (len * sizeof(WCHAR)); 1686 MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, -1, 1687 toolPtr->lpszText, len); 1688 } 1689 } 1690 } 1691 } 1692 1693 if (ti->cbSize >= TTTOOLINFOW_V2_SIZE) 1694 toolPtr->lParam = ti->lParam; 1695 1696 if (infoPtr->nCurrentTool == nTool) 1697 { 1698 TOOLTIPS_GetTipText (infoPtr, infoPtr->nCurrentTool, infoPtr->szTipText); 1699 1700 if (infoPtr->szTipText[0] == 0) 1701 TOOLTIPS_Hide(infoPtr); 1702 else 1703 TOOLTIPS_Show (infoPtr, FALSE); 1704 } 1705 1706 return 0; 1707 } 1708 1709 1710 static LRESULT 1711 TOOLTIPS_TrackActivate (TOOLTIPS_INFO *infoPtr, BOOL track_activate, const TTTOOLINFOA *ti) 1712 { 1713 if (track_activate) { 1714 1715 if (!ti) return 0; 1716 if (ti->cbSize < TTTOOLINFOA_V1_SIZE) 1717 return FALSE; 1718 1719 /* activate */ 1720 infoPtr->nTrackTool = TOOLTIPS_GetToolFromInfoT (infoPtr, (const TTTOOLINFOW*)ti); 1721 if (infoPtr->nTrackTool != -1) { 1722 TRACE("activated\n"); 1723 infoPtr->bTrackActive = TRUE; 1724 TOOLTIPS_TrackShow (infoPtr); 1725 } 1726 } 1727 else { 1728 /* deactivate */ 1729 TOOLTIPS_TrackHide (infoPtr); 1730 1731 infoPtr->bTrackActive = FALSE; 1732 infoPtr->nTrackTool = -1; 1733 1734 TRACE("deactivated\n"); 1735 } 1736 1737 return 0; 1738 } 1739 1740 1741 static LRESULT 1742 TOOLTIPS_TrackPosition (TOOLTIPS_INFO *infoPtr, LPARAM coord) 1743 { 1744 infoPtr->xTrackPos = (INT)LOWORD(coord); 1745 infoPtr->yTrackPos = (INT)HIWORD(coord); 1746 1747 if (infoPtr->bTrackActive) { 1748 TRACE("[%d %d]\n", 1749 infoPtr->xTrackPos, infoPtr->yTrackPos); 1750 1751 TOOLTIPS_TrackShow (infoPtr); 1752 } 1753 1754 return 0; 1755 } 1756 1757 1758 static LRESULT 1759 TOOLTIPS_Update (TOOLTIPS_INFO *infoPtr) 1760 { 1761 if (infoPtr->nCurrentTool != -1) 1762 UpdateWindow (infoPtr->hwndSelf); 1763 1764 return 0; 1765 } 1766 1767 1768 static LRESULT 1769 TOOLTIPS_UpdateTipTextT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW) 1770 { 1771 TTTOOL_INFO *toolPtr; 1772 INT nTool; 1773 1774 if (!ti) return 0; 1775 if (ti->cbSize < TTTOOLINFOW_V1_SIZE) 1776 return FALSE; 1777 1778 nTool = TOOLTIPS_GetToolFromInfoT (infoPtr, ti); 1779 if (nTool == -1) 1780 return 0; 1781 1782 TRACE("tool %d\n", nTool); 1783 1784 toolPtr = &infoPtr->tools[nTool]; 1785 1786 /* copy tool text */ 1787 toolPtr->hinst = ti->hinst; 1788 1789 if (IS_INTRESOURCE(ti->lpszText)){ 1790 toolPtr->lpszText = ti->lpszText; 1791 } 1792 else if (ti->lpszText) { 1793 if (TOOLTIPS_IsCallbackString(ti->lpszText, isW)) 1794 toolPtr->lpszText = LPSTR_TEXTCALLBACKW; 1795 else { 1796 if ( (toolPtr->lpszText) && 1797 !IS_INTRESOURCE(toolPtr->lpszText) ) { 1798 if( toolPtr->lpszText != LPSTR_TEXTCALLBACKW) 1799 Free (toolPtr->lpszText); 1800 toolPtr->lpszText = NULL; 1801 } 1802 if (ti->lpszText) { 1803 if (isW) { 1804 INT len = lstrlenW (ti->lpszText); 1805 toolPtr->lpszText = Alloc ((len+1)*sizeof(WCHAR)); 1806 strcpyW (toolPtr->lpszText, ti->lpszText); 1807 } 1808 else { 1809 INT len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, 1810 -1, NULL, 0); 1811 toolPtr->lpszText = Alloc (len * sizeof(WCHAR)); 1812 MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, -1, 1813 toolPtr->lpszText, len); 1814 } 1815 } 1816 } 1817 } 1818 1819 if(infoPtr->nCurrentTool == -1) return 0; 1820 /* force repaint */ 1821 if (infoPtr->bActive) 1822 TOOLTIPS_Show (infoPtr, FALSE); 1823 else if (infoPtr->bTrackActive) 1824 TOOLTIPS_Show (infoPtr, TRUE); 1825 1826 return 0; 1827 } 1828 1829 1830 static LRESULT 1831 TOOLTIPS_Create (HWND hwnd) 1832 { 1833 TOOLTIPS_INFO *infoPtr; 1834 1835 /* allocate memory for info structure */ 1836 infoPtr = Alloc (sizeof(TOOLTIPS_INFO)); 1837 SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr); 1838 1839 /* initialize info structure */ 1840 infoPtr->bActive = TRUE; 1841 infoPtr->bTrackActive = FALSE; 1842 1843 infoPtr->nMaxTipWidth = -1; 1844 infoPtr->nTool = -1; 1845 infoPtr->nCurrentTool = -1; 1846 infoPtr->nTrackTool = -1; 1847 infoPtr->hwndSelf = hwnd; 1848 1849 /* initialize colours and fonts */ 1850 TOOLTIPS_InitSystemSettings(infoPtr); 1851 1852 TOOLTIPS_SetDelayTime(infoPtr, TTDT_AUTOMATIC, 0); 1853 1854 SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOZORDER | SWP_HIDEWINDOW | SWP_NOACTIVATE); 1855 1856 return 0; 1857 } 1858 1859 1860 static LRESULT 1861 TOOLTIPS_Destroy (TOOLTIPS_INFO *infoPtr) 1862 { 1863 TTTOOL_INFO *toolPtr; 1864 UINT i; 1865 1866 /* free tools */ 1867 if (infoPtr->tools) { 1868 for (i = 0; i < infoPtr->uNumTools; i++) { 1869 toolPtr = &infoPtr->tools[i]; 1870 if (toolPtr->lpszText) { 1871 if ( (toolPtr->lpszText != LPSTR_TEXTCALLBACKW) && 1872 !IS_INTRESOURCE(toolPtr->lpszText) ) 1873 { 1874 Free (toolPtr->lpszText); 1875 toolPtr->lpszText = NULL; 1876 } 1877 } 1878 1879 TOOLTIPS_ResetSubclass (toolPtr); 1880 } 1881 1882 Free (infoPtr->tools); 1883 } 1884 1885 /* free title string */ 1886 Free (infoPtr->pszTitle); 1887 /* free title icon if not a standard one */ 1888 if (TOOLTIPS_GetTitleIconIndex(infoPtr->hTitleIcon) > TTI_ERROR) 1889 DeleteObject(infoPtr->hTitleIcon); 1890 1891 /* delete fonts */ 1892 DeleteObject (infoPtr->hFont); 1893 DeleteObject (infoPtr->hTitleFont); 1894 1895 /* free tool tips info data */ 1896 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0); 1897 Free (infoPtr); 1898 1899 return 0; 1900 } 1901 1902 1903 static inline LRESULT 1904 TOOLTIPS_GetFont (const TOOLTIPS_INFO *infoPtr) 1905 { 1906 return (LRESULT)infoPtr->hFont; 1907 } 1908 1909 1910 static LRESULT 1911 TOOLTIPS_MouseMessage (TOOLTIPS_INFO *infoPtr) 1912 { 1913 TOOLTIPS_Hide (infoPtr); 1914 1915 return 0; 1916 } 1917 1918 1919 static LRESULT 1920 TOOLTIPS_NCCreate (HWND hwnd) 1921 { 1922 DWORD dwStyle = GetWindowLongW (hwnd, GWL_STYLE); 1923 DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE); 1924 1925 dwStyle &= ~(WS_CHILD | /*WS_MAXIMIZE |*/ WS_BORDER | WS_DLGFRAME); 1926 dwStyle |= (WS_POPUP | WS_BORDER | WS_CLIPSIBLINGS); 1927 1928 /* WS_BORDER only draws a border round the window rect, not the 1929 * window region, therefore it is useless to us in balloon mode */ 1930 if (dwStyle & TTS_BALLOON) dwStyle &= ~WS_BORDER; 1931 1932 SetWindowLongW (hwnd, GWL_STYLE, dwStyle); 1933 1934 dwExStyle |= WS_EX_TOOLWINDOW; 1935 SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle); 1936 1937 return TRUE; 1938 } 1939 1940 1941 static LRESULT 1942 TOOLTIPS_NCHitTest (const TOOLTIPS_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 1943 { 1944 INT nTool = (infoPtr->bTrackActive) ? infoPtr->nTrackTool : infoPtr->nTool; 1945 1946 TRACE(" nTool=%d\n", nTool); 1947 1948 if ((nTool > -1) && (nTool < infoPtr->uNumTools)) { 1949 if (infoPtr->tools[nTool].uFlags & TTF_TRANSPARENT) { 1950 TRACE("-- in transparent mode\n"); 1951 return HTTRANSPARENT; 1952 } 1953 } 1954 1955 return DefWindowProcW (infoPtr->hwndSelf, WM_NCHITTEST, wParam, lParam); 1956 } 1957 1958 1959 static LRESULT 1960 TOOLTIPS_NotifyFormat (TOOLTIPS_INFO *infoPtr, WPARAM wParam, LPARAM lParam) 1961 { 1962 #ifdef __REACTOS__ 1963 TTTOOL_INFO *toolPtr = infoPtr->tools; 1964 LRESULT nResult; 1965 1966 TRACE("infoPtr=%p wParam=%lx lParam=%p\n", infoPtr, wParam, (PVOID)lParam); 1967 1968 if (lParam == NF_QUERY) { 1969 if (toolPtr->bNotifyUnicode) { 1970 return NFR_UNICODE; 1971 } else { 1972 return NFR_ANSI; 1973 } 1974 } 1975 else if (lParam == NF_REQUERY) { 1976 nResult = SendMessageW (toolPtr->hwnd, WM_NOTIFYFORMAT, 1977 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY); 1978 if (nResult == NFR_ANSI) { 1979 toolPtr->bNotifyUnicode = FALSE; 1980 TRACE(" -- WM_NOTIFYFORMAT returns: NFR_ANSI\n"); 1981 } else if (nResult == NFR_UNICODE) { 1982 toolPtr->bNotifyUnicode = TRUE; 1983 TRACE(" -- WM_NOTIFYFORMAT returns: NFR_UNICODE\n"); 1984 } else { 1985 TRACE (" -- WM_NOTIFYFORMAT returns: error!\n"); 1986 } 1987 return nResult; 1988 } 1989 #else 1990 FIXME ("hwnd=%p wParam=%lx lParam=%lx\n", infoPtr->hwndSelf, wParam, lParam); 1991 #endif 1992 1993 return 0; 1994 } 1995 1996 1997 static LRESULT 1998 TOOLTIPS_Paint (const TOOLTIPS_INFO *infoPtr, HDC hDC) 1999 { 2000 HDC hdc; 2001 PAINTSTRUCT ps; 2002 2003 hdc = (hDC == NULL) ? BeginPaint (infoPtr->hwndSelf, &ps) : hDC; 2004 TOOLTIPS_Refresh (infoPtr, hdc); 2005 if (!hDC) 2006 EndPaint (infoPtr->hwndSelf, &ps); 2007 return 0; 2008 } 2009 2010 2011 static LRESULT 2012 TOOLTIPS_SetFont (TOOLTIPS_INFO *infoPtr, HFONT hFont, BOOL redraw) 2013 { 2014 LOGFONTW lf; 2015 2016 if(!GetObjectW(hFont, sizeof(lf), &lf)) 2017 return 0; 2018 2019 DeleteObject (infoPtr->hFont); 2020 infoPtr->hFont = CreateFontIndirectW(&lf); 2021 2022 DeleteObject (infoPtr->hTitleFont); 2023 lf.lfWeight = FW_BOLD; 2024 infoPtr->hTitleFont = CreateFontIndirectW(&lf); 2025 2026 if (redraw && infoPtr->nCurrentTool != -1) 2027 FIXME("full redraw needed\n"); 2028 2029 return 0; 2030 } 2031 2032 /****************************************************************** 2033 * TOOLTIPS_GetTextLength 2034 * 2035 * This function is called when the tooltip receive a 2036 * WM_GETTEXTLENGTH message. 2037 * 2038 * returns the length, in characters, of the tip text 2039 */ 2040 static inline LRESULT 2041 TOOLTIPS_GetTextLength(const TOOLTIPS_INFO *infoPtr) 2042 { 2043 return strlenW(infoPtr->szTipText); 2044 } 2045 2046 /****************************************************************** 2047 * TOOLTIPS_OnWMGetText 2048 * 2049 * This function is called when the tooltip receive a 2050 * WM_GETTEXT message. 2051 * wParam : specifies the maximum number of characters to be copied 2052 * lParam : is the pointer to the buffer that will receive 2053 * the tip text 2054 * 2055 * returns the number of characters copied 2056 */ 2057 static LRESULT 2058 TOOLTIPS_OnWMGetText (const TOOLTIPS_INFO *infoPtr, WPARAM size, LPWSTR pszText) 2059 { 2060 LRESULT res; 2061 2062 if(!size) 2063 return 0; 2064 2065 res = min(strlenW(infoPtr->szTipText)+1, size); 2066 memcpy(pszText, infoPtr->szTipText, res*sizeof(WCHAR)); 2067 pszText[res-1] = '\0'; 2068 return res-1; 2069 } 2070 2071 static LRESULT 2072 TOOLTIPS_Timer (TOOLTIPS_INFO *infoPtr, INT iTimer) 2073 { 2074 INT nOldTool; 2075 2076 TRACE("timer %d (%p) expired\n", iTimer, infoPtr->hwndSelf); 2077 2078 switch (iTimer) { 2079 case ID_TIMERSHOW: 2080 KillTimer (infoPtr->hwndSelf, ID_TIMERSHOW); 2081 nOldTool = infoPtr->nTool; 2082 if ((infoPtr->nTool = TOOLTIPS_CheckTool (infoPtr, TRUE)) == nOldTool) 2083 TOOLTIPS_Show (infoPtr, FALSE); 2084 break; 2085 2086 case ID_TIMERPOP: 2087 TOOLTIPS_Hide (infoPtr); 2088 break; 2089 2090 case ID_TIMERLEAVE: 2091 nOldTool = infoPtr->nTool; 2092 infoPtr->nTool = TOOLTIPS_CheckTool (infoPtr, FALSE); 2093 TRACE("tool (%p) %d %d %d\n", infoPtr->hwndSelf, nOldTool, 2094 infoPtr->nTool, infoPtr->nCurrentTool); 2095 if (infoPtr->nTool != nOldTool) { 2096 if(infoPtr->nTool == -1) { /* Moved out of all tools */ 2097 TOOLTIPS_Hide(infoPtr); 2098 KillTimer(infoPtr->hwndSelf, ID_TIMERLEAVE); 2099 } else if (nOldTool == -1) { /* Moved from outside */ 2100 ERR("How did this happen?\n"); 2101 } else { /* Moved from one to another */ 2102 TOOLTIPS_Hide (infoPtr); 2103 KillTimer(infoPtr->hwndSelf, ID_TIMERLEAVE); 2104 if(infoPtr->bActive) { 2105 SetTimer (infoPtr->hwndSelf, ID_TIMERSHOW, infoPtr->nReshowTime, 0); 2106 TRACE("timer 1 started!\n"); 2107 } 2108 } 2109 } 2110 break; 2111 2112 default: 2113 ERR("Unknown timer id %d\n", iTimer); 2114 break; 2115 } 2116 return 0; 2117 } 2118 2119 2120 static LRESULT 2121 TOOLTIPS_WinIniChange (TOOLTIPS_INFO *infoPtr) 2122 { 2123 TOOLTIPS_InitSystemSettings (infoPtr); 2124 2125 return 0; 2126 } 2127 2128 2129 static LRESULT CALLBACK 2130 TOOLTIPS_SubclassProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRef) 2131 { 2132 TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr ((HWND)dwRef); 2133 MSG msg; 2134 2135 switch (message) 2136 { 2137 case WM_MOUSEMOVE: 2138 case WM_LBUTTONDOWN: 2139 case WM_LBUTTONUP: 2140 case WM_MBUTTONDOWN: 2141 case WM_MBUTTONUP: 2142 case WM_RBUTTONDOWN: 2143 case WM_RBUTTONUP: 2144 if (infoPtr) 2145 { 2146 msg.hwnd = hwnd; 2147 msg.message = message; 2148 msg.wParam = wParam; 2149 msg.lParam = lParam; 2150 TOOLTIPS_RelayEvent(infoPtr, &msg); 2151 } 2152 break; 2153 case WM_NCDESTROY: 2154 RemoveWindowSubclass(hwnd, TOOLTIPS_SubclassProc, 1); 2155 break; 2156 default: 2157 break; 2158 } 2159 2160 return DefSubclassProc(hwnd, message, wParam, lParam); 2161 } 2162 2163 2164 static LRESULT CALLBACK 2165 TOOLTIPS_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 2166 { 2167 TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd); 2168 2169 TRACE("hwnd=%p msg=%x wparam=%lx lParam=%lx\n", hwnd, uMsg, wParam, lParam); 2170 if (!infoPtr && (uMsg != WM_CREATE) && (uMsg != WM_NCCREATE)) 2171 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 2172 switch (uMsg) 2173 { 2174 case TTM_ACTIVATE: 2175 return TOOLTIPS_Activate (infoPtr, (BOOL)wParam); 2176 2177 case TTM_ADDTOOLA: 2178 case TTM_ADDTOOLW: 2179 return TOOLTIPS_AddToolT (infoPtr, (LPTTTOOLINFOW)lParam, uMsg == TTM_ADDTOOLW); 2180 2181 case TTM_DELTOOLA: 2182 case TTM_DELTOOLW: 2183 return TOOLTIPS_DelToolT (infoPtr, (LPTOOLINFOW)lParam, 2184 uMsg == TTM_DELTOOLW); 2185 case TTM_ENUMTOOLSA: 2186 case TTM_ENUMTOOLSW: 2187 return TOOLTIPS_EnumToolsT (infoPtr, (UINT)wParam, (LPTTTOOLINFOW)lParam, 2188 uMsg == TTM_ENUMTOOLSW); 2189 case TTM_GETBUBBLESIZE: 2190 return TOOLTIPS_GetBubbleSize (infoPtr, (LPTTTOOLINFOW)lParam); 2191 2192 case TTM_GETCURRENTTOOLA: 2193 case TTM_GETCURRENTTOOLW: 2194 return TOOLTIPS_GetCurrentToolT (infoPtr, (LPTTTOOLINFOW)lParam, 2195 uMsg == TTM_GETCURRENTTOOLW); 2196 2197 case TTM_GETDELAYTIME: 2198 return TOOLTIPS_GetDelayTime (infoPtr, (DWORD)wParam); 2199 2200 case TTM_GETMARGIN: 2201 return TOOLTIPS_GetMargin (infoPtr, (LPRECT)lParam); 2202 2203 case TTM_GETMAXTIPWIDTH: 2204 return TOOLTIPS_GetMaxTipWidth (infoPtr); 2205 2206 case TTM_GETTEXTA: 2207 case TTM_GETTEXTW: 2208 return TOOLTIPS_GetTextT (infoPtr, (LPTTTOOLINFOW)lParam, 2209 uMsg == TTM_GETTEXTW); 2210 2211 case TTM_GETTIPBKCOLOR: 2212 return TOOLTIPS_GetTipBkColor (infoPtr); 2213 2214 case TTM_GETTIPTEXTCOLOR: 2215 return TOOLTIPS_GetTipTextColor (infoPtr); 2216 2217 case TTM_GETTOOLCOUNT: 2218 return TOOLTIPS_GetToolCount (infoPtr); 2219 2220 case TTM_GETTOOLINFOA: 2221 case TTM_GETTOOLINFOW: 2222 return TOOLTIPS_GetToolInfoT (infoPtr, (LPTTTOOLINFOW)lParam, 2223 uMsg == TTM_GETTOOLINFOW); 2224 2225 case TTM_HITTESTA: 2226 case TTM_HITTESTW: 2227 return TOOLTIPS_HitTestT (infoPtr, (LPTTHITTESTINFOW)lParam, 2228 uMsg == TTM_HITTESTW); 2229 case TTM_NEWTOOLRECTA: 2230 case TTM_NEWTOOLRECTW: 2231 return TOOLTIPS_NewToolRectT (infoPtr, (LPTTTOOLINFOW)lParam); 2232 2233 case TTM_POP: 2234 return TOOLTIPS_Pop (infoPtr); 2235 2236 case TTM_RELAYEVENT: 2237 return TOOLTIPS_RelayEvent (infoPtr, (LPMSG)lParam); 2238 2239 case TTM_SETDELAYTIME: 2240 return TOOLTIPS_SetDelayTime (infoPtr, (DWORD)wParam, (INT)LOWORD(lParam)); 2241 2242 case TTM_SETMARGIN: 2243 return TOOLTIPS_SetMargin (infoPtr, (LPRECT)lParam); 2244 2245 case TTM_SETMAXTIPWIDTH: 2246 return TOOLTIPS_SetMaxTipWidth (infoPtr, (INT)lParam); 2247 2248 case TTM_SETTIPBKCOLOR: 2249 return TOOLTIPS_SetTipBkColor (infoPtr, (COLORREF)wParam); 2250 2251 case TTM_SETTIPTEXTCOLOR: 2252 return TOOLTIPS_SetTipTextColor (infoPtr, (COLORREF)wParam); 2253 2254 case TTM_SETTITLEA: 2255 case TTM_SETTITLEW: 2256 return TOOLTIPS_SetTitleT (infoPtr, (UINT_PTR)wParam, (LPCWSTR)lParam, 2257 uMsg == TTM_SETTITLEW); 2258 2259 case TTM_SETTOOLINFOA: 2260 case TTM_SETTOOLINFOW: 2261 return TOOLTIPS_SetToolInfoT (infoPtr, (LPTTTOOLINFOW)lParam, 2262 uMsg == TTM_SETTOOLINFOW); 2263 2264 case TTM_TRACKACTIVATE: 2265 return TOOLTIPS_TrackActivate (infoPtr, (BOOL)wParam, (LPTTTOOLINFOA)lParam); 2266 2267 case TTM_TRACKPOSITION: 2268 return TOOLTIPS_TrackPosition (infoPtr, lParam); 2269 2270 case TTM_UPDATE: 2271 return TOOLTIPS_Update (infoPtr); 2272 2273 case TTM_UPDATETIPTEXTA: 2274 case TTM_UPDATETIPTEXTW: 2275 return TOOLTIPS_UpdateTipTextT (infoPtr, (LPTTTOOLINFOW)lParam, 2276 uMsg == TTM_UPDATETIPTEXTW); 2277 2278 case TTM_WINDOWFROMPOINT: 2279 return (LRESULT)WindowFromPoint (*((LPPOINT)lParam)); 2280 2281 case WM_CREATE: 2282 return TOOLTIPS_Create (hwnd); 2283 2284 case WM_DESTROY: 2285 return TOOLTIPS_Destroy (infoPtr); 2286 2287 case WM_ERASEBKGND: 2288 /* we draw the background in WM_PAINT */ 2289 return 0; 2290 2291 case WM_GETFONT: 2292 return TOOLTIPS_GetFont (infoPtr); 2293 2294 case WM_GETTEXT: 2295 return TOOLTIPS_OnWMGetText (infoPtr, wParam, (LPWSTR)lParam); 2296 2297 case WM_GETTEXTLENGTH: 2298 return TOOLTIPS_GetTextLength (infoPtr); 2299 2300 case WM_LBUTTONDOWN: 2301 case WM_LBUTTONUP: 2302 case WM_MBUTTONDOWN: 2303 case WM_MBUTTONUP: 2304 case WM_RBUTTONDOWN: 2305 case WM_RBUTTONUP: 2306 case WM_MOUSEMOVE: 2307 return TOOLTIPS_MouseMessage (infoPtr); 2308 2309 case WM_NCCREATE: 2310 return TOOLTIPS_NCCreate (hwnd); 2311 2312 case WM_NCHITTEST: 2313 return TOOLTIPS_NCHitTest (infoPtr, wParam, lParam); 2314 2315 case WM_NOTIFYFORMAT: 2316 return TOOLTIPS_NotifyFormat (infoPtr, wParam, lParam); 2317 2318 case WM_PRINTCLIENT: 2319 case WM_PAINT: 2320 return TOOLTIPS_Paint (infoPtr, (HDC)wParam); 2321 2322 case WM_SETFONT: 2323 return TOOLTIPS_SetFont (infoPtr, (HFONT)wParam, LOWORD(lParam)); 2324 2325 case WM_SYSCOLORCHANGE: 2326 COMCTL32_RefreshSysColors(); 2327 return 0; 2328 2329 case WM_TIMER: 2330 return TOOLTIPS_Timer (infoPtr, (INT)wParam); 2331 2332 case WM_WININICHANGE: 2333 return TOOLTIPS_WinIniChange (infoPtr); 2334 2335 default: 2336 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg)) 2337 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", 2338 uMsg, wParam, lParam); 2339 return DefWindowProcW (hwnd, uMsg, wParam, lParam); 2340 } 2341 } 2342 2343 2344 VOID 2345 TOOLTIPS_Register (void) 2346 { 2347 WNDCLASSW wndClass; 2348 2349 ZeroMemory (&wndClass, sizeof(WNDCLASSW)); 2350 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS; 2351 wndClass.lpfnWndProc = TOOLTIPS_WindowProc; 2352 wndClass.cbClsExtra = 0; 2353 wndClass.cbWndExtra = sizeof(TOOLTIPS_INFO *); 2354 wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW); 2355 wndClass.hbrBackground = 0; 2356 wndClass.lpszClassName = TOOLTIPS_CLASSW; 2357 2358 RegisterClassW (&wndClass); 2359 2360 hTooltipIcons[TTI_NONE] = NULL; 2361 hTooltipIcons[TTI_INFO] = LoadImageW(COMCTL32_hModule, 2362 (LPCWSTR)MAKEINTRESOURCE(IDI_TT_INFO_SM), IMAGE_ICON, 0, 0, 0); 2363 hTooltipIcons[TTI_WARNING] = LoadImageW(COMCTL32_hModule, 2364 (LPCWSTR)MAKEINTRESOURCE(IDI_TT_WARN_SM), IMAGE_ICON, 0, 0, 0); 2365 hTooltipIcons[TTI_ERROR] = LoadImageW(COMCTL32_hModule, 2366 (LPCWSTR)MAKEINTRESOURCE(IDI_TT_ERROR_SM), IMAGE_ICON, 0, 0, 0); 2367 } 2368 2369 2370 VOID 2371 TOOLTIPS_Unregister (void) 2372 { 2373 int i; 2374 for (i = TTI_INFO; i <= TTI_ERROR; i++) 2375 DestroyIcon(hTooltipIcons[i]); 2376 UnregisterClassW (TOOLTIPS_CLASSW, NULL); 2377 } 2378