1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/sizer.cpp
3 // Purpose: provide new wxSizer class for layout
4 // Author: Robert Roebling and Robin Dunn, contributions by
5 // Dirk Holtwick, Ron Lee
6 // Modified by: Ron Lee
9 // Copyright: (c) Robin Dunn, Robert Roebling
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
13 // For compilers that support precompilation, includes "wx.h".
14 #include "wx/wxprec.h"
21 #include "wx/private/flagscheck.h"
24 #include "wx/string.h"
28 #include "wx/settings.h"
29 #include "wx/button.h"
30 #include "wx/statbox.h"
31 #include "wx/toplevel.h"
34 #include "wx/display.h"
35 #include "wx/vector.h"
36 #include "wx/listimpl.cpp"
39 //---------------------------------------------------------------------------
41 IMPLEMENT_CLASS(wxSizerItem
, wxObject
)
42 IMPLEMENT_CLASS(wxSizer
, wxObject
)
43 IMPLEMENT_CLASS(wxGridSizer
, wxSizer
)
44 IMPLEMENT_CLASS(wxFlexGridSizer
, wxGridSizer
)
45 IMPLEMENT_CLASS(wxBoxSizer
, wxSizer
)
47 IMPLEMENT_CLASS(wxStaticBoxSizer
, wxBoxSizer
)
50 IMPLEMENT_CLASS(wxStdDialogButtonSizer
, wxBoxSizer
)
53 WX_DEFINE_EXPORTED_LIST( wxSizerItemList
)
88 // ----------------------------------------------------------------------------
90 // ----------------------------------------------------------------------------
92 // check for flags conflicts
93 static const int SIZER_FLAGS_MASK
=
95 wxADD_FLAG(wxHORIZONTAL
,
96 wxADD_FLAG(wxVERTICAL
,
101 wxADD_FLAG(wxALIGN_NOT
,
102 wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL
,
103 wxADD_FLAG(wxALIGN_RIGHT
,
104 wxADD_FLAG(wxALIGN_BOTTOM
,
105 wxADD_FLAG(wxALIGN_CENTER_VERTICAL
,
106 wxADD_FLAG(wxFIXED_MINSIZE
,
107 wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN
,
108 wxADD_FLAG(wxSTRETCH_NOT
,
114 #define ASSERT_VALID_SIZER_FLAGS(f) wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK)
117 void wxSizerItem::Init(const wxSizerFlags
& flags
)
121 m_proportion
= flags
.GetProportion();
122 m_flag
= flags
.GetFlags();
123 m_border
= flags
.GetBorderInPixels();
125 ASSERT_VALID_SIZER_FLAGS( m_flag
);
128 wxSizerItem::wxSizerItem()
139 void wxSizerItem::DoSetWindow(wxWindow
*window
)
141 wxCHECK_RET( window
, wxT("NULL window in wxSizerItem::SetWindow()") );
143 m_kind
= Item_Window
;
146 // window doesn't become smaller than its initial size, whatever happens
147 m_minSize
= window
->GetSize();
149 if ( m_flag
& wxFIXED_MINSIZE
)
150 window
->SetMinSize(m_minSize
);
152 // aspect ratio calculated from initial size
156 wxSizerItem::wxSizerItem(wxWindow
*window
,
162 m_proportion(proportion
),
168 ASSERT_VALID_SIZER_FLAGS( m_flag
);
174 void wxSizerItem::DoSetSizer(wxSizer
*sizer
)
180 wxSizerItem::wxSizerItem(wxSizer
*sizer
,
187 m_proportion(proportion
),
194 ASSERT_VALID_SIZER_FLAGS( m_flag
);
198 // m_minSize is set later
202 void wxSizerItem::DoSetSpacer(const wxSize
& size
)
204 m_kind
= Item_Spacer
;
205 m_spacer
= new wxSizerSpacer(size
);
210 wxSize
wxSizerItem::AddBorderToSize(const wxSize
& size
) const
212 wxSize result
= size
;
214 // Notice that we shouldn't modify the unspecified component(s) of the
215 // size, it's perfectly valid to have either min or max size specified in
216 // one direction only and it shouldn't be applied in the other one then.
218 if ( result
.x
!= wxDefaultCoord
)
221 result
.x
+= m_border
;
223 result
.x
+= m_border
;
226 if ( result
.y
!= wxDefaultCoord
)
228 if (m_flag
& wxNORTH
)
229 result
.y
+= m_border
;
230 if (m_flag
& wxSOUTH
)
231 result
.y
+= m_border
;
237 wxSizerItem::wxSizerItem(int width
,
245 m_minSize(width
, height
), // minimal size is the initial size
246 m_proportion(proportion
),
252 ASSERT_VALID_SIZER_FLAGS( m_flag
);
254 DoSetSpacer(wxSize(width
, height
));
257 wxSizerItem::~wxSizerItem()
263 void wxSizerItem::Free()
271 m_window
->SetContainingSizer(NULL
);
284 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
290 wxSize
wxSizerItem::GetSpacer() const
293 if ( m_kind
== Item_Spacer
)
294 size
= m_spacer
->GetSize();
300 wxSize
wxSizerItem::GetSize() const
309 ret
= m_window
->GetSize();
313 ret
= m_sizer
->GetSize();
317 ret
= m_spacer
->GetSize();
322 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
329 if (m_flag
& wxNORTH
)
331 if (m_flag
& wxSOUTH
)
337 bool wxSizerItem::InformFirstDirection(int direction
, int size
, int availableOtherDir
)
339 // The size that come here will be including borders. Child items should get it
343 if( direction
==wxHORIZONTAL
)
350 else if( direction
==wxVERTICAL
)
352 if (m_flag
& wxNORTH
)
354 if (m_flag
& wxSOUTH
)
360 // Pass the information along to the held object
363 didUse
= GetSizer()->InformFirstDirection(direction
,size
,availableOtherDir
);
365 m_minSize
= GetSizer()->CalcMin();
369 didUse
= GetWindow()->InformFirstDirection(direction
,size
,availableOtherDir
);
371 m_minSize
= m_window
->GetEffectiveMinSize();
373 // This information is useful for items with wxSHAPED flag, since
374 // we can request an optimal min size for such an item. Even if
375 // we overwrite the m_minSize member here, we can read it back from
376 // the owned window (happens automatically).
377 if( (m_flag
& wxSHAPED
) && (m_flag
& wxEXPAND
) && direction
)
379 if( !wxIsNullDouble(m_ratio
) )
381 wxCHECK_MSG( (m_proportion
==0), false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
382 if( direction
==wxHORIZONTAL
&& !wxIsNullDouble(m_ratio
) )
384 // Clip size so that we don't take too much
385 if( availableOtherDir
>=0 && int(size
/m_ratio
)-m_minSize
.y
>availableOtherDir
)
386 size
= int((availableOtherDir
+m_minSize
.y
)*m_ratio
);
387 m_minSize
= wxSize(size
,int(size
/m_ratio
));
389 else if( direction
==wxVERTICAL
)
391 // Clip size so that we don't take too much
392 if( availableOtherDir
>=0 && int(size
*m_ratio
)-m_minSize
.x
>availableOtherDir
)
393 size
= int((availableOtherDir
+m_minSize
.x
)/m_ratio
);
394 m_minSize
= wxSize(int(size
*m_ratio
),size
);
404 wxSize
wxSizerItem::CalcMin()
408 m_minSize
= m_sizer
->GetMinSize();
410 // if we have to preserve aspect ratio _AND_ this is
411 // the first-time calculation, consider ret to be initial size
412 if ( (m_flag
& wxSHAPED
) && wxIsNullDouble(m_ratio
) )
415 else if ( IsWindow() )
417 // Since the size of the window may change during runtime, we
418 // should use the current minimal/best size.
419 m_minSize
= m_window
->GetEffectiveMinSize();
422 return GetMinSizeWithBorder();
425 wxSize
wxSizerItem::GetMinSizeWithBorder() const
427 return AddBorderToSize(m_minSize
);
430 wxSize
wxSizerItem::GetMaxSizeWithBorder() const
432 return AddBorderToSize(GetMaxSize());
435 void wxSizerItem::SetDimension( const wxPoint
& pos_
, const wxSize
& size_
)
439 if (m_flag
& wxSHAPED
)
441 // adjust aspect ratio
442 int rwidth
= (int) (size
.y
* m_ratio
);
446 int rheight
= (int) (size
.x
/ m_ratio
);
447 // add vertical space
448 if (m_flag
& wxALIGN_CENTER_VERTICAL
)
449 pos
.y
+= (size
.y
- rheight
) / 2;
450 else if (m_flag
& wxALIGN_BOTTOM
)
451 pos
.y
+= (size
.y
- rheight
);
452 // use reduced dimensions
455 else if (rwidth
< size
.x
)
457 // add horizontal space
458 if (m_flag
& wxALIGN_CENTER_HORIZONTAL
)
459 pos
.x
+= (size
.x
- rwidth
) / 2;
460 else if (m_flag
& wxALIGN_RIGHT
)
461 pos
.x
+= (size
.x
- rwidth
);
466 // This is what GetPosition() returns. Since we calculate
467 // borders afterwards, GetPosition() will be the left/top
468 // corner of the surrounding border.
480 if (m_flag
& wxNORTH
)
485 if (m_flag
& wxSOUTH
)
495 m_rect
= wxRect(pos
, size
);
500 wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
505 // Use wxSIZE_FORCE_EVENT here since a sizer item might
506 // have changed alignment or some other property which would
507 // not change the size of the window. In such a case, no
508 // wxSizeEvent would normally be generated and thus the
509 // control wouldn't get laid out correctly here.
511 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
512 wxSIZE_ALLOW_MINUS_ONE
|wxSIZE_FORCE_EVENT
);
514 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
515 wxSIZE_ALLOW_MINUS_ONE
);
520 m_sizer
->SetDimension(pos
, size
);
524 m_spacer
->SetSize(size
);
529 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
533 void wxSizerItem::DeleteWindows()
542 //We are deleting the window from this sizer - normally
543 //the window destroys the sizer associated with it,
544 //which might destroy this, which we don't want
545 m_window
->SetContainingSizer(NULL
);
547 //Putting this after the switch will result in a spacer
548 //not being deleted properly on destruction
553 m_sizer
->DeleteWindows();
558 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
563 void wxSizerItem::Show( bool show
)
568 wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
572 m_window
->Show(show
);
580 m_spacer
->Show(show
);
585 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
589 bool wxSizerItem::IsShown() const
591 if ( m_flag
& wxRESERVE_SPACE_EVEN_IF_HIDDEN
)
597 // we may be called from CalcMin(), just return false so that we're
602 return m_window
->IsShown();
605 // arbitrarily decide that if at least one of our elements is
606 // shown, so are we (this arbitrariness is the reason for
607 // deprecating this function)
608 return m_sizer
->AreAnyItemsShown();
611 return m_spacer
->IsShown();
615 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
621 #if WXWIN_COMPATIBILITY_2_6
622 void wxSizerItem::SetOption( int option
)
624 SetProportion( option
);
627 int wxSizerItem::GetOption() const
629 return GetProportion();
631 #endif // WXWIN_COMPATIBILITY_2_6
634 //---------------------------------------------------------------------------
636 //---------------------------------------------------------------------------
640 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
643 wxSizerItem
* wxSizer::DoInsert( size_t index
, wxSizerItem
*item
)
645 m_children
.Insert( index
, item
);
647 if ( item
->GetWindow() )
648 item
->GetWindow()->SetContainingSizer( this );
650 if ( item
->GetSizer() )
651 item
->GetSizer()->SetContainingWindow( m_containingWindow
);
656 void wxSizer::SetContainingWindow(wxWindow
*win
)
658 if ( win
== m_containingWindow
)
661 m_containingWindow
= win
;
663 // set the same window for all nested sizers as well, they also are in the
665 for ( wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
667 node
= node
->GetNext() )
669 wxSizerItem
*const item
= node
->GetData();
670 wxSizer
*const sizer
= item
->GetSizer();
674 sizer
->SetContainingWindow(win
);
679 #if WXWIN_COMPATIBILITY_2_6
680 bool wxSizer::Remove( wxWindow
*window
)
682 return Detach( window
);
684 #endif // WXWIN_COMPATIBILITY_2_6
686 bool wxSizer::Remove( wxSizer
*sizer
)
688 wxASSERT_MSG( sizer
, wxT("Removing NULL sizer") );
690 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
693 wxSizerItem
*item
= node
->GetData();
695 if (item
->GetSizer() == sizer
)
698 m_children
.Erase( node
);
702 node
= node
->GetNext();
708 bool wxSizer::Remove( int index
)
710 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
712 wxT("Remove index is out of range") );
714 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
716 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
718 delete node
->GetData();
719 m_children
.Erase( node
);
724 bool wxSizer::Detach( wxSizer
*sizer
)
726 wxASSERT_MSG( sizer
, wxT("Detaching NULL sizer") );
728 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
731 wxSizerItem
*item
= node
->GetData();
733 if (item
->GetSizer() == sizer
)
737 m_children
.Erase( node
);
740 node
= node
->GetNext();
746 bool wxSizer::Detach( wxWindow
*window
)
748 wxASSERT_MSG( window
, wxT("Detaching NULL window") );
750 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
753 wxSizerItem
*item
= node
->GetData();
755 if (item
->GetWindow() == window
)
758 m_children
.Erase( node
);
761 node
= node
->GetNext();
767 bool wxSizer::Detach( int index
)
769 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
771 wxT("Detach index is out of range") );
773 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
775 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
777 wxSizerItem
*item
= node
->GetData();
779 if ( item
->IsSizer() )
783 m_children
.Erase( node
);
787 bool wxSizer::Replace( wxWindow
*oldwin
, wxWindow
*newwin
, bool recursive
)
789 wxASSERT_MSG( oldwin
, wxT("Replacing NULL window") );
790 wxASSERT_MSG( newwin
, wxT("Replacing with NULL window") );
792 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
795 wxSizerItem
*item
= node
->GetData();
797 if (item
->GetWindow() == oldwin
)
799 item
->AssignWindow(newwin
);
800 newwin
->SetContainingSizer( this );
803 else if (recursive
&& item
->IsSizer())
805 if (item
->GetSizer()->Replace( oldwin
, newwin
, true ))
809 node
= node
->GetNext();
815 bool wxSizer::Replace( wxSizer
*oldsz
, wxSizer
*newsz
, bool recursive
)
817 wxASSERT_MSG( oldsz
, wxT("Replacing NULL sizer") );
818 wxASSERT_MSG( newsz
, wxT("Replacing with NULL sizer") );
820 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
823 wxSizerItem
*item
= node
->GetData();
825 if (item
->GetSizer() == oldsz
)
827 item
->AssignSizer(newsz
);
830 else if (recursive
&& item
->IsSizer())
832 if (item
->GetSizer()->Replace( oldsz
, newsz
, true ))
836 node
= node
->GetNext();
842 bool wxSizer::Replace( size_t old
, wxSizerItem
*newitem
)
844 wxCHECK_MSG( old
< m_children
.GetCount(), false, wxT("Replace index is out of range") );
845 wxASSERT_MSG( newitem
, wxT("Replacing with NULL item") );
847 wxSizerItemList::compatibility_iterator node
= m_children
.Item( old
);
849 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
851 wxSizerItem
*item
= node
->GetData();
852 node
->SetData(newitem
);
854 if (item
->IsWindow() && item
->GetWindow())
855 item
->GetWindow()->SetContainingSizer(NULL
);
862 void wxSizer::Clear( bool delete_windows
)
864 // First clear the ContainingSizer pointers
865 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
868 wxSizerItem
*item
= node
->GetData();
870 if (item
->IsWindow())
871 item
->GetWindow()->SetContainingSizer( NULL
);
872 node
= node
->GetNext();
875 // Destroy the windows if needed
879 // Now empty the list
880 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
883 void wxSizer::DeleteWindows()
885 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
888 wxSizerItem
*item
= node
->GetData();
890 item
->DeleteWindows();
891 node
= node
->GetNext();
895 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
897 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
899 // take the min size by default and limit it by max size
900 wxSize size
= GetMinClientSize(window
);
903 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
906 // hack for small screen devices where TLWs are always full screen
907 if ( tlw
->IsAlwaysMaximized() )
909 return tlw
->GetClientSize();
912 // limit the window to the size of the display it is on
913 int disp
= wxDisplay::GetFromWindow(window
);
914 if ( disp
== wxNOT_FOUND
)
916 // or, if we don't know which one it is, of the main one
920 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
922 // If determining the display size failed, skip the max size checks as
923 // we really don't want to create windows of (0, 0) size.
924 if ( !sizeMax
.x
|| !sizeMax
.y
)
927 // space for decorations and toolbars etc.
928 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
932 sizeMax
= GetMaxClientSize(window
);
935 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
937 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
943 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
945 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
947 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
950 wxSize
wxSizer::Fit( wxWindow
*window
)
952 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
955 window
->SetClientSize(ComputeFittingClientSize(window
));
957 // return entire size
958 return window
->GetSize();
961 void wxSizer::FitInside( wxWindow
*window
)
964 if (window
->IsTopLevel())
965 size
= VirtualFitSize( window
);
967 size
= GetMinClientSize( window
);
969 window
->SetVirtualSize( size
);
972 void wxSizer::Layout()
974 // (re)calculates minimums needed for each item and other preparations
978 // Applies the layout and repositions/resizes the items
979 wxWindow::ChildrenRepositioningGuard
repositionGuard(m_containingWindow
);
984 void wxSizer::SetSizeHints( wxWindow
*window
)
986 // Preserve the window's max size hints, but set the
987 // lower bound according to the sizer calculations.
989 // This is equivalent to calling Fit(), except that we need to set
990 // the size hints _in between_ the two steps performed by Fit
991 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
992 // otherwise SetClientSize() could have no effect if there already are
993 // size hints in effect that forbid requested client size.
995 const wxSize clientSize
= ComputeFittingClientSize(window
);
997 window
->SetMinClientSize(clientSize
);
998 window
->SetClientSize(clientSize
);
1001 #if WXWIN_COMPATIBILITY_2_8
1002 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
1004 FitInside( window
);
1006 #endif // WXWIN_COMPATIBILITY_2_8
1008 // TODO on mac we need a function that determines how much free space this
1009 // min size contains, in order to make sure that we have 20 pixels of free
1010 // space around the controls
1011 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
1013 return window
->WindowToClientSize(window
->GetMaxSize());
1016 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
1018 return GetMinSize(); // Already returns client size.
1021 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1023 wxSize size
= GetMinClientSize( window
);
1024 wxSize sizeMax
= GetMaxClientSize( window
);
1026 // Limit the size if sizeMax != wxDefaultSize
1028 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1030 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1036 wxSize
wxSizer::GetMinSize()
1038 wxSize
ret( CalcMin() );
1039 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1040 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1044 void wxSizer::DoSetMinSize( int width
, int height
)
1046 m_minSize
.x
= width
;
1047 m_minSize
.y
= height
;
1050 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1052 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1054 // Is it our immediate child?
1056 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1059 wxSizerItem
*item
= node
->GetData();
1061 if (item
->GetWindow() == window
)
1063 item
->SetMinSize( width
, height
);
1066 node
= node
->GetNext();
1069 // No? Search any subsizers we own then
1071 node
= m_children
.GetFirst();
1074 wxSizerItem
*item
= node
->GetData();
1076 if ( item
->GetSizer() &&
1077 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1079 // A child sizer found the requested windw, exit.
1082 node
= node
->GetNext();
1088 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1090 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1092 // Is it our immediate child?
1094 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1097 wxSizerItem
*item
= node
->GetData();
1099 if (item
->GetSizer() == sizer
)
1101 item
->GetSizer()->DoSetMinSize( width
, height
);
1104 node
= node
->GetNext();
1107 // No? Search any subsizers we own then
1109 node
= m_children
.GetFirst();
1112 wxSizerItem
*item
= node
->GetData();
1114 if ( item
->GetSizer() &&
1115 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1117 // A child found the requested sizer, exit.
1120 node
= node
->GetNext();
1126 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1128 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1130 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1132 wxSizerItem
*item
= node
->GetData();
1134 if (item
->GetSizer())
1136 // Sizers contains the minimal size in them, if not calculated ...
1137 item
->GetSizer()->DoSetMinSize( width
, height
);
1141 // ... but the minimal size of spacers and windows is stored via the item
1142 item
->SetMinSize( width
, height
);
1148 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1150 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1152 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1155 wxSizerItem
*item
= node
->GetData();
1157 if (item
->GetWindow() == window
)
1161 else if (recursive
&& item
->IsSizer())
1163 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1168 node
= node
->GetNext();
1174 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1176 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1178 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1181 wxSizerItem
*item
= node
->GetData();
1183 if (item
->GetSizer() == sizer
)
1187 else if (recursive
&& item
->IsSizer())
1189 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1194 node
= node
->GetNext();
1200 wxSizerItem
* wxSizer::GetItem( size_t index
)
1202 wxCHECK_MSG( index
< m_children
.GetCount(),
1204 wxT("GetItem index is out of range") );
1206 return m_children
.Item( index
)->GetData();
1209 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1211 // This gets a sizer item by the id of the sizer item
1212 // and NOT the id of a window if the item is a window.
1214 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1217 wxSizerItem
*item
= node
->GetData();
1219 if (item
->GetId() == id
)
1223 else if (recursive
&& item
->IsSizer())
1225 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1230 node
= node
->GetNext();
1236 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1238 wxSizerItem
*item
= GetItem( window
, recursive
);
1249 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1251 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1262 bool wxSizer::Show( size_t index
, bool show
)
1264 wxSizerItem
*item
= GetItem( index
);
1275 void wxSizer::ShowItems( bool show
)
1277 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1280 node
->GetData()->Show( show
);
1281 node
= node
->GetNext();
1285 bool wxSizer::AreAnyItemsShown() const
1287 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1290 if ( node
->GetData()->IsShown() )
1292 node
= node
->GetNext();
1298 bool wxSizer::IsShown( wxWindow
*window
) const
1300 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1303 wxSizerItem
*item
= node
->GetData();
1305 if (item
->GetWindow() == window
)
1307 return item
->IsShown();
1309 node
= node
->GetNext();
1312 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1317 bool wxSizer::IsShown( wxSizer
*sizer
) const
1319 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1322 wxSizerItem
*item
= node
->GetData();
1324 if (item
->GetSizer() == sizer
)
1326 return item
->IsShown();
1328 node
= node
->GetNext();
1331 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1336 bool wxSizer::IsShown( size_t index
) const
1338 wxCHECK_MSG( index
< m_children
.GetCount(),
1340 wxT("IsShown index is out of range") );
1342 return m_children
.Item( index
)->GetData()->IsShown();
1346 //---------------------------------------------------------------------------
1348 //---------------------------------------------------------------------------
1350 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1351 : m_rows( cols
== 0 ? 1 : 0 ),
1356 wxASSERT(cols
>= 0);
1359 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1360 : m_rows( cols
== 0 ? 1 : 0 ),
1362 m_vgap( gap
.GetHeight() ),
1363 m_hgap( gap
.GetWidth() )
1365 wxASSERT(cols
>= 0);
1368 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1369 : m_rows( rows
|| cols
? rows
: 1 ),
1374 wxASSERT(rows
>= 0 && cols
>= 0);
1377 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1378 : m_rows( rows
|| cols
? rows
: 1 ),
1380 m_vgap( gap
.GetHeight() ),
1381 m_hgap( gap
.GetWidth() )
1383 wxASSERT(rows
>= 0 && cols
>= 0);
1386 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1388 // if only the number of columns or the number of rows is specified for a
1389 // sizer, arbitrarily many items can be added to it but if both of them are
1390 // fixed, then the sizer can't have more than that many items -- check for
1391 // this here to ensure that we detect errors as soon as possible
1392 if ( m_cols
&& m_rows
)
1394 const int nitems
= m_children
.GetCount();
1395 if ( nitems
== m_cols
*m_rows
)
1399 "too many items (%d > %d*%d) in grid sizer (maybe you "
1400 "should omit the number of either rows or columns?)",
1401 nitems
+ 1, m_cols
, m_rows
)
1404 // additionally, continuing to use the specified number of columns
1405 // and rows is not a good idea as callers of CalcRowsCols() expect
1406 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1407 // which is not the case if there are too many items and results in
1408 // crashes, so let it compute the number of rows automatically by
1409 // forgetting the (wrong) number of rows specified (this also has a
1410 // nice side effect of giving only one assert even if there are
1411 // many more items than allowed in this sizer)
1416 return wxSizer::DoInsert(index
, item
);
1419 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1421 const int nitems
= m_children
.GetCount();
1423 ncols
= GetEffectiveColsCount();
1424 nrows
= GetEffectiveRowsCount();
1426 // Since Insert() checks for overpopulation, the following
1427 // should only assert if the grid was shrunk via SetRows() / SetCols()
1428 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1433 void wxGridSizer::RecalcSizes()
1435 int nitems
, nrows
, ncols
;
1436 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1439 wxSize
sz( GetSize() );
1440 wxPoint
pt( GetPosition() );
1442 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1443 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1446 for (int c
= 0; c
< ncols
; c
++)
1449 for (int r
= 0; r
< nrows
; r
++)
1451 int i
= r
* ncols
+ c
;
1454 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1456 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1458 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1466 wxSize
wxGridSizer::CalcMin()
1469 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1472 // Find the max width and height for any component
1476 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1479 wxSizerItem
*item
= node
->GetData();
1480 wxSize
sz( item
->CalcMin() );
1482 w
= wxMax( w
, sz
.x
);
1483 h
= wxMax( h
, sz
.y
);
1485 node
= node
->GetNext();
1488 // In case we have a nested sizer with a two step algo , give it
1489 // a chance to adjust to that (we give it width component)
1490 node
= m_children
.GetFirst();
1491 bool didChangeMinSize
= false;
1494 wxSizerItem
*item
= node
->GetData();
1495 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1497 node
= node
->GetNext();
1500 // And redo iteration in case min size changed
1501 if( didChangeMinSize
)
1503 node
= m_children
.GetFirst();
1507 wxSizerItem
*item
= node
->GetData();
1508 wxSize
sz( item
->GetMinSizeWithBorder() );
1510 w
= wxMax( w
, sz
.x
);
1511 h
= wxMax( h
, sz
.y
);
1513 node
= node
->GetNext();
1517 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1518 nrows
* h
+ (nrows
-1) * m_vgap
);
1521 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1524 wxSize
sz( item
->GetMinSizeWithBorder() );
1525 int flag
= item
->GetFlag();
1527 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1533 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1535 pt
.x
= x
+ (w
- sz
.x
) / 2;
1537 else if (flag
& wxALIGN_RIGHT
)
1539 pt
.x
= x
+ (w
- sz
.x
);
1542 if (flag
& wxALIGN_CENTER_VERTICAL
)
1544 pt
.y
= y
+ (h
- sz
.y
) / 2;
1546 else if (flag
& wxALIGN_BOTTOM
)
1548 pt
.y
= y
+ (h
- sz
.y
);
1552 item
->SetDimension(pt
, sz
);
1555 //---------------------------------------------------------------------------
1557 //---------------------------------------------------------------------------
1559 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1560 : wxGridSizer( cols
, vgap
, hgap
),
1561 m_flexDirection(wxBOTH
),
1562 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1566 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1567 : wxGridSizer( cols
, gap
),
1568 m_flexDirection(wxBOTH
),
1569 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1573 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1574 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1575 m_flexDirection(wxBOTH
),
1576 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1580 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1581 : wxGridSizer( rows
, cols
, gap
),
1582 m_flexDirection(wxBOTH
),
1583 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1587 wxFlexGridSizer::~wxFlexGridSizer()
1591 void wxFlexGridSizer::RecalcSizes()
1594 if ( !CalcRowsCols(nrows
, ncols
) )
1597 const wxPoint
pt(GetPosition());
1598 const wxSize
sz(GetSize());
1600 AdjustForGrowables(sz
);
1602 wxSizerItemList::const_iterator i
= m_children
.begin();
1603 const wxSizerItemList::const_iterator end
= m_children
.end();
1606 for ( int r
= 0; r
< nrows
; r
++ )
1608 if ( m_rowHeights
[r
] == -1 )
1610 // this row is entirely hidden, skip it
1611 for ( int c
= 0; c
< ncols
; c
++ )
1622 const int hrow
= m_rowHeights
[r
];
1623 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1628 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1630 const int wcol
= m_colWidths
[c
];
1635 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1639 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1651 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1652 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1654 // Sum total minimum size, including gaps between rows/columns.
1655 // -1 is used as a magic number meaning empty row/column.
1658 const size_t count
= sizes
.size();
1659 for ( size_t n
= 0; n
< count
; n
++ )
1661 if ( sizes
[n
] != -1 )
1664 total
+= gap
; // separate from the previous column
1673 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1675 // We have to recalculate the sizes in case the item minimum size has
1676 // changed since the previous layout, or the item has been hidden using
1677 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1678 // dimension of the row/column will be -1, indicating that the column
1679 // itself is hidden.
1680 m_rowHeights
.assign(nrows
, -1);
1681 m_colWidths
.assign(ncols
, -1);
1683 // n is the index of the item in left-to-right top-to-bottom order
1685 for ( wxSizerItemList::iterator i
= m_children
.begin();
1686 i
!= m_children
.end();
1689 wxSizerItem
* const item
= *i
;
1690 if ( item
->IsShown() )
1692 // NOTE: Not doing the calculation here, this is just
1693 // for finding max values.
1694 const wxSize
sz(item
->GetMinSizeWithBorder());
1696 const int row
= n
/ ncols
;
1697 const int col
= n
% ncols
;
1699 if ( sz
.y
> m_rowHeights
[row
] )
1700 m_rowHeights
[row
] = sz
.y
;
1701 if ( sz
.x
> m_colWidths
[col
] )
1702 m_colWidths
[col
] = sz
.x
;
1706 AdjustForFlexDirection();
1708 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1709 SumArraySizes(m_rowHeights
, m_vgap
));
1712 wxSize
wxFlexGridSizer::CalcMin()
1717 // Number of rows/columns can change as items are added or removed.
1718 if ( !CalcRowsCols(nrows
, ncols
) )
1722 // We have to recalculate the sizes in case the item minimum size has
1723 // changed since the previous layout, or the item has been hidden using
1724 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1725 // dimension of the row/column will be -1, indicating that the column
1726 // itself is hidden.
1727 m_rowHeights
.assign(nrows
, -1);
1728 m_colWidths
.assign(ncols
, -1);
1730 for ( wxSizerItemList::iterator i
= m_children
.begin();
1731 i
!= m_children
.end();
1734 wxSizerItem
* const item
= *i
;
1735 if ( item
->IsShown() )
1741 // The stage of looking for max values in each row/column has been
1742 // made a separate function, since it's reused in AdjustForGrowables.
1743 FindWidthsAndHeights(nrows
,ncols
);
1745 return m_calculatedMinSize
;
1748 void wxFlexGridSizer::AdjustForFlexDirection()
1750 // the logic in CalcMin works when we resize flexibly in both directions
1751 // but maybe this is not the case
1752 if ( m_flexDirection
!= wxBOTH
)
1754 // select the array corresponding to the direction in which we do *not*
1756 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1759 const size_t count
= array
.GetCount();
1761 // find the largest value in this array
1765 for ( n
= 0; n
< count
; ++n
)
1767 if ( array
[n
] > largest
)
1771 // and now fill it with the largest value
1772 for ( n
= 0; n
< count
; ++n
)
1774 // don't touch hidden rows
1775 if ( array
[n
] != -1 )
1781 // helper of AdjustForGrowables() which is called for rows/columns separately
1784 // delta: the extra space, we do nothing unless it's positive
1785 // growable: indices or growable rows/cols in sizes array
1786 // sizes: the height/widths of rows/cols to adjust
1787 // proportions: proportions of the growable rows/cols or NULL if they all
1788 // should be assumed to have proportion of 1
1790 DoAdjustForGrowables(int delta
,
1791 const wxArrayInt
& growable
,
1793 const wxArrayInt
*proportions
)
1798 // total sum of proportions of all non-hidden rows
1799 int sum_proportions
= 0;
1801 // number of currently shown growable rows
1804 const int max_idx
= sizes
.size();
1806 const size_t count
= growable
.size();
1808 for ( idx
= 0; idx
< count
; idx
++ )
1810 // Since the number of rows/columns can change as items are
1811 // inserted/deleted, we need to verify at runtime that the
1812 // requested growable rows/columns are still valid.
1813 if ( growable
[idx
] >= max_idx
)
1816 // If all items in a row/column are hidden, that row/column will
1817 // have a dimension of -1. This causes the row/column to be
1818 // hidden completely.
1819 if ( sizes
[growable
[idx
]] == -1 )
1823 sum_proportions
+= (*proportions
)[idx
];
1831 // the remaining extra free space, adjusted during each iteration
1832 for ( idx
= 0; idx
< count
; idx
++ )
1834 if ( growable
[idx
] >= max_idx
)
1837 if ( sizes
[ growable
[idx
] ] == -1 )
1841 if ( sum_proportions
== 0 )
1843 // no growable rows -- divide extra space evenly among all
1844 cur_delta
= delta
/num
;
1847 else // allocate extra space proportionally
1849 const int cur_prop
= (*proportions
)[idx
];
1850 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1851 sum_proportions
-= cur_prop
;
1854 sizes
[growable
[idx
]] += cur_delta
;
1859 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1862 // by the time this function is called, the sizer should be already fully
1863 // initialized and hence the number of its columns and rows is known and we
1864 // can check that all indices in m_growableCols/Rows are valid (see also
1865 // comments in AddGrowableCol/Row())
1866 if ( !m_rows
|| !m_cols
)
1870 int nrows
= CalcRows();
1872 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1874 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1875 "invalid growable row index" );
1881 int ncols
= CalcCols();
1883 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1885 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1886 "invalid growable column index" );
1890 #endif // wxDEBUG_LEVEL
1893 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1895 DoAdjustForGrowables
1897 sz
.x
- m_calculatedMinSize
.x
,
1900 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1904 // This gives nested objects that benefit from knowing one size
1905 // component in advance the chance to use that.
1906 bool didAdjustMinSize
= false;
1908 // Iterate over all items and inform about column width
1909 const int ncols
= GetEffectiveColsCount();
1911 for ( wxSizerItemList::iterator i
= m_children
.begin();
1912 i
!= m_children
.end();
1915 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1916 if ( ++col
== ncols
)
1920 // Only redo if info was actually used
1921 if( didAdjustMinSize
)
1923 DoAdjustForGrowables
1925 sz
.x
- m_calculatedMinSize
.x
,
1928 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1934 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1936 // pass NULL instead of proportions if the grow mode is ALL as we
1937 // should treat all rows as having proportion of 1 then
1938 DoAdjustForGrowables
1940 sz
.y
- m_calculatedMinSize
.y
,
1943 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1949 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1951 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1954 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1956 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1959 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1961 wxASSERT_MSG( !IsRowGrowable( idx
),
1962 "AddGrowableRow() called for growable row" );
1964 // notice that we intentionally don't check the index validity here in (the
1965 // common) case when the number of rows was not specified in the ctor -- in
1966 // this case it will be computed only later, when all items are added to
1967 // the sizer, and the check will be done in AdjustForGrowables()
1968 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1970 m_growableRows
.Add( idx
);
1971 m_growableRowsProportions
.Add( proportion
);
1974 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1976 wxASSERT_MSG( !IsColGrowable( idx
),
1977 "AddGrowableCol() called for growable column" );
1979 // see comment in AddGrowableRow(): although it's less common to omit the
1980 // specification of the number of columns, it still can also happen
1981 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1983 m_growableCols
.Add( idx
);
1984 m_growableColsProportions
.Add( proportion
);
1987 // helper function for RemoveGrowableCol/Row()
1989 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1991 const size_t count
= items
.size();
1992 for ( size_t n
= 0; n
< count
; n
++ )
1994 if ( (size_t)items
[n
] == idx
)
1997 proportions
.RemoveAt(n
);
2002 wxFAIL_MSG( wxT("column/row is already not growable") );
2005 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
2007 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
2010 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
2012 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
2015 //---------------------------------------------------------------------------
2017 //---------------------------------------------------------------------------
2019 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
2021 return IsVertical() ? Add(0, size
) : Add(size
, 0);
2028 Helper of RecalcSizes(): checks if there is enough remaining space for the
2029 min size of the given item and returns its min size or the entire remaining
2030 space depending on which one is greater.
2032 This function updates the remaining space parameter to account for the size
2033 effectively allocated to the item.
2036 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2038 int& remainingSpace
= *remainingSpace_
;
2041 if ( remainingSpace
> 0 )
2043 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2044 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2046 if ( size
>= remainingSpace
)
2048 // truncate the item to fit in the remaining space, this is better
2049 // than showing it only partially in general, even if both choices
2050 // are bad -- but there is nothing else we can do
2051 size
= remainingSpace
;
2054 remainingSpace
-= size
;
2056 else // no remaining space
2058 // no space at all left, no need to even query the item for its min
2059 // size as we can't give it to it anyhow
2066 } // anonymous namespace
2068 void wxBoxSizer::RecalcSizes()
2070 if ( m_children
.empty() )
2073 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2074 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2076 // the amount of free space which we should redistribute among the
2077 // stretchable items (i.e. those with non zero proportion)
2078 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2080 // declare loop variables used below:
2081 wxSizerItemList::const_iterator i
; // iterator in m_children list
2082 unsigned n
= 0; // item index in majorSizes array
2085 // First, inform item about the available size in minor direction as this
2086 // can change their size in the major direction. Also compute the number of
2087 // visible items and sum of their min sizes in major direction.
2089 int minMajorSize
= 0;
2090 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2092 wxSizerItem
* const item
= *i
;
2094 if ( !item
->IsShown() )
2097 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2098 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2099 wxSize szMin
= item
->GetMinSizeWithBorder();
2100 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2103 // Since we passed available space along to the item, it should not
2104 // take too much, so delta should not become negative.
2105 delta
-= deltaChange
;
2107 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2110 // update our min size have changed
2111 SizeInMajorDir(m_minSize
) = minMajorSize
;
2114 // space and sum of proportions for the remaining items, both may change
2116 wxCoord remaining
= totalMajorSize
;
2117 int totalProportion
= m_totalProportion
;
2119 // size of the (visible) items in major direction, -1 means "not fixed yet"
2120 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2123 // Check for the degenerated case when we don't have enough space for even
2124 // the min sizes of all the items: in this case we really can't do much
2125 // more than to allocate the min size to as many of fixed size items as
2126 // possible (on the assumption that variable size items such as text zones
2127 // or list boxes may use scrollbars to show their content even if their
2128 // size is less than min size but that fixed size items such as buttons
2129 // will suffer even more if we don't give them their min size)
2130 if ( totalMajorSize
< minMajorSize
)
2132 // Second degenerated case pass: allocate min size to all fixed size
2134 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2136 wxSizerItem
* const item
= *i
;
2138 if ( !item
->IsShown() )
2141 // deal with fixed size items only during this pass
2142 if ( item
->GetProportion() )
2145 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2149 // Third degenerated case pass: allocate min size to all the remaining,
2150 // i.e. non-fixed size, items.
2151 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2153 wxSizerItem
* const item
= *i
;
2155 if ( !item
->IsShown() )
2158 // we've already dealt with fixed size items above
2159 if ( !item
->GetProportion() )
2162 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2165 else // we do have enough space to give at least min sizes to all items
2167 // Second and maybe more passes in the non-degenerated case: deal with
2168 // fixed size items and items whose min size is greater than what we
2169 // would allocate to them taking their proportion into account. For
2170 // both of them, we will just use their min size, but for the latter we
2171 // also need to reexamine all the items as the items which fitted
2172 // before we adjusted their size upwards might not fit any more. This
2173 // does make for a quadratic algorithm but it's not obvious how to
2174 // avoid it and hopefully it's not a huge problem in practice as the
2175 // sizers don't have many items usually (and, of course, the algorithm
2176 // still reduces into a linear one if there is enough space for all the
2178 bool nonFixedSpaceChanged
= false;
2179 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2181 if ( nonFixedSpaceChanged
)
2183 i
= m_children
.begin();
2185 nonFixedSpaceChanged
= false;
2188 // check for the end of the loop only after the check above as
2189 // otherwise we wouldn't do another pass if the last child resulted
2190 // in non fixed space reduction
2191 if ( i
== m_children
.end() )
2194 wxSizerItem
* const item
= *i
;
2196 if ( !item
->IsShown() )
2199 // don't check the item which we had already dealt with during a
2200 // previous pass (this is more than an optimization, the code
2201 // wouldn't work correctly if we kept adjusting for the same item
2202 // over and over again)
2203 if ( majorSizes
[n
] != wxDefaultCoord
)
2206 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2208 // it doesn't make sense for min size to be negative but right now
2209 // it's possible to create e.g. a spacer with (-1, 10) as size and
2210 // people do it in their code apparently (see #11842) so ensure
2211 // that we don't use this -1 as real min size as it conflicts with
2212 // the meaning we use for it here and negative min sizes just don't
2213 // make sense anyhow (which is why it might be a better idea to
2214 // deal with them at wxSizerItem level in the future but for now
2215 // this is the minimal fix for the bug)
2219 const int propItem
= item
->GetProportion();
2222 // is the desired size of this item big enough?
2223 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2225 // yes, it is, we'll determine the real size of this
2226 // item later, for now just leave it as wxDefaultCoord
2230 // the proportion of this item won't count, it has
2231 // effectively become fixed
2232 totalProportion
-= propItem
;
2235 // we can already allocate space for this item
2236 majorSizes
[n
] = minMajor
;
2238 // change the amount of the space remaining to the other items,
2239 // as this can result in not being able to satisfy their
2240 // proportions any more we will need to redo another loop
2242 remaining
-= minMajor
;
2244 nonFixedSpaceChanged
= true;
2247 // Similar to the previous loop, but dealing with items whose max size
2248 // is less than what we would allocate to them taking their proportion
2250 nonFixedSpaceChanged
= false;
2251 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2253 if ( nonFixedSpaceChanged
)
2255 i
= m_children
.begin();
2257 nonFixedSpaceChanged
= false;
2260 // check for the end of the loop only after the check above as
2261 // otherwise we wouldn't do another pass if the last child resulted
2262 // in non fixed space reduction
2263 if ( i
== m_children
.end() )
2266 wxSizerItem
* const item
= *i
;
2268 if ( !item
->IsShown() )
2271 // don't check the item which we had already dealt with during a
2272 // previous pass (this is more than an optimization, the code
2273 // wouldn't work correctly if we kept adjusting for the same item
2274 // over and over again)
2275 if ( majorSizes
[n
] != wxDefaultCoord
)
2278 wxCoord maxMajor
= GetSizeInMajorDir(item
->GetMaxSizeWithBorder());
2280 // must be nonzero, fixed-size items were dealt with in previous loop
2281 const int propItem
= item
->GetProportion();
2283 // is the desired size of this item small enough?
2284 if ( maxMajor
< 0 ||
2285 (remaining
*propItem
)/totalProportion
<= maxMajor
)
2287 // yes, it is, we'll determine the real size of this
2288 // item later, for now just leave it as wxDefaultCoord
2292 // the proportion of this item won't count, it has
2293 // effectively become fixed
2294 totalProportion
-= propItem
;
2296 // we can already allocate space for this item
2297 majorSizes
[n
] = maxMajor
;
2299 // change the amount of the space remaining to the other items,
2300 // as this can result in not being able to satisfy their
2301 // proportions any more we will need to redo another loop
2303 remaining
-= maxMajor
;
2305 nonFixedSpaceChanged
= true;
2308 // Last by one pass: distribute the remaining space among the non-fixed
2309 // items whose size weren't fixed yet according to their proportions.
2310 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2312 wxSizerItem
* const item
= *i
;
2314 if ( !item
->IsShown() )
2317 if ( majorSizes
[n
] == wxDefaultCoord
)
2319 const int propItem
= item
->GetProportion();
2320 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2322 remaining
-= majorSizes
[n
];
2323 totalProportion
-= propItem
;
2329 // the position at which we put the next child
2330 wxPoint
pt(m_position
);
2333 // Final pass: finally do position the items correctly using their sizes as
2334 // determined above.
2335 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2337 wxSizerItem
* const item
= *i
;
2339 if ( !item
->IsShown() )
2342 const int majorSize
= majorSizes
[n
];
2344 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2346 // apply the alignment in the minor direction
2347 wxPoint
posChild(pt
);
2349 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2350 const int flag
= item
->GetFlag();
2351 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2353 // occupy all the available space if wxEXPAND was given and also if
2354 // the item is too big to fit -- in this case we truncate it below
2355 // its minimal size which is bad but better than not showing parts
2356 // of the window at all
2357 minorSize
= totalMinorSize
;
2359 // do not allow the size in the minor direction to grow beyond the max
2360 // size of the item in the minor direction
2361 const wxCoord maxMinorSize
= GetSizeInMinorDir(item
->GetMaxSizeWithBorder());
2362 if ( maxMinorSize
>= 0 && minorSize
> maxMinorSize
)
2363 minorSize
= maxMinorSize
;
2366 if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2368 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2370 // NB: wxCENTRE is used here only for backwards compatibility,
2371 // wxALIGN_CENTRE should be used in new code
2372 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2373 : wxALIGN_CENTRE_VERTICAL
)) )
2375 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2379 // apply RTL adjustment for horizontal sizers:
2380 if ( !IsVertical() && m_containingWindow
)
2382 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2390 // finally set size of this child and advance to the next one
2391 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2393 PosInMajorDir(pt
) += majorSize
;
2397 wxSize
wxBoxSizer::CalcMin()
2399 m_totalProportion
= 0;
2400 m_minSize
= wxSize(0, 0);
2402 // The minimal size for the sizer should be big enough to allocate its
2403 // element at least its minimal size but also, and this is the non trivial
2404 // part, to respect the children proportion. To satisfy the latter
2405 // condition we must find the greatest min-size-to-proportion ratio for all
2406 // elements with non-zero proportion.
2407 float maxMinSizeToProp
= 0.;
2408 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2409 i
!= m_children
.end();
2412 wxSizerItem
* const item
= *i
;
2414 if ( !item
->IsShown() )
2417 const wxSize sizeMinThis
= item
->CalcMin();
2418 if ( const int propThis
= item
->GetProportion() )
2420 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2421 minSizeToProp
/= propThis
;
2423 if ( minSizeToProp
> maxMinSizeToProp
)
2424 maxMinSizeToProp
= minSizeToProp
;
2426 m_totalProportion
+= item
->GetProportion();
2428 else // fixed size item
2430 // Just account for its size directly
2431 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2434 // In the transversal direction we just need to find the maximum.
2435 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2436 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2439 // Using the max ratio ensures that the min size is big enough for all
2440 // items to have their min size and satisfy the proportions among them.
2441 SizeInMajorDir(m_minSize
) += (int)(maxMinSizeToProp
*m_totalProportion
);
2446 //---------------------------------------------------------------------------
2448 //---------------------------------------------------------------------------
2452 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2453 : wxBoxSizer( orient
),
2456 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2458 // do this so that our Detach() is called if the static box is destroyed
2460 m_staticBox
->SetContainingSizer(this);
2463 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2464 : wxBoxSizer(orient
),
2465 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2468 m_staticBox
->SetContainingSizer(this);
2471 wxStaticBoxSizer::~wxStaticBoxSizer()
2476 void wxStaticBoxSizer::RecalcSizes()
2478 int top_border
, other_border
;
2479 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2481 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2483 wxSize
old_size( m_size
);
2484 m_size
.x
-= 2*other_border
;
2485 m_size
.y
-= top_border
+ other_border
;
2487 wxPoint
old_pos( m_position
);
2488 if (m_staticBox
->GetChildren().GetCount() > 0)
2490 #if defined( __WXGTK20__ )
2491 // if the wxStaticBox has created a wxPizza to contain its children
2492 // (see wxStaticBox::AddChild) then we need to place the items it contains
2493 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2494 // to the top-left corner of the staticbox:
2495 m_position
.x
= m_position
.y
= 0;
2496 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
2497 // the distance from the 'inner' content view to the embedded controls
2498 // this is independent of the title, therefore top_border is not relevant
2499 m_position
.x
= m_position
.y
= 10;
2501 // if the wxStaticBox has children, then these windows must be placed
2502 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2503 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2504 // to keep in count the static borders here!):
2505 m_position
.x
= other_border
;
2506 m_position
.y
= top_border
;
2511 // the windows contained in the staticbox have been created as siblings of the
2512 // staticbox (this is the "old" way of staticbox contents creation); in this
2513 // case we need to position them with coordinates relative to our common parent
2514 m_position
.x
+= other_border
;
2515 m_position
.y
+= top_border
;
2518 wxBoxSizer::RecalcSizes();
2520 m_position
= old_pos
;
2524 wxSize
wxStaticBoxSizer::CalcMin()
2526 int top_border
, other_border
;
2527 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2529 wxSize
ret( wxBoxSizer::CalcMin() );
2530 ret
.x
+= 2*other_border
;
2532 // ensure that we're wide enough to show the static box label (there is no
2533 // need to check for the static box best size in vertical direction though)
2534 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2535 if ( ret
.x
< boxWidth
)
2538 ret
.y
+= other_border
+ top_border
;
2543 void wxStaticBoxSizer::ShowItems( bool show
)
2545 m_staticBox
->Show( show
);
2546 wxBoxSizer::ShowItems( show
);
2549 bool wxStaticBoxSizer::AreAnyItemsShown() const
2551 // We don't need to check the status of our child items: if the box is
2552 // shown, this sizer should be considered shown even if all its elements
2553 // are hidden (or, more prosaically, there are no elements at all). And,
2554 // conversely, if the box is hidden then all our items, which are its
2555 // children, are hidden too.
2556 return m_staticBox
->IsShown();
2559 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2561 // avoid deleting m_staticBox in our dtor if it's being detached from the
2562 // sizer (which can happen because it's being already destroyed for
2564 if ( window
== m_staticBox
)
2570 return wxSizer::Detach( window
);
2573 #endif // wxUSE_STATBOX
2575 //---------------------------------------------------------------------------
2576 // wxStdDialogButtonSizer
2577 //---------------------------------------------------------------------------
2581 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2582 : wxBoxSizer(wxHORIZONTAL
)
2584 // Vertical buttons with lots of space on either side
2585 // looks rubbish on WinCE, so let's not do this for now.
2586 // If we are going to use vertical buttons, we should
2587 // put the sizer to the right of other controls in the dialog,
2588 // and that's beyond the scope of this sizer.
2590 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2591 // If we have a PDA screen, put yes/no button over
2592 // all other buttons, otherwise on the left side.
2594 m_orient
= wxVERTICAL
;
2597 m_buttonAffirmative
= NULL
;
2598 m_buttonApply
= NULL
;
2599 m_buttonNegative
= NULL
;
2600 m_buttonCancel
= NULL
;
2601 m_buttonHelp
= NULL
;
2604 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2606 switch (mybutton
->GetId())
2611 m_buttonAffirmative
= mybutton
;
2614 m_buttonApply
= mybutton
;
2617 m_buttonNegative
= mybutton
;
2621 m_buttonCancel
= mybutton
;
2624 case wxID_CONTEXT_HELP
:
2625 m_buttonHelp
= mybutton
;
2632 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2634 m_buttonAffirmative
= button
;
2637 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2639 m_buttonNegative
= button
;
2642 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2644 m_buttonCancel
= button
;
2647 void wxStdDialogButtonSizer::Realize()
2650 Add(0, 0, 0, wxLEFT
, 6);
2652 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2654 if (m_buttonNegative
){
2655 // HIG POLICE BULLETIN - destructive buttons need extra padding
2656 // 24 pixels on either side
2657 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2660 // extra whitespace between help/negative and cancel/ok buttons
2661 Add(0, 0, 1, wxEXPAND
, 0);
2663 if (m_buttonCancel
){
2664 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2665 // Cancel or help should be default
2666 // m_buttonCancel->SetDefaultButton();
2669 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2670 // figure the best place is between Cancel and OK
2672 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2674 if (m_buttonAffirmative
){
2675 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2677 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2678 // these buttons have set labels under Mac so we should use them
2679 m_buttonAffirmative
->SetLabel(_("Save"));
2680 if (m_buttonNegative
)
2681 m_buttonNegative
->SetLabel(_("Don't Save"));
2685 // Extra space around and at the right
2687 #elif defined(__WXGTK20__)
2688 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2689 // says that the correct button order is
2691 // [Help] [Alternative] [Cancel] [Affirmative]
2693 // Flags ensuring that margins between the buttons are 6 pixels.
2695 flagsBtn
= wxSizerFlags().Centre().Border(wxLEFT
| wxRIGHT
, 3);
2697 // Margin around the entire sizer button should be 12.
2701 Add(m_buttonHelp
, flagsBtn
);
2703 // Align the rest of the buttons to the right.
2706 if (m_buttonNegative
)
2707 Add(m_buttonNegative
, flagsBtn
);
2710 Add(m_buttonApply
, flagsBtn
);
2713 Add(m_buttonCancel
, flagsBtn
);
2715 if (m_buttonAffirmative
)
2716 Add(m_buttonAffirmative
, flagsBtn
);
2718 // Ensure that the right margin is 12 as well.
2720 #elif defined(__WXMSW__)
2723 // right-justify buttons
2724 Add(0, 0, 1, wxEXPAND
, 0);
2726 if (m_buttonAffirmative
){
2727 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2730 if (m_buttonNegative
){
2731 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2734 if (m_buttonCancel
){
2735 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2738 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2741 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2743 // GTK+1 and any other platform
2745 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2747 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2749 // extra whitespace between help and cancel/ok buttons
2750 Add(0, 0, 1, wxEXPAND
, 0);
2753 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2755 if (m_buttonAffirmative
){
2756 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2759 if (m_buttonNegative
){
2760 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2763 if (m_buttonCancel
){
2764 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2765 // Cancel or help should be default
2766 // m_buttonCancel->SetDefaultButton();
2772 #endif // wxUSE_BUTTON