1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/sizer.cpp
3 // Purpose: provide new wxSizer class for layout
4 // Author: Robert Roebling and Robin Dunn, contributions by
5 // Dirk Holtwick, Ron Lee
6 // Modified by: Ron Lee
9 // Copyright: (c) Robin Dunn, Robert Roebling
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
13 // For compilers that support precompilation, includes "wx.h".
14 #include "wx/wxprec.h"
21 #include "wx/private/flagscheck.h"
24 #include "wx/string.h"
28 #include "wx/settings.h"
29 #include "wx/button.h"
30 #include "wx/statbox.h"
31 #include "wx/toplevel.h"
34 #include "wx/display.h"
35 #include "wx/vector.h"
36 #include "wx/listimpl.cpp"
39 //---------------------------------------------------------------------------
41 IMPLEMENT_CLASS(wxSizerItem
, wxObject
)
42 IMPLEMENT_CLASS(wxSizer
, wxObject
)
43 IMPLEMENT_CLASS(wxGridSizer
, wxSizer
)
44 IMPLEMENT_CLASS(wxFlexGridSizer
, wxGridSizer
)
45 IMPLEMENT_CLASS(wxBoxSizer
, wxSizer
)
47 IMPLEMENT_CLASS(wxStaticBoxSizer
, wxBoxSizer
)
50 IMPLEMENT_CLASS(wxStdDialogButtonSizer
, wxBoxSizer
)
53 WX_DEFINE_EXPORTED_LIST( wxSizerItemList
)
88 // ----------------------------------------------------------------------------
90 // ----------------------------------------------------------------------------
92 // check for flags conflicts
93 static const int SIZER_FLAGS_MASK
=
95 wxADD_FLAG(wxHORIZONTAL
,
96 wxADD_FLAG(wxVERTICAL
,
101 wxADD_FLAG(wxALIGN_NOT
,
102 wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL
,
103 wxADD_FLAG(wxALIGN_RIGHT
,
104 wxADD_FLAG(wxALIGN_BOTTOM
,
105 wxADD_FLAG(wxALIGN_CENTER_VERTICAL
,
106 wxADD_FLAG(wxFIXED_MINSIZE
,
107 wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN
,
108 wxADD_FLAG(wxSTRETCH_NOT
,
114 #define ASSERT_VALID_SIZER_FLAGS(f) wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK)
117 void wxSizerItem::Init(const wxSizerFlags
& flags
)
121 m_proportion
= flags
.GetProportion();
122 m_flag
= flags
.GetFlags();
123 m_border
= flags
.GetBorderInPixels();
125 ASSERT_VALID_SIZER_FLAGS( m_flag
);
128 wxSizerItem::wxSizerItem()
139 void wxSizerItem::DoSetWindow(wxWindow
*window
)
141 wxCHECK_RET( window
, wxT("NULL window in wxSizerItem::SetWindow()") );
143 m_kind
= Item_Window
;
146 // window doesn't become smaller than its initial size, whatever happens
147 m_minSize
= window
->GetSize();
149 if ( m_flag
& wxFIXED_MINSIZE
)
150 window
->SetMinSize(m_minSize
);
152 // aspect ratio calculated from initial size
156 wxSizerItem::wxSizerItem(wxWindow
*window
,
162 m_proportion(proportion
),
168 ASSERT_VALID_SIZER_FLAGS( m_flag
);
174 void wxSizerItem::DoSetSizer(wxSizer
*sizer
)
180 wxSizerItem::wxSizerItem(wxSizer
*sizer
,
187 m_proportion(proportion
),
194 ASSERT_VALID_SIZER_FLAGS( m_flag
);
198 // m_minSize is set later
202 void wxSizerItem::DoSetSpacer(const wxSize
& size
)
204 m_kind
= Item_Spacer
;
205 m_spacer
= new wxSizerSpacer(size
);
210 wxSizerItem::wxSizerItem(int width
,
218 m_minSize(width
, height
), // minimal size is the initial size
219 m_proportion(proportion
),
225 ASSERT_VALID_SIZER_FLAGS( m_flag
);
227 DoSetSpacer(wxSize(width
, height
));
230 wxSizerItem::~wxSizerItem()
236 void wxSizerItem::Free()
244 m_window
->SetContainingSizer(NULL
);
257 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
263 wxSize
wxSizerItem::GetSpacer() const
266 if ( m_kind
== Item_Spacer
)
267 size
= m_spacer
->GetSize();
273 wxSize
wxSizerItem::GetSize() const
282 ret
= m_window
->GetSize();
286 ret
= m_sizer
->GetSize();
290 ret
= m_spacer
->GetSize();
295 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
302 if (m_flag
& wxNORTH
)
304 if (m_flag
& wxSOUTH
)
310 bool wxSizerItem::InformFirstDirection(int direction
, int size
, int availableOtherDir
)
312 // The size that come here will be including borders. Child items should get it
316 if( direction
==wxHORIZONTAL
)
323 else if( direction
==wxVERTICAL
)
325 if (m_flag
& wxNORTH
)
327 if (m_flag
& wxSOUTH
)
333 // Pass the information along to the held object
336 didUse
= GetSizer()->InformFirstDirection(direction
,size
,availableOtherDir
);
338 m_minSize
= GetSizer()->CalcMin();
342 didUse
= GetWindow()->InformFirstDirection(direction
,size
,availableOtherDir
);
344 m_minSize
= m_window
->GetEffectiveMinSize();
346 // This information is useful for items with wxSHAPED flag, since
347 // we can request an optimal min size for such an item. Even if
348 // we overwrite the m_minSize member here, we can read it back from
349 // the owned window (happens automatically).
350 if( (m_flag
& wxSHAPED
) && (m_flag
& wxEXPAND
) && direction
)
352 if( !wxIsNullDouble(m_ratio
) )
354 wxCHECK_MSG( (m_proportion
==0), false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
355 if( direction
==wxHORIZONTAL
&& !wxIsNullDouble(m_ratio
) )
357 // Clip size so that we don't take too much
358 if( availableOtherDir
>=0 && int(size
/m_ratio
)-m_minSize
.y
>availableOtherDir
)
359 size
= int((availableOtherDir
+m_minSize
.y
)*m_ratio
);
360 m_minSize
= wxSize(size
,int(size
/m_ratio
));
362 else if( direction
==wxVERTICAL
)
364 // Clip size so that we don't take too much
365 if( availableOtherDir
>=0 && int(size
*m_ratio
)-m_minSize
.x
>availableOtherDir
)
366 size
= int((availableOtherDir
+m_minSize
.x
)/m_ratio
);
367 m_minSize
= wxSize(int(size
*m_ratio
),size
);
377 wxSize
wxSizerItem::CalcMin()
381 m_minSize
= m_sizer
->GetMinSize();
383 // if we have to preserve aspect ratio _AND_ this is
384 // the first-time calculation, consider ret to be initial size
385 if ( (m_flag
& wxSHAPED
) && wxIsNullDouble(m_ratio
) )
388 else if ( IsWindow() )
390 // Since the size of the window may change during runtime, we
391 // should use the current minimal/best size.
392 m_minSize
= m_window
->GetEffectiveMinSize();
395 return GetMinSizeWithBorder();
398 wxSize
wxSizerItem::GetMinSizeWithBorder() const
400 wxSize ret
= m_minSize
;
406 if (m_flag
& wxNORTH
)
408 if (m_flag
& wxSOUTH
)
415 void wxSizerItem::SetDimension( const wxPoint
& pos_
, const wxSize
& size_
)
419 if (m_flag
& wxSHAPED
)
421 // adjust aspect ratio
422 int rwidth
= (int) (size
.y
* m_ratio
);
426 int rheight
= (int) (size
.x
/ m_ratio
);
427 // add vertical space
428 if (m_flag
& wxALIGN_CENTER_VERTICAL
)
429 pos
.y
+= (size
.y
- rheight
) / 2;
430 else if (m_flag
& wxALIGN_BOTTOM
)
431 pos
.y
+= (size
.y
- rheight
);
432 // use reduced dimensions
435 else if (rwidth
< size
.x
)
437 // add horizontal space
438 if (m_flag
& wxALIGN_CENTER_HORIZONTAL
)
439 pos
.x
+= (size
.x
- rwidth
) / 2;
440 else if (m_flag
& wxALIGN_RIGHT
)
441 pos
.x
+= (size
.x
- rwidth
);
446 // This is what GetPosition() returns. Since we calculate
447 // borders afterwards, GetPosition() will be the left/top
448 // corner of the surrounding border.
460 if (m_flag
& wxNORTH
)
465 if (m_flag
& wxSOUTH
)
475 m_rect
= wxRect(pos
, size
);
480 wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
485 // Use wxSIZE_FORCE_EVENT here since a sizer item might
486 // have changed alignment or some other property which would
487 // not change the size of the window. In such a case, no
488 // wxSizeEvent would normally be generated and thus the
489 // control wouldn't get layed out correctly here.
491 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
492 wxSIZE_ALLOW_MINUS_ONE
|wxSIZE_FORCE_EVENT
);
494 m_window
->SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
,
495 wxSIZE_ALLOW_MINUS_ONE
);
500 m_sizer
->SetDimension(pos
, size
);
504 m_spacer
->SetSize(size
);
509 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
513 void wxSizerItem::DeleteWindows()
522 //We are deleting the window from this sizer - normally
523 //the window destroys the sizer associated with it,
524 //which might destroy this, which we don't want
525 m_window
->SetContainingSizer(NULL
);
527 //Putting this after the switch will result in a spacer
528 //not being deleted properly on destruction
533 m_sizer
->DeleteWindows();
538 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
543 void wxSizerItem::Show( bool show
)
548 wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
552 m_window
->Show(show
);
560 m_spacer
->Show(show
);
565 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
569 bool wxSizerItem::IsShown() const
571 if ( m_flag
& wxRESERVE_SPACE_EVEN_IF_HIDDEN
)
577 // we may be called from CalcMin(), just return false so that we're
582 return m_window
->IsShown();
586 // arbitrarily decide that if at least one of our elements is
587 // shown, so are we (this arbitrariness is the reason for
588 // deprecating this function)
589 for ( wxSizerItemList::compatibility_iterator
590 node
= m_sizer
->GetChildren().GetFirst();
592 node
= node
->GetNext() )
594 if ( node
->GetData()->IsShown() )
601 return m_spacer
->IsShown();
605 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
611 #if WXWIN_COMPATIBILITY_2_6
612 void wxSizerItem::SetOption( int option
)
614 SetProportion( option
);
617 int wxSizerItem::GetOption() const
619 return GetProportion();
621 #endif // WXWIN_COMPATIBILITY_2_6
624 //---------------------------------------------------------------------------
626 //---------------------------------------------------------------------------
630 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
633 wxSizerItem
* wxSizer::DoInsert( size_t index
, wxSizerItem
*item
)
635 m_children
.Insert( index
, item
);
637 if ( item
->GetWindow() )
638 item
->GetWindow()->SetContainingSizer( this );
640 if ( item
->GetSizer() )
641 item
->GetSizer()->SetContainingWindow( m_containingWindow
);
646 void wxSizer::SetContainingWindow(wxWindow
*win
)
648 if ( win
== m_containingWindow
)
651 m_containingWindow
= win
;
653 // set the same window for all nested sizers as well, they also are in the
655 for ( wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
657 node
= node
->GetNext() )
659 wxSizerItem
*const item
= node
->GetData();
660 wxSizer
*const sizer
= item
->GetSizer();
664 sizer
->SetContainingWindow(win
);
669 #if WXWIN_COMPATIBILITY_2_6
670 bool wxSizer::Remove( wxWindow
*window
)
672 return Detach( window
);
674 #endif // WXWIN_COMPATIBILITY_2_6
676 bool wxSizer::Remove( wxSizer
*sizer
)
678 wxASSERT_MSG( sizer
, wxT("Removing NULL sizer") );
680 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
683 wxSizerItem
*item
= node
->GetData();
685 if (item
->GetSizer() == sizer
)
688 m_children
.Erase( node
);
692 node
= node
->GetNext();
698 bool wxSizer::Remove( int index
)
700 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
702 wxT("Remove index is out of range") );
704 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
706 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
708 delete node
->GetData();
709 m_children
.Erase( node
);
714 bool wxSizer::Detach( wxSizer
*sizer
)
716 wxASSERT_MSG( sizer
, wxT("Detaching NULL sizer") );
718 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
721 wxSizerItem
*item
= node
->GetData();
723 if (item
->GetSizer() == sizer
)
727 m_children
.Erase( node
);
730 node
= node
->GetNext();
736 bool wxSizer::Detach( wxWindow
*window
)
738 wxASSERT_MSG( window
, wxT("Detaching NULL window") );
740 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
743 wxSizerItem
*item
= node
->GetData();
745 if (item
->GetWindow() == window
)
748 m_children
.Erase( node
);
751 node
= node
->GetNext();
757 bool wxSizer::Detach( int index
)
759 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
761 wxT("Detach index is out of range") );
763 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
765 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
767 wxSizerItem
*item
= node
->GetData();
769 if ( item
->IsSizer() )
773 m_children
.Erase( node
);
777 bool wxSizer::Replace( wxWindow
*oldwin
, wxWindow
*newwin
, bool recursive
)
779 wxASSERT_MSG( oldwin
, wxT("Replacing NULL window") );
780 wxASSERT_MSG( newwin
, wxT("Replacing with NULL window") );
782 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
785 wxSizerItem
*item
= node
->GetData();
787 if (item
->GetWindow() == oldwin
)
789 item
->AssignWindow(newwin
);
790 newwin
->SetContainingSizer( this );
793 else if (recursive
&& item
->IsSizer())
795 if (item
->GetSizer()->Replace( oldwin
, newwin
, true ))
799 node
= node
->GetNext();
805 bool wxSizer::Replace( wxSizer
*oldsz
, wxSizer
*newsz
, bool recursive
)
807 wxASSERT_MSG( oldsz
, wxT("Replacing NULL sizer") );
808 wxASSERT_MSG( newsz
, wxT("Replacing with NULL sizer") );
810 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
813 wxSizerItem
*item
= node
->GetData();
815 if (item
->GetSizer() == oldsz
)
817 item
->AssignSizer(newsz
);
820 else if (recursive
&& item
->IsSizer())
822 if (item
->GetSizer()->Replace( oldsz
, newsz
, true ))
826 node
= node
->GetNext();
832 bool wxSizer::Replace( size_t old
, wxSizerItem
*newitem
)
834 wxCHECK_MSG( old
< m_children
.GetCount(), false, wxT("Replace index is out of range") );
835 wxASSERT_MSG( newitem
, wxT("Replacing with NULL item") );
837 wxSizerItemList::compatibility_iterator node
= m_children
.Item( old
);
839 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
841 wxSizerItem
*item
= node
->GetData();
842 node
->SetData(newitem
);
848 void wxSizer::Clear( bool delete_windows
)
850 // First clear the ContainingSizer pointers
851 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
854 wxSizerItem
*item
= node
->GetData();
856 if (item
->IsWindow())
857 item
->GetWindow()->SetContainingSizer( NULL
);
858 node
= node
->GetNext();
861 // Destroy the windows if needed
865 // Now empty the list
866 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
869 void wxSizer::DeleteWindows()
871 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
874 wxSizerItem
*item
= node
->GetData();
876 item
->DeleteWindows();
877 node
= node
->GetNext();
881 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
883 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
885 // take the min size by default and limit it by max size
886 wxSize size
= GetMinClientSize(window
);
889 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
892 // hack for small screen devices where TLWs are always full screen
893 if ( tlw
->IsAlwaysMaximized() )
895 return tlw
->GetClientSize();
898 // limit the window to the size of the display it is on
899 int disp
= wxDisplay::GetFromWindow(window
);
900 if ( disp
== wxNOT_FOUND
)
902 // or, if we don't know which one it is, of the main one
906 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
908 // space for decorations and toolbars etc.
909 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
913 sizeMax
= GetMaxClientSize(window
);
916 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
918 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
924 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
926 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
928 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
931 wxSize
wxSizer::Fit( wxWindow
*window
)
933 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
936 window
->SetClientSize(ComputeFittingClientSize(window
));
938 // return entire size
939 return window
->GetSize();
942 void wxSizer::FitInside( wxWindow
*window
)
945 if (window
->IsTopLevel())
946 size
= VirtualFitSize( window
);
948 size
= GetMinClientSize( window
);
950 window
->SetVirtualSize( size
);
953 void wxSizer::Layout()
955 // (re)calculates minimums needed for each item and other preparations
959 // Applies the layout and repositions/resizes the items
963 void wxSizer::SetSizeHints( wxWindow
*window
)
965 // Preserve the window's max size hints, but set the
966 // lower bound according to the sizer calculations.
968 // This is equivalent to calling Fit(), except that we need to set
969 // the size hints _in between_ the two steps performed by Fit
970 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
971 // otherwise SetClientSize() could have no effect if there already are
972 // size hints in effect that forbid requested client size.
974 const wxSize clientSize
= ComputeFittingClientSize(window
);
976 window
->SetMinClientSize(clientSize
);
977 window
->SetClientSize(clientSize
);
980 #if WXWIN_COMPATIBILITY_2_8
981 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
985 #endif // WXWIN_COMPATIBILITY_2_8
987 // TODO on mac we need a function that determines how much free space this
988 // min size contains, in order to make sure that we have 20 pixels of free
989 // space around the controls
990 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
992 return window
->WindowToClientSize(window
->GetMaxSize());
995 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
997 return GetMinSize(); // Already returns client size.
1000 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1002 wxSize size
= GetMinClientSize( window
);
1003 wxSize sizeMax
= GetMaxClientSize( window
);
1005 // Limit the size if sizeMax != wxDefaultSize
1007 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1009 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1015 wxSize
wxSizer::GetMinSize()
1017 wxSize
ret( CalcMin() );
1018 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1019 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1023 void wxSizer::DoSetMinSize( int width
, int height
)
1025 m_minSize
.x
= width
;
1026 m_minSize
.y
= height
;
1029 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1031 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1033 // Is it our immediate child?
1035 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1038 wxSizerItem
*item
= node
->GetData();
1040 if (item
->GetWindow() == window
)
1042 item
->SetMinSize( width
, height
);
1045 node
= node
->GetNext();
1048 // No? Search any subsizers we own then
1050 node
= m_children
.GetFirst();
1053 wxSizerItem
*item
= node
->GetData();
1055 if ( item
->GetSizer() &&
1056 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1058 // A child sizer found the requested windw, exit.
1061 node
= node
->GetNext();
1067 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1069 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1071 // Is it our immediate child?
1073 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1076 wxSizerItem
*item
= node
->GetData();
1078 if (item
->GetSizer() == sizer
)
1080 item
->GetSizer()->DoSetMinSize( width
, height
);
1083 node
= node
->GetNext();
1086 // No? Search any subsizers we own then
1088 node
= m_children
.GetFirst();
1091 wxSizerItem
*item
= node
->GetData();
1093 if ( item
->GetSizer() &&
1094 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1096 // A child found the requested sizer, exit.
1099 node
= node
->GetNext();
1105 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1107 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1109 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1111 wxSizerItem
*item
= node
->GetData();
1113 if (item
->GetSizer())
1115 // Sizers contains the minimal size in them, if not calculated ...
1116 item
->GetSizer()->DoSetMinSize( width
, height
);
1120 // ... but the minimal size of spacers and windows is stored via the item
1121 item
->SetMinSize( width
, height
);
1127 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1129 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1131 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1134 wxSizerItem
*item
= node
->GetData();
1136 if (item
->GetWindow() == window
)
1140 else if (recursive
&& item
->IsSizer())
1142 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1147 node
= node
->GetNext();
1153 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1155 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1157 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1160 wxSizerItem
*item
= node
->GetData();
1162 if (item
->GetSizer() == sizer
)
1166 else if (recursive
&& item
->IsSizer())
1168 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1173 node
= node
->GetNext();
1179 wxSizerItem
* wxSizer::GetItem( size_t index
)
1181 wxCHECK_MSG( index
< m_children
.GetCount(),
1183 wxT("GetItem index is out of range") );
1185 return m_children
.Item( index
)->GetData();
1188 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1190 // This gets a sizer item by the id of the sizer item
1191 // and NOT the id of a window if the item is a window.
1193 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1196 wxSizerItem
*item
= node
->GetData();
1198 if (item
->GetId() == id
)
1202 else if (recursive
&& item
->IsSizer())
1204 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1209 node
= node
->GetNext();
1215 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1217 wxSizerItem
*item
= GetItem( window
, recursive
);
1228 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1230 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1241 bool wxSizer::Show( size_t index
, bool show
)
1243 wxSizerItem
*item
= GetItem( index
);
1254 void wxSizer::ShowItems( bool show
)
1256 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1259 node
->GetData()->Show( show
);
1260 node
= node
->GetNext();
1264 bool wxSizer::IsShown( wxWindow
*window
) const
1266 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1269 wxSizerItem
*item
= node
->GetData();
1271 if (item
->GetWindow() == window
)
1273 return item
->IsShown();
1275 node
= node
->GetNext();
1278 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1283 bool wxSizer::IsShown( wxSizer
*sizer
) const
1285 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1288 wxSizerItem
*item
= node
->GetData();
1290 if (item
->GetSizer() == sizer
)
1292 return item
->IsShown();
1294 node
= node
->GetNext();
1297 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1302 bool wxSizer::IsShown( size_t index
) const
1304 wxCHECK_MSG( index
< m_children
.GetCount(),
1306 wxT("IsShown index is out of range") );
1308 return m_children
.Item( index
)->GetData()->IsShown();
1312 //---------------------------------------------------------------------------
1314 //---------------------------------------------------------------------------
1316 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1317 : m_rows( cols
== 0 ? 1 : 0 ),
1324 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1325 : m_rows( cols
== 0 ? 1 : 0 ),
1327 m_vgap( gap
.GetHeight() ),
1328 m_hgap( gap
.GetWidth() )
1332 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1333 : m_rows( rows
|| cols
? rows
: 1 ),
1340 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1341 : m_rows( rows
|| cols
? rows
: 1 ),
1343 m_vgap( gap
.GetHeight() ),
1344 m_hgap( gap
.GetWidth() )
1348 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1350 // if only the number of columns or the number of rows is specified for a
1351 // sizer, arbitrarily many items can be added to it but if both of them are
1352 // fixed, then the sizer can't have more than that many items -- check for
1353 // this here to ensure that we detect errors as soon as possible
1354 if ( m_cols
&& m_rows
)
1356 const int nitems
= m_children
.GetCount();
1357 if ( nitems
== m_cols
*m_rows
)
1361 "too many items (%d > %d*%d) in grid sizer (maybe you "
1362 "should omit the number of either rows or columns?)",
1363 nitems
+ 1, m_cols
, m_rows
)
1366 // additionally, continuing to use the specified number of columns
1367 // and rows is not a good idea as callers of CalcRowsCols() expect
1368 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1369 // which is not the case if there are too many items and results in
1370 // crashes, so let it compute the number of rows automatically by
1371 // forgetting the (wrong) number of rows specified (this also has a
1372 // nice side effect of giving only one assert even if there are
1373 // many more items than allowed in this sizer)
1378 return wxSizer::DoInsert(index
, item
);
1381 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1383 const int nitems
= m_children
.GetCount();
1385 ncols
= GetEffectiveColsCount();
1386 nrows
= GetEffectiveRowsCount();
1388 // Since Insert() checks for overpopulation, the following
1389 // should only assert if the grid was shrunk via SetRows() / SetCols()
1390 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1395 void wxGridSizer::RecalcSizes()
1397 int nitems
, nrows
, ncols
;
1398 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1401 wxSize
sz( GetSize() );
1402 wxPoint
pt( GetPosition() );
1404 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1405 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1408 for (int c
= 0; c
< ncols
; c
++)
1411 for (int r
= 0; r
< nrows
; r
++)
1413 int i
= r
* ncols
+ c
;
1416 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1418 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1420 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1428 wxSize
wxGridSizer::CalcMin()
1431 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1434 // Find the max width and height for any component
1438 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1441 wxSizerItem
*item
= node
->GetData();
1442 wxSize
sz( item
->CalcMin() );
1444 w
= wxMax( w
, sz
.x
);
1445 h
= wxMax( h
, sz
.y
);
1447 node
= node
->GetNext();
1450 // In case we have a nested sizer with a two step algo , give it
1451 // a chance to adjust to that (we give it width component)
1452 node
= m_children
.GetFirst();
1453 bool didChangeMinSize
= false;
1456 wxSizerItem
*item
= node
->GetData();
1457 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1459 node
= node
->GetNext();
1462 // And redo iteration in case min size changed
1463 if( didChangeMinSize
)
1465 node
= m_children
.GetFirst();
1469 wxSizerItem
*item
= node
->GetData();
1470 wxSize
sz( item
->GetMinSizeWithBorder() );
1472 w
= wxMax( w
, sz
.x
);
1473 h
= wxMax( h
, sz
.y
);
1475 node
= node
->GetNext();
1479 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1480 nrows
* h
+ (nrows
-1) * m_vgap
);
1483 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1486 wxSize
sz( item
->GetMinSizeWithBorder() );
1487 int flag
= item
->GetFlag();
1489 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1495 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1497 pt
.x
= x
+ (w
- sz
.x
) / 2;
1499 else if (flag
& wxALIGN_RIGHT
)
1501 pt
.x
= x
+ (w
- sz
.x
);
1504 if (flag
& wxALIGN_CENTER_VERTICAL
)
1506 pt
.y
= y
+ (h
- sz
.y
) / 2;
1508 else if (flag
& wxALIGN_BOTTOM
)
1510 pt
.y
= y
+ (h
- sz
.y
);
1514 item
->SetDimension(pt
, sz
);
1517 //---------------------------------------------------------------------------
1519 //---------------------------------------------------------------------------
1521 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1522 : wxGridSizer( cols
, vgap
, hgap
),
1523 m_flexDirection(wxBOTH
),
1524 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1528 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1529 : wxGridSizer( cols
, gap
),
1530 m_flexDirection(wxBOTH
),
1531 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1535 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1536 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1537 m_flexDirection(wxBOTH
),
1538 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1542 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1543 : wxGridSizer( rows
, cols
, gap
),
1544 m_flexDirection(wxBOTH
),
1545 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1549 wxFlexGridSizer::~wxFlexGridSizer()
1553 void wxFlexGridSizer::RecalcSizes()
1556 if ( !CalcRowsCols(nrows
, ncols
) )
1559 const wxPoint
pt(GetPosition());
1560 const wxSize
sz(GetSize());
1562 AdjustForGrowables(sz
);
1564 wxSizerItemList::const_iterator i
= m_children
.begin();
1565 const wxSizerItemList::const_iterator end
= m_children
.end();
1568 for ( int r
= 0; r
< nrows
; r
++ )
1570 if ( m_rowHeights
[r
] == -1 )
1572 // this row is entirely hidden, skip it
1573 for ( int c
= 0; c
< ncols
; c
++ )
1584 const int hrow
= m_rowHeights
[r
];
1585 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1590 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1592 const int wcol
= m_colWidths
[c
];
1597 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1601 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1613 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1614 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1616 // Sum total minimum size, including gaps between rows/columns.
1617 // -1 is used as a magic number meaning empty row/column.
1620 const size_t count
= sizes
.size();
1621 for ( size_t n
= 0; n
< count
; n
++ )
1623 if ( sizes
[n
] != -1 )
1626 total
+= gap
; // separate from the previous column
1635 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1637 // We have to recalculate the sizes in case the item minimum size has
1638 // changed since the previous layout, or the item has been hidden using
1639 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1640 // dimension of the row/column will be -1, indicating that the column
1641 // itself is hidden.
1642 m_rowHeights
.assign(nrows
, -1);
1643 m_colWidths
.assign(ncols
, -1);
1645 // n is the index of the item in left-to-right top-to-bottom order
1647 for ( wxSizerItemList::iterator i
= m_children
.begin();
1648 i
!= m_children
.end();
1651 wxSizerItem
* const item
= *i
;
1652 if ( item
->IsShown() )
1654 // NOTE: Not doing the calculation here, this is just
1655 // for finding max values.
1656 const wxSize
sz(item
->GetMinSizeWithBorder());
1658 const int row
= n
/ ncols
;
1659 const int col
= n
% ncols
;
1661 if ( sz
.y
> m_rowHeights
[row
] )
1662 m_rowHeights
[row
] = sz
.y
;
1663 if ( sz
.x
> m_colWidths
[col
] )
1664 m_colWidths
[col
] = sz
.x
;
1668 AdjustForFlexDirection();
1670 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1671 SumArraySizes(m_rowHeights
, m_vgap
));
1674 wxSize
wxFlexGridSizer::CalcMin()
1679 // Number of rows/columns can change as items are added or removed.
1680 if ( !CalcRowsCols(nrows
, ncols
) )
1684 // We have to recalculate the sizes in case the item minimum size has
1685 // changed since the previous layout, or the item has been hidden using
1686 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1687 // dimension of the row/column will be -1, indicating that the column
1688 // itself is hidden.
1689 m_rowHeights
.assign(nrows
, -1);
1690 m_colWidths
.assign(ncols
, -1);
1692 for ( wxSizerItemList::iterator i
= m_children
.begin();
1693 i
!= m_children
.end();
1696 wxSizerItem
* const item
= *i
;
1697 if ( item
->IsShown() )
1703 // The stage of looking for max values in each row/column has been
1704 // made a separate function, since it's reused in AdjustForGrowables.
1705 FindWidthsAndHeights(nrows
,ncols
);
1707 return m_calculatedMinSize
;
1710 void wxFlexGridSizer::AdjustForFlexDirection()
1712 // the logic in CalcMin works when we resize flexibly in both directions
1713 // but maybe this is not the case
1714 if ( m_flexDirection
!= wxBOTH
)
1716 // select the array corresponding to the direction in which we do *not*
1718 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1721 const size_t count
= array
.GetCount();
1723 // find the largest value in this array
1727 for ( n
= 0; n
< count
; ++n
)
1729 if ( array
[n
] > largest
)
1733 // and now fill it with the largest value
1734 for ( n
= 0; n
< count
; ++n
)
1736 // don't touch hidden rows
1737 if ( array
[n
] != -1 )
1743 // helper of AdjustForGrowables() which is called for rows/columns separately
1746 // delta: the extra space, we do nothing unless it's positive
1747 // growable: indices or growable rows/cols in sizes array
1748 // sizes: the height/widths of rows/cols to adjust
1749 // proportions: proportions of the growable rows/cols or NULL if they all
1750 // should be assumed to have proportion of 1
1752 DoAdjustForGrowables(int delta
,
1753 const wxArrayInt
& growable
,
1755 const wxArrayInt
*proportions
)
1760 // total sum of proportions of all non-hidden rows
1761 int sum_proportions
= 0;
1763 // number of currently shown growable rows
1766 const int max_idx
= sizes
.size();
1768 const size_t count
= growable
.size();
1770 for ( idx
= 0; idx
< count
; idx
++ )
1772 // Since the number of rows/columns can change as items are
1773 // inserted/deleted, we need to verify at runtime that the
1774 // requested growable rows/columns are still valid.
1775 if ( growable
[idx
] >= max_idx
)
1778 // If all items in a row/column are hidden, that row/column will
1779 // have a dimension of -1. This causes the row/column to be
1780 // hidden completely.
1781 if ( sizes
[growable
[idx
]] == -1 )
1785 sum_proportions
+= (*proportions
)[idx
];
1793 // the remaining extra free space, adjusted during each iteration
1794 for ( idx
= 0; idx
< count
; idx
++ )
1796 if ( growable
[idx
] >= max_idx
)
1799 if ( sizes
[ growable
[idx
] ] == -1 )
1803 if ( sum_proportions
== 0 )
1805 // no growable rows -- divide extra space evenly among all
1806 cur_delta
= delta
/num
;
1809 else // allocate extra space proportionally
1811 const int cur_prop
= (*proportions
)[idx
];
1812 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1813 sum_proportions
-= cur_prop
;
1816 sizes
[growable
[idx
]] += cur_delta
;
1821 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1824 // by the time this function is called, the sizer should be already fully
1825 // initialized and hence the number of its columns and rows is known and we
1826 // can check that all indices in m_growableCols/Rows are valid (see also
1827 // comments in AddGrowableCol/Row())
1828 if ( !m_rows
|| !m_cols
)
1832 int nrows
= CalcRows();
1834 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1836 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1837 "invalid growable row index" );
1843 int ncols
= CalcCols();
1845 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1847 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1848 "invalid growable column index" );
1852 #endif // wxDEBUG_LEVEL
1855 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1857 DoAdjustForGrowables
1859 sz
.x
- m_calculatedMinSize
.x
,
1862 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1866 // This gives nested objects that benefit from knowing one size
1867 // component in advance the chance to use that.
1868 bool didAdjustMinSize
= false;
1870 // Iterate over all items and inform about column width
1871 const int ncols
= GetEffectiveColsCount();
1873 for ( wxSizerItemList::iterator i
= m_children
.begin();
1874 i
!= m_children
.end();
1877 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1878 if ( ++col
== ncols
)
1882 // Only redo if info was actually used
1883 if( didAdjustMinSize
)
1885 DoAdjustForGrowables
1887 sz
.x
- m_calculatedMinSize
.x
,
1890 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1896 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1898 // pass NULL instead of proportions if the grow mode is ALL as we
1899 // should treat all rows as having proportion of 1 then
1900 DoAdjustForGrowables
1902 sz
.y
- m_calculatedMinSize
.y
,
1905 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1911 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1913 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1916 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1918 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1921 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1923 wxASSERT_MSG( !IsRowGrowable( idx
),
1924 "AddGrowableRow() called for growable row" );
1926 // notice that we intentionally don't check the index validity here in (the
1927 // common) case when the number of rows was not specified in the ctor -- in
1928 // this case it will be computed only later, when all items are added to
1929 // the sizer, and the check will be done in AdjustForGrowables()
1930 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1932 m_growableRows
.Add( idx
);
1933 m_growableRowsProportions
.Add( proportion
);
1936 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1938 wxASSERT_MSG( !IsColGrowable( idx
),
1939 "AddGrowableCol() called for growable column" );
1941 // see comment in AddGrowableRow(): although it's less common to omit the
1942 // specification of the number of columns, it still can also happen
1943 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1945 m_growableCols
.Add( idx
);
1946 m_growableColsProportions
.Add( proportion
);
1949 // helper function for RemoveGrowableCol/Row()
1951 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1953 const size_t count
= items
.size();
1954 for ( size_t n
= 0; n
< count
; n
++ )
1956 if ( (size_t)items
[n
] == idx
)
1959 proportions
.RemoveAt(n
);
1964 wxFAIL_MSG( wxT("column/row is already not growable") );
1967 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
1969 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
1972 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
1974 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
1977 //---------------------------------------------------------------------------
1979 //---------------------------------------------------------------------------
1981 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
1983 return IsVertical() ? Add(0, size
) : Add(size
, 0);
1990 Helper of RecalcSizes(): checks if there is enough remaining space for the
1991 min size of the given item and returns its min size or the entire remaining
1992 space depending on which one is greater.
1994 This function updates the remaining space parameter to account for the size
1995 effectively allocated to the item.
1998 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2000 int& remainingSpace
= *remainingSpace_
;
2003 if ( remainingSpace
> 0 )
2005 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2006 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2008 if ( size
>= remainingSpace
)
2010 // truncate the item to fit in the remaining space, this is better
2011 // than showing it only partially in general, even if both choices
2012 // are bad -- but there is nothing else we can do
2013 size
= remainingSpace
;
2016 remainingSpace
-= size
;
2018 else // no remaining space
2020 // no space at all left, no need to even query the item for its min
2021 // size as we can't give it to it anyhow
2028 } // anonymous namespace
2030 void wxBoxSizer::RecalcSizes()
2032 if ( m_children
.empty() )
2035 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2036 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2038 // the amount of free space which we should redistribute among the
2039 // stretchable items (i.e. those with non zero proportion)
2040 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2042 // declare loop variables used below:
2043 wxSizerItemList::const_iterator i
; // iterator in m_children list
2044 unsigned n
= 0; // item index in majorSizes array
2047 // First, inform item about the available size in minor direction as this
2048 // can change their size in the major direction. Also compute the number of
2049 // visible items and sum of their min sizes in major direction.
2051 int minMajorSize
= 0;
2052 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2054 wxSizerItem
* const item
= *i
;
2056 if ( !item
->IsShown() )
2059 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2060 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2061 wxSize szMin
= item
->GetMinSizeWithBorder();
2062 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2065 // Since we passed available space along to the item, it should not
2066 // take too much, so delta should not become negative.
2067 delta
-= deltaChange
;
2069 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2072 // update our min size and delta which may have changed
2073 SizeInMajorDir(m_minSize
) = minMajorSize
;
2074 delta
= totalMajorSize
- minMajorSize
;
2077 // space and sum of proportions for the remaining items, both may change
2079 wxCoord remaining
= totalMajorSize
;
2080 int totalProportion
= m_totalProportion
;
2082 // size of the (visible) items in major direction, -1 means "not fixed yet"
2083 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2086 // Check for the degenerated case when we don't have enough space for even
2087 // the min sizes of all the items: in this case we really can't do much
2088 // more than to to allocate the min size to as many of fixed size items as
2089 // possible (on the assumption that variable size items such as text zones
2090 // or list boxes may use scrollbars to show their content even if their
2091 // size is less than min size but that fixed size items such as buttons
2092 // will suffer even more if we don't give them their min size)
2093 if ( totalMajorSize
< minMajorSize
)
2095 // Second degenerated case pass: allocate min size to all fixed size
2097 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2099 wxSizerItem
* const item
= *i
;
2101 if ( !item
->IsShown() )
2104 // deal with fixed size items only during this pass
2105 if ( item
->GetProportion() )
2108 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2112 // Third degenerated case pass: allocate min size to all the remaining,
2113 // i.e. non-fixed size, items.
2114 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2116 wxSizerItem
* const item
= *i
;
2118 if ( !item
->IsShown() )
2121 // we've already dealt with fixed size items above
2122 if ( !item
->GetProportion() )
2125 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2128 else // we do have enough space to give at least min sizes to all items
2130 // Second and maybe more passes in the non-degenerated case: deal with
2131 // fixed size items and items whose min size is greater than what we
2132 // would allocate to them taking their proportion into account. For
2133 // both of them, we will just use their min size, but for the latter we
2134 // also need to reexamine all the items as the items which fitted
2135 // before we adjusted their size upwards might not fit any more. This
2136 // does make for a quadratic algorithm but it's not obvious how to
2137 // avoid it and hopefully it's not a huge problem in practice as the
2138 // sizers don't have many items usually (and, of course, the algorithm
2139 // still reduces into a linear one if there is enough space for all the
2141 bool nonFixedSpaceChanged
= false;
2142 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2144 if ( nonFixedSpaceChanged
)
2146 i
= m_children
.begin();
2148 nonFixedSpaceChanged
= false;
2151 // check for the end of the loop only after the check above as
2152 // otherwise we wouldn't do another pass if the last child resulted
2153 // in non fixed space reduction
2154 if ( i
== m_children
.end() )
2157 wxSizerItem
* const item
= *i
;
2159 if ( !item
->IsShown() )
2162 // don't check the item which we had already dealt with during a
2163 // previous pass (this is more than an optimization, the code
2164 // wouldn't work correctly if we kept adjusting for the same item
2165 // over and over again)
2166 if ( majorSizes
[n
] != wxDefaultCoord
)
2169 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2171 // it doesn't make sense for min size to be negative but right now
2172 // it's possible to create e.g. a spacer with (-1, 10) as size and
2173 // people do it in their code apparently (see #11842) so ensure
2174 // that we don't use this -1 as real min size as it conflicts with
2175 // the meaning we use for it here and negative min sizes just don't
2176 // make sense anyhow (which is why it might be a better idea to
2177 // deal with them at wxSizerItem level in the future but for now
2178 // this is the minimal fix for the bug)
2182 const int propItem
= item
->GetProportion();
2185 // is the desired size of this item big enough?
2186 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2188 // yes, it is, we'll determine the real size of this
2189 // item later, for now just leave it as wxDefaultCoord
2193 // the proportion of this item won't count, it has
2194 // effectively become fixed
2195 totalProportion
-= propItem
;
2198 // we can already allocate space for this item
2199 majorSizes
[n
] = minMajor
;
2201 // change the amount of the space remaining to the other items,
2202 // as this can result in not being able to satisfy their
2203 // proportions any more we will need to redo another loop
2205 remaining
-= minMajor
;
2207 nonFixedSpaceChanged
= true;
2211 // Last by one pass: distribute the remaining space among the non-fixed
2212 // items whose size weren't fixed yet according to their proportions.
2213 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2215 wxSizerItem
* const item
= *i
;
2217 if ( !item
->IsShown() )
2220 if ( majorSizes
[n
] == wxDefaultCoord
)
2222 const int propItem
= item
->GetProportion();
2223 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2225 remaining
-= majorSizes
[n
];
2226 totalProportion
-= propItem
;
2232 // the position at which we put the next child
2233 wxPoint
pt(m_position
);
2236 // Final pass: finally do position the items correctly using their sizes as
2237 // determined above.
2238 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2240 wxSizerItem
* const item
= *i
;
2242 if ( !item
->IsShown() )
2245 const int majorSize
= majorSizes
[n
];
2247 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2249 // apply the alignment in the minor direction
2250 wxPoint
posChild(pt
);
2252 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2253 const int flag
= item
->GetFlag();
2254 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2256 // occupy all the available space if wxEXPAND was given and also if
2257 // the item is too big to fit -- in this case we truncate it below
2258 // its minimal size which is bad but better than not showing parts
2259 // of the window at all
2260 minorSize
= totalMinorSize
;
2262 else if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2264 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2266 // NB: wxCENTRE is used here only for backwards compatibility,
2267 // wxALIGN_CENTRE should be used in new code
2268 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2269 : wxALIGN_CENTRE_VERTICAL
)) )
2271 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2275 // apply RTL adjustment for horizontal sizers:
2276 if ( !IsVertical() && m_containingWindow
)
2278 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2286 // finally set size of this child and advance to the next one
2287 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2289 PosInMajorDir(pt
) += majorSize
;
2293 wxSize
wxBoxSizer::CalcMin()
2295 m_totalProportion
= 0;
2296 m_minSize
= wxSize(0, 0);
2298 // The minimal size for the sizer should be big enough to allocate its
2299 // element at least its minimal size but also, and this is the non trivial
2300 // part, to respect the children proportion. To satisfy the latter
2301 // condition we must find the greatest min-size-to-proportion ratio for all
2302 // elements with non-zero proportion.
2303 float maxMinSizeToProp
= 0.;
2304 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2305 i
!= m_children
.end();
2308 wxSizerItem
* const item
= *i
;
2310 if ( !item
->IsShown() )
2313 const wxSize sizeMinThis
= item
->CalcMin();
2314 if ( const int propThis
= item
->GetProportion() )
2316 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2317 minSizeToProp
/= propThis
;
2319 if ( minSizeToProp
> maxMinSizeToProp
)
2320 maxMinSizeToProp
= minSizeToProp
;
2322 m_totalProportion
+= item
->GetProportion();
2324 else // fixed size item
2326 // Just account for its size directly
2327 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2330 // In the transversal direction we just need to find the maximum.
2331 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2332 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2335 // Using the max ratio ensures that the min size is big enough for all
2336 // items to have their min size and satisfy the proportions among them.
2337 SizeInMajorDir(m_minSize
) += (int)(maxMinSizeToProp
*m_totalProportion
);
2342 //---------------------------------------------------------------------------
2344 //---------------------------------------------------------------------------
2348 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2349 : wxBoxSizer( orient
),
2352 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2354 // do this so that our Detach() is called if the static box is destroyed
2356 m_staticBox
->SetContainingSizer(this);
2359 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2360 : wxBoxSizer(orient
),
2361 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2364 m_staticBox
->SetContainingSizer(this);
2367 wxStaticBoxSizer::~wxStaticBoxSizer()
2372 void wxStaticBoxSizer::RecalcSizes()
2374 int top_border
, other_border
;
2375 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2377 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2379 wxSize
old_size( m_size
);
2380 m_size
.x
-= 2*other_border
;
2381 m_size
.y
-= top_border
+ other_border
;
2383 wxPoint
old_pos( m_position
);
2384 if (m_staticBox
->GetChildren().GetCount() > 0)
2386 #if defined( __WXGTK20__ )
2387 // if the wxStaticBox has created a wxPizza to contain its children
2388 // (see wxStaticBox::AddChild) then we need to place the items it contains
2389 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2390 // to the top-left corner of the staticbox:
2391 m_position
.x
= m_position
.y
= 0;
2393 // if the wxStaticBox has childrens, then these windows must be placed
2394 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2395 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2396 // to keep in count the static borders here!):
2397 m_position
.x
= other_border
;
2398 m_position
.y
= top_border
;
2403 // the windows contained in the staticbox have been created as siblings of the
2404 // staticbox (this is the "old" way of staticbox contents creation); in this
2405 // case we need to position them with coordinates relative to our common parent
2406 m_position
.x
+= other_border
;
2407 m_position
.y
+= top_border
;
2410 wxBoxSizer::RecalcSizes();
2412 m_position
= old_pos
;
2416 wxSize
wxStaticBoxSizer::CalcMin()
2418 int top_border
, other_border
;
2419 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2421 wxSize
ret( wxBoxSizer::CalcMin() );
2422 ret
.x
+= 2*other_border
;
2424 // ensure that we're wide enough to show the static box label (there is no
2425 // need to check for the static box best size in vertical direction though)
2426 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2427 if ( ret
.x
< boxWidth
)
2430 ret
.y
+= other_border
+ top_border
;
2435 void wxStaticBoxSizer::ShowItems( bool show
)
2437 m_staticBox
->Show( show
);
2438 wxBoxSizer::ShowItems( show
);
2441 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2443 // avoid deleting m_staticBox in our dtor if it's being detached from the
2444 // sizer (which can happen because it's being already destroyed for
2446 if ( window
== m_staticBox
)
2452 return wxSizer::Detach( window
);
2455 #endif // wxUSE_STATBOX
2457 //---------------------------------------------------------------------------
2458 // wxStdDialogButtonSizer
2459 //---------------------------------------------------------------------------
2463 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2464 : wxBoxSizer(wxHORIZONTAL
)
2466 // Vertical buttons with lots of space on either side
2467 // looks rubbish on WinCE, so let's not do this for now.
2468 // If we are going to use vertical buttons, we should
2469 // put the sizer to the right of other controls in the dialog,
2470 // and that's beyond the scope of this sizer.
2472 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2473 // If we have a PDA screen, put yes/no button over
2474 // all other buttons, otherwise on the left side.
2476 m_orient
= wxVERTICAL
;
2479 m_buttonAffirmative
= NULL
;
2480 m_buttonApply
= NULL
;
2481 m_buttonNegative
= NULL
;
2482 m_buttonCancel
= NULL
;
2483 m_buttonHelp
= NULL
;
2486 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2488 switch (mybutton
->GetId())
2493 m_buttonAffirmative
= mybutton
;
2496 m_buttonApply
= mybutton
;
2499 m_buttonNegative
= mybutton
;
2503 m_buttonCancel
= mybutton
;
2506 case wxID_CONTEXT_HELP
:
2507 m_buttonHelp
= mybutton
;
2514 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2516 m_buttonAffirmative
= button
;
2519 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2521 m_buttonNegative
= button
;
2524 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2526 m_buttonCancel
= button
;
2534 // Returns true only if the button is non-NULL and has the given id and text
2535 // (possible translated).
2536 bool IsStdButtonWithStdText(wxButton
*btn
, wxWindowID id
, const char *label
)
2541 if ( btn
->GetId() != id
)
2544 const wxString labelText
= btn
->GetLabelText();
2545 return labelText
== label
|| labelText
== wxGetTranslation(label
);
2548 } // anonymous namespace
2550 #endif // __WXGTK20__
2552 void wxStdDialogButtonSizer::Realize()
2555 Add(0, 0, 0, wxLEFT
, 6);
2557 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2559 if (m_buttonNegative
){
2560 // HIG POLICE BULLETIN - destructive buttons need extra padding
2561 // 24 pixels on either side
2562 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2565 // extra whitespace between help/negative and cancel/ok buttons
2566 Add(0, 0, 1, wxEXPAND
, 0);
2568 if (m_buttonCancel
){
2569 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2570 // Cancel or help should be default
2571 // m_buttonCancel->SetDefaultButton();
2574 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2575 // figure the best place is between Cancel and OK
2577 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2579 if (m_buttonAffirmative
){
2580 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2582 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2583 // these buttons have set labels under Mac so we should use them
2584 m_buttonAffirmative
->SetLabel(_("Save"));
2585 if (m_buttonNegative
)
2586 m_buttonNegative
->SetLabel(_("Don't Save"));
2590 // Extra space around and at the right
2592 #elif defined(__WXGTK20__)
2593 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2594 // describes the margins and the buttons order but basically it is
2596 // [Help] [Alternative] [Cancel] [Affirmative]
2598 // in general case but, somewhat confusingly, the native message box
2599 // uses "No Yes Cancel" with these particular buttons so do we as well.
2601 // Flags ensuring that margins between the buttons are 6 pixels.
2603 flagsBtn
= wxSizerFlags().Centre().Border(wxLEFT
| wxRIGHT
, 3);
2605 // Margin around the entire sizer button should be 12.
2609 Add(m_buttonHelp
, flagsBtn
);
2611 // Align the rest of the buttons to the right.
2614 // "No Yes Cancel" is an exception to the general rule according to
2615 // which the affirmative buttons goes after "Cancel" so treat it
2617 if ( IsStdButtonWithStdText(m_buttonNegative
, wxID_NO
, "No") &&
2618 IsStdButtonWithStdText(m_buttonAffirmative
, wxID_YES
, "Yes") &&
2619 IsStdButtonWithStdText(m_buttonCancel
, wxID_CANCEL
, "Cancel") )
2621 Add(m_buttonNegative
, flagsBtn
);
2622 Add(m_buttonAffirmative
, flagsBtn
);
2623 Add(m_buttonCancel
, flagsBtn
);
2625 else // Use standard layout
2627 if (m_buttonNegative
)
2628 Add(m_buttonNegative
, flagsBtn
);
2631 Add(m_buttonApply
, flagsBtn
);
2634 Add(m_buttonCancel
, flagsBtn
);
2636 if (m_buttonAffirmative
)
2637 Add(m_buttonAffirmative
, flagsBtn
);
2640 // Ensure that the right margin is 12 as well.
2642 #elif defined(__WXMSW__)
2645 // right-justify buttons
2646 Add(0, 0, 1, wxEXPAND
, 0);
2648 if (m_buttonAffirmative
){
2649 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2652 if (m_buttonNegative
){
2653 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2656 if (m_buttonCancel
){
2657 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2660 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2663 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2665 // GTK+1 and any other platform
2667 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2669 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2671 // extra whitespace between help and cancel/ok buttons
2672 Add(0, 0, 1, wxEXPAND
, 0);
2675 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2677 if (m_buttonAffirmative
){
2678 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2681 if (m_buttonNegative
){
2682 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2685 if (m_buttonCancel
){
2686 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2687 // Cancel or help should be default
2688 // m_buttonCancel->SetDefaultButton();
2694 #endif // wxUSE_BUTTON