home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / SRC / TOOLTIP.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-30  |  12.1 KB  |  445 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1997 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_CMNCTL_SEG
  14. #pragma code_seg(AFX_CMNCTL_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // CToolTipCtrl
  26.  
  27. BEGIN_MESSAGE_MAP(CToolTipCtrl, CWnd)
  28.     //{{AFX_MSG_MAP(CToolTipCtrl)
  29.     ON_MESSAGE(WM_DISABLEMODAL, OnDisableModal)
  30.     ON_MESSAGE(TTM_WINDOWFROMPOINT, OnWindowFromPoint)
  31.     ON_MESSAGE(TTM_ADDTOOL, OnAddTool)
  32.     //}}AFX_MSG_MAP
  33. END_MESSAGE_MAP()
  34.  
  35. CToolTipCtrl::CToolTipCtrl()
  36. {
  37. }
  38.  
  39. BOOL CToolTipCtrl::Create(CWnd* pParentWnd, DWORD dwStyle)
  40. {
  41.     BOOL bResult = CWnd::CreateEx(NULL, TOOLTIPS_CLASS, NULL,
  42.         WS_POPUP | dwStyle, // force WS_POPUP
  43.         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  44.         pParentWnd->GetSafeHwnd(), NULL, NULL);
  45.  
  46.     if (bResult)
  47.         SetOwner(pParentWnd);
  48.     return bResult;
  49. }
  50.  
  51. CToolTipCtrl::~CToolTipCtrl()
  52. {
  53.     DestroyWindow();
  54. }
  55.  
  56. BOOL CToolTipCtrl::DestroyToolTipCtrl()
  57. {
  58. #ifdef _AFXDLL
  59.     BOOL bDestroy = (AfxGetModuleState() == m_pModuleState);
  60. #else
  61.     BOOL bDestroy = TRUE;
  62. #endif
  63.  
  64.     if (bDestroy)
  65.     {
  66.         DestroyWindow();
  67.         delete this;
  68.     }
  69.     return bDestroy;
  70. }
  71.  
  72. LRESULT CToolTipCtrl::OnAddTool(WPARAM wParam, LPARAM lParam)
  73. {
  74.     TOOLINFO ti = *(LPTOOLINFO)lParam;
  75.     if ((ti.hinst == NULL) && (ti.lpszText != LPSTR_TEXTCALLBACK)
  76.         && (ti.lpszText != NULL))
  77.     {
  78.         void* pv;
  79.         if (!m_mapString.Lookup(ti.lpszText, pv))
  80.             m_mapString.SetAt(ti.lpszText, NULL);
  81.         // set lpszText to point to the permanent memory associated
  82.         // with the CString
  83.         VERIFY(m_mapString.LookupKey(ti.lpszText, ti.lpszText));
  84.     }
  85.     return DefWindowProc(TTM_ADDTOOL, wParam, (LPARAM)&ti);
  86. }
  87.  
  88. LRESULT CToolTipCtrl::OnDisableModal(WPARAM, LPARAM)
  89. {
  90.     SendMessage(TTM_ACTIVATE, 0);
  91.     return FALSE;
  92. }
  93.  
  94. LRESULT CToolTipCtrl::OnWindowFromPoint(WPARAM, LPARAM lParam)
  95. {
  96.     ASSERT(lParam != NULL);
  97.  
  98.     // the default implementation of tooltips just calls WindowFromPoint
  99.     // which does not work for certain kinds of combo boxes
  100.     CPoint pt = *(POINT*)lParam;
  101.     HWND hWnd = ::WindowFromPoint(pt);
  102.     if (hWnd == NULL)
  103.         return 0;
  104.  
  105.     // try to hit combobox instead of edit control for CBS_DROPDOWN styles
  106.     HWND hWndTemp = ::GetParent(hWnd);
  107.     if (hWndTemp != NULL && _AfxIsComboBoxControl(hWndTemp, CBS_DROPDOWN))
  108.         return (LRESULT)hWndTemp;
  109.  
  110.     // handle special case of disabled child windows
  111.     ::ScreenToClient(hWnd, &pt);
  112.     hWndTemp = _AfxChildWindowFromPoint(hWnd, pt);
  113.     if (hWndTemp != NULL && !::IsWindowEnabled(hWndTemp))
  114.         return (LRESULT)hWndTemp;
  115.  
  116.     return (LRESULT)hWnd;
  117. }
  118.  
  119. BOOL CToolTipCtrl::AddTool(CWnd* pWnd, LPCTSTR lpszText, LPCRECT lpRectTool,
  120.     UINT nIDTool)
  121. {
  122.     ASSERT(::IsWindow(m_hWnd));
  123.     ASSERT(pWnd != NULL);
  124.     ASSERT(lpszText != NULL);
  125.     // the toolrect and toolid must both be zero or both valid
  126.     ASSERT((lpRectTool != NULL && nIDTool != 0) ||
  127.            (lpRectTool == NULL) && (nIDTool == 0));
  128.  
  129.     TOOLINFO ti;
  130.     FillInToolInfo(ti, pWnd, nIDTool);
  131.     if (lpRectTool != NULL)
  132.         memcpy(&ti.rect, lpRectTool, sizeof(RECT));
  133.     ti.lpszText = (LPTSTR)lpszText;
  134.     return (BOOL) ::SendMessage(m_hWnd, TTM_ADDTOOL, 0, (LPARAM)&ti);
  135. }
  136.  
  137. BOOL CToolTipCtrl::AddTool(CWnd* pWnd, UINT nIDText, LPCRECT lpRectTool,
  138.     UINT nIDTool)
  139. {
  140.     ASSERT(::IsWindow(m_hWnd));
  141.     ASSERT(nIDText != 0);
  142.     ASSERT(pWnd != NULL);
  143.     // the toolrect and toolid must both be zero or both valid
  144.     ASSERT((lpRectTool != NULL && nIDTool != 0) ||
  145.            (lpRectTool == NULL) && (nIDTool == 0));
  146.  
  147.     TOOLINFO ti;
  148.     FillInToolInfo(ti, pWnd, nIDTool);
  149.     if (lpRectTool != NULL)
  150.         memcpy(&ti.rect, lpRectTool, sizeof(RECT));
  151.     ti.hinst = AfxFindResourceHandle(MAKEINTRESOURCE((nIDText>>4)+1),
  152.         RT_STRING);
  153.     ASSERT(ti.hinst != NULL);
  154.     ti.lpszText = (LPTSTR)MAKEINTRESOURCE(nIDText);
  155.     return (BOOL) ::SendMessage(m_hWnd, TTM_ADDTOOL, 0, (LPARAM)&ti);
  156. }
  157.  
  158. void CToolTipCtrl::DelTool(CWnd* pWnd, UINT nIDTool)
  159. {
  160.     ASSERT(::IsWindow(m_hWnd));
  161.     ASSERT(pWnd != NULL);
  162.  
  163.     TOOLINFO ti;
  164.     FillInToolInfo(ti, pWnd, nIDTool);
  165.     ::SendMessage(m_hWnd, TTM_DELTOOL, 0, (LPARAM)&ti);
  166. }
  167.  
  168. void CToolTipCtrl::GetText(CString& str, CWnd* pWnd, UINT nIDTool) const
  169. {
  170.     ASSERT(::IsWindow(m_hWnd));
  171.     ASSERT(pWnd != NULL);
  172.  
  173.     TOOLINFO ti;
  174.     FillInToolInfo(ti, pWnd, nIDTool);
  175.     ti.lpszText = str.GetBuffer(256);
  176.     ::SendMessage(m_hWnd, TTM_GETTEXT, 0, (LPARAM)&ti);
  177.     str.ReleaseBuffer();
  178. }
  179.  
  180. BOOL CToolTipCtrl::GetToolInfo(CToolInfo& ToolInfo, CWnd* pWnd,
  181.     UINT nIDTool) const
  182. {
  183.     ASSERT(::IsWindow(m_hWnd));
  184.     ASSERT(pWnd != NULL);
  185.  
  186.     FillInToolInfo(ToolInfo, pWnd, nIDTool);
  187.     ToolInfo.lpszText = ToolInfo.szText;
  188.     return (BOOL)::SendMessage(m_hWnd, TTM_GETTOOLINFO, 0, (LPARAM)&ToolInfo);
  189. }
  190.  
  191. BOOL CToolTipCtrl::HitTest(CWnd* pWnd, CPoint pt, LPTOOLINFO lpToolInfo) const
  192. {
  193.     ASSERT(::IsWindow(m_hWnd));
  194.     ASSERT(pWnd != NULL);
  195.     ASSERT(lpToolInfo != NULL);
  196.  
  197.     TTHITTESTINFO hti;
  198.     memset(&hti, 0, sizeof(hti));
  199.     hti.ti.cbSize = sizeof(TOOLINFO);
  200.     hti.hwnd = pWnd->GetSafeHwnd();
  201.     hti.pt.x = pt.x;
  202.     hti.pt.y = pt.y;
  203.     if ((BOOL)::SendMessage(m_hWnd, TTM_HITTEST, 0, (LPARAM)&hti))
  204.     {
  205.         memcpy(lpToolInfo, &hti.ti, sizeof(TOOLINFO));
  206.         return TRUE;
  207.     }
  208.     return FALSE;
  209. }
  210.  
  211. void CToolTipCtrl::SetToolRect(CWnd* pWnd, UINT nIDTool, LPCRECT lpRect)
  212. {
  213.     ASSERT(::IsWindow(m_hWnd));
  214.     ASSERT(pWnd != NULL);
  215.     ASSERT(nIDTool != 0);
  216.  
  217.     TOOLINFO ti;
  218.     FillInToolInfo(ti, pWnd, nIDTool);
  219.     memcpy(&ti.rect, lpRect, sizeof(RECT));
  220.     ::SendMessage(m_hWnd, TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
  221. }
  222.  
  223. void CToolTipCtrl::UpdateTipText(LPCTSTR lpszText, CWnd* pWnd, UINT nIDTool)
  224. {
  225.     ASSERT(::IsWindow(m_hWnd));
  226.     ASSERT(pWnd != NULL);
  227.  
  228.     TOOLINFO ti;
  229.     FillInToolInfo(ti, pWnd, nIDTool);
  230.     ti.lpszText = (LPTSTR)lpszText;
  231.     ::SendMessage(m_hWnd, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  232. }
  233.  
  234. void CToolTipCtrl::UpdateTipText(UINT nIDText, CWnd* pWnd, UINT nIDTool)
  235. {
  236.     ASSERT(nIDText != 0);
  237.  
  238.     CString str;
  239.     VERIFY(str.LoadString(nIDText));
  240.     UpdateTipText(str, pWnd, nIDTool);
  241. }
  242.  
  243. /////////////////////////////////////////////////////////////////////////////
  244. // CToolTipCtrl Implementation
  245.  
  246. void CToolTipCtrl::FillInToolInfo(TOOLINFO& ti, CWnd* pWnd, UINT nIDTool) const
  247. {
  248.     memset(&ti, 0, sizeof(ti));
  249.     ti.cbSize = sizeof(ti);
  250.     HWND hwnd = pWnd->GetSafeHwnd();
  251.     if (nIDTool == 0)
  252.     {
  253.         ti.hwnd = ::GetParent(hwnd);
  254.         ti.uFlags = TTF_IDISHWND;
  255.         ti.uId = (UINT)hwnd;
  256.     }
  257.     else
  258.     {
  259.         ti.hwnd = hwnd;
  260.         ti.uFlags = 0;
  261.         ti.uId = nIDTool;
  262.     }
  263. }
  264.  
  265. /////////////////////////////////////////////////////////////////////////////
  266. // CWnd tooltip support
  267.  
  268. BOOL CWnd::EnableToolTips(BOOL bEnable)
  269. {
  270.     _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  271.     CToolTipCtrl* pToolTip = pThreadState->m_pToolTip;
  272.  
  273.     if (!bEnable)
  274.     {
  275.         // nothing to do if tooltips not enabled
  276.         if (!(m_nFlags & WF_TOOLTIPS))
  277.             return TRUE;
  278.  
  279.         // cancel tooltip if this window is active
  280.         if (pThreadState->m_pLastHit == this)
  281.             CancelToolTips(TRUE);
  282.  
  283.         // remove "dead-area" toolbar
  284.         if (pToolTip->GetSafeHwnd() != NULL)
  285.         {
  286.             TOOLINFO ti; memset(&ti, 0, sizeof(TOOLINFO));
  287.             ti.cbSize = sizeof(TOOLINFO);
  288.             ti.uFlags = TTF_IDISHWND;
  289.             ti.hwnd = m_hWnd;
  290.             ti.uId = (UINT)m_hWnd;
  291.             pToolTip->SendMessage(TTM_DELTOOL, 0, (LPARAM)&ti);
  292.         }
  293.  
  294.         // success
  295.         m_nFlags &= ~WF_TOOLTIPS;
  296.         return TRUE;
  297.     }
  298.  
  299.     // if already enabled for tooltips, nothing to do
  300.     if (!(m_nFlags & WF_TOOLTIPS))
  301.     {
  302.         // success
  303.         AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
  304.         pModuleState->m_pfnFilterToolTipMessage = &CWnd::_FilterToolTipMessage;
  305.         m_nFlags |= WF_TOOLTIPS;
  306.     }
  307.     return TRUE;
  308. }
  309.  
  310. static void AFXAPI RelayToolTipMessage(CToolTipCtrl* pToolTip, MSG* pMsg)
  311. {
  312.     // transate the message based on TTM_WINDOWFROMPOINT
  313.     MSG msg = *pMsg;
  314.     msg.hwnd = (HWND)pToolTip->SendMessage(TTM_WINDOWFROMPOINT, 0, (LPARAM)&msg.pt);
  315.     CPoint pt = pMsg->pt;
  316.     if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)
  317.         ::ScreenToClient(msg.hwnd, &pt);
  318.     msg.lParam = MAKELONG(pt.x, pt.y);
  319.  
  320.     // relay mouse event before deleting old tool
  321.     pToolTip->SendMessage(TTM_RELAYEVENT, 0, (LPARAM)&msg);
  322. }
  323.  
  324. void PASCAL CWnd::_FilterToolTipMessage(MSG* pMsg, CWnd* pWnd)
  325. {
  326.     pWnd->FilterToolTipMessage(pMsg);
  327. }
  328.  
  329. void CWnd::FilterToolTipMessage(MSG* pMsg)
  330. {
  331.     UINT message = pMsg->message;
  332.     if ((m_nFlags & WF_TOOLTIPS) &&
  333.         (message == WM_MOUSEMOVE || message == WM_NCMOUSEMOVE ||
  334.          message == WM_LBUTTONUP || message == WM_RBUTTONUP ||
  335.          message == WM_MBUTTONUP) &&
  336.         (GetKeyState(VK_LBUTTON) >= 0 && GetKeyState(VK_RBUTTON) >= 0 &&
  337.          GetKeyState(VK_MBUTTON) >= 0))
  338.     {
  339.         // make sure that tooltips are not already being handled
  340.         CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
  341.         while (pWnd != NULL && pWnd != this && !(pWnd->m_nFlags & WF_TOOLTIPS))
  342.             pWnd = pWnd->GetParent();
  343.         if (pWnd != this)
  344.             return;
  345.  
  346.         _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
  347.         CToolTipCtrl* pToolTip = pThreadState->m_pToolTip;
  348.         CWnd* pOwner = GetParentOwner();
  349.         if (pToolTip != NULL && pToolTip->GetOwner() != pOwner)
  350.         {
  351.             pToolTip->DestroyWindow();
  352.             delete pToolTip;
  353.             pThreadState->m_pToolTip = NULL;
  354.             pToolTip = NULL;
  355.         }
  356.         if (pToolTip == NULL)
  357.         {
  358.             pToolTip = new CToolTipCtrl;
  359.             if (!pToolTip->Create(pOwner, TTS_ALWAYSTIP))
  360.             {
  361.                 delete pToolTip;
  362.                 return;
  363.             }
  364.             pToolTip->SendMessage(TTM_ACTIVATE, FALSE);
  365.             pThreadState->m_pToolTip = pToolTip;
  366.         }
  367.  
  368.         ASSERT_VALID(pToolTip);
  369.         ASSERT(::IsWindow(pToolTip->m_hWnd));
  370.  
  371.         // add a "dead-area" tool for areas between toolbar buttons
  372.         TOOLINFO ti; memset(&ti, 0, sizeof(TOOLINFO));
  373.         ti.cbSize = sizeof(TOOLINFO);
  374.         ti.uFlags = TTF_IDISHWND;
  375.         ti.hwnd = m_hWnd;
  376.         ti.uId = (UINT)m_hWnd;
  377.         if (!pToolTip->SendMessage(TTM_GETTOOLINFO, 0, (LPARAM)&ti))
  378.         {
  379.             ASSERT(ti.cbSize == sizeof(TOOLINFO));
  380.             ASSERT(ti.uFlags == TTF_IDISHWND);
  381.             ASSERT(ti.hwnd == m_hWnd);
  382.             ASSERT(ti.uId == (UINT)m_hWnd);
  383.             VERIFY(pToolTip->SendMessage(TTM_ADDTOOL, 0, (LPARAM)&ti));
  384.         }
  385.  
  386.         // determine which tool was hit
  387.         CPoint point = pMsg->pt;
  388.         ::ScreenToClient(m_hWnd, &point);
  389.         TOOLINFO tiHit; memset(&tiHit, 0, sizeof(TOOLINFO));
  390.         tiHit.cbSize = sizeof(TOOLINFO);
  391.         int nHit = OnToolHitTest(point, &tiHit);
  392.  
  393.         // build new toolinfo and if different than current, register it
  394.         CWnd* pHitWnd = nHit == -1 ? NULL : this;
  395.         if (pThreadState->m_nLastHit != nHit || pThreadState->m_pLastHit != pHitWnd)
  396.         {
  397.             if (nHit != -1)
  398.             {
  399.                 // add new tool and activate the tip
  400.                 ti = tiHit;
  401.                 ti.uFlags &= ~(TTF_NOTBUTTON|TTF_ALWAYSTIP);
  402.                 VERIFY(pToolTip->SendMessage(TTM_ADDTOOL, 0, (LPARAM)&ti));
  403.                 if ((tiHit.uFlags & TTF_ALWAYSTIP) || IsTopParentActive())
  404.                 {
  405.                     // allow the tooltip to popup when it should
  406.                     pToolTip->SendMessage(TTM_ACTIVATE, TRUE);
  407.  
  408.                     // bring the tooltip window above other popup windows
  409.                     ::SetWindowPos(pToolTip->m_hWnd, HWND_TOP, 0, 0, 0, 0,
  410.                         SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
  411.                 }
  412.             }
  413.  
  414.             // relay mouse event before deleting old tool
  415.             RelayToolTipMessage(pToolTip, pMsg);
  416.  
  417.             // now safe to delete the old tool
  418.             if (pThreadState->m_lastInfo.cbSize == sizeof(TOOLINFO))
  419.                 pToolTip->SendMessage(TTM_DELTOOL, 0, (LPARAM)&pThreadState->m_lastInfo);
  420.             pThreadState->m_pLastHit = pHitWnd;
  421.             pThreadState->m_nLastHit = nHit;
  422.             pThreadState->m_lastInfo = tiHit;
  423.         }
  424.         else
  425.         {
  426.             // relay mouse events through the tooltip
  427.             if (nHit != -1)
  428.                 RelayToolTipMessage(pToolTip, pMsg);
  429.         }
  430.  
  431.         if ((tiHit.lpszText != LPSTR_TEXTCALLBACK) && (tiHit.hinst == 0))
  432.             free(tiHit.lpszText);
  433.     }
  434. }
  435.  
  436. /////////////////////////////////////////////////////////////////////////////
  437.  
  438. #ifdef AFX_INIT_SEG
  439. #pragma code_seg(AFX_INIT_SEG)
  440. #endif
  441.  
  442. IMPLEMENT_DYNAMIC(CToolTipCtrl, CWnd)
  443.  
  444. /////////////////////////////////////////////////////////////////////////////
  445.