]> git.saurik.com Git - wxWidgets.git/blobdiff - src/ribbon/panel.cpp
Misc validity fixes to samples/xrc/rc/*.xrc.
[wxWidgets.git] / src / ribbon / panel.cpp
index bb6257dfcf27cb7d48be03f6f9a04c176d642a02..fd354bf38fcf0f78f422fdeb1b00b45042e36ab2 100644 (file)
@@ -4,7 +4,6 @@
 // Author:      Peter Cawley
 // Modified by:
 // Created:     2009-05-25
-// RCS-ID:      $Id$
 // Copyright:   (C) Peter Cawley
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
@@ -22,6 +21,7 @@
 #include "wx/ribbon/bar.h"
 #include "wx/dcbuffer.h"
 #include "wx/display.h"
+#include "wx/sizer.h"
 
 #ifndef WX_PRECOMP
 #include "wx/frame.h"
 #include "wx/msw/private.h"
 #endif
 
+wxDEFINE_EVENT(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, wxRibbonPanelEvent);
+
+IMPLEMENT_DYNAMIC_CLASS(wxRibbonPanelEvent, wxCommandEvent)
+
 IMPLEMENT_CLASS(wxRibbonPanel, wxRibbonControl)
 
 BEGIN_EVENT_TABLE(wxRibbonPanel, wxRibbonControl)
@@ -38,6 +42,7 @@ BEGIN_EVENT_TABLE(wxRibbonPanel, wxRibbonControl)
     EVT_ERASE_BACKGROUND(wxRibbonPanel::OnEraseBackground)
     EVT_KILL_FOCUS(wxRibbonPanel::OnKillFocus)
     EVT_LEAVE_WINDOW(wxRibbonPanel::OnMouseLeave)
+    EVT_MOTION(wxRibbonPanel::OnMotion)
     EVT_LEFT_DOWN(wxRibbonPanel::OnMouseClick)
     EVT_PAINT(wxRibbonPanel::OnPaint)
     EVT_SIZE(wxRibbonPanel::OnSize)
@@ -110,7 +115,7 @@ void wxRibbonPanel::CommonInit(const wxString& label, const wxBitmap& icon, long
     SetLabel(label);
 
     m_minimised_size = wxDefaultSize; // Unknown / none
-    m_smallest_unminimised_size = wxSize(INT_MAX, INT_MAX); // Unknown / none
+    m_smallest_unminimised_size = wxDefaultSize;// Unknown / none for IsFullySpecified()
     m_preferred_expand_direction = wxSOUTH;
     m_expanded_dummy = NULL;
     m_expanded_panel = NULL;
@@ -118,6 +123,7 @@ void wxRibbonPanel::CommonInit(const wxString& label, const wxBitmap& icon, long
     m_minimised_icon = icon;
     m_minimised = false;
     m_hovered = false;
+    m_ext_button_hovered = false;
 
     if(m_art == NULL)
     {
@@ -143,6 +149,11 @@ bool wxRibbonPanel::IsHovered() const
     return m_hovered;
 }
 
+bool wxRibbonPanel::IsExtButtonHovered() const
+{
+    return m_ext_button_hovered;
+}
+
 void wxRibbonPanel::OnMouseEnter(wxMouseEvent& evt)
 {
     TestPositionForHover(evt.GetPosition());
@@ -177,9 +188,14 @@ void wxRibbonPanel::OnMouseLeaveChild(wxMouseEvent& evt)
     evt.Skip();
 }
 
+void wxRibbonPanel::OnMotion(wxMouseEvent& evt)
+{
+    TestPositionForHover(evt.GetPosition());
+}
+
 void wxRibbonPanel::TestPositionForHover(const wxPoint& pos)
 {
-    bool hovered = false;
+    bool hovered = false, ext_button_hovered = false;
     if(pos.x >= 0 && pos.y >= 0)
     {
         wxSize size = GetSize();
@@ -188,9 +204,17 @@ void wxRibbonPanel::TestPositionForHover(const wxPoint& pos)
             hovered = true;
         }
     }
-    if(hovered != m_hovered)
+    if(hovered)
+    {
+        if(HasExtButton())
+            ext_button_hovered = m_ext_button_rect.Contains(pos);
+        else
+            ext_button_hovered = false;
+    }
+    if(hovered != m_hovered || ext_button_hovered != m_ext_button_hovered)
     {
         m_hovered = hovered;
+        m_ext_button_hovered = ext_button_hovered;
         Refresh(false);
     }
 }
@@ -203,18 +227,27 @@ void wxRibbonPanel::AddChild(wxWindowBase *child)
     // for children of the window. The panel wants to be in the hovered state
     // whenever the mouse cursor is within its boundary, so the events need to
     // be attached to children too.
-    child->Connect(wxEVT_ENTER_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseEnterChild, NULL, this);
-    child->Connect(wxEVT_LEAVE_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseLeaveChild, NULL, this);
+    child->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(wxRibbonPanel::OnMouseEnterChild), NULL, this);
+    child->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(wxRibbonPanel::OnMouseLeaveChild), NULL, this);
 }
 
 void wxRibbonPanel::RemoveChild(wxWindowBase *child)
 {
-    child->Disconnect(wxEVT_ENTER_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseEnterChild, NULL, this);
-    child->Disconnect(wxEVT_LEAVE_WINDOW, (wxObjectEventFunction)&wxRibbonPanel::OnMouseLeaveChild, NULL, this);
+    child->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(wxRibbonPanel::OnMouseEnterChild), NULL, this);
+    child->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(wxRibbonPanel::OnMouseLeaveChild), NULL, this);
 
     wxRibbonControl::RemoveChild(child);
 }
 
+bool wxRibbonPanel::HasExtButton()const
+{
+    wxRibbonBar* bar = GetAncestorRibbonBar();
+    if(bar==NULL)
+        return false;
+    return (m_flags & wxRIBBON_PANEL_EXT_BUTTON) &&
+        (bar->GetWindowStyleFlag() & wxRIBBON_BAR_SHOW_PANEL_EXT_BUTTONS);
+}
+
 void wxRibbonPanel::OnSize(wxSizeEvent& evt)
 {
     if(GetAutoLayout())
@@ -233,11 +266,13 @@ void wxRibbonPanel::DoSetSize(int x, int y, int width, int height, int sizeFlags
     // will refuse to grow any larger while in limbo between minimised and non.
 
     bool minimised = (m_flags & wxRIBBON_PANEL_NO_AUTO_MINIMISE) == 0 &&
-        IsMinimised(wxSize(width, height));
+        IsMinimised(wxSize(width, height)); // check if would be at this size
     if(minimised != m_minimised)
     {
         m_minimised = minimised;
-
+        // Note that for sizers, this routine disallows the use of mixed shown
+        // and hidden controls
+        // TODO ? use some list of user set invisible children to restore status.
         for (wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
                   node;
                   node = node->GetNext())
@@ -247,17 +282,29 @@ void wxRibbonPanel::DoSetSize(int x, int y, int width, int height, int sizeFlags
 
         Refresh();
     }
-    
+
     wxRibbonControl::DoSetSize(x, y, width, height, sizeFlags);
 }
 
+// Checks if panel would be minimised at (client size) at_size
 bool wxRibbonPanel::IsMinimised(wxSize at_size) const
 {
+    if(GetSizer())
+    {
+        // we have no information on size change direction
+        // so check both
+        wxSize size = GetMinNotMinimisedSize();
+        if(size.x > at_size.x || size.y > at_size.y)
+            return true;
+
+        return false;
+    }
+
     if(!m_minimised_size.IsFullySpecified())
         return false;
 
     return (at_size.GetX() <= m_minimised_size.GetX() &&
-        at_size.GetY() <= m_minimised_size.GetY()) || 
+        at_size.GetY() <= m_minimised_size.GetY()) ||
         at_size.GetX() < m_smallest_unminimised_size.GetX() ||
         at_size.GetY() < m_smallest_unminimised_size.GetY();
 }
@@ -288,7 +335,31 @@ bool wxRibbonPanel::IsSizingContinuous() const
 {
     // A panel never sizes continuously, even if all of its children can,
     // as it would appear out of place along side non-continuous panels.
-    return false;
+
+    // JS 2012-03-09: introducing wxRIBBON_PANEL_STRETCH to allow
+    // the panel to fill its parent page. For example we might have
+    // a list of styles in one of the pages, which should stretch to
+    // fill available space.
+    return (m_flags & wxRIBBON_PANEL_STRETCH) != 0;
+}
+
+// Finds the best width and height given the parent's width and height
+wxSize wxRibbonPanel::GetBestSizeForParentSize(const wxSize& parentSize) const
+{
+    if (GetChildren().GetCount() == 1)
+    {
+        wxWindow* win = GetChildren().GetFirst()->GetData();
+        wxRibbonControl* control = wxDynamicCast(win, wxRibbonControl);
+        if (control)
+        {
+            wxClientDC temp_dc((wxRibbonPanel*) this);
+            wxSize clientParentSize = m_art->GetPanelClientSize(temp_dc, this, parentSize, NULL);
+            wxSize childSize = control->GetBestSizeForParentSize(clientParentSize);
+            wxSize overallSize = m_art->GetPanelSize(temp_dc, this, childSize, NULL);
+            return overallSize;
+        }
+    }
+    return GetSize();
 }
 
 wxSize wxRibbonPanel::DoGetNextSmallerSize(wxOrientation direction,
@@ -301,46 +372,70 @@ wxSize wxRibbonPanel::DoGetNextSmallerSize(wxOrientation direction,
         return m_expanded_panel->DoGetNextSmallerSize(direction, relative_to);
     }
 
-    // TODO: Check for, and delegate to, a sizer
-
-    // Simple (and common) case of single ribbon child
-    if(GetChildren().GetCount() == 1)
+    if(m_art != NULL)
     {
-        wxWindow* child = GetChildren().Item(0)->GetData();
-        wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
-        if(m_art != NULL && ribbon_child != NULL)
+        wxClientDC dc((wxRibbonPanel*) this);
+        wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
+        wxSize smaller(-1, -1);
+        bool minimise = false;
+
+        if(GetSizer())
         {
-            wxClientDC dc((wxRibbonPanel*) this);
-            wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
-            wxSize smaller = ribbon_child->GetNextSmallerSize(direction, child_relative);
-            if(smaller == child_relative)
+            // Get smallest non minimised size
+            smaller = GetMinSize();
+            // and adjust to child_relative for parent page
+            if(m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL)
             {
-                if(CanAutoMinimise())
-                {
-                    wxSize minimised = m_minimised_size;
-                    switch(direction)
-                    {
-                    case wxHORIZONTAL:
-                        minimised.SetHeight(relative_to.GetHeight());
-                        break;
-                    case wxVERTICAL:
-                        minimised.SetWidth(relative_to.GetWidth());
-                        break;
-                    default:
-                        break;
-                    }
-                    return minimised;
-                }
-                else
+                 minimise = (child_relative.y <= smaller.y);
+                 if(smaller.x < child_relative.x)
+                    smaller.x = child_relative.x;
+            }
+            else
+            {
+                minimise = (child_relative.x <= smaller.x);
+                if(smaller.y < child_relative.y)
+                    smaller.y = child_relative.y;
+            }
+        }
+        else if(GetChildren().GetCount() == 1)
+        {
+            // Simple (and common) case of single ribbon child or Sizer
+            wxWindow* child = GetChildren().Item(0)->GetData();
+            wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
+            if(ribbon_child != NULL)
+            {
+                smaller = ribbon_child->GetNextSmallerSize(direction, child_relative);
+                minimise = (smaller == child_relative);
+            }
+        }
+
+        if(minimise)
+        {
+            if(CanAutoMinimise())
+            {
+                wxSize minimised = m_minimised_size;
+                switch(direction)
                 {
-                    return relative_to;
+                case wxHORIZONTAL:
+                    minimised.SetHeight(relative_to.GetHeight());
+                    break;
+                case wxVERTICAL:
+                    minimised.SetWidth(relative_to.GetWidth());
+                    break;
+                default:
+                    break;
                 }
+                return minimised;
             }
             else
             {
-                return m_art->GetPanelSize(dc, this, smaller, NULL);
+                return relative_to;
             }
         }
+        else if(smaller.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
+        {
+            return m_art->GetPanelSize(dc, this, smaller, NULL);
+        }
     }
 
     // Fallback: Decrease by 20% (or minimum size, whichever larger)
@@ -398,25 +493,47 @@ wxSize wxRibbonPanel::DoGetNextLargerSize(wxOrientation direction,
         }
     }
 
-    // TODO: Check for, and delegate to, a sizer
-
-    // Simple (and common) case of single ribbon child
-    if(GetChildren().GetCount() == 1)
+    if(m_art != NULL)
     {
-        wxWindow* child = GetChildren().Item(0)->GetData();
-        wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
-        if(ribbon_child != NULL)
+        wxClientDC dc((wxRibbonPanel*) this);
+        wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
+        wxSize larger(-1, -1);
+
+        if(GetSizer())
+        {
+            // We could just let the sizer expand in flow direction but see comment
+            // in IsSizingContinuous()
+            larger = GetPanelSizerBestSize();
+            // and adjust for page in non flow direction
+            if(m_art->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL)
+            {
+                 if(larger.x != child_relative.x)
+                    larger.x = child_relative.x;
+            }
+            else if(larger.y != child_relative.y)
+            {
+                larger.y = child_relative.y;
+            }
+        }
+        else if(GetChildren().GetCount() == 1)
+        {
+            // Simple (and common) case of single ribbon child
+            wxWindow* child = GetChildren().Item(0)->GetData();
+            wxRibbonControl* ribbon_child = wxDynamicCast(child, wxRibbonControl);
+            if(ribbon_child != NULL)
+            {
+                larger = ribbon_child->GetNextLargerSize(direction, child_relative);
+            }
+        }
+
+        if(larger.IsFullySpecified()) // Use fallback if !(sizer/child = 1)
         {
-            wxClientDC dc((wxRibbonPanel*) this);
-            wxSize child_relative = m_art->GetPanelClientSize(dc, this, relative_to, NULL);
-            wxSize larger = ribbon_child->GetNextLargerSize(direction, child_relative);
             if(larger == child_relative)
             {
                 return relative_to;
             }
             else
             {
-                wxClientDC dc((wxRibbonPanel*) this);
                 return m_art->GetPanelSize(dc, this, larger, NULL);
             }
         }
@@ -467,11 +584,15 @@ wxSize wxRibbonPanel::GetMinSize() const
 
 wxSize wxRibbonPanel::GetMinNotMinimisedSize() const
 {
-    // TODO: Ask sizer
-
-    // Common case of no sizer and single child taking up the entire panel
-    if(GetChildren().GetCount() == 1)
+    // Ask sizer if present
+    if(GetSizer())
+    {
+        wxClientDC dc((wxRibbonPanel*) this);
+        return m_art->GetPanelSize(dc, this, GetPanelSizerMinSize(), NULL);
+    }
+    else if(GetChildren().GetCount() == 1)
     {
+        // Common case of single child taking up the entire panel
         wxWindow* child = GetChildren().Item(0)->GetData();
         wxClientDC dc((wxRibbonPanel*) this);
         return m_art->GetPanelSize(dc, this, child->GetMinSize(), NULL);
@@ -480,13 +601,47 @@ wxSize wxRibbonPanel::GetMinNotMinimisedSize() const
     return wxRibbonControl::GetMinSize();
 }
 
-wxSize wxRibbonPanel::DoGetBestSize() const
+wxSize wxRibbonPanel::GetPanelSizerMinSize() const
 {
-    // TODO: Ask sizer
+    // Called from Realize() to set m_smallest_unminimised_size and from other
+    // functions to get the minimum size.
+    // The panel will be invisible when minimised and sizer calcs will be 0
+    // Uses m_smallest_unminimised_size in preference to GetSizer()->CalcMin()
+    // to eliminate flicker.
+
+    // Check if is visible and not previously calculated
+    if(IsShown() && !m_smallest_unminimised_size.IsFullySpecified())
+    {
+         return GetSizer()->CalcMin();
+    }
+    // else use previously calculated m_smallest_unminimised_size
+    wxClientDC dc((wxRibbonPanel*) this);
+    return m_art->GetPanelClientSize(dc,
+                                    this,
+                                    m_smallest_unminimised_size,
+                                    NULL);
+}
 
-    // Common case of no sizer and single child taking up the entire panel
-    if(GetChildren().GetCount() == 1)
+wxSize wxRibbonPanel::GetPanelSizerBestSize() const
+{
+    wxSize size = GetPanelSizerMinSize();
+    // TODO allow panel to increase its size beyond minimum size
+    // by steps similarly to ribbon control panels (preferred for aesthetics)
+    // or continuously.
+    return size;
+}
+
+wxSize wxRibbonPanel::DoGetBestSize() const
+{
+    // Ask sizer if present
+    if( GetSizer())
+    {
+        wxClientDC dc((wxRibbonPanel*) this);
+        return m_art->GetPanelSize(dc, this, GetPanelSizerBestSize(), NULL);
+    }
+    else if(GetChildren().GetCount() == 1)
     {
+        // Common case of no sizer and single child taking up the entire panel
         wxWindow* child = GetChildren().Item(0)->GetData();
         wxClientDC dc((wxRibbonPanel*) this);
         return m_art->GetPanelSize(dc, this, child->GetBestSize(), NULL);
@@ -515,8 +670,13 @@ bool wxRibbonPanel::Realize()
     }
 
     wxSize minimum_children_size(0, 0);
-    // TODO: Ask sizer if there is one
-    if(GetChildren().GetCount() == 1)
+
+    // Ask sizer if there is one present
+    if(GetSizer())
+    {
+        minimum_children_size = GetPanelSizerMinSize();
+    }
+    else if(GetChildren().GetCount() == 1)
     {
         minimum_children_size = GetChildren().GetFirst()->GetData()->GetMinSize();
     }
@@ -577,17 +737,26 @@ bool wxRibbonPanel::Layout()
         return true;
     }
 
-    // TODO: Delegate to a sizer
+    // Get wxRibbonPanel client size
+    wxPoint position;
+    wxClientDC dc(this);
+    wxSize size = m_art->GetPanelClientSize(dc, this, GetSize(), &position);
 
-    // Common case of no sizer and single child taking up the entire panel
-    if(GetChildren().GetCount() == 1)
+    // If there is a sizer, use it
+    if(GetSizer())
     {
+        GetSizer()->SetDimension(position, size); // SetSize and Layout()
+    }
+    else if(GetChildren().GetCount() == 1)
+    {
+        // Common case of no sizer and single child taking up the entire panel
         wxWindow* child = GetChildren().Item(0)->GetData();
-        wxPoint position;
-        wxClientDC dc(this);
-        wxSize size = m_art->GetPanelClientSize(dc, this, GetSize(), &position);
         child->SetSize(position.x, position.y, size.GetWidth(), size.GetHeight());
     }
+
+    if(HasExtButton())
+        m_ext_button_rect = m_art->GetPanelExtButtonArea(dc, this, GetSize());
+
     return true;
 }
 
@@ -604,6 +773,13 @@ void wxRibbonPanel::OnMouseClick(wxMouseEvent& WXUNUSED(evt))
             ShowExpanded();
         }
     }
+    else if(IsExtButtonHovered())
+    {
+        wxRibbonPanelEvent notification(wxEVT_RIBBONPANEL_EXTBUTTON_ACTIVATED, GetId());
+        notification.SetEventObject(this);
+        notification.SetPanel(this);
+        ProcessEvent(notification);
+    }
 }
 
 wxRibbonPanel* wxRibbonPanel::GetExpandedDummy()
@@ -628,6 +804,13 @@ bool wxRibbonPanel::ShowExpanded()
     }
 
     wxSize size = GetBestSize();
+
+    // Special case for flexible panel layout, where GetBestSize doesn't work
+    if (GetFlags() & wxRIBBON_PANEL_FLEXIBLE)
+    {
+        size = GetBestSizeForParentSize(wxSize(400, 1000));
+    }
+
     wxPoint pos = GetExpandedPosition(wxRect(GetScreenPosition(), GetSize()),
         size, m_preferred_expand_direction).GetTopLeft();
 
@@ -636,7 +819,7 @@ bool wxRibbonPanel::ShowExpanded()
         pos, size, wxFRAME_NO_TASKBAR | wxBORDER_NONE);
 
     m_expanded_panel = new wxRibbonPanel(container, wxID_ANY,
-        GetLabel(), m_minimised_icon, wxPoint(0, 0), size, m_flags);
+        GetLabel(), m_minimised_icon, wxPoint(0, 0), size, (m_flags /* & ~wxRIBBON_PANEL_FLEXIBLE */));
 
     m_expanded_panel->SetArtProvider(m_art);
     m_expanded_panel->m_expanded_dummy = this;
