]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/toolbar.cpp
Generate wxEVT_KEY_DOWN events for navigation keys in wxMSW wxTreeCtrl.
[wxWidgets.git] / src / msw / toolbar.cpp
index 3faedbf4ad792e6c00589aa1d4f810b17e6d737f..31233565bd00a64bd82e1f518ad4f49fcf46df67 100644 (file)
@@ -36,6 +36,7 @@
     #include "wx/intl.h"
     #include "wx/settings.h"
     #include "wx/bitmap.h"
+    #include "wx/region.h"
     #include "wx/dcmemory.h"
     #include "wx/control.h"
     #include "wx/app.h"         // for GetComCtl32Version
@@ -126,7 +127,6 @@ IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl)
 BEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase)
     EVT_MOUSE_EVENTS(wxToolBar::OnMouseEvent)
     EVT_SYS_COLOUR_CHANGED(wxToolBar::OnSysColourChanged)
-    EVT_ERASE_BACKGROUND(wxToolBar::OnEraseBackground)
 END_EVENT_TABLE()
 
 // ----------------------------------------------------------------------------
@@ -336,17 +336,11 @@ bool wxToolBar::Create(wxWindow *parent,
 
     wxSetCCUnicodeFormat(GetHwnd());
 
-    // workaround for flat toolbar on Windows XP classic style: we have to set
-    // the style after creating the control; doing it at creation time doesn't work
-#if wxUSE_UXTHEME
-    if ( style & wxTB_FLAT )
-    {
-        LRESULT style = GetMSWToolbarStyle();
-
-        if ( !(style & TBSTYLE_FLAT) )
-            ::SendMessage(GetHwnd(), TB_SETSTYLE, 0, style | TBSTYLE_FLAT);
-    }
-#endif // wxUSE_UXTHEME
+    // we always erase our background on WM_PAINT so there is no need to do it
+    // in WM_ERASEBKGND too (by default this won't be done but if the toolbar
+    // has a non default background colour, then it would be used in both
+    // places resulting in flicker)
+    SetBackgroundStyle(wxBG_STYLE_PAINT);
 
     return true;
 }
@@ -501,21 +495,11 @@ WXDWORD wxToolBar::MSWGetStyle(long style, WXDWORD *exstyle) const
     if ( !(style & wxTB_NO_TOOLTIPS) )
         msStyle |= TBSTYLE_TOOLTIPS;
 
-    if ( style & (wxTB_FLAT | wxTB_HORZ_LAYOUT) )
-    {
-        // static as it doesn't change during the program lifetime
-        static const int s_verComCtl = wxApp::GetComCtl32Version();
-
-        // comctl32.dll 4.00 doesn't support the flat toolbars and using this
-        // style with 6.00 (part of Windows XP) leads to the toolbar with
-        // incorrect background colour - and not using it still results in the
-        // correct (flat) toolbar, so don't use it there
-        if ( s_verComCtl > 400 && s_verComCtl < 600 )
-            msStyle |= TBSTYLE_FLAT | TBSTYLE_TRANSPARENT;
-
-        if ( s_verComCtl >= 470 && style & wxTB_HORZ_LAYOUT )
-            msStyle |= TBSTYLE_LIST;
-    }
+    if ( style & wxTB_FLAT && wxApp::GetComCtl32Version() > 400 )
+        msStyle |= TBSTYLE_FLAT;
+
+    if ( style & wxTB_HORZ_LAYOUT && wxApp::GetComCtl32Version() >= 470 )
+        msStyle |= TBSTYLE_LIST;
 
     if ( style & wxTB_NODIVIDER )
         msStyle |= CCS_NODIVIDER;
@@ -532,6 +516,15 @@ WXDWORD wxToolBar::MSWGetStyle(long style, WXDWORD *exstyle) const
     if ( style & wxTB_RIGHT )
         msStyle |= CCS_RIGHT;
 
+    // always use TBSTYLE_TRANSPARENT because the background is not drawn
+    // correctly without it in all themes and, for whatever reason, the control
+    // also flickers horribly when it is resized if this style is not used
+    //
+    // note that this is implicitly enabled by the native toolbar itself when
+    // TBSTYLE_FLAT is used (i.e. it's impossible to use TBSTYLE_FLAT without
+    // TBSTYLE_TRANSPARENT) but turn it on explicitly in any case
+    msStyle |= TBSTYLE_TRANSPARENT;
+
     return msStyle;
 }
 
