]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/statusbar.cpp
Correct week day returned from wxCalendarCtrl::HitTest() on header click.
[wxWidgets.git] / src / msw / statusbar.cpp
index a913d193089804d59a88c1a426867ec21591334c..72da7d99804357920c2ddddc48891320ff8a2cd8 100644 (file)
     #include "wx/dcclient.h"
     #include "wx/intl.h"
     #include "wx/log.h"
+    #include "wx/control.h"
 #endif
 
 #include "wx/msw/private.h"
+#include "wx/tooltip.h"
 #include <windowsx.h>
 
 #if wxUSE_UXTHEME
     #include "wx/msw/uxtheme.h"
 #endif
 
+// no idea for a default width, just choose something
+#define DEFAULT_FIELD_WIDTH 25
+
 // ----------------------------------------------------------------------------
 // macros
 // ----------------------------------------------------------------------------
@@ -59,6 +64,7 @@ wxStatusBar::wxStatusBar()
     SetParent(NULL);
     m_hWnd = 0;
     m_windowId = 0;
+    m_pDC = NULL;
 }
 
 bool wxStatusBar::Create(wxWindow *parent,
@@ -66,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);
@@ -84,9 +90,9 @@ bool wxStatusBar::Create(wxWindow *parent,
     // setting SBARS_SIZEGRIP is perfectly useless: it's always on by default
     // (at least in the version of comctl32.dll I'm using), and the only way to
     // turn it off is to use CCS_TOP style - as we position the status bar
-    // manually anyhow (see DoMoveWindow), use CCS_TOP style if wxST_SIZEGRIP
+    // manually anyhow (see DoMoveWindow), use CCS_TOP style if wxSTB_SIZEGRIP
     // is not given
-    if ( !(style & wxST_SIZEGRIP) )
+    if ( !(style & wxSTB_SIZEGRIP) )
     {
         wstyle |= CCS_TOP;
     }
@@ -102,7 +108,7 @@ bool wxStatusBar::Create(wxWindow *parent,
     m_hWnd = CreateWindow
              (
                 STATUSCLASSNAME,
-                _T(""),
+                wxT(""),
                 wstyle,
                 0, 0, 0, 0,
                 GetHwndOf(parent),
@@ -119,6 +125,12 @@ bool wxStatusBar::Create(wxWindow *parent,
 
     SetFieldsCount(1);
     SubclassWin(m_hWnd);
+
+    // cache the DC instance used by UpdateFieldText:
+    // NOTE: create the DC before calling InheritAttributes() since
+    //       it may result in a call to our SetFont()
+    m_pDC = new wxClientDC(this);
+
     InheritAttributes();
 
     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR));
@@ -140,16 +152,52 @@ wxStatusBar::~wxStatusBar()
     // frame is not - otherwise statusbar leaves a hole in the place it used to
     // occupy
     PostSizeEventToParent();
+
+    // delete existing tooltips
+    for (size_t i=0; i<m_tooltips.size(); i++)
+    {
+        if (m_tooltips[i])
+        {
+            delete m_tooltips[i];
+            m_tooltips[i] = NULL;
+        }
+    }
+
+    wxDELETE(m_pDC);
+}
+
+bool wxStatusBar::SetFont(const wxFont& font)
+{
+    if (!wxWindow::SetFont(font))
+        return false;
+
+    if (m_pDC) m_pDC->SetFont(font);
+    return true;
 }
 
 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++)
+    {
+        if (m_tooltips[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[])
@@ -169,38 +217,59 @@ 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 ( int 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) && (nField < m_nFields),
-                 _T("invalid statusbar field index") );
+    wxCHECK_RET( (nField >= 0) && ((size_t)nField < m_panes.GetCount()),
+                 "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);
+
+    UpdateFieldText(nField);
+}
+
+void wxStatusBar::UpdateFieldText(int nField)
+{
+    if (!m_pDC)
+        return;
+
     // Get field style, if any
     int style;
-    switch(m_panes[nField].nStyle)
+    switch(m_panes[nField].GetStyle())
     {
     case wxSB_RAISED:
         style = SBT_POPOUT;
@@ -215,27 +284,81 @@ void wxStatusBar::SetStatusText(const wxString& strText, int nField)
         break;
     }
 
-    // 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, strText.wx_str()) )
+    wxRect rc;
+    GetFieldRect(nField, rc);
+
+    int margin;
+    if (nField == GetFieldsCount()-1)
+        margin = -6;        // windows reports a smaller rect for the last field; enlarge it
+    else
+        margin = 4;
+
+    int maxWidth = rc.GetWidth() - margin;    // leave a small margin
+    wxString text = GetStatusText(nField);
+
+    // do we need to ellipsize this string?
+    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)
     {
-        wxLogLastError(wxT("StatusBar_SetText"));
+        // 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
+    {
+        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));
     }
-}
-
-wxString wxStatusBar::GetStatusText(int nField) const
-{
-    wxCHECK_MSG( (nField >= 0) && (nField < m_panes.GetCount()), wxEmptyString,
-                 _T("invalid statusbar field index") );
 
-    wxString str;
-    int len = StatusBar_GetTextLen(GetHwnd(), nField);
-    if ( len > 0 )
+    // 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()) )
     {
-        StatusBar_GetText(GetHwnd(), nField, wxStringBuffer(str, len));
+        wxLogLastError("StatusBar_SetText");
     }
 
-    return str;
+    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
+        }
+    }
 }
 
 int wxStatusBar::GetBorderX() const
@@ -258,23 +381,23 @@ 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) && (i < m_panes.GetCount()), false,
-                 _T("invalid statusbar field index") );
+    wxCHECK_MSG( (i >= 0) && ((size_t)i < m_panes.GetCount()), false,
+                 "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
@@ -295,9 +418,6 @@ bool wxStatusBar::GetFieldRect(int i, wxRect& rect) const
     return true;
 }
 
-// no idea for a default width, just choose something
-#define DEFAULT_FIELD_WIDTH 25
-
 wxSize wxStatusBar::DoGetBestSize() const
 {
     int borders[3];
@@ -305,10 +425,10 @@ wxSize wxStatusBar::DoGetBestSize() const
 
     // calculate width
     int width = 0;
-    for ( int i = 0; i < m_panes.GetCount(); ++i )
+    for ( size_t i = 0; i < m_panes.GetCount(); ++i )
     {
         int widthField =
-            m_bSameWidthForAllPanes ? DEFAULT_FIELD_WIDTH : m_panes[i].nWidth;
+            m_bSameWidthForAllPanes ? DEFAULT_FIELD_WIDTH : m_panes[i].GetWidth();
         if ( widthField >= 0 )
         {
             width += widthField;
@@ -330,7 +450,6 @@ wxSize wxStatusBar::DoGetBestSize() const
         width = 2*DEFAULT_FIELD_WIDTH;
     }
 
-
     // calculate height
     int height;
     wxGetCharSize(GetHWND(), NULL, &height, GetFont());
@@ -380,7 +499,7 @@ void wxStatusBar::SetStatusStyles(int n, const int styles[])
 {
     wxStatusBarBase::SetStatusStyles(n, styles);
 
-    if (n != m_panes.GetCount())
+    if (n != (int)m_panes.GetCount())
         return;
 
     for (int i = 0; i < n; i++)
@@ -399,13 +518,14 @@ 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");
         }
     }
 }
@@ -454,7 +574,61 @@ wxStatusBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
     }
 #endif
 
+    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);
+            // re-set the field text, in case we need to ellipsize
+            // (or de-ellipsize) some parts of it
+    }
+
     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