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 wxSizerItem::wxSizerItem(int width
,
218 m_minSize(width
, height
), // minimal size is the initial size
219 m_proportion(proportion
),
225 ASSERT_VALID_SIZER_FLAGS( m_flag
);
227 DoSetSpacer(wxSize(width
, height
));
230 wxSizerItem::~wxSizerItem()
236 void wxSizerItem::Free()
244 m_window
->SetContainingSizer(NULL
);
257 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
263 wxSize
wxSizerItem::GetSpacer() const
266 if ( m_kind
== Item_Spacer
)
267 size
= m_spacer
->GetSize();
273 wxSize
wxSizerItem::GetSize() const
282 ret
= m_window
->GetSize();
286 ret
= m_sizer
->GetSize();
290 ret
= m_spacer
->GetSize();
295 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
302 if (m_flag
& wxNORTH
)
304 if (m_flag
& wxSOUTH
)
310 bool wxSizerItem::InformFirstDirection(int direction
, int size
, int availableOtherDir
)
312 // The size that come here will be including borders. Child items should get it
316 if( direction
==wxHORIZONTAL
)
323 else if( direction
==wxVERTICAL
)
325 if (m_flag
& wxNORTH
)
327 if (m_flag
& wxSOUTH
)
333 // Pass the information along to the held object
336 didUse
= GetSizer()->InformFirstDirection(direction
,size
,availableOtherDir
);
338 m_minSize
= GetSizer()->CalcMin();
342 didUse
= GetWindow()->InformFirstDirection(direction
,size
,availableOtherDir
);
344 m_minSize
= m_window
->GetEffectiveMinSize();
346 // This information is useful for items with wxSHAPED flag, since
347 // we can request an optimal min size for such an item. Even if
348 // we overwrite the m_minSize member here, we can read it back from
349 // the owned window (happens automatically).
350 if( (m_flag
& wxSHAPED
) && (m_flag
& wxEXPAND
) && direction
)
352 if( !wxIsNullDouble(m_ratio
) )
354 wxCHECK_MSG( (m_proportion
==0), false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
355 if( direction
==wxHORIZONTAL
&& !wxIsNullDouble(m_ratio
) )
357 // Clip size so that we don't take too much
358 if( availableOtherDir
>=0 && int(size
/m_ratio
)-m_minSize
.y
>availableOtherDir
)
359 size
= int((availableOtherDir
+m_minSize
.y
)*m_ratio
);
360 m_minSize
= wxSize(size
,int(size
/m_ratio
));
362 else if( direction
==wxVERTICAL
)
364 // Clip size so that we don't take too much
365 if( availableOtherDir
>=0 && int(size
*m_ratio
)-m_minSize
.x
>availableOtherDir
)
366 size
= int((availableOtherDir
+m_minSize
.x
)/m_ratio
);
367 m_minSize
= wxSize(int(size
*m_ratio
),size
);
377 wxSize
wxSizerItem::CalcMin()
381 m_minSize
= m_sizer
->GetMinSize();
383 // if we have to preserve aspect ratio _AND_ this is
384 // the first-time calculation, consider ret to be initial size
385 if ( (m_flag
& wxSHAPED
) && wxIsNullDouble(m_ratio
) )
388 else if ( IsWindow() )
390 // Since the size of the window may change during runtime, we
391 // should use the current minimal/best size.
392 m_minSize
= m_window
->GetEffectiveMinSize();
395 return GetMinSizeWithBorder();
398 wxSize
wxSizerItem::GetMinSizeWithBorder() const
400 wxSize ret
= m_minSize
;
406 if (m_flag
& wxNORTH
)
408 if (m_flag
& wxSOUTH
)
415 void wxSizerItem::SetDimension( const wxPoint
& pos_
, const wxSize
& size_
)
419 if (m_flag
& wxSHAPED
)
421 // adjust aspect ratio
422 int rwidth
= (int) (size
.y
* m_ratio
);
426 int rheight
= (int) (size
.x
/ m_ratio
);
427 // add vertical space
428 if (m_flag
& wxALIGN_CENTER_VERTICAL
)
429 pos
.y
+= (size
.y
- rheight
) / 2;
430 else if (m_flag
& wxALIGN_BOTTOM
)
431 pos
.y
+= (size
.y
- rheight
);
432 // use reduced dimensions
435 else if (rwidth
< size
.x
)
437 // add horizontal space
438 if (m_flag
& wxALIGN_CENTER_HORIZONTAL
)
439 pos
.x
+= (size
.x
- rwidth
) / 2;
440 else if (m_flag
& wxALIGN_RIGHT
)
441 pos
.x
+= (size
.x
- rwidth
);
446 // This is what GetPosition() returns. Since we calculate
447 // borders afterwards, GetPosition() will be the left/top
448 // corner of the surrounding border.
460 if (m_flag
& wxNORTH
)
465 if (m_flag
& wxSOUTH
)
475 m_rect
= wxRect(pos
, size
);
480 wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
485 // Use wxSIZE_FORCE_EVENT here since a sizer item might
486 // have changed alignment or some other property which would
487 // not change the size of the window. In such a case, no
488 // wxSizeEvent would normally be generated and thus the
489 // control wouldn't get layed out correctly here.
491 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
492 wxSIZE_ALLOW_MINUS_ONE
|wxSIZE_FORCE_EVENT
);
494 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
495 wxSIZE_ALLOW_MINUS_ONE
);
500 m_sizer
->SetDimension(pos
, size
);
504 m_spacer
->SetSize(size
);
509 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
513 void wxSizerItem::DeleteWindows()
522 //We are deleting the window from this sizer - normally
523 //the window destroys the sizer associated with it,
524 //which might destroy this, which we don't want
525 m_window
->SetContainingSizer(NULL
);
527 //Putting this after the switch will result in a spacer
528 //not being deleted properly on destruction
533 m_sizer
->DeleteWindows();
538 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
543 void wxSizerItem::Show( bool show
)
548 wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
552 m_window
->Show(show
);
560 m_spacer
->Show(show
);
565 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
569 bool wxSizerItem::IsShown() const
571 if ( m_flag
& wxRESERVE_SPACE_EVEN_IF_HIDDEN
)
577 // we may be called from CalcMin(), just return false so that we're
582 return m_window
->IsShown();
585 // arbitrarily decide that if at least one of our elements is
586 // shown, so are we (this arbitrariness is the reason for
587 // deprecating this function)
588 for ( wxSizerItemList::compatibility_iterator
589 node
= m_sizer
->GetChildren().GetFirst();
591 node
= node
->GetNext() )
593 if ( node
->GetData()->IsShown() )
599 return m_spacer
->IsShown();
603 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
609 #if WXWIN_COMPATIBILITY_2_6
610 void wxSizerItem::SetOption( int option
)
612 SetProportion( option
);
615 int wxSizerItem::GetOption() const
617 return GetProportion();
619 #endif // WXWIN_COMPATIBILITY_2_6
622 //---------------------------------------------------------------------------
624 //---------------------------------------------------------------------------
628 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
631 wxSizerItem
* wxSizer::DoInsert( size_t index
, wxSizerItem
*item
)
633 m_children
.Insert( index
, item
);
635 if ( item
->GetWindow() )
636 item
->GetWindow()->SetContainingSizer( this );
638 if ( item
->GetSizer() )
639 item
->GetSizer()->SetContainingWindow( m_containingWindow
);
644 void wxSizer::SetContainingWindow(wxWindow
*win
)
646 if ( win
== m_containingWindow
)
649 m_containingWindow
= win
;
651 // set the same window for all nested sizers as well, they also are in the
653 for ( wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
655 node
= node
->GetNext() )
657 wxSizerItem
*const item
= node
->GetData();
658 wxSizer
*const sizer
= item
->GetSizer();
662 sizer
->SetContainingWindow(win
);
667 #if WXWIN_COMPATIBILITY_2_6
668 bool wxSizer::Remove( wxWindow
*window
)
670 return Detach( window
);
672 #endif // WXWIN_COMPATIBILITY_2_6
674 bool wxSizer::Remove( wxSizer
*sizer
)
676 wxASSERT_MSG( sizer
, wxT("Removing NULL sizer") );
678 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
681 wxSizerItem
*item
= node
->GetData();
683 if (item
->GetSizer() == sizer
)
686 m_children
.Erase( node
);
690 node
= node
->GetNext();
696 bool wxSizer::Remove( int index
)
698 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
700 wxT("Remove index is out of range") );
702 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
704 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
706 delete node
->GetData();
707 m_children
.Erase( node
);
712 bool wxSizer::Detach( wxSizer
*sizer
)
714 wxASSERT_MSG( sizer
, wxT("Detaching NULL sizer") );
716 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
719 wxSizerItem
*item
= node
->GetData();
721 if (item
->GetSizer() == sizer
)
725 m_children
.Erase( node
);
728 node
= node
->GetNext();
734 bool wxSizer::Detach( wxWindow
*window
)
736 wxASSERT_MSG( window
, wxT("Detaching NULL window") );
738 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
741 wxSizerItem
*item
= node
->GetData();
743 if (item
->GetWindow() == window
)
746 m_children
.Erase( node
);
749 node
= node
->GetNext();
755 bool wxSizer::Detach( int index
)
757 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
759 wxT("Detach index is out of range") );
761 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
763 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
765 wxSizerItem
*item
= node
->GetData();
767 if ( item
->IsSizer() )
771 m_children
.Erase( node
);
775 bool wxSizer::Replace( wxWindow
*oldwin
, wxWindow
*newwin
, bool recursive
)
777 wxASSERT_MSG( oldwin
, wxT("Replacing NULL window") );
778 wxASSERT_MSG( newwin
, wxT("Replacing with NULL window") );
780 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
783 wxSizerItem
*item
= node
->GetData();
785 if (item
->GetWindow() == oldwin
)
787 item
->AssignWindow(newwin
);
788 newwin
->SetContainingSizer( this );
791 else if (recursive
&& item
->IsSizer())
793 if (item
->GetSizer()->Replace( oldwin
, newwin
, true ))
797 node
= node
->GetNext();
803 bool wxSizer::Replace( wxSizer
*oldsz
, wxSizer
*newsz
, bool recursive
)
805 wxASSERT_MSG( oldsz
, wxT("Replacing NULL sizer") );
806 wxASSERT_MSG( newsz
, wxT("Replacing with NULL sizer") );
808 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
811 wxSizerItem
*item
= node
->GetData();
813 if (item
->GetSizer() == oldsz
)
815 item
->AssignSizer(newsz
);
818 else if (recursive
&& item
->IsSizer())
820 if (item
->GetSizer()->Replace( oldsz
, newsz
, true ))
824 node
= node
->GetNext();
830 bool wxSizer::Replace( size_t old
, wxSizerItem
*newitem
)
832 wxCHECK_MSG( old
< m_children
.GetCount(), false, wxT("Replace index is out of range") );
833 wxASSERT_MSG( newitem
, wxT("Replacing with NULL item") );
835 wxSizerItemList::compatibility_iterator node
= m_children
.Item( old
);
837 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
839 wxSizerItem
*item
= node
->GetData();
840 node
->SetData(newitem
);
846 void wxSizer::Clear( bool delete_windows
)
848 // First clear the ContainingSizer pointers
849 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
852 wxSizerItem
*item
= node
->GetData();
854 if (item
->IsWindow())
855 item
->GetWindow()->SetContainingSizer( NULL
);
856 node
= node
->GetNext();
859 // Destroy the windows if needed
863 // Now empty the list
864 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
867 void wxSizer::DeleteWindows()
869 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
872 wxSizerItem
*item
= node
->GetData();
874 item
->DeleteWindows();
875 node
= node
->GetNext();
879 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
881 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
883 // take the min size by default and limit it by max size
884 wxSize size
= GetMinClientSize(window
);
887 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
890 // hack for small screen devices where TLWs are always full screen
891 if ( tlw
->IsAlwaysMaximized() )
893 return tlw
->GetClientSize();
896 // limit the window to the size of the display it is on
897 int disp
= wxDisplay::GetFromWindow(window
);
898 if ( disp
== wxNOT_FOUND
)
900 // or, if we don't know which one it is, of the main one
904 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
906 // space for decorations and toolbars etc.
907 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
911 sizeMax
= GetMaxClientSize(window
);
914 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
916 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
922 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
924 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
926 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
929 wxSize
wxSizer::Fit( wxWindow
*window
)
931 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
934 window
->SetClientSize(ComputeFittingClientSize(window
));
936 // return entire size
937 return window
->GetSize();
940 void wxSizer::FitInside( wxWindow
*window
)
943 if (window
->IsTopLevel())
944 size
= VirtualFitSize( window
);
946 size
= GetMinClientSize( window
);
948 window
->SetVirtualSize( size
);
951 void wxSizer::Layout()
953 // (re)calculates minimums needed for each item and other preparations
957 // Applies the layout and repositions/resizes the items
961 void wxSizer::SetSizeHints( wxWindow
*window
)
963 // Preserve the window's max size hints, but set the
964 // lower bound according to the sizer calculations.
966 // This is equivalent to calling Fit(), except that we need to set
967 // the size hints _in between_ the two steps performed by Fit
968 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
969 // otherwise SetClientSize() could have no effect if there already are
970 // size hints in effect that forbid requested client size.
972 const wxSize clientSize
= ComputeFittingClientSize(window
);
974 window
->SetMinClientSize(clientSize
);
975 window
->SetClientSize(clientSize
);
978 #if WXWIN_COMPATIBILITY_2_8
979 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
983 #endif // WXWIN_COMPATIBILITY_2_8
985 // TODO on mac we need a function that determines how much free space this
986 // min size contains, in order to make sure that we have 20 pixels of free
987 // space around the controls
988 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
990 return window
->WindowToClientSize(window
->GetMaxSize());
993 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
995 return GetMinSize(); // Already returns client size.
998 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1000 wxSize size
= GetMinClientSize( window
);
1001 wxSize sizeMax
= GetMaxClientSize( window
);
1003 // Limit the size if sizeMax != wxDefaultSize
1005 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1007 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1013 wxSize
wxSizer::GetMinSize()
1015 wxSize
ret( CalcMin() );
1016 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1017 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1021 void wxSizer::DoSetMinSize( int width
, int height
)
1023 m_minSize
.x
= width
;
1024 m_minSize
.y
= height
;
1027 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1029 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1031 // Is it our immediate child?
1033 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1036 wxSizerItem
*item
= node
->GetData();
1038 if (item
->GetWindow() == window
)
1040 item
->SetMinSize( width
, height
);
1043 node
= node
->GetNext();
1046 // No? Search any subsizers we own then
1048 node
= m_children
.GetFirst();
1051 wxSizerItem
*item
= node
->GetData();
1053 if ( item
->GetSizer() &&
1054 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1056 // A child sizer found the requested windw, exit.
1059 node
= node
->GetNext();
1065 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1067 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1069 // Is it our immediate child?
1071 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1074 wxSizerItem
*item
= node
->GetData();
1076 if (item
->GetSizer() == sizer
)
1078 item
->GetSizer()->DoSetMinSize( width
, height
);
1081 node
= node
->GetNext();
1084 // No? Search any subsizers we own then
1086 node
= m_children
.GetFirst();
1089 wxSizerItem
*item
= node
->GetData();
1091 if ( item
->GetSizer() &&
1092 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1094 // A child found the requested sizer, exit.
1097 node
= node
->GetNext();
1103 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1105 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1107 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1109 wxSizerItem
*item
= node
->GetData();
1111 if (item
->GetSizer())
1113 // Sizers contains the minimal size in them, if not calculated ...
1114 item
->GetSizer()->DoSetMinSize( width
, height
);
1118 // ... but the minimal size of spacers and windows is stored via the item
1119 item
->SetMinSize( width
, height
);
1125 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1127 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1129 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1132 wxSizerItem
*item
= node
->GetData();
1134 if (item
->GetWindow() == window
)
1138 else if (recursive
&& item
->IsSizer())
1140 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1145 node
= node
->GetNext();
1151 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1153 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1155 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1158 wxSizerItem
*item
= node
->GetData();
1160 if (item
->GetSizer() == sizer
)
1164 else if (recursive
&& item
->IsSizer())
1166 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1171 node
= node
->GetNext();
1177 wxSizerItem
* wxSizer::GetItem( size_t index
)
1179 wxCHECK_MSG( index
< m_children
.GetCount(),
1181 wxT("GetItem index is out of range") );
1183 return m_children
.Item( index
)->GetData();
1186 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1188 // This gets a sizer item by the id of the sizer item
1189 // and NOT the id of a window if the item is a window.
1191 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1194 wxSizerItem
*item
= node
->GetData();
1196 if (item
->GetId() == id
)
1200 else if (recursive
&& item
->IsSizer())
1202 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1207 node
= node
->GetNext();
1213 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1215 wxSizerItem
*item
= GetItem( window
, recursive
);
1226 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1228 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1239 bool wxSizer::Show( size_t index
, bool show
)
1241 wxSizerItem
*item
= GetItem( index
);
1252 void wxSizer::ShowItems( bool show
)
1254 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1257 node
->GetData()->Show( show
);
1258 node
= node
->GetNext();
1262 bool wxSizer::IsShown( wxWindow
*window
) const
1264 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1267 wxSizerItem
*item
= node
->GetData();
1269 if (item
->GetWindow() == window
)
1271 return item
->IsShown();
1273 node
= node
->GetNext();
1276 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1281 bool wxSizer::IsShown( wxSizer
*sizer
) const
1283 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1286 wxSizerItem
*item
= node
->GetData();
1288 if (item
->GetSizer() == sizer
)
1290 return item
->IsShown();
1292 node
= node
->GetNext();
1295 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1300 bool wxSizer::IsShown( size_t index
) const
1302 wxCHECK_MSG( index
< m_children
.GetCount(),
1304 wxT("IsShown index is out of range") );
1306 return m_children
.Item( index
)->GetData()->IsShown();
1310 //---------------------------------------------------------------------------
1312 //---------------------------------------------------------------------------
1314 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1315 : m_rows( cols
== 0 ? 1 : 0 ),
1322 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1323 : m_rows( cols
== 0 ? 1 : 0 ),
1325 m_vgap( gap
.GetHeight() ),
1326 m_hgap( gap
.GetWidth() )
1330 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1331 : m_rows( rows
|| cols
? rows
: 1 ),
1338 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1339 : m_rows( rows
|| cols
? rows
: 1 ),
1341 m_vgap( gap
.GetHeight() ),
1342 m_hgap( gap
.GetWidth() )
1346 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1348 // if only the number of columns or the number of rows is specified for a
1349 // sizer, arbitrarily many items can be added to it but if both of them are
1350 // fixed, then the sizer can't have more than that many items -- check for
1351 // this here to ensure that we detect errors as soon as possible
1352 if ( m_cols
&& m_rows
)
1354 const int nitems
= m_children
.GetCount();
1355 if ( nitems
== m_cols
*m_rows
)
1359 "too many items (%d > %d*%d) in grid sizer (maybe you "
1360 "should omit the number of either rows or columns?)",
1361 nitems
+ 1, m_cols
, m_rows
)
1364 // additionally, continuing to use the specified number of columns
1365 // and rows is not a good idea as callers of CalcRowsCols() expect
1366 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1367 // which is not the case if there are too many items and results in
1368 // crashes, so let it compute the number of rows automatically by
1369 // forgetting the (wrong) number of rows specified (this also has a
1370 // nice side effect of giving only one assert even if there are
1371 // many more items than allowed in this sizer)
1376 return wxSizer::DoInsert(index
, item
);
1379 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1381 const int nitems
= m_children
.GetCount();
1383 ncols
= GetEffectiveColsCount();
1384 nrows
= GetEffectiveRowsCount();
1386 // Since Insert() checks for overpopulation, the following
1387 // should only assert if the grid was shrunk via SetRows() / SetCols()
1388 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1393 void wxGridSizer::RecalcSizes()
1395 int nitems
, nrows
, ncols
;
1396 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1399 wxSize
sz( GetSize() );
1400 wxPoint
pt( GetPosition() );
1402 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1403 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1406 for (int c
= 0; c
< ncols
; c
++)
1409 for (int r
= 0; r
< nrows
; r
++)
1411 int i
= r
* ncols
+ c
;
1414 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1416 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1418 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1426 wxSize
wxGridSizer::CalcMin()
1429 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1432 // Find the max width and height for any component
1436 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1439 wxSizerItem
*item
= node
->GetData();
1440 wxSize
sz( item
->CalcMin() );
1442 w
= wxMax( w
, sz
.x
);
1443 h
= wxMax( h
, sz
.y
);
1445 node
= node
->GetNext();
1448 // In case we have a nested sizer with a two step algo , give it
1449 // a chance to adjust to that (we give it width component)
1450 node
= m_children
.GetFirst();
1451 bool didChangeMinSize
= false;
1454 wxSizerItem
*item
= node
->GetData();
1455 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1457 node
= node
->GetNext();
1460 // And redo iteration in case min size changed
1461 if( didChangeMinSize
)
1463 node
= m_children
.GetFirst();
1467 wxSizerItem
*item
= node
->GetData();
1468 wxSize
sz( item
->GetMinSizeWithBorder() );
1470 w
= wxMax( w
, sz
.x
);
1471 h
= wxMax( h
, sz
.y
);
1473 node
= node
->GetNext();
1477 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1478 nrows
* h
+ (nrows
-1) * m_vgap
);
1481 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1484 wxSize
sz( item
->GetMinSizeWithBorder() );
1485 int flag
= item
->GetFlag();
1487 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1493 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1495 pt
.x
= x
+ (w
- sz
.x
) / 2;
1497 else if (flag
& wxALIGN_RIGHT
)
1499 pt
.x
= x
+ (w
- sz
.x
);
1502 if (flag
& wxALIGN_CENTER_VERTICAL
)
1504 pt
.y
= y
+ (h
- sz
.y
) / 2;
1506 else if (flag
& wxALIGN_BOTTOM
)
1508 pt
.y
= y
+ (h
- sz
.y
);
1512 item
->SetDimension(pt
, sz
);
1515 //---------------------------------------------------------------------------
1517 //---------------------------------------------------------------------------
1519 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1520 : wxGridSizer( cols
, vgap
, hgap
),
1521 m_flexDirection(wxBOTH
),
1522 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1526 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1527 : wxGridSizer( cols
, gap
),
1528 m_flexDirection(wxBOTH
),
1529 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1533 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1534 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1535 m_flexDirection(wxBOTH
),
1536 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1540 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1541 : wxGridSizer( rows
, cols
, gap
),
1542 m_flexDirection(wxBOTH
),
1543 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1547 wxFlexGridSizer::~wxFlexGridSizer()
1551 void wxFlexGridSizer::RecalcSizes()
1554 if ( !CalcRowsCols(nrows
, ncols
) )
1557 const wxPoint
pt(GetPosition());
1558 const wxSize
sz(GetSize());
1560 AdjustForGrowables(sz
);
1562 wxSizerItemList::const_iterator i
= m_children
.begin();
1563 const wxSizerItemList::const_iterator end
= m_children
.end();
1566 for ( int r
= 0; r
< nrows
; r
++ )
1568 if ( m_rowHeights
[r
] == -1 )
1570 // this row is entirely hidden, skip it
1571 for ( int c
= 0; c
< ncols
; c
++ )
1582 const int hrow
= m_rowHeights
[r
];
1583 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1588 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1590 const int wcol
= m_colWidths
[c
];
1595 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1599 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1611 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1612 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1614 // Sum total minimum size, including gaps between rows/columns.
1615 // -1 is used as a magic number meaning empty row/column.
1618 const size_t count
= sizes
.size();
1619 for ( size_t n
= 0; n
< count
; n
++ )
1621 if ( sizes
[n
] != -1 )
1624 total
+= gap
; // separate from the previous column
1633 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1635 // We have to recalculate the sizes in case the item minimum size has
1636 // changed since the previous layout, or the item has been hidden using
1637 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1638 // dimension of the row/column will be -1, indicating that the column
1639 // itself is hidden.
1640 m_rowHeights
.assign(nrows
, -1);
1641 m_colWidths
.assign(ncols
, -1);
1643 // n is the index of the item in left-to-right top-to-bottom order
1645 for ( wxSizerItemList::iterator i
= m_children
.begin();
1646 i
!= m_children
.end();
1649 wxSizerItem
* const item
= *i
;
1650 if ( item
->IsShown() )
1652 // NOTE: Not doing the calculation here, this is just
1653 // for finding max values.
1654 const wxSize
sz(item
->GetMinSizeWithBorder());
1656 const int row
= n
/ ncols
;
1657 const int col
= n
% ncols
;
1659 if ( sz
.y
> m_rowHeights
[row
] )
1660 m_rowHeights
[row
] = sz
.y
;
1661 if ( sz
.x
> m_colWidths
[col
] )
1662 m_colWidths
[col
] = sz
.x
;
1666 AdjustForFlexDirection();
1668 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1669 SumArraySizes(m_rowHeights
, m_vgap
));
1672 wxSize
wxFlexGridSizer::CalcMin()
1677 // Number of rows/columns can change as items are added or removed.
1678 if ( !CalcRowsCols(nrows
, ncols
) )
1682 // We have to recalculate the sizes in case the item minimum size has
1683 // changed since the previous layout, or the item has been hidden using
1684 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1685 // dimension of the row/column will be -1, indicating that the column
1686 // itself is hidden.
1687 m_rowHeights
.assign(nrows
, -1);
1688 m_colWidths
.assign(ncols
, -1);
1690 for ( wxSizerItemList::iterator i
= m_children
.begin();
1691 i
!= m_children
.end();
1694 wxSizerItem
* const item
= *i
;
1695 if ( item
->IsShown() )
1701 // The stage of looking for max values in each row/column has been
1702 // made a separate function, since it's reused in AdjustForGrowables.
1703 FindWidthsAndHeights(nrows
,ncols
);
1705 return m_calculatedMinSize
;
1708 void wxFlexGridSizer::AdjustForFlexDirection()
1710 // the logic in CalcMin works when we resize flexibly in both directions
1711 // but maybe this is not the case
1712 if ( m_flexDirection
!= wxBOTH
)
1714 // select the array corresponding to the direction in which we do *not*
1716 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1719 const size_t count
= array
.GetCount();
1721 // find the largest value in this array
1725 for ( n
= 0; n
< count
; ++n
)
1727 if ( array
[n
] > largest
)
1731 // and now fill it with the largest value
1732 for ( n
= 0; n
< count
; ++n
)
1734 // don't touch hidden rows
1735 if ( array
[n
] != -1 )
1741 // helper of AdjustForGrowables() which is called for rows/columns separately
1744 // delta: the extra space, we do nothing unless it's positive
1745 // growable: indices or growable rows/cols in sizes array
1746 // sizes: the height/widths of rows/cols to adjust
1747 // proportions: proportions of the growable rows/cols or NULL if they all
1748 // should be assumed to have proportion of 1
1750 DoAdjustForGrowables(int delta
,
1751 const wxArrayInt
& growable
,
1753 const wxArrayInt
*proportions
)
1758 // total sum of proportions of all non-hidden rows
1759 int sum_proportions
= 0;
1761 // number of currently shown growable rows
1764 const int max_idx
= sizes
.size();
1766 const size_t count
= growable
.size();
1768 for ( idx
= 0; idx
< count
; idx
++ )
1770 // Since the number of rows/columns can change as items are
1771 // inserted/deleted, we need to verify at runtime that the
1772 // requested growable rows/columns are still valid.
1773 if ( growable
[idx
] >= max_idx
)
1776 // If all items in a row/column are hidden, that row/column will
1777 // have a dimension of -1. This causes the row/column to be
1778 // hidden completely.
1779 if ( sizes
[growable
[idx
]] == -1 )
1783 sum_proportions
+= (*proportions
)[idx
];
1791 // the remaining extra free space, adjusted during each iteration
1792 for ( idx
= 0; idx
< count
; idx
++ )
1794 if ( growable
[idx
] >= max_idx
)
1797 if ( sizes
[ growable
[idx
] ] == -1 )
1801 if ( sum_proportions
== 0 )
1803 // no growable rows -- divide extra space evenly among all
1804 cur_delta
= delta
/num
;
1807 else // allocate extra space proportionally
1809 const int cur_prop
= (*proportions
)[idx
];
1810 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1811 sum_proportions
-= cur_prop
;
1814 sizes
[growable
[idx
]] += cur_delta
;
1819 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1822 // by the time this function is called, the sizer should be already fully
1823 // initialized and hence the number of its columns and rows is known and we
1824 // can check that all indices in m_growableCols/Rows are valid (see also
1825 // comments in AddGrowableCol/Row())
1826 if ( !m_rows
|| !m_cols
)
1830 int nrows
= CalcRows();
1832 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1834 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1835 "invalid growable row index" );
1841 int ncols
= CalcCols();
1843 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1845 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1846 "invalid growable column index" );
1850 #endif // wxDEBUG_LEVEL
1853 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1855 DoAdjustForGrowables
1857 sz
.x
- m_calculatedMinSize
.x
,
1860 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1864 // This gives nested objects that benefit from knowing one size
1865 // component in advance the chance to use that.
1866 bool didAdjustMinSize
= false;
1868 // Iterate over all items and inform about column width
1869 const int ncols
= GetEffectiveColsCount();
1871 for ( wxSizerItemList::iterator i
= m_children
.begin();
1872 i
!= m_children
.end();
1875 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1876 if ( ++col
== ncols
)
1880 // Only redo if info was actually used
1881 if( didAdjustMinSize
)
1883 DoAdjustForGrowables
1885 sz
.x
- m_calculatedMinSize
.x
,
1888 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1894 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1896 // pass NULL instead of proportions if the grow mode is ALL as we
1897 // should treat all rows as having proportion of 1 then
1898 DoAdjustForGrowables
1900 sz
.y
- m_calculatedMinSize
.y
,
1903 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1909 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1911 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1914 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1916 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1919 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1921 wxASSERT_MSG( !IsRowGrowable( idx
),
1922 "AddGrowableRow() called for growable row" );
1924 // notice that we intentionally don't check the index validity here in (the
1925 // common) case when the number of rows was not specified in the ctor -- in
1926 // this case it will be computed only later, when all items are added to
1927 // the sizer, and the check will be done in AdjustForGrowables()
1928 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1930 m_growableRows
.Add( idx
);
1931 m_growableRowsProportions
.Add( proportion
);
1934 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1936 wxASSERT_MSG( !IsColGrowable( idx
),
1937 "AddGrowableCol() called for growable column" );
1939 // see comment in AddGrowableRow(): although it's less common to omit the
1940 // specification of the number of columns, it still can also happen
1941 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1943 m_growableCols
.Add( idx
);
1944 m_growableColsProportions
.Add( proportion
);
1947 // helper function for RemoveGrowableCol/Row()
1949 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1951 const size_t count
= items
.size();
1952 for ( size_t n
= 0; n
< count
; n
++ )
1954 if ( (size_t)items
[n
] == idx
)
1957 proportions
.RemoveAt(n
);
1962 wxFAIL_MSG( wxT("column/row is already not growable") );
1965 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
1967 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
1970 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
1972 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
1975 //---------------------------------------------------------------------------
1977 //---------------------------------------------------------------------------
1979 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
1981 return IsVertical() ? Add(0, size
) : Add(size
, 0);
1988 Helper of RecalcSizes(): checks if there is enough remaining space for the
1989 min size of the given item and returns its min size or the entire remaining
1990 space depending on which one is greater.
1992 This function updates the remaining space parameter to account for the size
1993 effectively allocated to the item.
1996 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
1998 int& remainingSpace
= *remainingSpace_
;
2001 if ( remainingSpace
> 0 )
2003 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2004 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2006 if ( size
>= remainingSpace
)
2008 // truncate the item to fit in the remaining space, this is better
2009 // than showing it only partially in general, even if both choices
2010 // are bad -- but there is nothing else we can do
2011 size
= remainingSpace
;
2014 remainingSpace
-= size
;
2016 else // no remaining space
2018 // no space at all left, no need to even query the item for its min
2019 // size as we can't give it to it anyhow
2026 } // anonymous namespace
2028 void wxBoxSizer::RecalcSizes()
2030 if ( m_children
.empty() )
2033 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2034 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2036 // the amount of free space which we should redistribute among the
2037 // stretchable items (i.e. those with non zero proportion)
2038 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2040 // declare loop variables used below:
2041 wxSizerItemList::const_iterator i
; // iterator in m_children list
2042 unsigned n
= 0; // item index in majorSizes array
2045 // First, inform item about the available size in minor direction as this
2046 // can change their size in the major direction. Also compute the number of
2047 // visible items and sum of their min sizes in major direction.
2049 int minMajorSize
= 0;
2050 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2052 wxSizerItem
* const item
= *i
;
2054 if ( !item
->IsShown() )
2057 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2058 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2059 wxSize szMin
= item
->GetMinSizeWithBorder();
2060 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2063 // Since we passed available space along to the item, it should not
2064 // take too much, so delta should not become negative.
2065 delta
-= deltaChange
;
2067 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2070 // update our min size and delta which may have changed
2071 SizeInMajorDir(m_minSize
) = minMajorSize
;
2072 delta
= totalMajorSize
- minMajorSize
;
2075 // space and sum of proportions for the remaining items, both may change
2077 wxCoord remaining
= totalMajorSize
;
2078 int totalProportion
= m_totalProportion
;
2080 // size of the (visible) items in major direction, -1 means "not fixed yet"
2081 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2084 // Check for the degenerated case when we don't have enough space for even
2085 // the min sizes of all the items: in this case we really can't do much
2086 // more than to to allocate the min size to as many of fixed size items as
2087 // possible (on the assumption that variable size items such as text zones
2088 // or list boxes may use scrollbars to show their content even if their
2089 // size is less than min size but that fixed size items such as buttons
2090 // will suffer even more if we don't give them their min size)
2091 if ( totalMajorSize
< minMajorSize
)
2093 // Second degenerated case pass: allocate min size to all fixed size
2095 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2097 wxSizerItem
* const item
= *i
;
2099 if ( !item
->IsShown() )
2102 // deal with fixed size items only during this pass
2103 if ( item
->GetProportion() )
2106 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2110 // Third degenerated case pass: allocate min size to all the remaining,
2111 // i.e. non-fixed size, items.
2112 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2114 wxSizerItem
* const item
= *i
;
2116 if ( !item
->IsShown() )
2119 // we've already dealt with fixed size items above
2120 if ( !item
->GetProportion() )
2123 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2126 else // we do have enough space to give at least min sizes to all items
2128 // Second and maybe more passes in the non-degenerated case: deal with
2129 // fixed size items and items whose min size is greater than what we
2130 // would allocate to them taking their proportion into account. For
2131 // both of them, we will just use their min size, but for the latter we
2132 // also need to reexamine all the items as the items which fitted
2133 // before we adjusted their size upwards might not fit any more. This
2134 // does make for a quadratic algorithm but it's not obvious how to
2135 // avoid it and hopefully it's not a huge problem in practice as the
2136 // sizers don't have many items usually (and, of course, the algorithm
2137 // still reduces into a linear one if there is enough space for all the
2139 bool nonFixedSpaceChanged
= false;
2140 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2142 if ( nonFixedSpaceChanged
)
2144 i
= m_children
.begin();
2146 nonFixedSpaceChanged
= false;
2149 // check for the end of the loop only after the check above as
2150 // otherwise we wouldn't do another pass if the last child resulted
2151 // in non fixed space reduction
2152 if ( i
== m_children
.end() )
2155 wxSizerItem
* const item
= *i
;
2157 if ( !item
->IsShown() )
2160 // don't check the item which we had already dealt with during a
2161 // previous pass (this is more than an optimization, the code
2162 // wouldn't work correctly if we kept adjusting for the same item
2163 // over and over again)
2164 if ( majorSizes
[n
] != wxDefaultCoord
)
2167 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2169 // it doesn't make sense for min size to be negative but right now
2170 // it's possible to create e.g. a spacer with (-1, 10) as size and
2171 // people do it in their code apparently (see #11842) so ensure
2172 // that we don't use this -1 as real min size as it conflicts with
2173 // the meaning we use for it here and negative min sizes just don't
2174 // make sense anyhow (which is why it might be a better idea to
2175 // deal with them at wxSizerItem level in the future but for now
2176 // this is the minimal fix for the bug)
2180 const int propItem
= item
->GetProportion();
2183 // is the desired size of this item big enough?
2184 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2186 // yes, it is, we'll determine the real size of this
2187 // item later, for now just leave it as wxDefaultCoord
2191 // the proportion of this item won't count, it has
2192 // effectively become fixed
2193 totalProportion
-= propItem
;
2196 // we can already allocate space for this item
2197 majorSizes
[n
] = minMajor
;
2199 // change the amount of the space remaining to the other items,
2200 // as this can result in not being able to satisfy their
2201 // proportions any more we will need to redo another loop
2203 remaining
-= minMajor
;
2205 nonFixedSpaceChanged
= true;
2209 // Last by one pass: distribute the remaining space among the non-fixed
2210 // items whose size weren't fixed yet according to their proportions.
2211 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2213 wxSizerItem
* const item
= *i
;
2215 if ( !item
->IsShown() )
2218 if ( majorSizes
[n
] == wxDefaultCoord
)
2220 const int propItem
= item
->GetProportion();
2221 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2223 remaining
-= majorSizes
[n
];
2224 totalProportion
-= propItem
;
2230 // the position at which we put the next child
2231 wxPoint
pt(m_position
);
2234 // Final pass: finally do position the items correctly using their sizes as
2235 // determined above.
2236 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2238 wxSizerItem
* const item
= *i
;
2240 if ( !item
->IsShown() )
2243 const int majorSize
= majorSizes
[n
];
2245 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2247 // apply the alignment in the minor direction
2248 wxPoint
posChild(pt
);
2250 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2251 const int flag
= item
->GetFlag();
2252 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2254 // occupy all the available space if wxEXPAND was given and also if
2255 // the item is too big to fit -- in this case we truncate it below
2256 // its minimal size which is bad but better than not showing parts
2257 // of the window at all
2258 minorSize
= totalMinorSize
;
2260 else if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2262 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2264 // NB: wxCENTRE is used here only for backwards compatibility,
2265 // wxALIGN_CENTRE should be used in new code
2266 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2267 : wxALIGN_CENTRE_VERTICAL
)) )
2269 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2273 // apply RTL adjustment for horizontal sizers:
2274 if ( !IsVertical() && m_containingWindow
)
2276 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2284 // finally set size of this child and advance to the next one
2285 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2287 PosInMajorDir(pt
) += majorSize
;
2291 wxSize
wxBoxSizer::CalcMin()
2293 m_totalProportion
= 0;
2294 m_minSize
= wxSize(0, 0);
2296 // The minimal size for the sizer should be big enough to allocate its
2297 // element at least its minimal size but also, and this is the non trivial
2298 // part, to respect the children proportion. To satisfy the latter
2299 // condition we must find the greatest min-size-to-proportion ratio for all
2300 // elements with non-zero proportion.
2301 float maxMinSizeToProp
= 0.;
2302 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2303 i
!= m_children
.end();
2306 wxSizerItem
* const item
= *i
;
2308 if ( !item
->IsShown() )
2311 const wxSize sizeMinThis
= item
->CalcMin();
2312 if ( const int propThis
= item
->GetProportion() )
2314 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2315 minSizeToProp
/= propThis
;
2317 if ( minSizeToProp
> maxMinSizeToProp
)
2318 maxMinSizeToProp
= minSizeToProp
;
2320 m_totalProportion
+= item
->GetProportion();
2322 else // fixed size item
2324 // Just account for its size directly
2325 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2328 // In the transversal direction we just need to find the maximum.
2329 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2330 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2333 // Using the max ratio ensures that the min size is big enough for all
2334 // items to have their min size and satisfy the proportions among them.
2335 SizeInMajorDir(m_minSize
) += maxMinSizeToProp
*m_totalProportion
;
2340 //---------------------------------------------------------------------------
2342 //---------------------------------------------------------------------------
2346 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2347 : wxBoxSizer( orient
),
2350 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2352 // do this so that our Detach() is called if the static box is destroyed
2354 m_staticBox
->SetContainingSizer(this);
2357 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2358 : wxBoxSizer(orient
),
2359 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2362 m_staticBox
->SetContainingSizer(this);
2365 wxStaticBoxSizer::~wxStaticBoxSizer()
2370 void wxStaticBoxSizer::RecalcSizes()
2372 int top_border
, other_border
;
2373 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2375 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2377 wxSize
old_size( m_size
);
2378 m_size
.x
-= 2*other_border
;
2379 m_size
.y
-= top_border
+ other_border
;
2381 wxPoint
old_pos( m_position
);
2382 if (m_staticBox
->GetChildren().GetCount() > 0)
2384 #if defined( __WXGTK20__ )
2385 // if the wxStaticBox has created a wxPizza to contain its children
2386 // (see wxStaticBox::AddChild) then we need to place the items it contains
2387 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2388 // to the top-left corner of the staticbox:
2389 m_position
.x
= m_position
.y
= 0;
2391 // if the wxStaticBox has childrens, then these windows must be placed
2392 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2393 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2394 // to keep in count the static borders here!):
2395 m_position
.x
= other_border
;
2396 m_position
.y
= top_border
;
2401 // the windows contained in the staticbox have been created as siblings of the
2402 // staticbox (this is the "old" way of staticbox contents creation); in this
2403 // case we need to position them with coordinates relative to our common parent
2404 m_position
.x
+= other_border
;
2405 m_position
.y
+= top_border
;
2408 wxBoxSizer::RecalcSizes();
2410 m_position
= old_pos
;
2414 wxSize
wxStaticBoxSizer::CalcMin()
2416 int top_border
, other_border
;
2417 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2419 wxSize
ret( wxBoxSizer::CalcMin() );
2420 ret
.x
+= 2*other_border
;
2422 // ensure that we're wide enough to show the static box label (there is no
2423 // need to check for the static box best size in vertical direction though)
2424 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2425 if ( ret
.x
< boxWidth
)
2428 ret
.y
+= other_border
+ top_border
;
2433 void wxStaticBoxSizer::ShowItems( bool show
)
2435 m_staticBox
->Show( show
);
2436 wxBoxSizer::ShowItems( show
);
2439 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2441 // avoid deleting m_staticBox in our dtor if it's being detached from the
2442 // sizer (which can happen because it's being already destroyed for
2444 if ( window
== m_staticBox
)
2450 return wxSizer::Detach( window
);
2453 #endif // wxUSE_STATBOX
2455 //---------------------------------------------------------------------------
2456 // wxStdDialogButtonSizer
2457 //---------------------------------------------------------------------------
2461 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2462 : wxBoxSizer(wxHORIZONTAL
)
2464 // Vertical buttons with lots of space on either side
2465 // looks rubbish on WinCE, so let's not do this for now.
2466 // If we are going to use vertical buttons, we should
2467 // put the sizer to the right of other controls in the dialog,
2468 // and that's beyond the scope of this sizer.
2470 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2471 // If we have a PDA screen, put yes/no button over
2472 // all other buttons, otherwise on the left side.
2474 m_orient
= wxVERTICAL
;
2477 m_buttonAffirmative
= NULL
;
2478 m_buttonApply
= NULL
;
2479 m_buttonNegative
= NULL
;
2480 m_buttonCancel
= NULL
;
2481 m_buttonHelp
= NULL
;
2484 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2486 switch (mybutton
->GetId())
2491 m_buttonAffirmative
= mybutton
;
2494 m_buttonApply
= mybutton
;
2497 m_buttonNegative
= mybutton
;
2501 m_buttonCancel
= mybutton
;
2504 case wxID_CONTEXT_HELP
:
2505 m_buttonHelp
= mybutton
;
2512 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2514 m_buttonAffirmative
= button
;
2517 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2519 m_buttonNegative
= button
;
2522 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2524 m_buttonCancel
= button
;
2527 void wxStdDialogButtonSizer::Realize()
2530 Add(0, 0, 0, wxLEFT
, 6);
2532 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2534 if (m_buttonNegative
){
2535 // HIG POLICE BULLETIN - destructive buttons need extra padding
2536 // 24 pixels on either side
2537 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2540 // extra whitespace between help/negative and cancel/ok buttons
2541 Add(0, 0, 1, wxEXPAND
, 0);
2543 if (m_buttonCancel
){
2544 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2545 // Cancel or help should be default
2546 // m_buttonCancel->SetDefaultButton();
2549 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2550 // figure the best place is between Cancel and OK
2552 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2554 if (m_buttonAffirmative
){
2555 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2557 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2558 // these buttons have set labels under Mac so we should use them
2559 m_buttonAffirmative
->SetLabel(_("Save"));
2560 if (m_buttonNegative
)
2561 m_buttonNegative
->SetLabel(_("Don't Save"));
2565 // Extra space around and at the right
2567 #elif defined(__WXGTK20__)
2568 Add(0, 0, 0, wxLEFT
, 9);
2570 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 3);
2572 // extra whitespace between help and cancel/ok buttons
2573 Add(0, 0, 1, wxEXPAND
, 0);
2575 if (m_buttonNegative
){
2576 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 3);
2579 // according to HIG, in explicit apply windows the order is:
2580 // [ Help Apply Cancel OK ]
2582 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 3);
2584 if (m_buttonCancel
){
2585 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 3);
2586 // Cancel or help should be default
2587 // m_buttonCancel->SetDefaultButton();
2590 if (m_buttonAffirmative
)
2591 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2592 #elif defined(__WXMSW__)
2595 // right-justify buttons
2596 Add(0, 0, 1, wxEXPAND
, 0);
2598 if (m_buttonAffirmative
){
2599 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2602 if (m_buttonNegative
){
2603 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2606 if (m_buttonCancel
){
2607 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2610 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2613 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2615 // GTK+1 and any other platform
2617 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2619 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2621 // extra whitespace between help and cancel/ok buttons
2622 Add(0, 0, 1, wxEXPAND
, 0);
2625 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2627 if (m_buttonAffirmative
){
2628 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2631 if (m_buttonNegative
){
2632 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2635 if (m_buttonCancel
){
2636 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2637 // Cancel or help should be default
2638 // m_buttonCancel->SetDefaultButton();
2644 #endif // wxUSE_BUTTON