1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/sizer.cpp
3 // Purpose: provide new wxSizer class for layout
4 // Author: Robert Roebling and Robin Dunn, contributions by
5 // Dirk Holtwick, Ron Lee
6 // Modified by: Ron Lee
9 // Copyright: (c) Robin Dunn, Robert Roebling
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
13 // For compilers that support precompilation, includes "wx.h".
14 #include "wx/wxprec.h"
21 #include "wx/private/flagscheck.h"
24 #include "wx/string.h"
28 #include "wx/settings.h"
29 #include "wx/button.h"
30 #include "wx/statbox.h"
31 #include "wx/toplevel.h"
34 #include "wx/display.h"
35 #include "wx/vector.h"
36 #include "wx/listimpl.cpp"
39 //---------------------------------------------------------------------------
41 IMPLEMENT_CLASS(wxSizerItem
, wxObject
)
42 IMPLEMENT_CLASS(wxSizer
, wxObject
)
43 IMPLEMENT_CLASS(wxGridSizer
, wxSizer
)
44 IMPLEMENT_CLASS(wxFlexGridSizer
, wxGridSizer
)
45 IMPLEMENT_CLASS(wxBoxSizer
, wxSizer
)
47 IMPLEMENT_CLASS(wxStaticBoxSizer
, wxBoxSizer
)
50 IMPLEMENT_CLASS(wxStdDialogButtonSizer
, wxBoxSizer
)
53 WX_DEFINE_EXPORTED_LIST( wxSizerItemList
)
88 // ----------------------------------------------------------------------------
90 // ----------------------------------------------------------------------------
92 // check for flags conflicts
93 static const int SIZER_FLAGS_MASK
=
95 wxADD_FLAG(wxHORIZONTAL
,
96 wxADD_FLAG(wxVERTICAL
,
101 wxADD_FLAG(wxALIGN_NOT
,
102 wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL
,
103 wxADD_FLAG(wxALIGN_RIGHT
,
104 wxADD_FLAG(wxALIGN_BOTTOM
,
105 wxADD_FLAG(wxALIGN_CENTER_VERTICAL
,
106 wxADD_FLAG(wxFIXED_MINSIZE
,
107 wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN
,
108 wxADD_FLAG(wxSTRETCH_NOT
,
114 #define ASSERT_VALID_SIZER_FLAGS(f) wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK)
117 void wxSizerItem::Init(const wxSizerFlags
& flags
)
121 m_proportion
= flags
.GetProportion();
122 m_flag
= flags
.GetFlags();
123 m_border
= flags
.GetBorderInPixels();
125 ASSERT_VALID_SIZER_FLAGS( m_flag
);
128 wxSizerItem::wxSizerItem()
139 void wxSizerItem::DoSetWindow(wxWindow
*window
)
141 wxCHECK_RET( window
, wxT("NULL window in wxSizerItem::SetWindow()") );
143 m_kind
= Item_Window
;
146 // window doesn't become smaller than its initial size, whatever happens
147 m_minSize
= window
->GetSize();
149 if ( m_flag
& wxFIXED_MINSIZE
)
150 window
->SetMinSize(m_minSize
);
152 // aspect ratio calculated from initial size
156 wxSizerItem::wxSizerItem(wxWindow
*window
,
162 m_proportion(proportion
),
168 ASSERT_VALID_SIZER_FLAGS( m_flag
);
174 void wxSizerItem::DoSetSizer(wxSizer
*sizer
)
180 wxSizerItem::wxSizerItem(wxSizer
*sizer
,
187 m_proportion(proportion
),
194 ASSERT_VALID_SIZER_FLAGS( m_flag
);
198 // m_minSize is set later
202 void wxSizerItem::DoSetSpacer(const wxSize
& size
)
204 m_kind
= Item_Spacer
;
205 m_spacer
= new wxSizerSpacer(size
);
210 wxSize
wxSizerItem::AddBorderToSize(const wxSize
& size
) const
212 if (size
== wxDefaultSize
)
215 wxSize result
= size
;
217 result
.x
+= m_border
;
219 result
.x
+= m_border
;
220 if (m_flag
& wxNORTH
)
221 result
.y
+= m_border
;
222 if (m_flag
& wxSOUTH
)
223 result
.y
+= m_border
;
227 wxSizerItem::wxSizerItem(int width
,
235 m_minSize(width
, height
), // minimal size is the initial size
236 m_proportion(proportion
),
242 ASSERT_VALID_SIZER_FLAGS( m_flag
);
244 DoSetSpacer(wxSize(width
, height
));
247 wxSizerItem::~wxSizerItem()
253 void wxSizerItem::Free()
261 m_window
->SetContainingSizer(NULL
);
274 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
280 wxSize
wxSizerItem::GetSpacer() const
283 if ( m_kind
== Item_Spacer
)
284 size
= m_spacer
->GetSize();
290 wxSize
wxSizerItem::GetSize() const
299 ret
= m_window
->GetSize();
303 ret
= m_sizer
->GetSize();
307 ret
= m_spacer
->GetSize();
312 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
319 if (m_flag
& wxNORTH
)
321 if (m_flag
& wxSOUTH
)
327 bool wxSizerItem::InformFirstDirection(int direction
, int size
, int availableOtherDir
)
329 // The size that come here will be including borders. Child items should get it
333 if( direction
==wxHORIZONTAL
)
340 else if( direction
==wxVERTICAL
)
342 if (m_flag
& wxNORTH
)
344 if (m_flag
& wxSOUTH
)
350 // Pass the information along to the held object
353 didUse
= GetSizer()->InformFirstDirection(direction
,size
,availableOtherDir
);
355 m_minSize
= GetSizer()->CalcMin();
359 didUse
= GetWindow()->InformFirstDirection(direction
,size
,availableOtherDir
);
361 m_minSize
= m_window
->GetEffectiveMinSize();
363 // This information is useful for items with wxSHAPED flag, since
364 // we can request an optimal min size for such an item. Even if
365 // we overwrite the m_minSize member here, we can read it back from
366 // the owned window (happens automatically).
367 if( (m_flag
& wxSHAPED
) && (m_flag
& wxEXPAND
) && direction
)
369 if( !wxIsNullDouble(m_ratio
) )
371 wxCHECK_MSG( (m_proportion
==0), false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
372 if( direction
==wxHORIZONTAL
&& !wxIsNullDouble(m_ratio
) )
374 // Clip size so that we don't take too much
375 if( availableOtherDir
>=0 && int(size
/m_ratio
)-m_minSize
.y
>availableOtherDir
)
376 size
= int((availableOtherDir
+m_minSize
.y
)*m_ratio
);
377 m_minSize
= wxSize(size
,int(size
/m_ratio
));
379 else if( direction
==wxVERTICAL
)
381 // Clip size so that we don't take too much
382 if( availableOtherDir
>=0 && int(size
*m_ratio
)-m_minSize
.x
>availableOtherDir
)
383 size
= int((availableOtherDir
+m_minSize
.x
)/m_ratio
);
384 m_minSize
= wxSize(int(size
*m_ratio
),size
);
394 wxSize
wxSizerItem::CalcMin()
398 m_minSize
= m_sizer
->GetMinSize();
400 // if we have to preserve aspect ratio _AND_ this is
401 // the first-time calculation, consider ret to be initial size
402 if ( (m_flag
& wxSHAPED
) && wxIsNullDouble(m_ratio
) )
405 else if ( IsWindow() )
407 // Since the size of the window may change during runtime, we
408 // should use the current minimal/best size.
409 m_minSize
= m_window
->GetEffectiveMinSize();
412 return GetMinSizeWithBorder();
415 wxSize
wxSizerItem::GetMinSizeWithBorder() const
417 return AddBorderToSize(m_minSize
);
421 void wxSizerItem::SetDimension( const wxPoint
& pos_
, const wxSize
& size_
)
425 if (m_flag
& wxSHAPED
)
427 // adjust aspect ratio
428 int rwidth
= (int) (size
.y
* m_ratio
);
432 int rheight
= (int) (size
.x
/ m_ratio
);
433 // add vertical space
434 if (m_flag
& wxALIGN_CENTER_VERTICAL
)
435 pos
.y
+= (size
.y
- rheight
) / 2;
436 else if (m_flag
& wxALIGN_BOTTOM
)
437 pos
.y
+= (size
.y
- rheight
);
438 // use reduced dimensions
441 else if (rwidth
< size
.x
)
443 // add horizontal space
444 if (m_flag
& wxALIGN_CENTER_HORIZONTAL
)
445 pos
.x
+= (size
.x
- rwidth
) / 2;
446 else if (m_flag
& wxALIGN_RIGHT
)
447 pos
.x
+= (size
.x
- rwidth
);
452 // This is what GetPosition() returns. Since we calculate
453 // borders afterwards, GetPosition() will be the left/top
454 // corner of the surrounding border.
466 if (m_flag
& wxNORTH
)
471 if (m_flag
& wxSOUTH
)
481 m_rect
= wxRect(pos
, size
);
486 wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
491 // Use wxSIZE_FORCE_EVENT here since a sizer item might
492 // have changed alignment or some other property which would
493 // not change the size of the window. In such a case, no
494 // wxSizeEvent would normally be generated and thus the
495 // control wouldn't get laid out correctly here.
497 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
498 wxSIZE_ALLOW_MINUS_ONE
|wxSIZE_FORCE_EVENT
);
500 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
501 wxSIZE_ALLOW_MINUS_ONE
);
506 m_sizer
->SetDimension(pos
, size
);
510 m_spacer
->SetSize(size
);
515 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
519 void wxSizerItem::DeleteWindows()
528 //We are deleting the window from this sizer - normally
529 //the window destroys the sizer associated with it,
530 //which might destroy this, which we don't want
531 m_window
->SetContainingSizer(NULL
);
533 //Putting this after the switch will result in a spacer
534 //not being deleted properly on destruction
539 m_sizer
->DeleteWindows();
544 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
549 void wxSizerItem::Show( bool show
)
554 wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
558 m_window
->Show(show
);
566 m_spacer
->Show(show
);
571 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
575 bool wxSizerItem::IsShown() const
577 if ( m_flag
& wxRESERVE_SPACE_EVEN_IF_HIDDEN
)
583 // we may be called from CalcMin(), just return false so that we're
588 return m_window
->IsShown();
592 // arbitrarily decide that if at least one of our elements is
593 // shown, so are we (this arbitrariness is the reason for
594 // deprecating this function)
595 for ( wxSizerItemList::compatibility_iterator
596 node
= m_sizer
->GetChildren().GetFirst();
598 node
= node
->GetNext() )
600 if ( node
->GetData()->IsShown() )
607 return m_spacer
->IsShown();
611 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
617 #if WXWIN_COMPATIBILITY_2_6
618 void wxSizerItem::SetOption( int option
)
620 SetProportion( option
);
623 int wxSizerItem::GetOption() const
625 return GetProportion();
627 #endif // WXWIN_COMPATIBILITY_2_6
630 //---------------------------------------------------------------------------
632 //---------------------------------------------------------------------------
636 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
639 wxSizerItem
* wxSizer::DoInsert( size_t index
, wxSizerItem
*item
)
641 m_children
.Insert( index
, item
);
643 if ( item
->GetWindow() )
644 item
->GetWindow()->SetContainingSizer( this );
646 if ( item
->GetSizer() )
647 item
->GetSizer()->SetContainingWindow( m_containingWindow
);
652 void wxSizer::SetContainingWindow(wxWindow
*win
)
654 if ( win
== m_containingWindow
)
657 m_containingWindow
= win
;
659 // set the same window for all nested sizers as well, they also are in the
661 for ( wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
663 node
= node
->GetNext() )
665 wxSizerItem
*const item
= node
->GetData();
666 wxSizer
*const sizer
= item
->GetSizer();
670 sizer
->SetContainingWindow(win
);
675 #if WXWIN_COMPATIBILITY_2_6
676 bool wxSizer::Remove( wxWindow
*window
)
678 return Detach( window
);
680 #endif // WXWIN_COMPATIBILITY_2_6
682 bool wxSizer::Remove( wxSizer
*sizer
)
684 wxASSERT_MSG( sizer
, wxT("Removing NULL sizer") );
686 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
689 wxSizerItem
*item
= node
->GetData();
691 if (item
->GetSizer() == sizer
)
694 m_children
.Erase( node
);
698 node
= node
->GetNext();
704 bool wxSizer::Remove( int index
)
706 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
708 wxT("Remove index is out of range") );
710 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
712 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
714 delete node
->GetData();
715 m_children
.Erase( node
);
720 bool wxSizer::Detach( wxSizer
*sizer
)
722 wxASSERT_MSG( sizer
, wxT("Detaching NULL sizer") );
724 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
727 wxSizerItem
*item
= node
->GetData();
729 if (item
->GetSizer() == sizer
)
733 m_children
.Erase( node
);
736 node
= node
->GetNext();
742 bool wxSizer::Detach( wxWindow
*window
)
744 wxASSERT_MSG( window
, wxT("Detaching NULL window") );
746 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
749 wxSizerItem
*item
= node
->GetData();
751 if (item
->GetWindow() == window
)
754 m_children
.Erase( node
);
757 node
= node
->GetNext();
763 bool wxSizer::Detach( int index
)
765 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
767 wxT("Detach index is out of range") );
769 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
771 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
773 wxSizerItem
*item
= node
->GetData();
775 if ( item
->IsSizer() )
779 m_children
.Erase( node
);
783 bool wxSizer::Replace( wxWindow
*oldwin
, wxWindow
*newwin
, bool recursive
)
785 wxASSERT_MSG( oldwin
, wxT("Replacing NULL window") );
786 wxASSERT_MSG( newwin
, wxT("Replacing with NULL window") );
788 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
791 wxSizerItem
*item
= node
->GetData();
793 if (item
->GetWindow() == oldwin
)
795 item
->AssignWindow(newwin
);
796 newwin
->SetContainingSizer( this );
799 else if (recursive
&& item
->IsSizer())
801 if (item
->GetSizer()->Replace( oldwin
, newwin
, true ))
805 node
= node
->GetNext();
811 bool wxSizer::Replace( wxSizer
*oldsz
, wxSizer
*newsz
, bool recursive
)
813 wxASSERT_MSG( oldsz
, wxT("Replacing NULL sizer") );
814 wxASSERT_MSG( newsz
, wxT("Replacing with NULL sizer") );
816 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
819 wxSizerItem
*item
= node
->GetData();
821 if (item
->GetSizer() == oldsz
)
823 item
->AssignSizer(newsz
);
826 else if (recursive
&& item
->IsSizer())
828 if (item
->GetSizer()->Replace( oldsz
, newsz
, true ))
832 node
= node
->GetNext();
838 bool wxSizer::Replace( size_t old
, wxSizerItem
*newitem
)
840 wxCHECK_MSG( old
< m_children
.GetCount(), false, wxT("Replace index is out of range") );
841 wxASSERT_MSG( newitem
, wxT("Replacing with NULL item") );
843 wxSizerItemList::compatibility_iterator node
= m_children
.Item( old
);
845 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
847 wxSizerItem
*item
= node
->GetData();
848 node
->SetData(newitem
);
850 if (item
->IsWindow() && item
->GetWindow())
851 item
->GetWindow()->SetContainingSizer(NULL
);
858 void wxSizer::Clear( bool delete_windows
)
860 // First clear the ContainingSizer pointers
861 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
864 wxSizerItem
*item
= node
->GetData();
866 if (item
->IsWindow())
867 item
->GetWindow()->SetContainingSizer( NULL
);
868 node
= node
->GetNext();
871 // Destroy the windows if needed
875 // Now empty the list
876 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
879 void wxSizer::DeleteWindows()
881 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
884 wxSizerItem
*item
= node
->GetData();
886 item
->DeleteWindows();
887 node
= node
->GetNext();
891 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
893 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
895 // take the min size by default and limit it by max size
896 wxSize size
= GetMinClientSize(window
);
899 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
902 // hack for small screen devices where TLWs are always full screen
903 if ( tlw
->IsAlwaysMaximized() )
905 return tlw
->GetClientSize();
908 // limit the window to the size of the display it is on
909 int disp
= wxDisplay::GetFromWindow(window
);
910 if ( disp
== wxNOT_FOUND
)
912 // or, if we don't know which one it is, of the main one
916 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
918 // If determining the display size failed, skip the max size checks as
919 // we really don't want to create windows of (0, 0) size.
920 if ( !sizeMax
.x
|| !sizeMax
.y
)
923 // space for decorations and toolbars etc.
924 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
928 sizeMax
= GetMaxClientSize(window
);
931 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
933 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
939 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
941 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
943 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
946 wxSize
wxSizer::Fit( wxWindow
*window
)
948 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
951 window
->SetClientSize(ComputeFittingClientSize(window
));
953 // return entire size
954 return window
->GetSize();
957 void wxSizer::FitInside( wxWindow
*window
)
960 if (window
->IsTopLevel())
961 size
= VirtualFitSize( window
);
963 size
= GetMinClientSize( window
);
965 window
->SetVirtualSize( size
);
968 void wxSizer::Layout()
970 // (re)calculates minimums needed for each item and other preparations
974 // Applies the layout and repositions/resizes the items
978 void wxSizer::SetSizeHints( wxWindow
*window
)
980 // Preserve the window's max size hints, but set the
981 // lower bound according to the sizer calculations.
983 // This is equivalent to calling Fit(), except that we need to set
984 // the size hints _in between_ the two steps performed by Fit
985 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
986 // otherwise SetClientSize() could have no effect if there already are
987 // size hints in effect that forbid requested client size.
989 const wxSize clientSize
= ComputeFittingClientSize(window
);
991 window
->SetMinClientSize(clientSize
);
992 window
->SetClientSize(clientSize
);
995 #if WXWIN_COMPATIBILITY_2_8
996 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
1000 #endif // WXWIN_COMPATIBILITY_2_8
1002 // TODO on mac we need a function that determines how much free space this
1003 // min size contains, in order to make sure that we have 20 pixels of free
1004 // space around the controls
1005 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
1007 return window
->WindowToClientSize(window
->GetMaxSize());
1010 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
1012 return GetMinSize(); // Already returns client size.
1015 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1017 wxSize size
= GetMinClientSize( window
);
1018 wxSize sizeMax
= GetMaxClientSize( window
);
1020 // Limit the size if sizeMax != wxDefaultSize
1022 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1024 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1030 wxSize
wxSizer::GetMinSize()
1032 wxSize
ret( CalcMin() );
1033 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1034 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1038 void wxSizer::DoSetMinSize( int width
, int height
)
1040 m_minSize
.x
= width
;
1041 m_minSize
.y
= height
;
1044 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1046 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1048 // Is it our immediate child?
1050 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1053 wxSizerItem
*item
= node
->GetData();
1055 if (item
->GetWindow() == window
)
1057 item
->SetMinSize( width
, height
);
1060 node
= node
->GetNext();
1063 // No? Search any subsizers we own then
1065 node
= m_children
.GetFirst();
1068 wxSizerItem
*item
= node
->GetData();
1070 if ( item
->GetSizer() &&
1071 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1073 // A child sizer found the requested windw, exit.
1076 node
= node
->GetNext();
1082 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1084 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1086 // Is it our immediate child?
1088 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1091 wxSizerItem
*item
= node
->GetData();
1093 if (item
->GetSizer() == sizer
)
1095 item
->GetSizer()->DoSetMinSize( width
, height
);
1098 node
= node
->GetNext();
1101 // No? Search any subsizers we own then
1103 node
= m_children
.GetFirst();
1106 wxSizerItem
*item
= node
->GetData();
1108 if ( item
->GetSizer() &&
1109 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1111 // A child found the requested sizer, exit.
1114 node
= node
->GetNext();
1120 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1122 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1124 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1126 wxSizerItem
*item
= node
->GetData();
1128 if (item
->GetSizer())
1130 // Sizers contains the minimal size in them, if not calculated ...
1131 item
->GetSizer()->DoSetMinSize( width
, height
);
1135 // ... but the minimal size of spacers and windows is stored via the item
1136 item
->SetMinSize( width
, height
);
1142 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1144 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1146 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1149 wxSizerItem
*item
= node
->GetData();
1151 if (item
->GetWindow() == window
)
1155 else if (recursive
&& item
->IsSizer())
1157 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1162 node
= node
->GetNext();
1168 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1170 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1172 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1175 wxSizerItem
*item
= node
->GetData();
1177 if (item
->GetSizer() == sizer
)
1181 else if (recursive
&& item
->IsSizer())
1183 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1188 node
= node
->GetNext();
1194 wxSizerItem
* wxSizer::GetItem( size_t index
)
1196 wxCHECK_MSG( index
< m_children
.GetCount(),
1198 wxT("GetItem index is out of range") );
1200 return m_children
.Item( index
)->GetData();
1203 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1205 // This gets a sizer item by the id of the sizer item
1206 // and NOT the id of a window if the item is a window.
1208 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1211 wxSizerItem
*item
= node
->GetData();
1213 if (item
->GetId() == id
)
1217 else if (recursive
&& item
->IsSizer())
1219 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1224 node
= node
->GetNext();
1230 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1232 wxSizerItem
*item
= GetItem( window
, recursive
);
1243 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1245 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1256 bool wxSizer::Show( size_t index
, bool show
)
1258 wxSizerItem
*item
= GetItem( index
);
1269 void wxSizer::ShowItems( bool show
)
1271 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1274 node
->GetData()->Show( show
);
1275 node
= node
->GetNext();
1279 bool wxSizer::IsShown( wxWindow
*window
) const
1281 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1284 wxSizerItem
*item
= node
->GetData();
1286 if (item
->GetWindow() == window
)
1288 return item
->IsShown();
1290 node
= node
->GetNext();
1293 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1298 bool wxSizer::IsShown( wxSizer
*sizer
) const
1300 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1303 wxSizerItem
*item
= node
->GetData();
1305 if (item
->GetSizer() == sizer
)
1307 return item
->IsShown();
1309 node
= node
->GetNext();
1312 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1317 bool wxSizer::IsShown( size_t index
) const
1319 wxCHECK_MSG( index
< m_children
.GetCount(),
1321 wxT("IsShown index is out of range") );
1323 return m_children
.Item( index
)->GetData()->IsShown();
1327 //---------------------------------------------------------------------------
1329 //---------------------------------------------------------------------------
1331 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1332 : m_rows( cols
== 0 ? 1 : 0 ),
1337 wxASSERT(cols
>= 0);
1340 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1341 : m_rows( cols
== 0 ? 1 : 0 ),
1343 m_vgap( gap
.GetHeight() ),
1344 m_hgap( gap
.GetWidth() )
1346 wxASSERT(cols
>= 0);
1349 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1350 : m_rows( rows
|| cols
? rows
: 1 ),
1355 wxASSERT(rows
>= 0 && cols
>= 0);
1358 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1359 : m_rows( rows
|| cols
? rows
: 1 ),
1361 m_vgap( gap
.GetHeight() ),
1362 m_hgap( gap
.GetWidth() )
1364 wxASSERT(rows
>= 0 && cols
>= 0);
1367 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1369 // if only the number of columns or the number of rows is specified for a
1370 // sizer, arbitrarily many items can be added to it but if both of them are
1371 // fixed, then the sizer can't have more than that many items -- check for
1372 // this here to ensure that we detect errors as soon as possible
1373 if ( m_cols
&& m_rows
)
1375 const int nitems
= m_children
.GetCount();
1376 if ( nitems
== m_cols
*m_rows
)
1380 "too many items (%d > %d*%d) in grid sizer (maybe you "
1381 "should omit the number of either rows or columns?)",
1382 nitems
+ 1, m_cols
, m_rows
)
1385 // additionally, continuing to use the specified number of columns
1386 // and rows is not a good idea as callers of CalcRowsCols() expect
1387 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1388 // which is not the case if there are too many items and results in
1389 // crashes, so let it compute the number of rows automatically by
1390 // forgetting the (wrong) number of rows specified (this also has a
1391 // nice side effect of giving only one assert even if there are
1392 // many more items than allowed in this sizer)
1397 return wxSizer::DoInsert(index
, item
);
1400 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1402 const int nitems
= m_children
.GetCount();
1404 ncols
= GetEffectiveColsCount();
1405 nrows
= GetEffectiveRowsCount();
1407 // Since Insert() checks for overpopulation, the following
1408 // should only assert if the grid was shrunk via SetRows() / SetCols()
1409 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1414 void wxGridSizer::RecalcSizes()
1416 int nitems
, nrows
, ncols
;
1417 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1420 wxSize
sz( GetSize() );
1421 wxPoint
pt( GetPosition() );
1423 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1424 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1427 for (int c
= 0; c
< ncols
; c
++)
1430 for (int r
= 0; r
< nrows
; r
++)
1432 int i
= r
* ncols
+ c
;
1435 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1437 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1439 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1447 wxSize
wxGridSizer::CalcMin()
1450 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1453 // Find the max width and height for any component
1457 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1460 wxSizerItem
*item
= node
->GetData();
1461 wxSize
sz( item
->CalcMin() );
1463 w
= wxMax( w
, sz
.x
);
1464 h
= wxMax( h
, sz
.y
);
1466 node
= node
->GetNext();
1469 // In case we have a nested sizer with a two step algo , give it
1470 // a chance to adjust to that (we give it width component)
1471 node
= m_children
.GetFirst();
1472 bool didChangeMinSize
= false;
1475 wxSizerItem
*item
= node
->GetData();
1476 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1478 node
= node
->GetNext();
1481 // And redo iteration in case min size changed
1482 if( didChangeMinSize
)
1484 node
= m_children
.GetFirst();
1488 wxSizerItem
*item
= node
->GetData();
1489 wxSize
sz( item
->GetMinSizeWithBorder() );
1491 w
= wxMax( w
, sz
.x
);
1492 h
= wxMax( h
, sz
.y
);
1494 node
= node
->GetNext();
1498 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1499 nrows
* h
+ (nrows
-1) * m_vgap
);
1502 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1505 wxSize
sz( item
->GetMinSizeWithBorder() );
1506 int flag
= item
->GetFlag();
1508 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1514 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1516 pt
.x
= x
+ (w
- sz
.x
) / 2;
1518 else if (flag
& wxALIGN_RIGHT
)
1520 pt
.x
= x
+ (w
- sz
.x
);
1523 if (flag
& wxALIGN_CENTER_VERTICAL
)
1525 pt
.y
= y
+ (h
- sz
.y
) / 2;
1527 else if (flag
& wxALIGN_BOTTOM
)
1529 pt
.y
= y
+ (h
- sz
.y
);
1533 item
->SetDimension(pt
, sz
);
1536 //---------------------------------------------------------------------------
1538 //---------------------------------------------------------------------------
1540 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1541 : wxGridSizer( cols
, vgap
, hgap
),
1542 m_flexDirection(wxBOTH
),
1543 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1547 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1548 : wxGridSizer( cols
, gap
),
1549 m_flexDirection(wxBOTH
),
1550 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1554 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1555 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1556 m_flexDirection(wxBOTH
),
1557 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1561 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1562 : wxGridSizer( rows
, cols
, gap
),
1563 m_flexDirection(wxBOTH
),
1564 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1568 wxFlexGridSizer::~wxFlexGridSizer()
1572 void wxFlexGridSizer::RecalcSizes()
1575 if ( !CalcRowsCols(nrows
, ncols
) )
1578 const wxPoint
pt(GetPosition());
1579 const wxSize
sz(GetSize());
1581 AdjustForGrowables(sz
);
1583 wxSizerItemList::const_iterator i
= m_children
.begin();
1584 const wxSizerItemList::const_iterator end
= m_children
.end();
1587 for ( int r
= 0; r
< nrows
; r
++ )
1589 if ( m_rowHeights
[r
] == -1 )
1591 // this row is entirely hidden, skip it
1592 for ( int c
= 0; c
< ncols
; c
++ )
1603 const int hrow
= m_rowHeights
[r
];
1604 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1609 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1611 const int wcol
= m_colWidths
[c
];
1616 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1620 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1632 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1633 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1635 // Sum total minimum size, including gaps between rows/columns.
1636 // -1 is used as a magic number meaning empty row/column.
1639 const size_t count
= sizes
.size();
1640 for ( size_t n
= 0; n
< count
; n
++ )
1642 if ( sizes
[n
] != -1 )
1645 total
+= gap
; // separate from the previous column
1654 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1656 // We have to recalculate the sizes in case the item minimum size has
1657 // changed since the previous layout, or the item has been hidden using
1658 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1659 // dimension of the row/column will be -1, indicating that the column
1660 // itself is hidden.
1661 m_rowHeights
.assign(nrows
, -1);
1662 m_colWidths
.assign(ncols
, -1);
1664 // n is the index of the item in left-to-right top-to-bottom order
1666 for ( wxSizerItemList::iterator i
= m_children
.begin();
1667 i
!= m_children
.end();
1670 wxSizerItem
* const item
= *i
;
1671 if ( item
->IsShown() )
1673 // NOTE: Not doing the calculation here, this is just
1674 // for finding max values.
1675 const wxSize
sz(item
->GetMinSizeWithBorder());
1677 const int row
= n
/ ncols
;
1678 const int col
= n
% ncols
;
1680 if ( sz
.y
> m_rowHeights
[row
] )
1681 m_rowHeights
[row
] = sz
.y
;
1682 if ( sz
.x
> m_colWidths
[col
] )
1683 m_colWidths
[col
] = sz
.x
;
1687 AdjustForFlexDirection();
1689 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1690 SumArraySizes(m_rowHeights
, m_vgap
));
1693 wxSize
wxFlexGridSizer::CalcMin()
1698 // Number of rows/columns can change as items are added or removed.
1699 if ( !CalcRowsCols(nrows
, ncols
) )
1703 // We have to recalculate the sizes in case the item minimum size has
1704 // changed since the previous layout, or the item has been hidden using
1705 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1706 // dimension of the row/column will be -1, indicating that the column
1707 // itself is hidden.
1708 m_rowHeights
.assign(nrows
, -1);
1709 m_colWidths
.assign(ncols
, -1);
1711 for ( wxSizerItemList::iterator i
= m_children
.begin();
1712 i
!= m_children
.end();
1715 wxSizerItem
* const item
= *i
;
1716 if ( item
->IsShown() )
1722 // The stage of looking for max values in each row/column has been
1723 // made a separate function, since it's reused in AdjustForGrowables.
1724 FindWidthsAndHeights(nrows
,ncols
);
1726 return m_calculatedMinSize
;
1729 void wxFlexGridSizer::AdjustForFlexDirection()
1731 // the logic in CalcMin works when we resize flexibly in both directions
1732 // but maybe this is not the case
1733 if ( m_flexDirection
!= wxBOTH
)
1735 // select the array corresponding to the direction in which we do *not*
1737 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1740 const size_t count
= array
.GetCount();
1742 // find the largest value in this array
1746 for ( n
= 0; n
< count
; ++n
)
1748 if ( array
[n
] > largest
)
1752 // and now fill it with the largest value
1753 for ( n
= 0; n
< count
; ++n
)
1755 // don't touch hidden rows
1756 if ( array
[n
] != -1 )
1762 // helper of AdjustForGrowables() which is called for rows/columns separately
1765 // delta: the extra space, we do nothing unless it's positive
1766 // growable: indices or growable rows/cols in sizes array
1767 // sizes: the height/widths of rows/cols to adjust
1768 // proportions: proportions of the growable rows/cols or NULL if they all
1769 // should be assumed to have proportion of 1
1771 DoAdjustForGrowables(int delta
,
1772 const wxArrayInt
& growable
,
1774 const wxArrayInt
*proportions
)
1779 // total sum of proportions of all non-hidden rows
1780 int sum_proportions
= 0;
1782 // number of currently shown growable rows
1785 const int max_idx
= sizes
.size();
1787 const size_t count
= growable
.size();
1789 for ( idx
= 0; idx
< count
; idx
++ )
1791 // Since the number of rows/columns can change as items are
1792 // inserted/deleted, we need to verify at runtime that the
1793 // requested growable rows/columns are still valid.
1794 if ( growable
[idx
] >= max_idx
)
1797 // If all items in a row/column are hidden, that row/column will
1798 // have a dimension of -1. This causes the row/column to be
1799 // hidden completely.
1800 if ( sizes
[growable
[idx
]] == -1 )
1804 sum_proportions
+= (*proportions
)[idx
];
1812 // the remaining extra free space, adjusted during each iteration
1813 for ( idx
= 0; idx
< count
; idx
++ )
1815 if ( growable
[idx
] >= max_idx
)
1818 if ( sizes
[ growable
[idx
] ] == -1 )
1822 if ( sum_proportions
== 0 )
1824 // no growable rows -- divide extra space evenly among all
1825 cur_delta
= delta
/num
;
1828 else // allocate extra space proportionally
1830 const int cur_prop
= (*proportions
)[idx
];
1831 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1832 sum_proportions
-= cur_prop
;
1835 sizes
[growable
[idx
]] += cur_delta
;
1840 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1843 // by the time this function is called, the sizer should be already fully
1844 // initialized and hence the number of its columns and rows is known and we
1845 // can check that all indices in m_growableCols/Rows are valid (see also
1846 // comments in AddGrowableCol/Row())
1847 if ( !m_rows
|| !m_cols
)
1851 int nrows
= CalcRows();
1853 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1855 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1856 "invalid growable row index" );
1862 int ncols
= CalcCols();
1864 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1866 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1867 "invalid growable column index" );
1871 #endif // wxDEBUG_LEVEL
1874 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1876 DoAdjustForGrowables
1878 sz
.x
- m_calculatedMinSize
.x
,
1881 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1885 // This gives nested objects that benefit from knowing one size
1886 // component in advance the chance to use that.
1887 bool didAdjustMinSize
= false;
1889 // Iterate over all items and inform about column width
1890 const int ncols
= GetEffectiveColsCount();
1892 for ( wxSizerItemList::iterator i
= m_children
.begin();
1893 i
!= m_children
.end();
1896 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1897 if ( ++col
== ncols
)
1901 // Only redo if info was actually used
1902 if( didAdjustMinSize
)
1904 DoAdjustForGrowables
1906 sz
.x
- m_calculatedMinSize
.x
,
1909 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1915 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1917 // pass NULL instead of proportions if the grow mode is ALL as we
1918 // should treat all rows as having proportion of 1 then
1919 DoAdjustForGrowables
1921 sz
.y
- m_calculatedMinSize
.y
,
1924 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1930 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1932 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1935 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1937 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1940 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1942 wxASSERT_MSG( !IsRowGrowable( idx
),
1943 "AddGrowableRow() called for growable row" );
1945 // notice that we intentionally don't check the index validity here in (the
1946 // common) case when the number of rows was not specified in the ctor -- in
1947 // this case it will be computed only later, when all items are added to
1948 // the sizer, and the check will be done in AdjustForGrowables()
1949 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1951 m_growableRows
.Add( idx
);
1952 m_growableRowsProportions
.Add( proportion
);
1955 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1957 wxASSERT_MSG( !IsColGrowable( idx
),
1958 "AddGrowableCol() called for growable column" );
1960 // see comment in AddGrowableRow(): although it's less common to omit the
1961 // specification of the number of columns, it still can also happen
1962 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1964 m_growableCols
.Add( idx
);
1965 m_growableColsProportions
.Add( proportion
);
1968 // helper function for RemoveGrowableCol/Row()
1970 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1972 const size_t count
= items
.size();
1973 for ( size_t n
= 0; n
< count
; n
++ )
1975 if ( (size_t)items
[n
] == idx
)
1978 proportions
.RemoveAt(n
);
1983 wxFAIL_MSG( wxT("column/row is already not growable") );
1986 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
1988 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
1991 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
1993 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
1996 //---------------------------------------------------------------------------
1998 //---------------------------------------------------------------------------
2000 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
2002 return IsVertical() ? Add(0, size
) : Add(size
, 0);
2009 Helper of RecalcSizes(): checks if there is enough remaining space for the
2010 min size of the given item and returns its min size or the entire remaining
2011 space depending on which one is greater.
2013 This function updates the remaining space parameter to account for the size
2014 effectively allocated to the item.
2017 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2019 int& remainingSpace
= *remainingSpace_
;
2022 if ( remainingSpace
> 0 )
2024 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2025 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2027 if ( size
>= remainingSpace
)
2029 // truncate the item to fit in the remaining space, this is better
2030 // than showing it only partially in general, even if both choices
2031 // are bad -- but there is nothing else we can do
2032 size
= remainingSpace
;
2035 remainingSpace
-= size
;
2037 else // no remaining space
2039 // no space at all left, no need to even query the item for its min
2040 // size as we can't give it to it anyhow
2047 } // anonymous namespace
2049 void wxBoxSizer::RecalcSizes()
2051 if ( m_children
.empty() )
2054 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2055 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2057 // the amount of free space which we should redistribute among the
2058 // stretchable items (i.e. those with non zero proportion)
2059 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2061 // declare loop variables used below:
2062 wxSizerItemList::const_iterator i
; // iterator in m_children list
2063 unsigned n
= 0; // item index in majorSizes array
2066 // First, inform item about the available size in minor direction as this
2067 // can change their size in the major direction. Also compute the number of
2068 // visible items and sum of their min sizes in major direction.
2070 int minMajorSize
= 0;
2071 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2073 wxSizerItem
* const item
= *i
;
2075 if ( !item
->IsShown() )
2078 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2079 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2080 wxSize szMin
= item
->GetMinSizeWithBorder();
2081 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2084 // Since we passed available space along to the item, it should not
2085 // take too much, so delta should not become negative.
2086 delta
-= deltaChange
;
2088 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2091 // update our min size and delta which may have changed
2092 SizeInMajorDir(m_minSize
) = minMajorSize
;
2093 delta
= totalMajorSize
- minMajorSize
;
2096 // space and sum of proportions for the remaining items, both may change
2098 wxCoord remaining
= totalMajorSize
;
2099 int totalProportion
= m_totalProportion
;
2101 // size of the (visible) items in major direction, -1 means "not fixed yet"
2102 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2105 // Check for the degenerated case when we don't have enough space for even
2106 // the min sizes of all the items: in this case we really can't do much
2107 // more than to allocate the min size to as many of fixed size items as
2108 // possible (on the assumption that variable size items such as text zones
2109 // or list boxes may use scrollbars to show their content even if their
2110 // size is less than min size but that fixed size items such as buttons
2111 // will suffer even more if we don't give them their min size)
2112 if ( totalMajorSize
< minMajorSize
)
2114 // Second degenerated case pass: allocate min size to all fixed size
2116 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2118 wxSizerItem
* const item
= *i
;
2120 if ( !item
->IsShown() )
2123 // deal with fixed size items only during this pass
2124 if ( item
->GetProportion() )
2127 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2131 // Third degenerated case pass: allocate min size to all the remaining,
2132 // i.e. non-fixed size, items.
2133 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2135 wxSizerItem
* const item
= *i
;
2137 if ( !item
->IsShown() )
2140 // we've already dealt with fixed size items above
2141 if ( !item
->GetProportion() )
2144 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2147 else // we do have enough space to give at least min sizes to all items
2149 // Second and maybe more passes in the non-degenerated case: deal with
2150 // fixed size items and items whose min size is greater than what we
2151 // would allocate to them taking their proportion into account. For
2152 // both of them, we will just use their min size, but for the latter we
2153 // also need to reexamine all the items as the items which fitted
2154 // before we adjusted their size upwards might not fit any more. This
2155 // does make for a quadratic algorithm but it's not obvious how to
2156 // avoid it and hopefully it's not a huge problem in practice as the
2157 // sizers don't have many items usually (and, of course, the algorithm
2158 // still reduces into a linear one if there is enough space for all the
2160 bool nonFixedSpaceChanged
= false;
2161 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2163 if ( nonFixedSpaceChanged
)
2165 i
= m_children
.begin();
2167 nonFixedSpaceChanged
= false;
2170 // check for the end of the loop only after the check above as
2171 // otherwise we wouldn't do another pass if the last child resulted
2172 // in non fixed space reduction
2173 if ( i
== m_children
.end() )
2176 wxSizerItem
* const item
= *i
;
2178 if ( !item
->IsShown() )
2181 // don't check the item which we had already dealt with during a
2182 // previous pass (this is more than an optimization, the code
2183 // wouldn't work correctly if we kept adjusting for the same item
2184 // over and over again)
2185 if ( majorSizes
[n
] != wxDefaultCoord
)
2188 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2190 // it doesn't make sense for min size to be negative but right now
2191 // it's possible to create e.g. a spacer with (-1, 10) as size and
2192 // people do it in their code apparently (see #11842) so ensure
2193 // that we don't use this -1 as real min size as it conflicts with
2194 // the meaning we use for it here and negative min sizes just don't
2195 // make sense anyhow (which is why it might be a better idea to
2196 // deal with them at wxSizerItem level in the future but for now
2197 // this is the minimal fix for the bug)
2201 const int propItem
= item
->GetProportion();
2204 // is the desired size of this item big enough?
2205 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2207 // yes, it is, we'll determine the real size of this
2208 // item later, for now just leave it as wxDefaultCoord
2212 // the proportion of this item won't count, it has
2213 // effectively become fixed
2214 totalProportion
-= propItem
;
2217 // we can already allocate space for this item
2218 majorSizes
[n
] = minMajor
;
2220 // change the amount of the space remaining to the other items,
2221 // as this can result in not being able to satisfy their
2222 // proportions any more we will need to redo another loop
2224 remaining
-= minMajor
;
2226 nonFixedSpaceChanged
= true;
2230 // Last by one pass: distribute the remaining space among the non-fixed
2231 // items whose size weren't fixed yet according to their proportions.
2232 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2234 wxSizerItem
* const item
= *i
;
2236 if ( !item
->IsShown() )
2239 if ( majorSizes
[n
] == wxDefaultCoord
)
2241 const int propItem
= item
->GetProportion();
2242 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2244 remaining
-= majorSizes
[n
];
2245 totalProportion
-= propItem
;
2251 // the position at which we put the next child
2252 wxPoint
pt(m_position
);
2255 // Final pass: finally do position the items correctly using their sizes as
2256 // determined above.
2257 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2259 wxSizerItem
* const item
= *i
;
2261 if ( !item
->IsShown() )
2264 const int majorSize
= majorSizes
[n
];
2266 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2268 // apply the alignment in the minor direction
2269 wxPoint
posChild(pt
);
2271 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2272 const int flag
= item
->GetFlag();
2273 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2275 // occupy all the available space if wxEXPAND was given and also if
2276 // the item is too big to fit -- in this case we truncate it below
2277 // its minimal size which is bad but better than not showing parts
2278 // of the window at all
2279 minorSize
= totalMinorSize
;
2281 else if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2283 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2285 // NB: wxCENTRE is used here only for backwards compatibility,
2286 // wxALIGN_CENTRE should be used in new code
2287 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2288 : wxALIGN_CENTRE_VERTICAL
)) )
2290 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2294 // apply RTL adjustment for horizontal sizers:
2295 if ( !IsVertical() && m_containingWindow
)
2297 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2305 // finally set size of this child and advance to the next one
2306 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2308 PosInMajorDir(pt
) += majorSize
;
2312 wxSize
wxBoxSizer::CalcMin()
2314 m_totalProportion
= 0;
2315 m_minSize
= wxSize(0, 0);
2317 // The minimal size for the sizer should be big enough to allocate its
2318 // element at least its minimal size but also, and this is the non trivial
2319 // part, to respect the children proportion. To satisfy the latter
2320 // condition we must find the greatest min-size-to-proportion ratio for all
2321 // elements with non-zero proportion.
2322 float maxMinSizeToProp
= 0.;
2323 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2324 i
!= m_children
.end();
2327 wxSizerItem
* const item
= *i
;
2329 if ( !item
->IsShown() )
2332 const wxSize sizeMinThis
= item
->CalcMin();
2333 if ( const int propThis
= item
->GetProportion() )
2335 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2336 minSizeToProp
/= propThis
;
2338 if ( minSizeToProp
> maxMinSizeToProp
)
2339 maxMinSizeToProp
= minSizeToProp
;
2341 m_totalProportion
+= item
->GetProportion();
2343 else // fixed size item
2345 // Just account for its size directly
2346 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2349 // In the transversal direction we just need to find the maximum.
2350 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2351 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2354 // Using the max ratio ensures that the min size is big enough for all
2355 // items to have their min size and satisfy the proportions among them.
2356 SizeInMajorDir(m_minSize
) += (int)(maxMinSizeToProp
*m_totalProportion
);
2361 //---------------------------------------------------------------------------
2363 //---------------------------------------------------------------------------
2367 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2368 : wxBoxSizer( orient
),
2371 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2373 // do this so that our Detach() is called if the static box is destroyed
2375 m_staticBox
->SetContainingSizer(this);
2378 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2379 : wxBoxSizer(orient
),
2380 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2383 m_staticBox
->SetContainingSizer(this);
2386 wxStaticBoxSizer::~wxStaticBoxSizer()
2391 void wxStaticBoxSizer::RecalcSizes()
2393 int top_border
, other_border
;
2394 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2396 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2398 wxSize
old_size( m_size
);
2399 m_size
.x
-= 2*other_border
;
2400 m_size
.y
-= top_border
+ other_border
;
2402 wxPoint
old_pos( m_position
);
2403 if (m_staticBox
->GetChildren().GetCount() > 0)
2405 #if defined( __WXGTK20__ )
2406 // if the wxStaticBox has created a wxPizza to contain its children
2407 // (see wxStaticBox::AddChild) then we need to place the items it contains
2408 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2409 // to the top-left corner of the staticbox:
2410 m_position
.x
= m_position
.y
= 0;
2411 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
2412 // the distance from the 'inner' content view to the embedded controls
2413 // this is independent of the title, therefore top_border is not relevant
2414 m_position
.x
= m_position
.y
= 10;
2416 // if the wxStaticBox has children, then these windows must be placed
2417 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2418 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2419 // to keep in count the static borders here!):
2420 m_position
.x
= other_border
;
2421 m_position
.y
= top_border
;
2426 // the windows contained in the staticbox have been created as siblings of the
2427 // staticbox (this is the "old" way of staticbox contents creation); in this
2428 // case we need to position them with coordinates relative to our common parent
2429 m_position
.x
+= other_border
;
2430 m_position
.y
+= top_border
;
2433 wxBoxSizer::RecalcSizes();
2435 m_position
= old_pos
;
2439 wxSize
wxStaticBoxSizer::CalcMin()
2441 int top_border
, other_border
;
2442 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2444 wxSize
ret( wxBoxSizer::CalcMin() );
2445 ret
.x
+= 2*other_border
;
2447 // ensure that we're wide enough to show the static box label (there is no
2448 // need to check for the static box best size in vertical direction though)
2449 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2450 if ( ret
.x
< boxWidth
)
2453 ret
.y
+= other_border
+ top_border
;
2458 void wxStaticBoxSizer::ShowItems( bool show
)
2460 m_staticBox
->Show( show
);
2461 wxBoxSizer::ShowItems( show
);
2464 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2466 // avoid deleting m_staticBox in our dtor if it's being detached from the
2467 // sizer (which can happen because it's being already destroyed for
2469 if ( window
== m_staticBox
)
2475 return wxSizer::Detach( window
);
2478 #endif // wxUSE_STATBOX
2480 //---------------------------------------------------------------------------
2481 // wxStdDialogButtonSizer
2482 //---------------------------------------------------------------------------
2486 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2487 : wxBoxSizer(wxHORIZONTAL
)
2489 // Vertical buttons with lots of space on either side
2490 // looks rubbish on WinCE, so let's not do this for now.
2491 // If we are going to use vertical buttons, we should
2492 // put the sizer to the right of other controls in the dialog,
2493 // and that's beyond the scope of this sizer.
2495 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2496 // If we have a PDA screen, put yes/no button over
2497 // all other buttons, otherwise on the left side.
2499 m_orient
= wxVERTICAL
;
2502 m_buttonAffirmative
= NULL
;
2503 m_buttonApply
= NULL
;
2504 m_buttonNegative
= NULL
;
2505 m_buttonCancel
= NULL
;
2506 m_buttonHelp
= NULL
;
2509 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2511 switch (mybutton
->GetId())
2516 m_buttonAffirmative
= mybutton
;
2519 m_buttonApply
= mybutton
;
2522 m_buttonNegative
= mybutton
;
2526 m_buttonCancel
= mybutton
;
2529 case wxID_CONTEXT_HELP
:
2530 m_buttonHelp
= mybutton
;
2537 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2539 m_buttonAffirmative
= button
;
2542 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2544 m_buttonNegative
= button
;
2547 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2549 m_buttonCancel
= button
;
2552 void wxStdDialogButtonSizer::Realize()
2555 Add(0, 0, 0, wxLEFT
, 6);
2557 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2559 if (m_buttonNegative
){
2560 // HIG POLICE BULLETIN - destructive buttons need extra padding
2561 // 24 pixels on either side
2562 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2565 // extra whitespace between help/negative and cancel/ok buttons
2566 Add(0, 0, 1, wxEXPAND
, 0);
2568 if (m_buttonCancel
){
2569 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2570 // Cancel or help should be default
2571 // m_buttonCancel->SetDefaultButton();
2574 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2575 // figure the best place is between Cancel and OK
2577 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2579 if (m_buttonAffirmative
){
2580 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2582 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2583 // these buttons have set labels under Mac so we should use them
2584 m_buttonAffirmative
->SetLabel(_("Save"));
2585 if (m_buttonNegative
)
2586 m_buttonNegative
->SetLabel(_("Don't Save"));
2590 // Extra space around and at the right
2592 #elif defined(__WXGTK20__)
2593 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2594 // says that the correct button order is
2596 // [Help] [Alternative] [Cancel] [Affirmative]
2598 // Flags ensuring that margins between the buttons are 6 pixels.
2600 flagsBtn
= wxSizerFlags().Centre().Border(wxLEFT
| wxRIGHT
, 3);
2602 // Margin around the entire sizer button should be 12.
2606 Add(m_buttonHelp
, flagsBtn
);
2608 // Align the rest of the buttons to the right.
2611 if (m_buttonNegative
)
2612 Add(m_buttonNegative
, flagsBtn
);
2615 Add(m_buttonApply
, flagsBtn
);
2618 Add(m_buttonCancel
, flagsBtn
);
2620 if (m_buttonAffirmative
)
2621 Add(m_buttonAffirmative
, flagsBtn
);
2623 // Ensure that the right margin is 12 as well.
2625 #elif defined(__WXMSW__)
2628 // right-justify buttons
2629 Add(0, 0, 1, wxEXPAND
, 0);
2631 if (m_buttonAffirmative
){
2632 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2635 if (m_buttonNegative
){
2636 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2639 if (m_buttonCancel
){
2640 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2643 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2646 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2648 // GTK+1 and any other platform
2650 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2652 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2654 // extra whitespace between help and cancel/ok buttons
2655 Add(0, 0, 1, wxEXPAND
, 0);
2658 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2660 if (m_buttonAffirmative
){
2661 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2664 if (m_buttonNegative
){
2665 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2668 if (m_buttonCancel
){
2669 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2670 // Cancel or help should be default
2671 // m_buttonCancel->SetDefaultButton();
2677 #endif // wxUSE_BUTTON