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