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
);
420 wxSize
wxSizerItem::GetMaxSizeWithBorder() const
422 return AddBorderToSize(GetMaxSize());
425 void wxSizerItem::SetDimension( const wxPoint
& pos_
, const wxSize
& size_
)
429 if (m_flag
& wxSHAPED
)
431 // adjust aspect ratio
432 int rwidth
= (int) (size
.y
* m_ratio
);
436 int rheight
= (int) (size
.x
/ m_ratio
);
437 // add vertical space
438 if (m_flag
& wxALIGN_CENTER_VERTICAL
)
439 pos
.y
+= (size
.y
- rheight
) / 2;
440 else if (m_flag
& wxALIGN_BOTTOM
)
441 pos
.y
+= (size
.y
- rheight
);
442 // use reduced dimensions
445 else if (rwidth
< size
.x
)
447 // add horizontal space
448 if (m_flag
& wxALIGN_CENTER_HORIZONTAL
)
449 pos
.x
+= (size
.x
- rwidth
) / 2;
450 else if (m_flag
& wxALIGN_RIGHT
)
451 pos
.x
+= (size
.x
- rwidth
);
456 // This is what GetPosition() returns. Since we calculate
457 // borders afterwards, GetPosition() will be the left/top
458 // corner of the surrounding border.
470 if (m_flag
& wxNORTH
)
475 if (m_flag
& wxSOUTH
)
485 m_rect
= wxRect(pos
, size
);
490 wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
495 // Use wxSIZE_FORCE_EVENT here since a sizer item might
496 // have changed alignment or some other property which would
497 // not change the size of the window. In such a case, no
498 // wxSizeEvent would normally be generated and thus the
499 // control wouldn't get laid out correctly here.
501 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
502 wxSIZE_ALLOW_MINUS_ONE
|wxSIZE_FORCE_EVENT
);
504 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
505 wxSIZE_ALLOW_MINUS_ONE
);
510 m_sizer
->SetDimension(pos
, size
);
514 m_spacer
->SetSize(size
);
519 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
523 void wxSizerItem::DeleteWindows()
532 //We are deleting the window from this sizer - normally
533 //the window destroys the sizer associated with it,
534 //which might destroy this, which we don't want
535 m_window
->SetContainingSizer(NULL
);
537 //Putting this after the switch will result in a spacer
538 //not being deleted properly on destruction
543 m_sizer
->DeleteWindows();
548 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
553 void wxSizerItem::Show( bool show
)
558 wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
562 m_window
->Show(show
);
570 m_spacer
->Show(show
);
575 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
579 bool wxSizerItem::IsShown() const
581 if ( m_flag
& wxRESERVE_SPACE_EVEN_IF_HIDDEN
)
587 // we may be called from CalcMin(), just return false so that we're
592 return m_window
->IsShown();
596 // arbitrarily decide that if at least one of our elements is
597 // shown, so are we (this arbitrariness is the reason for
598 // deprecating this function)
599 for ( wxSizerItemList::compatibility_iterator
600 node
= m_sizer
->GetChildren().GetFirst();
602 node
= node
->GetNext() )
604 if ( node
->GetData()->IsShown() )
611 return m_spacer
->IsShown();
615 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
621 #if WXWIN_COMPATIBILITY_2_6
622 void wxSizerItem::SetOption( int option
)
624 SetProportion( option
);
627 int wxSizerItem::GetOption() const
629 return GetProportion();
631 #endif // WXWIN_COMPATIBILITY_2_6
634 //---------------------------------------------------------------------------
636 //---------------------------------------------------------------------------
640 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
643 wxSizerItem
* wxSizer::DoInsert( size_t index
, wxSizerItem
*item
)
645 m_children
.Insert( index
, item
);
647 if ( item
->GetWindow() )
648 item
->GetWindow()->SetContainingSizer( this );
650 if ( item
->GetSizer() )
651 item
->GetSizer()->SetContainingWindow( m_containingWindow
);
656 void wxSizer::SetContainingWindow(wxWindow
*win
)
658 if ( win
== m_containingWindow
)
661 m_containingWindow
= win
;
663 // set the same window for all nested sizers as well, they also are in the
665 for ( wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
667 node
= node
->GetNext() )
669 wxSizerItem
*const item
= node
->GetData();
670 wxSizer
*const sizer
= item
->GetSizer();
674 sizer
->SetContainingWindow(win
);
679 #if WXWIN_COMPATIBILITY_2_6
680 bool wxSizer::Remove( wxWindow
*window
)
682 return Detach( window
);
684 #endif // WXWIN_COMPATIBILITY_2_6
686 bool wxSizer::Remove( wxSizer
*sizer
)
688 wxASSERT_MSG( sizer
, wxT("Removing NULL sizer") );
690 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
693 wxSizerItem
*item
= node
->GetData();
695 if (item
->GetSizer() == sizer
)
698 m_children
.Erase( node
);
702 node
= node
->GetNext();
708 bool wxSizer::Remove( int index
)
710 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
712 wxT("Remove index is out of range") );
714 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
716 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
718 delete node
->GetData();
719 m_children
.Erase( node
);
724 bool wxSizer::Detach( wxSizer
*sizer
)
726 wxASSERT_MSG( sizer
, wxT("Detaching NULL sizer") );
728 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
731 wxSizerItem
*item
= node
->GetData();
733 if (item
->GetSizer() == sizer
)
737 m_children
.Erase( node
);
740 node
= node
->GetNext();
746 bool wxSizer::Detach( wxWindow
*window
)
748 wxASSERT_MSG( window
, wxT("Detaching NULL window") );
750 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
753 wxSizerItem
*item
= node
->GetData();
755 if (item
->GetWindow() == window
)
758 m_children
.Erase( node
);
761 node
= node
->GetNext();
767 bool wxSizer::Detach( int index
)
769 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
771 wxT("Detach index is out of range") );
773 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
775 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
777 wxSizerItem
*item
= node
->GetData();
779 if ( item
->IsSizer() )
783 m_children
.Erase( node
);
787 bool wxSizer::Replace( wxWindow
*oldwin
, wxWindow
*newwin
, bool recursive
)
789 wxASSERT_MSG( oldwin
, wxT("Replacing NULL window") );
790 wxASSERT_MSG( newwin
, wxT("Replacing with NULL window") );
792 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
795 wxSizerItem
*item
= node
->GetData();
797 if (item
->GetWindow() == oldwin
)
799 item
->AssignWindow(newwin
);
800 newwin
->SetContainingSizer( this );
803 else if (recursive
&& item
->IsSizer())
805 if (item
->GetSizer()->Replace( oldwin
, newwin
, true ))
809 node
= node
->GetNext();
815 bool wxSizer::Replace( wxSizer
*oldsz
, wxSizer
*newsz
, bool recursive
)
817 wxASSERT_MSG( oldsz
, wxT("Replacing NULL sizer") );
818 wxASSERT_MSG( newsz
, wxT("Replacing with NULL sizer") );
820 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
823 wxSizerItem
*item
= node
->GetData();
825 if (item
->GetSizer() == oldsz
)
827 item
->AssignSizer(newsz
);
830 else if (recursive
&& item
->IsSizer())
832 if (item
->GetSizer()->Replace( oldsz
, newsz
, true ))
836 node
= node
->GetNext();
842 bool wxSizer::Replace( size_t old
, wxSizerItem
*newitem
)
844 wxCHECK_MSG( old
< m_children
.GetCount(), false, wxT("Replace index is out of range") );
845 wxASSERT_MSG( newitem
, wxT("Replacing with NULL item") );
847 wxSizerItemList::compatibility_iterator node
= m_children
.Item( old
);
849 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
851 wxSizerItem
*item
= node
->GetData();
852 node
->SetData(newitem
);
854 if (item
->IsWindow() && item
->GetWindow())
855 item
->GetWindow()->SetContainingSizer(NULL
);
862 void wxSizer::Clear( bool delete_windows
)
864 // First clear the ContainingSizer pointers
865 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
868 wxSizerItem
*item
= node
->GetData();
870 if (item
->IsWindow())
871 item
->GetWindow()->SetContainingSizer( NULL
);
872 node
= node
->GetNext();
875 // Destroy the windows if needed
879 // Now empty the list
880 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
883 void wxSizer::DeleteWindows()
885 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
888 wxSizerItem
*item
= node
->GetData();
890 item
->DeleteWindows();
891 node
= node
->GetNext();
895 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
897 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
899 // take the min size by default and limit it by max size
900 wxSize size
= GetMinClientSize(window
);
903 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
906 // hack for small screen devices where TLWs are always full screen
907 if ( tlw
->IsAlwaysMaximized() )
909 return tlw
->GetClientSize();
912 // limit the window to the size of the display it is on
913 int disp
= wxDisplay::GetFromWindow(window
);
914 if ( disp
== wxNOT_FOUND
)
916 // or, if we don't know which one it is, of the main one
920 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
922 // If determining the display size failed, skip the max size checks as
923 // we really don't want to create windows of (0, 0) size.
924 if ( !sizeMax
.x
|| !sizeMax
.y
)
927 // space for decorations and toolbars etc.
928 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
932 sizeMax
= GetMaxClientSize(window
);
935 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
937 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
943 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
945 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
947 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
950 wxSize
wxSizer::Fit( wxWindow
*window
)
952 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
955 window
->SetClientSize(ComputeFittingClientSize(window
));
957 // return entire size
958 return window
->GetSize();
961 void wxSizer::FitInside( wxWindow
*window
)
964 if (window
->IsTopLevel())
965 size
= VirtualFitSize( window
);
967 size
= GetMinClientSize( window
);
969 window
->SetVirtualSize( size
);
972 void wxSizer::Layout()
974 // (re)calculates minimums needed for each item and other preparations
978 // Applies the layout and repositions/resizes the items
982 void wxSizer::SetSizeHints( wxWindow
*window
)
984 // Preserve the window's max size hints, but set the
985 // lower bound according to the sizer calculations.
987 // This is equivalent to calling Fit(), except that we need to set
988 // the size hints _in between_ the two steps performed by Fit
989 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
990 // otherwise SetClientSize() could have no effect if there already are
991 // size hints in effect that forbid requested client size.
993 const wxSize clientSize
= ComputeFittingClientSize(window
);
995 window
->SetMinClientSize(clientSize
);
996 window
->SetClientSize(clientSize
);
999 #if WXWIN_COMPATIBILITY_2_8
1000 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
1002 FitInside( window
);
1004 #endif // WXWIN_COMPATIBILITY_2_8
1006 // TODO on mac we need a function that determines how much free space this
1007 // min size contains, in order to make sure that we have 20 pixels of free
1008 // space around the controls
1009 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
1011 return window
->WindowToClientSize(window
->GetMaxSize());
1014 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
1016 return GetMinSize(); // Already returns client size.
1019 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1021 wxSize size
= GetMinClientSize( window
);
1022 wxSize sizeMax
= GetMaxClientSize( window
);
1024 // Limit the size if sizeMax != wxDefaultSize
1026 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1028 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1034 wxSize
wxSizer::GetMinSize()
1036 wxSize
ret( CalcMin() );
1037 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1038 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1042 void wxSizer::DoSetMinSize( int width
, int height
)
1044 m_minSize
.x
= width
;
1045 m_minSize
.y
= height
;
1048 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1050 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1052 // Is it our immediate child?
1054 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1057 wxSizerItem
*item
= node
->GetData();
1059 if (item
->GetWindow() == window
)
1061 item
->SetMinSize( width
, height
);
1064 node
= node
->GetNext();
1067 // No? Search any subsizers we own then
1069 node
= m_children
.GetFirst();
1072 wxSizerItem
*item
= node
->GetData();
1074 if ( item
->GetSizer() &&
1075 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1077 // A child sizer found the requested windw, exit.
1080 node
= node
->GetNext();
1086 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1088 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1090 // Is it our immediate child?
1092 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1095 wxSizerItem
*item
= node
->GetData();
1097 if (item
->GetSizer() == sizer
)
1099 item
->GetSizer()->DoSetMinSize( width
, height
);
1102 node
= node
->GetNext();
1105 // No? Search any subsizers we own then
1107 node
= m_children
.GetFirst();
1110 wxSizerItem
*item
= node
->GetData();
1112 if ( item
->GetSizer() &&
1113 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1115 // A child found the requested sizer, exit.
1118 node
= node
->GetNext();
1124 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1126 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1128 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1130 wxSizerItem
*item
= node
->GetData();
1132 if (item
->GetSizer())
1134 // Sizers contains the minimal size in them, if not calculated ...
1135 item
->GetSizer()->DoSetMinSize( width
, height
);
1139 // ... but the minimal size of spacers and windows is stored via the item
1140 item
->SetMinSize( width
, height
);
1146 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1148 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1150 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1153 wxSizerItem
*item
= node
->GetData();
1155 if (item
->GetWindow() == window
)
1159 else if (recursive
&& item
->IsSizer())
1161 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1166 node
= node
->GetNext();
1172 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1174 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1176 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1179 wxSizerItem
*item
= node
->GetData();
1181 if (item
->GetSizer() == sizer
)
1185 else if (recursive
&& item
->IsSizer())
1187 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1192 node
= node
->GetNext();
1198 wxSizerItem
* wxSizer::GetItem( size_t index
)
1200 wxCHECK_MSG( index
< m_children
.GetCount(),
1202 wxT("GetItem index is out of range") );
1204 return m_children
.Item( index
)->GetData();
1207 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1209 // This gets a sizer item by the id of the sizer item
1210 // and NOT the id of a window if the item is a window.
1212 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1215 wxSizerItem
*item
= node
->GetData();
1217 if (item
->GetId() == id
)
1221 else if (recursive
&& item
->IsSizer())
1223 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1228 node
= node
->GetNext();
1234 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1236 wxSizerItem
*item
= GetItem( window
, recursive
);
1247 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1249 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1260 bool wxSizer::Show( size_t index
, bool show
)
1262 wxSizerItem
*item
= GetItem( index
);
1273 void wxSizer::ShowItems( bool show
)
1275 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1278 node
->GetData()->Show( show
);
1279 node
= node
->GetNext();
1283 bool wxSizer::IsShown( wxWindow
*window
) const
1285 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1288 wxSizerItem
*item
= node
->GetData();
1290 if (item
->GetWindow() == window
)
1292 return item
->IsShown();
1294 node
= node
->GetNext();
1297 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1302 bool wxSizer::IsShown( wxSizer
*sizer
) const
1304 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1307 wxSizerItem
*item
= node
->GetData();
1309 if (item
->GetSizer() == sizer
)
1311 return item
->IsShown();
1313 node
= node
->GetNext();
1316 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1321 bool wxSizer::IsShown( size_t index
) const
1323 wxCHECK_MSG( index
< m_children
.GetCount(),
1325 wxT("IsShown index is out of range") );
1327 return m_children
.Item( index
)->GetData()->IsShown();
1331 //---------------------------------------------------------------------------
1333 //---------------------------------------------------------------------------
1335 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1336 : m_rows( cols
== 0 ? 1 : 0 ),
1341 wxASSERT(cols
>= 0);
1344 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1345 : m_rows( cols
== 0 ? 1 : 0 ),
1347 m_vgap( gap
.GetHeight() ),
1348 m_hgap( gap
.GetWidth() )
1350 wxASSERT(cols
>= 0);
1353 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1354 : m_rows( rows
|| cols
? rows
: 1 ),
1359 wxASSERT(rows
>= 0 && cols
>= 0);
1362 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1363 : m_rows( rows
|| cols
? rows
: 1 ),
1365 m_vgap( gap
.GetHeight() ),
1366 m_hgap( gap
.GetWidth() )
1368 wxASSERT(rows
>= 0 && cols
>= 0);
1371 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1373 // if only the number of columns or the number of rows is specified for a
1374 // sizer, arbitrarily many items can be added to it but if both of them are
1375 // fixed, then the sizer can't have more than that many items -- check for
1376 // this here to ensure that we detect errors as soon as possible
1377 if ( m_cols
&& m_rows
)
1379 const int nitems
= m_children
.GetCount();
1380 if ( nitems
== m_cols
*m_rows
)
1384 "too many items (%d > %d*%d) in grid sizer (maybe you "
1385 "should omit the number of either rows or columns?)",
1386 nitems
+ 1, m_cols
, m_rows
)
1389 // additionally, continuing to use the specified number of columns
1390 // and rows is not a good idea as callers of CalcRowsCols() expect
1391 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1392 // which is not the case if there are too many items and results in
1393 // crashes, so let it compute the number of rows automatically by
1394 // forgetting the (wrong) number of rows specified (this also has a
1395 // nice side effect of giving only one assert even if there are
1396 // many more items than allowed in this sizer)
1401 return wxSizer::DoInsert(index
, item
);
1404 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1406 const int nitems
= m_children
.GetCount();
1408 ncols
= GetEffectiveColsCount();
1409 nrows
= GetEffectiveRowsCount();
1411 // Since Insert() checks for overpopulation, the following
1412 // should only assert if the grid was shrunk via SetRows() / SetCols()
1413 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1418 void wxGridSizer::RecalcSizes()
1420 int nitems
, nrows
, ncols
;
1421 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1424 wxSize
sz( GetSize() );
1425 wxPoint
pt( GetPosition() );
1427 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1428 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1431 for (int c
= 0; c
< ncols
; c
++)
1434 for (int r
= 0; r
< nrows
; r
++)
1436 int i
= r
* ncols
+ c
;
1439 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1441 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1443 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1451 wxSize
wxGridSizer::CalcMin()
1454 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1457 // Find the max width and height for any component
1461 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1464 wxSizerItem
*item
= node
->GetData();
1465 wxSize
sz( item
->CalcMin() );
1467 w
= wxMax( w
, sz
.x
);
1468 h
= wxMax( h
, sz
.y
);
1470 node
= node
->GetNext();
1473 // In case we have a nested sizer with a two step algo , give it
1474 // a chance to adjust to that (we give it width component)
1475 node
= m_children
.GetFirst();
1476 bool didChangeMinSize
= false;
1479 wxSizerItem
*item
= node
->GetData();
1480 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1482 node
= node
->GetNext();
1485 // And redo iteration in case min size changed
1486 if( didChangeMinSize
)
1488 node
= m_children
.GetFirst();
1492 wxSizerItem
*item
= node
->GetData();
1493 wxSize
sz( item
->GetMinSizeWithBorder() );
1495 w
= wxMax( w
, sz
.x
);
1496 h
= wxMax( h
, sz
.y
);
1498 node
= node
->GetNext();
1502 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1503 nrows
* h
+ (nrows
-1) * m_vgap
);
1506 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1509 wxSize
sz( item
->GetMinSizeWithBorder() );
1510 int flag
= item
->GetFlag();
1512 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1518 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1520 pt
.x
= x
+ (w
- sz
.x
) / 2;
1522 else if (flag
& wxALIGN_RIGHT
)
1524 pt
.x
= x
+ (w
- sz
.x
);
1527 if (flag
& wxALIGN_CENTER_VERTICAL
)
1529 pt
.y
= y
+ (h
- sz
.y
) / 2;
1531 else if (flag
& wxALIGN_BOTTOM
)
1533 pt
.y
= y
+ (h
- sz
.y
);
1537 item
->SetDimension(pt
, sz
);
1540 //---------------------------------------------------------------------------
1542 //---------------------------------------------------------------------------
1544 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1545 : wxGridSizer( cols
, vgap
, hgap
),
1546 m_flexDirection(wxBOTH
),
1547 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1551 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1552 : wxGridSizer( cols
, gap
),
1553 m_flexDirection(wxBOTH
),
1554 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1558 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1559 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1560 m_flexDirection(wxBOTH
),
1561 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1565 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1566 : wxGridSizer( rows
, cols
, gap
),
1567 m_flexDirection(wxBOTH
),
1568 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1572 wxFlexGridSizer::~wxFlexGridSizer()
1576 void wxFlexGridSizer::RecalcSizes()
1579 if ( !CalcRowsCols(nrows
, ncols
) )
1582 const wxPoint
pt(GetPosition());
1583 const wxSize
sz(GetSize());
1585 AdjustForGrowables(sz
);
1587 wxSizerItemList::const_iterator i
= m_children
.begin();
1588 const wxSizerItemList::const_iterator end
= m_children
.end();
1591 for ( int r
= 0; r
< nrows
; r
++ )
1593 if ( m_rowHeights
[r
] == -1 )
1595 // this row is entirely hidden, skip it
1596 for ( int c
= 0; c
< ncols
; c
++ )
1607 const int hrow
= m_rowHeights
[r
];
1608 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1613 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1615 const int wcol
= m_colWidths
[c
];
1620 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1624 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1636 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1637 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1639 // Sum total minimum size, including gaps between rows/columns.
1640 // -1 is used as a magic number meaning empty row/column.
1643 const size_t count
= sizes
.size();
1644 for ( size_t n
= 0; n
< count
; n
++ )
1646 if ( sizes
[n
] != -1 )
1649 total
+= gap
; // separate from the previous column
1658 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1660 // We have to recalculate the sizes in case the item minimum size has
1661 // changed since the previous layout, or the item has been hidden using
1662 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1663 // dimension of the row/column will be -1, indicating that the column
1664 // itself is hidden.
1665 m_rowHeights
.assign(nrows
, -1);
1666 m_colWidths
.assign(ncols
, -1);
1668 // n is the index of the item in left-to-right top-to-bottom order
1670 for ( wxSizerItemList::iterator i
= m_children
.begin();
1671 i
!= m_children
.end();
1674 wxSizerItem
* const item
= *i
;
1675 if ( item
->IsShown() )
1677 // NOTE: Not doing the calculation here, this is just
1678 // for finding max values.
1679 const wxSize
sz(item
->GetMinSizeWithBorder());
1681 const int row
= n
/ ncols
;
1682 const int col
= n
% ncols
;
1684 if ( sz
.y
> m_rowHeights
[row
] )
1685 m_rowHeights
[row
] = sz
.y
;
1686 if ( sz
.x
> m_colWidths
[col
] )
1687 m_colWidths
[col
] = sz
.x
;
1691 AdjustForFlexDirection();
1693 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1694 SumArraySizes(m_rowHeights
, m_vgap
));
1697 wxSize
wxFlexGridSizer::CalcMin()
1702 // Number of rows/columns can change as items are added or removed.
1703 if ( !CalcRowsCols(nrows
, ncols
) )
1707 // We have to recalculate the sizes in case the item minimum size has
1708 // changed since the previous layout, or the item has been hidden using
1709 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1710 // dimension of the row/column will be -1, indicating that the column
1711 // itself is hidden.
1712 m_rowHeights
.assign(nrows
, -1);
1713 m_colWidths
.assign(ncols
, -1);
1715 for ( wxSizerItemList::iterator i
= m_children
.begin();
1716 i
!= m_children
.end();
1719 wxSizerItem
* const item
= *i
;
1720 if ( item
->IsShown() )
1726 // The stage of looking for max values in each row/column has been
1727 // made a separate function, since it's reused in AdjustForGrowables.
1728 FindWidthsAndHeights(nrows
,ncols
);
1730 return m_calculatedMinSize
;
1733 void wxFlexGridSizer::AdjustForFlexDirection()
1735 // the logic in CalcMin works when we resize flexibly in both directions
1736 // but maybe this is not the case
1737 if ( m_flexDirection
!= wxBOTH
)
1739 // select the array corresponding to the direction in which we do *not*
1741 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1744 const size_t count
= array
.GetCount();
1746 // find the largest value in this array
1750 for ( n
= 0; n
< count
; ++n
)
1752 if ( array
[n
] > largest
)
1756 // and now fill it with the largest value
1757 for ( n
= 0; n
< count
; ++n
)
1759 // don't touch hidden rows
1760 if ( array
[n
] != -1 )
1766 // helper of AdjustForGrowables() which is called for rows/columns separately
1769 // delta: the extra space, we do nothing unless it's positive
1770 // growable: indices or growable rows/cols in sizes array
1771 // sizes: the height/widths of rows/cols to adjust
1772 // proportions: proportions of the growable rows/cols or NULL if they all
1773 // should be assumed to have proportion of 1
1775 DoAdjustForGrowables(int delta
,
1776 const wxArrayInt
& growable
,
1778 const wxArrayInt
*proportions
)
1783 // total sum of proportions of all non-hidden rows
1784 int sum_proportions
= 0;
1786 // number of currently shown growable rows
1789 const int max_idx
= sizes
.size();
1791 const size_t count
= growable
.size();
1793 for ( idx
= 0; idx
< count
; idx
++ )
1795 // Since the number of rows/columns can change as items are
1796 // inserted/deleted, we need to verify at runtime that the
1797 // requested growable rows/columns are still valid.
1798 if ( growable
[idx
] >= max_idx
)
1801 // If all items in a row/column are hidden, that row/column will
1802 // have a dimension of -1. This causes the row/column to be
1803 // hidden completely.
1804 if ( sizes
[growable
[idx
]] == -1 )
1808 sum_proportions
+= (*proportions
)[idx
];
1816 // the remaining extra free space, adjusted during each iteration
1817 for ( idx
= 0; idx
< count
; idx
++ )
1819 if ( growable
[idx
] >= max_idx
)
1822 if ( sizes
[ growable
[idx
] ] == -1 )
1826 if ( sum_proportions
== 0 )
1828 // no growable rows -- divide extra space evenly among all
1829 cur_delta
= delta
/num
;
1832 else // allocate extra space proportionally
1834 const int cur_prop
= (*proportions
)[idx
];
1835 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1836 sum_proportions
-= cur_prop
;
1839 sizes
[growable
[idx
]] += cur_delta
;
1844 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1847 // by the time this function is called, the sizer should be already fully
1848 // initialized and hence the number of its columns and rows is known and we
1849 // can check that all indices in m_growableCols/Rows are valid (see also
1850 // comments in AddGrowableCol/Row())
1851 if ( !m_rows
|| !m_cols
)
1855 int nrows
= CalcRows();
1857 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1859 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1860 "invalid growable row index" );
1866 int ncols
= CalcCols();
1868 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1870 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1871 "invalid growable column index" );
1875 #endif // wxDEBUG_LEVEL
1878 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1880 DoAdjustForGrowables
1882 sz
.x
- m_calculatedMinSize
.x
,
1885 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1889 // This gives nested objects that benefit from knowing one size
1890 // component in advance the chance to use that.
1891 bool didAdjustMinSize
= false;
1893 // Iterate over all items and inform about column width
1894 const int ncols
= GetEffectiveColsCount();
1896 for ( wxSizerItemList::iterator i
= m_children
.begin();
1897 i
!= m_children
.end();
1900 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1901 if ( ++col
== ncols
)
1905 // Only redo if info was actually used
1906 if( didAdjustMinSize
)
1908 DoAdjustForGrowables
1910 sz
.x
- m_calculatedMinSize
.x
,
1913 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1919 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1921 // pass NULL instead of proportions if the grow mode is ALL as we
1922 // should treat all rows as having proportion of 1 then
1923 DoAdjustForGrowables
1925 sz
.y
- m_calculatedMinSize
.y
,
1928 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1934 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1936 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1939 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1941 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1944 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1946 wxASSERT_MSG( !IsRowGrowable( idx
),
1947 "AddGrowableRow() called for growable row" );
1949 // notice that we intentionally don't check the index validity here in (the
1950 // common) case when the number of rows was not specified in the ctor -- in
1951 // this case it will be computed only later, when all items are added to
1952 // the sizer, and the check will be done in AdjustForGrowables()
1953 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1955 m_growableRows
.Add( idx
);
1956 m_growableRowsProportions
.Add( proportion
);
1959 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1961 wxASSERT_MSG( !IsColGrowable( idx
),
1962 "AddGrowableCol() called for growable column" );
1964 // see comment in AddGrowableRow(): although it's less common to omit the
1965 // specification of the number of columns, it still can also happen
1966 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1968 m_growableCols
.Add( idx
);
1969 m_growableColsProportions
.Add( proportion
);
1972 // helper function for RemoveGrowableCol/Row()
1974 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1976 const size_t count
= items
.size();
1977 for ( size_t n
= 0; n
< count
; n
++ )
1979 if ( (size_t)items
[n
] == idx
)
1982 proportions
.RemoveAt(n
);
1987 wxFAIL_MSG( wxT("column/row is already not growable") );
1990 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
1992 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
1995 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
1997 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
2000 //---------------------------------------------------------------------------
2002 //---------------------------------------------------------------------------
2004 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
2006 return IsVertical() ? Add(0, size
) : Add(size
, 0);
2013 Helper of RecalcSizes(): checks if there is enough remaining space for the
2014 min size of the given item and returns its min size or the entire remaining
2015 space depending on which one is greater.
2017 This function updates the remaining space parameter to account for the size
2018 effectively allocated to the item.
2021 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2023 int& remainingSpace
= *remainingSpace_
;
2026 if ( remainingSpace
> 0 )
2028 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2029 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2031 if ( size
>= remainingSpace
)
2033 // truncate the item to fit in the remaining space, this is better
2034 // than showing it only partially in general, even if both choices
2035 // are bad -- but there is nothing else we can do
2036 size
= remainingSpace
;
2039 remainingSpace
-= size
;
2041 else // no remaining space
2043 // no space at all left, no need to even query the item for its min
2044 // size as we can't give it to it anyhow
2051 } // anonymous namespace
2053 void wxBoxSizer::RecalcSizes()
2055 if ( m_children
.empty() )
2058 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2059 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2061 // the amount of free space which we should redistribute among the
2062 // stretchable items (i.e. those with non zero proportion)
2063 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2065 // declare loop variables used below:
2066 wxSizerItemList::const_iterator i
; // iterator in m_children list
2067 unsigned n
= 0; // item index in majorSizes array
2070 // First, inform item about the available size in minor direction as this
2071 // can change their size in the major direction. Also compute the number of
2072 // visible items and sum of their min sizes in major direction.
2074 int minMajorSize
= 0;
2075 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2077 wxSizerItem
* const item
= *i
;
2079 if ( !item
->IsShown() )
2082 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2083 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2084 wxSize szMin
= item
->GetMinSizeWithBorder();
2085 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2088 // Since we passed available space along to the item, it should not
2089 // take too much, so delta should not become negative.
2090 delta
-= deltaChange
;
2092 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2095 // update our min size and delta which may have changed
2096 SizeInMajorDir(m_minSize
) = minMajorSize
;
2097 delta
= totalMajorSize
- minMajorSize
;
2100 // space and sum of proportions for the remaining items, both may change
2102 wxCoord remaining
= totalMajorSize
;
2103 int totalProportion
= m_totalProportion
;
2105 // size of the (visible) items in major direction, -1 means "not fixed yet"
2106 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2109 // Check for the degenerated case when we don't have enough space for even
2110 // the min sizes of all the items: in this case we really can't do much
2111 // more than to allocate the min size to as many of fixed size items as
2112 // possible (on the assumption that variable size items such as text zones
2113 // or list boxes may use scrollbars to show their content even if their
2114 // size is less than min size but that fixed size items such as buttons
2115 // will suffer even more if we don't give them their min size)
2116 if ( totalMajorSize
< minMajorSize
)
2118 // Second degenerated case pass: allocate min size to all fixed size
2120 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2122 wxSizerItem
* const item
= *i
;
2124 if ( !item
->IsShown() )
2127 // deal with fixed size items only during this pass
2128 if ( item
->GetProportion() )
2131 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2135 // Third degenerated case pass: allocate min size to all the remaining,
2136 // i.e. non-fixed size, items.
2137 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2139 wxSizerItem
* const item
= *i
;
2141 if ( !item
->IsShown() )
2144 // we've already dealt with fixed size items above
2145 if ( !item
->GetProportion() )
2148 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2151 else // we do have enough space to give at least min sizes to all items
2153 // Second and maybe more passes in the non-degenerated case: deal with
2154 // fixed size items and items whose min size is greater than what we
2155 // would allocate to them taking their proportion into account. For
2156 // both of them, we will just use their min size, but for the latter we
2157 // also need to reexamine all the items as the items which fitted
2158 // before we adjusted their size upwards might not fit any more. This
2159 // does make for a quadratic algorithm but it's not obvious how to
2160 // avoid it and hopefully it's not a huge problem in practice as the
2161 // sizers don't have many items usually (and, of course, the algorithm
2162 // still reduces into a linear one if there is enough space for all the
2164 bool nonFixedSpaceChanged
= false;
2165 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2167 if ( nonFixedSpaceChanged
)
2169 i
= m_children
.begin();
2171 nonFixedSpaceChanged
= false;
2174 // check for the end of the loop only after the check above as
2175 // otherwise we wouldn't do another pass if the last child resulted
2176 // in non fixed space reduction
2177 if ( i
== m_children
.end() )
2180 wxSizerItem
* const item
= *i
;
2182 if ( !item
->IsShown() )
2185 // don't check the item which we had already dealt with during a
2186 // previous pass (this is more than an optimization, the code
2187 // wouldn't work correctly if we kept adjusting for the same item
2188 // over and over again)
2189 if ( majorSizes
[n
] != wxDefaultCoord
)
2192 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2194 // it doesn't make sense for min size to be negative but right now
2195 // it's possible to create e.g. a spacer with (-1, 10) as size and
2196 // people do it in their code apparently (see #11842) so ensure
2197 // that we don't use this -1 as real min size as it conflicts with
2198 // the meaning we use for it here and negative min sizes just don't
2199 // make sense anyhow (which is why it might be a better idea to
2200 // deal with them at wxSizerItem level in the future but for now
2201 // this is the minimal fix for the bug)
2205 const int propItem
= item
->GetProportion();
2208 // is the desired size of this item big enough?
2209 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2211 // yes, it is, we'll determine the real size of this
2212 // item later, for now just leave it as wxDefaultCoord
2216 // the proportion of this item won't count, it has
2217 // effectively become fixed
2218 totalProportion
-= propItem
;
2221 // we can already allocate space for this item
2222 majorSizes
[n
] = minMajor
;
2224 // change the amount of the space remaining to the other items,
2225 // as this can result in not being able to satisfy their
2226 // proportions any more we will need to redo another loop
2228 remaining
-= minMajor
;
2230 nonFixedSpaceChanged
= true;
2233 // Similar to the previous loop, but dealing with items whose max size
2234 // is less than what we would allocate to them taking their proportion
2236 nonFixedSpaceChanged
= false;
2237 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2239 if ( nonFixedSpaceChanged
)
2241 i
= m_children
.begin();
2243 nonFixedSpaceChanged
= false;
2246 // check for the end of the loop only after the check above as
2247 // otherwise we wouldn't do another pass if the last child resulted
2248 // in non fixed space reduction
2249 if ( i
== m_children
.end() )
2252 wxSizerItem
* const item
= *i
;
2254 if ( !item
->IsShown() )
2257 // don't check the item which we had already dealt with during a
2258 // previous pass (this is more than an optimization, the code
2259 // wouldn't work correctly if we kept adjusting for the same item
2260 // over and over again)
2261 if ( majorSizes
[n
] != wxDefaultCoord
)
2264 wxCoord maxMajor
= GetSizeInMajorDir(item
->GetMaxSizeWithBorder());
2266 // must be nonzero, fixed-size items were dealt with in previous loop
2267 const int propItem
= item
->GetProportion();
2269 // is the desired size of this item small enough?
2270 if ( maxMajor
< 0 ||
2271 (remaining
*propItem
)/totalProportion
<= maxMajor
)
2273 // yes, it is, we'll determine the real size of this
2274 // item later, for now just leave it as wxDefaultCoord
2278 // the proportion of this item won't count, it has
2279 // effectively become fixed
2280 totalProportion
-= propItem
;
2282 // we can already allocate space for this item
2283 majorSizes
[n
] = maxMajor
;
2285 // change the amount of the space remaining to the other items,
2286 // as this can result in not being able to satisfy their
2287 // proportions any more we will need to redo another loop
2289 remaining
-= maxMajor
;
2291 nonFixedSpaceChanged
= true;
2294 // Last by one pass: distribute the remaining space among the non-fixed
2295 // items whose size weren't fixed yet according to their proportions.
2296 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2298 wxSizerItem
* const item
= *i
;
2300 if ( !item
->IsShown() )
2303 if ( majorSizes
[n
] == wxDefaultCoord
)
2305 const int propItem
= item
->GetProportion();
2306 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2308 remaining
-= majorSizes
[n
];
2309 totalProportion
-= propItem
;
2315 // the position at which we put the next child
2316 wxPoint
pt(m_position
);
2319 // Final pass: finally do position the items correctly using their sizes as
2320 // determined above.
2321 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2323 wxSizerItem
* const item
= *i
;
2325 if ( !item
->IsShown() )
2328 const int majorSize
= majorSizes
[n
];
2330 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2332 // apply the alignment in the minor direction
2333 wxPoint
posChild(pt
);
2335 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2336 const int flag
= item
->GetFlag();
2337 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2339 // occupy all the available space if wxEXPAND was given and also if
2340 // the item is too big to fit -- in this case we truncate it below
2341 // its minimal size which is bad but better than not showing parts
2342 // of the window at all
2343 minorSize
= totalMinorSize
;
2345 // do not allow the size in the minor direction to grow beyond the max
2346 // size of the item in the minor direction
2347 const wxCoord maxMinorSize
= GetSizeInMinorDir(item
->GetMaxSizeWithBorder());
2348 if ( maxMinorSize
>= 0 && minorSize
> maxMinorSize
)
2349 minorSize
= maxMinorSize
;
2352 if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2354 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2356 // NB: wxCENTRE is used here only for backwards compatibility,
2357 // wxALIGN_CENTRE should be used in new code
2358 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2359 : wxALIGN_CENTRE_VERTICAL
)) )
2361 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2365 // apply RTL adjustment for horizontal sizers:
2366 if ( !IsVertical() && m_containingWindow
)
2368 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2376 // finally set size of this child and advance to the next one
2377 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2379 PosInMajorDir(pt
) += majorSize
;
2383 wxSize
wxBoxSizer::CalcMin()
2385 m_totalProportion
= 0;
2386 m_minSize
= wxSize(0, 0);
2388 // The minimal size for the sizer should be big enough to allocate its
2389 // element at least its minimal size but also, and this is the non trivial
2390 // part, to respect the children proportion. To satisfy the latter
2391 // condition we must find the greatest min-size-to-proportion ratio for all
2392 // elements with non-zero proportion.
2393 float maxMinSizeToProp
= 0.;
2394 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2395 i
!= m_children
.end();
2398 wxSizerItem
* const item
= *i
;
2400 if ( !item
->IsShown() )
2403 const wxSize sizeMinThis
= item
->CalcMin();
2404 if ( const int propThis
= item
->GetProportion() )
2406 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2407 minSizeToProp
/= propThis
;
2409 if ( minSizeToProp
> maxMinSizeToProp
)
2410 maxMinSizeToProp
= minSizeToProp
;
2412 m_totalProportion
+= item
->GetProportion();
2414 else // fixed size item
2416 // Just account for its size directly
2417 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2420 // In the transversal direction we just need to find the maximum.
2421 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2422 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2425 // Using the max ratio ensures that the min size is big enough for all
2426 // items to have their min size and satisfy the proportions among them.
2427 SizeInMajorDir(m_minSize
) += (int)(maxMinSizeToProp
*m_totalProportion
);
2432 //---------------------------------------------------------------------------
2434 //---------------------------------------------------------------------------
2438 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2439 : wxBoxSizer( orient
),
2442 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2444 // do this so that our Detach() is called if the static box is destroyed
2446 m_staticBox
->SetContainingSizer(this);
2449 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2450 : wxBoxSizer(orient
),
2451 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2454 m_staticBox
->SetContainingSizer(this);
2457 wxStaticBoxSizer::~wxStaticBoxSizer()
2462 void wxStaticBoxSizer::RecalcSizes()
2464 int top_border
, other_border
;
2465 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2467 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2469 wxSize
old_size( m_size
);
2470 m_size
.x
-= 2*other_border
;
2471 m_size
.y
-= top_border
+ other_border
;
2473 wxPoint
old_pos( m_position
);
2474 if (m_staticBox
->GetChildren().GetCount() > 0)
2476 #if defined( __WXGTK20__ )
2477 // if the wxStaticBox has created a wxPizza to contain its children
2478 // (see wxStaticBox::AddChild) then we need to place the items it contains
2479 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2480 // to the top-left corner of the staticbox:
2481 m_position
.x
= m_position
.y
= 0;
2482 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
2483 // the distance from the 'inner' content view to the embedded controls
2484 // this is independent of the title, therefore top_border is not relevant
2485 m_position
.x
= m_position
.y
= 10;
2487 // if the wxStaticBox has children, then these windows must be placed
2488 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2489 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2490 // to keep in count the static borders here!):
2491 m_position
.x
= other_border
;
2492 m_position
.y
= top_border
;
2497 // the windows contained in the staticbox have been created as siblings of the
2498 // staticbox (this is the "old" way of staticbox contents creation); in this
2499 // case we need to position them with coordinates relative to our common parent
2500 m_position
.x
+= other_border
;
2501 m_position
.y
+= top_border
;
2504 wxBoxSizer::RecalcSizes();
2506 m_position
= old_pos
;
2510 wxSize
wxStaticBoxSizer::CalcMin()
2512 int top_border
, other_border
;
2513 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2515 wxSize
ret( wxBoxSizer::CalcMin() );
2516 ret
.x
+= 2*other_border
;
2518 // ensure that we're wide enough to show the static box label (there is no
2519 // need to check for the static box best size in vertical direction though)
2520 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2521 if ( ret
.x
< boxWidth
)
2524 ret
.y
+= other_border
+ top_border
;
2529 void wxStaticBoxSizer::ShowItems( bool show
)
2531 m_staticBox
->Show( show
);
2532 wxBoxSizer::ShowItems( show
);
2535 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2537 // avoid deleting m_staticBox in our dtor if it's being detached from the
2538 // sizer (which can happen because it's being already destroyed for
2540 if ( window
== m_staticBox
)
2546 return wxSizer::Detach( window
);
2549 #endif // wxUSE_STATBOX
2551 //---------------------------------------------------------------------------
2552 // wxStdDialogButtonSizer
2553 //---------------------------------------------------------------------------
2557 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2558 : wxBoxSizer(wxHORIZONTAL
)
2560 // Vertical buttons with lots of space on either side
2561 // looks rubbish on WinCE, so let's not do this for now.
2562 // If we are going to use vertical buttons, we should
2563 // put the sizer to the right of other controls in the dialog,
2564 // and that's beyond the scope of this sizer.
2566 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2567 // If we have a PDA screen, put yes/no button over
2568 // all other buttons, otherwise on the left side.
2570 m_orient
= wxVERTICAL
;
2573 m_buttonAffirmative
= NULL
;
2574 m_buttonApply
= NULL
;
2575 m_buttonNegative
= NULL
;
2576 m_buttonCancel
= NULL
;
2577 m_buttonHelp
= NULL
;
2580 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2582 switch (mybutton
->GetId())
2587 m_buttonAffirmative
= mybutton
;
2590 m_buttonApply
= mybutton
;
2593 m_buttonNegative
= mybutton
;
2597 m_buttonCancel
= mybutton
;
2600 case wxID_CONTEXT_HELP
:
2601 m_buttonHelp
= mybutton
;
2608 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2610 m_buttonAffirmative
= button
;
2613 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2615 m_buttonNegative
= button
;
2618 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2620 m_buttonCancel
= button
;
2623 void wxStdDialogButtonSizer::Realize()
2626 Add(0, 0, 0, wxLEFT
, 6);
2628 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2630 if (m_buttonNegative
){
2631 // HIG POLICE BULLETIN - destructive buttons need extra padding
2632 // 24 pixels on either side
2633 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2636 // extra whitespace between help/negative and cancel/ok buttons
2637 Add(0, 0, 1, wxEXPAND
, 0);
2639 if (m_buttonCancel
){
2640 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2641 // Cancel or help should be default
2642 // m_buttonCancel->SetDefaultButton();
2645 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2646 // figure the best place is between Cancel and OK
2648 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2650 if (m_buttonAffirmative
){
2651 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2653 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2654 // these buttons have set labels under Mac so we should use them
2655 m_buttonAffirmative
->SetLabel(_("Save"));
2656 if (m_buttonNegative
)
2657 m_buttonNegative
->SetLabel(_("Don't Save"));
2661 // Extra space around and at the right
2663 #elif defined(__WXGTK20__)
2664 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2665 // says that the correct button order is
2667 // [Help] [Alternative] [Cancel] [Affirmative]
2669 // Flags ensuring that margins between the buttons are 6 pixels.
2671 flagsBtn
= wxSizerFlags().Centre().Border(wxLEFT
| wxRIGHT
, 3);
2673 // Margin around the entire sizer button should be 12.
2677 Add(m_buttonHelp
, flagsBtn
);
2679 // Align the rest of the buttons to the right.
2682 if (m_buttonNegative
)
2683 Add(m_buttonNegative
, flagsBtn
);
2686 Add(m_buttonApply
, flagsBtn
);
2689 Add(m_buttonCancel
, flagsBtn
);
2691 if (m_buttonAffirmative
)
2692 Add(m_buttonAffirmative
, flagsBtn
);
2694 // Ensure that the right margin is 12 as well.
2696 #elif defined(__WXMSW__)
2699 // right-justify buttons
2700 Add(0, 0, 1, wxEXPAND
, 0);
2702 if (m_buttonAffirmative
){
2703 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2706 if (m_buttonNegative
){
2707 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2710 if (m_buttonCancel
){
2711 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2714 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2717 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2719 // GTK+1 and any other platform
2721 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2723 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2725 // extra whitespace between help and cancel/ok buttons
2726 Add(0, 0, 1, wxEXPAND
, 0);
2729 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2731 if (m_buttonAffirmative
){
2732 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2735 if (m_buttonNegative
){
2736 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2739 if (m_buttonCancel
){
2740 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2741 // Cancel or help should be default
2742 // m_buttonCancel->SetDefaultButton();
2748 #endif // wxUSE_BUTTON