Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3883 lines
114 KiB

  1. #include <ctlspriv.h>
  2. #include <markup.h>
  3. /* current serious issues in markup conversion:
  4. = theme codepath not supported
  5. = notify in callback needs some help */
  6. #define TF_TT 0x10
  7. //#define TTDEBUG
  8. #define ACTIVE 0x10
  9. #define BUTTONISDOWN 0x20
  10. #define BUBBLEUP 0x40
  11. #define VIRTUALBUBBLEUP 0x80 // this is for dead areas so we won't
  12. //wait after moving through dead areas
  13. #define NEEDTEXT 0x100 // Set when processing a TTN_NEEDTEXT, to avoid recursion
  14. // if the app sends us other messages during the callback
  15. #define TRACKMODE 0x01
  16. #define NOFONT (HFONT) 1 // Used to clean up the font
  17. #define MAXTIPSIZE 128
  18. #define INITIALTIPSIZE 80
  19. #define XTEXTOFFSET 2
  20. #define YTEXTOFFSET 1
  21. #define XBALLOONOFFSET 10
  22. #define YBALLOONOFFSET 8
  23. #define BALLOON_X_CORNER 13
  24. #define BALLOON_Y_CORNER 13
  25. #define STEMOFFSET 16
  26. #define STEMHEIGHT 20
  27. #define STEMWIDTH 14
  28. #define MINBALLOONWIDTH 30 // min width for stem to show up
  29. #define TTT_INITIAL 1
  30. #define TTT_RESHOW 2
  31. #define TTT_POP 3
  32. #define TTT_AUTOPOP 4
  33. #define TTT_FADESHOW 5
  34. #define TTT_FADEHIDE 6
  35. #define TIMEBETWEENANIMATE 2000 // 2 Seconds between animates
  36. #define MAX_TIP_CHARACTERS 100
  37. #define TITLEICON_DIST (g_cySmIcon / 2) // Distance from Icon to Title
  38. #define TITLE_INFO_DIST (g_cySmIcon / 3) // Distance from the Title to the Tip Text
  39. #define TT_FADEHIDEDECREMENT 30
  40. #define TT_MAXFADESHOW 255 // Opaque as max....
  41. #define TT_FADESHOWINCREMENT 40
  42. #define TTTT_FADESHOW 30
  43. #define TTTT_FADEHIDE 30
  44. typedef struct
  45. {
  46. UINT cbSize;
  47. UINT uFlags;
  48. HWND hwnd;
  49. UINT uId;
  50. RECT rect;
  51. HINSTANCE hinst;
  52. LPSTR lpszText;
  53. } WIN95TTTOOLINFO;
  54. class CToolTipsMgr : public IMarkupCallback
  55. {
  56. protected:
  57. ~CToolTipsMgr(); // don't let anyone but our ::Release() call delete
  58. public:
  59. CToolTipsMgr();
  60. // IUnknown
  61. STDMETHODIMP QueryInterface(REFIID riid, void** ppv);
  62. STDMETHODIMP_(ULONG) AddRef();
  63. STDMETHODIMP_(ULONG) Release();
  64. // IMarkupCallback
  65. STDMETHODIMP GetState(UINT uState);
  66. STDMETHODIMP Notify(int nCode, int iLink);
  67. STDMETHODIMP InvalidateRect(RECT* prc);
  68. STDMETHODIMP OnCustomDraw(DWORD dwDrawStage, HDC hdc, const RECT *prc, DWORD dwItemSpec, UINT uItemState, LRESULT *pdwResult) { return E_NOTIMPL;};
  69. CCONTROLINFO _ci;
  70. LONG _cRef;
  71. int iNumTools;
  72. int iDelayTime;
  73. int iReshowTime;
  74. int iAutoPopTime;
  75. PTOOLINFO tools;
  76. TOOLINFO *pCurTool;
  77. BOOL fMyFont;
  78. HFONT hFont;
  79. HFONT hFontUnderline;
  80. DWORD dwFlags;
  81. // Timer info;
  82. UINT_PTR idTimer;
  83. POINT pt;
  84. UINT_PTR idtAutoPop;
  85. // Tip title buffer
  86. LPTSTR lpTipTitle;
  87. UINT cchTipTitle;
  88. UINT uTitleBitmap;
  89. int iTitleHeight;
  90. HIMAGELIST himlTitleBitmaps;
  91. POINT ptTrack; // the saved track point from TTM_TRACKPOSITION
  92. BOOL fBkColorSet :1;
  93. BOOL fTextColorSet :1;
  94. BOOL fUnderStem : 1; // true if stem is under the balloon
  95. BOOL fInWindowFromPoint:1; // handling a TTM_WINDOWFROMPOINT message
  96. BOOL fEverShown:1; // Have we ever been shown before?
  97. COLORREF clrTipBk; // This is joeb's idea...he wants it
  98. COLORREF clrTipText; // to be able to _blend_ more, so...
  99. int iMaxTipWidth; // the maximum tip width
  100. RECT rcMargin; // margin offset b/t border and text
  101. int iStemHeight; // balloon mode stem/wedge height
  102. DWORD dwLastDisplayTime; // The tick count taken at the last display. Used for animate puroposes.
  103. HTHEME hTheme;
  104. int iFadeState;
  105. RECT rcClose;
  106. int iStateId;
  107. // Markup additions
  108. IControlMarkup* pMarkup; // A markup we keep around for compatibility (old versions of TOOLINFO)
  109. };
  110. #define TTToolHwnd(pTool) ((pTool->uFlags & TTF_IDISHWND) ? (HWND)pTool->uId : pTool->hwnd)
  111. #define IsTextPtr(lpszText) (((lpszText) != LPSTR_TEXTCALLBACK) && (!IS_INTRESOURCE(lpszText)))
  112. inline IControlMarkup* GetToolMarkup(TOOLINFO *pTool)
  113. {
  114. return (IControlMarkup*)pTool->lpReserved;
  115. }
  116. IControlMarkup* CheckToolMarkup(TOOLINFO *pTool)
  117. {
  118. return (pTool && (pTool->cbSize == TTTOOLINFOW_V3_SIZE) && pTool->lpReserved)
  119. ? GetToolMarkup(pTool) : NULL;
  120. }
  121. IControlMarkup* GetCurToolBestMarkup(CToolTipsMgr *pTtm)
  122. {
  123. return CheckToolMarkup(pTtm->pCurTool) ? GetToolMarkup(pTtm->pCurTool) : pTtm->pMarkup;
  124. }
  125. LRESULT WINAPI ToolTipsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
  126. void TTSetDelayTime(CToolTipsMgr *pTtm, WPARAM wParam, LPARAM lParam);
  127. int TTGetDelayTime(CToolTipsMgr *pTtm, WPARAM wParam);
  128. BOOL ThunkToolInfoAtoW(LPTOOLINFOA lpTiA, LPTOOLINFOW lpTiW, BOOL bThunkText, UINT uiCodePage);
  129. BOOL ThunkToolInfoWtoA(LPTOOLINFOW lpTiW, LPTOOLINFOA lpTiA, UINT uiCodePage);
  130. BOOL ThunkToolTipTextAtoW(LPTOOLTIPTEXTA lpTttA, LPTOOLTIPTEXTW lpTttW, UINT uiCodePage);
  131. BOOL InitToolTipsClass(HINSTANCE hInstance)
  132. {
  133. WNDCLASS wc;
  134. // See if we must register a window class
  135. wc.lpfnWndProc = ToolTipsWndProc;
  136. wc.lpszClassName = c_szSToolTipsClass;
  137. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  138. wc.hIcon = NULL;
  139. wc.lpszMenuName = NULL;
  140. wc.hbrBackground = (HBRUSH)(NULL);
  141. wc.hInstance = hInstance;
  142. wc.style = CS_DBLCLKS | CS_GLOBALCLASS | CS_DROPSHADOW;
  143. wc.cbClsExtra = 0;
  144. wc.cbWndExtra = sizeof(CToolTipsMgr *);
  145. return (RegisterClass(&wc) || (GetLastError() == ERROR_CLASS_ALREADY_EXISTS));
  146. }
  147. // make this a member of CToolTipsMgr when that becomes a C++ class
  148. CToolTipsMgr::CToolTipsMgr() : _cRef(1)
  149. {
  150. }
  151. CToolTipsMgr::~CToolTipsMgr()
  152. {
  153. }
  154. STDMETHODIMP CToolTipsMgr::QueryInterface(REFIID riid, void** ppv)
  155. {
  156. static const QITAB qit[] =
  157. {
  158. QITABENT(CToolTipsMgr, IMarkupCallback),
  159. { 0 },
  160. };
  161. return QISearch(this, qit, riid, ppv);
  162. }
  163. STDMETHODIMP_(ULONG) CToolTipsMgr::AddRef()
  164. {
  165. return InterlockedIncrement(&_cRef);
  166. }
  167. STDMETHODIMP_(ULONG) CToolTipsMgr::Release()
  168. {
  169. ASSERT( 0 != _cRef );
  170. ULONG cRef = InterlockedDecrement(&_cRef);
  171. if ( 0 == cRef )
  172. {
  173. delete this;
  174. }
  175. return cRef;
  176. }
  177. STDMETHODIMP CToolTipsMgr::GetState(UINT uState)
  178. {
  179. HRESULT hr = E_FAIL;
  180. switch (uState)
  181. {
  182. case MARKUPSTATE_FOCUSED:
  183. hr = (GetFocus() == _ci.hwnd) ? S_OK : S_FALSE;
  184. break;
  185. case MARKUPSTATE_ALLOWMARKUP:
  186. if (pCurTool && (pCurTool->uFlags & TTF_PARSELINKS))
  187. {
  188. hr = S_OK;
  189. }
  190. break;
  191. }
  192. return hr;
  193. }
  194. STDMETHODIMP CToolTipsMgr::Notify(int nCode, int iLink)
  195. {
  196. HRESULT hr = S_OK;
  197. if (nCode == MARKUPMESSAGE_WANTFOCUS)
  198. {
  199. // Markup complaining it wants focus due to a mouse click
  200. SetFocus(_ci.hwnd);
  201. }
  202. if (nCode == MARKUPMESSAGE_KEYEXECUTE || nCode == MARKUPMESSAGE_CLICKEXECUTE)
  203. {
  204. // Markup didn't SHELLEXEC a Url and is telling us about it.
  205. // Get manager
  206. CToolTipsMgr *pTtm = this;
  207. // Send a notify back to the parent window
  208. NMLINK nm = {0};
  209. nm.hdr.hwndFrom = _ci.hwnd;
  210. nm.hdr.idFrom = (UINT_PTR)GetWindowLong(_ci.hwnd, GWL_ID);
  211. nm.hdr.code = TTN_LINKCLICK;
  212. // Fill LITEM with szID, szUrl, and iLink
  213. DWORD dwCchID = ARRAYSIZE(nm.item.szID);
  214. DWORD dwCchURL = ARRAYSIZE(nm.item.szUrl);
  215. nm.item.iLink = iLink;
  216. GetCurToolBestMarkup(pTtm)->GetLinkText(iLink, MARKUPLINKTEXT_ID, nm.item.szID, &dwCchID);
  217. GetCurToolBestMarkup(pTtm)->GetLinkText(iLink, MARKUPLINKTEXT_URL, nm.item.szUrl, &dwCchURL);
  218. if (pTtm->pCurTool)
  219. {
  220. hr = (HRESULT) SendMessage(pTtm->pCurTool->hwnd, WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm);
  221. }
  222. }
  223. return hr;
  224. }
  225. STDMETHODIMP CToolTipsMgr::InvalidateRect(RECT* prc)
  226. {
  227. return S_OK;
  228. }
  229. /* _ G E T H C U R S O R P D Y 3 */
  230. /*-------------------------------------------------------------------------
  231. %%Function: _GetHcursorPdy3
  232. %%Contact: migueldc
  233. With the new mouse drivers that allow you to customize the mouse
  234. pointer size, GetSystemMetrics returns useless values regarding
  235. that pointer size.
  236. Assumptions:
  237. 1. The pointer's width is equal to its height. We compute
  238. its height and infer its width.
  239. 2. The pointer's leftmost pixel is located in the 0th column
  240. of the bitmap describing it.
  241. 3. The pointer's topmost pixel is located in the 0th row
  242. of the bitmap describing it.
  243. This function looks at the mouse pointer bitmap,
  244. to find out the height of the mouse pointer (not returned),
  245. the vertical distance between the cursor's hot spot and
  246. the cursor's lowest visible pixel (pdyBottom),
  247. the horizontal distance between the hot spot and the pointer's
  248. left edge (pdxLeft) annd the horizontal distance between the
  249. hot spot and the pointer's right edge (pdxRight).
  250. -------------------------------------------------------------------------*/
  251. typedef WORD CURMASK;
  252. #define _BitSizeOf(x) (sizeof(x)*8)
  253. void _GetHcursorPdy3(int *pdxRight, int *pdyBottom)
  254. {
  255. int i;
  256. int iXOR = 0;
  257. int dy, dx;
  258. CURMASK CurMask[16*8];
  259. ICONINFO iconinfo;
  260. BITMAP bm;
  261. *pdyBottom = 0;
  262. *pdxRight = 0;
  263. HCURSOR hCursor = GetCursor();
  264. if (hCursor)
  265. {
  266. *pdyBottom = 16; //best guess
  267. *pdxRight = 16; //best guess
  268. if (!GetIconInfo(hCursor, &iconinfo))
  269. return;
  270. if (!GetObject(iconinfo.hbmMask, sizeof(bm), (LPSTR)&bm))
  271. return;
  272. if (!GetBitmapBits(iconinfo.hbmMask, sizeof(CurMask), CurMask))
  273. return;
  274. i = (int)(bm.bmWidth * bm.bmHeight / _BitSizeOf(CURMASK));
  275. if (!iconinfo.hbmColor)
  276. {
  277. // if no color bitmap, then the hbmMask is a double height bitmap
  278. // with the cursor and the mask stacked.
  279. iXOR = i - 1;
  280. i /= 2;
  281. }
  282. if (i >= sizeof(CurMask)) i = sizeof(CurMask) -1;
  283. if (iXOR >= sizeof(CurMask)) iXOR = 0;
  284. for (i--; i >= 0; i--)
  285. {
  286. if (CurMask[i] != 0xFFFF || (iXOR && (CurMask[iXOR--] != 0)))
  287. break;
  288. }
  289. if (iconinfo.hbmColor)
  290. DeleteObject(iconinfo.hbmColor);
  291. if (iconinfo.hbmMask)
  292. DeleteObject(iconinfo.hbmMask);
  293. // Compute the pointer height
  294. dy = (i + 1) * _BitSizeOf(CURMASK) / (int)bm.bmWidth;
  295. dx = (i + 1) * _BitSizeOf(CURMASK) / (int)bm.bmHeight;
  296. // Compute the distance between the pointer's lowest, left, rightmost
  297. // pixel and the HotSpotspot
  298. *pdyBottom = dy - (int)iconinfo.yHotspot;
  299. *pdxRight = dx - (int)iconinfo.xHotspot;
  300. }
  301. }
  302. BOOL FadeEnabled()
  303. {
  304. BOOL fFadeTurnedOn = FALSE;
  305. BOOL fAnimate = TRUE;
  306. SystemParametersInfo(SPI_GETTOOLTIPANIMATION, 0, &fAnimate, 0);
  307. SystemParametersInfo(SPI_GETTOOLTIPFADE, 0, &fFadeTurnedOn, 0);
  308. return fFadeTurnedOn && fAnimate;
  309. }
  310. // this returns the values in work area coordinates because
  311. // that's what set window placement uses
  312. void _GetCursorLowerLeft(int *piLeft, int *piTop, int *piWidth, int *piHeight)
  313. {
  314. DWORD dwPos;
  315. dwPos = GetMessagePos();
  316. _GetHcursorPdy3(piWidth, piHeight);
  317. *piLeft = GET_X_LPARAM(dwPos);
  318. *piTop = GET_Y_LPARAM(dwPos) + *piHeight;
  319. }
  320. void ToolTips_NewFont(CToolTipsMgr *pTtm, HFONT hFont)
  321. {
  322. if (pTtm->fMyFont && pTtm->hFont)
  323. {
  324. DeleteObject(pTtm->hFont);
  325. pTtm->fMyFont = FALSE;
  326. }
  327. if (!hFont)
  328. {
  329. hFont = CCCreateStatusFont();
  330. pTtm->fMyFont = TRUE;
  331. if (!hFont)
  332. {
  333. hFont = g_hfontSystem;
  334. pTtm->fMyFont = FALSE;
  335. }
  336. }
  337. pTtm->hFont = hFont;
  338. if (pTtm->hFontUnderline)
  339. {
  340. DeleteObject(pTtm->hFontUnderline);
  341. pTtm->hFontUnderline = NULL;
  342. }
  343. if (hFont != NOFONT)
  344. {
  345. pTtm->hFontUnderline = CCCreateUnderlineFont(hFont);
  346. }
  347. pTtm->_ci.uiCodePage = GetCodePageForFont(hFont);
  348. }
  349. BOOL ChildOfActiveWindow(HWND hwndChild)
  350. {
  351. HWND hwnd = hwndChild;
  352. HWND hwndActive = GetForegroundWindow();
  353. while (hwnd)
  354. {
  355. if (hwnd == hwndActive)
  356. return TRUE;
  357. else
  358. hwnd = GetParent(hwnd);
  359. }
  360. return FALSE;
  361. }
  362. void PopBubble2(CToolTipsMgr *pTtm, BOOL fForce)
  363. {
  364. BOOL fFadeTurnedOn = FadeEnabled();
  365. // Can't be pressed if we're down...
  366. pTtm->iStateId = TTCS_NORMAL;
  367. // we're at least waiting to show;
  368. DebugMsg(TF_TT, TEXT("PopBubble (killing timer)"));
  369. if (pTtm->idTimer)
  370. {
  371. KillTimer(pTtm->_ci.hwnd, pTtm->idTimer);
  372. pTtm->idTimer = 0;
  373. }
  374. if (pTtm->idtAutoPop)
  375. {
  376. KillTimer(pTtm->_ci.hwnd, pTtm->idtAutoPop);
  377. pTtm->idtAutoPop = 0;
  378. }
  379. if (IsWindowVisible(pTtm->_ci.hwnd) && pTtm->pCurTool)
  380. {
  381. NMHDR nmhdr;
  382. nmhdr.hwndFrom = pTtm->_ci.hwnd;
  383. nmhdr.idFrom = pTtm->pCurTool->uId;
  384. nmhdr.code = TTN_POP;
  385. SendNotifyEx(pTtm->pCurTool->hwnd, (HWND)-1,
  386. TTN_POP, &nmhdr,
  387. (pTtm->pCurTool->uFlags & TTF_UNICODE) ? 1 : 0);
  388. }
  389. KillTimer(pTtm->_ci.hwnd, TTT_POP);
  390. KillTimer(pTtm->_ci.hwnd, TTT_FADEHIDE);
  391. KillTimer(pTtm->_ci.hwnd, TTT_FADESHOW);
  392. if (pTtm->iFadeState > 0 && !fForce && fFadeTurnedOn)
  393. {
  394. SetTimer(pTtm->_ci.hwnd, TTT_FADEHIDE, TTTT_FADEHIDE, NULL);
  395. }
  396. else
  397. {
  398. ShowWindow(pTtm->_ci.hwnd, SW_HIDE);
  399. }
  400. pTtm->dwFlags &= ~(BUBBLEUP|VIRTUALBUBBLEUP);
  401. pTtm->pCurTool = NULL;
  402. }
  403. void NEAR PASCAL PopBubble(CToolTipsMgr *pTtm)
  404. {
  405. PopBubble2(pTtm, FALSE);
  406. }
  407. CToolTipsMgr *ToolTipsMgrCreate(HWND hwnd, CREATESTRUCT* lpCreateStruct)
  408. {
  409. CToolTipsMgr *pTtm = new CToolTipsMgr;
  410. if (pTtm)
  411. {
  412. CIInitialize(&pTtm->_ci, hwnd, lpCreateStruct);
  413. // LPTR zeros the rest of the struct for us
  414. TTSetDelayTime(pTtm, TTDT_AUTOMATIC, (LPARAM)-1);
  415. pTtm->dwFlags = ACTIVE;
  416. pTtm->iMaxTipWidth = -1;
  417. pTtm->_ci.fDPIAware = TRUE;
  418. // These are the defaults (straight from cutils.c),
  419. // but you can always change them...
  420. pTtm->clrTipBk = g_clrInfoBk;
  421. pTtm->clrTipText = g_clrInfoText;
  422. }
  423. return pTtm;
  424. }
  425. void TTSetTimer(CToolTipsMgr *pTtm, int id)
  426. {
  427. int iDelayTime = 0;
  428. if (pTtm->idTimer)
  429. {
  430. KillTimer(pTtm->_ci.hwnd, pTtm->idTimer);
  431. }
  432. switch (id)
  433. {
  434. case TTT_POP:
  435. case TTT_RESHOW:
  436. iDelayTime = pTtm->iReshowTime;
  437. if (iDelayTime < 0)
  438. iDelayTime = GetDoubleClickTime() / 5;
  439. break;
  440. case TTT_INITIAL:
  441. iDelayTime = pTtm->iDelayTime;
  442. if (iDelayTime < 0)
  443. iDelayTime = GetDoubleClickTime();
  444. break;
  445. case TTT_AUTOPOP:
  446. iDelayTime = pTtm->iAutoPopTime;
  447. if (iDelayTime < 0)
  448. iDelayTime = GetDoubleClickTime() * 10;
  449. pTtm->idtAutoPop = SetTimer(pTtm->_ci.hwnd, id, iDelayTime, NULL);
  450. return;
  451. }
  452. DebugMsg(TF_TT, TEXT("TTSetTimer %d for %d ms"), id, iDelayTime);
  453. if (SetTimer(pTtm->_ci.hwnd, id, iDelayTime, NULL) &&
  454. (id != TTT_POP))
  455. {
  456. pTtm->idTimer = id;
  457. GetCursorPos(&pTtm->pt);
  458. }
  459. }
  460. //
  461. // Double-hack to solve blinky-tooltips problems.
  462. //
  463. // fInWindowFromPoint makes us temporarily transparent.
  464. //
  465. // Clear the WS_DISABLED flag to trick USER into hit-testing against us.
  466. // USER by default skips disabled windows. Restore the flag afterwards.
  467. // VB in particular likes to run around disabling all top-level windows
  468. // owned by his process.
  469. //
  470. // We must use SetWindowBits() instead of EnableWindow() because
  471. // EnableWindow() will mess with the capture and focus.
  472. //
  473. HWND TTWindowFromPoint(CToolTipsMgr *pTtm, LPPOINT ppt)
  474. {
  475. HWND hwnd;
  476. DWORD dwStyle;
  477. dwStyle = SetWindowBits(pTtm->_ci.hwnd, GWL_STYLE, WS_DISABLED, 0);
  478. pTtm->fInWindowFromPoint = TRUE;
  479. hwnd = (HWND)SendMessage(pTtm->_ci.hwnd, TTM_WINDOWFROMPOINT, 0, (LPARAM)ppt);
  480. pTtm->fInWindowFromPoint = FALSE;
  481. SetWindowBits(pTtm->_ci.hwnd, GWL_STYLE, WS_DISABLED, dwStyle);
  482. return hwnd;
  483. }
  484. BOOL ToolHasMoved(CToolTipsMgr *pTtm)
  485. {
  486. // this is in case Raymond pulls something sneaky like moving
  487. // the tool out from underneath the cursor.
  488. RECT rc;
  489. TOOLINFO *pTool = pTtm->pCurTool;
  490. if (!pTool)
  491. return TRUE;
  492. HWND hwnd = TTToolHwnd(pTool);
  493. // if the window is no longer visible, or is no long a child
  494. // of the active (without the always tip flag)
  495. // also check window at point to ensure that the window isn't covered
  496. if (IsWindowVisible(hwnd) &&
  497. ((pTtm->_ci.style & TTS_ALWAYSTIP) || ChildOfActiveWindow(hwnd)) &&
  498. (hwnd == TTWindowFromPoint(pTtm, &pTtm->pt)))
  499. {
  500. GetWindowRect(hwnd, &rc);
  501. if (PtInRect(&rc, pTtm->pt))
  502. return FALSE;
  503. }
  504. return TRUE;
  505. }
  506. BOOL MouseHasMoved(CToolTipsMgr *pTtm)
  507. {
  508. POINT pt;
  509. GetCursorPos(&pt);
  510. return ((pt.x != pTtm->pt.x) || (pt.y != pTtm->pt.y));
  511. }
  512. TOOLINFO *FindTool(CToolTipsMgr *pTtm, TOOLINFO *pToolInfo)
  513. {
  514. if (!(pTtm && pToolInfo))
  515. {
  516. DebugMsg(TF_ALWAYS, TEXT("FindTool passed invalid argumnet. Exiting..."));
  517. return NULL;
  518. }
  519. if (pToolInfo->cbSize > sizeof(TOOLINFO))
  520. return NULL;
  521. TOOLINFO *pTool = NULL;
  522. // you can pass in an index or a toolinfo descriptor
  523. if (IS_INTRESOURCE(pToolInfo))
  524. {
  525. int i = PtrToUlong(pToolInfo);
  526. if (i < pTtm->iNumTools)
  527. {
  528. pTool = &pTtm->tools[i];
  529. return pTool;
  530. }
  531. }
  532. else
  533. {
  534. for (int i = 0; i < pTtm->iNumTools; i++)
  535. {
  536. pTool = &pTtm->tools[i];
  537. if ((pTool->hwnd == pToolInfo->hwnd) &&
  538. (pTool->uId == pToolInfo->uId))
  539. {
  540. return pTool;
  541. }
  542. }
  543. }
  544. return NULL;
  545. }
  546. LRESULT WINAPI TTSubclassProc(HWND hwnd, UINT message, WPARAM wParam,
  547. LPARAM lParam, UINT_PTR uIdSubclass, ULONG_PTR dwRefData);
  548. void TTUnsubclassHwnd(HWND hwnd, HWND hwndTT, BOOL fForce)
  549. {
  550. ULONG_PTR dwRefs;
  551. if (IsWindow(hwnd) &&
  552. GetWindowSubclass(hwnd, TTSubclassProc, (UINT_PTR)hwndTT, (PULONG_PTR) &dwRefs))
  553. {
  554. if (!fForce && (dwRefs > 1))
  555. SetWindowSubclass(hwnd, TTSubclassProc, (UINT_PTR)hwndTT, dwRefs - 1);
  556. else
  557. RemoveWindowSubclass(hwnd, TTSubclassProc, (UINT_PTR)hwndTT);
  558. }
  559. }
  560. LRESULT WINAPI TTSubclassProc(HWND hwnd, UINT message, WPARAM wParam,
  561. LPARAM lParam, UINT_PTR uIdSubclass, ULONG_PTR dwRefData)
  562. {
  563. if (((message >= WM_MOUSEFIRST) && (message <= WM_MOUSELAST)) ||
  564. (message == WM_NCMOUSEMOVE))
  565. {
  566. RelayToToolTips((HWND)uIdSubclass, hwnd, message, wParam, lParam);
  567. }
  568. else if (message == WM_NCDESTROY)
  569. {
  570. TTUnsubclassHwnd(hwnd, (HWND)uIdSubclass, TRUE);
  571. }
  572. return DefSubclassProc(hwnd, message, wParam, lParam);
  573. }
  574. void TTSubclassHwnd(TOOLINFO *pTool, HWND hwndTT)
  575. {
  576. HWND hwnd = TTToolHwnd(pTool);
  577. if (IsWindow(hwnd))
  578. {
  579. ULONG_PTR dwRefs;
  580. GetWindowSubclass(hwnd, TTSubclassProc, (UINT_PTR)hwndTT, &dwRefs);
  581. SetWindowSubclass(hwnd, TTSubclassProc, (UINT_PTR)hwndTT, dwRefs + 1);
  582. }
  583. }
  584. void TTSetTipText(TOOLINFO *pTool, LPTSTR lpszText)
  585. {
  586. // if it wasn't alloc'ed before, set it to NULL now so we'll alloc it
  587. // otherwise, don't touch it and it will be realloced
  588. if (!IsTextPtr(pTool->lpszText))
  589. {
  590. pTool->lpszText = NULL;
  591. }
  592. if (IsTextPtr(lpszText))
  593. {
  594. DebugMsg(TF_TT, TEXT("TTSetTipText %s"), lpszText);
  595. Str_Set(&pTool->lpszText, lpszText);
  596. }
  597. else
  598. {
  599. // if it was alloc'ed before free it now.
  600. Str_Set(&pTool->lpszText, NULL);
  601. pTool->lpszText = lpszText;
  602. }
  603. }
  604. void CopyTool(TOOLINFO *pTo, TOOLINFO *pFrom)
  605. {
  606. ASSERT(pFrom->cbSize <= sizeof(TOOLINFO));
  607. memcpy(pTo, pFrom, pFrom->cbSize);
  608. pTo->lpszText = NULL; // make sure these are zero
  609. pTo->lpReserved = NULL;
  610. }
  611. LRESULT AddTool(CToolTipsMgr *pTtm, TOOLINFO *pToolInfo)
  612. {
  613. if (pToolInfo->cbSize > sizeof(TOOLINFO))
  614. {
  615. ASSERT(0);
  616. return 0; // app bug, bad struct size
  617. }
  618. // on failure to alloc do nothing.
  619. TOOLINFO *ptoolsNew = (TOOLINFO *)CCLocalReAlloc(pTtm->tools, sizeof(TOOLINFO) * (pTtm->iNumTools + 1));
  620. if (!ptoolsNew)
  621. return 0;
  622. if (pTtm->tools)
  623. {
  624. // realloc could have moved stuff around. repoint pCurTool
  625. if (pTtm->pCurTool)
  626. {
  627. pTtm->pCurTool = ((PTOOLINFO)ptoolsNew) + (pTtm->pCurTool - pTtm->tools);
  628. }
  629. }
  630. pTtm->tools = ptoolsNew;
  631. TOOLINFO *pTool = &pTtm->tools[pTtm->iNumTools];
  632. pTtm->iNumTools++;
  633. CopyTool(pTool, pToolInfo);
  634. // If the tooltip will be displayed within a RTL mirrored window, then
  635. // simulate mirroring the tooltip. [samera]
  636. if (IS_WINDOW_RTL_MIRRORED(pToolInfo->hwnd) &&
  637. (!(pTtm->_ci.dwExStyle & RTL_MIRRORED_WINDOW)))
  638. {
  639. // toggle (mirror) the flags
  640. pTool->uFlags ^= (TTF_RTLREADING | TTF_RIGHT);
  641. }
  642. TTSetTipText(pTool, pToolInfo->lpszText);
  643. if (pTool->uFlags & TTF_SUBCLASS)
  644. {
  645. TTSubclassHwnd(pTool, pTtm->_ci.hwnd);
  646. }
  647. LRESULT lResult;
  648. if (!pToolInfo->hwnd || !IsWindow(pToolInfo->hwnd))
  649. {
  650. lResult = NFR_UNICODE;
  651. }
  652. else if (pTool->uFlags & TTF_UNICODE)
  653. {
  654. lResult = NFR_UNICODE;
  655. }
  656. else
  657. {
  658. lResult = SendMessage(pTool->hwnd, WM_NOTIFYFORMAT, (WPARAM)pTtm->_ci.hwnd, NF_QUERY);
  659. }
  660. if (lResult == NFR_UNICODE)
  661. {
  662. pTool->uFlags |= TTF_UNICODE;
  663. }
  664. // Create a markup
  665. if (pTool->cbSize == TTTOOLINFOW_V3_SIZE)
  666. {
  667. // lpReserved is void** because we don't want to make markup public
  668. Markup_Create(pTtm, NULL, NULL, IID_PPV_ARG(IControlMarkup, ((IControlMarkup **)&pTool->lpReserved)));
  669. }
  670. return 1;
  671. }
  672. void TTBeforeFreeTool(CToolTipsMgr *pTtm, TOOLINFO *pTool)
  673. {
  674. if (pTool->uFlags & TTF_SUBCLASS)
  675. TTUnsubclassHwnd(TTToolHwnd(pTool), pTtm->_ci.hwnd, FALSE);
  676. // clean up
  677. TTSetTipText(pTool, NULL);
  678. // Destroy the markup
  679. if (pTool->lpReserved)
  680. {
  681. GetToolMarkup(pTool)->Release();
  682. pTool->lpReserved = NULL;
  683. }
  684. }
  685. void DeleteTool(CToolTipsMgr *pTtm, TOOLINFO *pToolInfo)
  686. {
  687. // bail for right now;
  688. if (pToolInfo->cbSize > sizeof(TOOLINFO))
  689. {
  690. ASSERT(0);
  691. return;
  692. }
  693. TOOLINFO *pTool = FindTool(pTtm, pToolInfo);
  694. if (pTool)
  695. {
  696. if (pTtm->pCurTool == pTool)
  697. PopBubble2(pTtm, TRUE);
  698. TTBeforeFreeTool(pTtm, pTool);
  699. // replace it with the last one.. no need to waste cycles in realloc
  700. pTtm->iNumTools--;
  701. *pTool = pTtm->tools[pTtm->iNumTools]; // struct copy
  702. //cleanup if we moved the current tool
  703. if (pTtm->pCurTool == &pTtm->tools[pTtm->iNumTools])
  704. {
  705. pTtm->pCurTool = pTool;
  706. }
  707. }
  708. }
  709. // this strips out & markers so that people can use menu text strings
  710. void StripAccels(CToolTipsMgr *pTtm, LPTSTR lpTipText)
  711. {
  712. if (!(pTtm->_ci.style & TTS_NOPREFIX))
  713. {
  714. StripAccelerators(lpTipText, lpTipText, FALSE);
  715. }
  716. }
  717. //
  718. // The way we detect if a window is a toolbar or not is by asking it
  719. // for its MSAA class ID. We cannot use GetClassWord(GCL_ATOM) because
  720. // Microsoft LiquidMotion **superclasses** the toolbar, so the classname
  721. // won't match.
  722. //
  723. #define IsToolbarWindow(hwnd) \
  724. (SendMessage(hwnd, WM_GETOBJECT, 0, OBJID_QUERYCLASSNAMEIDX) == MSAA_CLASSNAMEIDX_TOOLBAR)
  725. LPTSTR GetToolText(CToolTipsMgr *pTtm, TOOLINFO *pTool)
  726. {
  727. int id;
  728. HINSTANCE hinst;
  729. DWORD dwStrLen;
  730. TOOLTIPTEXT ttt;
  731. if (!pTool)
  732. {
  733. return NULL;
  734. }
  735. TraceMsg(TF_TT, " **Enter GetToolText: ptr=%d, wFlags=%d, wid=%d, hwnd=%d",
  736. pTool, pTool->uFlags, pTool->uId, pTool->hwnd);
  737. LPTSTR lpTipText = (LPTSTR) LocalAlloc(LPTR, INITIALTIPSIZE * sizeof(TCHAR));
  738. if (lpTipText)
  739. {
  740. UINT cchTipText = INITIALTIPSIZE;
  741. if (pTool->lpszText == LPSTR_TEXTCALLBACK)
  742. {
  743. if (pTtm->dwFlags & NEEDTEXT) // Avoid recursion
  744. {
  745. goto Cleanup;
  746. }
  747. ttt.hdr.idFrom = pTool->uId;
  748. ttt.hdr.code = TTN_NEEDTEXT;
  749. ttt.hdr.hwndFrom = pTtm->_ci.hwnd;
  750. ttt.szText[0] = 0;
  751. ttt.lpszText = ttt.szText;
  752. ttt.uFlags = pTool->uFlags;
  753. ttt.lParam = pTool->lParam;
  754. ttt.hinst = NULL;
  755. pTtm->dwFlags |= NEEDTEXT;
  756. SendNotifyEx(pTool->hwnd, (HWND) -1,
  757. 0, (NMHDR *)&ttt,
  758. (pTool->uFlags & TTF_UNICODE) ? 1 : 0);
  759. pTtm->dwFlags &= ~NEEDTEXT;
  760. if (ttt.uFlags & TTF_DI_SETITEM)
  761. {
  762. if (IS_INTRESOURCE(ttt.lpszText))
  763. {
  764. pTool->lpszText = ttt.lpszText;
  765. pTool->hinst = ttt.hinst;
  766. }
  767. else if (ttt.lpszText != LPSTR_TEXTCALLBACK)
  768. {
  769. TTSetTipText(pTool, ttt.lpszText);
  770. }
  771. }
  772. if (IsFlagPtr(ttt.lpszText))
  773. goto Cleanup;
  774. //
  775. // we allow the RtlReading flag ONLY to be changed here.
  776. //
  777. if (ttt.uFlags & TTF_RTLREADING)
  778. pTool->uFlags |= TTF_RTLREADING;
  779. else
  780. pTool->uFlags &= ~TTF_RTLREADING;
  781. if (IS_INTRESOURCE(ttt.lpszText))
  782. {
  783. id = PtrToUlong(ttt.lpszText);
  784. hinst = ttt.hinst;
  785. ttt.lpszText = ttt.szText;
  786. goto LoadFromResource;
  787. }
  788. if (*ttt.lpszText == 0)
  789. goto Cleanup;
  790. dwStrLen = lstrlen(ttt.lpszText) + 1;
  791. if (cchTipText < dwStrLen)
  792. {
  793. LPTSTR psz = (LPTSTR) LocalReAlloc (lpTipText,
  794. dwStrLen * sizeof(TCHAR),
  795. LMEM_MOVEABLE);
  796. if (psz)
  797. {
  798. lpTipText = psz;
  799. cchTipText = dwStrLen;
  800. }
  801. }
  802. if (lpTipText)
  803. {
  804. StringCchCopy(lpTipText, cchTipText, ttt.lpszText);
  805. StripAccels(pTtm, lpTipText);
  806. }
  807. //
  808. // if ttt.lpszText != ttt.szText and the ttt.uFlags has TTF_MEMALLOCED, then
  809. // the ANSI thunk allocated the buffer for us, so free it.
  810. //
  811. if ((ttt.lpszText != ttt.szText) && (ttt.uFlags & TTF_MEMALLOCED))
  812. {
  813. LocalFree(ttt.lpszText);
  814. }
  815. }
  816. else if (pTool->lpszText && IS_INTRESOURCE(pTool->lpszText))
  817. {
  818. id = PtrToLong(pTool->lpszText);
  819. hinst = pTool->hinst;
  820. LoadFromResource:
  821. if (lpTipText)
  822. {
  823. if (!LoadString(hinst, id, lpTipText, cchTipText))
  824. goto Cleanup;
  825. StripAccels(pTtm, lpTipText);
  826. }
  827. }
  828. else
  829. {
  830. // supplied at creation time.
  831. TraceMsg(TF_TT, "GetToolText returns %s", pTool->lpszText);
  832. if (pTool->lpszText && *pTool->lpszText)
  833. {
  834. dwStrLen = lstrlen(pTool->lpszText) + 1;
  835. if (cchTipText < dwStrLen)
  836. {
  837. LPTSTR psz = (LPTSTR) LocalReAlloc (lpTipText,
  838. dwStrLen * sizeof(TCHAR),
  839. LMEM_MOVEABLE);
  840. if (psz)
  841. {
  842. lpTipText = psz;
  843. cchTipText = dwStrLen;
  844. }
  845. }
  846. if (lpTipText)
  847. {
  848. StringCchCopy(lpTipText, cchTipText, pTool->lpszText);
  849. StripAccels(pTtm, lpTipText);
  850. }
  851. }
  852. }
  853. TraceMsg(TF_TT, " **GetToolText returns %s", lpTipText ? lpTipText : TEXT("NULL"));
  854. }
  855. // Note that we don't parse the text into the markup. We'll do that only when we need it.
  856. return lpTipText;
  857. Cleanup: // Ick, Goto...
  858. if (lpTipText)
  859. LocalFree(lpTipText);
  860. return NULL;
  861. }
  862. LPTSTR GetCurToolText(CToolTipsMgr *pTtm)
  863. {
  864. LPTSTR psz = NULL;
  865. if (pTtm->pCurTool)
  866. psz = GetToolText(pTtm, pTtm->pCurTool);
  867. // this could have changed during the WM_NOTIFY back
  868. if (!pTtm->pCurTool)
  869. psz = NULL;
  870. return psz;
  871. }
  872. BOOL MarkupCurToolText(CToolTipsMgr *pTtm)
  873. {
  874. BOOL bResult = FALSE;
  875. LPTSTR lpsz = GetCurToolText(pTtm);
  876. if (lpsz)
  877. {
  878. // Now that we have the tiptext, parse it into the tool's markup
  879. GetCurToolBestMarkup(pTtm)->SetText(lpsz);
  880. // Also, set the font properly
  881. GetCurToolBestMarkup(pTtm)->SetFonts(pTtm->hFont, pTtm->hFontUnderline);
  882. if (*lpsz)
  883. {
  884. bResult = TRUE;
  885. }
  886. LocalFree(lpsz);
  887. }
  888. return bResult;
  889. }
  890. void GetToolRect(TOOLINFO *pTool, RECT *lprc)
  891. {
  892. if (pTool->uFlags & TTF_IDISHWND)
  893. {
  894. GetWindowRect((HWND)pTool->uId, lprc);
  895. }
  896. else
  897. {
  898. *lprc = pTool->rect;
  899. MapWindowPoints(pTool->hwnd, HWND_DESKTOP, (POINT *)lprc, 2);
  900. }
  901. }
  902. BOOL PointInTool(TOOLINFO *pTool, HWND hwnd, int x, int y)
  903. {
  904. // We never care if the point is in a track tool or we're using
  905. // a hit-test.
  906. if (pTool->uFlags & (TTF_TRACK | TTF_USEHITTEST))
  907. return FALSE;
  908. if (pTool->uFlags & TTF_IDISHWND)
  909. {
  910. if (hwnd == (HWND)pTool->uId)
  911. {
  912. return TRUE;
  913. }
  914. }
  915. else if (hwnd == pTool->hwnd)
  916. {
  917. POINT pt;
  918. pt.x = x;
  919. pt.y = y;
  920. if (PtInRect(&pTool->rect, pt))
  921. {
  922. return TRUE;
  923. }
  924. }
  925. return FALSE;
  926. }
  927. #define HittestInTool(pTool, hwnd, ht) \
  928. ((pTool->uFlags & TTF_USEHITTEST) && pTool->hwnd == hwnd && ht == pTool->rect.left)
  929. PTOOLINFO GetToolAtPoint(CToolTipsMgr *pTtm, HWND hwnd, int x, int y,
  930. int ht, BOOL fCheckText)
  931. {
  932. TOOLINFO *pToolReturn = NULL;
  933. TOOLINFO *pTool;
  934. // short cut.. if we're in the same too, and the bubble is up (not just virtual)
  935. // return it. this prevents us from having to poll all the time and
  936. // prevents us from switching to another tool when this one is good
  937. if ((pTtm->dwFlags & BUBBLEUP) && pTtm->pCurTool != NULL &&
  938. (HittestInTool(pTtm->pCurTool, hwnd, ht) ||
  939. PointInTool(pTtm->pCurTool, hwnd, x, y)))
  940. {
  941. return pTtm->pCurTool;
  942. }
  943. if (pTtm->iNumTools)
  944. {
  945. for (pTool = &pTtm->tools[pTtm->iNumTools-1]; pTool >= pTtm->tools; pTool--)
  946. {
  947. if (HittestInTool(pTool, hwnd, ht) || PointInTool(pTool, hwnd, x, y))
  948. {
  949. // if this tool has text, return it.
  950. // otherwise, save it away as a dead area tool,
  951. // and keep looking
  952. if (fCheckText)
  953. {
  954. LPTSTR psz = GetToolText(pTtm, pTool);
  955. if (psz)
  956. {
  957. LocalFree(psz);
  958. return pTool;
  959. }
  960. else if (pTtm->dwFlags & (BUBBLEUP|VIRTUALBUBBLEUP))
  961. {
  962. // only return this (only allow a virutal tool
  963. // if there was previously a tool up.
  964. // IE, we can't start things off with a virutal tool
  965. pToolReturn = pTool;
  966. }
  967. }
  968. else
  969. {
  970. return pTool;
  971. }
  972. }
  973. }
  974. }
  975. return pToolReturn;
  976. }
  977. void ShowVirtualBubble(CToolTipsMgr *pTtm)
  978. {
  979. TOOLINFO *pTool = pTtm->pCurTool;
  980. DebugMsg(TF_TT, TEXT("Entering ShowVirtualBubble so popping bubble"));
  981. PopBubble2(pTtm, TRUE);
  982. // Set this back in so that while we're in this tool's area,
  983. // we won't keep querying for info
  984. pTtm->pCurTool = pTool;
  985. pTtm->dwFlags |= VIRTUALBUBBLEUP;
  986. }
  987. #define TRACK_TOP 0
  988. #define TRACK_LEFT 1
  989. #define TRACK_BOTTOM 2
  990. #define TRACK_RIGHT 3
  991. void TTGetTipPosition(CToolTipsMgr *pTtm, LPRECT lprc, int cxText, int cyText, int *pxStem, int *pyStem)
  992. {
  993. RECT rcWorkArea;
  994. // ADJUSTRECT! Keep TTAdjustRect and TTM_GETBUBBLESIZE in sync.
  995. int cxMargin = pTtm->rcMargin.left + pTtm->rcMargin.right;
  996. int cyMargin = pTtm->rcMargin.top + pTtm->rcMargin.bottom;
  997. int iBubbleWidth = 2*XTEXTOFFSET * g_cxBorder + cxText + cxMargin;
  998. int iBubbleHeight = 2*YTEXTOFFSET * g_cyBorder + cyText + cyMargin;
  999. UINT uSide = (UINT)-1;
  1000. RECT rcTool;
  1001. MONITORINFO mi;
  1002. HMONITOR hMonitor;
  1003. POINT pt;
  1004. BOOL bBalloon = pTtm->_ci.style & TTS_BALLOON;
  1005. int xStem, yStem;
  1006. int iCursorHeight=0;
  1007. int iCursorWidth=0;
  1008. if (bBalloon || pTtm->cchTipTitle)
  1009. {
  1010. // ADJUSTRECT! Keep TTAdjustRect and TTM_GETBUBBLESIZE in sync.
  1011. iBubbleWidth += 2*XBALLOONOFFSET;
  1012. iBubbleHeight += 2*YBALLOONOFFSET;
  1013. if (bBalloon)
  1014. {
  1015. if (iBubbleWidth < MINBALLOONWIDTH)
  1016. pTtm->iStemHeight = 0;
  1017. else
  1018. {
  1019. pTtm->iStemHeight = STEMHEIGHT;
  1020. if (pTtm->iStemHeight > iBubbleHeight/3)
  1021. pTtm->iStemHeight = iBubbleHeight/3; // don't let the stem be longer than the bubble -- looks ugly
  1022. }
  1023. }
  1024. }
  1025. GetToolRect(pTtm->pCurTool, &rcTool);
  1026. if (pTtm->pCurTool->uFlags & TTF_TRACK)
  1027. {
  1028. lprc->left = pTtm->ptTrack.x;
  1029. lprc->top = pTtm->ptTrack.y;
  1030. if (bBalloon)
  1031. {
  1032. // adjust the desired left hand side
  1033. xStem = pTtm->ptTrack.x;
  1034. yStem = pTtm->ptTrack.y;
  1035. }
  1036. if (pTtm->pCurTool->uFlags & TTF_CENTERTIP)
  1037. {
  1038. // center the bubble around the ptTrack
  1039. lprc->left -= (iBubbleWidth / 2);
  1040. if (!bBalloon)
  1041. lprc->top -= (iBubbleHeight / 2);
  1042. }
  1043. if (pTtm->pCurTool->uFlags & TTF_ABSOLUTE)
  1044. {
  1045. // with goto bellow we'll skip adjusting
  1046. // bubble height -- so do it here
  1047. if (bBalloon)
  1048. iBubbleHeight += pTtm->iStemHeight;
  1049. goto CompleteRect;
  1050. }
  1051. // in balloon style the positioning depends on the position
  1052. // of the stem and we don't try to position the tooltip
  1053. // next to the tool rect
  1054. if (!bBalloon)
  1055. {
  1056. // now align it so that the tip sits beside the rect.
  1057. if (pTtm->ptTrack.y > rcTool.bottom)
  1058. {
  1059. uSide = TRACK_BOTTOM;
  1060. if (lprc->top < rcTool.bottom)
  1061. lprc->top = rcTool.bottom;
  1062. }
  1063. else if (pTtm->ptTrack.x < rcTool.left)
  1064. {
  1065. uSide = TRACK_LEFT;
  1066. if (lprc->left + iBubbleWidth > rcTool.left)
  1067. lprc->left = rcTool.left - iBubbleWidth;
  1068. }
  1069. else if (pTtm->ptTrack.y < rcTool.top)
  1070. {
  1071. uSide = TRACK_TOP;
  1072. if (lprc->top + iBubbleHeight > rcTool.top)
  1073. lprc->top = rcTool.top - iBubbleHeight;
  1074. }
  1075. else
  1076. {
  1077. uSide = TRACK_RIGHT;
  1078. if (lprc->left < rcTool.right)
  1079. lprc->left = rcTool.right;
  1080. }
  1081. }
  1082. }
  1083. else if (pTtm->pCurTool->uFlags & TTF_CENTERTIP)
  1084. {
  1085. lprc->left = (rcTool.right + rcTool.left - iBubbleWidth)/2;
  1086. lprc->top = rcTool.bottom;
  1087. if (bBalloon)
  1088. {
  1089. xStem = (rcTool.left + rcTool.right)/2;
  1090. yStem = rcTool.bottom;
  1091. }
  1092. }
  1093. else
  1094. {
  1095. // now set it
  1096. _GetCursorLowerLeft((LPINT)&lprc->left, (LPINT)&lprc->top, &iCursorWidth, &iCursorHeight);
  1097. if (pTtm->pCurTool->uFlags & TTF_EXCLUDETOOLAREA)
  1098. {
  1099. lprc->top = rcTool.top-iBubbleHeight;
  1100. }
  1101. if (g_fLeftAligned)
  1102. {
  1103. lprc->left -= iBubbleWidth;
  1104. }
  1105. if (bBalloon)
  1106. {
  1107. HMONITOR hMon1, hMon2;
  1108. POINT pt;
  1109. BOOL bOnSameMonitor = FALSE;
  1110. int iTop = lprc->top - (iCursorHeight + iBubbleHeight + pTtm->iStemHeight);
  1111. xStem = lprc->left;
  1112. yStem = lprc->top;
  1113. pt.x = xStem;
  1114. pt.y = lprc->top;
  1115. hMon1 = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  1116. pt.y = iTop;
  1117. hMon2 = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  1118. if (hMon1 == hMon2)
  1119. {
  1120. // the hmons are the same but maybe iTop is off any monitor and we just defaulted
  1121. // to the nearest one -- check if it's really on the monitor
  1122. mi.cbSize = sizeof(mi);
  1123. GetMonitorInfo(hMon1, &mi);
  1124. if (PtInRect(&mi.rcMonitor, pt))
  1125. {
  1126. // we'd like to show balloon above the cursor so that wedge/stem points
  1127. // to tip of the cursor not its bottom left corner
  1128. yStem -= iCursorHeight;
  1129. lprc->top = iTop;
  1130. bOnSameMonitor = TRUE;
  1131. }
  1132. }
  1133. if (!bOnSameMonitor)
  1134. {
  1135. xStem += iCursorWidth/2;
  1136. iCursorHeight = iCursorWidth = 0;
  1137. }
  1138. }
  1139. }
  1140. //
  1141. // At this point, (lprc->left, lprc->top) is the position
  1142. // at which we would prefer that the tooltip appear.
  1143. //
  1144. if (bBalloon)
  1145. {
  1146. // adjust the left point now that all calculations are done
  1147. // but only if we're not in the center tip mode
  1148. // note we use height as width so we can have 45 degree angle that looks nice
  1149. if (!(pTtm->pCurTool->uFlags & TTF_CENTERTIP) && iBubbleWidth > STEMOFFSET + pTtm->iStemHeight)
  1150. lprc->left -= STEMOFFSET;
  1151. // adjust the height to include stem
  1152. iBubbleHeight += pTtm->iStemHeight;
  1153. }
  1154. pt.x = lprc->left;
  1155. pt.y = lprc->top;
  1156. hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  1157. mi.cbSize = sizeof(mi);
  1158. GetMonitorInfo(hMonitor, &mi);
  1159. if (GetWindowLong(pTtm->_ci.hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST)
  1160. {
  1161. CopyRect(&rcWorkArea, &mi.rcMonitor);
  1162. }
  1163. else
  1164. {
  1165. CopyRect(&rcWorkArea, &mi.rcWork);
  1166. }
  1167. //
  1168. // At this point, rcWorkArea is the rectangle within which
  1169. // the tooltip should finally appear.
  1170. //
  1171. // Now fiddle with the coordinates to try to find a sane location
  1172. // for the tip.
  1173. //
  1174. // move it up if it's at the bottom of the screen
  1175. if ((lprc->top + iBubbleHeight) >= (rcWorkArea.bottom))
  1176. {
  1177. if (uSide == TRACK_BOTTOM)
  1178. lprc->top = rcTool.top - iBubbleHeight; // flip to top
  1179. else
  1180. {
  1181. //
  1182. // We can't "stick to bottom" because that would cause
  1183. // our tooltip to lie under the mouse cursor, causing it
  1184. // to pop immediately! So go just above the mouse cursor.
  1185. //
  1186. // cannot do that in the track mode -- tooltip randomly on the
  1187. // screen, not even near the button
  1188. //
  1189. // Bug#94368 raymondc v6: This messes up Lotus SmartCenter.
  1190. // Need to be smarter about when it is safe to flip up.
  1191. // Perhaps by checking if the upflip would put the tip too
  1192. // far away from the mouse.
  1193. if (pTtm->pCurTool->uFlags & TTF_TRACK)
  1194. lprc->top = pTtm->ptTrack.y - iBubbleHeight;
  1195. else
  1196. {
  1197. int y = GET_Y_LPARAM(GetMessagePos());
  1198. lprc->top = y - iBubbleHeight;
  1199. if (bBalloon)
  1200. yStem = y;
  1201. }
  1202. }
  1203. }
  1204. // If above the top of the screen...
  1205. if (lprc->top < rcWorkArea.top)
  1206. {
  1207. if (uSide == TRACK_TOP)
  1208. lprc->top = rcTool.bottom; // flip to bottom
  1209. else
  1210. lprc->top = rcWorkArea.top; // stick to top
  1211. }
  1212. // move it over if it extends past the right.
  1213. if ((lprc->left + iBubbleWidth) >= (rcWorkArea.right))
  1214. {
  1215. // flipping is not the right thing to do with balloon style
  1216. // because the wedge/stem can stick out of the window and
  1217. // would therefore be clipped so
  1218. if (bBalloon)
  1219. {
  1220. // move it to the left so that stem appears on the right side of the balloon
  1221. // again we use height as width so we can have 45 degree angle
  1222. if (iBubbleWidth >= MINBALLOONWIDTH)
  1223. lprc->left = xStem + min(STEMOFFSET, (iBubbleWidth-pTtm->iStemHeight)/2) - iBubbleWidth;
  1224. // are we still out?
  1225. if (lprc->left + iBubbleWidth >= rcWorkArea.right)
  1226. lprc->left = rcWorkArea.right - iBubbleWidth - 1;
  1227. }
  1228. else if (uSide == TRACK_RIGHT)
  1229. lprc->left = rcTool.left - iBubbleWidth; // flip to left
  1230. else
  1231. // not in right tracking mode, just scoot it over
  1232. lprc->left = rcWorkArea.right - iBubbleWidth - 1; // stick to right
  1233. }
  1234. // if too far left...
  1235. if (lprc->left < rcWorkArea.left)
  1236. {
  1237. if (uSide == TRACK_LEFT)
  1238. {
  1239. // flipping is not the right thing to do with balloon style
  1240. // because the wedge/stem can stick out of the window and
  1241. // would therefore be clipped so
  1242. if (bBalloon)
  1243. lprc->left = rcWorkArea.left; //pTtm->ptTrack.x;
  1244. else
  1245. lprc->left = rcTool.right; // flip to right
  1246. }
  1247. else
  1248. lprc->left = rcWorkArea.left; // stick to left
  1249. }
  1250. CompleteRect:
  1251. lprc->right = lprc->left + iBubbleWidth;
  1252. lprc->bottom = lprc->top + iBubbleHeight;
  1253. if (bBalloon && pxStem && pyStem)
  1254. {
  1255. *pxStem = xStem;
  1256. *pyStem = yStem;
  1257. }
  1258. }
  1259. void LoadAndAddToImagelist(HIMAGELIST himl, int id)
  1260. {
  1261. HICON hicon = (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(id), IMAGE_ICON, g_cxSmIcon, g_cySmIcon, LR_DEFAULTCOLOR | LR_SHARED);
  1262. if (hicon)
  1263. {
  1264. ImageList_AddIcon(himl, hicon);
  1265. DestroyIcon(hicon);
  1266. }
  1267. }
  1268. BOOL TTCreateTitleBitmaps(CToolTipsMgr *pTtm)
  1269. {
  1270. if (pTtm->himlTitleBitmaps)
  1271. return TRUE;
  1272. pTtm->himlTitleBitmaps = ImageList_Create(g_cxSmIcon, g_cySmIcon, ILC_COLOR32 | ILC_MASK, 3, 1);
  1273. if (pTtm->himlTitleBitmaps)
  1274. {
  1275. LoadAndAddToImagelist(pTtm->himlTitleBitmaps, IDI_TITLE_INFO);
  1276. LoadAndAddToImagelist(pTtm->himlTitleBitmaps, IDI_TITLE_WARNING);
  1277. LoadAndAddToImagelist(pTtm->himlTitleBitmaps, IDI_TITLE_ERROR);
  1278. return TRUE;
  1279. }
  1280. return FALSE;
  1281. }
  1282. // Called when caclulating the size of a "titled tool tip" or actually drawing
  1283. // based on the boolean value bCalcRect.
  1284. // TTRenderTitledTip is theme aware
  1285. BOOL TTRenderTitledTip(CToolTipsMgr *pTtm, HDC hdc, BOOL bCalcRect, RECT* prc, UINT uDrawFlags)
  1286. {
  1287. RECT rc;
  1288. int lWidth=0, lHeight=0;
  1289. HFONT hfont;
  1290. COLORREF crOldTextColor;
  1291. int iOldBKMode;
  1292. // If we don't have a title, we don't need to be here.
  1293. if (pTtm->cchTipTitle == 0)
  1294. return FALSE;
  1295. CopyRect(&rc, prc);
  1296. if (pTtm->uTitleBitmap != TTI_NONE)
  1297. {
  1298. int cx, cy;
  1299. CCGetIconSize(&pTtm->_ci, pTtm->himlTitleBitmaps, &cx, &cy);
  1300. lWidth = cx + TITLEICON_DIST;
  1301. lHeight += cy;
  1302. if (!bCalcRect && pTtm->himlTitleBitmaps)
  1303. {
  1304. ImageList_Draw(pTtm->himlTitleBitmaps, pTtm->uTitleBitmap - 1, hdc, rc.left, rc.top, ILD_TRANSPARENT | ILD_DPISCALE);
  1305. }
  1306. rc.left += lWidth;
  1307. }
  1308. if (!bCalcRect)
  1309. {
  1310. crOldTextColor = SetTextColor(hdc, pTtm->clrTipText);
  1311. iOldBKMode = SetBkMode(hdc, TRANSPARENT);
  1312. }
  1313. if (pTtm->lpTipTitle && pTtm->lpTipTitle[0] != 0)
  1314. {
  1315. LOGFONT lf;
  1316. HFONT hfTitle;
  1317. UINT uFlags = uDrawFlags | DT_SINGLELINE; // title should be on one line only
  1318. hfont = (HFONT) GetCurrentObject(hdc, OBJ_FONT);
  1319. GetObject(hfont, sizeof(lf), &lf);
  1320. CCAdjustForBold(&lf);
  1321. hfTitle = CreateFontIndirect(&lf);
  1322. if (hfTitle)
  1323. {
  1324. // hfont should already be set to this
  1325. hfont = (HFONT) SelectObject(hdc, hfTitle);
  1326. }
  1327. // drawtext does not calculate the height if these are specified
  1328. if (!bCalcRect)
  1329. uFlags |= DT_VCENTER;
  1330. // we need to calc title height -- either we did it before or we'll do it now
  1331. ASSERT(pTtm->iTitleHeight != 0 || uFlags & DT_CALCRECT);
  1332. // adjust the rect so we can stick the title to the bottom of it
  1333. rc.bottom = rc.top + max(pTtm->iTitleHeight, g_cySmIcon);
  1334. // problems in DrawText if margins make rc.right < rc.left
  1335. // even though we are asking for calculation of the rect nothing happens, so ...
  1336. if (bCalcRect)
  1337. rc.right = rc.left + (GetSystemMetrics(SM_CXICON) * 10); // 320 by default
  1338. SIZE szClose = {GetSystemMetrics(SM_CXMENUSIZE), GetSystemMetrics(SM_CYMENUSIZE)};
  1339. if (pTtm->_ci.style & TTS_CLOSE)
  1340. {
  1341. if (pTtm->hTheme)
  1342. {
  1343. GetThemePartSize(pTtm->hTheme, hdc, TTP_CLOSE, TTCS_NORMAL, NULL,
  1344. TS_TRUE, &szClose);
  1345. }
  1346. // We only want to do this if we are painting,
  1347. // because we don't want the text to overlap the close
  1348. if (!bCalcRect)
  1349. rc.right -= szClose.cx;
  1350. }
  1351. DrawText(hdc, pTtm->lpTipTitle, lstrlen(pTtm->lpTipTitle), &rc, uFlags);
  1352. if (pTtm->iTitleHeight == 0)
  1353. {
  1354. pTtm->iTitleHeight = max(RECTHEIGHT(rc), ABS(lf.lfHeight));
  1355. }
  1356. lHeight = max(lHeight, pTtm->iTitleHeight) + TITLE_INFO_DIST;
  1357. lWidth += RECTWIDTH(rc);
  1358. if (pTtm->_ci.style & TTS_CLOSE)
  1359. {
  1360. if (bCalcRect)
  1361. {
  1362. lHeight = max(lHeight, szClose.cy);
  1363. lWidth += szClose.cx;
  1364. }
  1365. else
  1366. {
  1367. SetRect(&pTtm->rcClose, rc.right + XBALLOONOFFSET - 5, rc.top - YBALLOONOFFSET + 5,
  1368. rc.right + szClose.cx + XBALLOONOFFSET - 5, rc.top + szClose.cy - YBALLOONOFFSET + 5);
  1369. if (pTtm->hTheme)
  1370. DrawThemeBackground(pTtm->hTheme, hdc, TTP_CLOSE, pTtm->iStateId, &pTtm->rcClose, 0);
  1371. else
  1372. DrawFrameControl(hdc, &pTtm->rcClose, DFC_CAPTION, DFCS_FLAT | DFCS_CAPTIONCLOSE | (pTtm->iStateId == TTCS_PRESSED?DFCS_PUSHED:0));
  1373. }
  1374. }
  1375. // Bypass title font cleanup if using themes
  1376. if (hfTitle)
  1377. {
  1378. SelectObject(hdc, hfont);
  1379. DeleteObject(hfTitle);
  1380. }
  1381. }
  1382. // adjust the rect for the info text
  1383. CopyRect(&rc, prc);
  1384. rc.top += lHeight;
  1385. // we want multi line text -- tooltip will give us single line if we did not set MAXWIDTH
  1386. uDrawFlags &= ~DT_SINGLELINE;
  1387. GetCurToolBestMarkup(pTtm)->SetRenderFlags(uDrawFlags);
  1388. GetCurToolBestMarkup(pTtm)->CalcIdealSize(hdc, MARKUPSIZE_CALCHEIGHT, &rc);
  1389. if (!bCalcRect)
  1390. GetCurToolBestMarkup(pTtm)->DrawText(hdc, &rc);
  1391. lHeight += RECTHEIGHT(rc);
  1392. lWidth = max(lWidth, RECTWIDTH(rc));
  1393. if (bCalcRect)
  1394. {
  1395. prc->right = prc->left + lWidth;
  1396. prc->bottom = prc->top + lHeight;
  1397. }
  1398. else
  1399. {
  1400. SetTextColor(hdc, crOldTextColor);
  1401. SetBkMode(hdc, iOldBKMode);
  1402. }
  1403. return TRUE;
  1404. }
  1405. // TTGetTipSize is theme aware
  1406. void TTGetTipSize(CToolTipsMgr *pTtm, TOOLINFO *pTool, LPINT pcxText, LPINT pcyText)
  1407. {
  1408. // get the size it will be
  1409. *pcxText = 0;
  1410. *pcyText = 0;
  1411. HDC hdcTemp = GetDC(pTtm->_ci.hwnd);
  1412. if (hdcTemp == NULL)
  1413. {
  1414. return;
  1415. }
  1416. HDC hdc = CreateCompatibleDC(hdcTemp);
  1417. ReleaseDC(pTtm->_ci.hwnd, hdcTemp);
  1418. if (hdc == NULL)
  1419. {
  1420. return;
  1421. }
  1422. HFONT hOldFont;
  1423. if (pTtm->hFont)
  1424. hOldFont = (HFONT) SelectObject(hdc, pTtm->hFont);
  1425. /* If need to fire off the pre-DrawText notify then do so, otherwise use the
  1426. original implementation that just called MGetTextExtent */
  1427. {
  1428. NMTTCUSTOMDRAW nm;
  1429. DWORD dwCustom;
  1430. UINT uDefDrawFlags = 0;
  1431. nm.nmcd.hdr.hwndFrom = pTtm->_ci.hwnd;
  1432. nm.nmcd.hdr.idFrom = pTool->uId;
  1433. nm.nmcd.hdr.code = NM_CUSTOMDRAW;
  1434. nm.nmcd.hdc = hdc;
  1435. // TTGetTipSize must use CDDS_PREPAINT so the client can tell
  1436. // whether we are measuring or painting
  1437. nm.nmcd.dwDrawStage = CDDS_PREPAINT;
  1438. nm.nmcd.rc.left = nm.nmcd.rc.top = 0;
  1439. if (pTtm->_ci.style & TTS_NOPREFIX)
  1440. uDefDrawFlags = DT_NOPREFIX;
  1441. if (pTtm->iMaxTipWidth == -1)
  1442. {
  1443. uDefDrawFlags |= DT_CALCRECT|DT_SINGLELINE |DT_LEFT;
  1444. GetCurToolBestMarkup(pTtm)->SetRenderFlags(uDefDrawFlags);
  1445. SetRect(&nm.nmcd.rc, 0, 0, 0, 0);
  1446. GetCurToolBestMarkup(pTtm)->CalcIdealSize(hdc, MARKUPSIZE_CALCHEIGHT, &nm.nmcd.rc);
  1447. *pcxText = nm.nmcd.rc.right;
  1448. *pcyText = nm.nmcd.rc.bottom;
  1449. }
  1450. else
  1451. {
  1452. uDefDrawFlags |= DT_CALCRECT | DT_LEFT | DT_WORDBREAK | DT_EXPANDTABS | DT_EXTERNALLEADING;
  1453. nm.nmcd.rc.right = pTtm->iMaxTipWidth;
  1454. nm.nmcd.rc.bottom = 0;
  1455. GetCurToolBestMarkup(pTtm)->SetRenderFlags(uDefDrawFlags);
  1456. GetCurToolBestMarkup(pTtm)->CalcIdealSize(hdc, MARKUPSIZE_CALCHEIGHT, &nm.nmcd.rc);
  1457. *pcxText = nm.nmcd.rc.right;
  1458. *pcyText = nm.nmcd.rc.bottom;
  1459. }
  1460. if ((pTtm->pCurTool->uFlags & TTF_RTLREADING) || (pTtm->_ci.dwExStyle & WS_EX_RTLREADING))
  1461. uDefDrawFlags |= DT_RTLREADING;
  1462. //
  1463. // Make it right aligned, if requested.
  1464. //
  1465. if (pTool->uFlags & TTF_RIGHT)
  1466. uDefDrawFlags |= DT_RIGHT;
  1467. nm.uDrawFlags = uDefDrawFlags;
  1468. dwCustom = (DWORD)SendNotifyEx(pTool->hwnd, (HWND) -1,
  1469. 0, (NMHDR*) &nm,
  1470. (pTool->uFlags & TTF_UNICODE) ? 1 : 0);
  1471. if (TTRenderTitledTip(pTtm, hdc, TRUE, &nm.nmcd.rc, uDefDrawFlags))
  1472. {
  1473. *pcxText = nm.nmcd.rc.right - nm.nmcd.rc.left;
  1474. *pcyText = nm.nmcd.rc.bottom - nm.nmcd.rc.top;
  1475. }
  1476. else if ((dwCustom & CDRF_NEWFONT) || nm.uDrawFlags != uDefDrawFlags)
  1477. {
  1478. GetCurToolBestMarkup(pTtm)->SetRenderFlags(nm.uDrawFlags);
  1479. GetCurToolBestMarkup(pTtm)->CalcIdealSize(hdc, MARKUPSIZE_CALCHEIGHT, &nm.nmcd.rc);
  1480. *pcxText = nm.nmcd.rc.right - nm.nmcd.rc.left;
  1481. *pcyText = nm.nmcd.rc.bottom - nm.nmcd.rc.top;
  1482. }
  1483. // did the owner specify the size?
  1484. else if (nm.nmcd.rc.right - nm.nmcd.rc.left != *pcxText ||
  1485. nm.nmcd.rc.bottom - nm.nmcd.rc.top != *pcyText)
  1486. {
  1487. *pcxText = nm.nmcd.rc.right - nm.nmcd.rc.left;
  1488. *pcyText = nm.nmcd.rc.bottom - nm.nmcd.rc.top;
  1489. }
  1490. // notify parent afterwards if they want us to
  1491. if (!(dwCustom & CDRF_SKIPDEFAULT) &&
  1492. dwCustom & CDRF_NOTIFYPOSTPAINT)
  1493. {
  1494. nm.nmcd.dwDrawStage = CDDS_POSTPAINT;
  1495. SendNotifyEx(pTool->hwnd, (HWND) -1,
  1496. 0, (NMHDR*) &nm,
  1497. (pTool->uFlags & TTF_UNICODE) ? 1 : 0);
  1498. }
  1499. }
  1500. if (pTtm->hFont)
  1501. SelectObject(hdc, hOldFont);
  1502. DeleteDC(hdc);
  1503. // after the calc rect, add a little space on the right
  1504. *pcxText += g_cxEdge;
  1505. *pcyText += g_cyEdge;
  1506. }
  1507. //
  1508. // Given an inner rectangle, return the coordinates of the outer,
  1509. // or vice versa.
  1510. //
  1511. // "outer rectangle" = window rectangle.
  1512. // "inner rectangle" = the area where we draw the text.
  1513. //
  1514. // This allows people like listview and treeview to position
  1515. // the tooltip so the inner rectangle exactly coincides with
  1516. // their existing text.
  1517. //
  1518. // All the places we do rectangle adjusting are marked with
  1519. // the comment
  1520. //
  1521. // // ADJUSTRECT! Keep TTAdjustRect in sync.
  1522. //
  1523. LRESULT TTAdjustRect(CToolTipsMgr *pTtm, BOOL fLarger, LPRECT prc)
  1524. {
  1525. RECT rc;
  1526. if (!prc)
  1527. return 0;
  1528. //
  1529. // Do all the work on our private little rectangle on the
  1530. // assumption that everything is getting bigger. At the end,
  1531. // we'll flip all the numbers around if in fact we're getting
  1532. // smaller.
  1533. //
  1534. rc.top = rc.left = rc.bottom = rc.right = 0;
  1535. // TTRender adjustments -
  1536. rc.left -= XTEXTOFFSET*g_cxBorder + pTtm->rcMargin.left;
  1537. rc.right += XTEXTOFFSET*g_cxBorder + pTtm->rcMargin.right;
  1538. rc.top -= YTEXTOFFSET*g_cyBorder + pTtm->rcMargin.top;
  1539. rc.bottom += YTEXTOFFSET*g_cyBorder + pTtm->rcMargin.bottom;
  1540. // Compensate for the hack in TTRender that futzes all the rectangles
  1541. // by one pixel. Look for "Account for off-by-one."
  1542. rc.bottom--;
  1543. rc.right--;
  1544. if (pTtm->_ci.style & TTS_BALLOON || pTtm->cchTipTitle)
  1545. {
  1546. InflateRect(&rc, XBALLOONOFFSET, YBALLOONOFFSET);
  1547. }
  1548. //
  1549. // Ask Windows how much adjusting he will do to us.
  1550. //
  1551. // Since we don't track WM_STYLECHANGED/GWL_EXSTYLE, we have to ask USER
  1552. // for our style information, since the app may have changed it.
  1553. //
  1554. AdjustWindowRectEx(&rc,
  1555. pTtm->_ci.style,
  1556. BOOLFROMPTR(GetMenu(pTtm->_ci.hwnd)),
  1557. GetWindowLong(pTtm->_ci.hwnd, GWL_EXSTYLE));
  1558. //
  1559. // Now adjust our caller's rectangle.
  1560. //
  1561. if (fLarger)
  1562. {
  1563. prc->left += rc.left;
  1564. prc->right += rc.right;
  1565. prc->top += rc.top;
  1566. prc->bottom += rc.bottom;
  1567. }
  1568. else
  1569. {
  1570. prc->left -= rc.left;
  1571. prc->right -= rc.right;
  1572. prc->top -= rc.top;
  1573. prc->bottom -= rc.bottom;
  1574. }
  1575. return TRUE;
  1576. }
  1577. #define CSTEMPOINTS 3
  1578. // bMirrored does not mean a mirrored tooltip.
  1579. // It means simulating the behavior or a mirrored tooltip for a tooltip created with a mirrored parent.
  1580. HRGN CreateBalloonRgn(int xStem, int yStem, int iWidth, int iHeight, int iStemHeight, BOOL bUnderStem, BOOL bMirrored)
  1581. {
  1582. int y = 0, yHeight = iHeight;
  1583. HRGN rgn;
  1584. if (bUnderStem)
  1585. yHeight -= iStemHeight;
  1586. else
  1587. y = iStemHeight;
  1588. rgn = CreateRoundRectRgn(0, y, iWidth, yHeight, BALLOON_X_CORNER, BALLOON_Y_CORNER);
  1589. if (rgn)
  1590. {
  1591. // create wedge/stem rgn
  1592. if (iWidth >= MINBALLOONWIDTH)
  1593. {
  1594. HRGN rgnStem;
  1595. POINT aptStemRgn[CSTEMPOINTS];
  1596. POINT *ppt = aptStemRgn;
  1597. POINT pt;
  1598. BOOL bCentered;
  1599. int iStemWidth = iStemHeight+1; // for a 45 degree angle
  1600. // we center the stem if we have TTF_CENTERTIP or the width
  1601. // of the balloon is not big enough to offset the stem by
  1602. // STEMOFFSET
  1603. // can't quite center the tip on TTF_CENTERTIP because it may be
  1604. // moved left or right it did not fit on the screen: just check
  1605. // if xStem is in the middle
  1606. bCentered = (xStem == iWidth/2) || (iWidth < 2*STEMOFFSET + iStemWidth);
  1607. if (bCentered)
  1608. pt.x = (iWidth - iStemWidth)/2;
  1609. else if (xStem > iWidth/2)
  1610. {
  1611. if (bMirrored)
  1612. {
  1613. pt.x = STEMOFFSET + iStemWidth;
  1614. }
  1615. else
  1616. {
  1617. pt.x = iWidth - STEMOFFSET - iStemWidth;
  1618. }
  1619. }
  1620. else
  1621. {
  1622. if (bMirrored)
  1623. {
  1624. pt.x = iWidth - STEMOFFSET;
  1625. }
  1626. else
  1627. {
  1628. pt.x = STEMOFFSET;
  1629. }
  1630. }
  1631. if (bMirrored && (ABS(pt.x - (iWidth - xStem)) <= 2))
  1632. {
  1633. pt.x = iWidth - xStem; // avoid rough edges, have a straight line
  1634. }
  1635. else if (!bMirrored && (ABS(pt.x - xStem) <= 2))
  1636. {
  1637. pt.x = xStem; // avoid rough edges, have a straight line
  1638. }
  1639. if (bUnderStem)
  1640. pt.y = iHeight - iStemHeight - 2;
  1641. else
  1642. pt.y = iStemHeight + 2;
  1643. *ppt++ = pt;
  1644. if (bMirrored)
  1645. {
  1646. pt.x -= iStemWidth;
  1647. }
  1648. else
  1649. {
  1650. pt.x += iStemWidth;
  1651. }
  1652. if (bMirrored && (ABS(pt.x - (iWidth - xStem)) <= 2))
  1653. {
  1654. pt.x = iWidth - xStem; // avoid rough edges, have a straight line
  1655. }
  1656. else if (!bMirrored && (ABS(pt.x - xStem) <= 2))
  1657. {
  1658. pt.x = xStem; // avoid rough edges, have a straight line
  1659. }
  1660. *ppt++ = pt;
  1661. if (bMirrored)
  1662. {
  1663. pt.x = iWidth - xStem;
  1664. }
  1665. else
  1666. {
  1667. pt.x = xStem;
  1668. }
  1669. pt.y = yStem;
  1670. *ppt = pt;
  1671. rgnStem = CreatePolygonRgn(aptStemRgn, CSTEMPOINTS, ALTERNATE);
  1672. if (rgnStem)
  1673. {
  1674. CombineRgn(rgn, rgn, rgnStem, RGN_OR);
  1675. DeleteObject(rgnStem);
  1676. }
  1677. }
  1678. }
  1679. return rgn;
  1680. }
  1681. /*----------------------------------------------------------
  1682. Purpose: Shows the tooltip. On NT4/Win95, this is a standard
  1683. show window. On NT5/Memphis, this slides the tooltip
  1684. bubble from an invisible point.
  1685. Returns: --
  1686. Cond: --
  1687. */
  1688. #define CMS_TOOLTIP 135
  1689. void SlideAnimate(HWND hwnd, LPCRECT prc)
  1690. {
  1691. DWORD dwPos, dwFlags;
  1692. dwPos = GetMessagePos();
  1693. if (GET_Y_LPARAM(dwPos) > prc->top + (prc->bottom - prc->top) / 2)
  1694. {
  1695. dwFlags = AW_VER_NEGATIVE;
  1696. }
  1697. else
  1698. {
  1699. dwFlags = AW_VER_POSITIVE;
  1700. }
  1701. AnimateWindow(hwnd, CMS_TOOLTIP, dwFlags | AW_SLIDE);
  1702. }
  1703. STDAPI_(void) CoolTooltipBubble(IN HWND hwnd, IN LPCRECT prc, BOOL fAllowFade, BOOL fAllowAnimate)
  1704. {
  1705. BOOL fSetWindowPos = FALSE;
  1706. BOOL fAnimate = TRUE;
  1707. ASSERT(prc);
  1708. SystemParametersInfo(SPI_GETTOOLTIPANIMATION, 0, &fAnimate, 0);
  1709. if (fAnimate)
  1710. {
  1711. fAnimate = FALSE;
  1712. SystemParametersInfo(SPI_GETTOOLTIPFADE, 0, &fAnimate, 0);
  1713. if (fAnimate && fAllowFade)
  1714. {
  1715. AnimateWindow(hwnd, CMS_TOOLTIP, AW_BLEND);
  1716. }
  1717. else if (fAllowAnimate)
  1718. {
  1719. SlideAnimate(hwnd, prc);
  1720. }
  1721. else
  1722. {
  1723. fSetWindowPos = TRUE;
  1724. }
  1725. }
  1726. else
  1727. {
  1728. fSetWindowPos = TRUE;
  1729. }
  1730. if (fSetWindowPos)
  1731. {
  1732. SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
  1733. SWP_NOACTIVATE|SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER);
  1734. }
  1735. }
  1736. void DoShowBubble(CToolTipsMgr *pTtm)
  1737. {
  1738. if (!g_fEnableBalloonTips && (pTtm->_ci.style & TTS_BALLOON))
  1739. return;
  1740. HFONT hFontPrev;
  1741. RECT rc;
  1742. int cxText, cyText;
  1743. int xStem = 0, yStem = 0;
  1744. NMTTSHOWINFO si;
  1745. BOOL fAllowFade = !(pTtm->_ci.style & TTS_NOFADE);
  1746. BOOL fAllowAnimate = !(pTtm->_ci.style & TTS_NOANIMATE);
  1747. DWORD dwCurrentTime = (pTtm->dwLastDisplayTime == 0)? TIMEBETWEENANIMATE : GetTickCount();
  1748. DWORD dwDelta = dwCurrentTime - pTtm->dwLastDisplayTime;
  1749. BOOL fFadeTurnedOn = FadeEnabled();
  1750. DebugMsg(TF_TT, TEXT("Entering DoShowBubble"));
  1751. BOOL bResult = MarkupCurToolText(pTtm);
  1752. if (pTtm->dwFlags & TRACKMODE)
  1753. {
  1754. if (bResult == FALSE)
  1755. {
  1756. PopBubble2(pTtm, TRUE);
  1757. pTtm->dwFlags &= ~TRACKMODE;
  1758. return;
  1759. }
  1760. }
  1761. else
  1762. {
  1763. TTSetTimer(pTtm, TTT_POP);
  1764. if (bResult == FALSE)
  1765. {
  1766. ShowVirtualBubble(pTtm);
  1767. return;
  1768. }
  1769. TTSetTimer(pTtm, TTT_AUTOPOP);
  1770. }
  1771. do
  1772. {
  1773. UINT uFlags = SWP_NOACTIVATE | SWP_NOZORDER;
  1774. // get the size it will be
  1775. TTGetTipSize(pTtm, pTtm->pCurTool, &cxText, &cyText);
  1776. TTGetTipPosition(pTtm, &rc, cxText, cyText, &xStem, &yStem);
  1777. SetWindowPos(pTtm->_ci.hwnd, NULL, rc.left, rc.top,
  1778. rc.right-rc.left, rc.bottom-rc.top, uFlags);
  1779. if (pTtm->pCurTool == NULL)
  1780. return;
  1781. si.hdr.hwndFrom = pTtm->_ci.hwnd;
  1782. si.hdr.idFrom = pTtm->pCurTool->uId;
  1783. si.hdr.code = TTN_SHOW;
  1784. si.dwStyle = pTtm->_ci.style;
  1785. hFontPrev = pTtm->hFont;
  1786. if (!SendNotifyEx(pTtm->pCurTool->hwnd, (HWND)-1,
  1787. TTN_SHOW, &si.hdr,
  1788. (pTtm->pCurTool->uFlags & TTF_UNICODE) ? 1 : 0))
  1789. {
  1790. uFlags = SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOOWNERZORDER;
  1791. SetWindowPos(pTtm->_ci.hwnd, HWND_TOP, rc.left, rc.top,
  1792. 0, 0, uFlags);
  1793. }
  1794. }
  1795. while (hFontPrev != pTtm->hFont);
  1796. // If we're under the minimum time between animates, then we don't animate
  1797. if (dwDelta < TIMEBETWEENANIMATE)
  1798. fAllowFade = fAllowAnimate = FALSE;
  1799. // create the balloon region if necessary
  1800. // Note: Don't use si.dwStyle here, since other parts of comctl32
  1801. // look at pTtm->_ci.style to decide what to do
  1802. if (pTtm->_ci.style & TTS_BALLOON)
  1803. {
  1804. HRGN rgn;
  1805. BOOL bMirrored = FALSE;
  1806. if (pTtm->pCurTool)
  1807. {
  1808. bMirrored = pTtm->_ci.dwExStyle & WS_EX_LAYOUTRTL;
  1809. }
  1810. pTtm->fUnderStem = yStem >= rc.bottom-1;
  1811. rgn = CreateBalloonRgn(xStem - rc.left, yStem-rc.top, rc.right-rc.left, rc.bottom-rc.top,
  1812. pTtm->iStemHeight, pTtm->fUnderStem, bMirrored);
  1813. if (rgn && !SetWindowRgn(pTtm->_ci.hwnd, rgn, FALSE))
  1814. DeleteObject(rgn);
  1815. }
  1816. pTtm->dwLastDisplayTime = GetTickCount();
  1817. // Don't Show and hide at the same time. This can cause fading tips to interfere with each other
  1818. KillTimer(pTtm->_ci.hwnd, TTT_FADEHIDE);
  1819. if (fFadeTurnedOn && fAllowFade)
  1820. {
  1821. // If we can fade, then setup the attributes to start from zero.
  1822. SetLayeredWindowAttributes(pTtm->_ci.hwnd, 0, (BYTE)pTtm->iFadeState, LWA_ALPHA);
  1823. RedrawWindow(pTtm->_ci.hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
  1824. // Position it.
  1825. SetWindowPos(pTtm->_ci.hwnd,HWND_TOP,0,0,0,0,SWP_NOACTIVATE|SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER);
  1826. // Start the fade in.
  1827. SetTimer(pTtm->_ci.hwnd, TTT_FADESHOW, TTTT_FADESHOW, NULL);
  1828. }
  1829. else
  1830. {
  1831. RedrawWindow(pTtm->_ci.hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
  1832. // Position it.
  1833. SetWindowPos(pTtm->_ci.hwnd,HWND_TOP,0,0,0,0,SWP_NOACTIVATE|SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER);
  1834. pTtm->iFadeState = TT_MAXFADESHOW;
  1835. SetLayeredWindowAttributes(pTtm->_ci.hwnd, 0, (BYTE)pTtm->iFadeState, LWA_ALPHA);
  1836. }
  1837. pTtm->dwFlags |= BUBBLEUP;
  1838. RedrawWindow(pTtm->_ci.hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
  1839. }
  1840. void ShowBubbleForTool(CToolTipsMgr *pTtm, TOOLINFO *pTool)
  1841. {
  1842. DebugMsg(TF_TT, TEXT("ShowBubbleForTool"));
  1843. // if there's a bubble up for a different tool, pop it.
  1844. if ((pTool != pTtm->pCurTool) && (pTtm->dwFlags & BUBBLEUP))
  1845. {
  1846. PopBubble2(pTtm, TRUE);
  1847. }
  1848. // if the bubble was for a different tool, or no bubble, show it
  1849. if ((pTool != pTtm->pCurTool) || !(pTtm->dwFlags & (VIRTUALBUBBLEUP|BUBBLEUP)))
  1850. {
  1851. pTtm->pCurTool = pTool;
  1852. DoShowBubble(pTtm);
  1853. }
  1854. else
  1855. {
  1856. DebugMsg(TF_TT, TEXT("ShowBubbleForTool not showinb bubble"));
  1857. }
  1858. }
  1859. void HandleRelayedMessage(CToolTipsMgr *pTtm, HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  1860. {
  1861. int ht = HTERROR;
  1862. if (pTtm->dwFlags & TRACKMODE)
  1863. {
  1864. // punt all messages if we're in track mode
  1865. return;
  1866. }
  1867. if (pTtm->dwFlags & BUTTONISDOWN)
  1868. {
  1869. // verify that the button is down
  1870. // this can happen if the tool didn't set capture so it didn't get the up message
  1871. if (GetKeyState(VK_LBUTTON) >= 0 &&
  1872. GetKeyState(VK_RBUTTON) >= 0 &&
  1873. GetKeyState(VK_MBUTTON) >= 0)
  1874. pTtm->dwFlags &= ~BUTTONISDOWN;
  1875. }
  1876. switch (message)
  1877. {
  1878. case WM_NCLBUTTONUP:
  1879. case WM_NCRBUTTONUP:
  1880. case WM_NCMBUTTONUP:
  1881. case WM_MBUTTONUP:
  1882. case WM_RBUTTONUP:
  1883. case WM_LBUTTONUP:
  1884. pTtm->dwFlags &= ~BUTTONISDOWN;
  1885. break;
  1886. case WM_NCLBUTTONDOWN:
  1887. case WM_NCRBUTTONDOWN:
  1888. case WM_NCMBUTTONDOWN:
  1889. case WM_MBUTTONDOWN:
  1890. case WM_RBUTTONDOWN:
  1891. case WM_LBUTTONDOWN:
  1892. pTtm->dwFlags |= BUTTONISDOWN;
  1893. ShowVirtualBubble(pTtm);
  1894. break;
  1895. case WM_NCMOUSEMOVE:
  1896. {
  1897. // convert to client coords
  1898. POINT pt;
  1899. pt.x = GET_X_LPARAM(lParam);
  1900. pt.y = GET_Y_LPARAM(lParam);
  1901. ScreenToClient(hwnd, &pt);
  1902. lParam = MAKELONG(pt.x, pt.y);
  1903. ht = (int) wParam;
  1904. // Fall thru...
  1905. }
  1906. case WM_MOUSEMOVE: {
  1907. TOOLINFO *pTool;
  1908. // to prevent us from popping up when some
  1909. // other app is active
  1910. if (((!(pTtm->_ci.style & TTS_ALWAYSTIP)) && !(ChildOfActiveWindow(hwnd))) ||
  1911. !(pTtm->dwFlags & ACTIVE) ||
  1912. (pTtm->dwFlags & BUTTONISDOWN))
  1913. {
  1914. break;
  1915. }
  1916. pTool = GetToolAtPoint(pTtm, hwnd, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), ht, FALSE);
  1917. if (pTool)
  1918. {
  1919. int id = 0;
  1920. // show only if another is showing
  1921. if (pTtm->dwFlags & (VIRTUALBUBBLEUP | BUBBLEUP))
  1922. {
  1923. // call show if bubble is up to make sure we're showing
  1924. // for the right tool
  1925. if (pTool != pTtm->pCurTool)
  1926. {
  1927. DebugMsg(TF_TT, TEXT("showing virtual bubble"));
  1928. PopBubble2(pTtm, TRUE);
  1929. pTtm->pCurTool = pTool;
  1930. ShowVirtualBubble(pTtm);
  1931. id = TTT_RESHOW;
  1932. }
  1933. else if (pTtm->idTimer == TTT_RESHOW)
  1934. {
  1935. // if the timer is currently waiting to reshow,
  1936. // don't reset the timer on mouse moves
  1937. id = 0;
  1938. }
  1939. }
  1940. else if (pTtm->idTimer != TTT_INITIAL || pTtm->pCurTool != pTool)
  1941. {
  1942. pTtm->pCurTool = pTool;
  1943. id = TTT_INITIAL;
  1944. }
  1945. DebugMsg(TF_TT, TEXT("MouseMove over pTool id = %d"), id);
  1946. if (id)
  1947. TTSetTimer(pTtm, id);
  1948. }
  1949. else
  1950. {
  1951. DebugMsg(TF_TT, TEXT("MouseMove over non-tool"));
  1952. PopBubble(pTtm);
  1953. }
  1954. break;
  1955. }
  1956. }
  1957. }
  1958. void TTUpdateTipText(CToolTipsMgr *pTtm, TOOLINFO *lpti)
  1959. {
  1960. TOOLINFO *lpTool = FindTool(pTtm, lpti);
  1961. if (lpTool)
  1962. {
  1963. lpTool->hinst = lpti->hinst;
  1964. TTSetTipText(lpTool, lpti->lpszText);
  1965. if (pTtm->dwFlags & TRACKMODE)
  1966. {
  1967. // if track mode is in effect and active, then
  1968. // redisplay the bubble.
  1969. if (pTtm->pCurTool)
  1970. DoShowBubble(pTtm);
  1971. }
  1972. else if (lpTool == pTtm->pCurTool)
  1973. {
  1974. // set the current position to our saved position.
  1975. // ToolHasMoved will return false for us if those this point
  1976. // is no longer within pCurTool's area
  1977. GetCursorPos(&pTtm->pt);
  1978. if (!ToolHasMoved(pTtm))
  1979. {
  1980. if (pTtm->dwFlags & (VIRTUALBUBBLEUP | BUBBLEUP))
  1981. DoShowBubble(pTtm);
  1982. }
  1983. else
  1984. {
  1985. DebugMsg(TF_TT, TEXT("TTUpdateTipText popping bubble"));
  1986. PopBubble2(pTtm, TRUE);
  1987. }
  1988. }
  1989. }
  1990. }
  1991. void TTSetFont(CToolTipsMgr *pTtm, HFONT hFont, BOOL fInval)
  1992. {
  1993. ToolTips_NewFont(pTtm, hFont);
  1994. if (hFont != NOFONT)
  1995. {
  1996. GetCurToolBestMarkup(pTtm)->SetFonts(pTtm->hFont, pTtm->hFontUnderline);
  1997. }
  1998. if (fInval)
  1999. {
  2000. // is a balloon up and is it in the track mode?
  2001. if ((pTtm->dwFlags & ACTIVE) && pTtm->pCurTool && (pTtm->pCurTool->uFlags & TTF_TRACK))
  2002. {
  2003. TOOLINFO *pCurTool = pTtm->pCurTool;
  2004. PopBubble2(pTtm, TRUE); // sets pTtm->pCurTool to NULL
  2005. ShowBubbleForTool(pTtm, pCurTool);
  2006. }
  2007. else
  2008. InvalidateRect(pTtm->_ci.hwnd, NULL, FALSE);
  2009. }
  2010. }
  2011. void TTSetDelayTime(CToolTipsMgr *pTtm, WPARAM wParam, LPARAM lParam)
  2012. {
  2013. int iDelayTime = GET_X_LPARAM(lParam);
  2014. switch (wParam)
  2015. {
  2016. case TTDT_INITIAL:
  2017. pTtm->iDelayTime = iDelayTime;
  2018. break;
  2019. case TTDT_AUTOPOP:
  2020. pTtm->iAutoPopTime = iDelayTime;
  2021. break;
  2022. case TTDT_RESHOW:
  2023. pTtm->iReshowTime = iDelayTime;
  2024. break;
  2025. case TTDT_AUTOMATIC:
  2026. if (iDelayTime > 0)
  2027. {
  2028. pTtm->iDelayTime = iDelayTime;
  2029. pTtm->iReshowTime = pTtm->iDelayTime / 5;
  2030. pTtm->iAutoPopTime = pTtm->iDelayTime * 10;
  2031. }
  2032. else
  2033. {
  2034. pTtm->iDelayTime = -1;
  2035. pTtm->iReshowTime = -1;
  2036. pTtm->iAutoPopTime = -1;
  2037. }
  2038. break;
  2039. }
  2040. }
  2041. int TTGetDelayTime(CToolTipsMgr *pTtm, WPARAM wParam)
  2042. {
  2043. switch (wParam)
  2044. {
  2045. case TTDT_AUTOMATIC:
  2046. case TTDT_INITIAL:
  2047. return (pTtm->iDelayTime < 0 ? GetDoubleClickTime() : pTtm->iDelayTime);
  2048. case TTDT_AUTOPOP:
  2049. return (pTtm->iAutoPopTime < 0 ? GetDoubleClickTime()*10 : pTtm->iAutoPopTime);
  2050. case TTDT_RESHOW:
  2051. return (pTtm->iReshowTime < 0 ? GetDoubleClickTime()/5 : pTtm->iReshowTime);
  2052. default:
  2053. return -1;
  2054. }
  2055. }
  2056. BOOL CopyToolInfoA(TOOLINFO *pToolSrc, PTOOLINFOA lpTool, UINT uiCodePage)
  2057. {
  2058. if (pToolSrc && lpTool)
  2059. {
  2060. if (lpTool->cbSize >= sizeof(TOOLINFOA) - sizeof(LPARAM))
  2061. {
  2062. lpTool->uFlags = pToolSrc->uFlags;
  2063. lpTool->hwnd = pToolSrc->hwnd;
  2064. lpTool->uId = pToolSrc->uId;
  2065. lpTool->rect = pToolSrc->rect;
  2066. lpTool->hinst = pToolSrc->hinst;
  2067. if ((pToolSrc->lpszText != LPSTR_TEXTCALLBACK) &&
  2068. !IS_INTRESOURCE(pToolSrc->lpszText))
  2069. {
  2070. if (lpTool->lpszText)
  2071. {
  2072. WideCharToMultiByte(uiCodePage, 0,
  2073. pToolSrc->lpszText,
  2074. -1,
  2075. lpTool->lpszText,
  2076. 80, NULL, NULL);
  2077. }
  2078. }
  2079. else
  2080. lpTool->lpszText = (LPSTR)pToolSrc->lpszText;
  2081. }
  2082. if (lpTool->cbSize > FIELD_OFFSET(TOOLINFOA, lParam))
  2083. lpTool->lParam = pToolSrc->lParam;
  2084. if (lpTool->cbSize > sizeof(TOOLINFOA))
  2085. return FALSE;
  2086. return TRUE;
  2087. }
  2088. else
  2089. return FALSE;
  2090. }
  2091. BOOL CopyToolInfo(TOOLINFO *pToolSrc, PTOOLINFO lpTool)
  2092. {
  2093. if (pToolSrc && lpTool && lpTool->cbSize <= sizeof(TOOLINFO))
  2094. {
  2095. if (lpTool->cbSize >= sizeof(TOOLINFO) - sizeof(LPARAM))
  2096. {
  2097. lpTool->uFlags = pToolSrc->uFlags;
  2098. lpTool->hwnd = pToolSrc->hwnd;
  2099. lpTool->uId = pToolSrc->uId;
  2100. lpTool->rect = pToolSrc->rect;
  2101. lpTool->hinst = pToolSrc->hinst;
  2102. if ((pToolSrc->lpszText != LPSTR_TEXTCALLBACK) && !IS_INTRESOURCE(pToolSrc->lpszText))
  2103. {
  2104. if (lpTool->lpszText)
  2105. {
  2106. // REVIEW: message parameters do not indicate the size of the
  2107. // destination buffer.
  2108. StringCchCopy(lpTool->lpszText, lstrlen(pToolSrc->lpszText)+1, pToolSrc->lpszText);
  2109. }
  2110. }
  2111. else
  2112. {
  2113. lpTool->lpszText = pToolSrc->lpszText;
  2114. }
  2115. }
  2116. if (lpTool->cbSize > FIELD_OFFSET(TOOLINFO, lParam))
  2117. {
  2118. lpTool->lParam = pToolSrc->lParam;
  2119. }
  2120. if (lpTool->cbSize > sizeof(TOOLINFO))
  2121. {
  2122. return FALSE;
  2123. }
  2124. return TRUE;
  2125. }
  2126. else
  2127. {
  2128. return FALSE;
  2129. }
  2130. }
  2131. PTOOLINFO TTToolAtMessagePos(CToolTipsMgr *pTtm)
  2132. {
  2133. TOOLINFO *pTool;
  2134. HWND hwndPt;
  2135. POINT pt;
  2136. DWORD dwPos = GetMessagePos();
  2137. //int ht;
  2138. pt.x = GET_X_LPARAM(dwPos);
  2139. pt.y = GET_Y_LPARAM(dwPos);
  2140. hwndPt = TTWindowFromPoint(pTtm, &pt);
  2141. //ht = SendMessage(hwndPt, WM_NCHITTEST, 0, MAKELONG(pt.x, pt.y));
  2142. ScreenToClient(hwndPt, &pt);
  2143. pTool = GetToolAtPoint(pTtm, hwndPt, pt.x, pt.y, HTERROR, FALSE);
  2144. return pTool;
  2145. }
  2146. void TTCheckCursorPos(CToolTipsMgr *pTtm)
  2147. {
  2148. TOOLINFO *pTool = TTToolAtMessagePos(pTtm);
  2149. if ((pTtm->pCurTool != pTool) ||
  2150. ToolHasMoved(pTtm))
  2151. {
  2152. PopBubble(pTtm);
  2153. DebugMsg(TF_TT, TEXT("TTCheckCursorPos popping bubble"));
  2154. }
  2155. }
  2156. void TTHandleTimer(CToolTipsMgr *pTtm, UINT_PTR id)
  2157. {
  2158. TOOLINFO *pTool;
  2159. switch (id)
  2160. {
  2161. case TTT_FADESHOW:
  2162. pTtm->iFadeState += TT_FADESHOWINCREMENT;
  2163. if (pTtm->iFadeState > TT_MAXFADESHOW)
  2164. {
  2165. pTtm->iFadeState = TT_MAXFADESHOW;
  2166. KillTimer(pTtm->_ci.hwnd, TTT_FADESHOW);
  2167. }
  2168. SetLayeredWindowAttributes(pTtm->_ci.hwnd, 0, (BYTE)pTtm->iFadeState, LWA_ALPHA);
  2169. break;
  2170. case TTT_FADEHIDE:
  2171. pTtm->iFadeState -= TT_FADEHIDEDECREMENT;
  2172. if (pTtm->iFadeState <= 0)
  2173. {
  2174. KillTimer(pTtm->_ci.hwnd, TTT_FADEHIDE);
  2175. pTtm->iFadeState = 0;
  2176. ShowWindow(pTtm->_ci.hwnd, SW_HIDE);
  2177. }
  2178. SetLayeredWindowAttributes(pTtm->_ci.hwnd, 0, (BYTE)pTtm->iFadeState, LWA_ALPHA);
  2179. break;
  2180. }
  2181. // punt all timers in track mode
  2182. if (pTtm->dwFlags & TRACKMODE)
  2183. return;
  2184. switch (id)
  2185. {
  2186. case TTT_AUTOPOP:
  2187. TTCheckCursorPos(pTtm);
  2188. if (pTtm->pCurTool)
  2189. {
  2190. DebugMsg(TF_TT, TEXT("ToolTips: Auto popping"));
  2191. ShowVirtualBubble(pTtm);
  2192. }
  2193. break;
  2194. case TTT_POP:
  2195. // this could be started up again by a slight mouse touch
  2196. if (pTtm->dwFlags & VIRTUALBUBBLEUP)
  2197. {
  2198. KillTimer(pTtm->_ci.hwnd, TTT_POP);
  2199. }
  2200. TTCheckCursorPos(pTtm);
  2201. break;
  2202. case TTT_INITIAL:
  2203. if (ToolHasMoved(pTtm))
  2204. {
  2205. // this means the timer went off
  2206. // without us getting a mouse move
  2207. // which means they left our tools.
  2208. PopBubble(pTtm);
  2209. break;
  2210. }
  2211. // else fall through
  2212. case TTT_RESHOW:
  2213. pTool = TTToolAtMessagePos(pTtm);
  2214. if (!pTool)
  2215. {
  2216. if (pTtm->pCurTool)
  2217. PopBubble(pTtm);
  2218. }
  2219. else if (pTtm->dwFlags & ACTIVE)
  2220. {
  2221. if (id == TTT_RESHOW)
  2222. {
  2223. // this will force a re-show
  2224. pTtm->dwFlags &= ~(BUBBLEUP|VIRTUALBUBBLEUP);
  2225. }
  2226. ShowBubbleForTool(pTtm, pTool);
  2227. }
  2228. break;
  2229. }
  2230. }
  2231. // TTRender is theme aware (RENDERS)
  2232. BOOL TTRender(CToolTipsMgr *pTtm, HDC hdc)
  2233. {
  2234. BOOL bRet = FALSE;
  2235. RECT rc;
  2236. if (pTtm->pCurTool && MarkupCurToolText(pTtm))
  2237. {
  2238. UINT uFlags;
  2239. NMTTCUSTOMDRAW nm;
  2240. UINT uDefDrawFlags = 0;
  2241. LPRECT prcMargin = &pTtm->rcMargin;
  2242. HBRUSH hbr;
  2243. DWORD dwCustomDraw = CDRF_DODEFAULT;
  2244. uFlags = 0;
  2245. if ((pTtm->pCurTool->uFlags & TTF_RTLREADING) || (pTtm->_ci.dwExStyle & WS_EX_RTLREADING))
  2246. uFlags |= ETO_RTLREADING;
  2247. SelectObject(hdc, pTtm->hFont);
  2248. GetClientRect(pTtm->_ci.hwnd, &rc);
  2249. SetTextColor(hdc, pTtm->clrTipText);
  2250. /* If we support pre-Draw text then call the client allowing them to modify
  2251. / the item, and then render. Otherwise just use ExTextOut */
  2252. nm.nmcd.hdr.hwndFrom = pTtm->_ci.hwnd;
  2253. nm.nmcd.hdr.idFrom = pTtm->pCurTool->uId;
  2254. nm.nmcd.hdr.code = NM_CUSTOMDRAW;
  2255. nm.nmcd.hdc = hdc;
  2256. nm.nmcd.dwDrawStage = CDDS_PREPAINT;
  2257. // ADJUSTRECT! Keep TTAdjustRect and TTGetTipPosition in sync.
  2258. nm.nmcd.rc.left = rc.left + XTEXTOFFSET*g_cxBorder + prcMargin->left;
  2259. nm.nmcd.rc.right = rc.right - XTEXTOFFSET*g_cxBorder - prcMargin->right;
  2260. nm.nmcd.rc.top = rc.top + YTEXTOFFSET*g_cyBorder + prcMargin->top;
  2261. nm.nmcd.rc.bottom = rc.bottom - YTEXTOFFSET*g_cyBorder - prcMargin->bottom;
  2262. if (pTtm->_ci.style & TTS_BALLOON)
  2263. {
  2264. InflateRect(&(nm.nmcd.rc), -XBALLOONOFFSET, -YBALLOONOFFSET);
  2265. if (!pTtm->fUnderStem)
  2266. OffsetRect(&(nm.nmcd.rc), 0, pTtm->iStemHeight);
  2267. }
  2268. if (pTtm->iMaxTipWidth == -1)
  2269. uDefDrawFlags = DT_SINGLELINE |DT_LEFT;
  2270. else
  2271. uDefDrawFlags = DT_LEFT | DT_WORDBREAK | DT_EXPANDTABS | DT_EXTERNALLEADING;
  2272. if (pTtm->_ci.style & TTS_NOPREFIX)
  2273. uDefDrawFlags |= DT_NOPREFIX;
  2274. if ((pTtm->pCurTool->uFlags & TTF_RTLREADING) || (pTtm->_ci.dwExStyle & WS_EX_RTLREADING))
  2275. uDefDrawFlags |= DT_RTLREADING;
  2276. //
  2277. // Make it right aligned, if requested. [samera]
  2278. //
  2279. if (pTtm->pCurTool->uFlags & TTF_RIGHT)
  2280. uDefDrawFlags |= DT_RIGHT;
  2281. nm.uDrawFlags = uDefDrawFlags;
  2282. dwCustomDraw = (DWORD)SendNotifyEx(pTtm->pCurTool->hwnd, (HWND) -1,
  2283. 0, (NMHDR*) &nm,
  2284. (pTtm->pCurTool->uFlags & TTF_UNICODE) ? 1 : 0);
  2285. // did the owner do custom draw? yes, we're done
  2286. if (dwCustomDraw == CDRF_SKIPDEFAULT)
  2287. return TRUE;
  2288. // if this fails, it may be the a dither...
  2289. // in which case, we can't set the bk color
  2290. hbr = CreateSolidBrush(pTtm->clrTipBk);
  2291. FillRect(hdc, &rc, hbr);
  2292. DeleteObject(hbr);
  2293. SetBkMode(hdc, TRANSPARENT);
  2294. uFlags |= ETO_CLIPPED;
  2295. // Account for off-by-one. Something wierd about DrawText
  2296. // clips the bottom-most pixelrow, so increase one more
  2297. // into the margin space.
  2298. // ADJUSTRECT! Keep TTAdjustRect in sync.
  2299. nm.nmcd.rc.bottom++;
  2300. nm.nmcd.rc.right++;
  2301. // if in balloon style the text is already indented so no need for inflate..
  2302. if (pTtm->cchTipTitle > 0 && !(pTtm->_ci.style & TTS_BALLOON))
  2303. InflateRect(&nm.nmcd.rc, -XBALLOONOFFSET, -YBALLOONOFFSET);
  2304. if (!TTRenderTitledTip(pTtm, hdc, FALSE, &nm.nmcd.rc, uDefDrawFlags))
  2305. {
  2306. GetCurToolBestMarkup(pTtm)->SetRenderFlags(nm.uDrawFlags);
  2307. GetCurToolBestMarkup(pTtm)->DrawText(hdc, &nm.nmcd.rc);
  2308. }
  2309. if (pTtm->_ci.style & TTS_BALLOON)
  2310. {
  2311. HRGN rgn = CreateRectRgn(1,1,2,2);
  2312. if (rgn)
  2313. {
  2314. int iRet = GetWindowRgn(pTtm->_ci.hwnd, rgn);
  2315. if (iRet != ERROR)
  2316. {
  2317. COLORREF crBrdr = pTtm->clrTipText;
  2318. HBRUSH hbr = CreateSolidBrush(crBrdr);
  2319. FrameRgn(hdc, rgn, hbr, 1, 1);
  2320. DeleteObject(hbr);
  2321. }
  2322. DeleteObject(rgn);
  2323. }
  2324. }
  2325. // notify parent afterwards if they want us to
  2326. if (!(dwCustomDraw & CDRF_SKIPDEFAULT) &&
  2327. dwCustomDraw & CDRF_NOTIFYPOSTPAINT)
  2328. {
  2329. // Convert PREPAINT to POSTPAINT and ITEMPREPAINT to ITEMPOSTPAINT
  2330. COMPILETIME_ASSERT(CDDS_POSTPAINT - CDDS_PREPAINT ==
  2331. CDDS_ITEMPOSTPAINT - CDDS_ITEMPREPAINT);
  2332. nm.nmcd.dwDrawStage += CDDS_POSTPAINT - CDDS_PREPAINT;
  2333. SendNotifyEx(pTtm->pCurTool->hwnd, (HWND) -1,
  2334. 0, (NMHDR*) &nm,
  2335. (pTtm->pCurTool->uFlags & TTF_UNICODE) ? 1 : 0);
  2336. }
  2337. bRet = TRUE;
  2338. }
  2339. return bRet;
  2340. }
  2341. void TTOnPaint(CToolTipsMgr *pTtm)
  2342. {
  2343. PAINTSTRUCT ps;
  2344. HDC hdc = BeginPaint(pTtm->_ci.hwnd, &ps);
  2345. if (!TTRender(pTtm, hdc))
  2346. {
  2347. DebugMsg(TF_TT, TEXT("TTOnPaint render failed popping bubble"));
  2348. PopBubble(pTtm);
  2349. }
  2350. EndPaint(pTtm->_ci.hwnd, &ps);
  2351. pTtm->fEverShown = TRUE; // See TTOnFirstShow
  2352. }
  2353. // ToolTipsWndProc is theme aware
  2354. LRESULT WINAPI ToolTipsWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2355. {
  2356. TOOLINFO *pTool;
  2357. TOOLINFO *pToolSrc;
  2358. CToolTipsMgr *pTtm = (CToolTipsMgr *) GetWindowPtr(hwnd, 0);
  2359. POINT pt;
  2360. if (!pTtm && uMsg != WM_CREATE)
  2361. goto DoDefault;
  2362. switch (uMsg)
  2363. {
  2364. case TTM_ACTIVATE:
  2365. if (wParam)
  2366. {
  2367. pTtm->dwFlags |= ACTIVE;
  2368. }
  2369. else
  2370. {
  2371. PopBubble(pTtm);
  2372. pTtm->dwFlags &= ~(ACTIVE | TRACKMODE);
  2373. }
  2374. break;
  2375. case TTM_SETDELAYTIME:
  2376. TTSetDelayTime(pTtm, wParam, lParam);
  2377. break;
  2378. case TTM_GETDELAYTIME:
  2379. return (LRESULT)(UINT)TTGetDelayTime(pTtm, wParam);
  2380. case TTM_ADDTOOLA:
  2381. {
  2382. TOOLINFOW ti;
  2383. if (!lParam)
  2384. return FALSE;
  2385. if (!ThunkToolInfoAtoW((LPTOOLINFOA)lParam, &ti, TRUE, pTtm->_ci.uiCodePage))
  2386. return FALSE;
  2387. LRESULT res = AddTool(pTtm, &ti);
  2388. if ((ti.uFlags & TTF_MEMALLOCED) && (ti.lpszText != LPSTR_TEXTCALLBACK))
  2389. LocalFree(ti.lpszText);
  2390. return res;
  2391. }
  2392. case TTM_ADDTOOL:
  2393. if (!lParam)
  2394. return FALSE;
  2395. return AddTool(pTtm, (LPTOOLINFO)lParam);
  2396. case TTM_DELTOOLA:
  2397. {
  2398. TOOLINFOW ti;
  2399. if (!lParam)
  2400. return FALSE;
  2401. if (!ThunkToolInfoAtoW((LPTOOLINFOA)lParam, &ti, FALSE, pTtm->_ci.uiCodePage))
  2402. break;
  2403. DeleteTool(pTtm, &ti);
  2404. break;
  2405. }
  2406. case TTM_DELTOOL:
  2407. if (!lParam)
  2408. return FALSE;
  2409. DeleteTool(pTtm, (LPTOOLINFO)lParam);
  2410. break;
  2411. case TTM_NEWTOOLRECTA:
  2412. {
  2413. TOOLINFOW ti;
  2414. if (!lParam)
  2415. return FALSE;
  2416. if (!ThunkToolInfoAtoW((LPTOOLINFOA)lParam, &ti, FALSE, pTtm->_ci.uiCodePage))
  2417. break;
  2418. pTool = FindTool(pTtm, &ti);
  2419. if (pTool)
  2420. pTool->rect = ((LPTOOLINFOA)lParam)->rect;
  2421. break;
  2422. }
  2423. case TTM_NEWTOOLRECT:
  2424. if (!lParam)
  2425. return FALSE;
  2426. pTool = FindTool(pTtm, (LPTOOLINFO)lParam);
  2427. if (pTool)
  2428. pTool->rect = ((LPTOOLINFO)lParam)->rect;
  2429. break;
  2430. case TTM_GETTOOLCOUNT:
  2431. return pTtm->iNumTools;
  2432. case TTM_GETTOOLINFOA:
  2433. {
  2434. TOOLINFOW ti;
  2435. if (!lParam)
  2436. return FALSE;
  2437. if (!ThunkToolInfoAtoW((LPTOOLINFOA)lParam, &ti, FALSE, pTtm->_ci.uiCodePage))
  2438. return FALSE;
  2439. pToolSrc = FindTool(pTtm, &ti);
  2440. return (LRESULT)(UINT)CopyToolInfoA(pToolSrc, (LPTOOLINFOA)lParam, pTtm->_ci.uiCodePage);
  2441. }
  2442. case TTM_GETCURRENTTOOLA:
  2443. if (lParam)
  2444. return (LRESULT)(UINT)CopyToolInfoA(pTtm->pCurTool, (LPTOOLINFOA)lParam, pTtm->_ci.uiCodePage);
  2445. else
  2446. return BOOLFROMPTR(pTtm->pCurTool);
  2447. case TTM_ENUMTOOLSA:
  2448. if (wParam < (UINT)pTtm->iNumTools)
  2449. {
  2450. pToolSrc = &pTtm->tools[wParam];
  2451. return (LRESULT)(UINT)CopyToolInfoA(pToolSrc, (LPTOOLINFOA)lParam, pTtm->_ci.uiCodePage);
  2452. }
  2453. return FALSE;
  2454. case TTM_GETTOOLINFO:
  2455. if (!lParam)
  2456. return FALSE;
  2457. pToolSrc = FindTool(pTtm, (LPTOOLINFO)lParam);
  2458. return (LRESULT)(UINT)CopyToolInfo(pToolSrc, (LPTOOLINFO)lParam);
  2459. case TTM_GETCURRENTTOOL:
  2460. if (lParam)
  2461. return (LRESULT)(UINT)CopyToolInfo(pTtm->pCurTool, (LPTOOLINFO)lParam);
  2462. else
  2463. return BOOLFROMPTR(pTtm->pCurTool);
  2464. case TTM_ENUMTOOLS:
  2465. if (wParam < (UINT)pTtm->iNumTools)
  2466. {
  2467. pToolSrc = &pTtm->tools[wParam];
  2468. return (LRESULT)(UINT)CopyToolInfo(pToolSrc, (LPTOOLINFO)lParam);
  2469. }
  2470. return FALSE;
  2471. case TTM_SETTOOLINFOA:
  2472. {
  2473. TOOLINFOW ti;
  2474. if (!lParam)
  2475. return FALSE;
  2476. if (!ThunkToolInfoAtoW((LPTOOLINFOA)lParam, &ti, TRUE, pTtm->_ci.uiCodePage))
  2477. return FALSE;
  2478. pTool = FindTool(pTtm, &ti);
  2479. if (pTool)
  2480. {
  2481. TTSetTipText(pTool, NULL);
  2482. CopyTool(pTool, &ti);
  2483. TTSetTipText(pTool, ti.lpszText);
  2484. if (pTool == pTtm->pCurTool)
  2485. {
  2486. DoShowBubble(pTtm);
  2487. }
  2488. }
  2489. if ((ti.uFlags & TTF_MEMALLOCED) && (ti.lpszText != LPSTR_TEXTCALLBACK))
  2490. LocalFree(ti.lpszText);
  2491. break;
  2492. }
  2493. case TTM_SETTOOLINFO:
  2494. if (!lParam)
  2495. return FALSE;
  2496. pTool = FindTool(pTtm, (LPTOOLINFO)lParam);
  2497. if (pTool)
  2498. {
  2499. TTSetTipText(pTool, NULL);
  2500. CopyTool(pTool, (LPTOOLINFO)lParam);
  2501. TTSetTipText(pTool, ((LPTOOLINFO)lParam)->lpszText);
  2502. if (pTool == pTtm->pCurTool)
  2503. {
  2504. DoShowBubble(pTtm);
  2505. }
  2506. }
  2507. break;
  2508. case TTM_HITTESTA:
  2509. #define lphitinfoA ((LPHITTESTINFOA)lParam)
  2510. if (!lParam)
  2511. return FALSE;
  2512. pTool = GetToolAtPoint(pTtm, lphitinfoA->hwnd, lphitinfoA->pt.x, lphitinfoA->pt.y, HTERROR, TRUE);
  2513. if (pTool)
  2514. {
  2515. ThunkToolInfoWtoA(pTool, (LPTOOLINFOA)(&(lphitinfoA->ti)), pTtm->_ci.uiCodePage);
  2516. return TRUE;
  2517. }
  2518. return FALSE;
  2519. case TTM_HITTEST:
  2520. #define lphitinfo ((LPHITTESTINFO)lParam)
  2521. if (!lParam)
  2522. return FALSE;
  2523. pTool = GetToolAtPoint(pTtm, lphitinfo->hwnd, lphitinfo->pt.x, lphitinfo->pt.y, HTERROR, TRUE);
  2524. if (pTool)
  2525. {
  2526. // for back compat... if thesize isn't set right, we only give
  2527. // them the win95 amount.
  2528. if (lphitinfo->ti.cbSize != sizeof(TTTOOLINFO))
  2529. {
  2530. *((WIN95TTTOOLINFO*)&lphitinfo->ti) = *(WIN95TTTOOLINFO*)pTool;
  2531. }
  2532. else
  2533. {
  2534. lphitinfo->ti = *pTool;
  2535. }
  2536. return TRUE;
  2537. }
  2538. return FALSE;
  2539. case TTM_GETTEXTA:
  2540. {
  2541. LPWSTR lpszTemp;
  2542. TOOLINFOW ti;
  2543. if (!lParam || !((LPTOOLINFOA)lParam)->lpszText)
  2544. return FALSE;
  2545. if (!ThunkToolInfoAtoW((LPTOOLINFOA)lParam, &ti, FALSE, pTtm->_ci.uiCodePage))
  2546. break;
  2547. ((LPTOOLINFOA)lParam)->lpszText[0] = 0;
  2548. pTool = FindTool(pTtm, &ti);
  2549. lpszTemp = GetToolText(pTtm, pTool);
  2550. if (lpszTemp)
  2551. {
  2552. WideCharToMultiByte(pTtm->_ci.uiCodePage,
  2553. 0,
  2554. lpszTemp,
  2555. -1,
  2556. (((LPTOOLINFOA)lParam)->lpszText),
  2557. 80, NULL, NULL);
  2558. LocalFree(lpszTemp);
  2559. }
  2560. }
  2561. break;
  2562. case TTM_GETTEXT:
  2563. {
  2564. if (!lParam || !pTtm || !((LPTOOLINFO)lParam)->lpszText)
  2565. return FALSE;
  2566. ((LPTOOLINFO)lParam)->lpszText[0] = 0;
  2567. pTool = FindTool(pTtm, (LPTOOLINFO)lParam);
  2568. LPTSTR lpszTemp = GetToolText(pTtm, pTool);
  2569. if (lpszTemp)
  2570. {
  2571. // REVIEW: message parameters do not indicate the size of the
  2572. // destination buffer.
  2573. StringCchCopy((((LPTOOLINFO)lParam)->lpszText), lstrlen(lpszTemp)+1, lpszTemp);
  2574. LocalFree(lpszTemp);
  2575. }
  2576. break;
  2577. }
  2578. case WM_GETTEXTLENGTH:
  2579. case WM_GETTEXT:
  2580. {
  2581. TCHAR *pszDest = uMsg == WM_GETTEXT ? (TCHAR *)lParam : NULL;
  2582. LRESULT lres = 0;
  2583. // Pre-terminate the string just in case
  2584. if (pszDest && wParam)
  2585. pszDest[0] = 0;
  2586. if (pTtm)
  2587. {
  2588. LPTSTR lpszStr = GetCurToolText(pTtm);
  2589. if (lpszStr)
  2590. {
  2591. if (pszDest && wParam)
  2592. {
  2593. StringCchCopy(pszDest, (int)wParam, lpszStr);
  2594. lres = lstrlen(pszDest);
  2595. }
  2596. else
  2597. {
  2598. lres = lstrlen(lpszStr);
  2599. }
  2600. LocalFree(lpszStr);
  2601. }
  2602. }
  2603. return lres;
  2604. }
  2605. case TTM_RELAYEVENT:
  2606. {
  2607. MSG* pmsg = ((MSG*)lParam);
  2608. if (!pmsg)
  2609. return FALSE;
  2610. HandleRelayedMessage(pTtm, pmsg->hwnd, pmsg->message, pmsg->wParam, pmsg->lParam);
  2611. }
  2612. break;
  2613. // this is here for people to subclass and fake out what we
  2614. // think the window from point is. this facilitates "transparent" windows
  2615. case TTM_WINDOWFROMPOINT:
  2616. {
  2617. HWND hwndPt = WindowFromPoint(*((POINT *)lParam));
  2618. DebugMsg(TF_TT, TEXT("TTM_WINDOWFROMPOINT %x"), hwndPt);
  2619. return (LRESULT)hwndPt;
  2620. }
  2621. case TTM_UPDATETIPTEXTA:
  2622. {
  2623. TOOLINFOW ti;
  2624. if (lParam)
  2625. {
  2626. if (!ThunkToolInfoAtoW((LPTOOLINFOA)lParam, &ti, TRUE, pTtm->_ci.uiCodePage))
  2627. {
  2628. break;
  2629. }
  2630. TTUpdateTipText(pTtm, &ti);
  2631. if ((ti.uFlags & TTF_MEMALLOCED) && (ti.lpszText != LPSTR_TEXTCALLBACK))
  2632. {
  2633. LocalFree(ti.lpszText);
  2634. }
  2635. }
  2636. break;
  2637. }
  2638. case TTM_UPDATETIPTEXT:
  2639. if (lParam)
  2640. TTUpdateTipText(pTtm, (LPTOOLINFO)lParam);
  2641. break;
  2642. /* Pop the current tooltip if there is one displayed, ensuring that the virtual
  2643. / bubble is also discarded. */
  2644. case TTM_POP:
  2645. {
  2646. if (pTtm->dwFlags & BUBBLEUP)
  2647. PopBubble(pTtm);
  2648. pTtm->dwFlags &= ~VIRTUALBUBBLEUP;
  2649. break;
  2650. }
  2651. case TTM_POPUP:
  2652. {
  2653. TOOLINFO *pTool;
  2654. pTool = TTToolAtMessagePos(pTtm);
  2655. if (pTool && pTtm->dwFlags & ACTIVE)
  2656. {
  2657. // this will force a re-show
  2658. pTtm->dwFlags &= ~(BUBBLEUP|VIRTUALBUBBLEUP);
  2659. ShowBubbleForTool(pTtm, pTool);
  2660. return TRUE;
  2661. }
  2662. }
  2663. break;
  2664. case TTM_TRACKPOSITION:
  2665. if ((GET_X_LPARAM(lParam) != pTtm->ptTrack.x) ||
  2666. (GET_Y_LPARAM(lParam) != pTtm->ptTrack.y))
  2667. {
  2668. pTtm->ptTrack.x = GET_X_LPARAM(lParam);
  2669. pTtm->ptTrack.y = GET_Y_LPARAM(lParam);
  2670. // if track mode is in effect, update the position
  2671. if ((pTtm->dwFlags & TRACKMODE) &&
  2672. pTtm->pCurTool)
  2673. {
  2674. DoShowBubble(pTtm);
  2675. }
  2676. }
  2677. break;
  2678. case TTM_UPDATE:
  2679. if (!lParam ||
  2680. lParam == (LPARAM)pTtm->pCurTool)
  2681. {
  2682. DoShowBubble(pTtm);
  2683. }
  2684. break;
  2685. case TTM_TRACKACTIVATE:
  2686. if (pTtm->dwFlags & ACTIVE)
  2687. {
  2688. if (wParam && lParam)
  2689. wParam = TRACKMODE;
  2690. else
  2691. wParam = 0;
  2692. if ((wParam ^ pTtm->dwFlags) & TRACKMODE)
  2693. {
  2694. // if the trackmode changes by this..
  2695. PopBubble2(pTtm, FALSE);
  2696. pTtm->dwFlags ^= TRACKMODE;
  2697. if (wParam)
  2698. {
  2699. // turning on track mode
  2700. pTool = FindTool(pTtm, (LPTOOLINFO)lParam);
  2701. if (pTool)
  2702. {
  2703. // only if the tool is found
  2704. ShowBubbleForTool(pTtm, pTool);
  2705. }
  2706. }
  2707. }
  2708. }
  2709. return TRUE;
  2710. case TTM_SETTIPBKCOLOR:
  2711. if (pTtm->clrTipBk != (COLORREF)wParam)
  2712. {
  2713. pTtm->clrTipBk = (COLORREF)wParam;
  2714. InvalidateRgn(pTtm->_ci.hwnd,NULL,TRUE);
  2715. }
  2716. pTtm->fBkColorSet = TRUE;
  2717. break;
  2718. case TTM_GETTIPBKCOLOR:
  2719. return (LRESULT)(UINT)pTtm->clrTipBk;
  2720. case TTM_SETTIPTEXTCOLOR:
  2721. if (pTtm->clrTipText != (COLORREF)wParam)
  2722. {
  2723. InvalidateRgn(pTtm->_ci.hwnd,NULL,TRUE);
  2724. pTtm->clrTipText = (COLORREF)wParam;
  2725. }
  2726. pTtm->fTextColorSet = TRUE;
  2727. break;
  2728. case TTM_GETTIPTEXTCOLOR:
  2729. return (LRESULT)(UINT)pTtm->clrTipText;
  2730. case TTM_SETMAXTIPWIDTH:
  2731. {
  2732. int iOld = pTtm->iMaxTipWidth;
  2733. pTtm->iMaxTipWidth = (int)lParam;
  2734. return iOld;
  2735. }
  2736. case TTM_GETMAXTIPWIDTH:
  2737. return pTtm->iMaxTipWidth;
  2738. case TTM_SETMARGIN:
  2739. if (lParam)
  2740. pTtm->rcMargin = *(LPRECT)lParam;
  2741. break;
  2742. case TTM_GETMARGIN:
  2743. if (lParam)
  2744. *(LPRECT)lParam = pTtm->rcMargin;
  2745. break;
  2746. case TTM_GETBUBBLESIZE:
  2747. if (lParam)
  2748. {
  2749. pTool = FindTool(pTtm, (LPTOOLINFO)lParam);
  2750. if (pTool)
  2751. {
  2752. // We actually have to insert this text into our markup to get the proper tipsize
  2753. // We don't reset it later because DoShowBubble and TTRender do when they draw.
  2754. if (CheckToolMarkup(pTool))
  2755. {
  2756. LPTSTR psz = GetToolText(pTtm, pTool);
  2757. if (psz)
  2758. {
  2759. GetToolMarkup(pTool)->SetText(psz);
  2760. LocalFree(psz);
  2761. }
  2762. }
  2763. int cxText, cyText, cxMargin, cyMargin, iBubbleWidth, iBubbleHeight;
  2764. TTGetTipSize(pTtm, pTool, &cxText, &cyText);
  2765. cxMargin = pTtm->rcMargin.left + pTtm->rcMargin.right;
  2766. cyMargin = pTtm->rcMargin.top + pTtm->rcMargin.bottom;
  2767. iBubbleWidth = 2*XTEXTOFFSET * g_cxBorder + cxText + cxMargin;
  2768. iBubbleHeight = 2*YTEXTOFFSET * g_cyBorder + cyText + cyMargin;
  2769. if (pTtm->_ci.style & TTS_BALLOON)
  2770. {
  2771. iBubbleWidth += 2*XBALLOONOFFSET;
  2772. iBubbleHeight += 2*YBALLOONOFFSET;
  2773. }
  2774. return MAKELONG(iBubbleWidth, iBubbleHeight);
  2775. }
  2776. }
  2777. break;
  2778. case TTM_ADJUSTRECT:
  2779. return TTAdjustRect(pTtm, BOOLFROMPTR(wParam), (LPRECT)lParam);
  2780. case TTM_SETTITLEA:
  2781. {
  2782. TCHAR szTitle[MAX_TIP_CHARACTERS];
  2783. pTtm->uTitleBitmap = (UINT)wParam;
  2784. Str_Set(&pTtm->lpTipTitle, NULL);
  2785. pTtm->iTitleHeight = 0;
  2786. TTCreateTitleBitmaps(pTtm);
  2787. if (lParam)
  2788. {
  2789. pTtm->cchTipTitle = lstrlenA((LPCSTR)lParam);
  2790. if (pTtm->cchTipTitle < ARRAYSIZE(szTitle))
  2791. {
  2792. ConvertAToWN(pTtm->_ci.uiCodePage, szTitle, ARRAYSIZE(szTitle),
  2793. (LPCSTR)lParam, -1);
  2794. Str_Set(&pTtm->lpTipTitle, szTitle);
  2795. if (pTtm->pCurTool)
  2796. {
  2797. INT cxText, cyText;
  2798. // recalculate the tip size
  2799. TTGetTipSize(pTtm, pTtm->pCurTool, &cxText, &cyText);
  2800. }
  2801. return TRUE;
  2802. }
  2803. }
  2804. pTtm->cchTipTitle = 0;
  2805. return FALSE;
  2806. }
  2807. break;
  2808. case TTM_SETTITLE:
  2809. {
  2810. pTtm->uTitleBitmap = (UINT)wParam;
  2811. Str_Set(&pTtm->lpTipTitle, NULL);
  2812. pTtm->iTitleHeight = 0;
  2813. TTCreateTitleBitmaps(pTtm);
  2814. if (lParam)
  2815. {
  2816. pTtm->cchTipTitle = lstrlen((LPCTSTR)lParam);
  2817. if (pTtm->cchTipTitle < MAX_TIP_CHARACTERS)
  2818. {
  2819. Str_Set(&pTtm->lpTipTitle, (LPCTSTR)lParam);
  2820. if (pTtm->pCurTool)
  2821. {
  2822. INT cxText, cyText;
  2823. // recalculate the tip size
  2824. TTGetTipSize(pTtm, pTtm->pCurTool, &cxText, &cyText);
  2825. }
  2826. return TRUE;
  2827. }
  2828. }
  2829. pTtm->cchTipTitle = 0;
  2830. return FALSE;
  2831. }
  2832. break;
  2833. case TTM_GETTITLE:
  2834. {
  2835. if (wParam != 0 || lParam == 0 || pTtm->lpTipTitle == NULL)
  2836. return FALSE;
  2837. TTGETTITLE* pgt = (TTGETTITLE*)lParam;
  2838. if (pgt->dwSize != sizeof(TTGETTITLE) ||
  2839. pgt->cch == 0 ||
  2840. pgt->pszTitle == NULL)
  2841. {
  2842. return FALSE;
  2843. }
  2844. StringCchCopy(pgt->pszTitle, pgt->cch, pTtm->lpTipTitle);
  2845. pgt->uTitleBitmap = pTtm->uTitleBitmap;
  2846. return TRUE;
  2847. }
  2848. break;
  2849. case TTM_SETWINDOWTHEME:
  2850. if (lParam)
  2851. {
  2852. SetWindowTheme(hwnd, (LPWSTR)lParam, NULL);
  2853. }
  2854. break;
  2855. /* uMsgs that REALLY came for me. */
  2856. case WM_CREATE:
  2857. {
  2858. DWORD dwBits, dwValue;
  2859. pTtm = ToolTipsMgrCreate(hwnd, (LPCREATESTRUCT)lParam);
  2860. if (!pTtm)
  2861. return -1;
  2862. // Create a markup for compatibility with old TOOLINFO
  2863. if (SUCCEEDED(Markup_Create(pTtm, NULL, NULL, IID_PPV_ARG(IControlMarkup, &pTtm->pMarkup))))
  2864. {
  2865. SetWindowPtr(hwnd, 0, pTtm);
  2866. SetWindowBits(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TOOLWINDOW, WS_EX_LAYERED | WS_EX_TOOLWINDOW);
  2867. dwBits = WS_CHILD | WS_POPUP | WS_BORDER | WS_DLGFRAME;
  2868. dwValue = WS_POPUP | WS_BORDER;
  2869. // we don't want border for balloon style
  2870. if (pTtm->_ci.style & TTS_BALLOON)
  2871. dwValue &= ~WS_BORDER;
  2872. SetWindowBits(hwnd, GWL_STYLE, dwBits, dwValue);
  2873. // Initialize themes
  2874. pTtm->hTheme = OpenThemeData(pTtm->_ci.hwnd, L"Tooltip");
  2875. TTSetFont(pTtm, 0, FALSE);
  2876. break;
  2877. }
  2878. else
  2879. {
  2880. LocalFree(pTtm);
  2881. return -1;
  2882. }
  2883. }
  2884. break;
  2885. case WM_TIMER:
  2886. TTHandleTimer(pTtm, wParam);
  2887. break;
  2888. case WM_NCHITTEST:
  2889. // we should not return HTTRANSPARENT here because then we don't receive the mouse events
  2890. // and we cannot forward them down to our parent. but because of the backcompat we keep doing
  2891. // it unless we are using comctl32 v5 or greater
  2892. //
  2893. // If we are inside TTWindowFromPoint, then respect transparency
  2894. // even on v5 clients.
  2895. //
  2896. // Otherwise, your tooltips flicker because the tip appears,
  2897. // then WM_NCHITTEST says "not over the tool any more" (because
  2898. // it's over the tooltip), so the bubble pops, and then the tip
  2899. // reappears, etc.
  2900. if (pTtm && (pTtm->_ci.iVersion < 5 || pTtm->fInWindowFromPoint) &&
  2901. pTtm->pCurTool && (pTtm->pCurTool->uFlags & TTF_TRANSPARENT))
  2902. {
  2903. return HTTRANSPARENT;
  2904. }
  2905. goto DoDefault;
  2906. case WM_MOUSEMOVE:
  2907. pt.x = GET_X_LPARAM(lParam);
  2908. pt.y = GET_Y_LPARAM(lParam);
  2909. // the cursor moved onto the tips window.
  2910. if (!(pTtm->dwFlags & TRACKMODE) && pTtm->pCurTool && !(pTtm->pCurTool->uFlags & TTF_TRANSPARENT))
  2911. PopBubble(pTtm);
  2912. if ((pTtm->_ci.style & TTS_CLOSE))
  2913. {
  2914. if (PtInRect(&pTtm->rcClose, pt))
  2915. {
  2916. pTtm->iStateId = TTCS_HOT;
  2917. InvalidateRect(pTtm->_ci.hwnd, &pTtm->rcClose, FALSE);
  2918. }
  2919. else if (pTtm->iStateId == TTCS_HOT)
  2920. {
  2921. pTtm->iStateId = TTCS_NORMAL;
  2922. InvalidateRect(pTtm->_ci.hwnd, &pTtm->rcClose, FALSE);
  2923. }
  2924. }
  2925. // fall through
  2926. case WM_LBUTTONDOWN:
  2927. case WM_RBUTTONDOWN:
  2928. case WM_MBUTTONDOWN:
  2929. {
  2930. BOOL fForward = TRUE;
  2931. // handle link clicking
  2932. pt.x = GET_X_LPARAM(lParam);
  2933. pt.y = GET_Y_LPARAM(lParam);
  2934. // Never forward if in the close button
  2935. if (PtInRect(&pTtm->rcClose, pt))
  2936. fForward = FALSE;
  2937. if (uMsg == WM_LBUTTONDOWN)
  2938. {
  2939. if ((pTtm->_ci.style & TTS_CLOSE) &&
  2940. pTtm->iStateId == TTCS_HOT)
  2941. {
  2942. pTtm->iStateId = TTCS_PRESSED;
  2943. InvalidateRect(pTtm->_ci.hwnd, &pTtm->rcClose, FALSE);
  2944. }
  2945. else
  2946. {
  2947. // Don't forward if clicking on a link
  2948. if (S_OK == GetCurToolBestMarkup(pTtm)->OnButtonDown(pt))
  2949. fForward = FALSE;
  2950. }
  2951. }
  2952. // Handle other actions
  2953. if (fForward && pTtm->pCurTool && (pTtm->pCurTool->uFlags & TTF_TRANSPARENT))
  2954. {
  2955. MapWindowPoints(pTtm->_ci.hwnd, pTtm->pCurTool->hwnd, &pt, 1);
  2956. SendMessage(pTtm->pCurTool->hwnd, uMsg, wParam, MAKELPARAM(pt.x, pt.y));
  2957. }
  2958. }
  2959. break;
  2960. case WM_LBUTTONUP:
  2961. // handle link clicking
  2962. pt.x = GET_X_LPARAM(lParam);
  2963. pt.y = GET_Y_LPARAM(lParam);
  2964. if (pTtm->iStateId == TTCS_PRESSED)
  2965. {
  2966. pTtm->iStateId = TTCS_NORMAL;
  2967. InvalidateRect(pTtm->_ci.hwnd, &pTtm->rcClose, FALSE);
  2968. }
  2969. if ((pTtm->_ci.style & TTS_CLOSE) &&
  2970. PtInRect(&pTtm->rcClose, pt))
  2971. {
  2972. PopBubble(pTtm);
  2973. }
  2974. else
  2975. {
  2976. GetCurToolBestMarkup(pTtm)->OnButtonUp(pt);
  2977. }
  2978. break;
  2979. case WM_SYSCOLORCHANGE:
  2980. InitGlobalColors();
  2981. if (pTtm)
  2982. {
  2983. if (!pTtm->fBkColorSet)
  2984. pTtm->clrTipBk = g_clrInfoBk;
  2985. if (!pTtm->fTextColorSet)
  2986. pTtm->clrTipText = g_clrInfoText;
  2987. }
  2988. break;
  2989. case WM_WININICHANGE:
  2990. InitGlobalMetrics(wParam);
  2991. if (pTtm->fMyFont)
  2992. TTSetFont(pTtm, 0, FALSE);
  2993. break;
  2994. case WM_PAINT:
  2995. TTOnPaint(pTtm);
  2996. break;
  2997. case WM_SETFONT:
  2998. TTSetFont(pTtm, (HFONT)wParam, (BOOL)lParam);
  2999. return(TRUE);
  3000. case WM_GETFONT:
  3001. if (pTtm)
  3002. return((LRESULT)pTtm->hFont);
  3003. break;
  3004. case WM_NOTIFYFORMAT:
  3005. if (lParam == NF_QUERY)
  3006. {
  3007. return NFR_UNICODE;
  3008. }
  3009. else if (lParam == NF_REQUERY)
  3010. {
  3011. for (int i = 0 ; i < pTtm->iNumTools; i++)
  3012. {
  3013. pTool = &pTtm->tools[i];
  3014. if (SendMessage(pTool->hwnd, WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_QUERY) == NFR_UNICODE)
  3015. {
  3016. pTool->uFlags |= TTF_UNICODE;
  3017. }
  3018. else
  3019. {
  3020. pTool->uFlags &= ~TTF_UNICODE;
  3021. }
  3022. }
  3023. return CIHandleNotifyFormat(&pTtm->_ci, lParam);
  3024. }
  3025. return 0;
  3026. case WM_ERASEBKGND:
  3027. break;
  3028. case WM_STYLECHANGED:
  3029. if ((wParam == GWL_STYLE) && pTtm)
  3030. {
  3031. DWORD dwNewStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
  3032. if (pTtm->_ci.style & TTS_BALLOON && // If the old style was a balloon,
  3033. !(dwNewStyle & TTS_BALLOON)) // And the new style is not a balloon,
  3034. {
  3035. // Then we need to unset the region.
  3036. SetWindowRgn(pTtm->_ci.hwnd, NULL, FALSE);
  3037. }
  3038. pTtm->_ci.style = ((LPSTYLESTRUCT)lParam)->styleNew;
  3039. }
  3040. break;
  3041. case WM_DESTROY:
  3042. {
  3043. if (pTtm->tools)
  3044. {
  3045. // free the tools
  3046. for (int i = 0; i < pTtm->iNumTools; i++)
  3047. {
  3048. TTBeforeFreeTool(pTtm, &pTtm->tools[i]);
  3049. }
  3050. LocalFree((HANDLE)pTtm->tools);
  3051. pTtm->tools = NULL;
  3052. }
  3053. TTSetFont(pTtm, NOFONT, FALSE); // delete font if we made one.
  3054. Str_Set(&pTtm->lpTipTitle, NULL);
  3055. if (pTtm->himlTitleBitmaps)
  3056. ImageList_Destroy(pTtm->himlTitleBitmaps);
  3057. // Close theme
  3058. if (pTtm->hTheme)
  3059. CloseThemeData(pTtm->hTheme);
  3060. // Release our compatibility markup
  3061. if (pTtm->pMarkup)
  3062. {
  3063. pTtm->pMarkup->Release();
  3064. pTtm->pMarkup = NULL;
  3065. }
  3066. pTtm->Release();
  3067. SetWindowPtr(hwnd, 0, 0);
  3068. }
  3069. break;
  3070. case WM_PRINTCLIENT:
  3071. TTRender(pTtm, (HDC)wParam);
  3072. break;
  3073. case WM_GETOBJECT:
  3074. if (lParam == OBJID_QUERYCLASSNAMEIDX)
  3075. return MSAA_CLASSNAMEIDX_TOOLTIPS;
  3076. goto DoDefault;
  3077. case WM_THEMECHANGED:
  3078. if (pTtm->hTheme)
  3079. CloseThemeData(pTtm->hTheme);
  3080. pTtm->hTheme = OpenThemeData(pTtm->_ci.hwnd, L"Tooltip");
  3081. InvalidateRect(pTtm->_ci.hwnd, NULL, TRUE);
  3082. break;
  3083. default:
  3084. {
  3085. LRESULT lres;
  3086. if (CCWndProc(&pTtm->_ci, uMsg, wParam, lParam, &lres))
  3087. return lres;
  3088. }
  3089. DoDefault:
  3090. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  3091. }
  3092. return 0;
  3093. }
  3094. //
  3095. // Purpose: Thunks a TOOLINFOA structure to a TOOLINFOW
  3096. // structure.
  3097. //
  3098. // Return: (BOOL) TRUE if successful
  3099. // FALSE if an error occurs
  3100. //
  3101. BOOL ThunkToolInfoAtoW(LPTOOLINFOA lpTiA, LPTOOLINFOW lpTiW, BOOL bThunkText, UINT uiCodePage)
  3102. {
  3103. lpTiW->uFlags = lpTiA->uFlags;
  3104. lpTiW->hwnd = lpTiA->hwnd;
  3105. lpTiW->uId = lpTiA->uId;
  3106. lpTiW->rect.left = lpTiA->rect.left;
  3107. lpTiW->rect.top = lpTiA->rect.top;
  3108. lpTiW->rect.right = lpTiA->rect.right;
  3109. lpTiW->rect.bottom = lpTiA->rect.bottom;
  3110. lpTiW->hinst = lpTiA->hinst;
  3111. //
  3112. // Set the size properly and optionally copy the new fields if the
  3113. // structure is large enough.
  3114. //
  3115. if (lpTiA->cbSize <= TTTOOLINFOA_V1_SIZE)
  3116. {
  3117. lpTiW->cbSize = TTTOOLINFOW_V1_SIZE;
  3118. }
  3119. else
  3120. {
  3121. lpTiW->cbSize = sizeof(TOOLINFOW);
  3122. lpTiW->lParam = lpTiA->lParam;
  3123. }
  3124. if (bThunkText)
  3125. {
  3126. // Thunk the string to the new structure.
  3127. // Special case LPSTR_TEXTCALLBACK.
  3128. if (lpTiA->lpszText == LPSTR_TEXTCALLBACKA)
  3129. {
  3130. lpTiW->lpszText = LPSTR_TEXTCALLBACKW;
  3131. }
  3132. else if (!IS_INTRESOURCE(lpTiA->lpszText))
  3133. {
  3134. int iResult;
  3135. DWORD dwBufSize = lstrlenA(lpTiA->lpszText) + 1;
  3136. lpTiW->lpszText = (LPWSTR) LocalAlloc (LPTR, dwBufSize * sizeof(WCHAR));
  3137. if (!lpTiW->lpszText)
  3138. {
  3139. return FALSE;
  3140. }
  3141. iResult = MultiByteToWideChar(uiCodePage, 0, lpTiA->lpszText, -1,
  3142. lpTiW->lpszText, dwBufSize);
  3143. // If iResult is 0, and GetLastError returns an error code,
  3144. // then MultiByteToWideCharfailed.
  3145. if (!iResult)
  3146. {
  3147. if (GetLastError())
  3148. {
  3149. return FALSE;
  3150. }
  3151. }
  3152. lpTiW->uFlags |= TTF_MEMALLOCED;
  3153. }
  3154. else
  3155. {
  3156. lpTiW->lpszText = (LPWSTR)lpTiA->lpszText;
  3157. }
  3158. }
  3159. return TRUE;
  3160. }
  3161. //
  3162. // Purpose: Thunks a TOOLINFOW structure to a TOOLINFOA
  3163. // structure.
  3164. //
  3165. // Return: (BOOL) TRUE if successful
  3166. // FALSE if an error occurs
  3167. //
  3168. BOOL ThunkToolInfoWtoA(LPTOOLINFOW lpTiW, LPTOOLINFOA lpTiA, UINT uiCodePage)
  3169. {
  3170. int iResult = 1;
  3171. lpTiA->uFlags = lpTiW->uFlags;
  3172. lpTiA->hwnd = lpTiW->hwnd;
  3173. lpTiA->uId = lpTiW->uId;
  3174. lpTiA->rect.left = lpTiW->rect.left;
  3175. lpTiA->rect.top = lpTiW->rect.top;
  3176. lpTiA->rect.right = lpTiW->rect.right;
  3177. lpTiA->rect.bottom = lpTiW->rect.bottom;
  3178. lpTiA->hinst = lpTiW->hinst;
  3179. //
  3180. // Set the size properly and optionally copy the new fields if the
  3181. // structure is large enough.
  3182. //
  3183. if (lpTiW->cbSize <= TTTOOLINFOW_V1_SIZE)
  3184. {
  3185. lpTiA->cbSize = TTTOOLINFOA_V1_SIZE;
  3186. }
  3187. else
  3188. {
  3189. lpTiA->cbSize = sizeof(TOOLINFOA);
  3190. lpTiA->lParam = lpTiA->lParam;
  3191. }
  3192. //
  3193. // Thunk the string to the new structure.
  3194. // Special case LPSTR_TEXTCALLBACK.
  3195. //
  3196. if (lpTiW->lpszText == LPSTR_TEXTCALLBACKW)
  3197. {
  3198. lpTiA->lpszText = LPSTR_TEXTCALLBACKA;
  3199. }
  3200. else if (!IS_INTRESOURCE(lpTiW->lpszText))
  3201. {
  3202. // It is assumed that lpTiA->lpszText is already setup to
  3203. // a valid buffer, and that buffer is 80 characters.
  3204. // 80 characters is defined in the TOOLTIPTEXT structure.
  3205. iResult = WideCharToMultiByte(uiCodePage, 0, lpTiW->lpszText, -1,
  3206. lpTiA->lpszText, 80, NULL, NULL);
  3207. }
  3208. else
  3209. {
  3210. lpTiA->lpszText = (LPSTR)lpTiW->lpszText;
  3211. }
  3212. //
  3213. // If iResult is 0, and GetLastError returns an error code,
  3214. // then WideCharToMultiByte failed.
  3215. //
  3216. if (!iResult)
  3217. {
  3218. if (GetLastError())
  3219. {
  3220. return FALSE;
  3221. }
  3222. }
  3223. return TRUE;
  3224. }
  3225. //*************************************************************
  3226. //
  3227. // ThunkToolTipTextAtoW()
  3228. //
  3229. // Purpose: Thunks a TOOLTIPTEXTA structure to a TOOLTIPTEXTW
  3230. // structure.
  3231. //
  3232. // Return: (BOOL) TRUE if successful
  3233. // FALSE if an error occurs
  3234. //
  3235. //*************************************************************
  3236. BOOL ThunkToolTipTextAtoW (LPTOOLTIPTEXTA lpTttA, LPTOOLTIPTEXTW lpTttW, UINT uiCodePage)
  3237. {
  3238. int iResult;
  3239. if (!lpTttA || !lpTttW)
  3240. return FALSE;
  3241. //
  3242. // Thunk the NMHDR structure.
  3243. //
  3244. lpTttW->hdr.hwndFrom = lpTttA->hdr.hwndFrom;
  3245. lpTttW->hdr.idFrom = lpTttA->hdr.idFrom;
  3246. lpTttW->hdr.code = TTN_NEEDTEXTW;
  3247. lpTttW->hinst = lpTttA->hinst;
  3248. lpTttW->uFlags = lpTttA->uFlags;
  3249. lpTttW->lParam = lpTttA->lParam;
  3250. //
  3251. // Thunk the string to the new structure.
  3252. // Special case LPSTR_TEXTCALLBACK.
  3253. //
  3254. if (lpTttA->lpszText == LPSTR_TEXTCALLBACKA)
  3255. {
  3256. lpTttW->lpszText = LPSTR_TEXTCALLBACKW;
  3257. }
  3258. else if (!IS_INTRESOURCE(lpTttA->lpszText))
  3259. {
  3260. // Transfer the lpszText into the lpTttW...
  3261. //
  3262. // First see if it fits into the buffer, and optimistically assume
  3263. // it will.
  3264. //
  3265. lpTttW->lpszText = lpTttW->szText;
  3266. iResult = MultiByteToWideChar(uiCodePage, 0, lpTttA->lpszText, -1,
  3267. lpTttW->szText, ARRAYSIZE(lpTttW->szText));
  3268. if (!iResult)
  3269. {
  3270. //
  3271. // Didn't fit into the small buffer; must alloc our own.
  3272. //
  3273. lpTttW->lpszText = ProduceWFromA(uiCodePage, lpTttA->lpszText);
  3274. lpTttW->uFlags |= TTF_MEMALLOCED;
  3275. }
  3276. }
  3277. else
  3278. {
  3279. lpTttW->lpszText = (LPWSTR)lpTttA->lpszText;
  3280. }
  3281. return TRUE;
  3282. }