@@ -941,6 +934,10 @@ bool wxToolBar::Realize()
                     // so we need a valid id for it and not wxID_SEPARATOR
                     // which is used by spacers by default
                     tool->AllocSpacerId();
+
+                    // also set the number of separators so that the logic in
+                    // HandlePaint() works correctly
+                    tool->SetSeparatorsCount(1);
                 }
 
                 button.idCommand = tool->GetId();
@@ -1197,7 +1194,11 @@ bool wxToolBar::Realize()
 
 void wxToolBar::UpdateStretchableSpacersSize()
 {
-    // we can't resize the spacers if TB_SETBUTTONINFO is not supported
+#ifdef TB_SETBUTTONINFO
+    // we can't resize the spacers if TB_SETBUTTONINFO is not supported (we
+    // could try to do it with multiple separators as for the controls but this
+    // is too painful and it just doesn't seem to be worth doing for the
+    // ancient systems)
     if ( wxApp::GetComCtl32Version() < 471 )
         return;
 
@@ -1264,6 +1265,7 @@ void wxToolBar::UpdateStretchableSpacersSize()
             offset += tbbi.cx - (rcOld.right - rcOld.left);
         }
     }
+#endif // TB_SETBUTTONINFO
 }
 
 // ----------------------------------------------------------------------------
@@ -1649,64 +1651,6 @@ void wxToolBar::OnMouseEvent(wxMouseEvent& event)
     }
 }
 
-// This handler is required to allow the toolbar to be set to a non-default
-// colour: for example, when it must blend in with a notebook page.
-void wxToolBar::OnEraseBackground(wxEraseEvent& event)
-{
-    RECT rect = wxGetClientRect(GetHwnd());
-
-    wxDC *dc = event.GetDC();
-    HDC hdc = GetHdcOf(*dc);
-
-#if wxUSE_UXTHEME
-    // we may need to draw themed colour so that we appear correctly on
-    // e.g. notebook page under XP with themes but only do it if the parent
-    // draws themed background itself
-    if ( !UseBgCol() && !GetParent()->UseBgCol() )
-    {
-        wxUxThemeEngine *theme = wxUxThemeEngine::GetIfActive();
-        if ( theme )
-        {
-            HRESULT
-                hr = theme->DrawThemeParentBackground(GetHwnd(), hdc, &rect);
-            if ( hr == S_OK )
-                return;
-
-            // it can also return S_FALSE which seems to simply say that it
-            // didn't draw anything but no error really occurred
-            if ( FAILED(hr) )
-            {
-                wxLogApiError(wxT("DrawThemeParentBackground(toolbar)"), hr);
-            }
-        }
-    }
-
-    if ( MSWEraseRect(*dc) )
-        return;
-#endif // wxUSE_UXTHEME
-
-    // we need to always draw our background under XP, as otherwise it doesn't
-    // appear correctly with some themes (e.g. Zune one)
-    if ( wxGetWinVersion() == wxWinVersion_XP ||
-            UseBgCol() || (GetMSWToolbarStyle() & TBSTYLE_TRANSPARENT) )
-    {
-        // do draw our background
-        //
-        // notice that this 'dumb' implementation may cause flicker for some of
-        // the controls in which case they should intercept wxEraseEvent and
-        // process it themselves somehow
-        AutoHBRUSH hBrush(wxColourToRGB(GetBackgroundColour()));
-
-        wxCHANGE_HDC_MAP_MODE(hdc, MM_TEXT);
-        ::FillRect(hdc, &rect, hBrush);
-    }
-    else // we have no non-default background colour
-    {
-        // let the system do it for us
-        event.Skip();
-    }
-}
-
 bool wxToolBar::HandleSize(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam)
 {
     // wait until we have some tools
@@ -1757,179 +1701,157 @@ bool wxToolBar::HandleSize(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam)
     return true;
 }
 
-#ifndef __WXWINCE__
+#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK
 
