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 laid 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
);
844 if (item
->IsWindow() && item
->GetWindow())
845 item
->GetWindow()->SetContainingSizer(NULL
);
852 void wxSizer::Clear( bool delete_windows
)
854 // First clear the ContainingSizer pointers
855 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
858 wxSizerItem
*item
= node
->GetData();
860 if (item
->IsWindow())
861 item
->GetWindow()->SetContainingSizer( NULL
);
862 node
= node
->GetNext();
865 // Destroy the windows if needed
869 // Now empty the list
870 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
873 void wxSizer::DeleteWindows()
875 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
878 wxSizerItem
*item
= node
->GetData();
880 item
->DeleteWindows();
881 node
= node
->GetNext();
885 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
887 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
889 // take the min size by default and limit it by max size
890 wxSize size
= GetMinClientSize(window
);
893 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
896 // hack for small screen devices where TLWs are always full screen
897 if ( tlw
->IsAlwaysMaximized() )
899 return tlw
->GetClientSize();
902 // limit the window to the size of the display it is on
903 int disp
= wxDisplay::GetFromWindow(window
);
904 if ( disp
== wxNOT_FOUND
)
906 // or, if we don't know which one it is, of the main one
910 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
912 // If determining the display size failed, skip the max size checks as
913 // we really don't want to create windows of (0, 0) size.
914 if ( !sizeMax
.x
|| !sizeMax
.y
)
917 // space for decorations and toolbars etc.
918 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
922 sizeMax
= GetMaxClientSize(window
);
925 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
927 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
933 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
935 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
937 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
940 wxSize
wxSizer::Fit( wxWindow
*window
)
942 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
945 window
->SetClientSize(ComputeFittingClientSize(window
));
947 // return entire size
948 return window
->GetSize();
951 void wxSizer::FitInside( wxWindow
*window
)
954 if (window
->IsTopLevel())
955 size
= VirtualFitSize( window
);
957 size
= GetMinClientSize( window
);
959 window
->SetVirtualSize( size
);
962 void wxSizer::Layout()
964 // (re)calculates minimums needed for each item and other preparations
968 // Applies the layout and repositions/resizes the items
972 void wxSizer::SetSizeHints( wxWindow
*window
)
974 // Preserve the window's max size hints, but set the
975 // lower bound according to the sizer calculations.
977 // This is equivalent to calling Fit(), except that we need to set
978 // the size hints _in between_ the two steps performed by Fit
979 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
980 // otherwise SetClientSize() could have no effect if there already are
981 // size hints in effect that forbid requested client size.
983 const wxSize clientSize
= ComputeFittingClientSize(window
);
985 window
->SetMinClientSize(clientSize
);
986 window
->SetClientSize(clientSize
);
989 #if WXWIN_COMPATIBILITY_2_8
990 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
994 #endif // WXWIN_COMPATIBILITY_2_8
996 // TODO on mac we need a function that determines how much free space this
997 // min size contains, in order to make sure that we have 20 pixels of free
998 // space around the controls
999 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
1001 return window
->WindowToClientSize(window
->GetMaxSize());
1004 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
1006 return GetMinSize(); // Already returns client size.
1009 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1011 wxSize size
= GetMinClientSize( window
);
1012 wxSize sizeMax
= GetMaxClientSize( window
);
1014 // Limit the size if sizeMax != wxDefaultSize
1016 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1018 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1024 wxSize
wxSizer::GetMinSize()
1026 wxSize
ret( CalcMin() );
1027 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1028 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1032 void wxSizer::DoSetMinSize( int width
, int height
)
1034 m_minSize
.x
= width
;
1035 m_minSize
.y
= height
;
1038 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1040 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1042 // Is it our immediate child?
1044 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1047 wxSizerItem
*item
= node
->GetData();
1049 if (item
->GetWindow() == window
)
1051 item
->SetMinSize( width
, height
);
1054 node
= node
->GetNext();
1057 // No? Search any subsizers we own then
1059 node
= m_children
.GetFirst();
1062 wxSizerItem
*item
= node
->GetData();
1064 if ( item
->GetSizer() &&
1065 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1067 // A child sizer found the requested windw, exit.
1070 node
= node
->GetNext();
1076 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1078 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1080 // Is it our immediate child?
1082 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1085 wxSizerItem
*item
= node
->GetData();
1087 if (item
->GetSizer() == sizer
)
1089 item
->GetSizer()->DoSetMinSize( width
, height
);
1092 node
= node
->GetNext();
1095 // No? Search any subsizers we own then
1097 node
= m_children
.GetFirst();
1100 wxSizerItem
*item
= node
->GetData();
1102 if ( item
->GetSizer() &&
1103 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1105 // A child found the requested sizer, exit.
1108 node
= node
->GetNext();
1114 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1116 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1118 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1120 wxSizerItem
*item
= node
->GetData();
1122 if (item
->GetSizer())
1124 // Sizers contains the minimal size in them, if not calculated ...
1125 item
->GetSizer()->DoSetMinSize( width
, height
);
1129 // ... but the minimal size of spacers and windows is stored via the item
1130 item
->SetMinSize( width
, height
);
1136 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1138 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1140 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1143 wxSizerItem
*item
= node
->GetData();
1145 if (item
->GetWindow() == window
)
1149 else if (recursive
&& item
->IsSizer())
1151 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1156 node
= node
->GetNext();
1162 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1164 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1166 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1169 wxSizerItem
*item
= node
->GetData();
1171 if (item
->GetSizer() == sizer
)
1175 else if (recursive
&& item
->IsSizer())
1177 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1182 node
= node
->GetNext();
1188 wxSizerItem
* wxSizer::GetItem( size_t index
)
1190 wxCHECK_MSG( index
< m_children
.GetCount(),
1192 wxT("GetItem index is out of range") );
1194 return m_children
.Item( index
)->GetData();
1197 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1199 // This gets a sizer item by the id of the sizer item
1200 // and NOT the id of a window if the item is a window.
1202 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1205 wxSizerItem
*item
= node
->GetData();
1207 if (item
->GetId() == id
)
1211 else if (recursive
&& item
->IsSizer())
1213 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1218 node
= node
->GetNext();
1224 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1226 wxSizerItem
*item
= GetItem( window
, recursive
);
1237 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1239 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1250 bool wxSizer::Show( size_t index
, bool show
)
1252 wxSizerItem
*item
= GetItem( index
);
1263 void wxSizer::ShowItems( bool show
)
1265 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1268 node
->GetData()->Show( show
);
1269 node
= node
->GetNext();
1273 bool wxSizer::IsShown( wxWindow
*window
) const
1275 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1278 wxSizerItem
*item
= node
->GetData();
1280 if (item
->GetWindow() == window
)
1282 return item
->IsShown();
1284 node
= node
->GetNext();
1287 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1292 bool wxSizer::IsShown( wxSizer
*sizer
) const
1294 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1297 wxSizerItem
*item
= node
->GetData();
1299 if (item
->GetSizer() == sizer
)
1301 return item
->IsShown();
1303 node
= node
->GetNext();
1306 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1311 bool wxSizer::IsShown( size_t index
) const
1313 wxCHECK_MSG( index
< m_children
.GetCount(),
1315 wxT("IsShown index is out of range") );
1317 return m_children
.Item( index
)->GetData()->IsShown();
1321 //---------------------------------------------------------------------------
1323 //---------------------------------------------------------------------------
1325 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1326 : m_rows( cols
== 0 ? 1 : 0 ),
1331 wxASSERT(cols
>= 0);
1334 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1335 : m_rows( cols
== 0 ? 1 : 0 ),
1337 m_vgap( gap
.GetHeight() ),
1338 m_hgap( gap
.GetWidth() )
1340 wxASSERT(cols
>= 0);
1343 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1344 : m_rows( rows
|| cols
? rows
: 1 ),
1349 wxASSERT(rows
>= 0 && cols
>= 0);
1352 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1353 : m_rows( rows
|| cols
? rows
: 1 ),
1355 m_vgap( gap
.GetHeight() ),
1356 m_hgap( gap
.GetWidth() )
1358 wxASSERT(rows
>= 0 && cols
>= 0);
1361 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1363 // if only the number of columns or the number of rows is specified for a
1364 // sizer, arbitrarily many items can be added to it but if both of them are
1365 // fixed, then the sizer can't have more than that many items -- check for
1366 // this here to ensure that we detect errors as soon as possible
1367 if ( m_cols
&& m_rows
)
1369 const int nitems
= m_children
.GetCount();
1370 if ( nitems
== m_cols
*m_rows
)
1374 "too many items (%d > %d*%d) in grid sizer (maybe you "
1375 "should omit the number of either rows or columns?)",
1376 nitems
+ 1, m_cols
, m_rows
)
1379 // additionally, continuing to use the specified number of columns
1380 // and rows is not a good idea as callers of CalcRowsCols() expect
1381 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1382 // which is not the case if there are too many items and results in
1383 // crashes, so let it compute the number of rows automatically by
1384 // forgetting the (wrong) number of rows specified (this also has a
1385 // nice side effect of giving only one assert even if there are
1386 // many more items than allowed in this sizer)
1391 return wxSizer::DoInsert(index
, item
);
1394 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1396 const int nitems
= m_children
.GetCount();
1398 ncols
= GetEffectiveColsCount();
1399 nrows
= GetEffectiveRowsCount();
1401 // Since Insert() checks for overpopulation, the following
1402 // should only assert if the grid was shrunk via SetRows() / SetCols()
1403 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1408 void wxGridSizer::RecalcSizes()
1410 int nitems
, nrows
, ncols
;
1411 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1414 wxSize
sz( GetSize() );
1415 wxPoint
pt( GetPosition() );
1417 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1418 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1421 for (int c
= 0; c
< ncols
; c
++)
1424 for (int r
= 0; r
< nrows
; r
++)
1426 int i
= r
* ncols
+ c
;
1429 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1431 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1433 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1441 wxSize
wxGridSizer::CalcMin()
1444 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1447 // Find the max width and height for any component
1451 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1454 wxSizerItem
*item
= node
->GetData();
1455 wxSize
sz( item
->CalcMin() );
1457 w
= wxMax( w
, sz
.x
);
1458 h
= wxMax( h
, sz
.y
);
1460 node
= node
->GetNext();
1463 // In case we have a nested sizer with a two step algo , give it
1464 // a chance to adjust to that (we give it width component)
1465 node
= m_children
.GetFirst();
1466 bool didChangeMinSize
= false;
1469 wxSizerItem
*item
= node
->GetData();
1470 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1472 node
= node
->GetNext();
1475 // And redo iteration in case min size changed
1476 if( didChangeMinSize
)
1478 node
= m_children
.GetFirst();
1482 wxSizerItem
*item
= node
->GetData();
1483 wxSize
sz( item
->GetMinSizeWithBorder() );
1485 w
= wxMax( w
, sz
.x
);
1486 h
= wxMax( h
, sz
.y
);
1488 node
= node
->GetNext();
1492 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1493 nrows
* h
+ (nrows
-1) * m_vgap
);
1496 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1499 wxSize
sz( item
->GetMinSizeWithBorder() );
1500 int flag
= item
->GetFlag();
1502 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1508 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1510 pt
.x
= x
+ (w
- sz
.x
) / 2;
1512 else if (flag
& wxALIGN_RIGHT
)
1514 pt
.x
= x
+ (w
- sz
.x
);
1517 if (flag
& wxALIGN_CENTER_VERTICAL
)
1519 pt
.y
= y
+ (h
- sz
.y
) / 2;
1521 else if (flag
& wxALIGN_BOTTOM
)
1523 pt
.y
= y
+ (h
- sz
.y
);
1527 item
->SetDimension(pt
, sz
);
1530 //---------------------------------------------------------------------------
1532 //---------------------------------------------------------------------------
1534 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1535 : wxGridSizer( cols
, vgap
, hgap
),
1536 m_flexDirection(wxBOTH
),
1537 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1541 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1542 : wxGridSizer( cols
, gap
),
1543 m_flexDirection(wxBOTH
),
1544 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1548 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1549 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1550 m_flexDirection(wxBOTH
),
1551 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1555 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1556 : wxGridSizer( rows
, cols
, gap
),
1557 m_flexDirection(wxBOTH
),
1558 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1562 wxFlexGridSizer::~wxFlexGridSizer()
1566 void wxFlexGridSizer::RecalcSizes()
1569 if ( !CalcRowsCols(nrows
, ncols
) )
1572 const wxPoint
pt(GetPosition());
1573 const wxSize
sz(GetSize());
1575 AdjustForGrowables(sz
);
1577 wxSizerItemList::const_iterator i
= m_children
.begin();
1578 const wxSizerItemList::const_iterator end
= m_children
.end();
1581 for ( int r
= 0; r
< nrows
; r
++ )
1583 if ( m_rowHeights
[r
] == -1 )
1585 // this row is entirely hidden, skip it
1586 for ( int c
= 0; c
< ncols
; c
++ )
1597 const int hrow
= m_rowHeights
[r
];
1598 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1603 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1605 const int wcol
= m_colWidths
[c
];
1610 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1614 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1626 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1627 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1629 // Sum total minimum size, including gaps between rows/columns.
1630 // -1 is used as a magic number meaning empty row/column.
1633 const size_t count
= sizes
.size();
1634 for ( size_t n
= 0; n
< count
; n
++ )
1636 if ( sizes
[n
] != -1 )
1639 total
+= gap
; // separate from the previous column
1648 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1650 // We have to recalculate the sizes in case the item minimum size has
1651 // changed since the previous layout, or the item has been hidden using
1652 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1653 // dimension of the row/column will be -1, indicating that the column
1654 // itself is hidden.
1655 m_rowHeights
.assign(nrows
, -1);
1656 m_colWidths
.assign(ncols
, -1);
1658 // n is the index of the item in left-to-right top-to-bottom order
1660 for ( wxSizerItemList::iterator i
= m_children
.begin();
1661 i
!= m_children
.end();
1664 wxSizerItem
* const item
= *i
;
1665 if ( item
->IsShown() )
1667 // NOTE: Not doing the calculation here, this is just
1668 // for finding max values.
1669 const wxSize
sz(item
->GetMinSizeWithBorder());
1671 const int row
= n
/ ncols
;
1672 const int col
= n
% ncols
;
1674 if ( sz
.y
> m_rowHeights
[row
] )
1675 m_rowHeights
[row
] = sz
.y
;
1676 if ( sz
.x
> m_colWidths
[col
] )
1677 m_colWidths
[col
] = sz
.x
;
1681 AdjustForFlexDirection();
1683 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1684 SumArraySizes(m_rowHeights
, m_vgap
));
1687 wxSize
wxFlexGridSizer::CalcMin()
1692 // Number of rows/columns can change as items are added or removed.
1693 if ( !CalcRowsCols(nrows
, ncols
) )
1697 // We have to recalculate the sizes in case the item minimum size has
1698 // changed since the previous layout, or the item has been hidden using
1699 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1700 // dimension of the row/column will be -1, indicating that the column
1701 // itself is hidden.
1702 m_rowHeights
.assign(nrows
, -1);
1703 m_colWidths
.assign(ncols
, -1);
1705 for ( wxSizerItemList::iterator i
= m_children
.begin();
1706 i
!= m_children
.end();
1709 wxSizerItem
* const item
= *i
;
1710 if ( item
->IsShown() )
1716 // The stage of looking for max values in each row/column has been
1717 // made a separate function, since it's reused in AdjustForGrowables.
1718 FindWidthsAndHeights(nrows
,ncols
);
1720 return m_calculatedMinSize
;
1723 void wxFlexGridSizer::AdjustForFlexDirection()
1725 // the logic in CalcMin works when we resize flexibly in both directions
1726 // but maybe this is not the case
1727 if ( m_flexDirection
!= wxBOTH
)
1729 // select the array corresponding to the direction in which we do *not*
1731 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1734 const size_t count
= array
.GetCount();
1736 // find the largest value in this array
1740 for ( n
= 0; n
< count
; ++n
)
1742 if ( array
[n
] > largest
)
1746 // and now fill it with the largest value
1747 for ( n
= 0; n
< count
; ++n
)
1749 // don't touch hidden rows
1750 if ( array
[n
] != -1 )
1756 // helper of AdjustForGrowables() which is called for rows/columns separately
1759 // delta: the extra space, we do nothing unless it's positive
1760 // growable: indices or growable rows/cols in sizes array
1761 // sizes: the height/widths of rows/cols to adjust
1762 // proportions: proportions of the growable rows/cols or NULL if they all
1763 // should be assumed to have proportion of 1
1765 DoAdjustForGrowables(int delta
,
1766 const wxArrayInt
& growable
,
1768 const wxArrayInt
*proportions
)
1773 // total sum of proportions of all non-hidden rows
1774 int sum_proportions
= 0;
1776 // number of currently shown growable rows
1779 const int max_idx
= sizes
.size();
1781 const size_t count
= growable
.size();
1783 for ( idx
= 0; idx
< count
; idx
++ )
1785 // Since the number of rows/columns can change as items are
1786 // inserted/deleted, we need to verify at runtime that the
1787 // requested growable rows/columns are still valid.
1788 if ( growable
[idx
] >= max_idx
)
1791 // If all items in a row/column are hidden, that row/column will
1792 // have a dimension of -1. This causes the row/column to be
1793 // hidden completely.
1794 if ( sizes
[growable
[idx
]] == -1 )
1798 sum_proportions
+= (*proportions
)[idx
];
1806 // the remaining extra free space, adjusted during each iteration
1807 for ( idx
= 0; idx
< count
; idx
++ )
1809 if ( growable
[idx
] >= max_idx
)
1812 if ( sizes
[ growable
[idx
] ] == -1 )
1816 if ( sum_proportions
== 0 )
1818 // no growable rows -- divide extra space evenly among all
1819 cur_delta
= delta
/num
;
1822 else // allocate extra space proportionally
1824 const int cur_prop
= (*proportions
)[idx
];
1825 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1826 sum_proportions
-= cur_prop
;
1829 sizes
[growable
[idx
]] += cur_delta
;
1834 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1837 // by the time this function is called, the sizer should be already fully
1838 // initialized and hence the number of its columns and rows is known and we
1839 // can check that all indices in m_growableCols/Rows are valid (see also
1840 // comments in AddGrowableCol/Row())
1841 if ( !m_rows
|| !m_cols
)
1845 int nrows
= CalcRows();
1847 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1849 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1850 "invalid growable row index" );
1856 int ncols
= CalcCols();
1858 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1860 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1861 "invalid growable column index" );
1865 #endif // wxDEBUG_LEVEL
1868 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1870 DoAdjustForGrowables
1872 sz
.x
- m_calculatedMinSize
.x
,
1875 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1879 // This gives nested objects that benefit from knowing one size
1880 // component in advance the chance to use that.
1881 bool didAdjustMinSize
= false;
1883 // Iterate over all items and inform about column width
1884 const int ncols
= GetEffectiveColsCount();
1886 for ( wxSizerItemList::iterator i
= m_children
.begin();
1887 i
!= m_children
.end();
1890 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1891 if ( ++col
== ncols
)
1895 // Only redo if info was actually used
1896 if( didAdjustMinSize
)
1898 DoAdjustForGrowables
1900 sz
.x
- m_calculatedMinSize
.x
,
1903 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1909 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1911 // pass NULL instead of proportions if the grow mode is ALL as we
1912 // should treat all rows as having proportion of 1 then
1913 DoAdjustForGrowables
1915 sz
.y
- m_calculatedMinSize
.y
,
1918 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1924 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1926 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1929 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1931 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1934 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1936 wxASSERT_MSG( !IsRowGrowable( idx
),
1937 "AddGrowableRow() called for growable row" );
1939 // notice that we intentionally don't check the index validity here in (the
1940 // common) case when the number of rows was not specified in the ctor -- in
1941 // this case it will be computed only later, when all items are added to
1942 // the sizer, and the check will be done in AdjustForGrowables()
1943 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1945 m_growableRows
.Add( idx
);
1946 m_growableRowsProportions
.Add( proportion
);
1949 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1951 wxASSERT_MSG( !IsColGrowable( idx
),
1952 "AddGrowableCol() called for growable column" );
1954 // see comment in AddGrowableRow(): although it's less common to omit the
1955 // specification of the number of columns, it still can also happen
1956 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1958 m_growableCols
.Add( idx
);
1959 m_growableColsProportions
.Add( proportion
);
1962 // helper function for RemoveGrowableCol/Row()
1964 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1966 const size_t count
= items
.size();
1967 for ( size_t n
= 0; n
< count
; n
++ )
1969 if ( (size_t)items
[n
] == idx
)
1972 proportions
.RemoveAt(n
);
1977 wxFAIL_MSG( wxT("column/row is already not growable") );
1980 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
1982 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
1985 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
1987 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
1990 //---------------------------------------------------------------------------
1992 //---------------------------------------------------------------------------
1994 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
1996 return IsVertical() ? Add(0, size
) : Add(size
, 0);
2003 Helper of RecalcSizes(): checks if there is enough remaining space for the
2004 min size of the given item and returns its min size or the entire remaining
2005 space depending on which one is greater.
2007 This function updates the remaining space parameter to account for the size
2008 effectively allocated to the item.
2011 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2013 int& remainingSpace
= *remainingSpace_
;
2016 if ( remainingSpace
> 0 )
2018 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2019 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2021 if ( size
>= remainingSpace
)
2023 // truncate the item to fit in the remaining space, this is better
2024 // than showing it only partially in general, even if both choices
2025 // are bad -- but there is nothing else we can do
2026 size
= remainingSpace
;
2029 remainingSpace
-= size
;
2031 else // no remaining space
2033 // no space at all left, no need to even query the item for its min
2034 // size as we can't give it to it anyhow
2041 } // anonymous namespace
2043 void wxBoxSizer::RecalcSizes()
2045 if ( m_children
.empty() )
2048 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2049 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2051 // the amount of free space which we should redistribute among the
2052 // stretchable items (i.e. those with non zero proportion)
2053 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2055 // declare loop variables used below:
2056 wxSizerItemList::const_iterator i
; // iterator in m_children list
2057 unsigned n
= 0; // item index in majorSizes array
2060 // First, inform item about the available size in minor direction as this
2061 // can change their size in the major direction. Also compute the number of
2062 // visible items and sum of their min sizes in major direction.
2064 int minMajorSize
= 0;
2065 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2067 wxSizerItem
* const item
= *i
;
2069 if ( !item
->IsShown() )
2072 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2073 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2074 wxSize szMin
= item
->GetMinSizeWithBorder();
2075 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2078 // Since we passed available space along to the item, it should not
2079 // take too much, so delta should not become negative.
2080 delta
-= deltaChange
;
2082 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2085 // update our min size and delta which may have changed
2086 SizeInMajorDir(m_minSize
) = minMajorSize
;
2087 delta
= totalMajorSize
- minMajorSize
;
2090 // space and sum of proportions for the remaining items, both may change
2092 wxCoord remaining
= totalMajorSize
;
2093 int totalProportion
= m_totalProportion
;
2095 // size of the (visible) items in major direction, -1 means "not fixed yet"
2096 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2099 // Check for the degenerated case when we don't have enough space for even
2100 // the min sizes of all the items: in this case we really can't do much
2101 // more than to allocate the min size to as many of fixed size items as
2102 // possible (on the assumption that variable size items such as text zones
2103 // or list boxes may use scrollbars to show their content even if their
2104 // size is less than min size but that fixed size items such as buttons
2105 // will suffer even more if we don't give them their min size)
2106 if ( totalMajorSize
< minMajorSize
)
2108 // Second degenerated case pass: allocate min size to all fixed size
2110 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2112 wxSizerItem
* const item
= *i
;
2114 if ( !item
->IsShown() )
2117 // deal with fixed size items only during this pass
2118 if ( item
->GetProportion() )
2121 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2125 // Third degenerated case pass: allocate min size to all the remaining,
2126 // i.e. non-fixed size, items.
2127 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2129 wxSizerItem
* const item
= *i
;
2131 if ( !item
->IsShown() )
2134 // we've already dealt with fixed size items above
2135 if ( !item
->GetProportion() )
2138 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2141 else // we do have enough space to give at least min sizes to all items
2143 // Second and maybe more passes in the non-degenerated case: deal with
2144 // fixed size items and items whose min size is greater than what we
2145 // would allocate to them taking their proportion into account. For
2146 // both of them, we will just use their min size, but for the latter we
2147 // also need to reexamine all the items as the items which fitted
2148 // before we adjusted their size upwards might not fit any more. This
2149 // does make for a quadratic algorithm but it's not obvious how to
2150 // avoid it and hopefully it's not a huge problem in practice as the
2151 // sizers don't have many items usually (and, of course, the algorithm
2152 // still reduces into a linear one if there is enough space for all the
2154 bool nonFixedSpaceChanged
= false;
2155 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2157 if ( nonFixedSpaceChanged
)
2159 i
= m_children
.begin();
2161 nonFixedSpaceChanged
= false;
2164 // check for the end of the loop only after the check above as
2165 // otherwise we wouldn't do another pass if the last child resulted
2166 // in non fixed space reduction
2167 if ( i
== m_children
.end() )
2170 wxSizerItem
* const item
= *i
;
2172 if ( !item
->IsShown() )
2175 // don't check the item which we had already dealt with during a
2176 // previous pass (this is more than an optimization, the code
2177 // wouldn't work correctly if we kept adjusting for the same item
2178 // over and over again)
2179 if ( majorSizes
[n
] != wxDefaultCoord
)
2182 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2184 // it doesn't make sense for min size to be negative but right now
2185 // it's possible to create e.g. a spacer with (-1, 10) as size and
2186 // people do it in their code apparently (see #11842) so ensure
2187 // that we don't use this -1 as real min size as it conflicts with
2188 // the meaning we use for it here and negative min sizes just don't
2189 // make sense anyhow (which is why it might be a better idea to
2190 // deal with them at wxSizerItem level in the future but for now
2191 // this is the minimal fix for the bug)
2195 const int propItem
= item
->GetProportion();
2198 // is the desired size of this item big enough?
2199 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2201 // yes, it is, we'll determine the real size of this
2202 // item later, for now just leave it as wxDefaultCoord
2206 // the proportion of this item won't count, it has
2207 // effectively become fixed
2208 totalProportion
-= propItem
;
2211 // we can already allocate space for this item
2212 majorSizes
[n
] = minMajor
;
2214 // change the amount of the space remaining to the other items,
2215 // as this can result in not being able to satisfy their
2216 // proportions any more we will need to redo another loop
2218 remaining
-= minMajor
;
2220 nonFixedSpaceChanged
= true;
2224 // Last by one pass: distribute the remaining space among the non-fixed
2225 // items whose size weren't fixed yet according to their proportions.
2226 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2228 wxSizerItem
* const item
= *i
;
2230 if ( !item
->IsShown() )
2233 if ( majorSizes
[n
] == wxDefaultCoord
)
2235 const int propItem
= item
->GetProportion();
2236 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2238 remaining
-= majorSizes
[n
];
2239 totalProportion
-= propItem
;
2245 // the position at which we put the next child
2246 wxPoint
pt(m_position
);
2249 // Final pass: finally do position the items correctly using their sizes as
2250 // determined above.
2251 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2253 wxSizerItem
* const item
= *i
;
2255 if ( !item
->IsShown() )
2258 const int majorSize
= majorSizes
[n
];
2260 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2262 // apply the alignment in the minor direction
2263 wxPoint
posChild(pt
);
2265 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2266 const int flag
= item
->GetFlag();
2267 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2269 // occupy all the available space if wxEXPAND was given and also if
2270 // the item is too big to fit -- in this case we truncate it below
2271 // its minimal size which is bad but better than not showing parts
2272 // of the window at all
2273 minorSize
= totalMinorSize
;
2275 else if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2277 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2279 // NB: wxCENTRE is used here only for backwards compatibility,
2280 // wxALIGN_CENTRE should be used in new code
2281 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2282 : wxALIGN_CENTRE_VERTICAL
)) )
2284 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2288 // apply RTL adjustment for horizontal sizers:
2289 if ( !IsVertical() && m_containingWindow
)
2291 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2299 // finally set size of this child and advance to the next one
2300 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2302 PosInMajorDir(pt
) += majorSize
;
2306 wxSize
wxBoxSizer::CalcMin()
2308 m_totalProportion
= 0;
2309 m_minSize
= wxSize(0, 0);
2311 // The minimal size for the sizer should be big enough to allocate its
2312 // element at least its minimal size but also, and this is the non trivial
2313 // part, to respect the children proportion. To satisfy the latter
2314 // condition we must find the greatest min-size-to-proportion ratio for all
2315 // elements with non-zero proportion.
2316 float maxMinSizeToProp
= 0.;
2317 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2318 i
!= m_children
.end();
2321 wxSizerItem
* const item
= *i
;
2323 if ( !item
->IsShown() )
2326 const wxSize sizeMinThis
= item
->CalcMin();
2327 if ( const int propThis
= item
->GetProportion() )
2329 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2330 minSizeToProp
/= propThis
;
2332 if ( minSizeToProp
> maxMinSizeToProp
)
2333 maxMinSizeToProp
= minSizeToProp
;
2335 m_totalProportion
+= item
->GetProportion();
2337 else // fixed size item
2339 // Just account for its size directly
2340 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2343 // In the transversal direction we just need to find the maximum.
2344 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2345 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2348 // Using the max ratio ensures that the min size is big enough for all
2349 // items to have their min size and satisfy the proportions among them.
2350 SizeInMajorDir(m_minSize
) += (int)(maxMinSizeToProp
*m_totalProportion
);
2355 //---------------------------------------------------------------------------
2357 //---------------------------------------------------------------------------
2361 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2362 : wxBoxSizer( orient
),
2365 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2367 // do this so that our Detach() is called if the static box is destroyed
2369 m_staticBox
->SetContainingSizer(this);
2372 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2373 : wxBoxSizer(orient
),
2374 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2377 m_staticBox
->SetContainingSizer(this);
2380 wxStaticBoxSizer::~wxStaticBoxSizer()
2385 void wxStaticBoxSizer::RecalcSizes()
2387 int top_border
, other_border
;
2388 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2390 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2392 wxSize
old_size( m_size
);
2393 m_size
.x
-= 2*other_border
;
2394 m_size
.y
-= top_border
+ other_border
;
2396 wxPoint
old_pos( m_position
);
2397 if (m_staticBox
->GetChildren().GetCount() > 0)
2399 #if defined( __WXGTK20__ )
2400 // if the wxStaticBox has created a wxPizza to contain its children
2401 // (see wxStaticBox::AddChild) then we need to place the items it contains
2402 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2403 // to the top-left corner of the staticbox:
2404 m_position
.x
= m_position
.y
= 0;
2405 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
2406 // the distance from the 'inner' content view to the embedded controls
2407 // this is independent of the title, therefore top_border is not relevant
2408 m_position
.x
= m_position
.y
= 10;
2410 // if the wxStaticBox has children, then these windows must be placed
2411 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2412 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2413 // to keep in count the static borders here!):
2414 m_position
.x
= other_border
;
2415 m_position
.y
= top_border
;
2420 // the windows contained in the staticbox have been created as siblings of the
2421 // staticbox (this is the "old" way of staticbox contents creation); in this
2422 // case we need to position them with coordinates relative to our common parent
2423 m_position
.x
+= other_border
;
2424 m_position
.y
+= top_border
;
2427 wxBoxSizer::RecalcSizes();
2429 m_position
= old_pos
;
2433 wxSize
wxStaticBoxSizer::CalcMin()
2435 int top_border
, other_border
;
2436 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2438 wxSize
ret( wxBoxSizer::CalcMin() );
2439 ret
.x
+= 2*other_border
;
2441 // ensure that we're wide enough to show the static box label (there is no
2442 // need to check for the static box best size in vertical direction though)
2443 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2444 if ( ret
.x
< boxWidth
)
2447 ret
.y
+= other_border
+ top_border
;
2452 void wxStaticBoxSizer::ShowItems( bool show
)
2454 m_staticBox
->Show( show
);
2455 wxBoxSizer::ShowItems( show
);
2458 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2460 // avoid deleting m_staticBox in our dtor if it's being detached from the
2461 // sizer (which can happen because it's being already destroyed for
2463 if ( window
== m_staticBox
)
2469 return wxSizer::Detach( window
);
2472 #endif // wxUSE_STATBOX
2474 //---------------------------------------------------------------------------
2475 // wxStdDialogButtonSizer
2476 //---------------------------------------------------------------------------
2480 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2481 : wxBoxSizer(wxHORIZONTAL
)
2483 // Vertical buttons with lots of space on either side
2484 // looks rubbish on WinCE, so let's not do this for now.
2485 // If we are going to use vertical buttons, we should
2486 // put the sizer to the right of other controls in the dialog,
2487 // and that's beyond the scope of this sizer.
2489 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2490 // If we have a PDA screen, put yes/no button over
2491 // all other buttons, otherwise on the left side.
2493 m_orient
= wxVERTICAL
;
2496 m_buttonAffirmative
= NULL
;
2497 m_buttonApply
= NULL
;
2498 m_buttonNegative
= NULL
;
2499 m_buttonCancel
= NULL
;
2500 m_buttonHelp
= NULL
;
2503 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2505 switch (mybutton
->GetId())
2510 m_buttonAffirmative
= mybutton
;
2513 m_buttonApply
= mybutton
;
2516 m_buttonNegative
= mybutton
;
2520 m_buttonCancel
= mybutton
;
2523 case wxID_CONTEXT_HELP
:
2524 m_buttonHelp
= mybutton
;
2531 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2533 m_buttonAffirmative
= button
;
2536 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2538 m_buttonNegative
= button
;
2541 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2543 m_buttonCancel
= button
;
2546 void wxStdDialogButtonSizer::Realize()
2549 Add(0, 0, 0, wxLEFT
, 6);
2551 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2553 if (m_buttonNegative
){
2554 // HIG POLICE BULLETIN - destructive buttons need extra padding
2555 // 24 pixels on either side
2556 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2559 // extra whitespace between help/negative and cancel/ok buttons
2560 Add(0, 0, 1, wxEXPAND
, 0);
2562 if (m_buttonCancel
){
2563 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2564 // Cancel or help should be default
2565 // m_buttonCancel->SetDefaultButton();
2568 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2569 // figure the best place is between Cancel and OK
2571 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2573 if (m_buttonAffirmative
){
2574 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2576 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2577 // these buttons have set labels under Mac so we should use them
2578 m_buttonAffirmative
->SetLabel(_("Save"));
2579 if (m_buttonNegative
)
2580 m_buttonNegative
->SetLabel(_("Don't Save"));
2584 // Extra space around and at the right
2586 #elif defined(__WXGTK20__)
2587 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2588 // says that the correct button order is
2590 // [Help] [Alternative] [Cancel] [Affirmative]
2592 // Flags ensuring that margins between the buttons are 6 pixels.
2594 flagsBtn
= wxSizerFlags().Centre().Border(wxLEFT
| wxRIGHT
, 3);
2596 // Margin around the entire sizer button should be 12.
2600 Add(m_buttonHelp
, flagsBtn
);
2602 // Align the rest of the buttons to the right.
2605 if (m_buttonNegative
)
2606 Add(m_buttonNegative
, flagsBtn
);
2609 Add(m_buttonApply
, flagsBtn
);
2612 Add(m_buttonCancel
, flagsBtn
);
2614 if (m_buttonAffirmative
)
2615 Add(m_buttonAffirmative
, flagsBtn
);
2617 // Ensure that the right margin is 12 as well.
2619 #elif defined(__WXMSW__)
2622 // right-justify buttons
2623 Add(0, 0, 1, wxEXPAND
, 0);
2625 if (m_buttonAffirmative
){
2626 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2629 if (m_buttonNegative
){
2630 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2633 if (m_buttonCancel
){
2634 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2637 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2640 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2642 // GTK+1 and any other platform
2644 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2646 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2648 // extra whitespace between help and cancel/ok buttons
2649 Add(0, 0, 1, wxEXPAND
, 0);
2652 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2654 if (m_buttonAffirmative
){
2655 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2658 if (m_buttonNegative
){
2659 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2662 if (m_buttonCancel
){
2663 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2664 // Cancel or help should be default
2665 // m_buttonCancel->SetDefaultButton();
2671 #endif // wxUSE_BUTTON