1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/ribbon/panel.cpp 
   3 // Purpose:     Ribbon-style container for a group of related tools / controls 
   4 // Author:      Peter Cawley 
   8 // Copyright:   (C) Peter Cawley 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  20 #include "wx/ribbon/panel.h" 
  21 #include "wx/ribbon/art.h" 
  22 #include "wx/ribbon/bar.h" 
  23 #include "wx/dcbuffer.h" 
  24 #include "wx/display.h" 
  32 #include "wx/msw/private.h" 
  35 IMPLEMENT_CLASS(wxRibbonPanel
, wxRibbonControl
) 
  37 BEGIN_EVENT_TABLE(wxRibbonPanel
, wxRibbonControl
) 
  38     EVT_ENTER_WINDOW(wxRibbonPanel::OnMouseEnter
) 
  39     EVT_ERASE_BACKGROUND(wxRibbonPanel::OnEraseBackground
) 
  40     EVT_KILL_FOCUS(wxRibbonPanel::OnKillFocus
) 
  41     EVT_LEAVE_WINDOW(wxRibbonPanel::OnMouseLeave
) 
  42     EVT_LEFT_DOWN(wxRibbonPanel::OnMouseClick
) 
  43     EVT_PAINT(wxRibbonPanel::OnPaint
) 
  44     EVT_SIZE(wxRibbonPanel::OnSize
) 
  47 wxRibbonPanel::wxRibbonPanel() : m_expanded_dummy(NULL
), m_expanded_panel(NULL
) 
  51 wxRibbonPanel::wxRibbonPanel(wxWindow
* parent
, 
  53                   const wxString
& label
, 
  54                   const wxBitmap
& minimised_icon
, 
  58     : wxRibbonControl(parent
, id
, pos
, size
, wxBORDER_NONE
) 
  60     CommonInit(label
, minimised_icon
, style
); 
  63 wxRibbonPanel::~wxRibbonPanel() 
  67         m_expanded_panel
->m_expanded_dummy 
= NULL
; 
  68         m_expanded_panel
->GetParent()->Destroy(); 
  72 bool wxRibbonPanel::Create(wxWindow
* parent
, 
  74                 const wxString
& label
, 
  80     if(!wxRibbonControl::Create(parent
, id
, pos
, size
, wxBORDER_NONE
)) 
  85     CommonInit(label
, icon
, style
); 
  90 void wxRibbonPanel::SetArtProvider(wxRibbonArtProvider
* art
) 
  93     for ( wxWindowList::compatibility_iterator node 
= GetChildren().GetFirst(); 
  95           node 
= node
->GetNext() ) 
  97         wxWindow
* child 
= node
->GetData(); 
  98         wxRibbonControl
* ribbon_child 
= wxDynamicCast(child
, wxRibbonControl
); 
 101             ribbon_child
->SetArtProvider(art
); 
 105         m_expanded_panel
->SetArtProvider(art
); 
 108 void wxRibbonPanel::CommonInit(const wxString
& label
, const wxBitmap
& icon
, long style
) 
 113     m_minimised_size 
= wxDefaultSize
; // Unknown / none 
 114     m_smallest_unminimised_size 
= wxSize(INT_MAX
, INT_MAX
); // Unknown / none 
 115     m_preferred_expand_direction 
= wxSOUTH
; 
 116     m_expanded_dummy 
= NULL
; 
 117     m_expanded_panel 
= NULL
; 
 119     m_minimised_icon 
= icon
; 
 125         wxRibbonControl
* parent 
= wxDynamicCast(GetParent(), wxRibbonControl
); 
 128             m_art 
= parent
->GetArtProvider(); 
 133     SetBackgroundStyle(wxBG_STYLE_CUSTOM
); 
 134     SetMinSize(wxSize(20, 20)); 
 137 bool wxRibbonPanel::IsMinimised() const 
 142 bool wxRibbonPanel::IsHovered() const 
 147 void wxRibbonPanel::OnMouseEnter(wxMouseEvent
& evt
) 
 149     TestPositionForHover(evt
.GetPosition()); 
 152 void wxRibbonPanel::OnMouseEnterChild(wxMouseEvent
& evt
) 
 154     wxPoint pos 
= evt
.GetPosition(); 
 155     wxWindow 
*child 
= wxDynamicCast(evt
.GetEventObject(), wxWindow
); 
 158         pos 
+= child
->GetPosition(); 
 159         TestPositionForHover(pos
); 
 164 void wxRibbonPanel::OnMouseLeave(wxMouseEvent
& evt
) 
 166     TestPositionForHover(evt
.GetPosition()); 
 169 void wxRibbonPanel::OnMouseLeaveChild(wxMouseEvent
& evt
) 
 171     wxPoint pos 
= evt
.GetPosition(); 
 172     wxWindow 
*child 
= wxDynamicCast(evt
.GetEventObject(), wxWindow
); 
 175         pos 
+= child
->GetPosition(); 
 176         TestPositionForHover(pos
); 
 181 void wxRibbonPanel::TestPositionForHover(const wxPoint
& pos
) 
 183     bool hovered 
= false; 
 184     if(pos
.x 
>= 0 && pos
.y 
>= 0) 
 186         wxSize size 
= GetSize(); 
 187         if(pos
.x 
< size
.GetWidth() && pos
.y 
< size
.GetHeight()) 
 192     if(hovered 
!= m_hovered
) 
 199 void wxRibbonPanel::AddChild(wxWindowBase 
*child
) 
 201     wxRibbonControl::AddChild(child
); 
 203     // Window enter / leave events count for only the window in question, not 
 204     // for children of the window. The panel wants to be in the hovered state 
 205     // whenever the mouse cursor is within its boundary, so the events need to 
 206     // be attached to children too. 
 207     child
->Connect(wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseEnterChild
, NULL
, this); 
 208     child
->Connect(wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseLeaveChild
, NULL
, this); 
 211 void wxRibbonPanel::RemoveChild(wxWindowBase 
*child
) 
 213     child
->Disconnect(wxEVT_ENTER_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseEnterChild
, NULL
, this); 
 214     child
->Disconnect(wxEVT_LEAVE_WINDOW
, (wxObjectEventFunction
)&wxRibbonPanel::OnMouseLeaveChild
, NULL
, this); 
 216     wxRibbonControl::RemoveChild(child
); 
 219 void wxRibbonPanel::OnSize(wxSizeEvent
& evt
) 
 227 void wxRibbonPanel::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
) 
 229     // At least on MSW, changing the size of a window will cause GetSize() to 
 230     // report the new size, but a size event may not be handled immediately. 
 231     // If this minimised check was performed in the OnSize handler, then 
 232     // GetSize() could return a size much larger than the minimised size while 
 233     // IsMinimised() returns true. This would then affect layout, as the panel 
 234     // will refuse to grow any larger while in limbo between minimised and non. 
 236     bool minimised 
= (m_flags 
& wxRIBBON_PANEL_NO_AUTO_MINIMISE
) == 0 && 
 237         IsMinimised(wxSize(width
, height
)); 
 238     if(minimised 
!= m_minimised
) 
 240         m_minimised 
= minimised
; 
 242         for (wxWindowList::compatibility_iterator node 
= GetChildren().GetFirst(); 
 244                   node 
= node
->GetNext()) 
 246             node
->GetData()->Show(!minimised
); 
 252     wxRibbonControl::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
 255 bool wxRibbonPanel::IsMinimised(wxSize at_size
) const 
 257     if(!m_minimised_size
.IsFullySpecified()) 
 260     return (at_size
.GetX() <= m_minimised_size
.GetX() && 
 261         at_size
.GetY() <= m_minimised_size
.GetY()) ||  
 262         at_size
.GetX() < m_smallest_unminimised_size
.GetX() || 
 263         at_size
.GetY() < m_smallest_unminimised_size
.GetY(); 
 266 void wxRibbonPanel::OnEraseBackground(wxEraseEvent
& WXUNUSED(evt
)) 
 268     // All painting done in main paint handler to minimise flicker 
 271 void wxRibbonPanel::OnPaint(wxPaintEvent
& WXUNUSED(evt
)) 
 273     wxAutoBufferedPaintDC 
dc(this); 
 279             m_art
->DrawMinimisedPanel(dc
, this, GetSize(), m_minimised_icon_resized
); 
 283             m_art
->DrawPanelBackground(dc
, this, GetSize()); 
 288 bool wxRibbonPanel::IsSizingContinuous() const 
 290     // A panel never sizes continuously, even if all of its children can, 
 291     // as it would appear out of place along side non-continuous panels. 
 295 wxSize 
wxRibbonPanel::DoGetNextSmallerSize(wxOrientation direction
, 
 296                                          wxSize relative_to
) const 
 298     if(m_expanded_panel 
!= NULL
) 
 300         // Next size depends upon children, who are currently in the 
 302         return m_expanded_panel
->DoGetNextSmallerSize(direction
, relative_to
); 
 305     // TODO: Check for, and delegate to, a sizer 
 307     // Simple (and common) case of single ribbon child 
 308     if(GetChildren().GetCount() == 1) 
 310         wxWindow
* child 
= GetChildren().Item(0)->GetData(); 
 311         wxRibbonControl
* ribbon_child 
= wxDynamicCast(child
, wxRibbonControl
); 
 312         if(m_art 
!= NULL 
&& ribbon_child 
!= NULL
) 
 314             wxClientDC 
dc((wxRibbonPanel
*) this); 
 315             wxSize child_relative 
= m_art
->GetPanelClientSize(dc
, this, relative_to
, NULL
); 
 316             wxSize smaller 
= ribbon_child
->GetNextSmallerSize(direction
, child_relative
); 
 317             if(smaller 
== child_relative
) 
 319                 if(CanAutoMinimise()) 
 321                     wxSize minimised 
= m_minimised_size
; 
 325                         minimised
.SetHeight(relative_to
.GetHeight()); 
 328                         minimised
.SetWidth(relative_to
.GetWidth()); 
 342                 return m_art
->GetPanelSize(dc
, this, smaller
, NULL
); 
 347     // Fallback: Decrease by 20% (or minimum size, whichever larger) 
 348     wxSize 
current(relative_to
); 
 349     wxSize 
minimum(GetMinSize()); 
 350     if(direction 
& wxHORIZONTAL
) 
 352         current
.x 
= (current
.x 
* 4) / 5; 
 353         if(current
.x 
< minimum
.x
) 
 355             current
.x 
= minimum
.x
; 
 358     if(direction 
& wxVERTICAL
) 
 360         current
.y 
= (current
.y 
* 4) / 5; 
 361         if(current
.y 
< minimum
.y
) 
 363             current
.y 
= minimum
.y
; 
 369 wxSize 
wxRibbonPanel::DoGetNextLargerSize(wxOrientation direction
, 
 370                                         wxSize relative_to
) const 
 372     if(m_expanded_panel 
!= NULL
) 
 374         // Next size depends upon children, who are currently in the 
 376         return m_expanded_panel
->DoGetNextLargerSize(direction
, relative_to
); 
 379     if(IsMinimised(relative_to
)) 
 381         wxSize current 
= relative_to
; 
 382         wxSize min_size 
= GetMinNotMinimisedSize(); 
 386             if(min_size
.x 
> current
.x 
&& min_size
.y 
== current
.y
) 
 390             if(min_size
.x 
== current
.x 
&& min_size
.y 
> current
.y
) 
 394             if(min_size
.x 
> current
.x 
&& min_size
.y 
> current
.y
) 
 402     // TODO: Check for, and delegate to, a sizer 
 404     // Simple (and common) case of single ribbon child 
 405     if(GetChildren().GetCount() == 1) 
 407         wxWindow
* child 
= GetChildren().Item(0)->GetData(); 
 408         wxRibbonControl
* ribbon_child 
= wxDynamicCast(child
, wxRibbonControl
); 
 409         if(ribbon_child 
!= NULL
) 
 411             wxClientDC 
dc((wxRibbonPanel
*) this); 
 412             wxSize child_relative 
= m_art
->GetPanelClientSize(dc
, this, relative_to
, NULL
); 
 413             wxSize larger 
= ribbon_child
->GetNextLargerSize(direction
, child_relative
); 
 414             if(larger 
== child_relative
) 
 420                 wxClientDC 
dc((wxRibbonPanel
*) this); 
 421                 return m_art
->GetPanelSize(dc
, this, larger
, NULL
); 
 426     // Fallback: Increase by 25% (equal to a prior or subsequent 20% decrease) 
 427     // Note that due to rounding errors, this increase may not exactly equal a 
 428     // matching decrease - an ideal solution would not have these errors, but 
 429     // avoiding them is non-trivial unless an increase is by 100% rather than 
 430     // a fractional amount. This would then be non-ideal as the resizes happen 
 431     // at very large intervals. 
 432     wxSize 
current(relative_to
); 
 433     if(direction 
& wxHORIZONTAL
) 
 435         current
.x 
= (current
.x 
* 5 + 3) / 4; 
 437     if(direction 
& wxVERTICAL
) 
 439         current
.y 
= (current
.y 
* 5 + 3) / 4; 
 444 bool wxRibbonPanel::CanAutoMinimise() const 
 446     return (m_flags 
& wxRIBBON_PANEL_NO_AUTO_MINIMISE
) == 0 
 447         && m_minimised_size
.IsFullySpecified(); 
 450 wxSize 
wxRibbonPanel::GetMinSize() const 
 452     if(m_expanded_panel 
!= NULL
) 
 454         // Minimum size depends upon children, who are currently in the 
 456         return m_expanded_panel
->GetMinSize(); 
 459     if(CanAutoMinimise()) 
 461         return m_minimised_size
; 
 465         return GetMinNotMinimisedSize(); 
 469 wxSize 
wxRibbonPanel::GetMinNotMinimisedSize() const 
 473     // Common case of no sizer and single child taking up the entire panel 
 474     if(GetChildren().GetCount() == 1) 
 476         wxWindow
* child 
= GetChildren().Item(0)->GetData(); 
 477         wxClientDC 
dc((wxRibbonPanel
*) this); 
 478         return m_art
->GetPanelSize(dc
, this, child
->GetMinSize(), NULL
); 
 481     return wxRibbonControl::GetMinSize(); 
 484 wxSize 
wxRibbonPanel::DoGetBestSize() const 
 488     // Common case of no sizer and single child taking up the entire panel 
 489     if(GetChildren().GetCount() == 1) 
 491         wxWindow
* child 
= GetChildren().Item(0)->GetData(); 
 492         wxClientDC 
dc((wxRibbonPanel
*) this); 
 493         return m_art
->GetPanelSize(dc
, this, child
->GetBestSize(), NULL
); 
 496     return wxRibbonControl::DoGetBestSize(); 
 499 bool wxRibbonPanel::Realize() 
 503     for (wxWindowList::compatibility_iterator node 
= GetChildren().GetFirst(); 
 505                   node 
= node
->GetNext()) 
 507         wxRibbonControl
* child 
= wxDynamicCast(node
->GetData(), wxRibbonControl
); 
 512         if(!child
->Realize()) 
 518     wxSize 
minimum_children_size(0, 0); 
 519     // TODO: Ask sizer if there is one 
 520     if(GetChildren().GetCount() == 1) 
 522         minimum_children_size 
= GetChildren().GetFirst()->GetData()->GetMinSize(); 
 527         wxClientDC 
temp_dc(this); 
 529         m_smallest_unminimised_size 
= 
 530             m_art
->GetPanelSize(temp_dc
, this, minimum_children_size
, NULL
); 
 533         wxSize panel_min_size 
= GetMinNotMinimisedSize(); 
 534         m_minimised_size 
= m_art
->GetMinimisedPanelMinimumSize(temp_dc
, this, 
 535             &bitmap_size
, &m_preferred_expand_direction
); 
 536         if(m_minimised_icon
.IsOk() && m_minimised_icon
.GetSize() != bitmap_size
) 
 538             wxImage 
img(m_minimised_icon
.ConvertToImage()); 
 539             img
.Rescale(bitmap_size
.GetWidth(), bitmap_size
.GetHeight(), wxIMAGE_QUALITY_HIGH
); 
 540             m_minimised_icon_resized 
= wxBitmap(img
); 
 544             m_minimised_icon_resized 
= m_minimised_icon
; 
 546         if(m_minimised_size
.x 
> panel_min_size
.x 
&& 
 547             m_minimised_size
.y 
> panel_min_size
.y
) 
 549             // No point in having a minimised size which is larger than the 
 550             // minimum size which the children can go to. 
 551             m_minimised_size 
= wxSize(-1, -1); 
 555             if(m_art
->GetFlags() & wxRIBBON_BAR_FLOW_VERTICAL
) 
 557                 m_minimised_size
.x 
= panel_min_size
.x
; 
 561                 m_minimised_size
.y 
= panel_min_size
.y
; 
 567         m_minimised_size 
= wxSize(-1, -1); 
 570     return Layout() && status
; 
 573 bool wxRibbonPanel::Layout() 
 577         // Children are all invisible when minimised 
 581     // Get wxRibbonPanel client size 
 584     wxSize size 
= m_art
->GetPanelClientSize(dc
, this, GetSize(), &position
); 
 586     // If there is a sizer, use it instead 
 589         GetSizer()->SetDimension(position
.x
, position
.y
, size
.GetWidth(), size
.GetHeight()); 
 591     else if(GetChildren().GetCount() == 1) 
 593         // Common case of no sizer and single child taking up the entire panel 
 594         wxWindow
* child 
= GetChildren().Item(0)->GetData(); 
 595         child
->SetSize(position
.x
, position
.y
, size
.GetWidth(), size
.GetHeight()); 
 600 void wxRibbonPanel::OnMouseClick(wxMouseEvent
& WXUNUSED(evt
)) 
 604         if(m_expanded_panel 
!= NULL
) 
 615 wxRibbonPanel
* wxRibbonPanel::GetExpandedDummy() 
 617     return m_expanded_dummy
; 
 620 wxRibbonPanel
* wxRibbonPanel::GetExpandedPanel() 
 622     return m_expanded_panel
; 
 625 bool wxRibbonPanel::ShowExpanded() 
 631     if(m_expanded_dummy 
!= NULL 
|| m_expanded_panel 
!= NULL
) 
 636     wxSize size 
= GetBestSize(); 
 637     wxPoint pos 
= GetExpandedPosition(wxRect(GetScreenPosition(), GetSize()), 
 638         size
, m_preferred_expand_direction
).GetTopLeft(); 
 640     // Need a top-level frame to contain the expanded panel 
 641     wxFrame 
*container 
= new wxFrame(NULL
, wxID_ANY
, GetLabel(), 
 642         pos
, size
, wxFRAME_NO_TASKBAR 
| wxBORDER_NONE
); 
 644     m_expanded_panel 
= new wxRibbonPanel(container
, wxID_ANY
, 
 645         GetLabel(), m_minimised_icon
, wxPoint(0, 0), size
, m_flags
); 
 647     m_expanded_panel
->SetArtProvider(m_art
); 
 648     m_expanded_panel
->m_expanded_dummy 
= this; 
 650     // Move all children to the new panel. 
 651     // Conceptually it might be simpler to reparent this entire panel to the 
 652     // container and create a new panel to sit in its place while expanded. 
 653     // This approach has a problem though - when the panel is reinserted into 
 654     // its original parent, it'll be at a different position in the child list 
 655     // and thus assume a new position. 
 656     // NB: Children iterators not used as behaviour is not well defined 
 657     // when iterating over a container which is being emptied 
 658     while(!GetChildren().IsEmpty()) 
 660         wxWindow 
*child 
= GetChildren().GetFirst()->GetData(); 
 661         child
->Reparent(m_expanded_panel
); 
 665     // TODO: Move sizer to new panel 
 667     m_expanded_panel
->Realize(); 
 670     m_expanded_panel
->SetFocus(); 
 675 bool wxRibbonPanel::ShouldSendEventToDummy(wxEvent
& evt
) 
 677     // For an expanded panel, filter events between being sent up to the 
 678     // floating top level window or to the dummy panel sitting in the ribbon 
 681     // Child focus events should not be redirected, as the child would not be a 
 682     // child of the window the event is redirected to. All other command events 
 683     // seem to be suitable for redirecting. 
 684     return evt
.IsCommandEvent() && evt
.GetEventType() != wxEVT_CHILD_FOCUS
; 
 687 bool wxRibbonPanel::TryAfter(wxEvent
& evt
) 
 689     if(m_expanded_dummy 
&& ShouldSendEventToDummy(evt
)) 
 691         wxPropagateOnce 
propagateOnce(evt
); 
 692         return m_expanded_dummy
->GetEventHandler()->ProcessEvent(evt
); 
 696         return wxRibbonControl::TryAfter(evt
); 
 700 static bool IsAncestorOf(wxWindow 
*ancestor
, wxWindow 
*window
) 
 702     while(window 
!= NULL
) 
 704         wxWindow 
*parent 
= window
->GetParent(); 
 705         if(parent 
== ancestor
) 
 713 void wxRibbonPanel::OnKillFocus(wxFocusEvent
& evt
) 
 717         wxWindow 
*receiver 
= evt
.GetWindow(); 
 718         if(IsAncestorOf(this, receiver
)) 
 720             m_child_with_focus 
= receiver
; 
 721             receiver
->Connect(wxEVT_KILL_FOCUS
, 
 722                 wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), 
 725         else if(receiver 
== NULL 
|| receiver 
!= m_expanded_dummy
) 
 732 void wxRibbonPanel::OnChildKillFocus(wxFocusEvent
& evt
) 
 734     if(m_child_with_focus 
== NULL
) 
 735         return; // Should never happen, but a check can't hurt 
 737     m_child_with_focus
->Disconnect(wxEVT_KILL_FOCUS
, 
 738       wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), NULL
, this); 
 739     m_child_with_focus 
