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