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
This commit is contained in:
Francesco Montorsi 2009-04-26 13:37:16 +00:00
parent 6540d8d2ba
commit 6418ad5ec8
6 changed files with 245 additions and 45 deletions

View 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)
};

View 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);

View 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;

View 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

View 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;
// 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);
int maxWidth = rc.GetWidth() - margin; // leave a small margin
wxString text = GetStatusText(nField);
// 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()) )
// 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));
}
// 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

View 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