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();
606 // arbitrarily decide that if at least one of our elements is
607 // shown, so are we (this arbitrariness is the reason for
608 // deprecating this function)
609 for ( wxSizerItemList::compatibility_iterator
610 node
= m_sizer
->GetChildren().GetFirst();
612 node
= node
->GetNext() )
614 if ( node
->GetData()->IsShown() )
621 return m_spacer
->IsShown();
625 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
631 #if WXWIN_COMPATIBILITY_2_6
632 void wxSizerItem::SetOption( int option
)
634 SetProportion( option
);
637 int wxSizerItem::GetOption() const
639 return GetProportion();
641 #endif // WXWIN_COMPATIBILITY_2_6
644 //---------------------------------------------------------------------------
646 //---------------------------------------------------------------------------
650 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
653 wxSizerItem
* wxSizer::DoInsert( size_t index
, wxSizerItem
*item
)
655 m_children
.Insert( index
, item
);
657 if ( item
->GetWindow() )
658 item
->GetWindow()->SetContainingSizer( this );
660 if ( item
->GetSizer() )
661 item
->GetSizer()->SetContainingWindow( m_containingWindow
);
666 void wxSizer::SetContainingWindow(wxWindow
*win
)
668 if ( win
== m_containingWindow
)
671 m_containingWindow
= win
;
673 // set the same window for all nested sizers as well, they also are in the
675 for ( wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
677 node
= node
->GetNext() )
679 wxSizerItem
*const item
= node
->GetData();
680 wxSizer
*const sizer
= item
->GetSizer();
684 sizer
->SetContainingWindow(win
);
689 #if WXWIN_COMPATIBILITY_2_6
690 bool wxSizer::Remove( wxWindow
*window
)
692 return Detach( window
);
694 #endif // WXWIN_COMPATIBILITY_2_6
696 bool wxSizer::Remove( wxSizer
*sizer
)
698 wxASSERT_MSG( sizer
, wxT("Removing NULL sizer") );
700 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
703 wxSizerItem
*item
= node
->GetData();
705 if (item
->GetSizer() == sizer
)
708 m_children
.Erase( node
);
712 node
= node
->GetNext();
718 bool wxSizer::Remove( int index
)
720 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
722 wxT("Remove index is out of range") );
724 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
726 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
728 delete node
->GetData();
729 m_children
.Erase( node
);
734 bool wxSizer::Detach( wxSizer
*sizer
)
736 wxASSERT_MSG( sizer
, wxT("Detaching NULL sizer") );
738 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
741 wxSizerItem
*item
= node
->GetData();
743 if (item
->GetSizer() == sizer
)
747 m_children
.Erase( node
);
750 node
= node
->GetNext();
756 bool wxSizer::Detach( wxWindow
*window
)
758 wxASSERT_MSG( window
, wxT("Detaching NULL window") );
760 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
763 wxSizerItem
*item
= node
->GetData();
765 if (item
->GetWindow() == window
)
768 m_children
.Erase( node
);
771 node
= node
->GetNext();
777 bool wxSizer::Detach( int index
)
779 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
781 wxT("Detach index is out of range") );
783 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
785 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
787 wxSizerItem
*item
= node
->GetData();
789 if ( item
->IsSizer() )
793 m_children
.Erase( node
);
797 bool wxSizer::Replace( wxWindow
*oldwin
, wxWindow
*newwin
, bool recursive
)
799 wxASSERT_MSG( oldwin
, wxT("Replacing NULL window") );
800 wxASSERT_MSG( newwin
, wxT("Replacing with NULL window") );
802 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
805 wxSizerItem
*item
= node
->GetData();
807 if (item
->GetWindow() == oldwin
)
809 item
->AssignWindow(newwin
);
810 newwin
->SetContainingSizer( this );
813 else if (recursive
&& item
->IsSizer())
815 if (item
->GetSizer()->Replace( oldwin
, newwin
, true ))
819 node
= node
->GetNext();
825 bool wxSizer::Replace( wxSizer
*oldsz
, wxSizer
*newsz
, bool recursive
)
827 wxASSERT_MSG( oldsz
, wxT("Replacing NULL sizer") );
828 wxASSERT_MSG( newsz
, wxT("Replacing with NULL sizer") );
830 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
833 wxSizerItem
*item
= node
->GetData();
835 if (item
->GetSizer() == oldsz
)
837 item
->AssignSizer(newsz
);
840 else if (recursive
&& item
->IsSizer())
842 if (item
->GetSizer()->Replace( oldsz
, newsz
, true ))
846 node
= node
->GetNext();
852 bool wxSizer::Replace( size_t old
, wxSizerItem
*newitem
)
854 wxCHECK_MSG( old
< m_children
.GetCount(), false, wxT("Replace index is out of range") );
855 wxASSERT_MSG( newitem
, wxT("Replacing with NULL item") );
857 wxSizerItemList::compatibility_iterator node
= m_children
.Item( old
);
859 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
861 wxSizerItem
*item
= node
->GetData();
862 node
->SetData(newitem
);
864 if (item
->IsWindow() && item
->GetWindow())
865 item
->GetWindow()->SetContainingSizer(NULL
);
872 void wxSizer::Clear( bool delete_windows
)
874 // First clear the ContainingSizer pointers
875 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
878 wxSizerItem
*item
= node
->GetData();
880 if (item
->IsWindow())
881 item
->GetWindow()->SetContainingSizer( NULL
);
882 node
= node
->GetNext();
885 // Destroy the windows if needed
889 // Now empty the list
890 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
893 void wxSizer::DeleteWindows()
895 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
898 wxSizerItem
*item
= node
->GetData();
900 item
->DeleteWindows();
901 node
= node
->GetNext();
905 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
907 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
909 // take the min size by default and limit it by max size
910 wxSize size
= GetMinClientSize(window
);
913 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
916 // hack for small screen devices where TLWs are always full screen
917 if ( tlw
->IsAlwaysMaximized() )
919 return tlw
->GetClientSize();
922 // limit the window to the size of the display it is on
923 int disp
= wxDisplay::GetFromWindow(window
);
924 if ( disp
== wxNOT_FOUND
)
926 // or, if we don't know which one it is, of the main one
930 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
932 // If determining the display size failed, skip the max size checks as
933 // we really don't want to create windows of (0, 0) size.
934 if ( !sizeMax
.x
|| !sizeMax
.y
)
937 // space for decorations and toolbars etc.
938 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
942 sizeMax
= GetMaxClientSize(window
);
945 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
947 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
953 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
955 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
957 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
960 wxSize
wxSizer::Fit( wxWindow
*window
)
962 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
965 window
->SetClientSize(ComputeFittingClientSize(window
));
967 // return entire size
968 return window
->GetSize();
971 void wxSizer::FitInside( wxWindow
*window
)
974 if (window
->IsTopLevel())
975 size
= VirtualFitSize( window
);
977 size
= GetMinClientSize( window
);
979 window
->SetVirtualSize( size
);
982 void wxSizer::Layout()
984 // (re)calculates minimums needed for each item and other preparations
988 // Applies the layout and repositions/resizes the items
992 void wxSizer::SetSizeHints( wxWindow
*window
)
994 // Preserve the window's max size hints, but set the
995 // lower bound according to the sizer calculations.
997 // This is equivalent to calling Fit(), except that we need to set
998 // the size hints _in between_ the two steps performed by Fit
999 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
1000 // otherwise SetClientSize() could have no effect if there already are
1001 // size hints in effect that forbid requested client size.
1003 const wxSize clientSize
= ComputeFittingClientSize(window
);
1005 window
->SetMinClientSize(clientSize
);
1006 window
->SetClientSize(clientSize
);
1009 #if WXWIN_COMPATIBILITY_2_8
1010 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
1012 FitInside( window
);
1014 #endif // WXWIN_COMPATIBILITY_2_8
1016 // TODO on mac we need a function that determines how much free space this
1017 // min size contains, in order to make sure that we have 20 pixels of free
1018 // space around the controls
1019 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
1021 return window
->WindowToClientSize(window
->GetMaxSize());
1024 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
1026 return GetMinSize(); // Already returns client size.
1029 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1031 wxSize size
= GetMinClientSize( window
);
1032 wxSize sizeMax
= GetMaxClientSize( window
);
1034 // Limit the size if sizeMax != wxDefaultSize
1036 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1038 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1044 wxSize
wxSizer::GetMinSize()
1046 wxSize
ret( CalcMin() );
1047 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1048 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1052 void wxSizer::DoSetMinSize( int width
, int height
)
1054 m_minSize
.x
= width
;
1055 m_minSize
.y
= height
;
1058 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1060 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1062 // Is it our immediate child?
1064 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1067 wxSizerItem
*item
= node
->GetData();
1069 if (item
->GetWindow() == window
)
1071 item
->SetMinSize( width
, height
);
1074 node
= node
->GetNext();
1077 // No? Search any subsizers we own then
1079 node
= m_children
.GetFirst();
1082 wxSizerItem
*item
= node
->GetData();
1084 if ( item
->GetSizer() &&
1085 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1087 // A child sizer found the requested windw, exit.
1090 node
= node
->GetNext();
1096 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1098 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1100 // Is it our immediate child?
1102 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1105 wxSizerItem
*item
= node
->GetData();
1107 if (item
->GetSizer() == sizer
)
1109 item
->GetSizer()->DoSetMinSize( width
, height
);
1112 node
= node
->GetNext();
1115 // No? Search any subsizers we own then
1117 node
= m_children
.GetFirst();
1120 wxSizerItem
*item
= node
->GetData();
1122 if ( item
->GetSizer() &&
1123 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1125 // A child found the requested sizer, exit.
1128 node
= node
->GetNext();
1134 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1136 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1138 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1140 wxSizerItem
*item
= node
->GetData();
1142 if (item
->GetSizer())
1144 // Sizers contains the minimal size in them, if not calculated ...
1145 item
->GetSizer()->DoSetMinSize( width
, height
);
1149 // ... but the minimal size of spacers and windows is stored via the item
1150 item
->SetMinSize( width
, height
);
1156 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1158 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1160 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1163 wxSizerItem
*item
= node
->GetData();
1165 if (item
->GetWindow() == window
)
1169 else if (recursive
&& item
->IsSizer())
1171 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1176 node
= node
->GetNext();
1182 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1184 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1186 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1189 wxSizerItem
*item
= node
->GetData();
1191 if (item
->GetSizer() == sizer
)
1195 else if (recursive
&& item
->IsSizer())
1197 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1202 node
= node
->GetNext();
1208 wxSizerItem
* wxSizer::GetItem( size_t index
)
1210 wxCHECK_MSG( index
< m_children
.GetCount(),
1212 wxT("GetItem index is out of range") );
1214 return m_children
.Item( index
)->GetData();
1217 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1219 // This gets a sizer item by the id of the sizer item
1220 // and NOT the id of a window if the item is a window.
1222 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1225 wxSizerItem
*item
= node
->GetData();
1227 if (item
->GetId() == id
)
1231 else if (recursive
&& item
->IsSizer())
1233 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1238 node
= node
->GetNext();
1244 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1246 wxSizerItem
*item
= GetItem( window
, recursive
);
1257 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1259 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1270 bool wxSizer::Show( size_t index
, bool show
)
1272 wxSizerItem
*item
= GetItem( index
);
1283 void wxSizer::ShowItems( bool show
)
1285 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1288 node
->GetData()->Show( show
);
1289 node
= node
->GetNext();
1293 bool wxSizer::IsShown( wxWindow
*window
) const
1295 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1298 wxSizerItem
*item
= node
->GetData();
1300 if (item
->GetWindow() == window
)
1302 return item
->IsShown();
1304 node
= node
->GetNext();
1307 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1312 bool wxSizer::IsShown( wxSizer
*sizer
) const
1314 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1317 wxSizerItem
*item
= node
->GetData();
1319 if (item
->GetSizer() == sizer
)
1321 return item
->IsShown();
1323 node
= node
->GetNext();
1326 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1331 bool wxSizer::IsShown( size_t index
) const
1333 wxCHECK_MSG( index
< m_children
.GetCount(),
1335 wxT("IsShown index is out of range") );
1337 return m_children
.Item( index
)->GetData()->IsShown();
1341 //---------------------------------------------------------------------------
1343 //---------------------------------------------------------------------------
1345 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1346 : m_rows( cols
== 0 ? 1 : 0 ),
1351 wxASSERT(cols
>= 0);
1354 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1355 : m_rows( cols
== 0 ? 1 : 0 ),
1357 m_vgap( gap
.GetHeight() ),
1358 m_hgap( gap
.GetWidth() )
1360 wxASSERT(cols
>= 0);
1363 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1364 : m_rows( rows
|| cols
? rows
: 1 ),
1369 wxASSERT(rows
>= 0 && cols
>= 0);
1372 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1373 : m_rows( rows
|| cols
? rows
: 1 ),
1375 m_vgap( gap
.GetHeight() ),
1376 m_hgap( gap
.GetWidth() )
1378 wxASSERT(rows
>= 0 && cols
>= 0);
1381 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1383 // if only the number of columns or the number of rows is specified for a
1384 // sizer, arbitrarily many items can be added to it but if both of them are
1385 // fixed, then the sizer can't have more than that many items -- check for
1386 // this here to ensure that we detect errors as soon as possible
1387 if ( m_cols
&& m_rows
)
1389 const int nitems
= m_children
.GetCount();
1390 if ( nitems
== m_cols
*m_rows
)
1394 "too many items (%d > %d*%d) in grid sizer (maybe you "
1395 "should omit the number of either rows or columns?)",
1396 nitems
+ 1, m_cols
, m_rows
)
1399 // additionally, continuing to use the specified number of columns
1400 // and rows is not a good idea as callers of CalcRowsCols() expect
1401 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1402 // which is not the case if there are too many items and results in
1403 // crashes, so let it compute the number of rows automatically by
1404 // forgetting the (wrong) number of rows specified (this also has a
1405 // nice side effect of giving only one assert even if there are
1406 // many more items than allowed in this sizer)
1411 return wxSizer::DoInsert(index
, item
);
1414 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1416 const int nitems
= m_children
.GetCount();
1418 ncols
= GetEffectiveColsCount();
1419 nrows
= GetEffectiveRowsCount();
1421 // Since Insert() checks for overpopulation, the following
1422 // should only assert if the grid was shrunk via SetRows() / SetCols()
1423 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1428 void wxGridSizer::RecalcSizes()
1430 int nitems
, nrows
, ncols
;
1431 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1434 wxSize
sz( GetSize() );
1435 wxPoint
pt( GetPosition() );
1437 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1438 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1441 for (int c
= 0; c
< ncols
; c
++)
1444 for (int r
= 0; r
< nrows
; r
++)
1446 int i
= r
* ncols
+ c
;
1449 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1451 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1453 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1461 wxSize
wxGridSizer::CalcMin()
1464 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1467 // Find the max width and height for any component
1471 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1474 wxSizerItem
*item
= node
->GetData();
1475 wxSize
sz( item
->CalcMin() );
1477 w
= wxMax( w
, sz
.x
);
1478 h
= wxMax( h
, sz
.y
);
1480 node
= node
->GetNext();
1483 // In case we have a nested sizer with a two step algo , give it
1484 // a chance to adjust to that (we give it width component)
1485 node
= m_children
.GetFirst();
1486 bool didChangeMinSize
= false;
1489 wxSizerItem
*item
= node
->GetData();
1490 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1492 node
= node
->GetNext();
1495 // And redo iteration in case min size changed
1496 if( didChangeMinSize
)
1498 node
= m_children
.GetFirst();
1502 wxSizerItem
*item
= node
->GetData();
1503 wxSize
sz( item
->GetMinSizeWithBorder() );
1505 w
= wxMax( w
, sz
.x
);
1506 h
= wxMax( h
, sz
.y
);
1508 node
= node
->GetNext();
1512 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1513 nrows
* h
+ (nrows
-1) * m_vgap
);
1516 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1519 wxSize
sz( item
->GetMinSizeWithBorder() );
1520 int flag
= item
->GetFlag();
1522 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1528 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1530 pt
.x
= x
+ (w
- sz
.x
) / 2;
1532 else if (flag
& wxALIGN_RIGHT
)
1534 pt
.x
= x
+ (w
- sz
.x
);
1537 if (flag
& wxALIGN_CENTER_VERTICAL
)
1539 pt
.y
= y
+ (h
- sz
.y
) / 2;
1541 else if (flag
& wxALIGN_BOTTOM
)
1543 pt
.y
= y
+ (h
- sz
.y
);
1547 item
->SetDimension(pt
, sz
);
1550 //---------------------------------------------------------------------------
1552 //---------------------------------------------------------------------------
1554 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1555 : wxGridSizer( cols
, vgap
, hgap
),
1556 m_flexDirection(wxBOTH
),
1557 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1561 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1562 : wxGridSizer( cols
, gap
),
1563 m_flexDirection(wxBOTH
),
1564 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1568 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1569 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1570 m_flexDirection(wxBOTH
),
1571 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1575 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1576 : wxGridSizer( rows
, cols
, gap
),
1577 m_flexDirection(wxBOTH
),
1578 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1582 wxFlexGridSizer::~wxFlexGridSizer()
1586 void wxFlexGridSizer::RecalcSizes()
1589 if ( !CalcRowsCols(nrows
, ncols
) )
1592 const wxPoint
pt(GetPosition());
1593 const wxSize
sz(GetSize());
1595 AdjustForGrowables(sz
);
1597 wxSizerItemList::const_iterator i
= m_children
.begin();
1598 const wxSizerItemList::const_iterator end
= m_children
.end();
1601 for ( int r
= 0; r
< nrows
; r
++ )
1603 if ( m_rowHeights
[r
] == -1 )
1605 // this row is entirely hidden, skip it
1606 for ( int c
= 0; c
< ncols
; c
++ )
1617 const int hrow
= m_rowHeights
[r
];
1618 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1623 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1625 const int wcol
= m_colWidths
[c
];
1630 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1634 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1646 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1647 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1649 // Sum total minimum size, including gaps between rows/columns.
1650 // -1 is used as a magic number meaning empty row/column.
1653 const size_t count
= sizes
.size();
1654 for ( size_t n
= 0; n
< count
; n
++ )
1656 if ( sizes
[n
] != -1 )
1659 total
+= gap
; // separate from the previous column
1668 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1670 // We have to recalculate the sizes in case the item minimum size has
1671 // changed since the previous layout, or the item has been hidden using
1672 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1673 // dimension of the row/column will be -1, indicating that the column
1674 // itself is hidden.
1675 m_rowHeights
.assign(nrows
, -1);
1676 m_colWidths
.assign(ncols
, -1);
1678 // n is the index of the item in left-to-right top-to-bottom order
1680 for ( wxSizerItemList::iterator i
= m_children
.begin();
1681 i
!= m_children
.end();
1684 wxSizerItem
* const item
= *i
;
1685 if ( item
->IsShown() )
1687 // NOTE: Not doing the calculation here, this is just
1688 // for finding max values.
1689 const wxSize
sz(item
->GetMinSizeWithBorder());
1691 const int row
= n
/ ncols
;
1692 const int col
= n
% ncols
;
1694 if ( sz
.y
> m_rowHeights
[row
] )
1695 m_rowHeights
[row
] = sz
.y
;
1696 if ( sz
.x
> m_colWidths
[col
] )
1697 m_colWidths
[col
] = sz
.x
;
1701 AdjustForFlexDirection();
1703 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1704 SumArraySizes(m_rowHeights
, m_vgap
));
1707 wxSize
wxFlexGridSizer::CalcMin()
1712 // Number of rows/columns can change as items are added or removed.
1713 if ( !CalcRowsCols(nrows
, ncols
) )
1717 // We have to recalculate the sizes in case the item minimum size has
1718 // changed since the previous layout, or the item has been hidden using
1719 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1720 // dimension of the row/column will be -1, indicating that the column
1721 // itself is hidden.
1722 m_rowHeights
.assign(nrows
, -1);
1723 m_colWidths
.assign(ncols
, -1);
1725 for ( wxSizerItemList::iterator i
= m_children
.begin();
1726 i
!= m_children
.end();
1729 wxSizerItem
* const item
= *i
;
1730 if ( item
->IsShown() )
1736 // The stage of looking for max values in each row/column has been
1737 // made a separate function, since it's reused in AdjustForGrowables.
1738 FindWidthsAndHeights(nrows
,ncols
);
1740 return m_calculatedMinSize
;
1743 void wxFlexGridSizer::AdjustForFlexDirection()
1745 // the logic in CalcMin works when we resize flexibly in both directions
1746 // but maybe this is not the case
1747 if ( m_flexDirection
!= wxBOTH
)
1749 // select the array corresponding to the direction in which we do *not*
1751 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1754 const size_t count
= array
.GetCount();
1756 // find the largest value in this array
1760 for ( n
= 0; n
< count
; ++n
)
1762 if ( array
[n
] > largest
)
1766 // and now fill it with the largest value
1767 for ( n
= 0; n
< count
; ++n
)
1769 // don't touch hidden rows
1770 if ( array
[n
] != -1 )
1776 // helper of AdjustForGrowables() which is called for rows/columns separately
1779 // delta: the extra space, we do nothing unless it's positive
1780 // growable: indices or growable rows/cols in sizes array
1781 // sizes: the height/widths of rows/cols to adjust
1782 // proportions: proportions of the growable rows/cols or NULL if they all
1783 // should be assumed to have proportion of 1
1785 DoAdjustForGrowables(int delta
,
1786 const wxArrayInt
& growable
,
1788 const wxArrayInt
*proportions
)
1793 // total sum of proportions of all non-hidden rows
1794 int sum_proportions
= 0;
1796 // number of currently shown growable rows
1799 const int max_idx
= sizes
.size();
1801 const size_t count
= growable
.size();
1803 for ( idx
= 0; idx
< count
; idx
++ )
1805 // Since the number of rows/columns can change as items are
1806 // inserted/deleted, we need to verify at runtime that the
1807 // requested growable rows/columns are still valid.
1808 if ( growable
[idx
] >= max_idx
)
1811 // If all items in a row/column are hidden, that row/column will
1812 // have a dimension of -1. This causes the row/column to be
1813 // hidden completely.
1814 if ( sizes
[growable
[idx
]] == -1 )
1818 sum_proportions
+= (*proportions
)[idx
];
1826 // the remaining extra free space, adjusted during each iteration
1827 for ( idx
= 0; idx
< count
; idx
++ )
1829 if ( growable
[idx
] >= max_idx
)
1832 if ( sizes
[ growable
[idx
] ] == -1 )
1836 if ( sum_proportions
== 0 )
1838 // no growable rows -- divide extra space evenly among all
1839 cur_delta
= delta
/num
;
1842 else // allocate extra space proportionally
1844 const int cur_prop
= (*proportions
)[idx
];
1845 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1846 sum_proportions
-= cur_prop
;
1849 sizes
[growable
[idx
]] += cur_delta
;
1854 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1857 // by the time this function is called, the sizer should be already fully
1858 // initialized and hence the number of its columns and rows is known and we
1859 // can check that all indices in m_growableCols/Rows are valid (see also
1860 // comments in AddGrowableCol/Row())
1861 if ( !m_rows
|| !m_cols
)
1865 int nrows
= CalcRows();
1867 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1869 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1870 "invalid growable row index" );
1876 int ncols
= CalcCols();
1878 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1880 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1881 "invalid growable column index" );
1885 #endif // wxDEBUG_LEVEL
1888 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1890 DoAdjustForGrowables
1892 sz
.x
- m_calculatedMinSize
.x
,
1895 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1899 // This gives nested objects that benefit from knowing one size
1900 // component in advance the chance to use that.
1901 bool didAdjustMinSize
= false;
1903 // Iterate over all items and inform about column width
1904 const int ncols
= GetEffectiveColsCount();
1906 for ( wxSizerItemList::iterator i
= m_children
.begin();
1907 i
!= m_children
.end();
1910 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1911 if ( ++col
== ncols
)
1915 // Only redo if info was actually used
1916 if( didAdjustMinSize
)
1918 DoAdjustForGrowables
1920 sz
.x
- m_calculatedMinSize
.x
,
1923 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1929 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1931 // pass NULL instead of proportions if the grow mode is ALL as we
1932 // should treat all rows as having proportion of 1 then
1933 DoAdjustForGrowables
1935 sz
.y
- m_calculatedMinSize
.y
,
1938 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1944 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1946 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1949 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1951 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1954 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1956 wxASSERT_MSG( !IsRowGrowable( idx
),
1957 "AddGrowableRow() called for growable row" );
1959 // notice that we intentionally don't check the index validity here in (the
1960 // common) case when the number of rows was not specified in the ctor -- in
1961 // this case it will be computed only later, when all items are added to
1962 // the sizer, and the check will be done in AdjustForGrowables()
1963 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1965 m_growableRows
.Add( idx
);
1966 m_growableRowsProportions
.Add( proportion
);
1969 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1971 wxASSERT_MSG( !IsColGrowable( idx
),
1972 "AddGrowableCol() called for growable column" );
1974 // see comment in AddGrowableRow(): although it's less common to omit the
1975 // specification of the number of columns, it still can also happen
1976 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1978 m_growableCols
.Add( idx
);
1979 m_growableColsProportions
.Add( proportion
);
1982 // helper function for RemoveGrowableCol/Row()
1984 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1986 const size_t count
= items
.size();
1987 for ( size_t n
= 0; n
< count
; n
++ )
1989 if ( (size_t)items
[n
] == idx
)
1992 proportions
.RemoveAt(n
);
1997 wxFAIL_MSG( wxT("column/row is already not growable") );
2000 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
2002 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
2005 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
2007 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
2010 //---------------------------------------------------------------------------
2012 //---------------------------------------------------------------------------
2014 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
2016 return IsVertical() ? Add(0, size
) : Add(size
, 0);
2023 Helper of RecalcSizes(): checks if there is enough remaining space for the
2024 min size of the given item and returns its min size or the entire remaining
2025 space depending on which one is greater.
2027 This function updates the remaining space parameter to account for the size
2028 effectively allocated to the item.
2031 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2033 int& remainingSpace
= *remainingSpace_
;
2036 if ( remainingSpace
> 0 )
2038 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2039 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2041 if ( size
>= remainingSpace
)
2043 // truncate the item to fit in the remaining space, this is better
2044 // than showing it only partially in general, even if both choices
2045 // are bad -- but there is nothing else we can do
2046 size
= remainingSpace
;
2049 remainingSpace
-= size
;
2051 else // no remaining space
2053 // no space at all left, no need to even query the item for its min
2054 // size as we can't give it to it anyhow
2061 } // anonymous namespace
2063 void wxBoxSizer::RecalcSizes()
2065 if ( m_children
.empty() )
2068 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2069 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2071 // the amount of free space which we should redistribute among the
2072 // stretchable items (i.e. those with non zero proportion)
2073 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2075 // declare loop variables used below:
2076 wxSizerItemList::const_iterator i
; // iterator in m_children list
2077 unsigned n
= 0; // item index in majorSizes array
2080 // First, inform item about the available size in minor direction as this
2081 // can change their size in the major direction. Also compute the number of
2082 // visible items and sum of their min sizes in major direction.
2084 int minMajorSize
= 0;
2085 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2087 wxSizerItem
* const item
= *i
;
2089 if ( !item
->IsShown() )
2092 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2093 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2094 wxSize szMin
= item
->GetMinSizeWithBorder();
2095 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2098 // Since we passed available space along to the item, it should not
2099 // take too much, so delta should not become negative.
2100 delta
-= deltaChange
;
2102 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2105 // update our min size and delta which may have changed
2106 SizeInMajorDir(m_minSize
) = minMajorSize
;
2107 delta
= totalMajorSize
- minMajorSize
;
2110 // space and sum of proportions for the remaining items, both may change
2112 wxCoord remaining
= totalMajorSize
;
2113 int totalProportion
= m_totalProportion
;
2115 // size of the (visible) items in major direction, -1 means "not fixed yet"
2116 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2119 // Check for the degenerated case when we don't have enough space for even
2120 // the min sizes of all the items: in this case we really can't do much
2121 // more than to allocate the min size to as many of fixed size items as
2122 // possible (on the assumption that variable size items such as text zones
2123 // or list boxes may use scrollbars to show their content even if their
2124 // size is less than min size but that fixed size items such as buttons
2125 // will suffer even more if we don't give them their min size)
2126 if ( totalMajorSize
< minMajorSize
)
2128 // Second degenerated case pass: allocate min size to all fixed size
2130 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2132 wxSizerItem
* const item
= *i
;
2134 if ( !item
->IsShown() )
2137 // deal with fixed size items only during this pass
2138 if ( item
->GetProportion() )
2141 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2145 // Third degenerated case pass: allocate min size to all the remaining,
2146 // i.e. non-fixed size, items.
2147 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2149 wxSizerItem
* const item
= *i
;
2151 if ( !item
->IsShown() )
2154 // we've already dealt with fixed size items above
2155 if ( !item
->GetProportion() )
2158 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2161 else // we do have enough space to give at least min sizes to all items
2163 // Second and maybe more passes in the non-degenerated case: deal with
2164 // fixed size items and items whose min size is greater than what we
2165 // would allocate to them taking their proportion into account. For
2166 // both of them, we will just use their min size, but for the latter we
2167 // also need to reexamine all the items as the items which fitted
2168 // before we adjusted their size upwards might not fit any more. This
2169 // does make for a quadratic algorithm but it's not obvious how to
2170 // avoid it and hopefully it's not a huge problem in practice as the
2171 // sizers don't have many items usually (and, of course, the algorithm
2172 // still reduces into a linear one if there is enough space for all the
2174 bool nonFixedSpaceChanged
= false;
2175 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2177 if ( nonFixedSpaceChanged
)
2179 i
= m_children
.begin();
2181 nonFixedSpaceChanged
= false;
2184 // check for the end of the loop only after the check above as
2185 // otherwise we wouldn't do another pass if the last child resulted
2186 // in non fixed space reduction
2187 if ( i
== m_children
.end() )
2190 wxSizerItem
* const item
= *i
;
2192 if ( !item
->IsShown() )
2195 // don't check the item which we had already dealt with during a
2196 // previous pass (this is more than an optimization, the code
2197 // wouldn't work correctly if we kept adjusting for the same item
2198 // over and over again)
2199 if ( majorSizes
[n
] != wxDefaultCoord
)
2202 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2204 // it doesn't make sense for min size to be negative but right now
2205 // it's possible to create e.g. a spacer with (-1, 10) as size and
2206 // people do it in their code apparently (see #11842) so ensure
2207 // that we don't use this -1 as real min size as it conflicts with
2208 // the meaning we use for it here and negative min sizes just don't
2209 // make sense anyhow (which is why it might be a better idea to
2210 // deal with them at wxSizerItem level in the future but for now
2211 // this is the minimal fix for the bug)
2215 const int propItem
= item
->GetProportion();
2218 // is the desired size of this item big enough?
2219 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2221 // yes, it is, we'll determine the real size of this
2222 // item later, for now just leave it as wxDefaultCoord
2226 // the proportion of this item won't count, it has
2227 // effectively become fixed
2228 totalProportion
-= propItem
;
2231 // we can already allocate space for this item
2232 majorSizes
[n
] = minMajor
;
2234 // change the amount of the space remaining to the other items,
2235 // as this can result in not being able to satisfy their
2236 // proportions any more we will need to redo another loop
2238 remaining
-= minMajor
;
2240 nonFixedSpaceChanged
= true;
2243 // Similar to the previous loop, but dealing with items whose max size
2244 // is less than what we would allocate to them taking their proportion
2246 nonFixedSpaceChanged
= false;
2247 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2249 if ( nonFixedSpaceChanged
)
2251 i
= m_children
.begin();
2253 nonFixedSpaceChanged
= false;
2256 // check for the end of the loop only after the check above as
2257 // otherwise we wouldn't do another pass if the last child resulted
2258 // in non fixed space reduction
2259 if ( i
== m_children
.end() )
2262 wxSizerItem
* const item
= *i
;
2264 if ( !item
->IsShown() )
2267 // don't check the item which we had already dealt with during a
2268 // previous pass (this is more than an optimization, the code
2269 // wouldn't work correctly if we kept adjusting for the same item
2270 // over and over again)
2271 if ( majorSizes
[n
] != wxDefaultCoord
)
2274 wxCoord maxMajor
= GetSizeInMajorDir(item
->GetMaxSizeWithBorder());
2276 // must be nonzero, fixed-size items were dealt with in previous loop
2277 const int propItem
= item
->GetProportion();
2279 // is the desired size of this item small enough?
2280 if ( maxMajor
< 0 ||
2281 (remaining
*propItem
)/totalProportion
<= maxMajor
)
2283 // yes, it is, we'll determine the real size of this
2284 // item later, for now just leave it as wxDefaultCoord
2288 // the proportion of this item won't count, it has
2289 // effectively become fixed
2290 totalProportion
-= propItem
;
2292 // we can already allocate space for this item
2293 majorSizes
[n
] = maxMajor
;
2295 // change the amount of the space remaining to the other items,
2296 // as this can result in not being able to satisfy their
2297 // proportions any more we will need to redo another loop
2299 remaining
-= maxMajor
;
2301 nonFixedSpaceChanged
= true;
2304 // Last by one pass: distribute the remaining space among the non-fixed
2305 // items whose size weren't fixed yet according to their proportions.
2306 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2308 wxSizerItem
* const item
= *i
;
2310 if ( !item
->IsShown() )
2313 if ( majorSizes
[n
] == wxDefaultCoord
)
2315 const int propItem
= item
->GetProportion();
2316 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2318 remaining
-= majorSizes
[n
];
2319 totalProportion
-= propItem
;
2325 // the position at which we put the next child
2326 wxPoint
pt(m_position
);
2329 // Final pass: finally do position the items correctly using their sizes as
2330 // determined above.
2331 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2333 wxSizerItem
* const item
= *i
;
2335 if ( !item
->IsShown() )
2338 const int majorSize
= majorSizes
[n
];
2340 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2342 // apply the alignment in the minor direction
2343 wxPoint
posChild(pt
);
2345 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2346 const int flag
= item
->GetFlag();
2347 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2349 // occupy all the available space if wxEXPAND was given and also if
2350 // the item is too big to fit -- in this case we truncate it below
2351 // its minimal size which is bad but better than not showing parts
2352 // of the window at all
2353 minorSize
= totalMinorSize
;
2355 // do not allow the size in the minor direction to grow beyond the max
2356 // size of the item in the minor direction
2357 const wxCoord maxMinorSize
= GetSizeInMinorDir(item
->GetMaxSizeWithBorder());
2358 if ( maxMinorSize
>= 0 && minorSize
> maxMinorSize
)
2359 minorSize
= maxMinorSize
;
2362 if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2364 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2366 // NB: wxCENTRE is used here only for backwards compatibility,
2367 // wxALIGN_CENTRE should be used in new code
2368 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2369 : wxALIGN_CENTRE_VERTICAL
)) )
2371 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2375 // apply RTL adjustment for horizontal sizers:
2376 if ( !IsVertical() && m_containingWindow
)
2378 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2386 // finally set size of this child and advance to the next one
2387 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2389 PosInMajorDir(pt
) += majorSize
;
2393 wxSize
wxBoxSizer::CalcMin()
2395 m_totalProportion
= 0;
2396 m_minSize
= wxSize(0, 0);
2398 // The minimal size for the sizer should be big enough to allocate its
2399 // element at least its minimal size but also, and this is the non trivial
2400 // part, to respect the children proportion. To satisfy the latter
2401 // condition we must find the greatest min-size-to-proportion ratio for all
2402 // elements with non-zero proportion.
2403 float maxMinSizeToProp
= 0.;
2404 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2405 i
!= m_children
.end();
2408 wxSizerItem
* const item
= *i
;
2410 if ( !item
->IsShown() )
2413 const wxSize sizeMinThis
= item
->CalcMin();
2414 if ( const int propThis
= item
->GetProportion() )
2416 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2417 minSizeToProp
/= propThis
;
2419 if ( minSizeToProp
> maxMinSizeToProp
)
2420 maxMinSizeToProp
= minSizeToProp
;
2422 m_totalProportion
+= item
->GetProportion();
2424 else // fixed size item
2426 // Just account for its size directly
2427 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2430 // In the transversal direction we just need to find the maximum.
2431 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2432 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2435 // Using the max ratio ensures that the min size is big enough for all
2436 // items to have their min size and satisfy the proportions among them.
2437 SizeInMajorDir(m_minSize
) += (int)(maxMinSizeToProp
*m_totalProportion
);
2442 //---------------------------------------------------------------------------
2444 //---------------------------------------------------------------------------
2448 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2449 : wxBoxSizer( orient
),
2452 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2454 // do this so that our Detach() is called if the static box is destroyed
2456 m_staticBox
->SetContainingSizer(this);
2459 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2460 : wxBoxSizer(orient
),
2461 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2464 m_staticBox
->SetContainingSizer(this);
2467 wxStaticBoxSizer::~wxStaticBoxSizer()
2472 void wxStaticBoxSizer::RecalcSizes()
2474 int top_border
, other_border
;
2475 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2477 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2479 wxSize
old_size( m_size
);
2480 m_size
.x
-= 2*other_border
;
2481 m_size
.y
-= top_border
+ other_border
;
2483 wxPoint
old_pos( m_position
);
2484 if (m_staticBox
->GetChildren().GetCount() > 0)
2486 #if defined( __WXGTK20__ )
2487 // if the wxStaticBox has created a wxPizza to contain its children
2488 // (see wxStaticBox::AddChild) then we need to place the items it contains
2489 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2490 // to the top-left corner of the staticbox:
2491 m_position
.x
= m_position
.y
= 0;
2492 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
2493 // the distance from the 'inner' content view to the embedded controls
2494 // this is independent of the title, therefore top_border is not relevant
2495 m_position
.x
= m_position
.y
= 10;
2497 // if the wxStaticBox has children, then these windows must be placed
2498 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2499 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2500 // to keep in count the static borders here!):
2501 m_position
.x
= other_border
;
2502 m_position
.y
= top_border
;
2507 // the windows contained in the staticbox have been created as siblings of the
2508 // staticbox (this is the "old" way of staticbox contents creation); in this
2509 // case we need to position them with coordinates relative to our common parent
2510 m_position
.x
+= other_border
;
2511 m_position
.y
+= top_border
;
2514 wxBoxSizer::RecalcSizes();
2516 m_position
= old_pos
;
2520 wxSize
wxStaticBoxSizer::CalcMin()
2522 int top_border
, other_border
;
2523 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2525 wxSize
ret( wxBoxSizer::CalcMin() );
2526 ret
.x
+= 2*other_border
;
2528 // ensure that we're wide enough to show the static box label (there is no
2529 // need to check for the static box best size in vertical direction though)
2530 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2531 if ( ret
.x
< boxWidth
)
2534 ret
.y
+= other_border
+ top_border
;
2539 void wxStaticBoxSizer::ShowItems( bool show
)
2541 m_staticBox
->Show( show
);
2542 wxBoxSizer::ShowItems( show
);
2545 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2547 // avoid deleting m_staticBox in our dtor if it's being detached from the
2548 // sizer (which can happen because it's being already destroyed for
2550 if ( window
== m_staticBox
)
2556 return wxSizer::Detach( window
);
2559 #endif // wxUSE_STATBOX
2561 //---------------------------------------------------------------------------
2562 // wxStdDialogButtonSizer
2563 //---------------------------------------------------------------------------
2567 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2568 : wxBoxSizer(wxHORIZONTAL
)
2570 // Vertical buttons with lots of space on either side
2571 // looks rubbish on WinCE, so let's not do this for now.
2572 // If we are going to use vertical buttons, we should
2573 // put the sizer to the right of other controls in the dialog,
2574 // and that's beyond the scope of this sizer.
2576 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2577 // If we have a PDA screen, put yes/no button over
2578 // all other buttons, otherwise on the left side.
2580 m_orient
= wxVERTICAL
;
2583 m_buttonAffirmative
= NULL
;
2584 m_buttonApply
= NULL
;
2585 m_buttonNegative
= NULL
;
2586 m_buttonCancel
= NULL
;
2587 m_buttonHelp
= NULL
;
2590 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2592 switch (mybutton
->GetId())
2597 m_buttonAffirmative
= mybutton
;
2600 m_buttonApply
= mybutton
;
2603 m_buttonNegative
= mybutton
;
2607 m_buttonCancel
= mybutton
;
2610 case wxID_CONTEXT_HELP
:
2611 m_buttonHelp
= mybutton
;
2618 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2620 m_buttonAffirmative
= button
;
2623 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2625 m_buttonNegative
= button
;
2628 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2630 m_buttonCancel
= button
;
2633 void wxStdDialogButtonSizer::Realize()
2636 Add(0, 0, 0, wxLEFT
, 6);
2638 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2640 if (m_buttonNegative
){
2641 // HIG POLICE BULLETIN - destructive buttons need extra padding
2642 // 24 pixels on either side
2643 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2646 // extra whitespace between help/negative and cancel/ok buttons
2647 Add(0, 0, 1, wxEXPAND
, 0);
2649 if (m_buttonCancel
){
2650 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2651 // Cancel or help should be default
2652 // m_buttonCancel->SetDefaultButton();
2655 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2656 // figure the best place is between Cancel and OK
2658 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2660 if (m_buttonAffirmative
){
2661 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2663 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2664 // these buttons have set labels under Mac so we should use them
2665 m_buttonAffirmative
->SetLabel(_("Save"));
2666 if (m_buttonNegative
)
2667 m_buttonNegative
->SetLabel(_("Don't Save"));
2671 // Extra space around and at the right
2673 #elif defined(__WXGTK20__)
2674 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2675 // says that the correct button order is
2677 // [Help] [Alternative] [Cancel] [Affirmative]
2679 // Flags ensuring that margins between the buttons are 6 pixels.
2681 flagsBtn
= wxSizerFlags().Centre().Border(wxLEFT
| wxRIGHT
, 3);
2683 // Margin around the entire sizer button should be 12.
2687 Add(m_buttonHelp
, flagsBtn
);
2689 // Align the rest of the buttons to the right.
2692 if (m_buttonNegative
)
2693 Add(m_buttonNegative
, flagsBtn
);
2696 Add(m_buttonApply
, flagsBtn
);
2699 Add(m_buttonCancel
, flagsBtn
);
2701 if (m_buttonAffirmative
)
2702 Add(m_buttonAffirmative
, flagsBtn
);
2704 // Ensure that the right margin is 12 as well.
2706 #elif defined(__WXMSW__)
2709 // right-justify buttons
2710 Add(0, 0, 1, wxEXPAND
, 0);
2712 if (m_buttonAffirmative
){
2713 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2716 if (m_buttonNegative
){
2717 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2720 if (m_buttonCancel
){
2721 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2724 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2727 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2729 // GTK+1 and any other platform
2731 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2733 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2735 // extra whitespace between help and cancel/ok buttons
2736 Add(0, 0, 1, wxEXPAND
, 0);
2739 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2741 if (m_buttonAffirmative
){
2742 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2745 if (m_buttonNegative
){
2746 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2749 if (m_buttonCancel
){
2750 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2751 // Cancel or help should be default
2752 // m_buttonCancel->SetDefaultButton();
2758 #endif // wxUSE_BUTTON