= NULL
; 
 741     wxWindow 
*receiver 
= evt
.GetWindow(); 
 742     if(receiver 
== this || IsAncestorOf(this, receiver
)) 
 744         m_child_with_focus 
= receiver
; 
 745         receiver
->Connect(wxEVT_KILL_FOCUS
, 
 746             wxFocusEventHandler(wxRibbonPanel::OnChildKillFocus
), NULL
, this); 
 749     else if(receiver 
== NULL 
|| receiver 
!= m_expanded_dummy
) 
 752         // Do not skip event, as the panel has been de-expanded, causing the 
 753         // child with focus to be reparented (and hidden). If the event 
 754         // continues propogation then bad things happen. 
 762 bool wxRibbonPanel::HideExpanded() 
 764     if(m_expanded_dummy 
== NULL
) 
 768             return m_expanded_panel
->HideExpanded(); 
 776     // Move children back to original panel 
 777     // NB: Children iterators not used as behaviour is not well defined 
 778     // when iterating over a container which is being emptied 
 779     while(!GetChildren().IsEmpty()) 
 781         wxWindow 
*child 
= GetChildren().GetFirst()->GetData(); 
 782         child
->Reparent(m_expanded_dummy
); 
 786     // TODO: Move sizer back 
 788     m_expanded_dummy
