]> git.saurik.com Git - wxWidgets.git/blobdiff - src/ribbon/page.cpp
Merged GSOC Ribbon work from SOC2009_RIBBON branch into trunk.
[wxWidgets.git] / src / ribbon / page.cpp
diff --git a/src/ribbon/page.cpp b/src/ribbon/page.cpp
new file mode 100644 (file)
index 0000000..812d0da
--- /dev/null
@@ -0,0 +1,1037 @@
+///////////////////////////////////////////////////////////////////////////////
+// Name:        src/ribbon/page.cpp
+// Purpose:     Container for ribbon-bar-style interface panels
+// Author:      Peter Cawley
+// Modified by:
+// Created:     2009-05-25
+// RCS-ID:      $Id$
+// Copyright:   (C) Peter Cawley
+// Licence:     wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+#include "wx/ribbon/page.h"
+
+#if wxUSE_RIBBON
+
+#include "wx/ribbon/art.h"
+#include "wx/ribbon/bar.h"
+#include "wx/dcbuffer.h"
+
+#ifndef WX_PRECOMP
+#endif
+
+#ifdef __WXMSW__
+#include "wx/msw/private.h"
+#endif
+
+static int GetSizeInOrientation(wxSize size, wxOrientation orientation);
+
+// As scroll buttons need to be rendered on top of a page's child windows, the
+// buttons themselves have to be proper child windows (rather than just painted
+// onto the page). In order to get proper clipping of a page's children (with
+// regard to the scroll button), the scroll buttons are created as children of
+// the ribbon bar rather than children of the page. This could not have been
+// achieved by creating buttons as children of the page and then doing some Z-order
+// manipulation, as this causes problems on win32 due to ribbon panels having the
+// transparent flag set.
+class wxRibbonPageScrollButton : public wxRibbonControl
+{
+public:
+    wxRibbonPageScrollButton(wxRibbonPage* sibling,
+                 wxWindowID id = wxID_ANY,
+                 const wxPoint& pos = wxDefaultPosition,
+                 const wxSize& size = wxDefaultSize,
+                 long style = 0);
+
+    virtual ~wxRibbonPageScrollButton();
+
+protected:
+    virtual wxBorder GetDefaultBorder() const { return wxBORDER_NONE; }
+
+    void OnEraseBackground(wxEraseEvent& evt);
+    void OnPaint(wxPaintEvent& evt);
+    void OnMouseEnter(wxMouseEvent& evt);
+    void OnMouseLeave(wxMouseEvent& evt);
+    void OnMouseDown(wxMouseEvent& evt);
+    void OnMouseUp(wxMouseEvent& evt);
+
+    wxRibbonPage* m_sibling;
+    long m_flags;
+
+    DECLARE_CLASS(wxRibbonPageScrollButton)
+    DECLARE_EVENT_TABLE()
+};
+
+IMPLEMENT_CLASS(wxRibbonPageScrollButton, wxRibbonControl)
+
+BEGIN_EVENT_TABLE(wxRibbonPageScrollButton, wxRibbonControl)
+    EVT_ENTER_WINDOW(wxRibbonPageScrollButton::OnMouseEnter)
+    EVT_ERASE_BACKGROUND(wxRibbonPageScrollButton::OnEraseBackground)
+    EVT_LEAVE_WINDOW(wxRibbonPageScrollButton::OnMouseLeave)
+    EVT_LEFT_DOWN(wxRibbonPageScrollButton::OnMouseDown)
+    EVT_LEFT_UP(wxRibbonPageScrollButton::OnMouseUp)
+    EVT_PAINT(wxRibbonPageScrollButton::OnPaint)
+END_EVENT_TABLE()
+
+wxRibbonPageScrollButton::wxRibbonPageScrollButton(wxRibbonPage* sibling,
+                 wxWindowID id,
+                 const wxPoint& pos,
+                 const wxSize& size,
+                 long style) : wxRibbonControl(sibling->GetParent(), id, pos, size, wxBORDER_NONE)
+{
+    SetBackgroundStyle(wxBG_STYLE_CUSTOM);
+    m_sibling = sibling;
+    m_flags = (style & wxRIBBON_SCROLL_BTN_DIRECTION_MASK) | wxRIBBON_SCROLL_BTN_FOR_PAGE;
+}
+
+wxRibbonPageScrollButton::~wxRibbonPageScrollButton()
+{
+}
+
+void wxRibbonPageScrollButton::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
+{
+    // Do nothing - all painting done in main paint handler
+}
+
+void wxRibbonPageScrollButton::OnPaint(wxPaintEvent& WXUNUSED(evt))
+{
+    wxAutoBufferedPaintDC dc(this);
+    if(m_art)
+    {
+        m_art->DrawScrollButton(dc, this, GetSize(), m_flags);
+    }
+}
+
+void wxRibbonPageScrollButton::OnMouseEnter(wxMouseEvent& WXUNUSED(evt))
+{
+    m_flags |= wxRIBBON_SCROLL_BTN_HOVERED;
+    Refresh(false);
+}
+
+void wxRibbonPageScrollButton::OnMouseLeave(wxMouseEvent& WXUNUSED(evt))
+{
+    m_flags &= ~wxRIBBON_SCROLL_BTN_HOVERED;
+    m_flags &= ~wxRIBBON_SCROLL_BTN_ACTIVE;
+    Refresh(false);
+}
+
+void wxRibbonPageScrollButton::OnMouseDown(wxMouseEvent& WXUNUSED(evt))
+{
+    m_flags |= wxRIBBON_SCROLL_BTN_ACTIVE;
+    Refresh(false);
+}
+
+void wxRibbonPageScrollButton::OnMouseUp(wxMouseEvent& WXUNUSED(evt))
+{
+    if(m_flags & wxRIBBON_SCROLL_BTN_ACTIVE)
+    {
+        m_flags &= ~wxRIBBON_SCROLL_BTN_ACTIVE;
+        Refresh(false);
+        switch(m_flags & wxRIBBON_SCROLL_BTN_DIRECTION_MASK)
+        {
+        case wxRIBBON_SCROLL_BTN_DOWN:
+        case wxRIBBON_SCROLL_BTN_RIGHT:
+            m_sibling->ScrollLines(1);
+            break;
+        case wxRIBBON_SCROLL_BTN_UP:
+        case wxRIBBON_SCROLL_BTN_LEFT:
+            m_sibling->ScrollLines(-1);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+IMPLEMENT_CLASS(wxRibbonPage, wxRibbonControl)
+
+BEGIN_EVENT_TABLE(wxRibbonPage, wxRibbonControl)
+    EVT_ERASE_BACKGROUND(wxRibbonPage::OnEraseBackground)
+    EVT_PAINT(wxRibbonPage::OnPaint)
+    EVT_SIZE(wxRibbonPage::OnSize)
+END_EVENT_TABLE()
+
+wxRibbonPage::wxRibbonPage()
+{
+    m_scroll_left_btn = NULL;
+    m_scroll_right_btn = NULL;
+    m_scroll_amount = 0;
+    m_scroll_buttons_visible = false;
+}
+
+wxRibbonPage::wxRibbonPage(wxRibbonBar* parent,
+                   wxWindowID id,
+                   const wxString& label,
+                   const wxBitmap& icon,
+                   long WXUNUSED(style))
+    : wxRibbonControl(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE)
+{
+    CommonInit(label, icon);
+}
+
+wxRibbonPage::~wxRibbonPage()
+{
+}
+
+bool wxRibbonPage::Create(wxRibbonBar* parent,
+                wxWindowID id,
+                const wxString& label,
+                const wxBitmap& icon,
+                long WXUNUSED(style))
+{
+    if(!wxRibbonControl::Create(parent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE))
+        return false;
+
+    CommonInit(label, icon);
+
+    return true;
+}
+
+void wxRibbonPage::CommonInit(const wxString& label, const wxBitmap& icon)
+{
+    SetName(label);
+
+    SetLabel(label);
+    m_old_size = wxSize(0, 0);
+    m_icon = icon;
+    m_scroll_left_btn = NULL;
+    m_scroll_right_btn = NULL;
+    m_scroll_amount = 0;
+    m_scroll_buttons_visible = false;
+
+    SetBackgroundStyle(wxBG_STYLE_CUSTOM);
+
+    wxDynamicCast(GetParent(), wxRibbonBar)->AddPage(this);
+}
+
+void wxRibbonPage::SetArtProvider(wxRibbonArtProvider* art)
+{
+    m_art = art;
+    for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+          node;
+          node = node->GetNext() )
+    {
+        wxWindow* child = node->GetData();
+        wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
+        if(ribbon_child)
+        {
+            ribbon_child->SetArtProvider(art);
+        }
+    }
+}
+
+void wxRibbonPage::AdjustRectToIncludeScrollButtons(wxRect* rect) const
+{
+    if(m_scroll_buttons_visible)
+    {
+        if(GetMajorAxis() == wxVERTICAL)
+        {
+            if(m_scroll_left_btn)
+            {
+                rect->SetY(rect->GetY() -
+                    m_scroll_left_btn->GetSize().GetHeight());
+                rect->SetHeight(rect->GetHeight() +
+                    m_scroll_left_btn->GetSize().GetHeight());
+            }
+            if(m_scroll_right_btn)
+            {
+                rect->SetHeight(rect->GetHeight() +
+                    m_scroll_right_btn->GetSize().GetHeight());
+            }
+        }
+        else
+        {
+            if(m_scroll_left_btn)
+            {
+                rect->SetX(rect->GetX() -
+                    m_scroll_left_btn->GetSize().GetWidth());
+                rect->SetWidth(rect->GetWidth() +
+                    m_scroll_left_btn->GetSize().GetWidth());
+            }
+            if(m_scroll_right_btn)
+            {
+                rect->SetWidth(rect->GetWidth() +
+                    m_scroll_right_btn->GetSize().GetWidth());
+            }
+        }
+    }
+}
+
+void wxRibbonPage::OnEraseBackground(wxEraseEvent& WXUNUSED(evt))
+{
+    // All painting done in main paint handler to minimise flicker
+}
+
+void wxRibbonPage::OnPaint(wxPaintEvent& WXUNUSED(evt))
+{
+    // No foreground painting done by the page itself, but a paint DC
+    // must be created anyway.
+    wxAutoBufferedPaintDC dc(this);
+    wxRect rect(GetSize());
+    AdjustRectToIncludeScrollButtons(&rect);
+    m_art->DrawPageBackground(dc, this, rect);
+}
+
+wxOrientation wxRibbonPage::GetMajorAxis() const
+{
+    if(m_art && (m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL))
+    {
+        return wxVERTICAL;
+    }
+    else
+    {
+        return wxHORIZONTAL;
+    }
+}
+
+bool wxRibbonPage::ScrollLines(int lines)
+{
+    return ScrollPixels(lines * 8);
+}
+
+bool wxRibbonPage::ScrollPixels(int pixels)
+{
+    if(pixels < 0)
+    {
+        if(m_scroll_amount == 0)
+            return false;
+        if(m_scroll_amount < -pixels)
+            pixels = -m_scroll_amount;
+    }
+    else if(pixels > 0)
+    {
+        if(m_scroll_amount == m_scroll_amount_limit)
+            return false;
+        if(m_scroll_amount + pixels > m_scroll_amount_limit)
+            pixels = m_scroll_amount_limit - m_scroll_amount;
+    }
+    else
+        return false;
+
+    m_scroll_amount += pixels;
+
+    for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+              node;
+              node = node->GetNext() )
+    {
+        wxWindow* child = node->GetData();
+        int x, y;
+        child->GetPosition(&x, &y);
+        if(GetMajorAxis() == wxHORIZONTAL)
+            x -= pixels;
+        else
+            y -= pixels;
+        child->SetPosition(wxPoint(x, y));
+    }
+
+    ShowScrollButtons();
+    Refresh();
+    return true;
+}
+
+void wxRibbonPage::SetSizeWithScrollButtonAdjustment(int x, int y, int width, int height)
+{
+    if(m_scroll_buttons_visible)
+    {
+        if(GetMajorAxis() == wxHORIZONTAL)
+        {
+            if(m_scroll_left_btn)
+            {
+                int w = m_scroll_left_btn->GetSize().GetWidth();
+                m_scroll_left_btn->SetPosition(wxPoint(x, y));
+                x += w;
+                width -= w;
+            }
+            if(m_scroll_right_btn)
+            {
+                int w = m_scroll_right_btn->GetSize().GetWidth();
+                width -= w;
+                m_scroll_right_btn->SetPosition(wxPoint(x + width, y));
+            }
+        }
+        else
+        {
+            if(m_scroll_left_btn)
+            {
+                int h = m_scroll_left_btn->GetSize().GetHeight();
+                m_scroll_left_btn->SetPosition(wxPoint(x, y));
+                y += h;
+                height -= h;
+            }
+            if(m_scroll_right_btn)
+            {
+                int h = m_scroll_right_btn->GetSize().GetHeight();
+                height -= h;
+                m_scroll_right_btn->SetPosition(wxPoint(x, y + height));
+            }
+        }
+    }
+    SetSize(x, y, width, height);
+}
+
+void wxRibbonPage::DoSetSize(int x, int y, int width, int height, int sizeFlags)
+{
+    // When a resize triggers the scroll buttons to become visible, the page is resized.
+    // This resize from within a resize event can cause (MSW) wxWidgets some confusion,
+    // and report the 1st size to the 2nd size event. Hence the most recent size is
+    // remembered internally and used in Layout() where appropiate.
+
+    if(GetMajorAxis() == wxHORIZONTAL)
+        m_size_in_major_axis_for_children = width;
+    else
+        m_size_in_major_axis_for_children = height;
+
+    wxRibbonControl::DoSetSize(x, y, width, height, sizeFlags);
+}
+
+void wxRibbonPage::OnSize(wxSizeEvent& evt)
+{
+    wxSize new_size = evt.GetSize();
+
+    wxMemoryDC temp_dc;
+    wxRect invalid_rect = m_art->GetPageBackgroundRedrawArea(temp_dc, this, m_old_size, new_size);
+    Refresh(true, &invalid_rect);
+
+    m_old_size = new_size;
+
+    if(new_size.GetX() > 0 && new_size.GetY() > 0)
+    {
+        Layout();
+    }
+    else
+    {
+        // Simplify other calculations by pretending new size is zero in both
+        // X and Y
+        new_size.Set(0, 0);
+        // When size == 0, no point in doing any layout
+    }
+
+    evt.Skip();
+}
+
+void wxRibbonPage::RemoveChild(wxWindowBase *child)
+{
+    // Remove all references to the child from the collapse stack
+    size_t count = m_collapse_stack.GetCount();
+    size_t src, dst;
+    for(src = 0, dst = 0; src < count; ++src, ++dst)
+    {
+        wxRibbonControl *item = m_collapse_stack.Item(src);
+        if(item == child)
+        {
+            ++src;
+            if(src == count)
+            {
+                break;
+            }
+        }
+        if(src != dst)
+        {
+            m_collapse_stack.Item(dst) = item;
+        }
+    }
+    if(src > dst)
+    {
+        m_collapse_stack.RemoveAt(dst, src - dst);
+    }
+
+    // ... and then proceed as normal
+    wxRibbonControl::RemoveChild(child);
+}
+
+bool wxRibbonPage::Realize()
+{
+    bool status = true;
+
+    m_collapse_stack.Clear();
+    for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+                  node;
+                  node = node->GetNext())
+    {
+        wxRibbonControl* child = wxDynamicCast(node->GetData(), wxRibbonControl);
+        if(child == NULL)
+        {
+            continue;
+        }
+        if(!child->Realize())
+        {
+            status = false;
+        }
+        child->SetSize(child->GetMinSize());
+    }
+
+    if(GetSize().GetX() > 0 && GetSize().GetY() > 0)
+    {
+        status = Layout() && status;
+    }
+
+    return status;
+}
+
+bool wxRibbonPage::Layout()
+{
+    if(GetChildren().GetCount() == 0)
+    {
+        return true;
+    }
+
+    wxPoint origin_(m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_LEFT_SIZE), m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_TOP_SIZE));
+    wxOrientation major_axis = GetMajorAxis();
+    if(m_scroll_buttons_visible)
+    {
+        if(major_axis == wxHORIZONTAL)
+        {
+            origin_.x -= m_scroll_amount;
+            if(m_scroll_left_btn)
+                origin_.x -= m_scroll_left_btn->GetSize().GetWidth();
+        }
+        else
+        {
+            origin_.y -= m_scroll_amount;
+            if(m_scroll_left_btn)
+                origin_.y -= m_scroll_left_btn->GetSize().GetHeight();
+        }
+    }
+    wxPoint origin(origin_);
+    int gap;
+    int minor_axis_size;
+    if(major_axis == wxHORIZONTAL)
+    {
+        gap = m_art->GetMetric(wxRIBBON_ART_PANEL_X_SEPARATION_SIZE);
+        minor_axis_size = GetSize().GetHeight() - origin.y - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE);
+    }
+    else
+    {
+        gap = m_art->GetMetric(wxRIBBON_ART_PANEL_Y_SEPARATION_SIZE);
+        minor_axis_size = GetSize().GetWidth() - origin.x - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE);
+    }
+
+    for(int iteration = 1; iteration <= 2; ++iteration)
+    {
+        for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+              node;
+              node = node->GetNext() )
+        {
+            wxWindow* child = node->GetData();
+            int w, h;
+            child->GetSize(&w, &h);
+            if(major_axis == wxHORIZONTAL)
+            {
+                child->SetSize(origin.x, origin.y, w, minor_axis_size);
+                origin.x += w + gap;
+            }
+            else
+            {
+                child->SetSize(origin.x, origin.y, minor_axis_size, h);
+                origin.y += h + gap;
+            }
+        }
+        if(iteration == 1)
+        {
+            int available_space;
+            if(major_axis == wxHORIZONTAL)
+                available_space = m_size_in_major_axis_for_children - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE) - origin.x + gap;
+            else
+                available_space = m_size_in_major_axis_for_children - m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE) - origin.y + gap;
+            if(m_scroll_buttons_visible)
+            {
+                available_space -= m_scroll_amount;
+                if(m_scroll_right_btn != NULL)
+                    available_space += GetSizeInOrientation(m_scroll_right_btn->GetSize(), major_axis);
+            }
+            if(available_space > 0)
+            {
+                if(m_scroll_buttons_visible)
+                {
+                    HideScrollButtons();
+                    break;
+                }
+
+                if(!ExpandPanels(major_axis, available_space))
+                    break;
+            }
+            else if(available_space < 0)
+            {
+                if(m_scroll_buttons_visible)
+                {
+                    // Scroll buttons already visible - not going to be able to downsize any more
+                    m_scroll_amount_limit = -available_space;
+                    if(m_scroll_amount > m_scroll_amount_limit)
+                    {
+                        ScrollPixels(m_scroll_amount_limit - m_scroll_amount);
+                    }
+                }
+                else
+                {
+                    if(!CollapsePanels(major_axis, -available_space))
+                    {
+                        m_scroll_amount = 0;
+                        m_scroll_amount_limit = -available_space;
+                        ShowScrollButtons();
+                        break;
+                    }
+                }
+            }
+            else
+            {
+                break;
+            }
+            origin = origin_; // Reset the origin
+        }
+    }
+
+    return true;
+}
+
+bool wxRibbonPage::Show(bool show)
+{
+    if(m_scroll_left_btn)
+        m_scroll_left_btn->Show(show);
+    if(m_scroll_right_btn)
+        m_scroll_right_btn->Show(show);
+    return wxRibbonControl::Show(show);
+}
+
+void wxRibbonPage::HideScrollButtons()
+{
+    m_scroll_amount = 0;
+    m_scroll_amount_limit = 0;
+    ShowScrollButtons();
+}
+
+void wxRibbonPage::ShowScrollButtons()
+{
+    bool show_left = true;
+    bool show_right = true;
+    bool reposition = false;
+    if(m_scroll_amount == 0)
+    {
+        show_left = false;
+    }
+    if(m_scroll_amount >= m_scroll_amount_limit)
+    {
+        show_right = false;
+        m_scroll_amount = m_scroll_amount_limit;
+    }
+    m_scroll_buttons_visible = show_left || show_right;
+
+    if(show_left)
+    {
+        if(m_scroll_left_btn == NULL)
+        {
+            wxMemoryDC temp_dc;
+            wxSize size;
+            long direction;
+            if(GetMajorAxis() == wxHORIZONTAL)
+            {
+                direction = wxRIBBON_SCROLL_BTN_LEFT;
+                size = m_art->GetScrollButtonMinimumSize(temp_dc, GetParent(), direction);
+                size.SetHeight(GetSize().GetHeight());
+            }
+            else
+            {
+                direction = wxRIBBON_SCROLL_BTN_UP;
+                size = m_art->GetScrollButtonMinimumSize(temp_dc, GetParent(), direction);
+                size.SetWidth(GetSize().GetWidth());
+            }
+            m_scroll_left_btn = new wxRibbonPageScrollButton(this, wxID_ANY, GetPosition(), size, direction);
+            if(!IsShown())
+            {
+                m_scroll_left_btn->Hide();
+            }
+            reposition = true;
+        }
+    }
+    else
+    {
+        if(m_scroll_left_btn != NULL)
+        {
+            m_scroll_left_btn->Destroy();
+            m_scroll_left_btn = NULL;
+            reposition = true;
+        }
+    }
+
+    if(show_right)
+    {
+        if(m_scroll_right_btn == NULL)
+        {
+            wxMemoryDC temp_dc;
+            wxSize size;
+            long direction;
+            if(GetMajorAxis() == wxHORIZONTAL)
+            {
+                direction = wxRIBBON_SCROLL_BTN_RIGHT;
+                size = m_art->GetScrollButtonMinimumSize(temp_dc, GetParent(), direction);
+                size.SetHeight(GetSize().GetHeight());
+            }
+            else
+            {
+                direction = wxRIBBON_SCROLL_BTN_DOWN;
+                size = m_art->GetScrollButtonMinimumSize(temp_dc, GetParent(), direction);
+                size.SetWidth(GetSize().GetWidth());
+            }
+            wxPoint initial_pos = GetPosition() + GetSize() - size;
+            m_scroll_right_btn = new wxRibbonPageScrollButton(this, wxID_ANY, initial_pos, size, direction);
+            if(!IsShown())
+            {
+                m_scroll_right_btn->Hide();
+            }
+            reposition = true;
+        }
+    }
+    else
+    {
+        if(m_scroll_right_btn != NULL)
+        {
+            m_scroll_right_btn->Destroy();
+            m_scroll_right_btn = NULL;
+            reposition = true;
+        }
+    }
+
+    if(reposition)
+    {
+        wxDynamicCast(GetParent(), wxRibbonBar)->RepositionPage(this);
+    }
+}
+
+static int GetSizeInOrientation(wxSize size, wxOrientation orientation)
+{
+    switch(orientation)
+    {
+    case wxHORIZONTAL: return size.GetWidth();
+    case wxVERTICAL: return size.GetHeight();
+    case wxBOTH: return size.GetWidth() * size.GetHeight();
+    default: return 0;
+    }
+}
+
+bool wxRibbonPage::ExpandPanels(wxOrientation direction, int maximum_amount)
+{
+    bool expanded_something = false;
+    while(maximum_amount > 0)
+    {
+        int smallest_size = INT_MAX;
+        wxRibbonPanel* smallest_panel = NULL;
+        for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+                  node;
+                  node = node->GetNext() )
+        {
+            wxRibbonPanel* panel = wxDynamicCast(node->GetData(), wxRibbonPanel);
+            if(panel == NULL)
+            {
+                continue;
+            }
+            if(panel->IsSizingContinuous())
+            {
+                int size = GetSizeInOrientation(panel->GetSize(), direction);
+                if(size < smallest_size)
+                {
+                    smallest_size = size;
+                    smallest_panel = panel;
+                }
+            }
+            else
+            {
+                wxSize current = panel->GetSize();
+                int size = GetSizeInOrientation(current, direction);
+                if(size < smallest_size)
+                {
+                    wxSize larger = panel->GetNextLargerSize(direction);
+                    if(larger != current && GetSizeInOrientation(larger, direction) > size)
+                    {
+                        smallest_size = size;
+                        smallest_panel = panel;
+                    }
+                }
+            }
+        }
+        if(smallest_panel != NULL)
+        {
+            if(smallest_panel->IsSizingContinuous())
+            {
+                wxSize size = smallest_panel->GetSize();
+                int amount = maximum_amount;
+                if(amount > 32)
+                {
+                    // For "large" growth, grow this panel a bit, and then re-allocate
+                    // the remainder (which may come to this panel again anyway)
+                    amount = 32;
+                }
+                if(direction & wxHORIZONTAL)
+                {
+                    size.x += amount;
+                }
+                if(direction & wxVERTICAL)
+                {
+                    size.y += amount;
+                }
+                smallest_panel->SetSize(size);
+                maximum_amount -= amount;
+                m_collapse_stack.Add(smallest_panel);
+                expanded_something = true;
+            }
+            else
+            {
+                wxSize current = smallest_panel->GetSize();
+                wxSize larger = smallest_panel->GetNextLargerSize(direction);
+                wxSize delta = larger - current;
+                if(GetSizeInOrientation(delta, direction) <= maximum_amount)
+                {
+                    smallest_panel->SetSize(larger);
+                    maximum_amount -= GetSizeInOrientation(delta, direction);
+                    m_collapse_stack.Add(smallest_panel);
+                    expanded_something = true;
+                }
+                else
+                {
+                    break;
+                }
+            }
+        }
+        else
+        {
+            break;
+        }
+    }
+    if(expanded_something)
+    {
+        Refresh();
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool wxRibbonPage::CollapsePanels(wxOrientation direction, int minimum_amount)
+{
+    bool collapsed_something = false;
+    while(minimum_amount > 0)
+    {
+        int largest_size = 0;
+        wxRibbonPanel* largest_panel = NULL;
+        if(!m_collapse_stack.IsEmpty())
+        {
+            // For a more consistent panel layout, try to collapse panels which
+            // were recently expanded.
+            largest_panel = wxDynamicCast(m_collapse_stack.Last(), wxRibbonPanel);
+            m_collapse_stack.RemoveAt(m_collapse_stack.GetCount() - 1);
+        }
+        else
+        {
+            for(wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+                      node;
+                      node = node->GetNext() )
+            {
+                wxRibbonPanel* panel = wxDynamicCast(node->GetData(), wxRibbonPanel);
+                if(panel == NULL)
+                {
+                    continue;
+                }
+                if(panel->IsSizingContinuous())
+                {
+                    int size = GetSizeInOrientation(panel->GetSize(), direction);
+                    if(size > largest_size)
+                    {
+                        largest_size = size;
+                        largest_panel = panel;
+                    }
+                }
+                else
+                {
+                    wxSize current = panel->GetSize();
+                    int size = GetSizeInOrientation(current, direction);
+                    if(size > largest_size)
+                    {
+                        wxSize smaller = panel->GetNextSmallerSize(direction);
+                        if(smaller != current &&
+                            GetSizeInOrientation(smaller, direction) < size)
+                        {
+                            largest_size = size;
+                            largest_panel = panel;
+                        }
+                    }
+                }
+            }
+        }
+        if(largest_panel != NULL)
+        {
+            if(largest_panel->IsSizingContinuous())
+            {
+                wxSize size = largest_panel->GetSize();
+                int amount = minimum_amount;
+                if(amount > 32)
+                {
+                    // For "large" contraction, reduce this panel a bit, and
+                    // then re-allocate the remainder of the quota (which may
+                    // come to this panel again anyway)
+                    amount = 32;
+                }
+                if(direction & wxHORIZONTAL)
+                {
+                    size.x -= amount;
+                }
+                if(direction & wxVERTICAL)
+                {
+                    size.y -= amount;
+                }
+                largest_panel->SetSize(size);
+                minimum_amount -= amount;
+                collapsed_something = true;
+            }
+            else
+            {
+                wxSize current = largest_panel->GetSize();
+                wxSize smaller = largest_panel->GetNextSmallerSize(direction);
+                wxSize delta = current - smaller;
+                largest_panel->SetSize(smaller);
+                minimum_amount -= GetSizeInOrientation(delta, direction);
+                collapsed_something = true;
+            }
+        }
+        else
+        {
+            break;
+        }
+    }
+    if(collapsed_something)
+    {
+        Refresh();
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool wxRibbonPage::DismissExpandedPanel()
+{
+    for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+              node;
+              node = node->GetNext() )
+    {
+        wxRibbonPanel* panel = wxDynamicCast(node->GetData(), wxRibbonPanel);
+        if(panel == NULL)
+        {
+            continue;
+        }
+        if(panel->GetExpandedPanel() != NULL)
+        {
+            return panel->HideExpanded();
+        }
+    }
+    return false;
+}
+
+wxSize wxRibbonPage::GetMinSize() const
+{
+    wxSize min(wxDefaultCoord, wxDefaultCoord);
+
+    for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+          node;
+          node = node->GetNext() )
+    {
+        wxWindow* child = node->GetData();
+        wxSize child_min(child->GetMinSize());
+
+        min.x = wxMax(min.x, child_min.x);
+        min.y = wxMax(min.y, child_min.y);
+    }
+
+    if(GetMajorAxis() == wxHORIZONTAL)
+    {
+        min.x = wxDefaultCoord;
+        if(min.y != wxDefaultCoord)
+        {
+            min.y += m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_TOP_SIZE) + m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE);
+        }
+    }
+    else
+    {
+        if(min.x != wxDefaultCoord)
+        {
+            min.x += m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_LEFT_SIZE) + m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE);
+        }
+        min.y = wxDefaultCoord;
+    }
+
+    return min;
+}
+
+wxSize wxRibbonPage::DoGetBestSize() const
+{
+    wxSize best(0, 0);
+    size_t count = 0;
+
+    if(GetMajorAxis() == wxHORIZONTAL)
+    {
+        best.y = wxDefaultCoord;
+
+        for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+          node;
+          node = node->GetNext() )
+        {
+            wxWindow* child = node->GetData();
+            wxSize child_best(child->GetBestSize());
+
+            if(child_best.x != wxDefaultCoord)
+            {
+                best.IncBy(child_best.x, 0);
+            }
+            best.y = wxMax(best.y, child_best.y);
+
+            ++count;
+        }
+
+        if(count > 1)
+        {
+            best.IncBy((count - 1) * m_art->GetMetric(wxRIBBON_ART_PANEL_X_SEPARATION_SIZE), 0);
+        }
+    }
+    else
+    {
+        best.x = wxDefaultCoord;
+
+        for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+          node;
+          node = node->GetNext() )
+        {
+            wxWindow* child = node->GetData();
+            wxSize child_best(child->GetBestSize());
+
+            best.x = wxMax(best.x, child_best.x);
+            if(child_best.y != wxDefaultCoord)
+            {
+                best.IncBy(0, child_best.y);
+            }
+
+            ++count;
+        }
+
+        if(count > 1)
+        {
+            best.IncBy(0, (count - 1) * m_art->GetMetric(wxRIBBON_ART_PANEL_Y_SEPARATION_SIZE));
+        }
+    }
+
+    if(best.x != wxDefaultCoord)
+    {
+        best.x += m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_LEFT_SIZE) + m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_RIGHT_SIZE);
+    }
+    if(best.y != wxDefaultCoord)
+    {
+        best.y += m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_TOP_SIZE) + m_art->GetMetric(wxRIBBON_ART_PAGE_BORDER_BOTTOM_SIZE);
+    }
+    return best;
+}
+
+#endif // wxUSE_RIBBON