-bool wxToolBar::MSWEraseRect(wxDC& dc, const wxRect *rectItem)
+bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam)
 {
-    // erase the given rectangle to hide the separator
-#if wxUSE_UXTHEME
-    // themed background doesn't look well under XP so only draw it for Vista
-    // and later
-    if ( !UseBgCol() && wxGetWinVersion() >= wxWinVersion_Vista )
+    // we must prevent the dummy separators corresponding to controls or
+    // stretchable spaces from being seen: we used to do it by painting over
+    // them but this, unsurprisingly, resulted in a lot of flicker so now we
+    // prevent the toolbar from painting them at all
+
+    // compute the region containing all dummy separators which we don't want
+    // to be seen
+    wxRegion rgnDummySeps;
+    const wxRect rectTotal = GetClientRect();
+    int toolIndex = 0;
+    for ( wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
+          node;
+          node = node->GetNext() )
     {
-        wxUxThemeEngine *theme = wxUxThemeEngine::GetIfActive();
-        if ( theme )
+        wxToolBarTool * const
+            tool = static_cast<wxToolBarTool *>(node->GetData());
+
+        if ( tool->IsControl() || tool->IsStretchableSpace() )
         {
-            wxUxThemeHandle hTheme(this, L"REBAR");
-
-            // Draw the whole background since the pattern may be position
-            // sensitive; but clip it to the area of interest.
-            RECT rcTotal;
-            wxCopyRectToRECT(GetClientSize(), rcTotal);
-
-            RECT rcItem;
-            if ( rectItem )
-                wxCopyRectToRECT(*rectItem, rcItem);
-
-            HRESULT hr = theme->DrawThemeBackground
-                                (
-                                    hTheme,
-                                    GetHdcOf(dc),
-                                    0, 0,
-                                    &rcTotal,
-                                    rectItem ? &rcItem : NULL
-                                );
-            if ( hr == S_OK )
-                return true;
-
-            // it can also return S_FALSE which seems to simply say that it
-            // didn't draw anything but no error really occurred
-            if ( FAILED(hr) )
+            const size_t numSeps = tool->GetSeparatorsCount();
+            for ( size_t n = 0; n < numSeps; n++, toolIndex++ )
             {
-                wxLogApiError(wxT("DrawThemeBackground(toolbar)"), hr);
+                // for some reason TB_GETITEMRECT returns a rectangle 1 pixel
+                // shorter than the full window size (at least under Windows 7)
+                // but we need to erase the full height below
+                RECT rcItem = wxGetTBItemRect(GetHwnd(), toolIndex);
+                rcItem.top = 0;
+                rcItem.bottom = rectTotal.height;
+
+                rgnDummySeps.Union(wxRectFromRECT(rcItem));
             }
         }
-    }
-#endif // wxUSE_UXTHEME
-
-    // this is a bit peculiar but we may simply do nothing here if no rectItem
-    // is specified (and hence we need to erase everything) as this only
-    // happens when we're called from OnEraseBackground() and in this case we
-    // may simply return false to let the systems default background erasing to
-    // take place
-    if ( rectItem )
-        dc.DrawRectangle(*rectItem);
-
-    return false;
-}
-
-bool wxToolBar::HandlePaint(WXWPARAM wParam, WXLPARAM lParam)
-{
-    // erase any dummy separators which were used only for reserving space in
-    // the toolbar (either for a control or just for a stretchable space)
-
-    // first of all, are there any controls at all?
-    wxToolBarToolsList::compatibility_iterator node;
-    for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
-    {
-        wxToolBarToolBase * const tool = node->GetData();
-        if ( tool->IsControl() || tool->IsStretchableSpace() )
-            break;
+        else
+        {
+            // normal tools never correspond to more than one native button
+            toolIndex++;
+        }
     }
 
-    if ( !node )
+    if ( rgnDummySeps.IsOk() )
     {
-        // no controls, nothing to erase
-        return false;
+        // exclude the area occupied by the controls and stretchable spaces
+        // from the update region to prevent the toolbar from drawing
+        // separators in it
+        if ( !::ValidateRgn(GetHwnd(), GetHrgnOf(rgnDummySeps)) )
+        {
+            wxLogLastError(wxT("ValidateRgn()"));
+        }
     }
 
-    // prepare the DC on which we'll be drawing
-    wxClientDC dc(this);
-    dc.SetPen(*wxTRANSPARENT_PEN);
+    // still let the native control draw everything else normally but set up a
+    // hook to be able to process the next WM_ERASEBKGND sent to our parent
+    // because toolbar will ask it to erase its background from its WM_PAINT
+    // handler (when using TBSTYLE_TRANSPARENT which we do always use)
+    //
+    // installing hook is not completely trivial as all kinds of strange
+    // situations are possible: sometimes we can be called recursively from
+    // inside the native toolbar WM_PAINT handler so the hook might already be
+    // installed and sometimes the native toolbar might not send WM_ERASEBKGND
+    // to the parent at all for whatever reason, so deal with all these cases
+    wxWindow * const parent = GetParent();
+    const bool hadHook = parent->MSWHasEraseBgHook();
+    if ( !hadHook )
+        GetParent()->MSWSetEraseBgHook(this);
 
-    RECT rcUpdate;
-    if ( !::GetUpdateRect(GetHwnd(), &rcUpdate, FALSE) )
-    {
-        // nothing to redraw anyhow
-        return false;
-    }
+    MSWDefWindowProc(WM_PAINT, wParam, lParam);
 
-    const wxRect rectUpdate = wxRectFromRECT(rcUpdate);
-    dc.SetClippingRegion(rectUpdate);
+    if ( !hadHook )
+        GetParent()->MSWSetEraseBgHook(NULL);
 
-    // draw the toolbar tools, separators &c normally
-    wxControl::MSWWindowProc(WM_PAINT, wParam, lParam);
 
-    // for each control in the toolbar find all the separators intersecting it
-    // and erase them
-    //
-    // NB: this is really the only way to do it as we don't know if a separator
-    //     corresponds to a control (i.e. is a dummy one) or a real one
-    //     otherwise
-    int toolIndex = 0;
-    for ( node = m_tools.GetFirst(); node; node = node->GetNext(), toolIndex++ )
+    if ( rgnDummySeps.IsOk() )
     {
-        wxToolBarTool *tool = (wxToolBarTool*)node->GetData();
-        if ( tool->IsControl() )
-        {
-            // get the control rect in our client coords
-            wxControl *control = tool->GetControl();
-            wxStaticText *staticText = tool->GetStaticText();
-            wxRect rectCtrl = control->GetRect();
-            wxRect rectStaticText;
-            if ( staticText )
-                rectStaticText = staticText->GetRect();
-
-            if ( !rectCtrl.Intersects(rectUpdate) &&
-                    (!staticText || !rectStaticText.Intersects(rectUpdate)) )
-                continue;
+        // erase the dummy separators region ourselves now as nobody painted
+        // over them
+        WindowHDC hdc(GetHwnd());
+        ::SelectClipRgn(hdc, GetHrgnOf(rgnDummySeps));
+        MSWDoEraseBackground(hdc);
+    }
 
-            // iterate over all buttons to find all separators intersecting
-            // this control
-            TBBUTTON tbb;
-            int count = ::SendMessage(GetHwnd(), TB_BUTTONCOUNT, 0, 0);
-            for ( int n = 0; n < count; n++ )
-            {
-                // is it a separator?
-                if ( !::SendMessage(GetHwnd(), TB_GETBUTTON,
-                                    n, (LPARAM)&tbb) )
-                {
-                    wxLogDebug(wxT("TB_GETBUTTON failed?"));
+    return true;
+}
 
-                    continue;
-                }
+WXHBRUSH wxToolBar::MSWGetToolbarBgBrush()
+{
+    // we conservatively use a solid brush here but we could also use a themed
+    // brush by using DrawThemeBackground() to create a bitmap brush (it'd need
+    // to be invalidated whenever the toolbar is resized and, also, correctly
+    // aligned using SetBrushOrgEx() before each use -- there is code for doing
+    // this in wxNotebook already so it'd need to be refactored into wxWindow)
+    //
+    // however inasmuch as there is a default background for the toolbar at all
+    // (and this is not a trivial question as different applications use very
+    // different colours), it seems to be a solid one and using REBAR
+    // background brush as we used to do before doesn't look good at all under
+    // Windows 7 (and probably Vista too), so for now we just keep it simple
+    wxColour const
+        colBg = m_hasBgCol ? GetBackgroundColour()
+                           : wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
+    wxBrush * const
+        brush = wxTheBrushList->FindOrCreateBrush(colBg);
+
+    return brush ? brush->GetResourceHandle() : 0;
+}
 
-                if ( tbb.fsStyle != TBSTYLE_SEP )
-                    continue;
+WXHBRUSH wxToolBar::MSWGetBgBrushForChild(WXHDC hDC, wxWindowMSW *child)
+{
+    WXHBRUSH hbr = wxToolBarBase::MSWGetBgBrushForChild(hDC, child);
+    if ( hbr )
+        return hbr;
 
-                // get the bounding rect of the separator
-                RECT r = wxGetTBItemRect(GetHwnd(), n);
-                if ( !r.right )
-                    continue;
+    // the base class version only returns a brush for erasing children
+    // background if we have a non-default background colour but as the toolbar
+    // doesn't erase its own background by default, we need to always do it for
+    // (semi-)transparent children
+    if ( child->GetParent() == this && child->HasTransparentBackground() )
+        return MSWGetToolbarBgBrush();
 
-                const wxRect rectItem = wxRectFromRECT(r);
+    return 0;
+}
 
-                // does it intersect the update region at all?
-                if ( !rectUpdate.Intersects(rectItem) )
-                    continue;
+void wxToolBar::MSWDoEraseBackground(WXHDC hDC)
+{
+    wxFillRect(GetHwnd(), (HDC)hDC, (HBRUSH)MSWGetToolbarBgBrush());
+}
 
-                // does it intersect the control itself or its label?
-                //
-                // if it does, refresh it so it's redrawn on top of the
-                // background
-                if ( rectCtrl.Intersects(rectItem) )
-                    control->Refresh(false);
-                else if ( staticText && rectStaticText.Intersects(rectItem) )
-                    staticText->Refresh(false);
-                else
-                    continue;
+bool wxToolBar::MSWEraseBgHook(WXHDC hDC)
+{
+    // toolbar WM_PAINT handler offsets the DC origin before sending
+    // WM_ERASEBKGND to the parent but as we handle it in the toolbar itself,
+    // we need to reset it back
+    HDC hdc = (HDC)hDC;
+    POINT ptOldOrg;
+    if ( !::SetWindowOrgEx(hdc, 0, 0, &ptOldOrg) )
+    {
+        wxLogLastError(wxT("SetWindowOrgEx(tbar-bg-hdc)"));
+        return false;
+    }
 
-                MSWEraseRect(dc, &rectItem);
-            }
-        }
-        else if ( tool->IsStretchableSpace() )
-        {
-            const wxRect
-                rectItem = wxRectFromRECT(wxGetTBItemRect(GetHwnd(), toolIndex));
+    MSWDoEraseBackground(hDC);
 
-            if ( rectUpdate.Intersects(rectItem) )
-                MSWEraseRect(dc, &rectItem);
-        }
-    }
+    ::SetWindowOrgEx(hdc, ptOldOrg.x, ptOldOrg.y, NULL);
 
     return true;
 }
-#endif // __WXWINCE__
+
+#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK
 
 void wxToolBar::HandleMouseMove(WXWPARAM WXUNUSED(wParam), WXLPARAM lParam)
 {
@@ -1960,7 +1882,7 @@ WXLRESULT wxToolBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam
                 return 0;
             break;
 
-#ifndef __WXWINCE__
+#ifdef wxHAS_MSW_BACKGROUND_ERASE_HOOK
         case WM_PAINT:
             // refreshing the controls in the toolbar inside a composite window
             // results in an endless stream of WM_PAINT messages -- and seems
@@ -1969,7 +1891,7 @@ WXLRESULT wxToolBar::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam
             if ( !IsDoubleBuffered() && HandlePaint(wParam, lParam) )
                 return 0;
             break;
-#endif // __WXWINCE__
+#endif // wxHAS_MSW_BACKGROUND_ERASE_HOOK
     }
 
     return wxControl::MSWWindowProc(nMsg, wParam, lParam);