1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/tooltip.cpp 
   3 // Purpose:     wxToolTip class implementation for MSW 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1999 Vadim Zeitlin 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #include "wx/wxprec.h" 
  32 #include "wx/tooltip.h" 
  33 #include "wx/msw/private.h" 
  35 // include <commctrl.h> "properly" 
  36 #include "wx/msw/wrapcctl.h" 
  38 // VZ: normally, the trick with subclassing the tooltip control and processing 
  39 //     TTM_WINDOWFROMPOINT should work but, somehow, it doesn't. I leave the 
  40 //     code here for now (but it's not compiled) in case we need it later. 
  42 //     For now I use an ugly workaround and process TTN_NEEDTEXT directly in 
  43 //     radio button wnd proc - fixing TTM_WINDOWFROMPOINT code would be nice 
  44 //     because it would then work for all controls, not only radioboxes but for 
  45 //     now I don't understand what's wrong with it... 
  46 #define wxUSE_TTM_WINDOWFROMPOINT   0 
  48 // ---------------------------------------------------------------------------- 
  50 // ---------------------------------------------------------------------------- 
  52 // the tooltip parent window 
  53 WXHWND 
wxToolTip::ms_hwndTT 
= (WXHWND
)NULL
; 
  55 #if wxUSE_TTM_WINDOWFROMPOINT 
  57 // the tooltip window proc 
  58 static WNDPROC gs_wndprocToolTip 
= (WNDPROC
)NULL
; 
  60 #endif // wxUSE_TTM_WINDOWFROMPOINT 
  62 // ---------------------------------------------------------------------------- 
  64 // ---------------------------------------------------------------------------- 
  66 // a wrapper around TOOLINFO Win32 structure 
  68     #pragma warning( disable : 4097 ) // we inherit from a typedef - so what? 
  71 class wxToolInfo 
: public TOOLINFO
 
  74     wxToolInfo(HWND hwndOwner
) 
  76         // initialize all members 
  77         ::ZeroMemory(this, sizeof(TOOLINFO
)); 
  79         // the structure TOOLINFO has been extended with a 4 byte field in 
  80         // version 4.70 of comctl32.dll and if we compile on a newer machine 
  81         // but run on one with the old version of comctl32, nothing will work 
  82         // because the library will detect that we rely on a more recent 
  83         // version of it. So we always use the old size - if we ever start 
  84         // using our lParam member, we'd have to check for comctl32 version 
  86 #if _WIN32_IE >= 0x0300 
  87         cbSize 
= sizeof(TOOLINFO
) - sizeof(LPARAM
); 
  89         cbSize 
= sizeof(TOOLINFO
); 
  90 #endif // compile-time comctl32.dll version 
  93         uFlags 
= TTF_IDISHWND
; 
  94         uId 
= (UINT
)hwndOwner
; 
  99     #pragma warning( default : 4097 ) 
 102 // ---------------------------------------------------------------------------- 
 104 // ---------------------------------------------------------------------------- 
 106 // send a message to the tooltip control 
 107 inline LRESULT 