@@ -656,10 +839,17 @@ bool wxRibbonPanel::ShowExpanded()
         child->Show();
     }
 
-    // TODO: Move sizer to new panel
+    // Move sizer to new panel
+    if(GetSizer())
+    {
+        wxSizer* sizer = GetSizer();
+        SetSizer(NULL, false);
+        m_expanded_panel->SetSizer(sizer);
+    }
 
     m_expanded_panel->Realize();
     Refresh();
+    container->SetMinClientSize(size);
     container->Show();
     m_expanded_panel->SetFocus();
 
@@ -745,7 +935,7 @@ void wxRibbonPanel::OnChildKillFocus(wxFocusEvent& evt)
         HideExpanded();
         // Do not skip event, as the panel has been de-expanded, causing the
         // child with focus to be reparented (and hidden). If the event
-        // continues propogation then bad things happen.
+        // continues propagation then bad things happen.
     }
     else
     {
@@ -777,7 +967,13 @@ bool wxRibbonPanel::HideExpanded()
         child->Hide();
     }
 
-    // TODO: Move sizer back
+    // Move sizer back
+    if(GetSizer())
+    {
+        wxSizer* sizer = GetSizer();
+        SetSizer(NULL, false);
+        m_expanded_dummy->SetSizer(sizer);
+    }
 
     m_expanded_dummy->m_expanded_panel = NULL;
     m_expanded_dummy->Realize();
@@ -902,4 +1098,9 @@ wxRect wxRibbonPanel::GetExpandedPosition(wxRect panel,
     return best;
 }
 
+void wxRibbonPanel::HideIfExpanded()
+{
+    wxStaticCast(m_parent, wxRibbonPage)->HideIfExpanded();
+}
+
 #endif // wxUSE_RIBBON