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