X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/9ccffa515ca1a12eff8a8a7c42cd160bc9aa75ba..15cac64f7564d48c274c6490774a77bd8c09f808:/src/msw/toolbar.cpp diff --git a/src/msw/toolbar.cpp b/src/msw/toolbar.cpp index 1c30b4ffa8..bdece22880 100644 --- a/src/msw/toolbar.cpp +++ b/src/msw/toolbar.cpp @@ -219,6 +219,33 @@ private: DECLARE_NO_COPY_CLASS(wxToolBarTool) }; +// ---------------------------------------------------------------------------- +// helper functions +// ---------------------------------------------------------------------------- + +// return the rectangle of the item at the given index +// +// returns an empty (0, 0, 0, 0) rectangle if fails so the caller may compare +// r.right or r.bottom with 0 to check for this +static RECT wxGetTBItemRect(HWND hwnd, int index) +{ + RECT r; + + // note that we use TB_GETITEMRECT and not TB_GETRECT because the latter + // only appeared in v4.70 of comctl32.dll + if ( !::SendMessage(hwnd, TB_GETITEMRECT, index, (LPARAM)&r) ) + { + wxLogLastError(wxT("TB_GETITEMRECT")); + + r.top = + r.left = + r.right = + r.bottom = 0; + } + + return r; +} + // ============================================================================ // implementation // ============================================================================ @@ -370,9 +397,7 @@ wxToolBar::~wxToolBar() { // we must refresh the frame size when the toolbar is deleted but the frame // is not - otherwise toolbar leaves a hole in the place it used to occupy - wxFrame *frame = wxDynamicCast(GetParent(), wxFrame); - if ( frame && !frame->IsBeingDeleted() ) - frame->SendSizeEvent(); + SendSizeEventToParent(); if ( m_hBitmap ) ::DeleteObject((HBITMAP) m_hBitmap); @@ -401,13 +426,27 @@ wxSize wxToolBar::DoGetBestSize() const sizeBest.y = t; } } - else + else // TB_GETMAXSIZE succeeded { + // but it could still return an incorrect result due to what appears to + // be a bug in old comctl32.dll versions which don't handle controls in + // the toolbar correctly, so work around it (see SF patch 1902358) + if ( !IsVertical() && wxApp::GetComCtl32Version() < 600 ) + { + // calculate the toolbar width in alternative way + const RECT rcFirst = wxGetTBItemRect(GetHwnd(), 0); + const RECT rcLast = wxGetTBItemRect(GetHwnd(), GetToolsCount() - 1); + + const int widthAlt = rcLast.right - rcFirst.left; + if ( widthAlt > size.cx ) + size.cx = widthAlt; + } + sizeBest.x = size.cx; sizeBest.y = size.cy; } - if (!IsVertical()) + if ( !IsVertical() ) { // Without the extra height, DoGetBestSize can report a size that's // smaller than the actual window, causing windows to overlap slightly @@ -511,11 +550,7 @@ bool wxToolBar::DoDeleteTool(size_t pos, wxToolBarToolBase *tool) size_t nButtonsToDelete = 1; // get the size of the button we're going to delete - RECT r; - if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT, pos, (LPARAM)&r) ) - { - wxLogLastError(_T("TB_GETITEMRECT")); - } + const RECT r = wxGetTBItemRect(GetHwnd(), pos); int width = r.right - r.left; @@ -544,13 +579,15 @@ bool wxToolBar::DoDeleteTool(size_t pos, wxToolBarToolBase *tool) wxToolBarTool *tool2 = (wxToolBarTool*)node->GetData(); if ( tool2->IsControl() ) { + wxControl * const control = tool2->GetControl(); + int x; - wxControl *control = tool2->GetControl(); control->GetPosition(&x, NULL); control->Move(x - width, wxDefaultCoord); - wxStaticText* staticText = tool2->GetStaticText(); - staticText->Move(x - width, wxDefaultCoord); + wxStaticText * const staticText = tool2->GetStaticText(); + if ( staticText ) + staticText->Move(x - width, wxDefaultCoord); } } @@ -1012,15 +1049,7 @@ bool wxToolBar::Realize() if ( !isControl && !IsVertical() ) continue; - // note that we use TB_GETITEMRECT and not TB_GETRECT because the - // latter only appeared in v4.70 of comctl32.dll - RECT r; - if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT, - index, (LPARAM)(LPRECT)&r) ) - { - wxLogLastError(wxT("TB_GETITEMRECT")); - } - + const RECT r = wxGetTBItemRect(GetHwnd(), index); if ( !isControl ) { // can only be control if isVertical @@ -1189,9 +1218,10 @@ bool wxToolBar::MSWCommand(WXUINT WXUNUSED(cmd), WXWORD id_) bool toggled = false; // just to suppress warnings + LRESULT state = ::SendMessage(GetHwnd(), TB_GETSTATE, id, 0); + if ( tool->CanBeToggled() ) { - LRESULT state = ::SendMessage(GetHwnd(), TB_GETSTATE, id, 0); toggled = (state & TBSTATE_CHECKED) != 0; // ignore the event when a radio button is released, as this doesn't @@ -1203,9 +1233,38 @@ bool wxToolBar::MSWCommand(WXUINT WXUNUSED(cmd), WXWORD id_) UnToggleRadioGroup(tool); } + // Without the two lines of code below, if the toolbar was repainted during + // OnLeftClick(), then it could end up without the tool bitmap temporarily + // (see http://lists.nongnu.org/archive/html/lmi/2008-10/msg00014.html). + // The Update() call bellow ensures that this won't happen, by repainting + // invalidated areas of the toolbar immediately. + // + // To complicate matters, the tool would be drawn in depressed state (this + // code is called when mouse button is released, not pressed). That's not + // ideal, having the tool pressed for the duration of OnLeftClick() + // provides the user with useful visual clue that the app is busy reacting + // to the event. So we manually put the tool into pressed state, handle the + // event and then finally restore tool's original state. + ::SendMessage(GetHwnd(), TB_SETSTATE, id, MAKELONG(state | TBSTATE_PRESSED, 0)); + Update(); + + bool allowLeftClick = OnLeftClick(id, toggled); + + // Restore the unpressed state. Enabled/toggled state might have been + // changed since so take care of it. + if (tool->IsEnabled()) + state |= TBSTATE_ENABLED; + else + state &= ~TBSTATE_ENABLED; + if (tool->IsToggled()) + state |= TBSTATE_CHECKED; + else + state &= ~TBSTATE_CHECKED; + ::SendMessage(GetHwnd(), TB_SETSTATE, id, MAKELONG(state, 0)); + // OnLeftClick() can veto the button state change - for buttons which // may be toggled only, of couse - if ( !OnLeftClick(id, toggled) && tool->CanBeToggled() ) + if ( !allowLeftClick && tool->CanBeToggled() ) { // revert back tool->Toggle(!toggled); @@ -1240,8 +1299,8 @@ bool wxToolBar::MSWOnNotify(int WXUNUSED(idCtrl), return false; // Display popup menu below button - RECT r; - if (::SendMessage(GetHwnd(), TB_GETITEMRECT, GetToolPos(tbhdr->iItem), (LPARAM)&r)) + const RECT r = wxGetTBItemRect(GetHwnd(), GetToolPos(tbhdr->iItem)); + if ( r.right ) PopupMenu(menu, r.left, r.bottom); return true; @@ -1363,7 +1422,7 @@ wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const // TB_HITTEST returns m_nButtons ( not -1 ) if ( index < 0 || (size_t)index >= m_nButtons ) // it's a separator or there is no tool at all there - return (wxToolBarToolBase *)NULL; + return NULL; // when TB_SETBUTTONINFO is available (both during compile- and run-time), // we don't use the dummy separators hack @@ -1391,11 +1450,7 @@ void wxToolBar::UpdateSize() // toolbar to full width again, but only if the parent is a frame and the // toolbar is managed by the frame. Otherwise assume that some other // layout mechanism is controlling the toolbar size and leave it alone. - wxFrame *frame = wxDynamicCast(GetParent(), wxFrame); - if ( frame && frame->GetToolBar() == this ) - { - frame->SendSizeEvent(); - } + SendSizeEventToParent(); } // ---------------------------------------------------------------------------- @@ -1455,7 +1510,7 @@ void wxToolBar::DoSetToggle(wxToolBarToolBase *WXUNUSED(tool), bool WXUNUSED(tog void wxToolBar::SetToolNormalBitmap( int id, const wxBitmap& bitmap ) { - wxToolBarTool* tool = wx_static_cast(wxToolBarTool*, FindById(id)); + wxToolBarTool* tool = static_cast(FindById(id)); if ( tool ) { wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools.")); @@ -1467,7 +1522,7 @@ void wxToolBar::SetToolNormalBitmap( int id, const wxBitmap& bitmap ) void wxToolBar::SetToolDisabledBitmap( int id, const wxBitmap& bitmap ) { - wxToolBarTool* tool = wx_static_cast(wxToolBarTool*, FindById(id)); + wxToolBarTool* tool = static_cast(FindById(id)); if ( tool ) { wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools.")); @@ -1613,48 +1668,50 @@ void wxToolBar::OnEraseBackground(wxEraseEvent& event) bool wxToolBar::HandleSize(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam) { + // wait until we have some tools + if ( !GetToolsCount() ) + return false; + // calculate our minor dimension ourselves - we're confusing the standard // logic (TB_AUTOSIZE) with our horizontal toolbars and other hacks - RECT r; - if ( ::SendMessage(GetHwnd(), TB_GETITEMRECT, 0, (LPARAM)&r) ) - { - int w, h; + const RECT r = wxGetTBItemRect(GetHwnd(), 0); + if ( !r.right ) + return false; - if ( IsVertical() ) + int w, h; + + if ( IsVertical() ) + { + w = r.right - r.left; + if ( m_maxRows ) { - w = r.right - r.left; - if ( m_maxRows ) - { - w *= (m_nButtons + m_maxRows - 1)/m_maxRows; - } - h = HIWORD(lParam); + w *= (m_nButtons + m_maxRows - 1)/m_maxRows; } + h = HIWORD(lParam); + } + else + { + w = LOWORD(lParam); + if (HasFlag( wxTB_FLAT )) + h = r.bottom - r.top - 3; else + h = r.bottom - r.top; + if ( m_maxRows ) { - w = LOWORD(lParam); - if (HasFlag( wxTB_FLAT )) - h = r.bottom - r.top - 3; - else - h = r.bottom - r.top; - if ( m_maxRows ) - { - // FIXME: hardcoded separator line height... - h += HasFlag(wxTB_NODIVIDER) ? 4 : 6; - h *= m_maxRows; - } - } - - if ( MAKELPARAM(w, h) != lParam ) - { - // size really changed - SetSize(w, h); + // FIXME: hardcoded separator line height... + h += HasFlag(wxTB_NODIVIDER) ? 4 : 6; + h *= m_maxRows; } + } - // message processed - return true; + if ( MAKELPARAM(w, h) != lParam ) + { + // size really changed + SetSize(w, h); } - return false; + // message processed + return true; } bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam) @@ -1735,14 +1792,9 @@ bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam) continue; // get the bounding rect of the separator - RECT r; - if ( !::SendMessage(GetHwnd(), TB_GETITEMRECT, - n, (LPARAM)&r) ) - { - wxLogDebug(_T("TB_GETITEMRECT failed?")); - + RECT r = wxGetTBItemRect(GetHwnd(), n); + if ( !r.right ) continue; - } // does it intersect the control? wxRect rectItem; @@ -1837,12 +1889,14 @@ WXLRESULT wxToolBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam #ifndef __WXWINCE__ case WM_PAINT: - if ( HandlePaint(wParam, lParam) ) + // refreshing the controls in the toolbar inside a composite window + // results in an endless stream of WM_PAINT messages -- and seems + // to be unnecessary anyhow as everything works just fine without + // any special workarounds in this case + if ( !IsDoubleBuffered() && HandlePaint(wParam, lParam) ) return 0; -#endif - - default: break; +#endif // __WXWINCE__ } return wxControl::MSWWindowProc(nMsg, wParam, lParam);