1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/sizer.cpp
3 // Purpose: provide new wxSizer class for layout
4 // Author: Robert Roebling and Robin Dunn, contributions by
5 // Dirk Holtwick, Ron Lee
6 // Modified by: Ron Lee
9 // Copyright: (c) Robin Dunn, Robert Roebling
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
13 // For compilers that support precompilation, includes "wx.h".
14 #include "wx/wxprec.h"
21 #include "wx/private/flagscheck.h"
24 #include "wx/string.h"
28 #include "wx/settings.h"
29 #include "wx/button.h"
30 #include "wx/statbox.h"
31 #include "wx/toplevel.h"
34 #include "wx/display.h"
35 #include "wx/vector.h"
36 #include "wx/listimpl.cpp"
39 //---------------------------------------------------------------------------
41 IMPLEMENT_CLASS(wxSizerItem
, wxObject
)
42 IMPLEMENT_CLASS(wxSizer
, wxObject
)
43 IMPLEMENT_CLASS(wxGridSizer
, wxSizer
)
44 IMPLEMENT_CLASS(wxFlexGridSizer
, wxGridSizer
)
45 IMPLEMENT_CLASS(wxBoxSizer
, wxSizer
)
47 IMPLEMENT_CLASS(wxStaticBoxSizer
, wxBoxSizer
)
50 IMPLEMENT_CLASS(wxStdDialogButtonSizer
, wxBoxSizer
)
53 WX_DEFINE_EXPORTED_LIST( wxSizerItemList
)
88 // ----------------------------------------------------------------------------
90 // ----------------------------------------------------------------------------
92 // check for flags conflicts
93 static const int SIZER_FLAGS_MASK
=
95 wxADD_FLAG(wxHORIZONTAL
,
96 wxADD_FLAG(wxVERTICAL
,
101 wxADD_FLAG(wxALIGN_NOT
,
102 wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL
,
103 wxADD_FLAG(wxALIGN_RIGHT
,
104 wxADD_FLAG(wxALIGN_BOTTOM
,
105 wxADD_FLAG(wxALIGN_CENTER_VERTICAL
,
106 wxADD_FLAG(wxFIXED_MINSIZE
,
107 wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN
,
108 wxADD_FLAG(wxSTRETCH_NOT
,
114 #define ASSERT_VALID_SIZER_FLAGS(f) wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK)
117 void wxSizerItem::Init(const wxSizerFlags
& flags
)
121 m_proportion
= flags
.GetProportion();
122 m_flag
= flags
.GetFlags();
123 m_border
= flags
.GetBorderInPixels();
125 ASSERT_VALID_SIZER_FLAGS( m_flag
);
128 wxSizerItem::wxSizerItem()
139 void wxSizerItem::DoSetWindow(wxWindow
*window
)
141 wxCHECK_RET( window
, wxT("NULL window in wxSizerItem::SetWindow()") );
143 m_kind
= Item_Window
;
146 // window doesn't become smaller than its initial size, whatever happens
147 m_minSize
= window
->GetSize();
149 if ( m_flag
& wxFIXED_MINSIZE
)
150 window
->SetMinSize(m_minSize
);
152 // aspect ratio calculated from initial size
156 wxSizerItem::wxSizerItem(wxWindow
*window
,
162 m_proportion(proportion
),
168 ASSERT_VALID_SIZER_FLAGS( m_flag
);
174 void wxSizerItem::DoSetSizer(wxSizer
*sizer
)
180 wxSizerItem::wxSizerItem(wxSizer
*sizer
,
187 m_proportion(proportion
),
194 ASSERT_VALID_SIZER_FLAGS( m_flag
);
198 // m_minSize is set later
202 void wxSizerItem::DoSetSpacer(const wxSize
& size
)
204 m_kind
= Item_Spacer
;
205 m_spacer
= new wxSizerSpacer(size
);
210 wxSize
wxSizerItem::AddBorderToSize(const wxSize
& size
) const
212 wxSize result
= size
;
214 // Notice that we shouldn't modify the unspecified component(s) of the
215 // size, it's perfectly valid to have either min or max size specified in
216 // one direction only and it shouldn't be applied in the other one then.
218 if ( result
.x
!= wxDefaultCoord
)
221 result
.x
+= m_border
;
223 result
.x
+= m_border
;
226 if ( result
.y
!= wxDefaultCoord
)
228 if (m_flag
& wxNORTH
)
229 result
.y
+= m_border
;
230 if (m_flag
& wxSOUTH
)
231 result
.y
+= m_border
;
237 wxSizerItem::wxSizerItem(int width
,
245 m_minSize(width
, height
), // minimal size is the initial size
246 m_proportion(proportion
),
252 ASSERT_VALID_SIZER_FLAGS( m_flag
);
254 DoSetSpacer(wxSize(width
, height
));
257 wxSizerItem::~wxSizerItem()
263 void wxSizerItem::Free()
271 m_window
->SetContainingSizer(NULL
);
284 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
290 wxSize
wxSizerItem::GetSpacer() const
293 if ( m_kind
== Item_Spacer
)
294 size
= m_spacer
->GetSize();
300 wxSize
wxSizerItem::GetSize() const
309 ret
= m_window
->GetSize();
313 ret
= m_sizer
->GetSize();
317 ret
= m_spacer
->GetSize();
322 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
329 if (m_flag
& wxNORTH
)
331 if (m_flag
& wxSOUTH
)
337 bool wxSizerItem::InformFirstDirection(int direction
, int size
, int availableOtherDir
)
339 // The size that come here will be including borders. Child items should get it
343 if( direction
==wxHORIZONTAL
)
350 else if( direction
==wxVERTICAL
)
352 if (m_flag
& wxNORTH
)
354 if (m_flag
& wxSOUTH
)
360 // Pass the information along to the held object
363 didUse
= GetSizer()->InformFirstDirection(direction
,size
,availableOtherDir
);
365 m_minSize
= GetSizer()->CalcMin();
369 didUse
= GetWindow()->InformFirstDirection(direction
,size
,availableOtherDir
);
371 m_minSize
= m_window
->GetEffectiveMinSize();
373 // This information is useful for items with wxSHAPED flag, since
374 // we can request an optimal min size for such an item. Even if
375 // we overwrite the m_minSize member here, we can read it back from
376 // the owned window (happens automatically).
377 if( (m_flag
& wxSHAPED
) && (m_flag
& wxEXPAND
) && direction
)
379 if( !wxIsNullDouble(m_ratio
) )
381 wxCHECK_MSG( (m_proportion
==0), false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
382 if( direction
==wxHORIZONTAL
&& !wxIsNullDouble(m_ratio
) )
384 // Clip size so that we don't take too much
385 if( availableOtherDir
>=0 && int(size
/m_ratio
)-m_minSize
.y
>availableOtherDir
)
386 size
= int((availableOtherDir
+m_minSize
.y
)*m_ratio
);
387 m_minSize
= wxSize(size
,int(size
/m_ratio
));
389 else if( direction
==wxVERTICAL
)
391 // Clip size so that we don't take too much
392 if( availableOtherDir
>=0 && int(size
*m_ratio
)-m_minSize
.x
>availableOtherDir
)
393 size
= int((availableOtherDir
+m_minSize
.x
)/m_ratio
);
394 m_minSize
= wxSize(int(size
*m_ratio
),size
);
404 wxSize
wxSizerItem::CalcMin()
408 m_minSize
= m_sizer
->GetMinSize();
410 // if we have to preserve aspect ratio _AND_ this is
411 // the first-time calculation, consider ret to be initial size
412 if ( (m_flag
& wxSHAPED
) && wxIsNullDouble(m_ratio
) )
415 else if ( IsWindow() )
417 // Since the size of the window may change during runtime, we
418 // should use the current minimal/best size.
419 m_minSize
= m_window
->GetEffectiveMinSize();
422 return GetMinSizeWithBorder();
425 wxSize
wxSizerItem::GetMinSizeWithBorder() const
427 return AddBorderToSize(m_minSize
);
430 wxSize
wxSizerItem::GetMaxSizeWithBorder() const
432 return AddBorderToSize(GetMaxSize());
435 void wxSizerItem::SetDimension( const wxPoint
& pos_
, const wxSize
& size_
)
439 if (m_flag
& wxSHAPED
)
441 // adjust aspect ratio
442 int rwidth
= (int) (size
.y
* m_ratio
);
446 int rheight
= (int) (size
.x
/ m_ratio
);
447 // add vertical space
448 if (m_flag
& wxALIGN_CENTER_VERTICAL
)
449 pos
.y
+= (size
.y
- rheight
) / 2;
450 else if (m_flag
& wxALIGN_BOTTOM
)
451 pos
.y
+= (size
.y
- rheight
);
452 // use reduced dimensions
455 else if (rwidth
< size
.x
)
457 // add horizontal space
458 if (m_flag
& wxALIGN_CENTER_HORIZONTAL
)
459 pos
.x
+= (size
.x
- rwidth
) / 2;
460 else if (m_flag
& wxALIGN_RIGHT
)
461 pos
.x
+= (size
.x
- rwidth
);
466 // This is what GetPosition() returns. Since we calculate
467 // borders afterwards, GetPosition() will be the left/top
468 // corner of the surrounding border.
480 if (m_flag
& wxNORTH
)
485 if (m_flag
& wxSOUTH
)
495 m_rect
= wxRect(pos
, size
);
500 wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
505 // Use wxSIZE_FORCE_EVENT here since a sizer item might
506 // have changed alignment or some other property which would
507 // not change the size of the window. In such a case, no
508 // wxSizeEvent would normally be generated and thus the
509 // control wouldn't get laid out correctly here.
511 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
512 wxSIZE_ALLOW_MINUS_ONE
|wxSIZE_FORCE_EVENT
);
514 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
515 wxSIZE_ALLOW_MINUS_ONE
);
520 m_sizer
->SetDimension(pos
, size
);
524 m_spacer
->SetSize(size
);
529 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
533 void wxSizerItem::DeleteWindows()
542 //We are deleting the window from this sizer - normally
543 //the window destroys the sizer associated with it,
544 //which might destroy this, which we don't want
545 m_window
->SetContainingSizer(NULL
);
547 //Putting this after the switch will result in a spacer
548 //not being deleted properly on destruction
553 m_sizer
->DeleteWindows();
558 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
563 void wxSizerItem::Show( bool show
)
568 wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
572 m_window
->Show(show
);
580 m_spacer
->Show(show
);
585 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
589 bool wxSizerItem::IsShown() const
591 if ( m_flag
& wxRESERVE_SPACE_EVEN_IF_HIDDEN
)
597 // we may be called from CalcMin(), just return false so that we're
602 return m_window
->IsShown();
605 // arbitrarily decide that if at least one of our elements is
606 // shown, so are we (this arbitrariness is the reason for
607 // deprecating this function)
608 return m_sizer
->AreAnyItemsShown();
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::AreAnyItemsShown() const
1285 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1288 if ( node
->GetData()->IsShown() )
1290 node
= node
->GetNext();
1296 bool wxSizer::IsShown( wxWindow
*window
) const
1298 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1301 wxSizerItem
*item
= node
->GetData();
1303 if (item
->GetWindow() == window
)
1305 return item
->IsShown();
1307 node
= node
->GetNext();
1310 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1315 bool wxSizer::IsShown( wxSizer
*sizer
) const
1317 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1320 wxSizerItem
*item
= node
->GetData();
1322 if (item
->GetSizer() == sizer
)
1324 return item
->IsShown();
1326 node
= node
->GetNext();
1329 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1334 bool wxSizer::IsShown( size_t index
) const
1336 wxCHECK_MSG( index
< m_children
.GetCount(),
1338 wxT("IsShown index is out of range") );
1340 return m_children
.Item( index
)->GetData()->IsShown();
1344 //---------------------------------------------------------------------------
1346 //---------------------------------------------------------------------------
1348 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1349 : m_rows( cols
== 0 ? 1 : 0 ),
1354 wxASSERT(cols
>= 0);
1357 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1358 : m_rows( cols
== 0 ? 1 : 0 ),
1360 m_vgap( gap
.GetHeight() ),
1361 m_hgap( gap
.GetWidth() )
1363 wxASSERT(cols
>= 0);
1366 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1367 : m_rows( rows
|| cols
? rows
: 1 ),
1372 wxASSERT(rows
>= 0 && cols
>= 0);
1375 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1376 : m_rows( rows
|| cols
? rows
: 1 ),
1378 m_vgap( gap
.GetHeight() ),
1379 m_hgap( gap
.GetWidth() )
1381 wxASSERT(rows
>= 0 && cols
>= 0);
1384 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1386 // if only the number of columns or the number of rows is specified for a
1387 // sizer, arbitrarily many items can be added to it but if both of them are
1388 // fixed, then the sizer can't have more than that many items -- check for
1389 // this here to ensure that we detect errors as soon as possible
1390 if ( m_cols
&& m_rows
)
1392 const int nitems
= m_children
.GetCount();
1393 if ( nitems
== m_cols
*m_rows
)
1397 "too many items (%d > %d*%d) in grid sizer (maybe you "
1398 "should omit the number of either rows or columns?)",
1399 nitems
+ 1, m_cols
, m_rows
)
1402 // additionally, continuing to use the specified number of columns
1403 // and rows is not a good idea as callers of CalcRowsCols() expect
1404 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1405 // which is not the case if there are too many items and results in
1406 // crashes, so let it compute the number of rows automatically by
1407 // forgetting the (wrong) number of rows specified (this also has a
1408 // nice side effect of giving only one assert even if there are
1409 // many more items than allowed in this sizer)
1414 return wxSizer::DoInsert(index
, item
);
1417 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1419 const int nitems
= m_children
.GetCount();
1421 ncols
= GetEffectiveColsCount();
1422 nrows
= GetEffectiveRowsCount();
1424 // Since Insert() checks for overpopulation, the following
1425 // should only assert if the grid was shrunk via SetRows() / SetCols()
1426 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1431 void wxGridSizer::RecalcSizes()
1433 int nitems
, nrows
, ncols
;
1434 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1437 wxSize
sz( GetSize() );
1438 wxPoint
pt( GetPosition() );
1440 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1441 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1444 for (int c
= 0; c
< ncols
; c
++)
1447 for (int r
= 0; r
< nrows
; r
++)
1449 int i
= r
* ncols
+ c
;
1452 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1454 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1456 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1464 wxSize
wxGridSizer::CalcMin()
1467 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1470 // Find the max width and height for any component
1474 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1477 wxSizerItem
*item
= node
->GetData();
1478 wxSize
sz( item
->CalcMin() );
1480 w
= wxMax( w
, sz
.x
);
1481 h
= wxMax( h
, sz
.y
);
1483 node
= node
->GetNext();
1486 // In case we have a nested sizer with a two step algo , give it
1487 // a chance to adjust to that (we give it width component)
1488 node
= m_children
.GetFirst();
1489 bool didChangeMinSize
= false;
1492 wxSizerItem
*item
= node
->GetData();
1493 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1495 node
= node
->GetNext();
1498 // And redo iteration in case min size changed
1499 if( didChangeMinSize
)
1501 node
= m_children
.GetFirst();
1505 wxSizerItem
*item
= node
->GetData();
1506 wxSize
sz( item
->GetMinSizeWithBorder() );
1508 w
= wxMax( w
, sz
.x
);
1509 h
= wxMax( h
, sz
.y
);
1511 node
= node
->GetNext();
1515 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1516 nrows
* h
+ (nrows
-1) * m_vgap
);
1519 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1522 wxSize
sz( item
->GetMinSizeWithBorder() );
1523 int flag
= item
->GetFlag();
1525 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1531 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1533 pt
.x
= x
+ (w
- sz
.x
) / 2;
1535 else if (flag
& wxALIGN_RIGHT
)
1537 pt
.x
= x
+ (w
- sz
.x
);
1540 if (flag
& wxALIGN_CENTER_VERTICAL
)
1542 pt
.y
= y
+ (h
- sz
.y
) / 2;
1544 else if (flag
& wxALIGN_BOTTOM
)
1546 pt
.y
= y
+ (h
- sz
.y
);
1550 item
->SetDimension(pt
, sz
);
1553 //---------------------------------------------------------------------------
1555 //---------------------------------------------------------------------------
1557 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1558 : wxGridSizer( cols
, vgap
, hgap
),
1559 m_flexDirection(wxBOTH
),
1560 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1564 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1565 : wxGridSizer( cols
, gap
),
1566 m_flexDirection(wxBOTH
),
1567 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1571 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1572 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1573 m_flexDirection(wxBOTH
),
1574 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1578 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1579 : wxGridSizer( rows
, cols
, gap
),
1580 m_flexDirection(wxBOTH
),
1581 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1585 wxFlexGridSizer::~wxFlexGridSizer()
1589 void wxFlexGridSizer::RecalcSizes()
1592 if ( !CalcRowsCols(nrows
, ncols
) )
1595 const wxPoint
pt(GetPosition());
1596 const wxSize
sz(GetSize());
1598 AdjustForGrowables(sz
);
1600 wxSizerItemList::const_iterator i
= m_children
.begin();
1601 const wxSizerItemList::const_iterator end
= m_children
.end();
1604 for ( int r
= 0; r
< nrows
; r
++ )
1606 if ( m_rowHeights
[r
] == -1 )
1608 // this row is entirely hidden, skip it
1609 for ( int c
= 0; c
< ncols
; c
++ )
1620 const int hrow
= m_rowHeights
[r
];
1621 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1626 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1628 const int wcol
= m_colWidths
[c
];
1633 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1637 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1649 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1650 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1652 // Sum total minimum size, including gaps between rows/columns.
1653 // -1 is used as a magic number meaning empty row/column.
1656 const size_t count
= sizes
.size();
1657 for ( size_t n
= 0; n
< count
; n
++ )
1659 if ( sizes
[n
] != -1 )
1662 total
+= gap
; // separate from the previous column
1671 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1673 // We have to recalculate the sizes in case the item minimum size has
1674 // changed since the previous layout, or the item has been hidden using
1675 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1676 // dimension of the row/column will be -1, indicating that the column
1677 // itself is hidden.
1678 m_rowHeights
.assign(nrows
, -1);
1679 m_colWidths
.assign(ncols
, -1);
1681 // n is the index of the item in left-to-right top-to-bottom order
1683 for ( wxSizerItemList::iterator i
= m_children
.begin();
1684 i
!= m_children
.end();
1687 wxSizerItem
* const item
= *i
;
1688 if ( item
->IsShown() )
1690 // NOTE: Not doing the calculation here, this is just
1691 // for finding max values.
1692 const wxSize
sz(item
->GetMinSizeWithBorder());
1694 const int row
= n
/ ncols
;
1695 const int col
= n
% ncols
;
1697 if ( sz
.y
> m_rowHeights
[row
] )
1698 m_rowHeights
[row
] = sz
.y
;
1699 if ( sz
.x
> m_colWidths
[col
] )
1700 m_colWidths
[col
] = sz
.x
;
1704 AdjustForFlexDirection();
1706 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1707 SumArraySizes(m_rowHeights
, m_vgap
));
1710 wxSize
wxFlexGridSizer::CalcMin()
1715 // Number of rows/columns can change as items are added or removed.
1716 if ( !CalcRowsCols(nrows
, ncols
) )
1720 // We have to recalculate the sizes in case the item minimum size has
1721 // changed since the previous layout, or the item has been hidden using
1722 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1723 // dimension of the row/column will be -1, indicating that the column
1724 // itself is hidden.
1725 m_rowHeights
.assign(nrows
, -1);
1726 m_colWidths
.assign(ncols
, -1);
1728 for ( wxSizerItemList::iterator i
= m_children
.begin();
1729 i
!= m_children
.end();
1732 wxSizerItem
* const item
= *i
;
1733 if ( item
->IsShown() )
1739 // The stage of looking for max values in each row/column has been
1740 // made a separate function, since it's reused in AdjustForGrowables.
1741 FindWidthsAndHeights(nrows
,ncols
);
1743 return m_calculatedMinSize
;
1746 void wxFlexGridSizer::AdjustForFlexDirection()
1748 // the logic in CalcMin works when we resize flexibly in both directions
1749 // but maybe this is not the case
1750 if ( m_flexDirection
!= wxBOTH
)
1752 // select the array corresponding to the direction in which we do *not*
1754 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1757 const size_t count
= array
.GetCount();
1759 // find the largest value in this array
1763 for ( n
= 0; n
< count
; ++n
)
1765 if ( array
[n
] > largest
)
1769 // and now fill it with the largest value
1770 for ( n
= 0; n
< count
; ++n
)
1772 // don't touch hidden rows
1773 if ( array
[n
] != -1 )
1779 // helper of AdjustForGrowables() which is called for rows/columns separately
1782 // delta: the extra space, we do nothing unless it's positive
1783 // growable: indices or growable rows/cols in sizes array
1784 // sizes: the height/widths of rows/cols to adjust
1785 // proportions: proportions of the growable rows/cols or NULL if they all
1786 // should be assumed to have proportion of 1
1788 DoAdjustForGrowables(int delta
,
1789 const wxArrayInt
& growable
,
1791 const wxArrayInt
*proportions
)
1796 // total sum of proportions of all non-hidden rows
1797 int sum_proportions
= 0;
1799 // number of currently shown growable rows
1802 const int max_idx
= sizes
.size();
1804 const size_t count
= growable
.size();
1806 for ( idx
= 0; idx
< count
; idx
++ )
1808 // Since the number of rows/columns can change as items are
1809 // inserted/deleted, we need to verify at runtime that the
1810 // requested growable rows/columns are still valid.
1811 if ( growable
[idx
] >= max_idx
)
1814 // If all items in a row/column are hidden, that row/column will
1815 // have a dimension of -1. This causes the row/column to be
1816 // hidden completely.
1817 if ( sizes
[growable
[idx
]] == -1 )
1821 sum_proportions
+= (*proportions
)[idx
];
1829 // the remaining extra free space, adjusted during each iteration
1830 for ( idx
= 0; idx
< count
; idx
++ )
1832 if ( growable
[idx
] >= max_idx
)
1835 if ( sizes
[ growable
[idx
] ] == -1 )
1839 if ( sum_proportions
== 0 )
1841 // no growable rows -- divide extra space evenly among all
1842 cur_delta
= delta
/num
;
1845 else // allocate extra space proportionally
1847 const int cur_prop
= (*proportions
)[idx
];
1848 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1849 sum_proportions
-= cur_prop
;
1852 sizes
[growable
[idx
]] += cur_delta
;
1857 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1860 // by the time this function is called, the sizer should be already fully
1861 // initialized and hence the number of its columns and rows is known and we
1862 // can check that all indices in m_growableCols/Rows are valid (see also
1863 // comments in AddGrowableCol/Row())
1864 if ( !m_rows
|| !m_cols
)
1868 int nrows
= CalcRows();
1870 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1872 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1873 "invalid growable row index" );
1879 int ncols
= CalcCols();
1881 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1883 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1884 "invalid growable column index" );
1888 #endif // wxDEBUG_LEVEL
1891 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1893 DoAdjustForGrowables
1895 sz
.x
- m_calculatedMinSize
.x
,
1898 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1902 // This gives nested objects that benefit from knowing one size
1903 // component in advance the chance to use that.
1904 bool didAdjustMinSize
= false;
1906 // Iterate over all items and inform about column width
1907 const int ncols
= GetEffectiveColsCount();
1909 for ( wxSizerItemList::iterator i
= m_children
.begin();
1910 i
!= m_children
.end();
1913 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1914 if ( ++col
== ncols
)
1918 // Only redo if info was actually used
1919 if( didAdjustMinSize
)
1921 DoAdjustForGrowables
1923 sz
.x
- m_calculatedMinSize
.x
,
1926 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1932 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1934 // pass NULL instead of proportions if the grow mode is ALL as we
1935 // should treat all rows as having proportion of 1 then
1936 DoAdjustForGrowables
1938 sz
.y
- m_calculatedMinSize
.y
,
1941 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1947 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1949 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1952 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1954 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1957 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1959 wxASSERT_MSG( !IsRowGrowable( idx
),
1960 "AddGrowableRow() called for growable row" );
1962 // notice that we intentionally don't check the index validity here in (the
1963 // common) case when the number of rows was not specified in the ctor -- in
1964 // this case it will be computed only later, when all items are added to
1965 // the sizer, and the check will be done in AdjustForGrowables()
1966 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1968 m_growableRows
.Add( idx
);
1969 m_growableRowsProportions
.Add( proportion
);
1972 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1974 wxASSERT_MSG( !IsColGrowable( idx
),
1975 "AddGrowableCol() called for growable column" );
1977 // see comment in AddGrowableRow(): although it's less common to omit the
1978 // specification of the number of columns, it still can also happen
1979 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1981 m_growableCols
.Add( idx
);
1982 m_growableColsProportions
.Add( proportion
);
1985 // helper function for RemoveGrowableCol/Row()
1987 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1989 const size_t count
= items
.size();
1990 for ( size_t n
= 0; n
< count
; n
++ )
1992 if ( (size_t)items
[n
] == idx
)
1995 proportions
.RemoveAt(n
);
2000 wxFAIL_MSG( wxT("column/row is already not growable") );
2003 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
2005 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
2008 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
2010 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
2013 //---------------------------------------------------------------------------
2015 //---------------------------------------------------------------------------
2017 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
2019 return IsVertical() ? Add(0, size
) : Add(size
, 0);
2026 Helper of RecalcSizes(): checks if there is enough remaining space for the
2027 min size of the given item and returns its min size or the entire remaining
2028 space depending on which one is greater.
2030 This function updates the remaining space parameter to account for the size
2031 effectively allocated to the item.
2034 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2036 int& remainingSpace
= *remainingSpace_
;
2039 if ( remainingSpace
> 0 )
2041 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2042 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2044 if ( size
>= remainingSpace
)
2046 // truncate the item to fit in the remaining space, this is better
2047 // than showing it only partially in general, even if both choices
2048 // are bad -- but there is nothing else we can do
2049 size
= remainingSpace
;
2052 remainingSpace
-= size
;
2054 else // no remaining space
2056 // no space at all left, no need to even query the item for its min
2057 // size as we can't give it to it anyhow
2064 } // anonymous namespace
2066 void wxBoxSizer::RecalcSizes()
2068 if ( m_children
.empty() )
2071 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2072 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2074 // the amount of free space which we should redistribute among the
2075 // stretchable items (i.e. those with non zero proportion)
2076 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2078 // declare loop variables used below:
2079 wxSizerItemList::const_iterator i
; // iterator in m_children list
2080 unsigned n
= 0; // item index in majorSizes array
2083 // First, inform item about the available size in minor direction as this
2084 // can change their size in the major direction. Also compute the number of
2085 // visible items and sum of their min sizes in major direction.
2087 int minMajorSize
= 0;
2088 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2090 wxSizerItem
* const item
= *i
;
2092 if ( !item
->IsShown() )
2095 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2096 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2097 wxSize szMin
= item
->GetMinSizeWithBorder();
2098 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2101 // Since we passed available space along to the item, it should not
2102 // take too much, so delta should not become negative.
2103 delta
-= deltaChange
;
2105 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2108 // update our min size have changed
2109 SizeInMajorDir(m_minSize
) = minMajorSize
;
2112 // space and sum of proportions for the remaining items, both may change
2114 wxCoord remaining
= totalMajorSize
;
2115 int totalProportion
= m_totalProportion
;
2117 // size of the (visible) items in major direction, -1 means "not fixed yet"
2118 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2121 // Check for the degenerated case when we don't have enough space for even
2122 // the min sizes of all the items: in this case we really can't do much
2123 // more than to allocate the min size to as many of fixed size items as
2124 // possible (on the assumption that variable size items such as text zones
2125 // or list boxes may use scrollbars to show their content even if their
2126 // size is less than min size but that fixed size items such as buttons
2127 // will suffer even more if we don't give them their min size)
2128 if ( totalMajorSize
< minMajorSize
)
2130 // Second degenerated case pass: allocate min size to all fixed size
2132 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2134 wxSizerItem
* const item
= *i
;
2136 if ( !item
->IsShown() )
2139 // deal with fixed size items only during this pass
2140 if ( item
->GetProportion() )
2143 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2147 // Third degenerated case pass: allocate min size to all the remaining,
2148 // i.e. non-fixed size, items.
2149 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2151 wxSizerItem
* const item
= *i
;
2153 if ( !item
->IsShown() )
2156 // we've already dealt with fixed size items above
2157 if ( !item
->GetProportion() )
2160 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2163 else // we do have enough space to give at least min sizes to all items
2165 // Second and maybe more passes in the non-degenerated case: deal with
2166 // fixed size items and items whose min size is greater than what we
2167 // would allocate to them taking their proportion into account. For
2168 // both of them, we will just use their min size, but for the latter we
2169 // also need to reexamine all the items as the items which fitted
2170 // before we adjusted their size upwards might not fit any more. This
2171 // does make for a quadratic algorithm but it's not obvious how to
2172 // avoid it and hopefully it's not a huge problem in practice as the
2173 // sizers don't have many items usually (and, of course, the algorithm
2174 // still reduces into a linear one if there is enough space for all the
2176 bool nonFixedSpaceChanged
= false;
2177 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2179 if ( nonFixedSpaceChanged
)
2181 i
= m_children
.begin();
2183 nonFixedSpaceChanged
= false;
2186 // check for the end of the loop only after the check above as
2187 // otherwise we wouldn't do another pass if the last child resulted
2188 // in non fixed space reduction
2189 if ( i
== m_children
.end() )
2192 wxSizerItem
* const item
= *i
;
2194 if ( !item
->IsShown() )
2197 // don't check the item which we had already dealt with during a
2198 // previous pass (this is more than an optimization, the code
2199 // wouldn't work correctly if we kept adjusting for the same item
2200 // over and over again)
2201 if ( majorSizes
[n
] != wxDefaultCoord
)
2204 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2206 // it doesn't make sense for min size to be negative but right now
2207 // it's possible to create e.g. a spacer with (-1, 10) as size and
2208 // people do it in their code apparently (see #11842) so ensure
2209 // that we don't use this -1 as real min size as it conflicts with
2210 // the meaning we use for it here and negative min sizes just don't
2211 // make sense anyhow (which is why it might be a better idea to
2212 // deal with them at wxSizerItem level in the future but for now
2213 // this is the minimal fix for the bug)
2217 const int propItem
= item
->GetProportion();
2220 // is the desired size of this item big enough?
2221 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2223 // yes, it is, we'll determine the real size of this
2224 // item later, for now just leave it as wxDefaultCoord
2228 // the proportion of this item won't count, it has
2229 // effectively become fixed
2230 totalProportion
-= propItem
;
2233 // we can already allocate space for this item
2234 majorSizes
[n
] = minMajor
;
2236 // change the amount of the space remaining to the other items,
2237 // as this can result in not being able to satisfy their
2238 // proportions any more we will need to redo another loop
2240 remaining
-= minMajor
;
2242 nonFixedSpaceChanged
= true;
2245 // Similar to the previous loop, but dealing with items whose max size
2246 // is less than what we would allocate to them taking their proportion
2248 nonFixedSpaceChanged
= false;
2249 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2251 if ( nonFixedSpaceChanged
)
2253 i
= m_children
.begin();
2255 nonFixedSpaceChanged
= false;
2258 // check for the end of the loop only after the check above as
2259 // otherwise we wouldn't do another pass if the last child resulted
2260 // in non fixed space reduction
2261 if ( i
== m_children
.end() )
2264 wxSizerItem
* const item
= *i
;
2266 if ( !item
->IsShown() )
2269 // don't check the item which we had already dealt with during a
2270 // previous pass (this is more than an optimization, the code
2271 // wouldn't work correctly if we kept adjusting for the same item
2272 // over and over again)
2273 if ( majorSizes
[n
] != wxDefaultCoord
)
2276 wxCoord maxMajor
= GetSizeInMajorDir(item
->GetMaxSizeWithBorder());
2278 // must be nonzero, fixed-size items were dealt with in previous loop
2279 const int propItem
= item
->GetProportion();
2281 // is the desired size of this item small enough?
2282 if ( maxMajor
< 0 ||
2283 (remaining
*propItem
)/totalProportion
<= maxMajor
)
2285 // yes, it is, we'll determine the real size of this
2286 // item later, for now just leave it as wxDefaultCoord
2290 // the proportion of this item won't count, it has
2291 // effectively become fixed
2292 totalProportion
-= propItem
;
2294 // we can already allocate space for this item
2295 majorSizes
[n
] = maxMajor
;
2297 // change the amount of the space remaining to the other items,
2298 // as this can result in not being able to satisfy their
2299 // proportions any more we will need to redo another loop
2301 remaining
-= maxMajor
;
2303 nonFixedSpaceChanged
= true;
2306 // Last by one pass: distribute the remaining space among the non-fixed
2307 // items whose size weren't fixed yet according to their proportions.
2308 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2310 wxSizerItem
* const item
= *i
;
2312 if ( !item
->IsShown() )
2315 if ( majorSizes
[n
] == wxDefaultCoord
)
2317 const int propItem
= item
->GetProportion();
2318 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2320 remaining
-= majorSizes
[n
];
2321 totalProportion
-= propItem
;
2327 // the position at which we put the next child
2328 wxPoint
pt(m_position
);
2331 // Final pass: finally do position the items correctly using their sizes as
2332 // determined above.
2333 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2335 wxSizerItem
* const item
= *i
;
2337 if ( !item
->IsShown() )
2340 const int majorSize
= majorSizes
[n
];
2342 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2344 // apply the alignment in the minor direction
2345 wxPoint
posChild(pt
);
2347 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2348 const int flag
= item
->GetFlag();
2349 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2351 // occupy all the available space if wxEXPAND was given and also if
2352 // the item is too big to fit -- in this case we truncate it below
2353 // its minimal size which is bad but better than not showing parts
2354 // of the window at all
2355 minorSize
= totalMinorSize
;
2357 // do not allow the size in the minor direction to grow beyond the max
2358 // size of the item in the minor direction
2359 const wxCoord maxMinorSize
= GetSizeInMinorDir(item
->GetMaxSizeWithBorder());
2360 if ( maxMinorSize
>= 0 && minorSize
> maxMinorSize
)
2361 minorSize
= maxMinorSize
;
2364 if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2366 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2368 // NB: wxCENTRE is used here only for backwards compatibility,
2369 // wxALIGN_CENTRE should be used in new code
2370 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2371 : wxALIGN_CENTRE_VERTICAL
)) )
2373 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2377 // apply RTL adjustment for horizontal sizers:
2378 if ( !IsVertical() && m_containingWindow
)
2380 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2388 // finally set size of this child and advance to the next one
2389 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2391 PosInMajorDir(pt
) += majorSize
;
2395 wxSize
wxBoxSizer::CalcMin()
2397 m_totalProportion
= 0;
2398 m_minSize
= wxSize(0, 0);
2400 // The minimal size for the sizer should be big enough to allocate its
2401 // element at least its minimal size but also, and this is the non trivial
2402 // part, to respect the children proportion. To satisfy the latter
2403 // condition we must find the greatest min-size-to-proportion ratio for all
2404 // elements with non-zero proportion.
2405 float maxMinSizeToProp
= 0.;
2406 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2407 i
!= m_children
.end();
2410 wxSizerItem
* const item
= *i
;
2412 if ( !item
->IsShown() )
2415 const wxSize sizeMinThis
= item
->CalcMin();
2416 if ( const int propThis
= item
->GetProportion() )
2418 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2419 minSizeToProp
/= propThis
;
2421 if ( minSizeToProp
> maxMinSizeToProp
)
2422 maxMinSizeToProp
= minSizeToProp
;
2424 m_totalProportion
+= item
->GetProportion();
2426 else // fixed size item
2428 // Just account for its size directly
2429 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2432 // In the transversal direction we just need to find the maximum.
2433 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2434 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2437 // Using the max ratio ensures that the min size is big enough for all
2438 // items to have their min size and satisfy the proportions among them.
2439 SizeInMajorDir(m_minSize
) += (int)(maxMinSizeToProp
*m_totalProportion
);
2444 //---------------------------------------------------------------------------
2446 //---------------------------------------------------------------------------
2450 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2451 : wxBoxSizer( orient
),
2454 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2456 // do this so that our Detach() is called if the static box is destroyed
2458 m_staticBox
->SetContainingSizer(this);
2461 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2462 : wxBoxSizer(orient
),
2463 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2466 m_staticBox
->SetContainingSizer(this);
2469 wxStaticBoxSizer::~wxStaticBoxSizer()
2474 void wxStaticBoxSizer::RecalcSizes()
2476 int top_border
, other_border
;
2477 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2479 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2481 wxSize
old_size( m_size
);
2482 m_size
.x
-= 2*other_border
;
2483 m_size
.y
-= top_border
+ other_border
;
2485 wxPoint
old_pos( m_position
);
2486 if (m_staticBox
->GetChildren().GetCount() > 0)
2488 #if defined( __WXGTK20__ )
2489 // if the wxStaticBox has created a wxPizza to contain its children
2490 // (see wxStaticBox::AddChild) then we need to place the items it contains
2491 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2492 // to the top-left corner of the staticbox:
2493 m_position
.x
= m_position
.y
= 0;
2494 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
2495 // the distance from the 'inner' content view to the embedded controls
2496 // this is independent of the title, therefore top_border is not relevant
2497 m_position
.x
= m_position
.y
= 10;
2499 // if the wxStaticBox has children, then these windows must be placed
2500 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2501 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2502 // to keep in count the static borders here!):
2503 m_position
.x
= other_border
;
2504 m_position
.y
= top_border
;
2509 // the windows contained in the staticbox have been created as siblings of the
2510 // staticbox (this is the "old" way of staticbox contents creation); in this
2511 // case we need to position them with coordinates relative to our common parent
2512 m_position
.x
+= other_border
;
2513 m_position
.y
+= top_border
;
2516 wxBoxSizer::RecalcSizes();
2518 m_position
= old_pos
;
2522 wxSize
wxStaticBoxSizer::CalcMin()
2524 int top_border
, other_border
;
2525 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2527 wxSize
ret( wxBoxSizer::CalcMin() );
2528 ret
.x
+= 2*other_border
;
2530 // ensure that we're wide enough to show the static box label (there is no
2531 // need to check for the static box best size in vertical direction though)
2532 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2533 if ( ret
.x
< boxWidth
)
2536 ret
.y
+= other_border
+ top_border
;
2541 void wxStaticBoxSizer::ShowItems( bool show
)
2543 m_staticBox
->Show( show
);
2544 wxBoxSizer::ShowItems( show
);
2547 bool wxStaticBoxSizer::AreAnyItemsShown() const
2549 // We don't need to check the status of our child items: if the box is
2550 // shown, this sizer should be considered shown even if all its elements
2551 // are hidden (or, more prosaically, there are no elements at all). And,
2552 // conversely, if the box is hidden then all our items, which are its
2553 // children, are hidden too.
2554 return m_staticBox
->IsShown();
2557 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2559 // avoid deleting m_staticBox in our dtor if it's being detached from the
2560 // sizer (which can happen because it's being already destroyed for
2562 if ( window
== m_staticBox
)
2568 return wxSizer::Detach( window
);
2571 #endif // wxUSE_STATBOX
2573 //---------------------------------------------------------------------------
2574 // wxStdDialogButtonSizer
2575 //---------------------------------------------------------------------------
2579 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2580 : wxBoxSizer(wxHORIZONTAL
)
2582 // Vertical buttons with lots of space on either side
2583 // looks rubbish on WinCE, so let's not do this for now.
2584 // If we are going to use vertical buttons, we should
2585 // put the sizer to the right of other controls in the dialog,
2586 // and that's beyond the scope of this sizer.
2588 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2589 // If we have a PDA screen, put yes/no button over
2590 // all other buttons, otherwise on the left side.
2592 m_orient
= wxVERTICAL
;
2595 m_buttonAffirmative
= NULL
;
2596 m_buttonApply
= NULL
;
2597 m_buttonNegative
= NULL
;
2598 m_buttonCancel
= NULL
;
2599 m_buttonHelp
= NULL
;
2602 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2604 switch (mybutton
->GetId())
2609 m_buttonAffirmative
= mybutton
;
2612 m_buttonApply
= mybutton
;
2615 m_buttonNegative
= mybutton
;
2619 m_buttonCancel
= mybutton
;
2622 case wxID_CONTEXT_HELP
:
2623 m_buttonHelp
= mybutton
;
2630 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2632 m_buttonAffirmative
= button
;
2635 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2637 m_buttonNegative
= button
;
2640 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2642 m_buttonCancel
= button
;
2645 void wxStdDialogButtonSizer::Realize()
2648 Add(0, 0, 0, wxLEFT
, 6);
2650 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2652 if (m_buttonNegative
){
2653 // HIG POLICE BULLETIN - destructive buttons need extra padding
2654 // 24 pixels on either side
2655 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2658 // extra whitespace between help/negative and cancel/ok buttons
2659 Add(0, 0, 1, wxEXPAND
, 0);
2661 if (m_buttonCancel
){
2662 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2663 // Cancel or help should be default
2664 // m_buttonCancel->SetDefaultButton();
2667 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2668 // figure the best place is between Cancel and OK
2670 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2672 if (m_buttonAffirmative
){
2673 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2675 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2676 // these buttons have set labels under Mac so we should use them
2677 m_buttonAffirmative
->SetLabel(_("Save"));
2678 if (m_buttonNegative
)
2679 m_buttonNegative
->SetLabel(_("Don't Save"));
2683 // Extra space around and at the right
2685 #elif defined(__WXGTK20__)
2686 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2687 // says that the correct button order is
2689 // [Help] [Alternative] [Cancel] [Affirmative]
2691 // Flags ensuring that margins between the buttons are 6 pixels.
2693 flagsBtn
= wxSizerFlags().Centre().Border(wxLEFT
| wxRIGHT
, 3);
2695 // Margin around the entire sizer button should be 12.
2699 Add(m_buttonHelp
, flagsBtn
);
2701 // Align the rest of the buttons to the right.
2704 if (m_buttonNegative
)
2705 Add(m_buttonNegative
, flagsBtn
);
2708 Add(m_buttonApply
, flagsBtn
);
2711 Add(m_buttonCancel
, flagsBtn
);
2713 if (m_buttonAffirmative
)
2714 Add(m_buttonAffirmative
, flagsBtn
);
2716 // Ensure that the right margin is 12 as well.
2718 #elif defined(__WXMSW__)
2721 // right-justify buttons
2722 Add(0, 0, 1, wxEXPAND
, 0);
2724 if (m_buttonAffirmative
){
2725 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2728 if (m_buttonNegative
){
2729 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2732 if (m_buttonCancel
){
2733 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2736 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2739 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2741 // GTK+1 and any other platform
2743 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2745 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2747 // extra whitespace between help and cancel/ok buttons
2748 Add(0, 0, 1, wxEXPAND
, 0);
2751 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2753 if (m_buttonAffirmative
){
2754 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2757 if (m_buttonNegative
){
2758 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2761 if (m_buttonCancel
){
2762 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2763 // Cancel or help should be default
2764 // m_buttonCancel->SetDefaultButton();
2770 #endif // wxUSE_BUTTON