From 6418ad5ec8e66c3b367b59b56e0849399774f3ec Mon Sep 17 00:00:00 2001 From: Francesco Montorsi Date: Sun, 26 Apr 2009 13:37:16 +0000 Subject: [PATCH] Add support for wxSTB_ELLIPSIZE_* and for wxSTB_SHOW_TIPS flags under wxMSW git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@60388 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/msw/statusbar.h | 13 ++- include/wx/msw/tooltip.h | 18 +++- include/wx/statusbr.h | 13 +++ src/msw/radiobox.cpp | 4 +- src/msw/statusbar.cpp | 178 ++++++++++++++++++++++++++++++------- src/msw/tooltip.cpp | 64 +++++++++++-- 6 files changed, 245 insertions(+), 45 deletions(-) diff --git a/include/wx/msw/statusbar.h b/include/wx/msw/statusbar.h index 8e6d7b7c0b..4d148481b5 100644 --- a/include/wx/msw/statusbar.h +++ b/include/wx/msw/statusbar.h @@ -14,6 +14,9 @@ #if wxUSE_NATIVE_STATUSBAR +#include "wx/vector.h" +#include "wx/tooltip.h" + class WXDLLIMPEXP_FWD_CORE wxClientDC; class WXDLLIMPEXP_CORE wxStatusBar : public wxStatusBarBase @@ -41,7 +44,7 @@ public: virtual void SetFieldsCount(int number = 1, const int *widths = NULL); // each field of status line has it's own text - virtual void SetStatusText(const wxString& text, int number = 0); + virtual void SetStatusText(const wxString& text, int number = 0); // set status line fields' widths virtual void SetStatusWidths(int n, const int widths_field[]); @@ -64,6 +67,7 @@ public: virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam); + protected: void CopyFieldsWidth(const int widths[]); void SetFieldsWidth(); @@ -72,10 +76,17 @@ protected: // override some base class virtuals virtual wxSize DoGetBestSize() const; virtual void DoMoveWindow(int x, int y, int width, int height); +#if wxUSE_TOOLTIPS + virtual bool MSWProcessMessage(WXMSG* pMsg); + virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM* result); +#endif // used by UpdateFieldText wxClientDC *m_pDC; + // the tooltips used when wxSTB_SHOW_TIPS is given + wxVector m_tooltips; + private: DECLARE_DYNAMIC_CLASS_NO_COPY(wxStatusBar) }; diff --git a/include/wx/msw/tooltip.h b/include/wx/msw/tooltip.h index 7575d5f3b0..24b2ee9b26 100644 --- a/include/wx/msw/tooltip.h +++ b/include/wx/msw/tooltip.h @@ -22,6 +22,11 @@ public: // ctor & dtor wxToolTip(const wxString &tip); virtual ~wxToolTip(); + + // ctor used by wxStatusBar to associate a tooltip to a portion of + // the status bar window: + wxToolTip(wxWindow* win, unsigned int id, + const wxString &tip, const wxRect& rc); // accessors // tip text @@ -49,14 +54,18 @@ public: // implementation only from now on // ------------------------------- - // should be called in responde to WM_MOUSEMOVE + // should be called in response to WM_MOUSEMOVE static void RelayEvent(WXMSG *msg); // add a window to the tooltip control void Add(WXHWND hwnd); // remove any tooltip from the window - static void Remove(WXHWND hwnd); + static void Remove(WXHWND hwnd, unsigned int id, const wxRect& rc); + + // the rect we're associated with + void SetRect(const wxRect& rc); + const wxRect& GetRect() const { return m_rect; } private: // the one and only one tooltip control we use - never access it directly @@ -73,7 +82,10 @@ private: void Remove(); wxString m_text; // tooltip text - wxWindow *m_window; // window we're associated with + wxWindow* m_window; // window we're associated with + wxRect m_rect; // the rect of the window for which this tooltip is shown + // (or a rect with width/height == 0 to show it for the entire window) + unsigned int m_id; // the id of this tooltip (ignored when m_rect width/height is 0) DECLARE_ABSTRACT_CLASS(wxToolTip) wxDECLARE_NO_COPY_CLASS(wxToolTip); diff --git a/include/wx/statusbr.h b/include/wx/statusbr.h index 2a36f8b59b..9b40bf23ca 100644 --- a/include/wx/statusbr.h +++ b/include/wx/statusbr.h @@ -183,8 +183,21 @@ public: virtual bool CanBeOutsideClientArea() const { return true; } protected: + + // wxWindow overrides: + + virtual void DoSetToolTip( wxToolTip *tip ) + { + wxASSERT_MSG(!HasFlag(wxSTB_SHOW_TIPS), + "Do not set tooltip(s) manually when using wxSTB_SHOW_TIPS!"); + return wxWindow::DoSetToolTip(tip); + } + virtual wxBorder GetDefaultBorder() const { return wxBORDER_NONE; } + + // internal helpers & data: + // calculate the real field widths for the given total available size wxArrayInt CalculateAbsWidths(wxCoord widthTotal) const; diff --git a/src/msw/radiobox.cpp b/src/msw/radiobox.cpp index cdf5141085..0bdcb7d346 100644 --- a/src/msw/radiobox.cpp +++ b/src/msw/radiobox.cpp @@ -462,7 +462,9 @@ void wxRadioBox::DoSetItemToolTip(unsigned int item, wxToolTip *tooltip) if ( tooltip != NULL ) tooltip->Add(hwndRbtn); else // unset the tooltip - wxToolTip::Remove(hwndRbtn); + wxToolTip::Remove(hwndRbtn, 0, wxRect(0,0,0,0)); + // the second parameter can be zero since it's ignored by Remove() + // as we pass a rect for which wxRect::IsEmpty()==true... } #endif // wxUSE_TOOLTIPS diff --git a/src/msw/statusbar.cpp b/src/msw/statusbar.cpp index c21dac1aa9..6d9ddf992d 100644 --- a/src/msw/statusbar.cpp +++ b/src/msw/statusbar.cpp @@ -31,6 +31,7 @@ #endif #include "wx/msw/private.h" +#include "wx/tooltip.h" #include #if wxUSE_UXTHEME @@ -71,7 +72,7 @@ bool wxStatusBar::Create(wxWindow *parent, long style, const wxString& name) { - wxCHECK_MSG( parent, false, wxT("status bar must have a parent") ); + wxCHECK_MSG( parent, false, "status bar must have a parent" ); SetName(name); SetWindowStyleFlag(style); @@ -167,11 +168,23 @@ bool wxStatusBar::SetFont(const wxFont& font) void wxStatusBar::SetFieldsCount(int nFields, const int *widths) { // this is a Windows limitation - wxASSERT_MSG( (nFields > 0) && (nFields < 255), _T("too many fields") ); + wxASSERT_MSG( (nFields > 0) && (nFields < 255), "too many fields" ); wxStatusBarBase::SetFieldsCount(nFields, widths); SetFieldsWidth(); + + // keep in synch also our m_tooltips array + + // reset all current tooltips + for (size_t i=0; i= 0) && ((size_t)nField < m_panes.GetCount()), - _T("invalid statusbar field index") ); + "invalid statusbar field index" ); if ( strText == GetStatusText(nField) ) { - // don't call StatusBar_SetText() to avoid flicker - return; + // don't call StatusBar_SetText() to avoid flicker + return; } wxStatusBarBase::SetStatusText(strText, nField); @@ -256,18 +278,69 @@ void wxStatusBar::UpdateFieldText(int nField) else margin = 4; + int maxWidth = rc.GetWidth() - margin; // leave a small margin + wxString text = GetStatusText(nField); + // do we need to ellipsize this string? - wxString ellipsizedStr = - wxControl::Ellipsize(GetStatusText(nField), *m_pDC, - GetLayoutDirection() == wxLayout_RightToLeft ? wxELLIPSIZE_START : wxELLIPSIZE_END, - rc.GetWidth() - margin, // leave a small margin - wxELLIPSIZE_EXPAND_TAB); - - // Pass both field number and style. MSDN library doesn't mention - // that nField and style have to be 'ORed' - if ( !StatusBar_SetText(GetHwnd(), nField | style, ellipsizedStr.wx_str()) ) + wxEllipsizeMode ellmode = (wxEllipsizeMode)-1; + if (HasFlag(wxSTB_ELLIPSIZE_START)) ellmode = wxELLIPSIZE_START; + else if (HasFlag(wxSTB_ELLIPSIZE_MIDDLE)) ellmode = wxELLIPSIZE_MIDDLE; + else if (HasFlag(wxSTB_ELLIPSIZE_END)) ellmode = wxELLIPSIZE_END; + + if (ellmode == (wxEllipsizeMode)-1) + { + // if we have the wxSTB_SHOW_TIPS we must set the ellipsized flag even if + // we don't ellipsize the text but just truncate it + if (HasFlag(wxSTB_SHOW_TIPS)) + SetEllipsizedFlag(nField, m_pDC->GetTextExtent(text).GetWidth() > maxWidth); + } + else { - wxLogLastError(wxT("StatusBar_SetText")); + text = wxControl::Ellipsize(text, + *m_pDC, + ellmode, + maxWidth, + wxELLIPSIZE_EXPAND_TAB); + + // update the ellipsization status for this pane; this is used later to + // decide whether a tooltip should be shown or not for this pane + // (if we have wxSTB_SHOW_TIPS) + SetEllipsizedFlag(nField, text != GetStatusText(nField)); + } + + // Set the status text in the native control passing both field number and style. + // NOTE: MSDN library doesn't mention that nField and style have to be 'ORed' + if ( !StatusBar_SetText(GetHwnd(), nField | style, text.wx_str()) ) + wxLogLastError("StatusBar_SetText"); + + if (HasFlag(wxSTB_SHOW_TIPS)) + { + wxASSERT(m_tooltips.size() == m_panes.GetCount()); + + if (m_tooltips[nField]) + { + if (GetField(nField).IsEllipsized()) + { + // update the rect of this tooltip: + m_tooltips[nField]->SetRect(rc); + + // update also the text: + m_tooltips[nField]->SetTip(GetStatusText(nField)); + } + else + { + // delete the tooltip associated with this pane; it's not needed anymore + delete m_tooltips[nField]; + m_tooltips[nField] = NULL; + } + } + else + { + // create a new tooltip for this pane if needed + if (GetField(nField).IsEllipsized()) + m_tooltips[nField] = new wxToolTip(this, nField, GetStatusText(nField), rc); + //else: leave m_tooltips[nField]==NULL + } } } @@ -291,23 +364,21 @@ void wxStatusBar::SetMinHeight(int height) { SendMessage(GetHwnd(), SB_SETMINHEIGHT, height + 2*GetBorderY(), 0); - // have to send a (dummy) WM_SIZE to redraw it now + // we have to send a (dummy) WM_SIZE to redraw it now SendMessage(GetHwnd(), WM_SIZE, 0, 0); } bool wxStatusBar::GetFieldRect(int i, wxRect& rect) const { wxCHECK_MSG( (i >= 0) && ((size_t)i < m_panes.GetCount()), false, - _T("invalid statusbar field index") ); + "invalid statusbar field index" ); RECT r; if ( !::SendMessage(GetHwnd(), SB_GETRECT, i, (LPARAM)&r) ) - { - wxLogLastError(wxT("SendMessage(SB_GETRECT)")); - } + wxLogLastError("SendMessage(SB_GETRECT)"); #if wxUSE_UXTHEME - wxUxThemeHandle theme((wxStatusBar *)this, L"Status"); // const_cast + wxUxThemeHandle theme(const_cast(this), L"Status"); if ( theme ) { // by default Windows has a 2 pixel border to the right of the left @@ -428,14 +499,13 @@ void wxStatusBar::SetStatusStyles(int n, const int styles[]) style = 0; break; } + // The SB_SETTEXT message is both used to set the field's text as well as - // the fields' styles. MSDN library doesn't mention - // that nField and style have to be 'ORed' + // the fields' styles. + // NOTE: MSDN library doesn't mention that nField and style have to be 'ORed' wxString text = GetStatusText(i); if (!StatusBar_SetText(GetHwnd(), style | i, text.wx_str())) - { - wxLogLastError(wxT("StatusBar_SetText")); - } + wxLogLastError("StatusBar_SetText"); } } @@ -483,7 +553,10 @@ wxStatusBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) } #endif - if ( nMsg == WM_SIZE ) + bool needsEllipsization = HasFlag(wxSTB_ELLIPSIZE_START) || + HasFlag(wxSTB_ELLIPSIZE_MIDDLE) || + HasFlag(wxSTB_ELLIPSIZE_END); + if ( nMsg == WM_SIZE && needsEllipsization ) { for (int i=0; imessage == WM_MOUSEMOVE ) + wxToolTip::RelayEvent(pMsg); + } + + return wxWindow::MSWProcessMessage(pMsg); +} + +bool wxStatusBar::MSWOnNotify(int WXUNUSED(idCtrl), WXLPARAM lParam, WXLPARAM* WXUNUSED(result)); +{ + if ( HasFlag(wxSTB_SHOW_TIPS) ) + { + // see comment in wxStatusBar::MSWProcessMessage for more info; + // basically we need to override wxWindow::MSWOnNotify because + // we have wxWindow::m_tooltip always NULL but we still use tooltips... + + NMHDR* hdr = (NMHDR *)lParam; + + wxString str; + if (hdr->idFrom < m_tooltips.size() && m_tooltips[hdr->idFrom]) + str = m_tooltips[hdr->idFrom]->GetTip(); + + if ( HandleTooltipNotify(hdr->code, lParam, str)) + { + // processed + return true; + } + } + + return false; +} +#endif // wxUSE_TOOLTIPS + #endif // wxUSE_STATUSBAR && wxUSE_NATIVE_STATUSBAR diff --git a/src/msw/tooltip.cpp b/src/msw/tooltip.cpp index 351f44ff6f..a0441a3c19 100644 --- a/src/msw/tooltip.cpp +++ b/src/msw/tooltip.cpp @@ -83,7 +83,7 @@ static WNDPROC gs_wndprocToolTip = (WNDPROC)NULL; class wxToolInfo : public TOOLINFO { public: - wxToolInfo(HWND hwndOwner) + wxToolInfo(HWND hwndOwner, unsigned int id, const wxRect& rc) { // initialize all members ::ZeroMemory(this, sizeof(TOOLINFO)); @@ -95,7 +95,27 @@ public: cbSize = TTTOOLINFO_V1_SIZE; hwnd = hwndOwner; - uFlags = TTF_IDISHWND; + + if (rc.IsEmpty()) + { + uFlags = TTF_IDISHWND; + uId = (UINT_PTR)hwndOwner; + } + else + { + // this tooltip must be shown only if the mouse hovers a specific rect + // of the hwnd parameter! + rect.left = rc.GetLeft(); + rect.right = rc.GetRight(); + rect.top = rc.GetTop(); + rect.bottom = rc.GetBottom(); + + // note that not setting TTF_IDISHWND from the uFlags member means that the + // ti.uId field should not contain the HWND but rather as MSDN says an + // "Application-defined identifier of the tool"; this is used internally by + // Windows to distinguish the different tooltips attached to the same window + uId = id; + } // we use TTF_TRANSPARENT to fix a problem which arises at least with // the text controls but may presumably happen with other controls @@ -107,8 +127,6 @@ public: { uFlags |= TTF_TRANSPARENT; } - - uId = (UINT_PTR)hwndOwner; } }; @@ -284,6 +302,21 @@ wxToolTip::wxToolTip(const wxString &tip) : m_text(tip) { m_window = NULL; + + // make sure m_rect.IsEmpty() == true + m_rect.SetWidth(0); + m_rect.SetHeight(0); + + // since m_rect is not valid, m_id is ignored by wxToolInfo ctor... + m_id = 0; +} + +wxToolTip::wxToolTip(wxWindow* win, unsigned int id, const wxString &tip, const wxRect& rc) + : m_text(tip), m_rect(rc), m_id(id) +{ + m_window = NULL; + + SetWindow(win); } wxToolTip::~wxToolTip() @@ -297,9 +330,11 @@ wxToolTip::~wxToolTip() // others // ---------------------------------------------------------------------------- -void wxToolTip::Remove(WXHWND hWnd) +/* static */ +void wxToolTip::Remove(WXHWND hWnd, unsigned int id, const wxRect& rc) { - wxToolInfo ti((HWND)hWnd); + wxToolInfo ti((HWND)hWnd, id, rc); + (void)SendTooltipMessage(GetToolTipCtrl(), TTM_DELTOOL, &ti); } @@ -308,7 +343,7 @@ void wxToolTip::Remove() // remove this tool from the tooltip control if ( m_window ) { - Remove(m_window->GetHWND()); + Remove(m_window->GetHWND(), m_id, m_rect); } } @@ -316,7 +351,7 @@ void wxToolTip::Add(WXHWND hWnd) { HWND hwnd = (HWND)hWnd; - wxToolInfo ti(hwnd); + wxToolInfo ti(hwnd, m_id, m_rect); // another possibility would be to specify LPSTR_TEXTCALLBACK here as we // store the tooltip text ourselves anyhow, and provide it in response to @@ -460,6 +495,17 @@ void wxToolTip::SetWindow(wxWindow *win) #endif // !defined(__WXUNIVERSAL__) } +void wxToolTip::SetRect(const wxRect& rc) +{ + m_rect = rc; + + if ( m_window ) + { + wxToolInfo ti(GetHwndOf(m_window), m_id, m_rect); + (void)SendTooltipMessage(GetToolTipCtrl(), TTM_NEWTOOLRECT, &ti); + } +} + void wxToolTip::SetTip(const wxString& tip) { m_text = tip; @@ -467,7 +513,7 @@ void wxToolTip::SetTip(const wxString& tip) if ( m_window ) { // update the tip text shown by the control - wxToolInfo ti(GetHwndOf(m_window)); + wxToolInfo ti(GetHwndOf(m_window), m_id, m_rect); // for some reason, changing the tooltip text directly results in // repaint of the controls under it, see #10520 -- but this doesn't -- 2.45.2