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