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