->m_expanded_panel 
= NULL
; 
 789     m_expanded_dummy
->Realize(); 
 790     m_expanded_dummy
->Refresh(); 
 791     wxWindow 
*parent 
= GetParent(); 
 798 wxRect 
wxRibbonPanel::GetExpandedPosition(wxRect panel
, 
 799                                           wxSize expanded_size
, 
 800                                           wxDirection direction
) 
 803     // 1) Determine primary position based on requested direction 
 804     // 2) Move the position so that it sits entirely within a display 
 805     //    (for single monitor systems, this moves it into the display region, 
 806     //     but for multiple monitors, it does so without splitting it over 
 807     //     more than one display) 
 808     // 2.1) Move in the primary axis 
 809     // 2.2) Move in the secondary axis 
 812     bool primary_x 
= false; 
 818         pos
.x 
= panel
.GetX() + (panel
.GetWidth() - expanded_size
.GetWidth()) / 2; 
 819         pos
.y 
= panel
.GetY() - expanded_size
.GetHeight(); 
 824         pos
.x 
= panel
.GetRight(); 
 825         pos
.y 
= panel
.GetY() + (panel
.GetHeight() - expanded_size
.GetHeight()) / 2; 
 829         pos
.x 
= panel
.GetX() + (panel
.GetWidth() - expanded_size
.GetWidth()) / 2; 
 830         pos
