xref: /reactos/dll/win32/comctl32/tooltips.c (revision 845faec4)
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