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