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