SendTooltipMessage(WXHWND hwnd
, 
 112     return hwnd 
? ::SendMessage((HWND
)hwnd
, msg
, wParam
, (LPARAM
)lParam
) 
 116 // send a message to all existing tooltip controls 
 117 static void SendTooltipMessageToAll(WXHWND hwnd
, 
 122     (void)SendTooltipMessage((WXHWND
)hwnd
, msg
, wParam
, (void *)lParam
); 
 125 // ============================================================================ 
 127 // ============================================================================ 
 129 #if wxUSE_TTM_WINDOWFROMPOINT 
 131 // ---------------------------------------------------------------------------- 
 132 // window proc for our tooltip control 
 133 // ---------------------------------------------------------------------------- 
 135 LRESULT APIENTRY 
wxToolTipWndProc(HWND hwndTT
, 
 140     if ( msg 
== TTM_WINDOWFROMPOINT 
) 
 142         LPPOINT ppt 
= (LPPOINT
)lParam
; 
 144         // the window on which event occured 
 145         HWND hwnd 
= ::WindowFromPoint(*ppt
); 
 147         OutputDebugString("TTM_WINDOWFROMPOINT: "); 
 148         OutputDebugString(wxString::Format("0x%08x => ", hwnd
)); 
 150         // return a HWND corresponding to a wxWindow because only wxWindows are 
 151         // associated with tooltips using TTM_ADDTOOL 
 152         wxWindow 
*win 
= wxGetWindowFromHWND((WXHWND
)hwnd
); 
 156             hwnd 
= GetHwndOf(win
); 
 157             OutputDebugString(wxString::Format("0x%08x\r\n", hwnd
)); 
 160             // modify the point too! 
 162             GetWindowRect(hwnd
, &rect
); 
 164             ppt
->x 
= (rect
.right 
- rect
.left
) / 2; 
 165             ppt
->y 
= (rect
.bottom 
- rect
.top
) / 2; 
 167             return (LRESULT
)hwnd
; 
 171             OutputDebugString("no window\r\n"); 
 175     return ::CallWindowProc(CASTWNDPROC gs_wndprocToolTip
, hwndTT
, msg
, wParam
, lParam
); 
 178 #endif // wxUSE_TTM_WINDOWFROMPOINT 
 180 // ---------------------------------------------------------------------------- 
 182 // ---------------------------------------------------------------------------- 
 184 void wxToolTip::Enable(bool flag
) 
 186     SendTooltipMessageToAll(ms_hwndTT
, TTM_ACTIVATE
, flag
, 0); 
 189 void wxToolTip::SetDelay(long milliseconds
) 
 191     SendTooltipMessageToAll(ms_hwndTT
, TTM_SETDELAYTIME
, 
 192                             TTDT_INITIAL
, milliseconds
); 
 195 // --------------------------------------------------------------------------- 
 196 // implementation helpers 
 197 // --------------------------------------------------------------------------- 
 199 // create the tooltip ctrl for our parent frame if it doesn't exist yet 
 200 WXHWND 
wxToolTip::GetToolTipCtrl() 
 204         ms_hwndTT 
= (WXHWND
)::CreateWindow(TOOLTIPS_CLASS
, 
 207                                            CW_USEDEFAULT
, CW_USEDEFAULT
, 
 208                                            CW_USEDEFAULT
, CW_USEDEFAULT
, 
 214            HWND hwnd 
= (HWND
)ms_hwndTT
; 
 215            SetWindowPos(hwnd
, HWND_TOPMOST
, 0, 0, 0, 0, 
 216                         SWP_NOMOVE 
| SWP_NOSIZE 
| SWP_NOACTIVATE
); 
 218 #if wxUSE_TTM_WINDOWFROMPOINT 
 219            // subclass the newly created control 
 220            gs_wndprocToolTip 
= (WNDPROC
)::GetWindowLong(hwnd
, GWL_WNDPROC
); 
 221            ::SetWindowLong(hwnd
, GWL_WNDPROC
, (long)wxToolTipWndProc
); 
 222 #endif // wxUSE_TTM_WINDOWFROMPOINT 
 229 void wxToolTip::RelayEvent(WXMSG 
*msg
) 
 231     (void)SendTooltipMessage(GetToolTipCtrl(), TTM_RELAYEVENT
, 0, msg
); 
 234 // ---------------------------------------------------------------------------- 
 236 // ---------------------------------------------------------------------------- 
 238 IMPLEMENT_ABSTRACT_CLASS(wxToolTip
, wxObject
) 
 240 wxToolTip::wxToolTip(const wxString 
&tip
) 
 246 wxToolTip::~wxToolTip() 
 248     // there is no need to Remove() this tool - it will be done automatically 
 252 // ---------------------------------------------------------------------------- 
 254 // ---------------------------------------------------------------------------- 
 256 void wxToolTip::Remove() 
 258     // remove this tool from the tooltip control 
 261         wxToolInfo 
ti(GetHwndOf(m_window
)); 
 262         (void)SendTooltipMessage(GetToolTipCtrl(), TTM_DELTOOL
, 0, &ti
); 
 266 void wxToolTip::Add(WXHWND hWnd
) 
 268     HWND hwnd 
= (HWND
)hWnd
; 
 272     // another possibility would be to specify LPSTR_TEXTCALLBACK here as we 
 273     // store the tooltip text ourselves anyhow, and provide it in response to 
 274     // TTN_NEEDTEXT (sent via WM_NOTIFY), but then we would be limited to 79 
 275     // character tooltips as this is the size of the szText buffer in 
 276     // NMTTDISPINFO struct -- and setting the tooltip here we can have tooltips 
 279     ti
.lpszText 
= (wxChar 
*)m_text
.c_str(); // const_cast 
 281     if ( !SendTooltipMessage(GetToolTipCtrl(), TTM_ADDTOOL
, 0, &ti
) ) 
 283         wxLogDebug(_T("Failed to create the tooltip '%s'"), m_text
.c_str()); 
 287         // check for multiline toopltip 
 288         int index 
= m_text
.Find(_T('\n')); 
 290         if ( index 
!= wxNOT_FOUND 
) 
 292 #ifdef TTM_SETMAXTIPWIDTH 
 293             if ( wxTheApp
->GetComCtl32Version() >= 470 ) 
 295                 // use TTM_SETMAXTIPWIDTH to make tooltip multiline using the 
 296                 // extent of its first line as max value 
 297                 HFONT hfont 
= (HFONT
)SendTooltipMessage(GetToolTipCtrl(), 
 302                     hfont 
= (HFONT
)GetStockObject(DEFAULT_GUI_FONT
); 
 305                         wxLogLastError(wxT("GetStockObject(DEFAULT_GUI_FONT)")); 
 312                     wxLogLastError(wxT("CreateCompatibleDC(NULL)")); 
 315                 if ( !SelectObject(hdc
, hfont
) ) 
 317                     wxLogLastError(wxT("SelectObject(hfont)")); 
 321                 if ( !GetTextExtentPoint(hdc
, m_text
, index
, &sz
) ) 
 323                     wxLogLastError(wxT("GetTextExtentPoint")); 
 326                 SendTooltipMessage(GetToolTipCtrl(), TTM_SETMAXTIPWIDTH
, 
 329 #endif // comctl32.dll >= 4.70 
 331             // replace the '\n's with spaces because otherwise they appear as 
 332             // unprintable characters in the tooltip string 
 333             m_text
.Replace(_T("\n"), _T(" ")); 
 338 void wxToolTip::SetWindow(wxWindow 
*win
) 
 344     // add the window itself 
 347         Add(m_window
->GetHWND()); 
 349 #if !defined(__WXUNIVERSAL__) 
 350     // and all of its subcontrols (e.g. radiobuttons in a radiobox) as well 
 351     wxControl 
*control 
= wxDynamicCast(m_window
, wxControl
); 
 354         const wxArrayLong
& subcontrols 
= control
->GetSubcontrols(); 
 355         size_t count 
= subcontrols
.GetCount(); 
 356         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 358             int id 
= subcontrols
[n
]; 
 359             HWND hwnd 
= GetDlgItem(GetHwndOf(m_window
), id
); 
 362                 // may be it's a child of parent of the control, in fact? 
 363                 // (radiobuttons are subcontrols, i.e. children of the radiobox 
 364                 // for wxWindows but are its siblings at Windows level) 
 365                 hwnd 
= GetDlgItem(GetHwndOf(m_window
->GetParent()), id
); 
 368             // must have it by now! 
 369             wxASSERT_MSG( hwnd
, _T("no hwnd for subcontrol?") ); 
 375     // VZ: it's ugly to do it here, but I don't want any major changes right 
 376     //     now, later we will probably want to have wxWindow::OnGotToolTip() or 
 377     //     something like this where the derived class can do such things 
 378     //     itself instead of wxToolTip "knowing" about them all 
 379     wxComboBox 
*combo 
= wxDynamicCast(control
, wxComboBox
); 
 382         WXHWND hwndComboEdit 
= combo
->GetWindowStyle() & wxCB_READONLY
 
 384                                 : combo
->GetEditHWND(); 
 390 #endif // !defined(__WXUNIVERSAL__) 
 393 void wxToolTip::SetTip(const wxString
& tip
) 
 399         // update the tip text shown by the control 
 400         wxToolInfo 
ti(GetHwndOf(m_window
)); 
 401         ti
.lpszText 
= (wxChar 
*)m_text
.c_str(); 
 403         (void)SendTooltipMessage(GetToolTipCtrl(), TTM_UPDATETIPTEXT
, 0, &ti
); 
 407 #endif // wxUSE_TOOLTIPS