]> git.saurik.com Git - wxWidgets.git/commitdiff
Add support for wxSTB_ELLIPSIZE_* and for wxSTB_SHOW_TIPS flags under wxMSW
authorFrancesco Montorsi <f18m_cpp217828@yahoo.it>
Sun, 26 Apr 2009 13:37:16 +0000 (13:37 +0000)
committerFrancesco Montorsi <f18m_cpp217828@yahoo.it>
Sun, 26 Apr 2009 13:37:16 +0000 (13:37 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@60388 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/msw/statusbar.h
include/wx/msw/tooltip.h
include/wx/statusbr.h
src/msw/radiobox.cpp
src/msw/statusbar.cpp
src/msw/tooltip.cpp

index 8e6d7b7c0b53e9b6cede74c5ce3b3874386cca21..4d148481b541568bf10d38ad090d6b4cc86e497f 100644 (file)
@@ -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<wxToolTip*> m_tooltips;
+
 private:
     DECLARE_DYNAMIC_CLASS_NO_COPY(wxStatusBar)
 };
index 7575d5f3b02053e9b16361a3d27ac148b6ef1610..24b2ee9b26a1d77c0f7d7f7c4c5fc91ec4adf9e8 100644 (file)
@@ -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);
index 2a36f8b59bc62f493520b48c1c0bbf6b72eb6433..9b40bf23cac2e660cb6b19255b6b6602a7fa6ee5 100644 (file)
@@ -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;
     
index cdf514108516ca6659673b6f31121f45cfe9c54b..0bdcb7d346da92bbe5a1e4097230595d1c006d2c 100644 (file)
@@ -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
index c21dac1aa9edbb650d6e4274e7a91b0df30c5d8c..6d9ddf992d43f782e79ad2cef8d571afd3a840c1 100644 (file)
@@ -31,6 +31,7 @@
 #endif
 
 #include "wx/msw/private.h"
+#include "wx/tooltip.h"
 #include <windowsx.h>
 
 #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<m_tooltips.size(); i++)
+    {
+        delete m_tooltips[i];
+        m_tooltips[i] = NULL;
+    }
+
+    // shrink/expand the array:
+    m_tooltips.resize(m_panes.GetCount(), NULL);
 }
 
 void wxStatusBar::SetStatusWidths(int n, const int widths[])
@@ -191,33 +204,42 @@ void wxStatusBar::SetFieldsWidth()
 
     int extraWidth = aBorders[2]; // space between fields
 
+
+    // distribute the available space (client width) among the various fields:
+
     wxArrayInt widthsAbs =
         CalculateAbsWidths(GetClientSize().x - extraWidth*(m_panes.GetCount() - 1));
 
+
+    // update the field widths in the native control:
+
     int *pWidths = new int[m_panes.GetCount()];
 
     int nCurPos = 0;
-    for ( size_t i = 0; i < m_panes.GetCount(); i++ ) {
+    for ( size_t i = 0; i < m_panes.GetCount(); i++ )
+    {
         nCurPos += widthsAbs[i] + extraWidth;
         pWidths[i] = nCurPos;
     }
 
-    if ( !StatusBar_SetParts(GetHwnd(), m_panes.GetCount(), pWidths) ) {
-        wxLogLastError(wxT("StatusBar_SetParts"));
-    }
+    if ( !StatusBar_SetParts(GetHwnd(), m_panes.GetCount(), pWidths) )
+        wxLogLastError("StatusBar_SetParts");
 
     delete [] pWidths;
+
+
+    // FIXME: we may want to call UpdateFieldText() here since we may need to (de)ellipsize status texts
 }
 
 void wxStatusBar::SetStatusText(const wxString& strText, int nField)
 {
     wxCHECK_RET( (nField >= 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<wxStatusBar*>(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; i<GetFieldsCount(); i++)
             UpdateFieldText(i);
@@ -494,4 +567,47 @@ wxStatusBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
     return wxStatusBarBase::MSWWindowProc(nMsg, wParam, lParam);
 }
 
+#if wxUSE_TOOLTIPS
+bool wxStatusBar::MSWProcessMessage(WXMSG* pMsg)
+{
+    if ( HasFlag(wxSTB_SHOW_TIPS) )
+    {
+        // for a tooltip to be shown, we need to relay mouse events to it;
+        // this is typically done by wxWindowMSW::MSWProcessMessage but only
+        // if wxWindow::m_tooltip pointer is non-NULL.
+        // Since wxStatusBar has multiple tooltips for a single HWND, it keeps
+        // wxWindow::m_tooltip == NULL and then relays mouse events here:
+        MSG *msg = (MSG *)pMsg;
+        if ( msg->message == 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
index 351f44ff6fd698944c199a8474ab17ad5fc63cc6..a0441a3c193fedba3b68e167a08660cca3db35a2 100644 (file)
@@ -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