comctl32: Replace inline static with static inline.
[wine] / dlls / comctl32 / tooltips.c
1 /*
2  * Tool tip control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 2004 Robert Shearman
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * NOTES
22  *
23  * This code was audited for completeness against the documented features
24  * of Comctl32.dll version 6.0 on Sep. 08, 2004, by Robert Shearman.
25  * 
26  * Unless otherwise noted, we believe this code to be complete, as per
27  * the specification mentioned above.
28  * If you discover missing features or bugs please note them below.
29  * 
30  * TODO:
31  *   - Custom draw support.
32  *   - Animation.
33  *   - Links.
34  *   - Messages:
35  *     o TTM_ADJUSTRECT
36  *     o TTM_GETTITLEA
37  *     o TTM_GETTTILEW
38  *     o TTM_POPUP
39  *   - Styles:
40  *     o TTS_NOANIMATE
41  *     o TTS_NOFADE
42  *     o TTS_CLOSE
43  *
44  * Testing:
45  *   - Run tests using Waite Group Windows95 API Bible Volume 2.
46  *     The second cdrom (chapter 3) contains executables activate.exe,
47  *     curtool.exe, deltool.exe, enumtools.exe, getinfo.exe, getiptxt.exe,
48  *     hittest.exe, needtext.exe, newrect.exe, updtext.exe and winfrpt.exe.
49  *
50  *   Timer logic.
51  *
52  * One important point to remember is that tools don't necessarily get
53  * a WM_MOUSEMOVE once the cursor leaves the tool, an example is when
54  * a tool sets TTF_IDISHWND (i.e. an entire window is a tool) because
55  * here WM_MOUSEMOVEs only get sent when the cursor is inside the
56  * client area.  Therefore the only reliable way to know that the
57  * cursor has left a tool is to keep a timer running and check the
58  * position every time it expires.  This is the role of timer
59  * ID_TIMERLEAVE.
60  *
61  *
62  * On entering a tool (detected in a relayed WM_MOUSEMOVE) we start
63  * ID_TIMERSHOW, if this times out and we're still in the tool we show
64  * the tip.  On showing a tip we start both ID_TIMERPOP and
65  * ID_TIMERLEAVE.  On hiding a tooltip we kill ID_TIMERPOP.
66  * ID_TIMERPOP is restarted on every relayed WM_MOUSEMOVE.  If
67  * ID_TIMERPOP expires the tool is hidden and ID_TIMERPOP is killed.
68  * ID_TIMERLEAVE remains running - this is important as we need to
69  * determine when the cursor leaves the tool.
70  *
71  * When ID_TIMERLEAVE expires or on a relayed WM_MOUSEMOVE if we're
72  * still in the tool do nothing (apart from restart ID_TIMERPOP if
73  * this is a WM_MOUSEMOVE) (ID_TIMERLEAVE remains running).  If we've
74  * left the tool and entered another one then hide the tip and start
75  * ID_TIMERSHOW with time ReshowTime and kill ID_TIMERLEAVE.  If we're
76  * outside all tools hide the tip and kill ID_TIMERLEAVE.  On Relayed
77  * mouse button messages hide the tip but leave ID_TIMERLEAVE running,
78  * this again will let us keep track of when the cursor leaves the
79  * tool.
80  *
81  *
82  * infoPtr->nTool is the tool the mouse was on on the last relayed MM
83  * or timer expiry or -1 if the mouse was not on a tool.
84  *
85  * infoPtr->nCurrentTool is the tool for which the tip is currently
86  * displaying text for or -1 if the tip is not shown.  Actually this
87  * will only ever be infoPtr-nTool or -1, so it could be changed to a
88  * BOOL.
89  *
90  */
91
92
93
94 #include <stdarg.h>
95 #include <string.h>
96
97 #include "windef.h"
98 #include "winbase.h"
99 #include "wine/unicode.h"
100 #include "wingdi.h"
101 #include "winuser.h"
102 #include "winnls.h"
103 #include "commctrl.h"
104 #include "comctl32.h"
105 #include "wine/debug.h"
106
107 WINE_DEFAULT_DEBUG_CHANNEL(tooltips);
108
109 static HICON hTooltipIcons[TTI_ERROR+1];
110
111 typedef struct
112 {
113     UINT      uFlags;
114     HWND      hwnd;
115     BOOL      bNotifyUnicode;
116     UINT_PTR  uId;
117     RECT      rect;
118     HINSTANCE hinst;
119     LPWSTR      lpszText;
120     LPARAM      lParam;
121 } TTTOOL_INFO;
122
123
124 typedef struct
125 {
126     WCHAR      szTipText[INFOTIPSIZE];
127     BOOL     bActive;
128     BOOL     bTrackActive;
129     UINT     uNumTools;
130     COLORREF   clrBk;
131     COLORREF   clrText;
132     HFONT    hFont;
133     HFONT    hTitleFont;
134     INT      xTrackPos;
135     INT      yTrackPos;
136     INT      nMaxTipWidth;
137     INT      nTool; /* tool that mouse was on on last relayed mouse move */
138     INT      nCurrentTool;
139     INT      nTrackTool;
140     INT      nReshowTime;
141     INT      nAutoPopTime;
142     INT      nInitialTime;
143     RECT     rcMargin;
144     BOOL     bToolBelow;
145     LPWSTR   pszTitle;
146     HICON    hTitleIcon;
147
148     TTTOOL_INFO *tools;
149 } TOOLTIPS_INFO;
150
151 #define ID_TIMERSHOW   1    /* show delay timer */
152 #define ID_TIMERPOP    2    /* auto pop timer */
153 #define ID_TIMERLEAVE  3    /* tool leave timer */
154
155
156 #define TOOLTIPS_GetInfoPtr(hWindow) ((TOOLTIPS_INFO *)GetWindowLongPtrW (hWindow, 0))
157
158 /* offsets from window edge to start of text */
159 #define NORMAL_TEXT_MARGIN 2
160 #define BALLOON_TEXT_MARGIN (NORMAL_TEXT_MARGIN+8)
161 /* value used for CreateRoundRectRgn that specifies how much
162  * each corner is curved */
163 #define BALLOON_ROUNDEDNESS 20
164 #define BALLOON_STEMHEIGHT 13
165 #define BALLOON_STEMWIDTH 10
166 #define BALLOON_STEMINDENT 20
167
168 #define BALLOON_ICON_TITLE_SPACING 8 /* horizontal spacing between icon and title */
169 #define BALLOON_TITLE_TEXT_SPACING 8 /* vertical spacing between icon/title and main text */
170 #define ICON_HEIGHT 16
171 #define ICON_WIDTH  16
172
173 static LRESULT CALLBACK
174 TOOLTIPS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRef);
175
176
177 static inline UINT_PTR
178 TOOLTIPS_GetTitleIconIndex(HICON hIcon)
179 {
180     UINT i;
181     for (i = 0; i <= TTI_ERROR; i++)
182         if (hTooltipIcons[i] == hIcon)
183             return i;
184     return (UINT_PTR)hIcon;
185 }
186
187 static void
188 TOOLTIPS_InitSystemSettings (TOOLTIPS_INFO *infoPtr)
189 {
190     NONCLIENTMETRICSW nclm;
191
192     infoPtr->clrBk   = GetSysColor (COLOR_INFOBK);
193     infoPtr->clrText = GetSysColor (COLOR_INFOTEXT);
194
195     DeleteObject (infoPtr->hFont);
196     nclm.cbSize = sizeof(nclm);
197     SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, sizeof(nclm), &nclm, 0);
198     infoPtr->hFont = CreateFontIndirectW (&nclm.lfStatusFont);
199
200     DeleteObject (infoPtr->hTitleFont);
201     nclm.lfStatusFont.lfWeight = FW_BOLD;
202     infoPtr->hTitleFont = CreateFontIndirectW (&nclm.lfStatusFont);
203 }
204
205 static void
206 TOOLTIPS_Refresh (HWND hwnd, HDC hdc)
207 {
208     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr(hwnd);
209     RECT rc;
210     INT oldBkMode;
211     HFONT hOldFont;
212     HBRUSH hBrush;
213     UINT uFlags = DT_EXTERNALLEADING;
214     HRGN hRgn = NULL;
215     DWORD dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
216
217     if (infoPtr->nMaxTipWidth > -1)
218         uFlags |= DT_WORDBREAK;
219     if (GetWindowLongW (hwnd, GWL_STYLE) & TTS_NOPREFIX)
220         uFlags |= DT_NOPREFIX;
221     GetClientRect (hwnd, &rc);
222
223     hBrush = CreateSolidBrush(infoPtr->clrBk);
224
225     oldBkMode = SetBkMode (hdc, TRANSPARENT);
226     SetTextColor (hdc, infoPtr->clrText);
227
228     if (dwStyle & TTS_BALLOON)
229     {
230         /* create a region to store result into */
231         hRgn = CreateRectRgn(0, 0, 0, 0);
232
233         GetWindowRgn(hwnd, hRgn);
234
235         /* fill the background */
236         FillRgn(hdc, hRgn, hBrush);
237         DeleteObject(hBrush);
238         hBrush = NULL;
239     }
240     else
241     {
242         /* fill the background */
243         FillRect(hdc, &rc, hBrush);
244         DeleteObject(hBrush);
245         hBrush = NULL;
246     }
247
248     if ((dwStyle & TTS_BALLOON) || infoPtr->pszTitle)
249     {
250         /* calculate text rectangle */
251         rc.left   += (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.left);
252         rc.top    += (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.top);
253         rc.right  -= (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.right);
254         rc.bottom -= (BALLOON_TEXT_MARGIN + infoPtr->rcMargin.bottom);
255         if(infoPtr->bToolBelow) rc.top += BALLOON_STEMHEIGHT;
256
257         if (infoPtr->pszTitle)
258         {
259             RECT rcTitle = {rc.left, rc.top, rc.right, rc.bottom};
260             int height;
261             BOOL icon_present;
262
263             /* draw icon */
264             icon_present = infoPtr->hTitleIcon && 
265                 DrawIconEx(hdc, rc.left, rc.top, infoPtr->hTitleIcon,
266                            ICON_WIDTH, ICON_HEIGHT, 0, NULL, DI_NORMAL);
267             if (icon_present)
268                 rcTitle.left += ICON_WIDTH + BALLOON_ICON_TITLE_SPACING;
269
270             rcTitle.bottom = rc.top + ICON_HEIGHT;
271
272             /* draw title text */
273             hOldFont = SelectObject (hdc, infoPtr->hTitleFont);
274             height = DrawTextW(hdc, infoPtr->pszTitle, -1, &rcTitle, DT_BOTTOM | DT_SINGLELINE | DT_NOPREFIX);
275             SelectObject (hdc, hOldFont);
276             rc.top += height + BALLOON_TITLE_TEXT_SPACING;
277         }
278     }
279     else
280     {
281         /* calculate text rectangle */
282         rc.left   += (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.left);
283         rc.top    += (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.top);
284         rc.right  -= (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.right);
285         rc.bottom -= (NORMAL_TEXT_MARGIN + infoPtr->rcMargin.bottom);
286     }
287
288     /* draw text */
289     hOldFont = SelectObject (hdc, infoPtr->hFont);
290     DrawTextW (hdc, infoPtr->szTipText, -1, &rc, uFlags);
291     /* be polite and reset the things we changed in the dc */
292     SelectObject (hdc, hOldFont);
293     SetBkMode (hdc, oldBkMode);
294
295     if (dwStyle & TTS_BALLOON)
296     {
297         /* frame region because default window proc doesn't do it */
298         INT width = GetSystemMetrics(SM_CXDLGFRAME) - GetSystemMetrics(SM_CXEDGE);
299         INT height = GetSystemMetrics(SM_CYDLGFRAME) - GetSystemMetrics(SM_CYEDGE);
300
301         hBrush = GetSysColorBrush(COLOR_WINDOWFRAME);
302         FrameRgn(hdc, hRgn, hBrush, width, height);
303     }
304
305     if (hRgn)
306         DeleteObject(hRgn);
307 }
308
309 static void TOOLTIPS_GetDispInfoA(HWND hwnd, TOOLTIPS_INFO *infoPtr, TTTOOL_INFO *toolPtr)
310 {
311     NMTTDISPINFOA ttnmdi;
312
313     /* fill NMHDR struct */
314     ZeroMemory (&ttnmdi, sizeof(NMTTDISPINFOA));
315     ttnmdi.hdr.hwndFrom = hwnd;
316     ttnmdi.hdr.idFrom = toolPtr->uId;
317     ttnmdi.hdr.code = TTN_GETDISPINFOA;
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) {
1387         if (lpToolInfo->cbSize < TTTOOLINFOA_V1_SIZE)
1388             return FALSE;
1389
1390         if (infoPtr->nCurrentTool > -1) {
1391             toolPtr = &infoPtr->tools[infoPtr->nCurrentTool];
1392
1393             /* copy tool data */
1394             lpToolInfo->uFlags   = toolPtr->uFlags;
1395             lpToolInfo->rect     = toolPtr->rect;
1396             lpToolInfo->hinst    = toolPtr->hinst;
1397 /*          lpToolInfo->lpszText = toolPtr->lpszText; */
1398             lpToolInfo->lpszText = NULL;  /* FIXME */
1399
1400             if (lpToolInfo->cbSize >= sizeof(TTTOOLINFOA))
1401                 lpToolInfo->lParam = toolPtr->lParam;
1402
1403             return TRUE;
1404         }
1405         else
1406             return FALSE;
1407     }
1408     else
1409         return (infoPtr->nCurrentTool != -1);
1410 }
1411
1412
1413 static LRESULT
1414 TOOLTIPS_GetCurrentToolW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1415 {
1416     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1417     LPTTTOOLINFOW lpToolInfo = (LPTTTOOLINFOW)lParam;
1418     TTTOOL_INFO *toolPtr;
1419
1420     if (lpToolInfo) {
1421         if (lpToolInfo->cbSize < TTTOOLINFOW_V1_SIZE)
1422             return FALSE;
1423
1424         if (infoPtr->nCurrentTool > -1) {
1425             toolPtr = &infoPtr->tools[infoPtr->nCurrentTool];
1426
1427             /* copy tool data */
1428             lpToolInfo->uFlags   = toolPtr->uFlags;
1429             lpToolInfo->rect     = toolPtr->rect;
1430             lpToolInfo->hinst    = toolPtr->hinst;
1431 /*          lpToolInfo->lpszText = toolPtr->lpszText; */
1432             lpToolInfo->lpszText = NULL;  /* FIXME */
1433
1434             if (lpToolInfo->cbSize >= sizeof(TTTOOLINFOW))
1435                 lpToolInfo->lParam = toolPtr->lParam;
1436
1437             return TRUE;
1438         }
1439         else
1440             return FALSE;
1441     }
1442     else
1443         return (infoPtr->nCurrentTool != -1);
1444 }
1445
1446
1447 static LRESULT
1448 TOOLTIPS_GetDelayTime (HWND hwnd, WPARAM wParam, LPARAM lParam)
1449 {
1450     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1451
1452     switch (wParam) {
1453     case TTDT_RESHOW:
1454         return infoPtr->nReshowTime;
1455
1456     case TTDT_AUTOPOP:
1457         return infoPtr->nAutoPopTime;
1458
1459     case TTDT_INITIAL:
1460     case TTDT_AUTOMATIC: /* Apparently TTDT_AUTOMATIC returns TTDT_INITIAL */
1461         return infoPtr->nInitialTime;
1462
1463     default:
1464         WARN("Invalid wParam %x\n", wParam);
1465         break;
1466     }
1467
1468     return -1;
1469 }
1470
1471
1472 static LRESULT
1473 TOOLTIPS_GetMargin (HWND hwnd, WPARAM wParam, LPARAM lParam)
1474 {
1475     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1476     LPRECT lpRect = (LPRECT)lParam;
1477
1478     lpRect->left   = infoPtr->rcMargin.left;
1479     lpRect->right  = infoPtr->rcMargin.right;
1480     lpRect->bottom = infoPtr->rcMargin.bottom;
1481     lpRect->top    = infoPtr->rcMargin.top;
1482
1483     return 0;
1484 }
1485
1486
1487 static inline LRESULT
1488 TOOLTIPS_GetMaxTipWidth (HWND hwnd, WPARAM wParam, LPARAM lParam)
1489 {
1490     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1491
1492     return infoPtr->nMaxTipWidth;
1493 }
1494
1495
1496 static LRESULT
1497 TOOLTIPS_GetTextA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1498 {
1499     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1500     LPTTTOOLINFOA lpToolInfo = (LPTTTOOLINFOA)lParam;
1501     INT nTool;
1502
1503     if (lpToolInfo == NULL)
1504         return 0;
1505     if (lpToolInfo->cbSize < TTTOOLINFOA_V1_SIZE)
1506         return 0;
1507
1508     nTool = TOOLTIPS_GetToolFromInfoA (infoPtr, lpToolInfo);
1509     if (nTool == -1) return 0;
1510
1511     /* NB this API is broken, there is no way for the app to determine
1512        what size buffer it requires nor a way to specify how long the
1513        one it supplies is.  We'll assume it's up to INFOTIPSIZE */
1514
1515     WideCharToMultiByte(CP_ACP, 0, infoPtr->tools[nTool].lpszText, -1,
1516                         lpToolInfo->lpszText, INFOTIPSIZE, NULL, NULL);
1517
1518     return 0;
1519 }
1520
1521
1522 static LRESULT
1523 TOOLTIPS_GetTextW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1524 {
1525     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1526     LPTTTOOLINFOW lpToolInfo = (LPTTTOOLINFOW)lParam;
1527     INT nTool;
1528
1529     if (lpToolInfo == NULL)
1530         return 0;
1531     if (lpToolInfo->cbSize < TTTOOLINFOW_V1_SIZE)
1532         return 0;
1533
1534     nTool = TOOLTIPS_GetToolFromInfoW (infoPtr, lpToolInfo);
1535     if (nTool == -1) return 0;
1536
1537     strcpyW (lpToolInfo->lpszText, infoPtr->tools[nTool].lpszText);
1538
1539     return 0;
1540 }
1541
1542
1543 static inline LRESULT
1544 TOOLTIPS_GetTipBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
1545 {
1546     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1547     return infoPtr->clrBk;
1548 }
1549
1550
1551 static inline LRESULT
1552 TOOLTIPS_GetTipTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
1553 {
1554     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1555     return infoPtr->clrText;
1556 }
1557
1558
1559 static inline LRESULT
1560 TOOLTIPS_GetToolCount (HWND hwnd, WPARAM wParam, LPARAM lParam)
1561 {
1562     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1563     return infoPtr->uNumTools;
1564 }
1565
1566
1567 static LRESULT
1568 TOOLTIPS_GetToolInfoA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1569 {
1570     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1571     LPTTTOOLINFOA lpToolInfo = (LPTTTOOLINFOA)lParam;
1572     TTTOOL_INFO *toolPtr;
1573     INT nTool;
1574
1575     if (lpToolInfo == NULL)
1576         return FALSE;
1577     if (lpToolInfo->cbSize < TTTOOLINFOA_V1_SIZE)
1578         return FALSE;
1579     if (infoPtr->uNumTools == 0)
1580         return FALSE;
1581
1582     nTool = TOOLTIPS_GetToolFromInfoA (infoPtr, lpToolInfo);
1583     if (nTool == -1)
1584         return FALSE;
1585
1586     TRACE("tool %d\n", nTool);
1587
1588     toolPtr = &infoPtr->tools[nTool];
1589
1590     /* copy tool data */
1591     lpToolInfo->uFlags   = toolPtr->uFlags;
1592     lpToolInfo->rect     = toolPtr->rect;
1593     lpToolInfo->hinst    = toolPtr->hinst;
1594 /*    lpToolInfo->lpszText = toolPtr->lpszText; */
1595     lpToolInfo->lpszText = NULL;  /* FIXME */
1596
1597     if (lpToolInfo->cbSize >= sizeof(TTTOOLINFOA))
1598         lpToolInfo->lParam = toolPtr->lParam;
1599
1600     return TRUE;
1601 }
1602
1603
1604 static LRESULT
1605 TOOLTIPS_GetToolInfoW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1606 {
1607     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1608     LPTTTOOLINFOW lpToolInfo = (LPTTTOOLINFOW)lParam;
1609     TTTOOL_INFO *toolPtr;
1610     INT nTool;
1611
1612     if (lpToolInfo == NULL)
1613         return FALSE;
1614     if (lpToolInfo->cbSize < TTTOOLINFOW_V1_SIZE)
1615         return FALSE;
1616     if (infoPtr->uNumTools == 0)
1617         return FALSE;
1618
1619     nTool = TOOLTIPS_GetToolFromInfoW (infoPtr, lpToolInfo);
1620     if (nTool == -1)
1621         return FALSE;
1622
1623     TRACE("tool %d\n", nTool);
1624
1625     toolPtr = &infoPtr->tools[nTool];
1626
1627     /* copy tool data */
1628     lpToolInfo->uFlags   = toolPtr->uFlags;
1629     lpToolInfo->rect     = toolPtr->rect;
1630     lpToolInfo->hinst    = toolPtr->hinst;
1631 /*    lpToolInfo->lpszText = toolPtr->lpszText; */
1632     lpToolInfo->lpszText = NULL;  /* FIXME */
1633
1634     if (lpToolInfo->cbSize >= sizeof(TTTOOLINFOW))
1635         lpToolInfo->lParam = toolPtr->lParam;
1636
1637     return TRUE;
1638 }
1639
1640
1641 static LRESULT
1642 TOOLTIPS_HitTestA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1643 {
1644     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1645     LPTTHITTESTINFOA lptthit = (LPTTHITTESTINFOA)lParam;
1646     TTTOOL_INFO *toolPtr;
1647     INT nTool;
1648
1649     if (lptthit == 0)
1650         return FALSE;
1651
1652     nTool = TOOLTIPS_GetToolFromPoint (infoPtr, lptthit->hwnd, &lptthit->pt);
1653     if (nTool == -1)
1654         return FALSE;
1655
1656     TRACE("tool %d!\n", nTool);
1657
1658     /* copy tool data */
1659     if (lptthit->ti.cbSize >= sizeof(TTTOOLINFOA)) {
1660         toolPtr = &infoPtr->tools[nTool];
1661
1662         lptthit->ti.uFlags   = toolPtr->uFlags;
1663         lptthit->ti.hwnd     = toolPtr->hwnd;
1664         lptthit->ti.uId      = toolPtr->uId;
1665         lptthit->ti.rect     = toolPtr->rect;
1666         lptthit->ti.hinst    = toolPtr->hinst;
1667 /*      lptthit->ti.lpszText = toolPtr->lpszText; */
1668         lptthit->ti.lpszText = NULL;  /* FIXME */
1669         lptthit->ti.lParam   = toolPtr->lParam;
1670     }
1671
1672     return TRUE;
1673 }
1674
1675
1676 static LRESULT
1677 TOOLTIPS_HitTestW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1678 {
1679     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1680     LPTTHITTESTINFOW lptthit = (LPTTHITTESTINFOW)lParam;
1681     TTTOOL_INFO *toolPtr;
1682     INT nTool;
1683
1684     if (lptthit == 0)
1685         return FALSE;
1686
1687     nTool = TOOLTIPS_GetToolFromPoint (infoPtr, lptthit->hwnd, &lptthit->pt);
1688     if (nTool == -1)
1689         return FALSE;
1690
1691     TRACE("tool %d!\n", nTool);
1692
1693     /* copy tool data */
1694     if (lptthit->ti.cbSize >= sizeof(TTTOOLINFOW)) {
1695         toolPtr = &infoPtr->tools[nTool];
1696
1697         lptthit->ti.uFlags   = toolPtr->uFlags;
1698         lptthit->ti.hwnd     = toolPtr->hwnd;
1699         lptthit->ti.uId      = toolPtr->uId;
1700         lptthit->ti.rect     = toolPtr->rect;
1701         lptthit->ti.hinst    = toolPtr->hinst;
1702 /*      lptthit->ti.lpszText = toolPtr->lpszText; */
1703         lptthit->ti.lpszText = NULL;  /* FIXME */
1704         lptthit->ti.lParam   = toolPtr->lParam;
1705     }
1706
1707     return TRUE;
1708 }
1709
1710
1711 static LRESULT
1712 TOOLTIPS_NewToolRectA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1713 {
1714     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1715     LPTTTOOLINFOA lpti = (LPTTTOOLINFOA)lParam;
1716     INT nTool;
1717
1718     if (lpti == NULL)
1719         return 0;
1720     if (lpti->cbSize < TTTOOLINFOA_V1_SIZE)
1721         return FALSE;
1722
1723     nTool = TOOLTIPS_GetToolFromInfoA (infoPtr, lpti);
1724
1725     TRACE("nTool = %d, rect = %s\n", nTool, wine_dbgstr_rect(&lpti->rect));
1726
1727     if (nTool == -1) return 0;
1728
1729     infoPtr->tools[nTool].rect = lpti->rect;
1730
1731     return 0;
1732 }
1733
1734
1735 static LRESULT
1736 TOOLTIPS_NewToolRectW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1737 {
1738     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1739     LPTTTOOLINFOW lpti = (LPTTTOOLINFOW)lParam;
1740     INT nTool;
1741
1742     if (lpti == NULL)
1743         return 0;
1744     if (lpti->cbSize < TTTOOLINFOW_V1_SIZE)
1745         return FALSE;
1746
1747     nTool = TOOLTIPS_GetToolFromInfoW (infoPtr, lpti);
1748
1749     TRACE("nTool = %d, rect = %s\n", nTool, wine_dbgstr_rect(&lpti->rect));
1750
1751     if (nTool == -1) return 0;
1752
1753     infoPtr->tools[nTool].rect = lpti->rect;
1754
1755     return 0;
1756 }
1757
1758
1759 static inline LRESULT
1760 TOOLTIPS_Pop (HWND hwnd, WPARAM wParam, LPARAM lParam)
1761 {
1762     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1763     TOOLTIPS_Hide (hwnd, infoPtr);
1764
1765     return 0;
1766 }
1767
1768
1769 static LRESULT
1770 TOOLTIPS_RelayEvent (HWND hwnd, WPARAM wParam, LPARAM lParam)
1771 {
1772     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1773     LPMSG lpMsg = (LPMSG)lParam;
1774     POINT pt;
1775     INT nOldTool;
1776
1777     if (lParam == 0) {
1778         ERR("lpMsg == NULL!\n");
1779         return 0;
1780     }
1781
1782     switch (lpMsg->message) {
1783         case WM_LBUTTONDOWN:
1784         case WM_LBUTTONUP:
1785         case WM_MBUTTONDOWN:
1786         case WM_MBUTTONUP:
1787         case WM_RBUTTONDOWN:
1788         case WM_RBUTTONUP:
1789             TOOLTIPS_Hide (hwnd, infoPtr);
1790             break;
1791
1792         case WM_MOUSEMOVE:
1793             pt.x = (short)LOWORD(lpMsg->lParam);
1794             pt.y = (short)HIWORD(lpMsg->lParam);
1795             nOldTool = infoPtr->nTool;
1796             infoPtr->nTool = TOOLTIPS_GetToolFromPoint(infoPtr, lpMsg->hwnd,
1797                                                        &pt);
1798             TRACE("tool (%p) %d %d %d\n", hwnd, nOldTool,
1799                   infoPtr->nTool, infoPtr->nCurrentTool);
1800             TRACE("WM_MOUSEMOVE (%p %d %d)\n", hwnd, pt.x, pt.y);
1801
1802             if (infoPtr->nTool != nOldTool) {
1803                 if(infoPtr->nTool == -1) { /* Moved out of all tools */
1804                     TOOLTIPS_Hide(hwnd, infoPtr);
1805                     KillTimer(hwnd, ID_TIMERLEAVE);
1806                 } else if (nOldTool == -1) { /* Moved from outside */
1807                     if(infoPtr->bActive) {
1808                         SetTimer(hwnd, ID_TIMERSHOW, infoPtr->nInitialTime, 0);
1809                         TRACE("timer 1 started!\n");
1810                     }
1811                 } else { /* Moved from one to another */
1812                     TOOLTIPS_Hide (hwnd, infoPtr);
1813                     KillTimer(hwnd, ID_TIMERLEAVE);
1814                     if(infoPtr->bActive) {
1815                         SetTimer (hwnd, ID_TIMERSHOW, infoPtr->nReshowTime, 0);
1816                         TRACE("timer 1 started!\n");
1817                     }
1818                 }
1819             } else if(infoPtr->nCurrentTool != -1) { /* restart autopop */
1820                 KillTimer(hwnd, ID_TIMERPOP);
1821                 SetTimer(hwnd, ID_TIMERPOP, infoPtr->nAutoPopTime, 0);
1822                 TRACE("timer 2 restarted\n");
1823             } else if(infoPtr->nTool != -1 && infoPtr->bActive) {
1824                 /* previous show attempt didn't result in tooltip so try again */
1825                 SetTimer(hwnd, ID_TIMERSHOW, infoPtr->nInitialTime, 0);
1826                 TRACE("timer 1 started!\n");
1827             }
1828             break;
1829     }
1830
1831     return 0;
1832 }
1833
1834
1835 static LRESULT
1836 TOOLTIPS_SetDelayTime (HWND hwnd, WPARAM wParam, LPARAM lParam)
1837 {
1838     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1839     INT nTime = (INT)LOWORD(lParam);
1840
1841     switch (wParam) {
1842     case TTDT_AUTOMATIC:
1843         if (nTime <= 0)
1844             nTime = GetDoubleClickTime();
1845         infoPtr->nReshowTime    = nTime / 5;
1846         infoPtr->nAutoPopTime   = nTime * 10;
1847         infoPtr->nInitialTime   = nTime;
1848         break;
1849
1850     case TTDT_RESHOW:
1851         if(nTime < 0)
1852             nTime = GetDoubleClickTime() / 5;
1853         infoPtr->nReshowTime = nTime;
1854         break;
1855
1856     case TTDT_AUTOPOP:
1857         if(nTime < 0)
1858             nTime = GetDoubleClickTime() * 10;
1859         infoPtr->nAutoPopTime = nTime;
1860         break;
1861
1862     case TTDT_INITIAL:
1863         if(nTime < 0)
1864             nTime = GetDoubleClickTime();
1865         infoPtr->nInitialTime = nTime;
1866             break;
1867
1868     default:
1869         WARN("Invalid wParam %x\n", wParam);
1870         break;
1871     }
1872
1873     return 0;
1874 }
1875
1876
1877 static LRESULT
1878 TOOLTIPS_SetMargin (HWND hwnd, WPARAM wParam, LPARAM lParam)
1879 {
1880     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1881     LPRECT lpRect = (LPRECT)lParam;
1882
1883     infoPtr->rcMargin.left   = lpRect->left;
1884     infoPtr->rcMargin.right  = lpRect->right;
1885     infoPtr->rcMargin.bottom = lpRect->bottom;
1886     infoPtr->rcMargin.top    = lpRect->top;
1887
1888     return 0;
1889 }
1890
1891
1892 static inline LRESULT
1893 TOOLTIPS_SetMaxTipWidth (HWND hwnd, WPARAM wParam, LPARAM lParam)
1894 {
1895     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1896     INT nTemp = infoPtr->nMaxTipWidth;
1897
1898     infoPtr->nMaxTipWidth = (INT)lParam;
1899
1900     return nTemp;
1901 }
1902
1903
1904 static inline LRESULT
1905 TOOLTIPS_SetTipBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
1906 {
1907     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1908
1909     infoPtr->clrBk = (COLORREF)wParam;
1910
1911     return 0;
1912 }
1913
1914
1915 static inline LRESULT
1916 TOOLTIPS_SetTipTextColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
1917 {
1918     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1919
1920     infoPtr->clrText = (COLORREF)wParam;
1921
1922     return 0;
1923 }
1924
1925
1926 static LRESULT
1927 TOOLTIPS_SetTitleA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1928 {
1929     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1930     LPCSTR pszTitle = (LPCSTR)lParam;
1931     UINT_PTR uTitleIcon = (UINT_PTR)wParam;
1932     UINT size;
1933
1934     TRACE("hwnd = %p, title = %s, icon = %p\n", hwnd, debugstr_a(pszTitle),
1935         (void*)uTitleIcon);
1936
1937     Free(infoPtr->pszTitle);
1938
1939     if (pszTitle)
1940     {
1941         size = sizeof(WCHAR)*MultiByteToWideChar(CP_ACP, 0, pszTitle, -1, NULL, 0);
1942         infoPtr->pszTitle = Alloc(size);
1943         if (!infoPtr->pszTitle)
1944             return FALSE;
1945         MultiByteToWideChar(CP_ACP, 0, pszTitle, -1, infoPtr->pszTitle, size/sizeof(WCHAR));
1946     }
1947     else
1948         infoPtr->pszTitle = NULL;
1949
1950     if (uTitleIcon <= TTI_ERROR)
1951         infoPtr->hTitleIcon = hTooltipIcons[uTitleIcon];
1952     else
1953         infoPtr->hTitleIcon = CopyIcon((HICON)wParam);
1954
1955     return TRUE;
1956 }
1957
1958
1959 static LRESULT
1960 TOOLTIPS_SetTitleW (HWND hwnd, WPARAM wParam, LPARAM lParam)
1961 {
1962     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1963     LPCWSTR pszTitle = (LPCWSTR)lParam;
1964     UINT_PTR uTitleIcon = (UINT_PTR)wParam;
1965     UINT size;
1966
1967     TRACE("hwnd = %p, title = %s, icon = %p\n", hwnd, debugstr_w(pszTitle),
1968         (void*)uTitleIcon);
1969
1970     Free(infoPtr->pszTitle);
1971
1972     if (pszTitle)
1973     {
1974         size = (strlenW(pszTitle)+1)*sizeof(WCHAR);
1975         infoPtr->pszTitle = Alloc(size);
1976         if (!infoPtr->pszTitle)
1977             return FALSE;
1978         memcpy(infoPtr->pszTitle, pszTitle, size);
1979     }
1980     else
1981         infoPtr->pszTitle = NULL;
1982
1983     if (uTitleIcon <= TTI_ERROR)
1984         infoPtr->hTitleIcon = hTooltipIcons[uTitleIcon];
1985     else
1986         infoPtr->hTitleIcon = CopyIcon((HICON)wParam);
1987
1988     TRACE("icon = %p\n", infoPtr->hTitleIcon);
1989
1990     return TRUE;
1991 }
1992
1993
1994 static LRESULT
1995 TOOLTIPS_SetToolInfoA (HWND hwnd, WPARAM wParam, LPARAM lParam)
1996 {
1997     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
1998     LPTTTOOLINFOA lpToolInfo = (LPTTTOOLINFOA)lParam;
1999     TTTOOL_INFO *toolPtr;
2000     INT nTool;
2001
2002     if (lpToolInfo == NULL)
2003         return 0;
2004     if (lpToolInfo->cbSize < TTTOOLINFOA_V1_SIZE)
2005         return 0;
2006
2007     nTool = TOOLTIPS_GetToolFromInfoA (infoPtr, lpToolInfo);
2008     if (nTool == -1) return 0;
2009
2010     TRACE("tool %d\n", nTool);
2011
2012     toolPtr = &infoPtr->tools[nTool];
2013
2014     /* copy tool data */
2015     toolPtr->uFlags = lpToolInfo->uFlags;
2016     toolPtr->hwnd   = lpToolInfo->hwnd;
2017     toolPtr->uId    = lpToolInfo->uId;
2018     toolPtr->rect   = lpToolInfo->rect;
2019     toolPtr->hinst  = lpToolInfo->hinst;
2020
2021     if (IS_INTRESOURCE(lpToolInfo->lpszText)) {
2022         TRACE("set string id %x\n", LOWORD(lpToolInfo->lpszText));
2023         toolPtr->lpszText = (LPWSTR)lpToolInfo->lpszText;
2024     }
2025     else if (lpToolInfo->lpszText) {
2026         if (lpToolInfo->lpszText == LPSTR_TEXTCALLBACKA)
2027             toolPtr->lpszText = LPSTR_TEXTCALLBACKW;
2028         else {
2029             if ( (toolPtr->lpszText) &&
2030                  !IS_INTRESOURCE(toolPtr->lpszText) ) {
2031                 if( toolPtr->lpszText != LPSTR_TEXTCALLBACKW)
2032                     Free (toolPtr->lpszText);
2033                 toolPtr->lpszText = NULL;
2034             }
2035             if (lpToolInfo->lpszText) {
2036                 INT len = MultiByteToWideChar(CP_ACP, 0, lpToolInfo->lpszText,
2037                                               -1, NULL, 0);
2038                 toolPtr->lpszText = Alloc (len * sizeof(WCHAR));
2039                 MultiByteToWideChar(CP_ACP, 0, lpToolInfo->lpszText, -1,
2040                                     toolPtr->lpszText, len);
2041             }
2042         }
2043     }
2044
2045     if (lpToolInfo->cbSize >= sizeof(TTTOOLINFOA))
2046         toolPtr->lParam = lpToolInfo->lParam;
2047
2048     return 0;
2049 }
2050
2051
2052 static LRESULT
2053 TOOLTIPS_SetToolInfoW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2054 {
2055     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2056     LPTTTOOLINFOW lpToolInfo = (LPTTTOOLINFOW)lParam;
2057     TTTOOL_INFO *toolPtr;
2058     INT nTool;
2059
2060     if (lpToolInfo == NULL)
2061         return 0;
2062     if (lpToolInfo->cbSize < TTTOOLINFOW_V1_SIZE)
2063         return 0;
2064
2065     nTool = TOOLTIPS_GetToolFromInfoW (infoPtr, lpToolInfo);
2066     if (nTool == -1) return 0;
2067
2068     TRACE("tool %d\n", nTool);
2069
2070     toolPtr = &infoPtr->tools[nTool];
2071
2072     /* copy tool data */
2073     toolPtr->uFlags = lpToolInfo->uFlags;
2074     toolPtr->hwnd   = lpToolInfo->hwnd;
2075     toolPtr->uId    = lpToolInfo->uId;
2076     toolPtr->rect   = lpToolInfo->rect;
2077     toolPtr->hinst  = lpToolInfo->hinst;
2078
2079     if (IS_INTRESOURCE(lpToolInfo->lpszText)) {
2080         TRACE("set string id %x!\n", LOWORD(lpToolInfo->lpszText));
2081         toolPtr->lpszText = lpToolInfo->lpszText;
2082     }
2083     else {
2084         if (lpToolInfo->lpszText == LPSTR_TEXTCALLBACKW)
2085             toolPtr->lpszText = LPSTR_TEXTCALLBACKW;
2086         else {
2087             if ( (toolPtr->lpszText) &&
2088                  !IS_INTRESOURCE(toolPtr->lpszText) ) {
2089                 if( toolPtr->lpszText != LPSTR_TEXTCALLBACKW)
2090                     Free (toolPtr->lpszText);
2091                 toolPtr->lpszText = NULL;
2092             }
2093             if (lpToolInfo->lpszText) {
2094                 INT len = lstrlenW (lpToolInfo->lpszText);
2095                 toolPtr->lpszText = Alloc ((len+1)*sizeof(WCHAR));
2096                 strcpyW (toolPtr->lpszText, lpToolInfo->lpszText);
2097             }
2098         }
2099     }
2100
2101     if (lpToolInfo->cbSize >= sizeof(TTTOOLINFOW))
2102         toolPtr->lParam = lpToolInfo->lParam;
2103
2104     if (infoPtr->nCurrentTool == nTool)
2105     {
2106         TOOLTIPS_GetTipText (hwnd, infoPtr, infoPtr->nCurrentTool);
2107
2108         if (infoPtr->szTipText[0] == 0)
2109             TOOLTIPS_Hide(hwnd, infoPtr);
2110         else
2111             TOOLTIPS_Show (hwnd, infoPtr);
2112     }
2113
2114     return 0;
2115 }
2116
2117
2118 static LRESULT
2119 TOOLTIPS_TrackActivate (HWND hwnd, WPARAM wParam, LPARAM lParam)
2120 {
2121     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2122
2123     if ((BOOL)wParam) {
2124         LPTTTOOLINFOA lpToolInfo = (LPTTTOOLINFOA)lParam;
2125
2126         if (lpToolInfo == NULL)
2127             return 0;
2128         if (lpToolInfo->cbSize < TTTOOLINFOA_V1_SIZE)
2129             return FALSE;
2130
2131         /* activate */
2132         infoPtr->nTrackTool = TOOLTIPS_GetToolFromInfoA (infoPtr, lpToolInfo);
2133         if (infoPtr->nTrackTool != -1) {
2134             TRACE("activated!\n");
2135             infoPtr->bTrackActive = TRUE;
2136             TOOLTIPS_TrackShow (hwnd, infoPtr);
2137         }
2138     }
2139     else {
2140         /* deactivate */
2141         TOOLTIPS_TrackHide (hwnd, infoPtr);
2142
2143         infoPtr->bTrackActive = FALSE;
2144         infoPtr->nTrackTool = -1;
2145
2146         TRACE("deactivated!\n");
2147     }
2148
2149     return 0;
2150 }
2151
2152
2153 static LRESULT
2154 TOOLTIPS_TrackPosition (HWND hwnd, WPARAM wParam, LPARAM lParam)
2155 {
2156     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2157
2158     infoPtr->xTrackPos = (INT)LOWORD(lParam);
2159     infoPtr->yTrackPos = (INT)HIWORD(lParam);
2160
2161     if (infoPtr->bTrackActive) {
2162         TRACE("[%d %d]\n",
2163                infoPtr->xTrackPos, infoPtr->yTrackPos);
2164
2165         TOOLTIPS_TrackShow (hwnd, infoPtr);
2166     }
2167
2168     return 0;
2169 }
2170
2171
2172 static LRESULT
2173 TOOLTIPS_Update (HWND hwnd, WPARAM wParam, LPARAM lParam)
2174 {
2175     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2176
2177     if (infoPtr->nCurrentTool != -1)
2178         UpdateWindow (hwnd);
2179
2180     return 0;
2181 }
2182
2183
2184 static LRESULT
2185 TOOLTIPS_UpdateTipTextA (HWND hwnd, WPARAM wParam, LPARAM lParam)
2186 {
2187     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2188     LPTTTOOLINFOA lpToolInfo = (LPTTTOOLINFOA)lParam;
2189     TTTOOL_INFO *toolPtr;
2190     INT nTool;
2191
2192     if (lpToolInfo == NULL)
2193         return 0;
2194     if (lpToolInfo->cbSize < TTTOOLINFOA_V1_SIZE)
2195         return FALSE;
2196
2197     nTool = TOOLTIPS_GetToolFromInfoA (infoPtr, lpToolInfo);
2198     if (nTool == -1) return 0;
2199
2200     TRACE("tool %d\n", nTool);
2201
2202     toolPtr = &infoPtr->tools[nTool];
2203
2204     /* copy tool text */
2205     toolPtr->hinst  = lpToolInfo->hinst;
2206
2207     if (IS_INTRESOURCE(lpToolInfo->lpszText)){
2208         toolPtr->lpszText = (LPWSTR)lpToolInfo->lpszText;
2209     }
2210     else if (lpToolInfo->lpszText) {
2211         if (lpToolInfo->lpszText == LPSTR_TEXTCALLBACKA)
2212             toolPtr->lpszText = LPSTR_TEXTCALLBACKW;
2213         else {
2214             if ( (toolPtr->lpszText) &&
2215                  !IS_INTRESOURCE(toolPtr->lpszText) ) {
2216                 if( toolPtr->lpszText != LPSTR_TEXTCALLBACKW)
2217                     Free (toolPtr->lpszText);
2218                 toolPtr->lpszText = NULL;
2219             }
2220             if (lpToolInfo->lpszText) {
2221                 INT len = MultiByteToWideChar(CP_ACP, 0, lpToolInfo->lpszText,
2222                                               -1, NULL, 0);
2223                 toolPtr->lpszText = Alloc (len * sizeof(WCHAR));
2224                 MultiByteToWideChar(CP_ACP, 0, lpToolInfo->lpszText, -1,
2225                                     toolPtr->lpszText, len);
2226             }
2227         }
2228     }
2229
2230     if(infoPtr->nCurrentTool == -1) return 0;
2231     /* force repaint */
2232     if (infoPtr->bActive)
2233         TOOLTIPS_Show (hwnd, infoPtr);
2234     else if (infoPtr->bTrackActive)
2235         TOOLTIPS_TrackShow (hwnd, infoPtr);
2236
2237     return 0;
2238 }
2239
2240
2241 static LRESULT
2242 TOOLTIPS_UpdateTipTextW (HWND hwnd, WPARAM wParam, LPARAM lParam)
2243 {
2244     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2245     LPTTTOOLINFOW lpToolInfo = (LPTTTOOLINFOW)lParam;
2246     TTTOOL_INFO *toolPtr;
2247     INT nTool;
2248
2249     if (lpToolInfo == NULL)
2250         return 0;
2251     if (lpToolInfo->cbSize < TTTOOLINFOW_V1_SIZE)
2252         return FALSE;
2253
2254     nTool = TOOLTIPS_GetToolFromInfoW (infoPtr, lpToolInfo);
2255     if (nTool == -1)
2256         return 0;
2257
2258     TRACE("tool %d\n", nTool);
2259
2260     toolPtr = &infoPtr->tools[nTool];
2261
2262     /* copy tool text */
2263     toolPtr->hinst  = lpToolInfo->hinst;
2264
2265     if (IS_INTRESOURCE(lpToolInfo->lpszText)){
2266         toolPtr->lpszText = lpToolInfo->lpszText;
2267     }
2268     else if (lpToolInfo->lpszText) {
2269         if (lpToolInfo->lpszText == LPSTR_TEXTCALLBACKW)
2270             toolPtr->lpszText = LPSTR_TEXTCALLBACKW;
2271         else {
2272             if ( (toolPtr->lpszText)  &&
2273                  !IS_INTRESOURCE(toolPtr->lpszText) ) {
2274                 if( toolPtr->lpszText != LPSTR_TEXTCALLBACKW)
2275                     Free (toolPtr->lpszText);
2276                 toolPtr->lpszText = NULL;
2277             }
2278             if (lpToolInfo->lpszText) {
2279                 INT len = lstrlenW (lpToolInfo->lpszText);
2280                 toolPtr->lpszText = Alloc ((len+1)*sizeof(WCHAR));
2281                 strcpyW (toolPtr->lpszText, lpToolInfo->lpszText);
2282             }
2283         }
2284     }
2285
2286     if(infoPtr->nCurrentTool == -1) return 0;
2287     /* force repaint */
2288     if (infoPtr->bActive)
2289         TOOLTIPS_Show (hwnd, infoPtr);
2290     else if (infoPtr->bTrackActive)
2291         TOOLTIPS_TrackShow (hwnd, infoPtr);
2292
2293     return 0;
2294 }
2295
2296
2297 static LRESULT
2298 TOOLTIPS_WindowFromPoint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2299 {
2300     return (LRESULT)WindowFromPoint (*((LPPOINT)lParam));
2301 }
2302
2303
2304
2305 static LRESULT
2306 TOOLTIPS_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
2307 {
2308     TOOLTIPS_INFO *infoPtr;
2309
2310     /* allocate memory for info structure */
2311     infoPtr = (TOOLTIPS_INFO *)Alloc (sizeof(TOOLTIPS_INFO));
2312     SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
2313
2314     /* initialize info structure */
2315     infoPtr->bActive = TRUE;
2316     infoPtr->bTrackActive = FALSE;
2317
2318     infoPtr->nMaxTipWidth = -1;
2319     infoPtr->nTool = -1;
2320     infoPtr->nCurrentTool = -1;
2321     infoPtr->nTrackTool = -1;
2322
2323     /* initialize colours and fonts */
2324     TOOLTIPS_InitSystemSettings(infoPtr);
2325
2326     TOOLTIPS_SetDelayTime(hwnd, TTDT_AUTOMATIC, 0L);
2327
2328     SetWindowPos (hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOZORDER | SWP_HIDEWINDOW | SWP_NOACTIVATE);
2329
2330     return 0;
2331 }
2332
2333
2334 static LRESULT
2335 TOOLTIPS_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
2336 {
2337     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2338     TTTOOL_INFO *toolPtr;
2339     UINT i;
2340
2341     /* free tools */
2342     if (infoPtr->tools) {
2343         for (i = 0; i < infoPtr->uNumTools; i++) {
2344             toolPtr = &infoPtr->tools[i];
2345             if (toolPtr->lpszText) {
2346                 if ( (toolPtr->lpszText != LPSTR_TEXTCALLBACKW) &&
2347                      !IS_INTRESOURCE(toolPtr->lpszText) )
2348                 {
2349                     Free (toolPtr->lpszText);
2350                     toolPtr->lpszText = NULL;
2351                 }
2352             }
2353
2354             /* remove subclassing */
2355         if (toolPtr->uFlags & TTF_SUBCLASS) {
2356             if (toolPtr->uFlags & TTF_IDISHWND) {
2357                 RemoveWindowSubclass((HWND)toolPtr->uId, TOOLTIPS_SubclassProc, 1);
2358             }
2359             else {
2360                 RemoveWindowSubclass(toolPtr->hwnd, TOOLTIPS_SubclassProc, 1);
2361             }
2362         }
2363     }
2364         Free (infoPtr->tools);
2365     }
2366
2367     /* free title string */
2368     Free (infoPtr->pszTitle);
2369     /* free title icon if not a standard one */
2370     if (TOOLTIPS_GetTitleIconIndex(infoPtr->hTitleIcon) > TTI_ERROR)
2371         DeleteObject(infoPtr->hTitleIcon);
2372
2373     /* delete fonts */
2374     DeleteObject (infoPtr->hFont);
2375     DeleteObject (infoPtr->hTitleFont);
2376
2377     /* free tool tips info data */
2378     Free (infoPtr);
2379     SetWindowLongPtrW(hwnd, 0, 0);
2380     return 0;
2381 }
2382
2383
2384 static LRESULT
2385 TOOLTIPS_GetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2386 {
2387     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2388
2389     return (LRESULT)infoPtr->hFont;
2390 }
2391
2392
2393 static LRESULT
2394 TOOLTIPS_MouseMessage (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2395 {
2396     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2397
2398     TOOLTIPS_Hide (hwnd, infoPtr);
2399
2400     return 0;
2401 }
2402
2403
2404 static LRESULT
2405 TOOLTIPS_NCCreate (HWND hwnd, WPARAM wParam, LPARAM lParam)
2406 {
2407     DWORD dwStyle = GetWindowLongW (hwnd, GWL_STYLE);
2408     DWORD dwExStyle = GetWindowLongW (hwnd, GWL_EXSTYLE);
2409
2410     dwStyle &= ~(WS_CHILD | /*WS_MAXIMIZE |*/ WS_BORDER | WS_DLGFRAME);
2411     dwStyle |= (WS_POPUP | WS_BORDER | WS_CLIPSIBLINGS);
2412
2413     /* WS_BORDER only draws a border round the window rect, not the
2414      * window region, therefore it is useless to us in balloon mode */
2415     if (dwStyle & TTS_BALLOON) dwStyle &= ~WS_BORDER;
2416
2417     SetWindowLongW (hwnd, GWL_STYLE, dwStyle);
2418
2419     dwExStyle |= WS_EX_TOOLWINDOW;
2420     SetWindowLongW (hwnd, GWL_EXSTYLE, dwExStyle);
2421
2422     return TRUE;
2423 }
2424
2425
2426 static LRESULT
2427 TOOLTIPS_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
2428 {
2429     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2430     INT nTool = (infoPtr->bTrackActive) ? infoPtr->nTrackTool : infoPtr->nTool;
2431
2432     TRACE(" nTool=%d\n", nTool);
2433
2434     if ((nTool > -1) && (nTool < infoPtr->uNumTools)) {
2435         if (infoPtr->tools[nTool].uFlags & TTF_TRANSPARENT) {
2436             TRACE("-- in transparent mode!\n");
2437             return HTTRANSPARENT;
2438         }
2439     }
2440
2441     return DefWindowProcW (hwnd, WM_NCHITTEST, wParam, lParam);
2442 }
2443
2444
2445 static LRESULT
2446 TOOLTIPS_NotifyFormat (HWND hwnd, WPARAM wParam, LPARAM lParam)
2447 {
2448     FIXME ("hwnd=%p wParam=%x lParam=%lx\n", hwnd, wParam, lParam);
2449
2450     return 0;
2451 }
2452
2453
2454 static LRESULT
2455 TOOLTIPS_Paint (HWND hwnd, WPARAM wParam, LPARAM lParam)
2456 {
2457     HDC hdc;
2458     PAINTSTRUCT ps;
2459
2460     hdc = (wParam == 0) ? BeginPaint (hwnd, &ps) : (HDC)wParam;
2461     TOOLTIPS_Refresh (hwnd, hdc);
2462     if (!wParam)
2463         EndPaint (hwnd, &ps);
2464     return 0;
2465 }
2466
2467
2468 static LRESULT
2469 TOOLTIPS_SetFont (HWND hwnd, WPARAM wParam, LPARAM lParam)
2470 {
2471     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2472     LOGFONTW lf;
2473
2474     if(!GetObjectW((HFONT)wParam, sizeof(lf), &lf))
2475         return 0;
2476
2477     DeleteObject (infoPtr->hFont);
2478     infoPtr->hFont = CreateFontIndirectW(&lf);
2479
2480     DeleteObject (infoPtr->hTitleFont);
2481     lf.lfWeight = FW_BOLD;
2482     infoPtr->hTitleFont = CreateFontIndirectW(&lf);
2483
2484     if ((LOWORD(lParam)) & (infoPtr->nCurrentTool != -1)) {
2485         FIXME("full redraw needed!\n");
2486     }
2487
2488     return 0;
2489 }
2490
2491 /******************************************************************
2492  * TOOLTIPS_GetTextLength
2493  *
2494  * This function is called when the tooltip receive a
2495  * WM_GETTEXTLENGTH message.
2496  * wParam : not used
2497  * lParam : not used
2498  *
2499  * returns the length, in characters, of the tip text
2500  */
2501 static LRESULT
2502 TOOLTIPS_GetTextLength(HWND hwnd, WPARAM wParam, LPARAM lParam)
2503 {
2504     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2505     return strlenW(infoPtr->szTipText);
2506 }
2507
2508 /******************************************************************
2509  * TOOLTIPS_OnWMGetText
2510  *
2511  * This function is called when the tooltip receive a
2512  * WM_GETTEXT message.
2513  * wParam : specifies the maximum number of characters to be copied
2514  * lParam : is the pointer to the buffer that will receive
2515  *          the tip text
2516  *
2517  * returns the number of characters copied
2518  */
2519 static LRESULT
2520 TOOLTIPS_OnWMGetText (HWND hwnd, WPARAM wParam, LPARAM lParam)
2521 {
2522     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2523     LRESULT res;
2524     LPWSTR pszText = (LPWSTR)lParam;
2525
2526     if(!infoPtr->szTipText || !wParam)
2527         return 0;
2528
2529     res = min(strlenW(infoPtr->szTipText)+1, wParam);
2530     memcpy(pszText, infoPtr->szTipText, res*sizeof(WCHAR));
2531     pszText[res-1] = '\0';
2532     return res-1;
2533 }
2534
2535 static LRESULT
2536 TOOLTIPS_Timer (HWND hwnd, WPARAM wParam, LPARAM lParam)
2537 {
2538     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2539     INT nOldTool;
2540
2541     TRACE("timer %d (%p) expired!\n", wParam, hwnd);
2542
2543     switch (wParam) {
2544     case ID_TIMERSHOW:
2545         KillTimer (hwnd, ID_TIMERSHOW);
2546         nOldTool = infoPtr->nTool;
2547         if ((infoPtr->nTool = TOOLTIPS_CheckTool (hwnd, TRUE)) == nOldTool)
2548             TOOLTIPS_Show (hwnd, infoPtr);
2549         break;
2550
2551     case ID_TIMERPOP:
2552         TOOLTIPS_Hide (hwnd, infoPtr);
2553         break;
2554
2555     case ID_TIMERLEAVE:
2556         nOldTool = infoPtr->nTool;
2557         infoPtr->nTool = TOOLTIPS_CheckTool (hwnd, FALSE);
2558         TRACE("tool (%p) %d %d %d\n", hwnd, nOldTool,
2559               infoPtr->nTool, infoPtr->nCurrentTool);
2560         if (infoPtr->nTool != nOldTool) {
2561             if(infoPtr->nTool == -1) { /* Moved out of all tools */
2562                 TOOLTIPS_Hide(hwnd, infoPtr);
2563                 KillTimer(hwnd, ID_TIMERLEAVE);
2564             } else if (nOldTool == -1) { /* Moved from outside */
2565                 ERR("How did this happen?\n");
2566             } else { /* Moved from one to another */
2567                 TOOLTIPS_Hide (hwnd, infoPtr);
2568                 KillTimer(hwnd, ID_TIMERLEAVE);
2569                 if(infoPtr->bActive) {
2570                     SetTimer (hwnd, ID_TIMERSHOW, infoPtr->nReshowTime, 0);
2571                     TRACE("timer 1 started!\n");
2572                 }
2573             }
2574         }
2575         break;
2576
2577     default:
2578         ERR("Unknown timer id %d\n", wParam);
2579         break;
2580     }
2581     return 0;
2582 }
2583
2584
2585 static LRESULT
2586 TOOLTIPS_WinIniChange (HWND hwnd, WPARAM wParam, LPARAM lParam)
2587 {
2588     TOOLTIPS_INFO *infoPtr = TOOLTIPS_GetInfoPtr (hwnd);
2589
2590     TOOLTIPS_InitSystemSettings (infoPtr);
2591
2592     return 0;
2593 }
2594
2595
2596 static LRESULT CALLBACK
2597 TOOLTIPS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRef)
2598 {
2599     MSG msg;
2600
2601     switch(uMsg) {
2602     case WM_MOUSEMOVE:
2603     case WM_LBUTTONDOWN:
2604     case WM_LBUTTONUP:
2605     case WM_MBUTTONDOWN:
2606     case WM_MBUTTONUP:
2607     case WM_RBUTTONDOWN:
2608     case WM_RBUTTONUP:
2609         msg.hwnd = hwnd;
2610         msg.message = uMsg;
2611         msg.wParam = wParam;
2612         msg.lParam = lParam;
2613         TOOLTIPS_RelayEvent((HWND)dwRef, 0, (LPARAM)&msg);
2614         break;
2615
2616     default:
2617         break;
2618     }
2619     return DefSubclassProc(hwnd, uMsg, wParam, lParam);
2620 }
2621
2622
2623 static LRESULT CALLBACK
2624 TOOLTIPS_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
2625 {
2626     TRACE("hwnd=%p msg=%x wparam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
2627     if (!TOOLTIPS_GetInfoPtr(hwnd) && (uMsg != WM_CREATE) && (uMsg != WM_NCCREATE))
2628         return DefWindowProcW (hwnd, uMsg, wParam, lParam);
2629     switch (uMsg)
2630     {
2631         case TTM_ACTIVATE:
2632             return TOOLTIPS_Activate (hwnd, wParam, lParam);
2633
2634         case TTM_ADDTOOLA:
2635             return TOOLTIPS_AddToolA (hwnd, wParam, lParam);
2636
2637         case TTM_ADDTOOLW:
2638             return TOOLTIPS_AddToolW (hwnd, wParam, lParam);
2639
2640         case TTM_DELTOOLA:
2641             return TOOLTIPS_DelToolA (hwnd, wParam, lParam);
2642
2643         case TTM_DELTOOLW:
2644             return TOOLTIPS_DelToolW (hwnd, wParam, lParam);
2645
2646         case TTM_ENUMTOOLSA:
2647             return TOOLTIPS_EnumToolsA (hwnd, wParam, lParam);
2648
2649         case TTM_ENUMTOOLSW:
2650             return TOOLTIPS_EnumToolsW (hwnd, wParam, lParam);
2651
2652         case TTM_GETBUBBLESIZE:
2653             return TOOLTIPS_GetBubbleSize (hwnd, wParam, lParam);
2654
2655         case TTM_GETCURRENTTOOLA:
2656             return TOOLTIPS_GetCurrentToolA (hwnd, wParam, lParam);
2657
2658         case TTM_GETCURRENTTOOLW:
2659             return TOOLTIPS_GetCurrentToolW (hwnd, wParam, lParam);
2660
2661         case TTM_GETDELAYTIME:
2662             return TOOLTIPS_GetDelayTime (hwnd, wParam, lParam);
2663
2664         case TTM_GETMARGIN:
2665             return TOOLTIPS_GetMargin (hwnd, wParam, lParam);
2666
2667         case TTM_GETMAXTIPWIDTH:
2668             return TOOLTIPS_GetMaxTipWidth (hwnd, wParam, lParam);
2669
2670         case TTM_GETTEXTA:
2671             return TOOLTIPS_GetTextA (hwnd, wParam, lParam);
2672
2673         case TTM_GETTEXTW:
2674             return TOOLTIPS_GetTextW (hwnd, wParam, lParam);
2675
2676         case TTM_GETTIPBKCOLOR:
2677             return TOOLTIPS_GetTipBkColor (hwnd, wParam, lParam);
2678
2679         case TTM_GETTIPTEXTCOLOR:
2680             return TOOLTIPS_GetTipTextColor (hwnd, wParam, lParam);
2681
2682         case TTM_GETTOOLCOUNT:
2683             return TOOLTIPS_GetToolCount (hwnd, wParam, lParam);
2684
2685         case TTM_GETTOOLINFOA:
2686             return TOOLTIPS_GetToolInfoA (hwnd, wParam, lParam);
2687
2688         case TTM_GETTOOLINFOW:
2689             return TOOLTIPS_GetToolInfoW (hwnd, wParam, lParam);
2690
2691         case TTM_HITTESTA:
2692             return TOOLTIPS_HitTestA (hwnd, wParam, lParam);
2693
2694         case TTM_HITTESTW:
2695             return TOOLTIPS_HitTestW (hwnd, wParam, lParam);
2696
2697         case TTM_NEWTOOLRECTA:
2698             return TOOLTIPS_NewToolRectA (hwnd, wParam, lParam);
2699
2700         case TTM_NEWTOOLRECTW:
2701             return TOOLTIPS_NewToolRectW (hwnd, wParam, lParam);
2702
2703         case TTM_POP:
2704             return TOOLTIPS_Pop (hwnd, wParam, lParam);
2705
2706         case TTM_RELAYEVENT:
2707             return TOOLTIPS_RelayEvent (hwnd, wParam, lParam);
2708
2709         case TTM_SETDELAYTIME:
2710             return TOOLTIPS_SetDelayTime (hwnd, wParam, lParam);
2711
2712         case TTM_SETMARGIN:
2713             return TOOLTIPS_SetMargin (hwnd, wParam, lParam);
2714
2715         case TTM_SETMAXTIPWIDTH:
2716             return TOOLTIPS_SetMaxTipWidth (hwnd, wParam, lParam);
2717
2718         case TTM_SETTIPBKCOLOR:
2719             return TOOLTIPS_SetTipBkColor (hwnd, wParam, lParam);
2720
2721         case TTM_SETTIPTEXTCOLOR:
2722             return TOOLTIPS_SetTipTextColor (hwnd, wParam, lParam);
2723
2724         case TTM_SETTITLEA:
2725             return TOOLTIPS_SetTitleA (hwnd, wParam, lParam);
2726
2727         case TTM_SETTITLEW:
2728             return TOOLTIPS_SetTitleW (hwnd, wParam, lParam);
2729
2730         case TTM_SETTOOLINFOA:
2731             return TOOLTIPS_SetToolInfoA (hwnd, wParam, lParam);
2732
2733         case TTM_SETTOOLINFOW:
2734             return TOOLTIPS_SetToolInfoW (hwnd, wParam, lParam);
2735
2736         case TTM_TRACKACTIVATE:
2737             return TOOLTIPS_TrackActivate (hwnd, wParam, lParam);
2738
2739         case TTM_TRACKPOSITION:
2740             return TOOLTIPS_TrackPosition (hwnd, wParam, lParam);
2741
2742         case TTM_UPDATE:
2743             return TOOLTIPS_Update (hwnd, wParam, lParam);
2744
2745         case TTM_UPDATETIPTEXTA:
2746             return TOOLTIPS_UpdateTipTextA (hwnd, wParam, lParam);
2747
2748         case TTM_UPDATETIPTEXTW:
2749             return TOOLTIPS_UpdateTipTextW (hwnd, wParam, lParam);
2750
2751         case TTM_WINDOWFROMPOINT:
2752             return TOOLTIPS_WindowFromPoint (hwnd, wParam, lParam);
2753
2754
2755         case WM_CREATE:
2756             return TOOLTIPS_Create (hwnd, (LPCREATESTRUCTW)lParam);
2757
2758         case WM_DESTROY:
2759             return TOOLTIPS_Destroy (hwnd, wParam, lParam);
2760
2761         case WM_ERASEBKGND:
2762             /* we draw the background in WM_PAINT */
2763             return 0;
2764
2765         case WM_GETFONT:
2766             return TOOLTIPS_GetFont (hwnd, wParam, lParam);
2767
2768         case WM_GETTEXT:
2769             return TOOLTIPS_OnWMGetText (hwnd, wParam, lParam);
2770
2771         case WM_GETTEXTLENGTH:
2772             return TOOLTIPS_GetTextLength (hwnd, wParam, lParam);
2773
2774         case WM_LBUTTONDOWN:
2775         case WM_LBUTTONUP:
2776         case WM_MBUTTONDOWN:
2777         case WM_MBUTTONUP:
2778         case WM_RBUTTONDOWN:
2779         case WM_RBUTTONUP:
2780         case WM_MOUSEMOVE:
2781             return TOOLTIPS_MouseMessage (hwnd, uMsg, wParam, lParam);
2782
2783         case WM_NCCREATE:
2784             return TOOLTIPS_NCCreate (hwnd, wParam, lParam);
2785
2786         case WM_NCHITTEST:
2787             return TOOLTIPS_NCHitTest (hwnd, wParam, lParam);
2788
2789         case WM_NOTIFYFORMAT:
2790             return TOOLTIPS_NotifyFormat (hwnd, wParam, lParam);
2791
2792         case WM_PRINTCLIENT:
2793         case WM_PAINT:
2794             return TOOLTIPS_Paint (hwnd, wParam, lParam);
2795
2796         case WM_SETFONT:
2797             return TOOLTIPS_SetFont (hwnd, wParam, lParam);
2798
2799         case WM_TIMER:
2800             return TOOLTIPS_Timer (hwnd, wParam, lParam);
2801
2802         case WM_WININICHANGE:
2803             return TOOLTIPS_WinIniChange (hwnd, wParam, lParam);
2804
2805         default:
2806             if ((uMsg >= WM_USER) && (uMsg < WM_APP))
2807                 ERR("unknown msg %04x wp=%08x lp=%08lx\n",
2808                      uMsg, wParam, lParam);
2809             return DefWindowProcW (hwnd, uMsg, wParam, lParam);
2810     }
2811 }
2812
2813
2814 VOID
2815 TOOLTIPS_Register (void)
2816 {
2817     WNDCLASSW wndClass;
2818
2819     ZeroMemory (&wndClass, sizeof(WNDCLASSW));
2820     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
2821     wndClass.lpfnWndProc   = TOOLTIPS_WindowProc;
2822     wndClass.cbClsExtra    = 0;
2823     wndClass.cbWndExtra    = sizeof(TOOLTIPS_INFO *);
2824     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
2825     wndClass.hbrBackground = 0;
2826     wndClass.lpszClassName = TOOLTIPS_CLASSW;
2827
2828     RegisterClassW (&wndClass);
2829
2830     hTooltipIcons[TTI_NONE] = NULL;
2831     hTooltipIcons[TTI_INFO] = LoadImageW(COMCTL32_hModule,
2832         (LPCWSTR)MAKEINTRESOURCE(IDI_TT_INFO_SM), IMAGE_ICON, 0, 0, 0);
2833     hTooltipIcons[TTI_WARNING] = LoadImageW(COMCTL32_hModule,
2834         (LPCWSTR)MAKEINTRESOURCE(IDI_TT_WARN_SM), IMAGE_ICON, 0, 0, 0);
2835     hTooltipIcons[TTI_ERROR] = LoadImageW(COMCTL32_hModule,
2836         (LPCWSTR)MAKEINTRESOURCE(IDI_TT_ERROR_SM), IMAGE_ICON, 0, 0, 0);
2837 }
2838
2839
2840 VOID
2841 TOOLTIPS_Unregister (void)
2842 {
2843     int i;
2844     for (i = TTI_INFO; i <= TTI_ERROR; i++)
2845         DestroyIcon(hTooltipIcons[i]);
2846     UnregisterClassW (TOOLTIPS_CLASSW, NULL);
2847 }