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
8 // Copyright: (c) Robin Dunn, Robert Roebling
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
20 #include "wx/private/flagscheck.h"
23 #include "wx/string.h"
27 #include "wx/settings.h"
28 #include "wx/button.h"
29 #include "wx/statbox.h"
30 #include "wx/toplevel.h"
33 #include "wx/display.h"
34 #include "wx/vector.h"
35 #include "wx/listimpl.cpp"
38 //---------------------------------------------------------------------------
40 IMPLEMENT_CLASS(wxSizerItem
, wxObject
)
41 IMPLEMENT_CLASS(wxSizer
, wxObject
)
42 IMPLEMENT_CLASS(wxGridSizer
, wxSizer
)
43 IMPLEMENT_CLASS(wxFlexGridSizer
, wxGridSizer
)
44 IMPLEMENT_CLASS(wxBoxSizer
, wxSizer
)
46 IMPLEMENT_CLASS(wxStaticBoxSizer
, wxBoxSizer
)
49 IMPLEMENT_CLASS(wxStdDialogButtonSizer
, wxBoxSizer
)
52 WX_DEFINE_EXPORTED_LIST( wxSizerItemList
)
87 // ----------------------------------------------------------------------------
89 // ----------------------------------------------------------------------------
91 // check for flags conflicts
92 static const int SIZER_FLAGS_MASK
=
94 wxADD_FLAG(wxHORIZONTAL
,
95 wxADD_FLAG(wxVERTICAL
,
100 wxADD_FLAG(wxALIGN_NOT
,
101 wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL
,
102 wxADD_FLAG(wxALIGN_RIGHT
,
103 wxADD_FLAG(wxALIGN_BOTTOM
,
104 wxADD_FLAG(wxALIGN_CENTER_VERTICAL
,
105 wxADD_FLAG(wxFIXED_MINSIZE
,
106 wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN
,
107 wxADD_FLAG(wxSTRETCH_NOT
,
113 #define ASSERT_VALID_SIZER_FLAGS(f) wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK)
116 void wxSizerItem::Init(const wxSizerFlags
& flags
)
120 m_proportion
= flags
.GetProportion();
121 m_flag
= flags
.GetFlags();
122 m_border
= flags
.GetBorderInPixels();
124 ASSERT_VALID_SIZER_FLAGS( m_flag
);
127 wxSizerItem::wxSizerItem()
138 void wxSizerItem::DoSetWindow(wxWindow
*window
)
140 wxCHECK_RET( window
, wxT("NULL window in wxSizerItem::SetWindow()") );
142 m_kind
= Item_Window
;
145 // window doesn't become smaller than its initial size, whatever happens
146 m_minSize
= window
->GetSize();
148 if ( m_flag
& wxFIXED_MINSIZE
)
149 window
->SetMinSize(m_minSize
);
151 // aspect ratio calculated from initial size
155 wxSizerItem::wxSizerItem(wxWindow
*window
,
161 m_proportion(proportion
),
167 ASSERT_VALID_SIZER_FLAGS( m_flag
);
173 void wxSizerItem::DoSetSizer(wxSizer
*sizer
)
179 wxSizerItem::wxSizerItem(wxSizer
*sizer
,
186 m_proportion(proportion
),
193 ASSERT_VALID_SIZER_FLAGS( m_flag
);
197 // m_minSize is set later
201 void wxSizerItem::DoSetSpacer(const wxSize
& size
)
203 m_kind
= Item_Spacer
;
204 m_spacer
= new wxSizerSpacer(size
);
209 wxSize
wxSizerItem::AddBorderToSize(const wxSize
& size
) const
211 wxSize result
= size
;
213 // Notice that we shouldn't modify the unspecified component(s) of the
214 // size, it's perfectly valid to have either min or max size specified in
215 // one direction only and it shouldn't be applied in the other one then.
217 if ( result
.x
!= wxDefaultCoord
)
220 result
.x
+= m_border
;
222 result
.x
+= m_border
;
225 if ( result
.y
!= wxDefaultCoord
)
227 if (m_flag
& wxNORTH
)
228 result
.y
+= m_border
;
229 if (m_flag
& wxSOUTH
)
230 result
.y
+= m_border
;
236 wxSizerItem::wxSizerItem(int width
,
244 m_minSize(width
, height
), // minimal size is the initial size
245 m_proportion(proportion
),
251 ASSERT_VALID_SIZER_FLAGS( m_flag
);
253 DoSetSpacer(wxSize(width
, height
));
256 wxSizerItem::~wxSizerItem()
262 void wxSizerItem::Free()
270 m_window
->SetContainingSizer(NULL
);
283 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
289 wxSize
wxSizerItem::GetSpacer() const
292 if ( m_kind
== Item_Spacer
)
293 size
= m_spacer
->GetSize();
299 wxSize
wxSizerItem::GetSize() const
308 ret
= m_window
->GetSize();
312 ret
= m_sizer
->GetSize();
316 ret
= m_spacer
->GetSize();
321 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
328 if (m_flag
& wxNORTH
)
330 if (m_flag
& wxSOUTH
)
336 bool wxSizerItem::InformFirstDirection(int direction
, int size
, int availableOtherDir
)
338 // The size that come here will be including borders. Child items should get it
342 if( direction
==wxHORIZONTAL
)
349 else if( direction
==wxVERTICAL
)
351 if (m_flag
& wxNORTH
)
353 if (m_flag
& wxSOUTH
)
359 // Pass the information along to the held object
362 didUse
= GetSizer()->InformFirstDirection(direction
,size
,availableOtherDir
);
364 m_minSize
= GetSizer()->CalcMin();
368 didUse
= GetWindow()->InformFirstDirection(direction
,size
,availableOtherDir
);
370 m_minSize
= m_window
->GetEffectiveMinSize();
372 // This information is useful for items with wxSHAPED flag, since
373 // we can request an optimal min size for such an item. Even if
374 // we overwrite the m_minSize member here, we can read it back from
375 // the owned window (happens automatically).
376 if( (m_flag
& wxSHAPED
) && (m_flag
& wxEXPAND
) && direction
)
378 if( !wxIsNullDouble(m_ratio
) )
380 wxCHECK_MSG( (m_proportion
==0), false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
381 if( direction
==wxHORIZONTAL
&& !wxIsNullDouble(m_ratio
) )
383 // Clip size so that we don't take too much
384 if( availableOtherDir
>=0 && int(size
/m_ratio
)-m_minSize
.y
>availableOtherDir
)
385 size
= int((availableOtherDir
+m_minSize
.y
)*m_ratio
);
386 m_minSize
= wxSize(size
,int(size
/m_ratio
));
388 else if( direction
==wxVERTICAL
)
390 // Clip size so that we don't take too much
391 if( availableOtherDir
>=0 && int(size
*m_ratio
)-m_minSize
.x
>availableOtherDir
)
392 size
= int((availableOtherDir
+m_minSize
.x
)/m_ratio
);
393 m_minSize
= wxSize(int(size
*m_ratio
),size
);
403 wxSize
wxSizerItem::CalcMin()
407 m_minSize
= m_sizer
->GetMinSize();
409 // if we have to preserve aspect ratio _AND_ this is
410 // the first-time calculation, consider ret to be initial size
411 if ( (m_flag
& wxSHAPED
) && wxIsNullDouble(m_ratio
) )
414 else if ( IsWindow() )
416 // Since the size of the window may change during runtime, we
417 // should use the current minimal/best size.
418 m_minSize
= m_window
->GetEffectiveMinSize();
421 return GetMinSizeWithBorder();
424 wxSize
wxSizerItem::GetMinSizeWithBorder() const
426 return AddBorderToSize(m_minSize
);
429 wxSize
wxSizerItem::GetMaxSizeWithBorder() const
431 return AddBorderToSize(GetMaxSize());
434 void wxSizerItem::SetDimension( const wxPoint
& pos_
, const wxSize
& size_
)
438 if (m_flag
& wxSHAPED
)
440 // adjust aspect ratio
441 int rwidth
= (int) (size
.y
* m_ratio
);
445 int rheight
= (int) (size
.x
/ m_ratio
);
446 // add vertical space
447 if (m_flag
& wxALIGN_CENTER_VERTICAL
)
448 pos
.y
+= (size
.y
- rheight
) / 2;
449 else if (m_flag
& wxALIGN_BOTTOM
)
450 pos
.y
+= (size
.y
- rheight
);
451 // use reduced dimensions
454 else if (rwidth
< size
.x
)
456 // add horizontal space
457 if (m_flag
& wxALIGN_CENTER_HORIZONTAL
)
458 pos
.x
+= (size
.x
- rwidth
) / 2;
459 else if (m_flag
& wxALIGN_RIGHT
)
460 pos
.x
+= (size
.x
- rwidth
);
465 // This is what GetPosition() returns. Since we calculate
466 // borders afterwards, GetPosition() will be the left/top
467 // corner of the surrounding border.
479 if (m_flag
& wxNORTH
)
484 if (m_flag
& wxSOUTH
)
494 m_rect
= wxRect(pos
, size
);
499 wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
504 // Use wxSIZE_FORCE_EVENT here since a sizer item might
505 // have changed alignment or some other property which would
506 // not change the size of the window. In such a case, no
507 // wxSizeEvent would normally be generated and thus the
508 // control wouldn't get laid out correctly here.
510 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
511 wxSIZE_ALLOW_MINUS_ONE
|wxSIZE_FORCE_EVENT
);
513 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
514 wxSIZE_ALLOW_MINUS_ONE
);
519 m_sizer
->SetDimension(pos
, size
);
523 m_spacer
->SetSize(size
);
528 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
532 void wxSizerItem::DeleteWindows()
541 //We are deleting the window from this sizer - normally
542 //the window destroys the sizer associated with it,
543 //which might destroy this, which we don't want
544 m_window
->SetContainingSizer(NULL
);
546 //Putting this after the switch will result in a spacer
547 //not being deleted properly on destruction
552 m_sizer
->DeleteWindows();
557 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
562 void wxSizerItem::Show( bool show
)
567 wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
571 m_window
->Show(show
);
579 m_spacer
->Show(show
);
584 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
588 bool wxSizerItem::IsShown() const
590 if ( m_flag
& wxRESERVE_SPACE_EVEN_IF_HIDDEN
)
596 // we may be called from CalcMin(), just return false so that we're
601 return m_window
->IsShown();
604 // arbitrarily decide that if at least one of our elements is
605 // shown, so are we (this arbitrariness is the reason for
606 // deprecating this function)
607 return m_sizer
->AreAnyItemsShown();
610 return m_spacer
->IsShown();
614 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
620 #if WXWIN_COMPATIBILITY_2_6
621 void wxSizerItem::SetOption( int option
)
623 SetProportion( option
);
626 int wxSizerItem::GetOption() const
628 return GetProportion();
630 #endif // WXWIN_COMPATIBILITY_2_6
633 //---------------------------------------------------------------------------
635 //---------------------------------------------------------------------------
639 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
642 wxSizerItem
* wxSizer::DoInsert( size_t index
, wxSizerItem
*item
)
644 m_children
.Insert( index
, item
);
646 if ( item
->GetWindow() )
647 item
->GetWindow()->SetContainingSizer( this );
649 if ( item
->GetSizer() )
650 item
->GetSizer()->SetContainingWindow( m_containingWindow
);
655 void wxSizer::SetContainingWindow(wxWindow
*win
)
657 if ( win
== m_containingWindow
)
660 m_containingWindow
= win
;
662 // set the same window for all nested sizers as well, they also are in the
664 for ( wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
666 node
= node
->GetNext() )
668 wxSizerItem
*const item
= node
->GetData();
669 wxSizer
*const sizer
= item
->GetSizer();
673 sizer
->SetContainingWindow(win
);
678 #if WXWIN_COMPATIBILITY_2_6
679 bool wxSizer::Remove( wxWindow
*window
)
681 return Detach( window
);
683 #endif // WXWIN_COMPATIBILITY_2_6
685 bool wxSizer::Remove( wxSizer
*sizer
)
687 wxASSERT_MSG( sizer
, wxT("Removing NULL sizer") );
689 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
692 wxSizerItem
*item
= node
->GetData();
694 if (item
->GetSizer() == sizer
)
697 m_children
.Erase( node
);
701 node
= node
->GetNext();
707 bool wxSizer::Remove( int index
)
709 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
711 wxT("Remove index is out of range") );
713 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
715 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
717 delete node
->GetData();
718 m_children
.Erase( node
);
723 bool wxSizer::Detach( wxSizer
*sizer
)
725 wxASSERT_MSG( sizer
, wxT("Detaching NULL sizer") );
727 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
730 wxSizerItem
*item
= node
->GetData();
732 if (item
->GetSizer() == sizer
)
736 m_children
.Erase( node
);
739 node
= node
->GetNext();
745 bool wxSizer::Detach( wxWindow
*window
)
747 wxASSERT_MSG( window
, wxT("Detaching NULL window") );
749 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
752 wxSizerItem
*item
= node
->GetData();
754 if (item
->GetWindow() == window
)
757 m_children
.Erase( node
);
760 node
= node
->GetNext();
766 bool wxSizer::Detach( int index
)
768 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
770 wxT("Detach index is out of range") );
772 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
774 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
776 wxSizerItem
*item
= node
->GetData();
778 if ( item
->IsSizer() )
782 m_children
.Erase( node
);
786 bool wxSizer::Replace( wxWindow
*oldwin
, wxWindow
*newwin
, bool recursive
)
788 wxASSERT_MSG( oldwin
, wxT("Replacing NULL window") );
789 wxASSERT_MSG( newwin
, wxT("Replacing with NULL window") );
791 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
794 wxSizerItem
*item
= node
->GetData();
796 if (item
->GetWindow() == oldwin
)
798 item
->AssignWindow(newwin
);
799 newwin
->SetContainingSizer( this );
802 else if (recursive
&& item
->IsSizer())
804 if (item
->GetSizer()->Replace( oldwin
, newwin
, true ))
808 node
= node
->GetNext();
814 bool wxSizer::Replace( wxSizer
*oldsz
, wxSizer
*newsz
, bool recursive
)
816 wxASSERT_MSG( oldsz
, wxT("Replacing NULL sizer") );
817 wxASSERT_MSG( newsz
, wxT("Replacing with NULL sizer") );
819 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
822 wxSizerItem
*item
= node
->GetData();
824 if (item
->GetSizer() == oldsz
)
826 item
->AssignSizer(newsz
);
829 else if (recursive
&& item
->IsSizer())
831 if (item
->GetSizer()->Replace( oldsz
, newsz
, true ))
835 node
= node
->GetNext();
841 bool wxSizer::Replace( size_t old
, wxSizerItem
*newitem
)
843 wxCHECK_MSG( old
< m_children
.GetCount(), false, wxT("Replace index is out of range") );
844 wxASSERT_MSG( newitem
, wxT("Replacing with NULL item") );
846 wxSizerItemList::compatibility_iterator node
= m_children
.Item( old
);
848 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
850 wxSizerItem
*item
= node
->GetData();
851 node
->SetData(newitem
);
853 if (item
->IsWindow() && item
->GetWindow())
854 item
->GetWindow()->SetContainingSizer(NULL
);
861 void wxSizer::Clear( bool delete_windows
)
863 // First clear the ContainingSizer pointers
864 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
867 wxSizerItem
*item
= node
->GetData();
869 if (item
->IsWindow())
870 item
->GetWindow()->SetContainingSizer( NULL
);
871 node
= node
->GetNext();
874 // Destroy the windows if needed
878 // Now empty the list
879 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
882 void wxSizer::DeleteWindows()
884 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
887 wxSizerItem
*item
= node
->GetData();
889 item
->DeleteWindows();
890 node
= node
->GetNext();
894 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
896 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
898 // take the min size by default and limit it by max size
899 wxSize size
= GetMinClientSize(window
);
902 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
905 // hack for small screen devices where TLWs are always full screen
906 if ( tlw
->IsAlwaysMaximized() )
908 return tlw
->GetClientSize();
911 // limit the window to the size of the display it is on
912 int disp
= wxDisplay::GetFromWindow(window
);
913 if ( disp
== wxNOT_FOUND
)
915 // or, if we don't know which one it is, of the main one
919 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
921 // If determining the display size failed, skip the max size checks as
922 // we really don't want to create windows of (0, 0) size.
923 if ( !sizeMax
.x
|| !sizeMax
.y
)
926 // space for decorations and toolbars etc.
927 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
931 sizeMax
= GetMaxClientSize(window
);
934 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
936 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
942 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
944 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
946 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
949 wxSize
wxSizer::Fit( wxWindow
*window
)
951 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
954 window
->SetClientSize(ComputeFittingClientSize(window
));
956 // return entire size
957 return window
->GetSize();
960 void wxSizer::FitInside( wxWindow
*window
)
963 if (window
->IsTopLevel())
964 size
= VirtualFitSize( window
);
966 size
= GetMinClientSize( window
);
968 window
->SetVirtualSize( size
);
971 void wxSizer::Layout()
973 // (re)calculates minimums needed for each item and other preparations
977 // Applies the layout and repositions/resizes the items
978 wxWindow::ChildrenRepositioningGuard
repositionGuard(m_containingWindow
);
983 void wxSizer::SetSizeHints( wxWindow
*window
)
985 // Preserve the window's max size hints, but set the
986 // lower bound according to the sizer calculations.
988 // This is equivalent to calling Fit(), except that we need to set
989 // the size hints _in between_ the two steps performed by Fit
990 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
991 // otherwise SetClientSize() could have no effect if there already are
992 // size hints in effect that forbid requested client size.
994 const wxSize clientSize
= ComputeFittingClientSize(window
);
996 window
->SetMinClientSize(clientSize
);
997 window
->SetClientSize(clientSize
);
1000 #if WXWIN_COMPATIBILITY_2_8
1001 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
1003 FitInside( window
);
1005 #endif // WXWIN_COMPATIBILITY_2_8
1007 // TODO on mac we need a function that determines how much free space this
1008 // min size contains, in order to make sure that we have 20 pixels of free
1009 // space around the controls
1010 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
1012 return window
->WindowToClientSize(window
->GetMaxSize());
1015 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
1017 return GetMinSize(); // Already returns client size.
1020 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1022 wxSize size
= GetMinClientSize( window
);
1023 wxSize sizeMax
= GetMaxClientSize( window
);
1025 // Limit the size if sizeMax != wxDefaultSize
1027 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1029 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1035 wxSize
wxSizer::GetMinSize()
1037 wxSize
ret( CalcMin() );
1038 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1039 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1043 void wxSizer::DoSetMinSize( int width
, int height
)
1045 m_minSize
.x
= width
;
1046 m_minSize
.y
= height
;
1049 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1051 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1053 // Is it our immediate child?
1055 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1058 wxSizerItem
*item
= node
->GetData();
1060 if (item
->GetWindow() == window
)
1062 item
->SetMinSize( width
, height
);
1065 node
= node
->GetNext();
1068 // No? Search any subsizers we own then
1070 node
= m_children
.GetFirst();
1073 wxSizerItem
*item
= node
->GetData();
1075 if ( item
->GetSizer() &&
1076 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1078 // A child sizer found the requested windw, exit.
1081 node
= node
->GetNext();
1087 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1089 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1091 // Is it our immediate child?
1093 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1096 wxSizerItem
*item
= node
->GetData();
1098 if (item
->GetSizer() == sizer
)
1100 item
->GetSizer()->DoSetMinSize( width
, height
);
1103 node
= node
->GetNext();
1106 // No? Search any subsizers we own then
1108 node
= m_children
.GetFirst();
1111 wxSizerItem
*item
= node
->GetData();
1113 if ( item
->GetSizer() &&
1114 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1116 // A child found the requested sizer, exit.
1119 node
= node
->GetNext();
1125 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1127 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1129 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1131 wxSizerItem
*item
= node
->GetData();
1133 if (item
->GetSizer())
1135 // Sizers contains the minimal size in them, if not calculated ...
1136 item
->GetSizer()->DoSetMinSize( width
, height
);
1140 // ... but the minimal size of spacers and windows is stored via the item
1141 item
->SetMinSize( width
, height
);
1147 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1149 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1151 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1154 wxSizerItem
*item
= node
->GetData();
1156 if (item
->GetWindow() == window
)
1160 else if (recursive
&& item
->IsSizer())
1162 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1167 node
= node
->GetNext();
1173 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1175 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1177 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1180 wxSizerItem
*item
= node
->GetData();
1182 if (item
->GetSizer() == sizer
)
1186 else if (recursive
&& item
->IsSizer())
1188 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1193 node
= node
->GetNext();
1199 wxSizerItem
* wxSizer::GetItem( size_t index
)
1201 wxCHECK_MSG( index
< m_children
.GetCount(),
1203 wxT("GetItem index is out of range") );
1205 return m_children
.Item( index
)->GetData();
1208 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1210 // This gets a sizer item by the id of the sizer item
1211 // and NOT the id of a window if the item is a window.
1213 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1216 wxSizerItem
*item
= node
->GetData();
1218 if (item
->GetId() == id
)
1222 else if (recursive
&& item
->IsSizer())
1224 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1229 node
= node
->GetNext();
1235 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1237 wxSizerItem
*item
= GetItem( window
, recursive
);
1248 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1250 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1261 bool wxSizer::Show( size_t index
, bool show
)
1263 wxSizerItem
*item
= GetItem( index
);
1274 void wxSizer::ShowItems( bool show
)
1276 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1279 node
->GetData()->Show( show
);
1280 node
= node
->GetNext();
1284 bool wxSizer::AreAnyItemsShown() const
1286 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1289 if ( node
->GetData()->IsShown() )
1291 node
= node
->GetNext();
1297 bool wxSizer::IsShown( wxWindow
*window
) const
1299 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1302 wxSizerItem
*item
= node
->GetData();
1304 if (item
->GetWindow() == window
)
1306 return item
->IsShown();
1308 node
= node
->GetNext();
1311 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1316 bool wxSizer::IsShown( wxSizer
*sizer
) const
1318 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1321 wxSizerItem
*item
= node
->GetData();
1323 if (item
->GetSizer() == sizer
)
1325 return item
->IsShown();
1327 node
= node
->GetNext();
1330 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1335 bool wxSizer::IsShown( size_t index
) const
1337 wxCHECK_MSG( index
< m_children
.GetCount(),
1339 wxT("IsShown index is out of range") );
1341 return m_children
.Item( index
)->GetData()->IsShown();
1345 //---------------------------------------------------------------------------
1347 //---------------------------------------------------------------------------
1349 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1350 : m_rows( cols
== 0 ? 1 : 0 ),
1355 wxASSERT(cols
>= 0);
1358 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1359 : m_rows( cols
== 0 ? 1 : 0 ),
1361 m_vgap( gap
.GetHeight() ),
1362 m_hgap( gap
.GetWidth() )
1364 wxASSERT(cols
>= 0);
1367 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1368 : m_rows( rows
|| cols
? rows
: 1 ),
1373 wxASSERT(rows
>= 0 && cols
>= 0);
1376 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1377 : m_rows( rows
|| cols
? rows
: 1 ),
1379 m_vgap( gap
.GetHeight() ),
1380 m_hgap( gap
.GetWidth() )
1382 wxASSERT(rows
>= 0 && cols
>= 0);
1385 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1387 // if only the number of columns or the number of rows is specified for a
1388 // sizer, arbitrarily many items can be added to it but if both of them are
1389 // fixed, then the sizer can't have more than that many items -- check for
1390 // this here to ensure that we detect errors as soon as possible
1391 if ( m_cols
&& m_rows
)
1393 const int nitems
= m_children
.GetCount();
1394 if ( nitems
== m_cols
*m_rows
)
1398 "too many items (%d > %d*%d) in grid sizer (maybe you "
1399 "should omit the number of either rows or columns?)",
1400 nitems
+ 1, m_cols
, m_rows
)
1403 // additionally, continuing to use the specified number of columns
1404 // and rows is not a good idea as callers of CalcRowsCols() expect
1405 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1406 // which is not the case if there are too many items and results in
1407 // crashes, so let it compute the number of rows automatically by
1408 // forgetting the (wrong) number of rows specified (this also has a
1409 // nice side effect of giving only one assert even if there are
1410 // many more items than allowed in this sizer)
1415 return wxSizer::DoInsert(index
, item
);
1418 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1420 const int nitems
= m_children
.GetCount();
1422 ncols
= GetEffectiveColsCount();
1423 nrows
= GetEffectiveRowsCount();
1425 // Since Insert() checks for overpopulation, the following
1426 // should only assert if the grid was shrunk via SetRows() / SetCols()
1427 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1432 void wxGridSizer::RecalcSizes()
1434 int nitems
, nrows
, ncols
;
1435 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1438 wxSize
sz( GetSize() );
1439 wxPoint
pt( GetPosition() );
1441 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1442 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1445 for (int c
= 0; c
< ncols
; c
++)
1448 for (int r
= 0; r
< nrows
; r
++)
1450 int i
= r
* ncols
+ c
;
1453 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1455 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1457 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1465 wxSize
wxGridSizer::CalcMin()
1468 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1471 // Find the max width and height for any component
1475 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1478 wxSizerItem
*item
= node
->GetData();
1479 wxSize
sz( item
->CalcMin() );
1481 w
= wxMax( w
, sz
.x
);
1482 h
= wxMax( h
, sz
.y
);
1484 node
= node
->GetNext();
1487 // In case we have a nested sizer with a two step algo , give it
1488 // a chance to adjust to that (we give it width component)
1489 node
= m_children
.GetFirst();
1490 bool didChangeMinSize
= false;
1493 wxSizerItem
*item
= node
->GetData();
1494 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1496 node
= node
->GetNext();
1499 // And redo iteration in case min size changed
1500 if( didChangeMinSize
)
1502 node
= m_children
.GetFirst();
1506 wxSizerItem
*item
= node
->GetData();
1507 wxSize
sz( item
->GetMinSizeWithBorder() );
1509 w
= wxMax( w
, sz
.x
);
1510 h
= wxMax( h
, sz
.y
);
1512 node
= node
->GetNext();
1516 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1517 nrows
* h
+ (nrows
-1) * m_vgap
);
1520 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1523 wxSize
sz( item
->GetMinSizeWithBorder() );
1524 int flag
= item
->GetFlag();
1526 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1532 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1534 pt
.x
= x
+ (w
- sz
.x
) / 2;
1536 else if (flag
& wxALIGN_RIGHT
)
1538 pt
.x
= x
+ (w
- sz
.x
);
1541 if (flag
& wxALIGN_CENTER_VERTICAL
)
1543 pt
.y
= y
+ (h
- sz
.y
) / 2;
1545 else if (flag
& wxALIGN_BOTTOM
)
1547 pt
.y
= y
+ (h
- sz
.y
);
1551 item
->SetDimension(pt
, sz
);
1554 //---------------------------------------------------------------------------
1556 //---------------------------------------------------------------------------
1558 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1559 : wxGridSizer( cols
, vgap
, hgap
),
1560 m_flexDirection(wxBOTH
),
1561 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1565 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1566 : wxGridSizer( cols
, gap
),
1567 m_flexDirection(wxBOTH
),
1568 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1572 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1573 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1574 m_flexDirection(wxBOTH
),
1575 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1579 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1580 : wxGridSizer( rows
, cols
, gap
),
1581 m_flexDirection(wxBOTH
),
1582 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1586 wxFlexGridSizer::~wxFlexGridSizer()
1590 void wxFlexGridSizer::RecalcSizes()
1593 if ( !CalcRowsCols(nrows
, ncols
) )
1596 const wxPoint
pt(GetPosition());
1597 const wxSize
sz(GetSize());
1599 AdjustForGrowables(sz
);
1601 wxSizerItemList::const_iterator i
= m_children
.begin();
1602 const wxSizerItemList::const_iterator end
= m_children
.end();
1605 for ( int r
= 0; r
< nrows
; r
++ )
1607 if ( m_rowHeights
[r
] == -1 )
1609 // this row is entirely hidden, skip it
1610 for ( int c
= 0; c
< ncols
; c
++ )
1621 const int hrow
= m_rowHeights
[r
];
1622 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1627 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1629 const int wcol
= m_colWidths
[c
];
1634 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1638 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1650 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1651 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1653 // Sum total minimum size, including gaps between rows/columns.
1654 // -1 is used as a magic number meaning empty row/column.
1657 const size_t count
= sizes
.size();
1658 for ( size_t n
= 0; n
< count
; n
++ )
1660 if ( sizes
[n
] != -1 )
1663 total
+= gap
; // separate from the previous column
1672 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1674 // We have to recalculate the sizes in case the item minimum size has
1675 // changed since the previous layout, or the item has been hidden using
1676 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1677 // dimension of the row/column will be -1, indicating that the column
1678 // itself is hidden.
1679 m_rowHeights
.assign(nrows
, -1);
1680 m_colWidths
.assign(ncols
, -1);
1682 // n is the index of the item in left-to-right top-to-bottom order
1684 for ( wxSizerItemList::iterator i
= m_children
.begin();
1685 i
!= m_children
.end();
1688 wxSizerItem
* const item
= *i
;
1689 if ( item
->IsShown() )
1691 // NOTE: Not doing the calculation here, this is just
1692 // for finding max values.
1693 const wxSize
sz(item
->GetMinSizeWithBorder());
1695 const int row
= n
/ ncols
;
1696 const int col
= n
% ncols
;
1698 if ( sz
.y
> m_rowHeights
[row
] )
1699 m_rowHeights
[row
] = sz
.y
;
1700 if ( sz
.x
> m_colWidths
[col
] )
1701 m_colWidths
[col
] = sz
.x
;
1705 AdjustForFlexDirection();
1707 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1708 SumArraySizes(m_rowHeights
, m_vgap
));
1711 wxSize
wxFlexGridSizer::CalcMin()
1716 // Number of rows/columns can change as items are added or removed.
1717 if ( !CalcRowsCols(nrows
, ncols
) )
1721 // We have to recalculate the sizes in case the item minimum size has
1722 // changed since the previous layout, or the item has been hidden using
1723 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1724 // dimension of the row/column will be -1, indicating that the column
1725 // itself is hidden.
1726 m_rowHeights
.assign(nrows
, -1);
1727 m_colWidths
.assign(ncols
, -1);
1729 for ( wxSizerItemList::iterator i
= m_children
.begin();
1730 i
!= m_children
.end();
1733 wxSizerItem
* const item
= *i
;
1734 if ( item
->IsShown() )
1740 // The stage of looking for max values in each row/column has been
1741 // made a separate function, since it's reused in AdjustForGrowables.
1742 FindWidthsAndHeights(nrows
,ncols
);
1744 return m_calculatedMinSize
;
1747 void wxFlexGridSizer::AdjustForFlexDirection()
1749 // the logic in CalcMin works when we resize flexibly in both directions
1750 // but maybe this is not the case
1751 if ( m_flexDirection
!= wxBOTH
)
1753 // select the array corresponding to the direction in which we do *not*
1755 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1758 const size_t count
= array
.GetCount();
1760 // find the largest value in this array
1764 for ( n
= 0; n
< count
; ++n
)
1766 if ( array
[n
] > largest
)
1770 // and now fill it with the largest value
1771 for ( n
= 0; n
< count
; ++n
)
1773 // don't touch hidden rows
1774 if ( array
[n
] != -1 )
1780 // helper of AdjustForGrowables() which is called for rows/columns separately
1783 // delta: the extra space, we do nothing unless it's positive
1784 // growable: indices or growable rows/cols in sizes array
1785 // sizes: the height/widths of rows/cols to adjust
1786 // proportions: proportions of the growable rows/cols or NULL if they all
1787 // should be assumed to have proportion of 1
1789 DoAdjustForGrowables(int delta
,
1790 const wxArrayInt
& growable
,
1792 const wxArrayInt
*proportions
)
1797 // total sum of proportions of all non-hidden rows
1798 int sum_proportions
= 0;
1800 // number of currently shown growable rows
1803 const int max_idx
= sizes
.size();
1805 const size_t count
= growable
.size();
1807 for ( idx
= 0; idx
< count
; idx
++ )
1809 // Since the number of rows/columns can change as items are
1810 // inserted/deleted, we need to verify at runtime that the
1811 // requested growable rows/columns are still valid.
1812 if ( growable
[idx
] >= max_idx
)
1815 // If all items in a row/column are hidden, that row/column will
1816 // have a dimension of -1. This causes the row/column to be
1817 // hidden completely.
1818 if ( sizes
[growable
[idx
]] == -1 )
1822 sum_proportions
+= (*proportions
)[idx
];
1830 // the remaining extra free space, adjusted during each iteration
1831 for ( idx
= 0; idx
< count
; idx
++ )
1833 if ( growable
[idx
] >= max_idx
)
1836 if ( sizes
[ growable
[idx
] ] == -1 )
1840 if ( sum_proportions
== 0 )
1842 // no growable rows -- divide extra space evenly among all
1843 cur_delta
= delta
/num
;
1846 else // allocate extra space proportionally
1848 const int cur_prop
= (*proportions
)[idx
];
1849 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1850 sum_proportions
-= cur_prop
;
1853 sizes
[growable
[idx
]] += cur_delta
;
1858 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1861 // by the time this function is called, the sizer should be already fully
1862 // initialized and hence the number of its columns and rows is known and we
1863 // can check that all indices in m_growableCols/Rows are valid (see also
1864 // comments in AddGrowableCol/Row())
1865 if ( !m_rows
|| !m_cols
)
1869 int nrows
= CalcRows();
1871 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1873 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1874 "invalid growable row index" );
1880 int ncols
= CalcCols();
1882 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1884 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1885 "invalid growable column index" );
1889 #endif // wxDEBUG_LEVEL
1892 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1894 DoAdjustForGrowables
1896 sz
.x
- m_calculatedMinSize
.x
,
1899 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1903 // This gives nested objects that benefit from knowing one size
1904 // component in advance the chance to use that.
1905 bool didAdjustMinSize
= false;
1907 // Iterate over all items and inform about column width
1908 const int ncols
= GetEffectiveColsCount();
1910 for ( wxSizerItemList::iterator i
= m_children
.begin();
1911 i
!= m_children
.end();
1914 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1915 if ( ++col
== ncols
)
1919 // Only redo if info was actually used
1920 if( didAdjustMinSize
)
1922 DoAdjustForGrowables
1924 sz
.x
- m_calculatedMinSize
.x
,
1927 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1933 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1935 // pass NULL instead of proportions if the grow mode is ALL as we
1936 // should treat all rows as having proportion of 1 then
1937 DoAdjustForGrowables
1939 sz
.y
- m_calculatedMinSize
.y
,
1942 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1948 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1950 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1953 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1955 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1958 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1960 wxASSERT_MSG( !IsRowGrowable( idx
),
1961 "AddGrowableRow() called for growable row" );
1963 // notice that we intentionally don't check the index validity here in (the
1964 // common) case when the number of rows was not specified in the ctor -- in
1965 // this case it will be computed only later, when all items are added to
1966 // the sizer, and the check will be done in AdjustForGrowables()
1967 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1969 m_growableRows
.Add( idx
);
1970 m_growableRowsProportions
.Add( proportion
);
1973 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1975 wxASSERT_MSG( !IsColGrowable( idx
),
1976 "AddGrowableCol() called for growable column" );
1978 // see comment in AddGrowableRow(): although it's less common to omit the
1979 // specification of the number of columns, it still can also happen
1980 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1982 m_growableCols
.Add( idx
);
1983 m_growableColsProportions
.Add( proportion
);
1986 // helper function for RemoveGrowableCol/Row()
1988 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1990 const size_t count
= items
.size();
1991 for ( size_t n
= 0; n
< count
; n
++ )
1993 if ( (size_t)items
[n
] == idx
)
1996 proportions
.RemoveAt(n
);
2001 wxFAIL_MSG( wxT("column/row is already not growable") );
2004 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
2006 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
2009 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
2011 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
2014 //---------------------------------------------------------------------------
2016 //---------------------------------------------------------------------------
2018 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
2020 return IsVertical() ? Add(0, size
) : Add(size
, 0);
2027 Helper of RecalcSizes(): checks if there is enough remaining space for the
2028 min size of the given item and returns its min size or the entire remaining
2029 space depending on which one is greater.
2031 This function updates the remaining space parameter to account for the size
2032 effectively allocated to the item.
2035 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2037 int& remainingSpace
= *remainingSpace_
;
2040 if ( remainingSpace
> 0 )
2042 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2043 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2045 if ( size
>= remainingSpace
)
2047 // truncate the item to fit in the remaining space, this is better
2048 // than showing it only partially in general, even if both choices
2049 // are bad -- but there is nothing else we can do
2050 size
= remainingSpace
;
2053 remainingSpace
-= size
;
2055 else // no remaining space
2057 // no space at all left, no need to even query the item for its min
2058 // size as we can't give it to it anyhow
2065 } // anonymous namespace
2067 void wxBoxSizer::RecalcSizes()
2069 if ( m_children
.empty() )
2072 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2073 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2075 // the amount of free space which we should redistribute among the
2076 // stretchable items (i.e. those with non zero proportion)
2077 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2079 // declare loop variables used below:
2080 wxSizerItemList::const_iterator i
; // iterator in m_children list
2081 unsigned n
= 0; // item index in majorSizes array
2084 // First, inform item about the available size in minor direction as this
2085 // can change their size in the major direction. Also compute the number of
2086 // visible items and sum of their min sizes in major direction.
2088 int minMajorSize
= 0;
2089 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2091 wxSizerItem
* const item
= *i
;
2093 if ( !item
->IsShown() )
2096 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2097 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2098 wxSize szMin
= item
->GetMinSizeWithBorder();
2099 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2102 // Since we passed available space along to the item, it should not
2103 // take too much, so delta should not become negative.
2104 delta
-= deltaChange
;
2106 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2109 // update our min size have changed
2110 SizeInMajorDir(m_minSize
) = minMajorSize
;
2113 // space and sum of proportions for the remaining items, both may change
2115 wxCoord remaining
= totalMajorSize
;
2116 int totalProportion
= m_totalProportion
;
2118 // size of the (visible) items in major direction, -1 means "not fixed yet"
2119 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2122 // Check for the degenerated case when we don't have enough space for even
2123 // the min sizes of all the items: in this case we really can't do much
2124 // more than to allocate the min size to as many of fixed size items as
2125 // possible (on the assumption that variable size items such as text zones
2126 // or list boxes may use scrollbars to show their content even if their
2127 // size is less than min size but that fixed size items such as buttons
2128 // will suffer even more if we don't give them their min size)
2129 if ( totalMajorSize
< minMajorSize
)
2131 // Second degenerated case pass: allocate min size to all fixed size
2133 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2135 wxSizerItem
* const item
= *i
;
2137 if ( !item
->IsShown() )
2140 // deal with fixed size items only during this pass
2141 if ( item
->GetProportion() )
2144 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2148 // Third degenerated case pass: allocate min size to all the remaining,
2149 // i.e. non-fixed size, items.
2150 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2152 wxSizerItem
* const item
= *i
;
2154 if ( !item
->IsShown() )
2157 // we've already dealt with fixed size items above
2158 if ( !item
->GetProportion() )
2161 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2164 else // we do have enough space to give at least min sizes to all items
2166 // Second and maybe more passes in the non-degenerated case: deal with
2167 // fixed size items and items whose min size is greater than what we
2168 // would allocate to them taking their proportion into account. For
2169 // both of them, we will just use their min size, but for the latter we
2170 // also need to reexamine all the items as the items which fitted
2171 // before we adjusted their size upwards might not fit any more. This
2172 // does make for a quadratic algorithm but it's not obvious how to
2173 // avoid it and hopefully it's not a huge problem in practice as the
2174 // sizers don't have many items usually (and, of course, the algorithm
2175 // still reduces into a linear one if there is enough space for all the
2177 bool nonFixedSpaceChanged
= false;
2178 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2180 if ( nonFixedSpaceChanged
)
2182 i
= m_children
.begin();
2184 nonFixedSpaceChanged
= false;
2187 // check for the end of the loop only after the check above as
2188 // otherwise we wouldn't do another pass if the last child resulted
2189 // in non fixed space reduction
2190 if ( i
== m_children
.end() )
2193 wxSizerItem
* const item
= *i
;
2195 if ( !item
->IsShown() )
2198 // don't check the item which we had already dealt with during a
2199 // previous pass (this is more than an optimization, the code
2200 // wouldn't work correctly if we kept adjusting for the same item
2201 // over and over again)
2202 if ( majorSizes
[n
] != wxDefaultCoord
)
2205 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2207 // it doesn't make sense for min size to be negative but right now
2208 // it's possible to create e.g. a spacer with (-1, 10) as size and
2209 // people do it in their code apparently (see #11842) so ensure
2210 // that we don't use this -1 as real min size as it conflicts with
2211 // the meaning we use for it here and negative min sizes just don't
2212 // make sense anyhow (which is why it might be a better idea to
2213 // deal with them at wxSizerItem level in the future but for now
2214 // this is the minimal fix for the bug)
2218 const int propItem
= item
->GetProportion();
2221 // is the desired size of this item big enough?
2222 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2224 // yes, it is, we'll determine the real size of this
2225 // item later, for now just leave it as wxDefaultCoord
2229 // the proportion of this item won't count, it has
2230 // effectively become fixed
2231 totalProportion
-= propItem
;
2234 // we can already allocate space for this item
2235 majorSizes
[n
] = minMajor
;
2237 // change the amount of the space remaining to the other items,
2238 // as this can result in not being able to satisfy their
2239 // proportions any more we will need to redo another loop
2241 remaining
-= minMajor
;
2243 nonFixedSpaceChanged
= true;
2246 // Similar to the previous loop, but dealing with items whose max size
2247 // is less than what we would allocate to them taking their proportion
2249 nonFixedSpaceChanged
= false;
2250 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2252 if ( nonFixedSpaceChanged
)
2254 i
= m_children
.begin();
2256 nonFixedSpaceChanged
= false;
2259 // check for the end of the loop only after the check above as
2260 // otherwise we wouldn't do another pass if the last child resulted
2261 // in non fixed space reduction
2262 if ( i
== m_children
.end() )
2265 wxSizerItem
* const item
= *i
;
2267 if ( !item
->IsShown() )
2270 // don't check the item which we had already dealt with during a
2271 // previous pass (this is more than an optimization, the code
2272 // wouldn't work correctly if we kept adjusting for the same item
2273 // over and over again)
2274 if ( majorSizes
[n
] != wxDefaultCoord
)
2277 wxCoord maxMajor
= GetSizeInMajorDir(item
->GetMaxSizeWithBorder());
2279 // must be nonzero, fixed-size items were dealt with in previous loop
2280 const int propItem
= item
->GetProportion();
2282 // is the desired size of this item small enough?
2283 if ( maxMajor
< 0 ||
2284 (remaining
*propItem
)/totalProportion
<= maxMajor
)
2286 // yes, it is, we'll determine the real size of this
2287 // item later, for now just leave it as wxDefaultCoord
2291 // the proportion of this item won't count, it has
2292 // effectively become fixed
2293 totalProportion
-= propItem
;
2295 // we can already allocate space for this item
2296 majorSizes
[n
] = maxMajor
;
2298 // change the amount of the space remaining to the other items,
2299 // as this can result in not being able to satisfy their
2300 // proportions any more we will need to redo another loop
2302 remaining
-= maxMajor
;
2304 nonFixedSpaceChanged
= true;
2307 // Last by one pass: distribute the remaining space among the non-fixed
2308 // items whose size weren't fixed yet according to their proportions.
2309 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2311 wxSizerItem
* const item
= *i
;
2313 if ( !item
->IsShown() )
2316 if ( majorSizes
[n
] == wxDefaultCoord
)
2318 const int propItem
= item
->GetProportion();
2319 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2321 remaining
-= majorSizes
[n
];
2322 totalProportion
-= propItem
;
2328 // the position at which we put the next child
2329 wxPoint
pt(m_position
);
2332 // Final pass: finally do position the items correctly using their sizes as
2333 // determined above.
2334 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2336 wxSizerItem
* const item
= *i
;
2338 if ( !item
->IsShown() )
2341 const int majorSize
= majorSizes
[n
];
2343 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2345 // apply the alignment in the minor direction
2346 wxPoint
posChild(pt
);
2348 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2349 const int flag
= item
->GetFlag();
2350 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2352 // occupy all the available space if wxEXPAND was given and also if
2353 // the item is too big to fit -- in this case we truncate it below
2354 // its minimal size which is bad but better than not showing parts
2355 // of the window at all
2356 minorSize
= totalMinorSize
;
2358 // do not allow the size in the minor direction to grow beyond the max
2359 // size of the item in the minor direction
2360 const wxCoord maxMinorSize
= GetSizeInMinorDir(item
->GetMaxSizeWithBorder());
2361 if ( maxMinorSize
>= 0 && minorSize
> maxMinorSize
)
2362 minorSize
= maxMinorSize
;
2365 if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2367 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2369 // NB: wxCENTRE is used here only for backwards compatibility,
2370 // wxALIGN_CENTRE should be used in new code
2371 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2372 : wxALIGN_CENTRE_VERTICAL
)) )
2374 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2378 // apply RTL adjustment for horizontal sizers:
2379 if ( !IsVertical() && m_containingWindow
)
2381 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2389 // finally set size of this child and advance to the next one
2390 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2392 PosInMajorDir(pt
) += majorSize
;
2396 wxSize
wxBoxSizer::CalcMin()
2398 m_totalProportion
= 0;
2399 m_minSize
= wxSize(0, 0);
2401 // The minimal size for the sizer should be big enough to allocate its
2402 // element at least its minimal size but also, and this is the non trivial
2403 // part, to respect the children proportion. To satisfy the latter
2404 // condition we must find the greatest min-size-to-proportion ratio for all
2405 // elements with non-zero proportion.
2406 float maxMinSizeToProp
= 0.;
2407 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2408 i
!= m_children
.end();
2411 wxSizerItem
* const item
= *i
;
2413 if ( !item
->IsShown() )
2416 const wxSize sizeMinThis
= item
->CalcMin();
2417 if ( const int propThis
= item
->GetProportion() )
2419 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2420 minSizeToProp
/= propThis
;
2422 if ( minSizeToProp
> maxMinSizeToProp
)
2423 maxMinSizeToProp
= minSizeToProp
;
2425 m_totalProportion
+= item
->GetProportion();
2427 else // fixed size item
2429 // Just account for its size directly
2430 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2433 // In the transversal direction we just need to find the maximum.
2434 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2435 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2438 // Using the max ratio ensures that the min size is big enough for all
2439 // items to have their min size and satisfy the proportions among them.
2440 SizeInMajorDir(m_minSize
) += (int)(maxMinSizeToProp
*m_totalProportion
);
2445 //---------------------------------------------------------------------------
2447 //---------------------------------------------------------------------------
2451 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2452 : wxBoxSizer( orient
),
2455 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2457 // do this so that our Detach() is called if the static box is destroyed
2459 m_staticBox
->SetContainingSizer(this);
2462 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2463 : wxBoxSizer(orient
),
2464 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2467 m_staticBox
->SetContainingSizer(this);
2470 wxStaticBoxSizer::~wxStaticBoxSizer()
2475 void wxStaticBoxSizer::RecalcSizes()
2477 int top_border
, other_border
;
2478 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2480 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2482 wxSize
old_size( m_size
);
2483 m_size
.x
-= 2*other_border
;
2484 m_size
.y
-= top_border
+ other_border
;
2486 wxPoint
old_pos( m_position
);
2487 if (m_staticBox
->GetChildren().GetCount() > 0)
2489 #if defined( __WXGTK20__ )
2490 // if the wxStaticBox has created a wxPizza to contain its children
2491 // (see wxStaticBox::AddChild) then we need to place the items it contains
2492 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2493 // to the top-left corner of the staticbox:
2494 m_position
.x
= m_position
.y
= 0;
2495 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
2496 // the distance from the 'inner' content view to the embedded controls
2497 // this is independent of the title, therefore top_border is not relevant
2498 m_position
.x
= m_position
.y
= 10;
2500 // if the wxStaticBox has children, then these windows must be placed
2501 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2502 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2503 // to keep in count the static borders here!):
2504 m_position
.x
= other_border
;
2505 m_position
.y
= top_border
;
2510 // the windows contained in the staticbox have been created as siblings of the
2511 // staticbox (this is the "old" way of staticbox contents creation); in this
2512 // case we need to position them with coordinates relative to our common parent
2513 m_position
.x
+= other_border
;
2514 m_position
.y
+= top_border
;
2517 wxBoxSizer::RecalcSizes();
2519 m_position
= old_pos
;
2523 wxSize
wxStaticBoxSizer::CalcMin()
2525 int top_border
, other_border
;
2526 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2528 wxSize
ret( wxBoxSizer::CalcMin() );
2529 ret
.x
+= 2*other_border
;
2531 // ensure that we're wide enough to show the static box label (there is no
2532 // need to check for the static box best size in vertical direction though)
2533 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2534 if ( ret
.x
< boxWidth
)
2537 ret
.y
+= other_border
+ top_border
;
2542 void wxStaticBoxSizer::ShowItems( bool show
)
2544 m_staticBox
->Show( show
);
2545 wxBoxSizer::ShowItems( show
);
2548 bool wxStaticBoxSizer::AreAnyItemsShown() const
2550 // We don't need to check the status of our child items: if the box is
2551 // shown, this sizer should be considered shown even if all its elements
2552 // are hidden (or, more prosaically, there are no elements at all). And,
2553 // conversely, if the box is hidden then all our items, which are its
2554 // children, are hidden too.
2555 return m_staticBox
->IsShown();
2558 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2560 // avoid deleting m_staticBox in our dtor if it's being detached from the
2561 // sizer (which can happen because it's being already destroyed for
2563 if ( window
== m_staticBox
)
2569 return wxSizer::Detach( window
);
2572 #endif // wxUSE_STATBOX
2574 //---------------------------------------------------------------------------
2575 // wxStdDialogButtonSizer
2576 //---------------------------------------------------------------------------
2580 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2581 : wxBoxSizer(wxHORIZONTAL
)
2583 // Vertical buttons with lots of space on either side
2584 // looks rubbish on WinCE, so let's not do this for now.
2585 // If we are going to use vertical buttons, we should
2586 // put the sizer to the right of other controls in the dialog,
2587 // and that's beyond the scope of this sizer.
2589 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2590 // If we have a PDA screen, put yes/no button over
2591 // all other buttons, otherwise on the left side.
2593 m_orient
= wxVERTICAL
;
2596 m_buttonAffirmative
= NULL
;
2597 m_buttonApply
= NULL
;
2598 m_buttonNegative
= NULL
;
2599 m_buttonCancel
= NULL
;
2600 m_buttonHelp
= NULL
;
2603 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2605 switch (mybutton
->GetId())
2610 m_buttonAffirmative
= mybutton
;
2613 m_buttonApply
= mybutton
;
2616 m_buttonNegative
= mybutton
;
2620 m_buttonCancel
= mybutton
;
2623 case wxID_CONTEXT_HELP
:
2624 m_buttonHelp
= mybutton
;
2631 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2633 m_buttonAffirmative
= button
;
2636 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2638 m_buttonNegative
= button
;
2641 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2643 m_buttonCancel
= button
;
2646 void wxStdDialogButtonSizer::Realize()
2649 Add(0, 0, 0, wxLEFT
, 6);
2651 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2653 if (m_buttonNegative
){
2654 // HIG POLICE BULLETIN - destructive buttons need extra padding
2655 // 24 pixels on either side
2656 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2659 // extra whitespace between help/negative and cancel/ok buttons
2660 Add(0, 0, 1, wxEXPAND
, 0);
2662 if (m_buttonCancel
){
2663 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2664 // Cancel or help should be default
2665 // m_buttonCancel->SetDefaultButton();
2668 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2669 // figure the best place is between Cancel and OK
2671 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2673 if (m_buttonAffirmative
){
2674 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2676 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2677 // these buttons have set labels under Mac so we should use them
2678 m_buttonAffirmative
->SetLabel(_("Save"));
2679 if (m_buttonNegative
)
2680 m_buttonNegative
->SetLabel(_("Don't Save"));
2684 // Extra space around and at the right
2686 #elif defined(__WXGTK20__)
2687 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2688 // says that the correct button order is
2690 // [Help] [Alternative] [Cancel] [Affirmative]
2692 // Flags ensuring that margins between the buttons are 6 pixels.
2694 flagsBtn
= wxSizerFlags().Centre().Border(wxLEFT
| wxRIGHT
, 3);
2696 // Margin around the entire sizer button should be 12.
2700 Add(m_buttonHelp
, flagsBtn
);
2702 // Align the rest of the buttons to the right.
2705 if (m_buttonNegative
)
2706 Add(m_buttonNegative
, flagsBtn
);
2709 Add(m_buttonApply
, flagsBtn
);
2712 Add(m_buttonCancel
, flagsBtn
);
2714 if (m_buttonAffirmative
)
2715 Add(m_buttonAffirmative
, flagsBtn
);
2717 // Ensure that the right margin is 12 as well.
2719 #elif defined(__WXMSW__)
2722 // right-justify buttons
2723 Add(0, 0, 1, wxEXPAND
, 0);
2725 if (m_buttonAffirmative
){
2726 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2729 if (m_buttonNegative
){
2730 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2733 if (m_buttonCancel
){
2734 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2737 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2740 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2742 // GTK+1 and any other platform
2744 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2746 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2748 // extra whitespace between help and cancel/ok buttons
2749 Add(0, 0, 1, wxEXPAND
, 0);
2752 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2754 if (m_buttonAffirmative
){
2755 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2758 if (m_buttonNegative
){
2759 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2762 if (m_buttonCancel
){
2763 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2764 // Cancel or help should be default
2765 // m_buttonCancel->SetDefaultButton();
2771 #endif // wxUSE_BUTTON