.y 
= panel
.GetBottom(); 
 836         pos
.x 
= panel
.GetX() - expanded_size
.GetWidth(); 
 837         pos
.y 
= panel
.GetY() + (panel
.GetHeight() - expanded_size
.GetHeight()) / 2; 
 841     wxRect 
expanded(pos
, expanded_size
); 
 843     wxRect 
best(expanded
); 
 844     int best_distance 
= INT_MAX
; 
 846     const unsigned display_n 
= wxDisplay::GetCount(); 
 848     for(display_i 
= 0; display_i 
< display_n
; ++display_i
) 
 850         wxRect display 
= wxDisplay(display_i
).GetGeometry(); 
 852         if(display
.Contains(expanded
)) 
 856         else if(display
.Intersects(expanded
)) 
 858             wxRect 
new_rect(expanded
); 
 863                 if(expanded
.GetRight() > display
.GetRight()) 
 865                     distance 
= expanded
.GetRight() - display
.GetRight(); 
 866                     new_rect
.x 
-= distance
; 
 868                 else if(expanded
.GetLeft() < display
.GetLeft()) 
 870                     distance 
= display
.GetLeft() - expanded
.GetLeft(); 
 871                     new_rect
.x 
+= distance
; 
 876                 if(expanded
.GetBottom() > display
.GetBottom()) 
 878                     distance 
= expanded
.GetBottom() - display
.GetBottom(); 
 879                     new_rect
.y 
-= distance
; 
 881                 else if(expanded
.GetTop() < display
.GetTop()) 
 883                     distance 
= display
.GetTop() - expanded
.GetTop(); 
 884                     new_rect
.y 
+= distance
; 
 887             if(!display
.Contains(new_rect
)) 
 889                 // Tried moving in primary axis, but failed. 
 890                 // Hence try moving in the secondary axis. 
 891                 int dx 
= secondary_x 
* (panel
.GetWidth() + expanded_size
.GetWidth()); 
 892                 int dy 
= secondary_y 
* (panel
.GetHeight() + expanded_size
.GetHeight()); 
 896                 // Squaring makes secondary moves more expensive (and also 
 897                 // prevents a negative cost) 
 898                 distance 
+= dx 
* dx 
+ dy 
* dy
; 
 900             if(display
.Contains(new_rect
) && distance 
< best_distance
) 
 903                 best_distance 
= distance
; 
 911 #endif // wxUSE_RIBBON