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();
585 // arbitrarily decide that if at least one of our elements is
586 // shown, so are we (this arbitrariness is the reason for
587 // deprecating this function)
589 // Some apps (such as dialog editors) depend on an empty sizer still
590 // being laid out correctly and reporting the correct size and position.
591 if (m_sizer
->GetChildren().GetCount() == 0)
594 for ( wxSizerItemList::compatibility_iterator
595 node
= m_sizer
->GetChildren().GetFirst();
597 node
= node
->GetNext() )
599 if ( node
->GetData()->IsShown() )
606 return m_spacer
->IsShown();
610 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
616 #if WXWIN_COMPATIBILITY_2_6
617 void wxSizerItem::SetOption( int option
)
619 SetProportion( option
);
622 int wxSizerItem::GetOption() const
624 return GetProportion();
626 #endif // WXWIN_COMPATIBILITY_2_6
629 //---------------------------------------------------------------------------
631 //---------------------------------------------------------------------------
635 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
638 wxSizerItem
* wxSizer::DoInsert( size_t index
, wxSizerItem
*item
)
640 m_children
.Insert( index
, item
);
642 if ( item
->GetWindow() )
643 item
->GetWindow()->SetContainingSizer( this );
645 if ( item
->GetSizer() )
646 item
->GetSizer()->SetContainingWindow( m_containingWindow
);
651 void wxSizer::SetContainingWindow(wxWindow
*win
)
653 if ( win
== m_containingWindow
)
656 m_containingWindow
= win
;
658 // set the same window for all nested sizers as well, they also are in the
660 for ( wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
662 node
= node
->GetNext() )
664 wxSizerItem
*const item
= node
->GetData();
665 wxSizer
*const sizer
= item
->GetSizer();
669 sizer
->SetContainingWindow(win
);
674 #if WXWIN_COMPATIBILITY_2_6
675 bool wxSizer::Remove( wxWindow
*window
)
677 return Detach( window
);
679 #endif // WXWIN_COMPATIBILITY_2_6
681 bool wxSizer::Remove( wxSizer
*sizer
)
683 wxASSERT_MSG( sizer
, wxT("Removing NULL sizer") );
685 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
688 wxSizerItem
*item
= node
->GetData();
690 if (item
->GetSizer() == sizer
)
693 m_children
.Erase( node
);
697 node
= node
->GetNext();
703 bool wxSizer::Remove( int index
)
705 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
707 wxT("Remove index is out of range") );
709 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
711 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
713 delete node
->GetData();
714 m_children
.Erase( node
);
719 bool wxSizer::Detach( wxSizer
*sizer
)
721 wxASSERT_MSG( sizer
, wxT("Detaching NULL sizer") );
723 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
726 wxSizerItem
*item
= node
->GetData();
728 if (item
->GetSizer() == sizer
)
732 m_children
.Erase( node
);
735 node
= node
->GetNext();
741 bool wxSizer::Detach( wxWindow
*window
)
743 wxASSERT_MSG( window
, wxT("Detaching NULL window") );
745 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
748 wxSizerItem
*item
= node
->GetData();
750 if (item
->GetWindow() == window
)
753 m_children
.Erase( node
);
756 node
= node
->GetNext();
762 bool wxSizer::Detach( int index
)
764 wxCHECK_MSG( index
>= 0 && (size_t)index
< m_children
.GetCount(),
766 wxT("Detach index is out of range") );
768 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
770 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
772 wxSizerItem
*item
= node
->GetData();
774 if ( item
->IsSizer() )
778 m_children
.Erase( node
);
782 bool wxSizer::Replace( wxWindow
*oldwin
, wxWindow
*newwin
, bool recursive
)
784 wxASSERT_MSG( oldwin
, wxT("Replacing NULL window") );
785 wxASSERT_MSG( newwin
, wxT("Replacing with NULL window") );
787 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
790 wxSizerItem
*item
= node
->GetData();
792 if (item
->GetWindow() == oldwin
)
794 item
->AssignWindow(newwin
);
795 newwin
->SetContainingSizer( this );
798 else if (recursive
&& item
->IsSizer())
800 if (item
->GetSizer()->Replace( oldwin
, newwin
, true ))
804 node
= node
->GetNext();
810 bool wxSizer::Replace( wxSizer
*oldsz
, wxSizer
*newsz
, bool recursive
)
812 wxASSERT_MSG( oldsz
, wxT("Replacing NULL sizer") );
813 wxASSERT_MSG( newsz
, wxT("Replacing with NULL sizer") );
815 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
818 wxSizerItem
*item
= node
->GetData();
820 if (item
->GetSizer() == oldsz
)
822 item
->AssignSizer(newsz
);
825 else if (recursive
&& item
->IsSizer())
827 if (item
->GetSizer()->Replace( oldsz
, newsz
, true ))
831 node
= node
->GetNext();
837 bool wxSizer::Replace( size_t old
, wxSizerItem
*newitem
)
839 wxCHECK_MSG( old
< m_children
.GetCount(), false, wxT("Replace index is out of range") );
840 wxASSERT_MSG( newitem
, wxT("Replacing with NULL item") );
842 wxSizerItemList::compatibility_iterator node
= m_children
.Item( old
);
844 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
846 wxSizerItem
*item
= node
->GetData();
847 node
->SetData(newitem
);
853 void wxSizer::Clear( bool delete_windows
)
855 // First clear the ContainingSizer pointers
856 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
859 wxSizerItem
*item
= node
->GetData();
861 if (item
->IsWindow())
862 item
->GetWindow()->SetContainingSizer( NULL
);
863 node
= node
->GetNext();
866 // Destroy the windows if needed
870 // Now empty the list
871 WX_CLEAR_LIST(wxSizerItemList
, m_children
);
874 void wxSizer::DeleteWindows()
876 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
879 wxSizerItem
*item
= node
->GetData();
881 item
->DeleteWindows();
882 node
= node
->GetNext();
886 wxSize
wxSizer::ComputeFittingClientSize(wxWindow
*window
)
888 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
890 // take the min size by default and limit it by max size
891 wxSize size
= GetMinClientSize(window
);
894 wxTopLevelWindow
*tlw
= wxDynamicCast(window
, wxTopLevelWindow
);
897 // hack for small screen devices where TLWs are always full screen
898 if ( tlw
->IsAlwaysMaximized() )
900 return tlw
->GetClientSize();
903 // limit the window to the size of the display it is on
904 int disp
= wxDisplay::GetFromWindow(window
);
905 if ( disp
== wxNOT_FOUND
)
907 // or, if we don't know which one it is, of the main one
911 sizeMax
= wxDisplay(disp
).GetClientArea().GetSize();
913 // space for decorations and toolbars etc.
914 sizeMax
= tlw
->WindowToClientSize(sizeMax
);
918 sizeMax
= GetMaxClientSize(window
);
921 if ( sizeMax
.x
!= wxDefaultCoord
&& size
.x
> sizeMax
.x
)
923 if ( sizeMax
.y
!= wxDefaultCoord
&& size
.y
> sizeMax
.y
)
929 wxSize
wxSizer::ComputeFittingWindowSize(wxWindow
*window
)
931 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
933 return window
->ClientToWindowSize(ComputeFittingClientSize(window
));
936 wxSize
wxSizer::Fit( wxWindow
*window
)
938 wxCHECK_MSG( window
, wxDefaultSize
, "window can't be NULL" );
941 window
->SetClientSize(ComputeFittingClientSize(window
));
943 // return entire size
944 return window
->GetSize();
947 void wxSizer::FitInside( wxWindow
*window
)
950 if (window
->IsTopLevel())
951 size
= VirtualFitSize( window
);
953 size
= GetMinClientSize( window
);
955 window
->SetVirtualSize( size
);
958 void wxSizer::Layout()
960 // (re)calculates minimums needed for each item and other preparations
964 // Applies the layout and repositions/resizes the items
968 void wxSizer::SetSizeHints( wxWindow
*window
)
970 // Preserve the window's max size hints, but set the
971 // lower bound according to the sizer calculations.
973 // This is equivalent to calling Fit(), except that we need to set
974 // the size hints _in between_ the two steps performed by Fit
975 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
976 // otherwise SetClientSize() could have no effect if there already are
977 // size hints in effect that forbid requested client size.
979 const wxSize clientSize
= ComputeFittingClientSize(window
);
981 window
->SetMinClientSize(clientSize
);
982 window
->SetClientSize(clientSize
);
985 #if WXWIN_COMPATIBILITY_2_8
986 void wxSizer::SetVirtualSizeHints( wxWindow
*window
)
990 #endif // WXWIN_COMPATIBILITY_2_8
992 // TODO on mac we need a function that determines how much free space this
993 // min size contains, in order to make sure that we have 20 pixels of free
994 // space around the controls
995 wxSize
wxSizer::GetMaxClientSize( wxWindow
*window
) const
997 return window
->WindowToClientSize(window
->GetMaxSize());
1000 wxSize
wxSizer::GetMinClientSize( wxWindow
*WXUNUSED(window
) )
1002 return GetMinSize(); // Already returns client size.
1005 wxSize
wxSizer::VirtualFitSize( wxWindow
*window
)
1007 wxSize size
= GetMinClientSize( window
);
1008 wxSize sizeMax
= GetMaxClientSize( window
);
1010 // Limit the size if sizeMax != wxDefaultSize
1012 if ( size
.x
> sizeMax
.x
&& sizeMax
.x
!= wxDefaultCoord
)
1014 if ( size
.y
> sizeMax
.y
&& sizeMax
.y
!= wxDefaultCoord
)
1020 wxSize
wxSizer::GetMinSize()
1022 wxSize
ret( CalcMin() );
1023 if (ret
.x
< m_minSize
.x
) ret
.x
= m_minSize
.x
;
1024 if (ret
.y
< m_minSize
.y
) ret
.y
= m_minSize
.y
;
1028 void wxSizer::DoSetMinSize( int width
, int height
)
1030 m_minSize
.x
= width
;
1031 m_minSize
.y
= height
;
1034 bool wxSizer::DoSetItemMinSize( wxWindow
*window
, int width
, int height
)
1036 wxASSERT_MSG( window
, wxT("SetMinSize for NULL window") );
1038 // Is it our immediate child?
1040 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1043 wxSizerItem
*item
= node
->GetData();
1045 if (item
->GetWindow() == window
)
1047 item
->SetMinSize( width
, height
);
1050 node
= node
->GetNext();
1053 // No? Search any subsizers we own then
1055 node
= m_children
.GetFirst();
1058 wxSizerItem
*item
= node
->GetData();
1060 if ( item
->GetSizer() &&
1061 item
->GetSizer()->DoSetItemMinSize( window
, width
, height
) )
1063 // A child sizer found the requested windw, exit.
1066 node
= node
->GetNext();
1072 bool wxSizer::DoSetItemMinSize( wxSizer
*sizer
, int width
, int height
)
1074 wxASSERT_MSG( sizer
, wxT("SetMinSize for NULL sizer") );
1076 // Is it our immediate child?
1078 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1081 wxSizerItem
*item
= node
->GetData();
1083 if (item
->GetSizer() == sizer
)
1085 item
->GetSizer()->DoSetMinSize( width
, height
);
1088 node
= node
->GetNext();
1091 // No? Search any subsizers we own then
1093 node
= m_children
.GetFirst();
1096 wxSizerItem
*item
= node
->GetData();
1098 if ( item
->GetSizer() &&
1099 item
->GetSizer()->DoSetItemMinSize( sizer
, width
, height
) )
1101 // A child found the requested sizer, exit.
1104 node
= node
->GetNext();
1110 bool wxSizer::DoSetItemMinSize( size_t index
, int width
, int height
)
1112 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
1114 wxCHECK_MSG( node
, false, wxT("Failed to find child node") );
1116 wxSizerItem
*item
= node
->GetData();
1118 if (item
->GetSizer())
1120 // Sizers contains the minimal size in them, if not calculated ...
1121 item
->GetSizer()->DoSetMinSize( width
, height
);
1125 // ... but the minimal size of spacers and windows is stored via the item
1126 item
->SetMinSize( width
, height
);
1132 wxSizerItem
* wxSizer::GetItem( wxWindow
*window
, bool recursive
)
1134 wxASSERT_MSG( window
, wxT("GetItem for NULL window") );
1136 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1139 wxSizerItem
*item
= node
->GetData();
1141 if (item
->GetWindow() == window
)
1145 else if (recursive
&& item
->IsSizer())
1147 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( window
, true );
1152 node
= node
->GetNext();
1158 wxSizerItem
* wxSizer::GetItem( wxSizer
*sizer
, bool recursive
)
1160 wxASSERT_MSG( sizer
, wxT("GetItem for NULL sizer") );
1162 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1165 wxSizerItem
*item
= node
->GetData();
1167 if (item
->GetSizer() == sizer
)
1171 else if (recursive
&& item
->IsSizer())
1173 wxSizerItem
*subitem
= item
->GetSizer()->GetItem( sizer
, true );
1178 node
= node
->GetNext();
1184 wxSizerItem
* wxSizer::GetItem( size_t index
)
1186 wxCHECK_MSG( index
< m_children
.GetCount(),
1188 wxT("GetItem index is out of range") );
1190 return m_children
.Item( index
)->GetData();
1193 wxSizerItem
* wxSizer::GetItemById( int id
, bool recursive
)
1195 // This gets a sizer item by the id of the sizer item
1196 // and NOT the id of a window if the item is a window.
1198 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1201 wxSizerItem
*item
= node
->GetData();
1203 if (item
->GetId() == id
)
1207 else if (recursive
&& item
->IsSizer())
1209 wxSizerItem
*subitem
= item
->GetSizer()->GetItemById( id
, true );
1214 node
= node
->GetNext();
1220 bool wxSizer::Show( wxWindow
*window
, bool show
, bool recursive
)
1222 wxSizerItem
*item
= GetItem( window
, recursive
);
1233 bool wxSizer::Show( wxSizer
*sizer
, bool show
, bool recursive
)
1235 wxSizerItem
*item
= GetItem( sizer
, recursive
);
1246 bool wxSizer::Show( size_t index
, bool show
)
1248 wxSizerItem
*item
= GetItem( index
);
1259 void wxSizer::ShowItems( bool show
)
1261 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1264 node
->GetData()->Show( show
);
1265 node
= node
->GetNext();
1269 bool wxSizer::IsShown( wxWindow
*window
) const
1271 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1274 wxSizerItem
*item
= node
->GetData();
1276 if (item
->GetWindow() == window
)
1278 return item
->IsShown();
1280 node
= node
->GetNext();
1283 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1288 bool wxSizer::IsShown( wxSizer
*sizer
) const
1290 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1293 wxSizerItem
*item
= node
->GetData();
1295 if (item
->GetSizer() == sizer
)
1297 return item
->IsShown();
1299 node
= node
->GetNext();
1302 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1307 bool wxSizer::IsShown( size_t index
) const
1309 wxCHECK_MSG( index
< m_children
.GetCount(),
1311 wxT("IsShown index is out of range") );
1313 return m_children
.Item( index
)->GetData()->IsShown();
1317 //---------------------------------------------------------------------------
1319 //---------------------------------------------------------------------------
1321 wxGridSizer::wxGridSizer( int cols
, int vgap
, int hgap
)
1322 : m_rows( cols
== 0 ? 1 : 0 ),
1329 wxGridSizer::wxGridSizer( int cols
, const wxSize
& gap
)
1330 : m_rows( cols
== 0 ? 1 : 0 ),
1332 m_vgap( gap
.GetHeight() ),
1333 m_hgap( gap
.GetWidth() )
1337 wxGridSizer::wxGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1338 : m_rows( rows
|| cols
? rows
: 1 ),
1345 wxGridSizer::wxGridSizer( int rows
, int cols
, const wxSize
& gap
)
1346 : m_rows( rows
|| cols
? rows
: 1 ),
1348 m_vgap( gap
.GetHeight() ),
1349 m_hgap( gap
.GetWidth() )
1353 wxSizerItem
*wxGridSizer::DoInsert(size_t index
, wxSizerItem
*item
)
1355 // if only the number of columns or the number of rows is specified for a
1356 // sizer, arbitrarily many items can be added to it but if both of them are
1357 // fixed, then the sizer can't have more than that many items -- check for
1358 // this here to ensure that we detect errors as soon as possible
1359 if ( m_cols
&& m_rows
)
1361 const int nitems
= m_children
.GetCount();
1362 if ( nitems
== m_cols
*m_rows
)
1366 "too many items (%d > %d*%d) in grid sizer (maybe you "
1367 "should omit the number of either rows or columns?)",
1368 nitems
+ 1, m_cols
, m_rows
)
1371 // additionally, continuing to use the specified number of columns
1372 // and rows is not a good idea as callers of CalcRowsCols() expect
1373 // that all sizer items can fit into m_cols-/m_rows-sized arrays
1374 // which is not the case if there are too many items and results in
1375 // crashes, so let it compute the number of rows automatically by
1376 // forgetting the (wrong) number of rows specified (this also has a
1377 // nice side effect of giving only one assert even if there are
1378 // many more items than allowed in this sizer)
1383 return wxSizer::DoInsert(index
, item
);
1386 int wxGridSizer::CalcRowsCols(int& nrows
, int& ncols
) const
1388 const int nitems
= m_children
.GetCount();
1390 ncols
= GetEffectiveColsCount();
1391 nrows
= GetEffectiveRowsCount();
1393 // Since Insert() checks for overpopulation, the following
1394 // should only assert if the grid was shrunk via SetRows() / SetCols()
1395 wxASSERT_MSG( nitems
<= ncols
*nrows
, "logic error in wxGridSizer" );
1400 void wxGridSizer::RecalcSizes()
1402 int nitems
, nrows
, ncols
;
1403 if ( (nitems
= CalcRowsCols(nrows
, ncols
)) == 0 )
1406 wxSize
sz( GetSize() );
1407 wxPoint
pt( GetPosition() );
1409 int w
= (sz
.x
- (ncols
- 1) * m_hgap
) / ncols
;
1410 int h
= (sz
.y
- (nrows
- 1) * m_vgap
) / nrows
;
1413 for (int c
= 0; c
< ncols
; c
++)
1416 for (int r
= 0; r
< nrows
; r
++)
1418 int i
= r
* ncols
+ c
;
1421 wxSizerItemList::compatibility_iterator node
= m_children
.Item( i
);
1423 wxASSERT_MSG( node
, wxT("Failed to find SizerItemList node") );
1425 SetItemBounds( node
->GetData(), x
, y
, w
, h
);
1433 wxSize
wxGridSizer::CalcMin()
1436 if ( CalcRowsCols(nrows
, ncols
) == 0 )
1439 // Find the max width and height for any component
1443 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
1446 wxSizerItem
*item
= node
->GetData();
1447 wxSize
sz( item
->CalcMin() );
1449 w
= wxMax( w
, sz
.x
);
1450 h
= wxMax( h
, sz
.y
);
1452 node
= node
->GetNext();
1455 // In case we have a nested sizer with a two step algo , give it
1456 // a chance to adjust to that (we give it width component)
1457 node
= m_children
.GetFirst();
1458 bool didChangeMinSize
= false;
1461 wxSizerItem
*item
= node
->GetData();
1462 didChangeMinSize
|= item
->InformFirstDirection( wxHORIZONTAL
, w
, -1 );
1464 node
= node
->GetNext();
1467 // And redo iteration in case min size changed
1468 if( didChangeMinSize
)
1470 node
= m_children
.GetFirst();
1474 wxSizerItem
*item
= node
->GetData();
1475 wxSize
sz( item
->GetMinSizeWithBorder() );
1477 w
= wxMax( w
, sz
.x
);
1478 h
= wxMax( h
, sz
.y
);
1480 node
= node
->GetNext();
1484 return wxSize( ncols
* w
+ (ncols
-1) * m_hgap
,
1485 nrows
* h
+ (nrows
-1) * m_vgap
);
1488 void wxGridSizer::SetItemBounds( wxSizerItem
*item
, int x
, int y
, int w
, int h
)
1491 wxSize
sz( item
->GetMinSizeWithBorder() );
1492 int flag
= item
->GetFlag();
1494 if ((flag
& wxEXPAND
) || (flag
& wxSHAPED
))
1500 if (flag
& wxALIGN_CENTER_HORIZONTAL
)
1502 pt
.x
= x
+ (w
- sz
.x
) / 2;
1504 else if (flag
& wxALIGN_RIGHT
)
1506 pt
.x
= x
+ (w
- sz
.x
);
1509 if (flag
& wxALIGN_CENTER_VERTICAL
)
1511 pt
.y
= y
+ (h
- sz
.y
) / 2;
1513 else if (flag
& wxALIGN_BOTTOM
)
1515 pt
.y
= y
+ (h
- sz
.y
);
1519 item
->SetDimension(pt
, sz
);
1522 //---------------------------------------------------------------------------
1524 //---------------------------------------------------------------------------
1526 wxFlexGridSizer::wxFlexGridSizer( int cols
, int vgap
, int hgap
)
1527 : wxGridSizer( cols
, vgap
, hgap
),
1528 m_flexDirection(wxBOTH
),
1529 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1533 wxFlexGridSizer::wxFlexGridSizer( int cols
, const wxSize
& gap
)
1534 : wxGridSizer( cols
, gap
),
1535 m_flexDirection(wxBOTH
),
1536 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1540 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, int vgap
, int hgap
)
1541 : wxGridSizer( rows
, cols
, vgap
, hgap
),
1542 m_flexDirection(wxBOTH
),
1543 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1547 wxFlexGridSizer::wxFlexGridSizer( int rows
, int cols
, const wxSize
& gap
)
1548 : wxGridSizer( rows
, cols
, gap
),
1549 m_flexDirection(wxBOTH
),
1550 m_growMode(wxFLEX_GROWMODE_SPECIFIED
)
1554 wxFlexGridSizer::~wxFlexGridSizer()
1558 void wxFlexGridSizer::RecalcSizes()
1561 if ( !CalcRowsCols(nrows
, ncols
) )
1564 const wxPoint
pt(GetPosition());
1565 const wxSize
sz(GetSize());
1567 AdjustForGrowables(sz
);
1569 wxSizerItemList::const_iterator i
= m_children
.begin();
1570 const wxSizerItemList::const_iterator end
= m_children
.end();
1573 for ( int r
= 0; r
< nrows
; r
++ )
1575 if ( m_rowHeights
[r
] == -1 )
1577 // this row is entirely hidden, skip it
1578 for ( int c
= 0; c
< ncols
; c
++ )
1589 const int hrow
= m_rowHeights
[r
];
1590 int h
= sz
.y
- y
; // max remaining height, don't overflow it
1595 for ( int c
= 0; c
< ncols
&& i
!= end
; c
++, ++i
)
1597 const int wcol
= m_colWidths
[c
];
1602 int w
= sz
.x
- x
; // max possible value, ensure we don't overflow
1606 SetItemBounds(*i
, pt
.x
+ x
, pt
.y
+ y
, w
, h
);
1618 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1619 static int SumArraySizes(const wxArrayInt
& sizes
, int gap
)
1621 // Sum total minimum size, including gaps between rows/columns.
1622 // -1 is used as a magic number meaning empty row/column.
1625 const size_t count
= sizes
.size();
1626 for ( size_t n
= 0; n
< count
; n
++ )
1628 if ( sizes
[n
] != -1 )
1631 total
+= gap
; // separate from the previous column
1640 void wxFlexGridSizer::FindWidthsAndHeights(int nrows
, int ncols
)
1642 // We have to recalculate the sizes in case the item minimum size has
1643 // changed since the previous layout, or the item has been hidden using
1644 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1645 // dimension of the row/column will be -1, indicating that the column
1646 // itself is hidden.
1647 m_rowHeights
.assign(nrows
, -1);
1648 m_colWidths
.assign(ncols
, -1);
1650 // n is the index of the item in left-to-right top-to-bottom order
1652 for ( wxSizerItemList::iterator i
= m_children
.begin();
1653 i
!= m_children
.end();
1656 wxSizerItem
* const item
= *i
;
1657 if ( item
->IsShown() )
1659 // NOTE: Not doing the calculation here, this is just
1660 // for finding max values.
1661 const wxSize
sz(item
->GetMinSizeWithBorder());
1663 const int row
= n
/ ncols
;
1664 const int col
= n
% ncols
;
1666 if ( sz
.y
> m_rowHeights
[row
] )
1667 m_rowHeights
[row
] = sz
.y
;
1668 if ( sz
.x
> m_colWidths
[col
] )
1669 m_colWidths
[col
] = sz
.x
;
1673 AdjustForFlexDirection();
1675 m_calculatedMinSize
= wxSize(SumArraySizes(m_colWidths
, m_hgap
),
1676 SumArraySizes(m_rowHeights
, m_vgap
));
1679 wxSize
wxFlexGridSizer::CalcMin()
1684 // Number of rows/columns can change as items are added or removed.
1685 if ( !CalcRowsCols(nrows
, ncols
) )
1689 // We have to recalculate the sizes in case the item minimum size has
1690 // changed since the previous layout, or the item has been hidden using
1691 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1692 // dimension of the row/column will be -1, indicating that the column
1693 // itself is hidden.
1694 m_rowHeights
.assign(nrows
, -1);
1695 m_colWidths
.assign(ncols
, -1);
1697 for ( wxSizerItemList::iterator i
= m_children
.begin();
1698 i
!= m_children
.end();
1701 wxSizerItem
* const item
= *i
;
1702 if ( item
->IsShown() )
1708 // The stage of looking for max values in each row/column has been
1709 // made a separate function, since it's reused in AdjustForGrowables.
1710 FindWidthsAndHeights(nrows
,ncols
);
1712 return m_calculatedMinSize
;
1715 void wxFlexGridSizer::AdjustForFlexDirection()
1717 // the logic in CalcMin works when we resize flexibly in both directions
1718 // but maybe this is not the case
1719 if ( m_flexDirection
!= wxBOTH
)
1721 // select the array corresponding to the direction in which we do *not*
1723 wxArrayInt
& array
= m_flexDirection
== wxVERTICAL
? m_colWidths
1726 const size_t count
= array
.GetCount();
1728 // find the largest value in this array
1732 for ( n
= 0; n
< count
; ++n
)
1734 if ( array
[n
] > largest
)
1738 // and now fill it with the largest value
1739 for ( n
= 0; n
< count
; ++n
)
1741 // don't touch hidden rows
1742 if ( array
[n
] != -1 )
1748 // helper of AdjustForGrowables() which is called for rows/columns separately
1751 // delta: the extra space, we do nothing unless it's positive
1752 // growable: indices or growable rows/cols in sizes array
1753 // sizes: the height/widths of rows/cols to adjust
1754 // proportions: proportions of the growable rows/cols or NULL if they all
1755 // should be assumed to have proportion of 1
1757 DoAdjustForGrowables(int delta
,
1758 const wxArrayInt
& growable
,
1760 const wxArrayInt
*proportions
)
1765 // total sum of proportions of all non-hidden rows
1766 int sum_proportions
= 0;
1768 // number of currently shown growable rows
1771 const int max_idx
= sizes
.size();
1773 const size_t count
= growable
.size();
1775 for ( idx
= 0; idx
< count
; idx
++ )
1777 // Since the number of rows/columns can change as items are
1778 // inserted/deleted, we need to verify at runtime that the
1779 // requested growable rows/columns are still valid.
1780 if ( growable
[idx
] >= max_idx
)
1783 // If all items in a row/column are hidden, that row/column will
1784 // have a dimension of -1. This causes the row/column to be
1785 // hidden completely.
1786 if ( sizes
[growable
[idx
]] == -1 )
1790 sum_proportions
+= (*proportions
)[idx
];
1798 // the remaining extra free space, adjusted during each iteration
1799 for ( idx
= 0; idx
< count
; idx
++ )
1801 if ( growable
[idx
] >= max_idx
)
1804 if ( sizes
[ growable
[idx
] ] == -1 )
1808 if ( sum_proportions
== 0 )
1810 // no growable rows -- divide extra space evenly among all
1811 cur_delta
= delta
/num
;
1814 else // allocate extra space proportionally
1816 const int cur_prop
= (*proportions
)[idx
];
1817 cur_delta
= (delta
*cur_prop
)/sum_proportions
;
1818 sum_proportions
-= cur_prop
;
1821 sizes
[growable
[idx
]] += cur_delta
;
1826 void wxFlexGridSizer::AdjustForGrowables(const wxSize
& sz
)
1829 // by the time this function is called, the sizer should be already fully
1830 // initialized and hence the number of its columns and rows is known and we
1831 // can check that all indices in m_growableCols/Rows are valid (see also
1832 // comments in AddGrowableCol/Row())
1833 if ( !m_rows
|| !m_cols
)
1837 int nrows
= CalcRows();
1839 for ( size_t n
= 0; n
< m_growableRows
.size(); n
++ )
1841 wxASSERT_MSG( m_growableRows
[n
] < nrows
,
1842 "invalid growable row index" );
1848 int ncols
= CalcCols();
1850 for ( size_t n
= 0; n
< m_growableCols
.size(); n
++ )
1852 wxASSERT_MSG( m_growableCols
[n
] < ncols
,
1853 "invalid growable column index" );
1857 #endif // wxDEBUG_LEVEL
1860 if ( (m_flexDirection
& wxHORIZONTAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1862 DoAdjustForGrowables
1864 sz
.x
- m_calculatedMinSize
.x
,
1867 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1871 // This gives nested objects that benefit from knowing one size
1872 // component in advance the chance to use that.
1873 bool didAdjustMinSize
= false;
1875 // Iterate over all items and inform about column width
1876 const int ncols
= GetEffectiveColsCount();
1878 for ( wxSizerItemList::iterator i
= m_children
.begin();
1879 i
!= m_children
.end();
1882 didAdjustMinSize
|= (*i
)->InformFirstDirection(wxHORIZONTAL
, m_colWidths
[col
], sz
.y
- m_calculatedMinSize
.y
);
1883 if ( ++col
== ncols
)
1887 // Only redo if info was actually used
1888 if( didAdjustMinSize
)
1890 DoAdjustForGrowables
1892 sz
.x
- m_calculatedMinSize
.x
,
1895 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableColsProportions
1901 if ( (m_flexDirection
& wxVERTICAL
) || (m_growMode
!= wxFLEX_GROWMODE_NONE
) )
1903 // pass NULL instead of proportions if the grow mode is ALL as we
1904 // should treat all rows as having proportion of 1 then
1905 DoAdjustForGrowables
1907 sz
.y
- m_calculatedMinSize
.y
,
1910 m_growMode
== wxFLEX_GROWMODE_SPECIFIED
? &m_growableRowsProportions
1916 bool wxFlexGridSizer::IsRowGrowable( size_t idx
)
1918 return m_growableRows
.Index( idx
) != wxNOT_FOUND
;
1921 bool wxFlexGridSizer::IsColGrowable( size_t idx
)
1923 return m_growableCols
.Index( idx
) != wxNOT_FOUND
;
1926 void wxFlexGridSizer::AddGrowableRow( size_t idx
, int proportion
)
1928 wxASSERT_MSG( !IsRowGrowable( idx
),
1929 "AddGrowableRow() called for growable row" );
1931 // notice that we intentionally don't check the index validity here in (the
1932 // common) case when the number of rows was not specified in the ctor -- in
1933 // this case it will be computed only later, when all items are added to
1934 // the sizer, and the check will be done in AdjustForGrowables()
1935 wxCHECK_RET( !m_rows
|| idx
< (size_t)m_rows
, "invalid row index" );
1937 m_growableRows
.Add( idx
);
1938 m_growableRowsProportions
.Add( proportion
);
1941 void wxFlexGridSizer::AddGrowableCol( size_t idx
, int proportion
)
1943 wxASSERT_MSG( !IsColGrowable( idx
),
1944 "AddGrowableCol() called for growable column" );
1946 // see comment in AddGrowableRow(): although it's less common to omit the
1947 // specification of the number of columns, it still can also happen
1948 wxCHECK_RET( !m_cols
|| idx
< (size_t)m_cols
, "invalid column index" );
1950 m_growableCols
.Add( idx
);
1951 m_growableColsProportions
.Add( proportion
);
1954 // helper function for RemoveGrowableCol/Row()
1956 DoRemoveFromArrays(size_t idx
, wxArrayInt
& items
, wxArrayInt
& proportions
)
1958 const size_t count
= items
.size();
1959 for ( size_t n
= 0; n
< count
; n
++ )
1961 if ( (size_t)items
[n
] == idx
)
1964 proportions
.RemoveAt(n
);
1969 wxFAIL_MSG( wxT("column/row is already not growable") );
1972 void wxFlexGridSizer::RemoveGrowableCol( size_t idx
)
1974 DoRemoveFromArrays(idx
, m_growableCols
, m_growableColsProportions
);
1977 void wxFlexGridSizer::RemoveGrowableRow( size_t idx
)
1979 DoRemoveFromArrays(idx
, m_growableRows
, m_growableRowsProportions
);
1982 //---------------------------------------------------------------------------
1984 //---------------------------------------------------------------------------
1986 wxSizerItem
*wxBoxSizer::AddSpacer(int size
)
1988 return IsVertical() ? Add(0, size
) : Add(size
, 0);
1995 Helper of RecalcSizes(): checks if there is enough remaining space for the
1996 min size of the given item and returns its min size or the entire remaining
1997 space depending on which one is greater.
1999 This function updates the remaining space parameter to account for the size
2000 effectively allocated to the item.
2003 GetMinOrRemainingSize(int orient
, const wxSizerItem
*item
, int *remainingSpace_
)
2005 int& remainingSpace
= *remainingSpace_
;
2008 if ( remainingSpace
> 0 )
2010 const wxSize sizeMin
= item
->GetMinSizeWithBorder();
2011 size
= orient
== wxHORIZONTAL
? sizeMin
.x
: sizeMin
.y
;
2013 if ( size
>= remainingSpace
)
2015 // truncate the item to fit in the remaining space, this is better
2016 // than showing it only partially in general, even if both choices
2017 // are bad -- but there is nothing else we can do
2018 size
= remainingSpace
;
2021 remainingSpace
-= size
;
2023 else // no remaining space
2025 // no space at all left, no need to even query the item for its min
2026 // size as we can't give it to it anyhow
2033 } // anonymous namespace
2035 void wxBoxSizer::RecalcSizes()
2037 if ( m_children
.empty() )
2040 const wxCoord totalMinorSize
= GetSizeInMinorDir(m_size
);
2041 const wxCoord totalMajorSize
= GetSizeInMajorDir(m_size
);
2043 // the amount of free space which we should redistribute among the
2044 // stretchable items (i.e. those with non zero proportion)
2045 int delta
= totalMajorSize
- GetSizeInMajorDir(m_minSize
);
2047 // declare loop variables used below:
2048 wxSizerItemList::const_iterator i
; // iterator in m_children list
2049 unsigned n
= 0; // item index in majorSizes array
2052 // First, inform item about the available size in minor direction as this
2053 // can change their size in the major direction. Also compute the number of
2054 // visible items and sum of their min sizes in major direction.
2056 int minMajorSize
= 0;
2057 for ( i
= m_children
.begin(); i
!= m_children
.end(); ++i
)
2059 wxSizerItem
* const item
= *i
;
2061 if ( !item
->IsShown() )
2064 wxSize szMinPrev
= item
->GetMinSizeWithBorder();
2065 item
->InformFirstDirection(m_orient
^wxBOTH
,totalMinorSize
,delta
);
2066 wxSize szMin
= item
->GetMinSizeWithBorder();
2067 int deltaChange
= GetSizeInMajorDir(szMin
-szMinPrev
);
2070 // Since we passed available space along to the item, it should not
2071 // take too much, so delta should not become negative.
2072 delta
-= deltaChange
;
2074 minMajorSize
+= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2077 // update our min size and delta which may have changed
2078 SizeInMajorDir(m_minSize
) = minMajorSize
;
2079 delta
= totalMajorSize
- minMajorSize
;
2082 // space and sum of proportions for the remaining items, both may change
2084 wxCoord remaining
= totalMajorSize
;
2085 int totalProportion
= m_totalProportion
;
2087 // size of the (visible) items in major direction, -1 means "not fixed yet"
2088 wxVector
<int> majorSizes(GetItemCount(), wxDefaultCoord
);
2091 // Check for the degenerated case when we don't have enough space for even
2092 // the min sizes of all the items: in this case we really can't do much
2093 // more than to to allocate the min size to as many of fixed size items as
2094 // possible (on the assumption that variable size items such as text zones
2095 // or list boxes may use scrollbars to show their content even if their
2096 // size is less than min size but that fixed size items such as buttons
2097 // will suffer even more if we don't give them their min size)
2098 if ( totalMajorSize
< minMajorSize
)
2100 // Second degenerated case pass: allocate min size to all fixed size
2102 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2104 wxSizerItem
* const item
= *i
;
2106 if ( !item
->IsShown() )
2109 // deal with fixed size items only during this pass
2110 if ( item
->GetProportion() )
2113 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2117 // Third degenerated case pass: allocate min size to all the remaining,
2118 // i.e. non-fixed size, items.
2119 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2121 wxSizerItem
* const item
= *i
;
2123 if ( !item
->IsShown() )
2126 // we've already dealt with fixed size items above
2127 if ( !item
->GetProportion() )
2130 majorSizes
[n
] = GetMinOrRemainingSize(m_orient
, item
, &remaining
);
2133 else // we do have enough space to give at least min sizes to all items
2135 // Second and maybe more passes in the non-degenerated case: deal with
2136 // fixed size items and items whose min size is greater than what we
2137 // would allocate to them taking their proportion into account. For
2138 // both of them, we will just use their min size, but for the latter we
2139 // also need to reexamine all the items as the items which fitted
2140 // before we adjusted their size upwards might not fit any more. This
2141 // does make for a quadratic algorithm but it's not obvious how to
2142 // avoid it and hopefully it's not a huge problem in practice as the
2143 // sizers don't have many items usually (and, of course, the algorithm
2144 // still reduces into a linear one if there is enough space for all the
2146 bool nonFixedSpaceChanged
= false;
2147 for ( i
= m_children
.begin(), n
= 0; ; ++i
, ++n
)
2149 if ( nonFixedSpaceChanged
)
2151 i
= m_children
.begin();
2153 nonFixedSpaceChanged
= false;
2156 // check for the end of the loop only after the check above as
2157 // otherwise we wouldn't do another pass if the last child resulted
2158 // in non fixed space reduction
2159 if ( i
== m_children
.end() )
2162 wxSizerItem
* const item
= *i
;
2164 if ( !item
->IsShown() )
2167 // don't check the item which we had already dealt with during a
2168 // previous pass (this is more than an optimization, the code
2169 // wouldn't work correctly if we kept adjusting for the same item
2170 // over and over again)
2171 if ( majorSizes
[n
] != wxDefaultCoord
)
2174 wxCoord minMajor
= GetSizeInMajorDir(item
->GetMinSizeWithBorder());
2176 // it doesn't make sense for min size to be negative but right now
2177 // it's possible to create e.g. a spacer with (-1, 10) as size and
2178 // people do it in their code apparently (see #11842) so ensure
2179 // that we don't use this -1 as real min size as it conflicts with
2180 // the meaning we use for it here and negative min sizes just don't
2181 // make sense anyhow (which is why it might be a better idea to
2182 // deal with them at wxSizerItem level in the future but for now
2183 // this is the minimal fix for the bug)
2187 const int propItem
= item
->GetProportion();
2190 // is the desired size of this item big enough?
2191 if ( (remaining
*propItem
)/totalProportion
>= minMajor
)
2193 // yes, it is, we'll determine the real size of this
2194 // item later, for now just leave it as wxDefaultCoord
2198 // the proportion of this item won't count, it has
2199 // effectively become fixed
2200 totalProportion
-= propItem
;
2203 // we can already allocate space for this item
2204 majorSizes
[n
] = minMajor
;
2206 // change the amount of the space remaining to the other items,
2207 // as this can result in not being able to satisfy their
2208 // proportions any more we will need to redo another loop
2210 remaining
-= minMajor
;
2212 nonFixedSpaceChanged
= true;
2216 // Last by one pass: distribute the remaining space among the non-fixed
2217 // items whose size weren't fixed yet according to their proportions.
2218 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2220 wxSizerItem
* const item
= *i
;
2222 if ( !item
->IsShown() )
2225 if ( majorSizes
[n
] == wxDefaultCoord
)
2227 const int propItem
= item
->GetProportion();
2228 majorSizes
[n
] = (remaining
*propItem
)/totalProportion
;
2230 remaining
-= majorSizes
[n
];
2231 totalProportion
-= propItem
;
2237 // the position at which we put the next child
2238 wxPoint
pt(m_position
);
2241 // Final pass: finally do position the items correctly using their sizes as
2242 // determined above.
2243 for ( i
= m_children
.begin(), n
= 0; i
!= m_children
.end(); ++i
, ++n
)
2245 wxSizerItem
* const item
= *i
;
2247 if ( !item
->IsShown() )
2250 const int majorSize
= majorSizes
[n
];
2252 const wxSize
sizeThis(item
->GetMinSizeWithBorder());
2254 // apply the alignment in the minor direction
2255 wxPoint
posChild(pt
);
2257 wxCoord minorSize
= GetSizeInMinorDir(sizeThis
);
2258 const int flag
= item
->GetFlag();
2259 if ( (flag
& (wxEXPAND
| wxSHAPED
)) || (minorSize
> totalMinorSize
) )
2261 // occupy all the available space if wxEXPAND was given and also if
2262 // the item is too big to fit -- in this case we truncate it below
2263 // its minimal size which is bad but better than not showing parts
2264 // of the window at all
2265 minorSize
= totalMinorSize
;
2267 else if ( flag
& (IsVertical() ? wxALIGN_RIGHT
: wxALIGN_BOTTOM
) )
2269 PosInMinorDir(posChild
) += totalMinorSize
- minorSize
;
2271 // NB: wxCENTRE is used here only for backwards compatibility,
2272 // wxALIGN_CENTRE should be used in new code
2273 else if ( flag
& (wxCENTER
| (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2274 : wxALIGN_CENTRE_VERTICAL
)) )
2276 PosInMinorDir(posChild
) += (totalMinorSize
- minorSize
) / 2;
2280 // apply RTL adjustment for horizontal sizers:
2281 if ( !IsVertical() && m_containingWindow
)
2283 posChild
.x
= m_containingWindow
->AdjustForLayoutDirection
2291 // finally set size of this child and advance to the next one
2292 item
->SetDimension(posChild
, SizeFromMajorMinor(majorSize
, minorSize
));
2294 PosInMajorDir(pt
) += majorSize
;
2298 wxSize
wxBoxSizer::CalcMin()
2300 m_totalProportion
= 0;
2301 m_minSize
= wxSize(0, 0);
2303 // The minimal size for the sizer should be big enough to allocate its
2304 // element at least its minimal size but also, and this is the non trivial
2305 // part, to respect the children proportion. To satisfy the latter
2306 // condition we must find the greatest min-size-to-proportion ratio for all
2307 // elements with non-zero proportion.
2308 float maxMinSizeToProp
= 0.;
2309 for ( wxSizerItemList::const_iterator i
= m_children
.begin();
2310 i
!= m_children
.end();
2313 wxSizerItem
* const item
= *i
;
2315 if ( !item
->IsShown() )
2318 const wxSize sizeMinThis
= item
->CalcMin();
2319 if ( const int propThis
= item
->GetProportion() )
2321 float minSizeToProp
= GetSizeInMajorDir(sizeMinThis
);
2322 minSizeToProp
/= propThis
;
2324 if ( minSizeToProp
> maxMinSizeToProp
)
2325 maxMinSizeToProp
= minSizeToProp
;
2327 m_totalProportion
+= item
->GetProportion();
2329 else // fixed size item
2331 // Just account for its size directly
2332 SizeInMajorDir(m_minSize
) += GetSizeInMajorDir(sizeMinThis
);
2335 // In the transversal direction we just need to find the maximum.
2336 if ( GetSizeInMinorDir(sizeMinThis
) > GetSizeInMinorDir(m_minSize
) )
2337 SizeInMinorDir(m_minSize
) = GetSizeInMinorDir(sizeMinThis
);
2340 // Using the max ratio ensures that the min size is big enough for all
2341 // items to have their min size and satisfy the proportions among them.
2342 SizeInMajorDir(m_minSize
) += maxMinSizeToProp
*m_totalProportion
;
2347 //---------------------------------------------------------------------------
2349 //---------------------------------------------------------------------------
2353 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox
*box
, int orient
)
2354 : wxBoxSizer( orient
),
2357 wxASSERT_MSG( box
, wxT("wxStaticBoxSizer needs a static box") );
2359 // do this so that our Detach() is called if the static box is destroyed
2361 m_staticBox
->SetContainingSizer(this);
2364 wxStaticBoxSizer::wxStaticBoxSizer(int orient
, wxWindow
*win
, const wxString
& s
)
2365 : wxBoxSizer(orient
),
2366 m_staticBox(new wxStaticBox(win
, wxID_ANY
, s
))
2369 m_staticBox
->SetContainingSizer(this);
2372 wxStaticBoxSizer::~wxStaticBoxSizer()
2377 void wxStaticBoxSizer::RecalcSizes()
2379 int top_border
, other_border
;
2380 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2382 m_staticBox
->SetSize( m_position
.x
, m_position
.y
, m_size
.x
, m_size
.y
);
2384 wxSize
old_size( m_size
);
2385 m_size
.x
-= 2*other_border
;
2386 m_size
.y
-= top_border
+ other_border
;
2388 wxPoint
old_pos( m_position
);
2389 if (m_staticBox
->GetChildren().GetCount() > 0)
2391 #if defined( __WXGTK20__ )
2392 // if the wxStaticBox has created a wxPizza to contain its children
2393 // (see wxStaticBox::AddChild) then we need to place the items it contains
2394 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
2395 // to the top-left corner of the staticbox:
2396 m_position
.x
= m_position
.y
= 0;
2398 // if the wxStaticBox has childrens, then these windows must be placed
2399 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2400 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2401 // to keep in count the static borders here!):
2402 m_position
.x
= other_border
;
2403 m_position
.y
= top_border
;
2408 // the windows contained in the staticbox have been created as siblings of the
2409 // staticbox (this is the "old" way of staticbox contents creation); in this
2410 // case we need to position them with coordinates relative to our common parent
2411 m_position
.x
+= other_border
;
2412 m_position
.y
+= top_border
;
2415 wxBoxSizer::RecalcSizes();
2417 m_position
= old_pos
;
2421 wxSize
wxStaticBoxSizer::CalcMin()
2423 int top_border
, other_border
;
2424 m_staticBox
->GetBordersForSizer(&top_border
, &other_border
);
2426 wxSize
ret( wxBoxSizer::CalcMin() );
2427 ret
.x
+= 2*other_border
;
2429 // ensure that we're wide enough to show the static box label (there is no
2430 // need to check for the static box best size in vertical direction though)
2431 const int boxWidth
= m_staticBox
->GetBestSize().x
;
2432 if ( ret
.x
< boxWidth
)
2435 ret
.y
+= other_border
+ top_border
;
2440 void wxStaticBoxSizer::ShowItems( bool show
)
2442 m_staticBox
->Show( show
);
2443 wxBoxSizer::ShowItems( show
);
2446 bool wxStaticBoxSizer::Detach( wxWindow
*window
)
2448 // avoid deleting m_staticBox in our dtor if it's being detached from the
2449 // sizer (which can happen because it's being already destroyed for
2451 if ( window
== m_staticBox
)
2457 return wxSizer::Detach( window
);
2460 #endif // wxUSE_STATBOX
2462 //---------------------------------------------------------------------------
2463 // wxStdDialogButtonSizer
2464 //---------------------------------------------------------------------------
2468 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2469 : wxBoxSizer(wxHORIZONTAL
)
2471 // Vertical buttons with lots of space on either side
2472 // looks rubbish on WinCE, so let's not do this for now.
2473 // If we are going to use vertical buttons, we should
2474 // put the sizer to the right of other controls in the dialog,
2475 // and that's beyond the scope of this sizer.
2477 bool is_pda
= (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA
);
2478 // If we have a PDA screen, put yes/no button over
2479 // all other buttons, otherwise on the left side.
2481 m_orient
= wxVERTICAL
;
2484 m_buttonAffirmative
= NULL
;
2485 m_buttonApply
= NULL
;
2486 m_buttonNegative
= NULL
;
2487 m_buttonCancel
= NULL
;
2488 m_buttonHelp
= NULL
;
2491 void wxStdDialogButtonSizer::AddButton(wxButton
*mybutton
)
2493 switch (mybutton
->GetId())
2498 m_buttonAffirmative
= mybutton
;
2501 m_buttonApply
= mybutton
;
2504 m_buttonNegative
= mybutton
;
2508 m_buttonCancel
= mybutton
;
2511 case wxID_CONTEXT_HELP
:
2512 m_buttonHelp
= mybutton
;
2519 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton
*button
)
2521 m_buttonAffirmative
= button
;
2524 void wxStdDialogButtonSizer::SetNegativeButton( wxButton
*button
)
2526 m_buttonNegative
= button
;
2529 void wxStdDialogButtonSizer::SetCancelButton( wxButton
*button
)
2531 m_buttonCancel
= button
;
2534 void wxStdDialogButtonSizer::Realize()
2537 Add(0, 0, 0, wxLEFT
, 6);
2539 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2541 if (m_buttonNegative
){
2542 // HIG POLICE BULLETIN - destructive buttons need extra padding
2543 // 24 pixels on either side
2544 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 12);
2547 // extra whitespace between help/negative and cancel/ok buttons
2548 Add(0, 0, 1, wxEXPAND
, 0);
2550 if (m_buttonCancel
){
2551 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2552 // Cancel or help should be default
2553 // m_buttonCancel->SetDefaultButton();
2556 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2557 // figure the best place is between Cancel and OK
2559 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 6);
2561 if (m_buttonAffirmative
){
2562 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2564 if (m_buttonAffirmative
->GetId() == wxID_SAVE
){
2565 // these buttons have set labels under Mac so we should use them
2566 m_buttonAffirmative
->SetLabel(_("Save"));
2567 if (m_buttonNegative
)
2568 m_buttonNegative
->SetLabel(_("Don't Save"));
2572 // Extra space around and at the right
2574 #elif defined(__WXGTK20__)
2575 Add(0, 0, 0, wxLEFT
, 9);
2577 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 3);
2579 // extra whitespace between help and cancel/ok buttons
2580 Add(0, 0, 1, wxEXPAND
, 0);
2582 if (m_buttonNegative
){
2583 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 3);
2586 // according to HIG, in explicit apply windows the order is:
2587 // [ Help Apply Cancel OK ]
2589 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 3);
2591 if (m_buttonCancel
){
2592 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 3);
2593 // Cancel or help should be default
2594 // m_buttonCancel->SetDefaultButton();
2597 if (m_buttonAffirmative
)
2598 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
, 6);
2599 #elif defined(__WXMSW__)
2602 // right-justify buttons
2603 Add(0, 0, 1, wxEXPAND
, 0);
2605 if (m_buttonAffirmative
){
2606 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2609 if (m_buttonNegative
){
2610 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2613 if (m_buttonCancel
){
2614 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2617 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2620 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(2, 0)).x
);
2622 // GTK+1 and any other platform
2624 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2626 Add((wxWindow
*)m_buttonHelp
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonHelp
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2628 // extra whitespace between help and cancel/ok buttons
2629 Add(0, 0, 1, wxEXPAND
, 0);
2632 Add((wxWindow
*)m_buttonApply
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonApply
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2634 if (m_buttonAffirmative
){
2635 Add((wxWindow
*)m_buttonAffirmative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonAffirmative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2638 if (m_buttonNegative
){
2639 Add((wxWindow
*)m_buttonNegative
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonNegative
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2642 if (m_buttonCancel
){
2643 Add((wxWindow
*)m_buttonCancel
, 0, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, m_buttonCancel
->ConvertDialogToPixels(wxSize(4, 0)).x
);
2644 // Cancel or help should be default
2645 // m_buttonCancel->SetDefaultButton();
2651 #endif // wxUSE_BUTTON