X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/3abb199dee0afdeae31bac69186751a02a37611e..7b0ccb8a603b4f97740acc65d9429bb58f7ba1bd:/src/common/sizer.cpp?ds=inline diff --git a/src/common/sizer.cpp b/src/common/sizer.cpp index a8abbb3445..930aa061bb 100644 --- a/src/common/sizer.cpp +++ b/src/common/sizer.cpp @@ -17,21 +17,23 @@ #pragma hdrstop #endif +#include "wx/display.h" +#include "wx/sizer.h" +#include "wx/private/flagscheck.h" + #ifndef WX_PRECOMP #include "wx/string.h" #include "wx/intl.h" #include "wx/math.h" + #include "wx/utils.h" + #include "wx/settings.h" + #include "wx/button.h" + #include "wx/statbox.h" + #include "wx/toplevel.h" #endif // WX_PRECOMP -#include "wx/sizer.h" -#include "wx/utils.h" -#include "wx/statbox.h" -#include "wx/settings.h" #include "wx/listimpl.cpp" -#include "wx/intl.h" -#if WXWIN_COMPATIBILITY_2_4 - #include "wx/notebook.h" -#endif + //--------------------------------------------------------------------------- @@ -86,6 +88,31 @@ WX_DEFINE_EXPORTED_LIST( wxSizerItemList ) // wxSizerItem // ---------------------------------------------------------------------------- +// check for flags conflicts +static const int SIZER_FLAGS_MASK = + wxADD_FLAG(wxCENTRE, + wxADD_FLAG(wxHORIZONTAL, + wxADD_FLAG(wxVERTICAL, + wxADD_FLAG(wxLEFT, + wxADD_FLAG(wxRIGHT, + wxADD_FLAG(wxUP, + wxADD_FLAG(wxDOWN, + wxADD_FLAG(wxALIGN_NOT, + wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL, + wxADD_FLAG(wxALIGN_RIGHT, + wxADD_FLAG(wxALIGN_BOTTOM, + wxADD_FLAG(wxALIGN_CENTER_VERTICAL, + wxADD_FLAG(wxFIXED_MINSIZE, + wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN, + wxADD_FLAG(wxSTRETCH_NOT, + wxADD_FLAG(wxSHRINK, + wxADD_FLAG(wxGROW, + wxADD_FLAG(wxSHAPED, + 0)))))))))))))))))); + +#define ASSERT_VALID_SIZER_FLAGS(f) wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK) + + void wxSizerItem::Init(const wxSizerFlags& flags) { Init(); @@ -93,6 +120,8 @@ void wxSizerItem::Init(const wxSizerFlags& flags) m_proportion = flags.GetProportion(); m_flag = flags.GetFlags(); m_border = flags.GetBorderInPixels(); + + ASSERT_VALID_SIZER_FLAGS( m_flag ); } wxSizerItem::wxSizerItem() @@ -102,12 +131,11 @@ wxSizerItem::wxSizerItem() m_proportion = 0; m_border = 0; m_flag = 0; - - m_kind = Item_None; + m_id = wxID_NONE; } // window item -void wxSizerItem::SetWindow(wxWindow *window) +void wxSizerItem::DoSetWindow(wxWindow *window) { wxCHECK_RET( window, _T("NULL window in wxSizerItem::SetWindow()") ); @@ -129,16 +157,20 @@ wxSizerItem::wxSizerItem(wxWindow *window, int flag, int border, wxObject* userData) - : m_proportion(proportion), + : m_kind(Item_None), + m_proportion(proportion), m_border(border), m_flag(flag), + m_id(wxID_NONE), m_userData(userData) { - SetWindow(window); + ASSERT_VALID_SIZER_FLAGS( m_flag ); + + DoSetWindow(window); } // sizer item -void wxSizerItem::SetSizer(wxSizer *sizer) +void wxSizerItem::DoSetSizer(wxSizer *sizer) { m_kind = Item_Sizer; m_sizer = sizer; @@ -149,19 +181,24 @@ wxSizerItem::wxSizerItem(wxSizer *sizer, int flag, int border, wxObject* userData) - : m_proportion(proportion), + : m_kind(Item_None), + m_sizer(NULL), + m_proportion(proportion), m_border(border), m_flag(flag), + m_id(wxID_NONE), m_ratio(0.0), m_userData(userData) { - SetSizer(sizer); + ASSERT_VALID_SIZER_FLAGS( m_flag ); + + DoSetSizer(sizer); // m_minSize is set later } // spacer item -void wxSizerItem::SetSpacer(const wxSize& size) +void wxSizerItem::DoSetSpacer(const wxSize& size) { m_kind = Item_Spacer; m_spacer = new wxSizerSpacer(size); @@ -175,19 +212,28 @@ wxSizerItem::wxSizerItem(int width, int flag, int border, wxObject* userData) - : m_minSize(width, height), // minimal size is the initial size + : m_kind(Item_None), + m_sizer(NULL), + m_minSize(width, height), // minimal size is the initial size m_proportion(proportion), m_border(border), m_flag(flag), + m_id(wxID_NONE), m_userData(userData) { - SetSpacer(width, height); + ASSERT_VALID_SIZER_FLAGS( m_flag ); + + DoSetSpacer(wxSize(width, height)); } wxSizerItem::~wxSizerItem() { delete m_userData; + Free(); +} +void wxSizerItem::Free() +{ switch ( m_kind ) { case Item_None: @@ -209,6 +255,8 @@ wxSizerItem::~wxSizerItem() default: wxFAIL_MSG( _T("unexpected wxSizerItem::m_kind") ); } + + m_kind = Item_None; } wxSize wxSizerItem::GetSpacer() const @@ -258,6 +306,73 @@ wxSize wxSizerItem::GetSize() const return ret; } +bool wxSizerItem::InformFirstDirection(int direction, int size, int availableOtherDir) +{ + // The size that come here will be including borders. Child items should get it + // without borders. + if( size>0 ) + { + if( direction==wxHORIZONTAL ) + { + if (m_flag & wxWEST) + size -= m_border; + if (m_flag & wxEAST) + size -= m_border; + } + else if( direction==wxVERTICAL ) + { + if (m_flag & wxNORTH) + size -= m_border; + if (m_flag & wxSOUTH) + size -= m_border; + } + } + + bool didUse = false; + // Pass the information along to the held object + if (IsSizer()) + { + didUse = GetSizer()->InformFirstDirection(direction,size,availableOtherDir); + if (didUse) + m_minSize = GetSizer()->CalcMin(); + } + else if (IsWindow()) + { + didUse = GetWindow()->InformFirstDirection(direction,size,availableOtherDir); + if (didUse) + m_minSize = m_window->GetEffectiveMinSize(); + + // This information is useful for items with wxSHAPED flag, since + // we can request an optimal min size for such an item. Even if + // we overwrite the m_minSize member here, we can read it back from + // the owned window (happens automatically). + if( (m_flag & wxSHAPED) && (m_flag & wxEXPAND) && direction ) + { + if( !wxIsNullDouble(m_ratio) ) + { + wxCHECK_MSG( (m_proportion==0), false, _T("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") ); + if( direction==wxHORIZONTAL && !wxIsNullDouble(m_ratio) ) + { + // Clip size so that we don't take too much + if( availableOtherDir>=0 && int(size/m_ratio)-m_minSize.y>availableOtherDir ) + size = int((availableOtherDir+m_minSize.y)*m_ratio); + m_minSize = wxSize(size,int(size/m_ratio)); + } + else if( direction==wxVERTICAL ) + { + // Clip size so that we don't take too much + if( availableOtherDir>=0 && int(size*m_ratio)-m_minSize.x>availableOtherDir ) + size = int((availableOtherDir+m_minSize.x)/m_ratio); + m_minSize = wxSize(int(size*m_ratio),size); + } + didUse = true; + } + } + } + + return didUse; +} + wxSize wxSizerItem::CalcMin() { if (IsSizer()) @@ -273,7 +388,7 @@ wxSize wxSizerItem::CalcMin() { // Since the size of the window may change during runtime, we // should use the current minimal/best size. - m_minSize = m_window->GetBestFittingSize(); + m_minSize = m_window->GetEffectiveMinSize(); } return GetMinSizeWithBorder(); @@ -351,6 +466,11 @@ void wxSizerItem::SetDimension( const wxPoint& pos_, const wxSize& size_ ) size.y -= m_border; } + if (size.x < 0) + size.x = 0; + if (size.y < 0) + size.y = 0; + m_rect = wxRect(pos, size); switch ( m_kind ) @@ -436,6 +556,9 @@ void wxSizerItem::Show( bool show ) bool wxSizerItem::IsShown() const { + if ( m_flag & wxRESERVE_SPACE_EVEN_IF_HIDDEN ) + return true; + switch ( m_kind ) { case Item_None: @@ -451,6 +574,11 @@ bool wxSizerItem::IsShown() const // shown, so are we (this arbitrariness is the reason for // deprecating this function) { + // Some apps (such as dialog editors) depend on an empty sizer still + // being laid out correctly and reporting the correct size and position. + if (m_sizer->GetChildren().GetCount() == 0) + return true; + for ( wxSizerItemList::compatibility_iterator node = m_sizer->GetChildren().GetFirst(); node; @@ -502,9 +630,35 @@ wxSizerItem* wxSizer::Insert( size_t index, wxSizerItem *item ) if ( item->GetWindow() ) item->GetWindow()->SetContainingSizer( this ); + if ( item->GetSizer() ) + item->GetSizer()->SetContainingWindow( m_containingWindow ); + return item; } +void wxSizer::SetContainingWindow(wxWindow *win) +{ + if ( win == m_containingWindow ) + return; + + m_containingWindow = win; + + // set the same window for all nested sizers as well, they also are in the + // same window + for ( wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); + node; + node = node->GetNext() ) + { + wxSizerItem *const item = node->GetData(); + wxSizer *const sizer = item->GetSizer(); + + if ( sizer ) + { + sizer->SetContainingWindow(win); + } + } +} + #if WXWIN_COMPATIBILITY_2_6 bool wxSizer::Remove( wxWindow *window ) { @@ -544,13 +698,9 @@ bool wxSizer::Remove( int index ) wxCHECK_MSG( node, false, _T("Failed to find child node") ); - wxSizerItem *item = node->GetData(); - - if ( item->IsWindow() ) - item->GetWindow()->SetContainingSizer( NULL ); - - delete item; + delete node->GetData(); m_children.Erase( node ); + return true; } @@ -587,7 +737,6 @@ bool wxSizer::Detach( wxWindow *window ) if (item->GetWindow() == window) { - item->GetWindow()->SetContainingSizer( NULL ); delete item; m_children.Erase( node ); return true; @@ -612,14 +761,83 @@ bool wxSizer::Detach( int index ) if ( item->IsSizer() ) item->DetachSizer(); - else if ( item->IsWindow() ) - item->GetWindow()->SetContainingSizer( NULL ); delete item; m_children.Erase( node ); return true; } +bool wxSizer::Replace( wxWindow *oldwin, wxWindow *newwin, bool recursive ) +{ + wxASSERT_MSG( oldwin, _T("Replacing NULL window") ); + wxASSERT_MSG( newwin, _T("Replacing with NULL window") ); + + wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); + while (node) + { + wxSizerItem *item = node->GetData(); + + if (item->GetWindow() == oldwin) + { + item->AssignWindow(newwin); + newwin->SetContainingSizer( this ); + return true; + } + else if (recursive && item->IsSizer()) + { + if (item->GetSizer()->Replace( oldwin, newwin, true )) + return true; + } + + node = node->GetNext(); + } + + return false; +} + +bool wxSizer::Replace( wxSizer *oldsz, wxSizer *newsz, bool recursive ) +{ + wxASSERT_MSG( oldsz, _T("Replacing NULL sizer") ); + wxASSERT_MSG( newsz, _T("Replacing with NULL sizer") ); + + wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); + while (node) + { + wxSizerItem *item = node->GetData(); + + if (item->GetSizer() == oldsz) + { + item->AssignSizer(newsz); + return true; + } + else if (recursive && item->IsSizer()) + { + if (item->GetSizer()->Replace( oldsz, newsz, true )) + return true; + } + + node = node->GetNext(); + } + + return false; +} + +bool wxSizer::Replace( size_t old, wxSizerItem *newitem ) +{ + wxCHECK_MSG( old < m_children.GetCount(), false, _T("Replace index is out of range") ); + wxASSERT_MSG( newitem, _T("Replacing with NULL item") ); + + wxSizerItemList::compatibility_iterator node = m_children.Item( old ); + + wxCHECK_MSG( node, false, _T("Failed to find child node") ); + + wxSizerItem *item = node->GetData(); + node->SetData(newitem); + delete item; + + return true; +} + void wxSizer::Clear( bool delete_windows ) { // First clear the ContainingSizer pointers @@ -653,16 +871,67 @@ void wxSizer::DeleteWindows() } } -wxSize wxSizer::Fit( wxWindow *window ) +wxSize wxSizer::ComputeFittingClientSize(wxWindow *window) { - wxSize size(window->IsTopLevel() ? FitSize(window) - : GetMinWindowSize(window)); + wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" ); + + // take the min size by default and limit it by max size + wxSize size = GetMinClientSize(window); + wxSize sizeMax; + + wxTopLevelWindow *tlw = wxDynamicCast(window, wxTopLevelWindow); + if ( tlw ) + { + // hack for small screen devices where TLWs are always full screen + if ( tlw->IsAlwaysMaximized() ) + { + return tlw->GetClientSize(); + } + + // limit the window to the size of the display it is on + int disp = wxDisplay::GetFromWindow(window); + if ( disp == wxNOT_FOUND ) + { + // or, if we don't know which one it is, of the main one + disp = 0; + } + + sizeMax = wxDisplay(disp).GetClientArea().GetSize(); - window->SetSize( size ); + // space for decorations and toolbars etc. + sizeMax = tlw->WindowToClientSize(sizeMax); + } + else + { + sizeMax = GetMaxClientSize(window); + } + + if ( sizeMax.x != wxDefaultCoord && size.x > sizeMax.x ) + size.x = sizeMax.x; + if ( sizeMax.y != wxDefaultCoord && size.y > sizeMax.y ) + size.y = sizeMax.y; return size; } +wxSize wxSizer::ComputeFittingWindowSize(wxWindow *window) +{ + wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" ); + + return window->ClientToWindowSize(ComputeFittingClientSize(window)); +} + +wxSize wxSizer::Fit( wxWindow *window ) +{ + wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" ); + + // set client size + window->SetClientSize(ComputeFittingClientSize(window)); + + // return entire size + return window->GetSize(); +} + void wxSizer::FitInside( wxWindow *window ) { wxSize size; @@ -689,76 +958,31 @@ void wxSizer::SetSizeHints( wxWindow *window ) // Preserve the window's max size hints, but set the // lower bound according to the sizer calculations. - wxSize size = Fit( window ); + // This is equivalent to calling Fit(), except that we need to set + // the size hints _in between_ the two steps performed by Fit + // (1. ComputeFittingClientSize, 2. SetClientSize). That's because + // otherwise SetClientSize() could have no effect if there already are + // size hints in effect that forbid requested client size. + + const wxSize clientSize = ComputeFittingClientSize(window); - window->SetSizeHints( size.x, - size.y, - window->GetMaxWidth(), - window->GetMaxHeight() ); + window->SetMinClientSize(clientSize); + window->SetClientSize(clientSize); } +#if WXWIN_COMPATIBILITY_2_8 void wxSizer::SetVirtualSizeHints( wxWindow *window ) { - // Preserve the window's max size hints, but set the - // lower bound according to the sizer calculations. - FitInside( window ); - wxSize size( window->GetVirtualSize() ); - window->SetVirtualSizeHints( size.x, - size.y, - window->GetMaxWidth(), - window->GetMaxHeight() ); -} - -wxSize wxSizer::GetMaxWindowSize( wxWindow *window ) const -{ - return window->GetMaxSize(); -} - -wxSize wxSizer::GetMinWindowSize( wxWindow *window ) -{ - wxSize minSize( GetMinSize() ); - wxSize size( window->GetSize() ); - wxSize client_size( window->GetClientSize() ); - - return wxSize( minSize.x+size.x-client_size.x, - minSize.y+size.y-client_size.y ); } +#endif // WXWIN_COMPATIBILITY_2_8 // TODO on mac we need a function that determines how much free space this // min size contains, in order to make sure that we have 20 pixels of free // space around the controls - -// Return a window size that will fit within the screens dimensions -wxSize wxSizer::FitSize( wxWindow *window ) -{ - wxSize size = GetMinWindowSize( window ); - wxSize sizeMax = GetMaxWindowSize( window ); - - // Limit the size if sizeMax != wxDefaultSize - - if ( size.x > sizeMax.x && sizeMax.x != wxDefaultCoord ) - size.x = sizeMax.x; - if ( size.y > sizeMax.y && sizeMax.y != wxDefaultCoord ) - size.y = sizeMax.y; - - return size; -} - wxSize wxSizer::GetMaxClientSize( wxWindow *window ) const { - wxSize maxSize( window->GetMaxSize() ); - - if ( maxSize != wxDefaultSize ) - { - wxSize size( window->GetSize() ); - wxSize client_size( window->GetClientSize() ); - - return wxSize( maxSize.x + client_size.x - size.x, - maxSize.y + client_size.y - size.y ); - } - else - return wxDefaultSize; + return window->WindowToClientSize(window->GetMaxSize()); } wxSize wxSizer::GetMinClientSize( wxWindow *WXUNUSED(window) ) @@ -963,6 +1187,33 @@ wxSizerItem* wxSizer::GetItem( size_t index ) return m_children.Item( index )->GetData(); } +wxSizerItem* wxSizer::GetItemById( int id, bool recursive ) +{ + // This gets a sizer item by the id of the sizer item + // and NOT the id of a window if the item is a window. + + wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); + while (node) + { + wxSizerItem *item = node->GetData(); + + if (item->GetId() == id) + { + return item; + } + else if (recursive && item->IsSizer()) + { + wxSizerItem *subitem = item->GetSizer()->GetItemById( id, true ); + if (subitem) + return subitem; + } + + node = node->GetNext(); + } + + return NULL; +} + bool wxSizer::Show( wxWindow *window, bool show, bool recursive ) { wxSizerItem *item = GetItem( window, recursive ); @@ -1143,7 +1394,7 @@ wxSize wxGridSizer::CalcMin() { int nrows, ncols; if ( CalcRowsCols(nrows, ncols) == 0 ) - return wxSize(10, 10); + return wxSize(); // Find the max width and height for any component int w = 0; @@ -1161,6 +1412,35 @@ wxSize wxGridSizer::CalcMin() node = node->GetNext(); } + // In case we have a nested sizer with a two step algo , give it + // a chance to adjust to that (we give it width component) + node = m_children.GetFirst(); + bool didChangeMinSize = false; + while (node) + { + wxSizerItem *item = node->GetData(); + didChangeMinSize |= item->InformFirstDirection( wxHORIZONTAL, w, -1 ); + + node = node->GetNext(); + } + + // And redo iteration in case min size changed + if( didChangeMinSize ) + { + node = m_children.GetFirst(); + w = h = 0; + while (node) + { + wxSizerItem *item = node->GetData(); + wxSize sz( item->GetMinSizeWithBorder() ); + + w = wxMax( w, sz.x ); + h = wxMax( h, sz.y ); + + node = node->GetNext(); + } + } + return wxSize( ncols * w + (ncols-1) * m_hgap, nrows * h + (nrows-1) * m_vgap ); } @@ -1223,105 +1503,160 @@ wxFlexGridSizer::~wxFlexGridSizer() void wxFlexGridSizer::RecalcSizes() { - int nitems, nrows, ncols; - if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 ) + int nrows, ncols; + if ( !CalcRowsCols(nrows, ncols) ) return; - wxPoint pt( GetPosition() ); - wxSize sz( GetSize() ); + const wxPoint pt(GetPosition()); + const wxSize sz(GetSize()); - AdjustForGrowables(sz, m_calculatedMinSize, nrows, ncols); + AdjustForGrowables(sz); - sz = wxSize( pt.x + sz.x, pt.y + sz.y ); + wxSizerItemList::const_iterator i = m_children.begin(); + const wxSizerItemList::const_iterator end = m_children.end(); - int x = pt.x; - for (int c = 0; c < ncols; c++) + int y = 0; + for ( int r = 0; r < nrows; r++ ) { - int y = pt.y; - for (int r = 0; r < nrows; r++) + if ( m_rowHeights[r] == -1 ) { - int i = r * ncols + c; - if (i < nitems) + // this row is entirely hidden, skip it + for ( int c = 0; c < ncols; c++ ) { - wxSizerItemList::compatibility_iterator node = m_children.Item( i ); + if ( i == end ) + return; - wxASSERT_MSG( node, _T("Failed to find node") ); + ++i; + } - int w = wxMax( 0, wxMin( m_colWidths[c], sz.x - x ) ); - int h = wxMax( 0, wxMin( m_rowHeights[r], sz.y - y ) ); + continue; + } - SetItemBounds( node->GetData(), x, y, w, h); - } - if (m_rowHeights[r] != -1) - y = y + m_rowHeights[r] + m_vgap; + const int hrow = m_rowHeights[r]; + int h = sz.y - y; // max remaining height, don't overflow it + if ( hrow < h ) + h = hrow; + + int x = 0; + for ( int c = 0; c < ncols && i != end; c++, ++i ) + { + const int wcol = m_colWidths[c]; + + if ( wcol == -1 ) + continue; + + int w = sz.x - x; // max possible value, ensure we don't overflow + if ( wcol < w ) + w = wcol; + + SetItemBounds(*i, pt.x + x, pt.y + y, w, h); + + x += wcol + m_hgap; } - if (m_colWidths[c] != -1) - x = x + m_colWidths[c] + m_hgap; + + if ( i == end ) + return; + + y += hrow + m_vgap; } } +// helper function used in CalcMin() to sum up the sizes of non-hidden items +static int SumArraySizes(const wxArrayInt& sizes, int gap) +{ + // Sum total minimum size, including gaps between rows/columns. + // -1 is used as a magic number meaning empty row/column. + int total = 0; + + const size_t count = sizes.size(); + for ( size_t n = 0; n < count; n++ ) + { + if ( sizes[n] != -1 ) + { + if ( total ) + total += gap; // separate from the previous column + + total += sizes[n]; + } + } + + return total; +} + +void wxFlexGridSizer::FindWidthsAndHeights(int nrows, int ncols) +{ + // We have to recalculate the sizes in case the item minimum size has + // changed since the previous layout, or the item has been hidden using + // wxSizer::Show(). If all the items in a row/column are hidden, the final + // dimension of the row/column will be -1, indicating that the column + // itself is hidden. + m_rowHeights.assign(nrows, -1); + m_colWidths.assign(ncols, -1); + + // n is the index of the item in left-to-right top-to-bottom order + size_t n = 0; + for ( wxSizerItemList::iterator i = m_children.begin(); + i != m_children.end(); + ++i, ++n ) + { + wxSizerItem * const item = *i; + if ( item->IsShown() ) + { + // NOTE: Not doing the calculation here, this is just + // for finding max values. + const wxSize sz(item->GetMinSizeWithBorder()); + + const int row = n / ncols; + const int col = n % ncols; + + if ( sz.y > m_rowHeights[row] ) + m_rowHeights[row] = sz.y; + if ( sz.x > m_colWidths[col] ) + m_colWidths[col] = sz.x; + } + } + + AdjustForFlexDirection(); + + m_calculatedMinSize = wxSize(SumArraySizes(m_colWidths, m_hgap), + SumArraySizes(m_rowHeights, m_vgap)); +} + wxSize wxFlexGridSizer::CalcMin() { - int nrows, - ncols; - size_t i, s; + int nrows, + ncols; // Number of rows/columns can change as items are added or removed. if ( !CalcRowsCols(nrows, ncols) ) - return wxSize(10, 10); + return wxSize(); - m_rowHeights.SetCount(nrows); - m_colWidths.SetCount(ncols); - // We have to recalcuate the sizes in case the item minimum size has + // We have to recalculate the sizes in case the item minimum size has // changed since the previous layout, or the item has been hidden using // wxSizer::Show(). If all the items in a row/column are hidden, the final // dimension of the row/column will be -1, indicating that the column // itself is hidden. - for( s = m_rowHeights.GetCount(), i = 0; i < s; ++i ) - m_rowHeights[ i ] = -1; - for( s = m_colWidths.GetCount(), i = 0; i < s; ++i ) - m_colWidths[ i ] = -1; - - wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); - - i = 0; - while (node) + m_rowHeights.assign(nrows, -1); + m_colWidths.assign(ncols, -1); + + // n is the index of the item in left-to-right top-to-bottom order + size_t n = 0; + for ( wxSizerItemList::iterator i = m_children.begin(); + i != m_children.end(); + ++i, ++n ) { - wxSizerItem *item = node->GetData(); + wxSizerItem * const item = *i; if ( item->IsShown() ) { - wxSize sz( item->CalcMin() ); - int row = i / ncols; - int col = i % ncols; - - m_rowHeights[ row ] = wxMax( wxMax( 0, sz.y ), m_rowHeights[ row ] ); - m_colWidths[ col ] = wxMax( wxMax( 0, sz.x ), m_colWidths[ col ] ); + item->CalcMin(); } - - node = node->GetNext(); - i++; } - AdjustForFlexDirection(); + // The stage of looking for max values in each row/column has been + // made a separate function, since it's reused in AdjustForGrowables. + FindWidthsAndHeights(nrows,ncols); - // Sum total minimum size, including gaps between rows/columns. - // -1 is used as a magic number meaning empty column. - int width = 0; - for (int col = 0; col < ncols; col++) - if ( m_colWidths[ col ] != -1 ) - width += m_colWidths[ col ] + m_hgap; - if (width > 0) - width -= m_hgap; - - int height = 0; - for (int row = 0; row < nrows; row++) - if ( m_rowHeights[ row ] != -1 ) - height += m_rowHeights[ row ] + m_vgap; - if (height > 0) - height -= m_vgap; - - m_calculatedMinSize = wxSize( width, height ); return m_calculatedMinSize; } @@ -1351,116 +1686,146 @@ void wxFlexGridSizer::AdjustForFlexDirection() // and now fill it with the largest value for ( n = 0; n < count; ++n ) { - array[n] = largest; + // don't touch hidden rows + if ( array[n] != -1 ) + array[n] = largest; } } } +// helper of AdjustForGrowables() which is called for rows/columns separately +// +// parameters: +// delta: the extra space, we do nothing unless it's positive +// growable: indices or growable rows/cols in sizes array +// sizes: the height/widths of rows/cols to adjust +// proportions: proportions of the growable rows/cols or NULL if they all +// should be assumed to have proportion of 1 +static void +DoAdjustForGrowables(int delta, + const wxArrayInt& growable, + wxArrayInt& sizes, + const wxArrayInt *proportions) +{ + if ( delta <= 0 ) + return; -void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz, const wxSize& minsz, - int nrows, int ncols) -{ - // what to do with the rows? by default, resize them proportionally - if ( sz.y > minsz.y && ( (m_flexDirection & wxVERTICAL) || (m_growMode == wxFLEX_GROWMODE_SPECIFIED) ) ) - { - int sum_proportions = 0; - int growable_space = 0; - int num = 0; - size_t idx; - for (idx = 0; idx < m_growableRows.GetCount(); idx++) - { - // Since the number of rows/columns can change as items are - // inserted/deleted, we need to verify at runtime that the - // requested growable rows/columns are still valid. - if (m_growableRows[idx] >= nrows) - continue; + // total sum of proportions of all non-hidden rows + int sum_proportions = 0; - // If all items in a row/column are hidden, that row/column will - // have a dimension of -1. This causes the row/column to be - // hidden completely. - if (m_rowHeights[ m_growableRows[idx] ] == -1) - continue; - sum_proportions += m_growableRowsProportions[idx]; - growable_space += m_rowHeights[ m_growableRows[idx] ]; - num++; - } + // number of currently shown growable rows + int num = 0; - if (num > 0) - { - for (idx = 0; idx < m_growableRows.GetCount(); idx++) - { - if (m_growableRows[idx] >= nrows ) - continue; - if (m_rowHeights[ m_growableRows[idx] ] == -1) - m_rowHeights[ m_growableRows[idx] ] = 0; - else - { - int delta = (sz.y - minsz.y); - if (sum_proportions == 0) - delta = (delta/num) + m_rowHeights[ m_growableRows[idx] ]; - else - delta = ((delta+growable_space)*m_growableRowsProportions[idx]) / sum_proportions; - m_rowHeights[ m_growableRows[idx] ] = delta; - } - } - } - } - else if ( (m_growMode == wxFLEX_GROWMODE_ALL) && (sz.y > minsz.y) ) + const int max_idx = sizes.size(); + + const size_t count = growable.size(); + size_t idx; + for ( idx = 0; idx < count; idx++ ) { - // rounding problem? - for ( int row = 0; row < nrows; ++row ) - m_rowHeights[ row ] = sz.y / nrows; + // Since the number of rows/columns can change as items are + // inserted/deleted, we need to verify at runtime that the + // requested growable rows/columns are still valid. + if ( growable[idx] >= max_idx ) + continue; + + // If all items in a row/column are hidden, that row/column will + // have a dimension of -1. This causes the row/column to be + // hidden completely. + if ( sizes[growable[idx]] == -1 ) + continue; + + if ( proportions ) + sum_proportions += (*proportions)[idx]; + + num++; } - // the same logic as above but for the columns - if ( sz.x > minsz.x && ( (m_flexDirection & wxHORIZONTAL) || (m_growMode == wxFLEX_GROWMODE_SPECIFIED) ) ) + if ( !num ) + return; + + // the remaining extra free space, adjusted during each iteration + for ( idx = 0; idx < count; idx++ ) { - int sum_proportions = 0; - int growable_space = 0; - int num = 0; - size_t idx; - for (idx = 0; idx < m_growableCols.GetCount(); idx++) - { - // Since the number of rows/columns can change as items are - // inserted/deleted, we need to verify at runtime that the - // requested growable rows/columns are still valid. - if (m_growableCols[idx] >= ncols) - continue; + if ( growable[idx] >= max_idx ) + continue; - // If all items in a row/column are hidden, that row/column will - // have a dimension of -1. This causes the column to be hidden - // completely. - if (m_colWidths[ m_growableCols[idx] ] == -1) - continue; - sum_proportions += m_growableColsProportions[idx]; - growable_space += m_colWidths[ m_growableCols[idx] ]; - num++; - } + if ( sizes[ growable[idx] ] == -1 ) + continue; - if (num > 0) + int cur_delta; + if ( sum_proportions == 0 ) { - for (idx = 0; idx < m_growableCols.GetCount(); idx++) - { - if (m_growableCols[idx] >= ncols ) - continue; - if (m_colWidths[ m_growableCols[idx] ] == -1) - m_colWidths[ m_growableCols[idx] ] = 0; - else - { - int delta = (sz.x - minsz.x); - if (sum_proportions == 0) - delta = (delta/num) + m_colWidths[ m_growableCols[idx] ]; - else - delta = ((delta+growable_space)*m_growableColsProportions[idx])/sum_proportions; - m_colWidths[ m_growableCols[idx] ] = delta; - } - } + // no growable rows -- divide extra space evenly among all + cur_delta = delta/num; + num--; } + else // allocate extra space proportionally + { + const int cur_prop = (*proportions)[idx]; + cur_delta = (delta*cur_prop)/sum_proportions; + sum_proportions -= cur_prop; + } + + sizes[growable[idx]] += cur_delta; + delta -= cur_delta; } - else if ( (m_growMode == wxFLEX_GROWMODE_ALL) && (sz.x > minsz.x) ) +} + +void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz) +{ + if ( (m_flexDirection & wxHORIZONTAL) || (m_growMode != wxFLEX_GROWMODE_NONE) ) + { + DoAdjustForGrowables + ( + sz.x - m_calculatedMinSize.x, + m_growableCols, + m_colWidths, + m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions + : NULL + ); + + // This gives nested objects that benefit from knowing one size + // component in advance the chance to use that. + bool didAdjustMinSize = false; + int nrows, ncols; + CalcRowsCols(nrows, ncols); + + // Iterate over all items and inform about column width + size_t n = 0; + for ( wxSizerItemList::iterator i = m_children.begin(); + i != m_children.end(); + ++i, ++n ) + { + const int col = n % ncols; + didAdjustMinSize |= (*i)->InformFirstDirection(wxHORIZONTAL, m_colWidths[col], sz.y - m_calculatedMinSize.y); + } + + // Only redo if info was actually used + if( didAdjustMinSize ) { - for ( int col=0; col < ncols; ++col ) - m_colWidths[ col ] = sz.x / ncols; + DoAdjustForGrowables + ( + sz.x - m_calculatedMinSize.x, + m_growableCols, + m_colWidths, + m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions + : NULL + ); + } +} + + if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) ) + { + // pass NULL instead of proportions if the grow mode is ALL as we + // should treat all rows as having proportion of 1 then + DoAdjustForGrowables + ( + sz.y - m_calculatedMinSize.y, + m_growableRows, + m_rowHeights, + m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableRowsProportions + : NULL + ); } } @@ -1471,214 +1836,398 @@ void wxFlexGridSizer::AddGrowableRow( size_t idx, int proportion ) m_growableRowsProportions.Add( proportion ); } -void wxFlexGridSizer::RemoveGrowableRow( size_t idx ) -{ - m_growableRows.Remove( idx ); -} - void wxFlexGridSizer::AddGrowableCol( size_t idx, int proportion ) { m_growableCols.Add( idx ); m_growableColsProportions.Add( proportion ); } +// helper function for RemoveGrowableCol/Row() +static void +DoRemoveFromArrays(size_t idx, wxArrayInt& items, wxArrayInt& proportions) +{ + const size_t count = items.size(); + for ( size_t n = 0; n < count; n++ ) + { + if ( (size_t)items[n] == idx ) + { + items.RemoveAt(n); + proportions.RemoveAt(n); + return; + } + } + + wxFAIL_MSG( _T("column/row is already not growable") ); +} + void wxFlexGridSizer::RemoveGrowableCol( size_t idx ) { - m_growableCols.Remove( idx ); + DoRemoveFromArrays(idx, m_growableCols, m_growableColsProportions); +} + +void wxFlexGridSizer::RemoveGrowableRow( size_t idx ) +{ + DoRemoveFromArrays(idx, m_growableRows, m_growableRowsProportions); } //--------------------------------------------------------------------------- // wxBoxSizer //--------------------------------------------------------------------------- -wxBoxSizer::wxBoxSizer( int orient ) - : m_orient( orient ) -{ -} - void wxBoxSizer::RecalcSizes() { - if (m_children.GetCount() == 0) + if ( m_children.empty() ) return; - int delta = 0; - if (m_stretchable) + const wxCoord totalMinorSize = GetSizeInMinorDir(m_size); + + // the amount of free space which we should redistribute among the + // stretchable items (i.e. those with non zero proportion) + int delta = GetSizeInMajorDir(m_size) - GetSizeInMajorDir(m_minSize); + + + // Inform child items about the size in minor direction, that can + // change how much free space we have in major dir and how to distribute it. + int majorMinSum = 0; + wxSizerItemList::const_iterator i ; + for ( i = m_children.begin(); + i != m_children.end(); + ++i ) { - if (m_orient == wxHORIZONTAL) - delta = m_size.x - m_fixedWidth; - else - delta = m_size.y - m_fixedHeight; + wxSizerItem * const item = *i; + + if ( !item->IsShown() ) + continue; + + wxSize szMinPrev = item->GetMinSizeWithBorder(); + item->InformFirstDirection(m_orient^wxBOTH,totalMinorSize,delta); + wxSize szMin = item->GetMinSizeWithBorder(); + int deltaChange = GetSizeInMajorDir(szMin-szMinPrev); + if( deltaChange ) + { + // Since we passed available space along to the item, it should not + // take too much, so delta should not become negative. + delta -= deltaChange; + } + majorMinSum += GetSizeInMajorDir(item->GetMinSizeWithBorder()); } + // And update our min size + SizeInMajorDir(m_minSize) = majorMinSum; - wxPoint pt( m_position ); - wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); - while (node) + // might have a new delta now + delta = GetSizeInMajorDir(m_size) - GetSizeInMajorDir(m_minSize); + + // the position at which we put the next child + wxPoint pt(m_position); + + int totalProportion = m_totalProportion; + for ( i = m_children.begin(); + i != m_children.end(); + ++i ) { - wxSizerItem *item = node->GetData(); + wxSizerItem * const item = *i; - if (item->IsShown()) - { - wxSize size( item->GetMinSizeWithBorder() ); + if ( !item->IsShown() ) + continue; - if (m_orient == wxVERTICAL) - { - wxCoord height = size.y; - if (item->GetProportion()) - { - // Because of at least one visible item has non-zero - // proportion then m_stretchable is not zero - height = (delta * item->GetProportion()) / m_stretchable; - } + const wxSize sizeThis(item->GetMinSizeWithBorder()); - wxPoint child_pos( pt ); - wxSize child_size( size.x, height ); + // adjust the size in the major direction using the proportion + wxCoord majorSize = GetSizeInMajorDir(sizeThis); + const int propItem = item->GetProportion(); + if ( propItem ) + { + const int deltaItem = (delta * propItem) / totalProportion; - if (item->GetFlag() & (wxEXPAND | wxSHAPED)) - child_size.x = m_size.x; - else if (item->GetFlag() & wxALIGN_RIGHT) - child_pos.x += m_size.x - size.x; - else if (item->GetFlag() & (wxCENTER | wxALIGN_CENTER_HORIZONTAL)) - // XXX wxCENTER is added for backward compatibility; - // wxALIGN_CENTER should be used in new code - child_pos.x += (m_size.x - size.x) / 2; + majorSize += deltaItem; - item->SetDimension( child_pos, child_size ); + delta -= deltaItem; + totalProportion -= propItem; + } - pt.y += height; - } - else - { - wxCoord width = size.x; - if (item->GetProportion()) - { - // Because of at least one visible item has non-zero - // proportion then m_stretchable is not zero - width = (delta * item->GetProportion()) / m_stretchable; - } - wxPoint child_pos( pt ); - wxSize child_size( width, size.y ); + // apply the alignment in the minor direction + wxPoint posChild(pt); - if (item->GetFlag() & (wxEXPAND | wxSHAPED)) - child_size.y = m_size.y; - else if (item->GetFlag() & wxALIGN_BOTTOM) - child_pos.y += m_size.y - size.y; - else if (item->GetFlag() & (wxCENTER | wxALIGN_CENTER_VERTICAL)) - // XXX wxCENTER is added for backward compatibility; - // wxALIGN_CENTER should be used in new code - child_pos.y += (m_size.y - size.y) / 2; + wxCoord minorSize = GetSizeInMinorDir(sizeThis); + const int flag = item->GetFlag(); + if ( flag & (wxEXPAND | wxSHAPED) ) + { + minorSize = totalMinorSize; + } + else if ( flag & (IsVertical() ? wxALIGN_RIGHT : wxALIGN_BOTTOM) ) + { + PosInMinorDir(posChild) += totalMinorSize - minorSize; + } + // NB: wxCENTRE is used here only for backwards compatibility, + // wxALIGN_CENTRE should be used in new code + else if ( flag & (wxCENTER | wxALIGN_CENTRE) ) + { + PosInMinorDir(posChild) += (totalMinorSize - minorSize) / 2; + } - item->SetDimension( child_pos, child_size ); - pt.x += width; - } + // apply RTL adjustment for horizontal sizers: + if ( !IsVertical() && m_containingWindow ) + { + posChild.x = m_containingWindow->AdjustForLayoutDirection + ( + posChild.x, + majorSize, + m_size.x + ); } - node = node->GetNext(); + // finally set size of this child and advance to the next one + item->SetDimension(posChild, SizeFromMajorMinor(majorSize, minorSize)); + + PosInMajorDir(pt) += majorSize; } } wxSize wxBoxSizer::CalcMin() { - if (m_children.GetCount() == 0) - return wxSize(10,10); + m_totalProportion = 0; + m_minSize = wxSize(0, 0); - m_stretchable = 0; - m_minWidth = 0; - m_minHeight = 0; - m_fixedWidth = 0; - m_fixedHeight = 0; - - // precalc item minsizes and count proportions - wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); - while (node) + // calculate the minimal sizes for all items and count sum of proportions + for ( wxSizerItemList::const_iterator i = m_children.begin(); + i != m_children.end(); + ++i ) { - wxSizerItem *item = node->GetData(); + wxSizerItem * const item = *i; - if ( item->IsShown() ) - { - item->CalcMin(); // result is stored in the item + if ( !item->IsShown() ) + continue; - m_stretchable += item->GetProportion(); - } + const wxSize sizeMinThis = item->CalcMin(); + SizeInMajorDir(m_minSize) += GetSizeInMajorDir(sizeMinThis); + if ( GetSizeInMinorDir(sizeMinThis) > GetSizeInMinorDir(m_minSize) ) + SizeInMinorDir(m_minSize) = GetSizeInMinorDir(sizeMinThis); - node = node->GetNext(); + m_totalProportion += item->GetProportion(); } - // Total minimum size (width or height) of sizer - int maxMinSize = 0; + return m_minSize; +} - node = m_children.GetFirst(); - while (node) +//--------------------------------------------------------------------------- +// wxWrapSizer +//--------------------------------------------------------------------------- + +#define wxDEFAULT_PROPORTION_LAST 1000000 + +// User data to hold old proportion for last item on line +// (which might be extended) +struct wxPropHolder : public wxObject +{ + wxPropHolder( ) : m_item(0), m_propOld(0) { } + void Init( wxSizerItem *item, int propOld ) { m_item=item; m_propOld=propOld; } + + wxSizerItem *m_item; + int m_propOld; +}; + +IMPLEMENT_DYNAMIC_CLASS(wxWrapSizer, wxBoxSizer); + +wxWrapSizer::wxWrapSizer( int orient, int flags ) + : wxBoxSizer(orient), + m_prim_size_last( -1 ), + m_rows(orient^wxBOTH), + m_flags(flags) +{ +} + +wxWrapSizer::~wxWrapSizer() +{ + // Have to clear grand child items so that they're not deleted twice + for( int ix=m_rows.GetChildren().GetCount()-1; ix>=0; ix-- ) { - wxSizerItem *item = node->GetData(); + wxSizer *psz = m_rows.GetItem((size_t)ix)->GetSizer(); + wxSizerItemList &sl = psz->GetChildren(); + while( sl.GetLast() ) + sl.Erase( sl.GetLast() ); + } +} - if (item->IsShown() && item->GetProportion() != 0) - { - int stretch = item->GetProportion(); - wxSize size( item->GetMinSizeWithBorder() ); - int minSize; - - // Integer division rounded up is (a + b - 1) / b - // Round up needed in order to guarantee that all - // all items will have size not less then their min size - if (m_orient == wxHORIZONTAL) - minSize = ( size.x*m_stretchable + stretch - 1)/stretch; - else - minSize = ( size.y*m_stretchable + stretch - 1)/stretch; - if (minSize > maxMinSize) - maxMinSize = minSize; - } - node = node->GetNext(); +bool wxWrapSizer::InformFirstDirection( int direction, int size, int WXUNUSED(availableOtherDir) ) +{ + if( !direction ) + { + // Better to keep value, then CalcMin will work better + //m_prim_size_last = -1; + return false; + } + if( direction==m_orient ) + { + // The direction is same as our primary, so we can make use of it + m_prim_size_last = size; + return true; } + else + return false; +} - // Calculate overall minimum size - node = m_children.GetFirst(); + +void wxWrapSizer::AdjustPropLastItem(wxSizer *psz, wxSizerItem *itemLast) +{ + wxSizerItem *psi = m_rows.GetItem(psz); + wxASSERT(psi); + wxPropHolder *pph = (wxPropHolder*)psi->GetUserData(); + if ( !pph ) + psi->SetUserData( pph=new wxPropHolder ); + + pph->Init( itemLast, itemLast->GetProportion() ); + itemLast->SetProportion( wxDEFAULT_PROPORTION_LAST ); +} + +void wxWrapSizer::RecalcSizes() +{ + wxASSERT( m_orient&wxBOTH ); + if (m_children.GetCount() == 0) + return; + + // What we do here is to put our items into child box sizers, + // as many of them as we have lines. + + // Empty all items in all rows in owned sizer. + // We have to access the list directly, since we don't want to + // destroy the wxSizerItems. + for( int ix=m_rows.GetChildren().GetCount()-1; ix>=0; ix-- ){ + wxSizerItem *psi = m_rows.GetItem( (size_t)ix ); + + // Restore proportion for last item on line (if item has not been deleted) + wxPropHolder *pph = (wxPropHolder*)psi->GetUserData(); + if( pph && GetChildren().Find(pph->m_item) ) + pph->m_item->SetProportion(pph->m_propOld); + + wxSizer *psz = psi->GetSizer(); + wxASSERT( psz ); + wxSizerItemList &sl = psz->GetChildren(); + while( sl.GetLast() ) + sl.Erase( sl.GetLast() ); + } + + int lineSumMajor = 0; + int majorSize = GetSizeInMajorDir(m_size); + + // Make sure we have at least one child sizer + m_n_line = 1; + if( !m_rows.GetChildren().GetCount() ) + m_rows.Add( new wxBoxSizer(GetOrientation()), 1, wxEXPAND ); + + // The sizer where to insert items in + wxSizer *psz = m_rows.GetItem((size_t)0)->GetSizer(); + wxASSERT( psz ); + + // Now put our child items into child sizers instead + wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); + wxSizerItem *item = NULL, *itemLast=NULL; while (node) { - wxSizerItem *item = node->GetData(); - - if (item->IsShown()) + item = node->GetData(); + if ( item->IsShown() ) { - wxSize size( item->GetMinSizeWithBorder() ); - if (item->GetProportion() != 0) + wxSize minSz = item->GetMinSize(); + int minSzMajor = GetSizeInMajorDir(minSz); + + // More space on this line? + if( !lineSumMajor || lineSumMajor+minSzMajor<=majorSize ) { - if (m_orient == wxHORIZONTAL) - size.x = (maxMinSize*item->GetProportion())/m_stretchable; - else - size.y = (maxMinSize*item->GetProportion())/m_stretchable; + lineSumMajor += minSzMajor; } else { - if (m_orient == wxVERTICAL) - { - m_fixedHeight += size.y; - m_fixedWidth = wxMax( m_fixedWidth, size.x ); - } - else - { - m_fixedWidth += size.x; - m_fixedHeight = wxMax( m_fixedHeight, size.y ); - } + lineSumMajor = minSzMajor; + // Get a new empty sizer to insert into + if( (int)m_rows.GetChildren().GetCount()<=m_n_line ) + m_rows.Add( new wxBoxSizer(GetOrientation()), 1, wxEXPAND ); + + // If we have extend-last-on-each-line mode, then do so now + // Note: We must store old proportion value then. + if( m_flags&wxEXTEND_LAST_ON_EACH_LINE ) + AdjustPropLastItem(psz,itemLast); + + // The sizer where to insert items in + psz = m_rows.GetItem(m_n_line++)->GetSizer(); } + itemLast = item; + psz->Add( item ); + // If item is a window, it now has a pointer to the child sizer, + // which is wrong. Set it to point to us. + if( item->GetWindow() ) + item->GetWindow()->SetContainingSizer( this ); + } + node = node->GetNext(); + } + + // If we have extend-last-on-each-line mode, then do so now + if( m_flags&wxEXTEND_LAST_ON_EACH_LINE ) + AdjustPropLastItem(psz,itemLast); + + // If we have more sizers than lines, remove them + while( (int)m_rows.GetChildren().GetCount()>m_n_line ) + m_rows.Remove( m_n_line ); + + // Now do layout on row sizer + m_rows.SetDimension( m_position.x, m_position.y, m_size.x, m_size.y ); + + // Remember this to next time (will be overridden by InformFirstDirection if used) + m_prim_size_last = GetSizeInMajorDir(m_size); +} - if (m_orient == wxHORIZONTAL) + +wxSize wxWrapSizer::CalcMin() +{ + if (m_children.GetCount() == 0) + return wxSize(); + + // Algorithm for calculating min size: (assuming horizontal orientation) + // X: Max width of all members + // Y: Based on last X, calculate how many lines needed + // First time around, assume all items fits on one line + + int maxMajor = 0; + int minorSum = 0; + int lineMaxMinor = 0; + int lineSumMajor = 0; + m_n_line = 0; + + // precalc item minsizes and fit on lines (preliminary) + wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); + while (node) + { + wxSizerItem *item = node->GetData(); + if ( item->IsShown() ) + { + wxSize minSz = item->CalcMin(); + int szMajor = GetSizeInMajorDir(minSz); + int szMinor = GetSizeInMinorDir(minSz); + if( szMajor>maxMajor ) maxMajor = szMajor; + // More space on this line? + if( m_prim_size_last<0 || !lineSumMajor || + lineSumMajor+szMajor<=m_prim_size_last ) { - m_minWidth += size.x; - m_minHeight = wxMax( m_minHeight, size.y ); + lineSumMajor += szMajor; + if( szMinor>lineMaxMinor ) + lineMaxMinor = szMinor; } else { - m_minHeight += size.y; - m_minWidth = wxMax( m_minWidth, size.x ); + minorSum += lineMaxMinor; // Add height of highest item on last line + m_n_line++; + lineMaxMinor = szMinor; + lineSumMajor = szMajor; } } node = node->GetNext(); } + minorSum += lineMaxMinor; // Add height of highest item on last line - return wxSize( m_minWidth, m_minHeight ); + m_minSize = SizeFromMajorMinor(maxMajor, minorSum); + return m_minSize; } //--------------------------------------------------------------------------- @@ -1688,16 +2237,27 @@ wxSize wxBoxSizer::CalcMin() #if wxUSE_STATBOX wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient ) - : wxBoxSizer( orient ) - , m_staticBox( box ) + : wxBoxSizer( orient ), + m_staticBox( box ) { wxASSERT_MSG( box, wxT("wxStaticBoxSizer needs a static box") ); + + // do this so that our Detach() is called if the static box is destroyed + // before we are + m_staticBox->SetContainingSizer(this); } wxStaticBoxSizer::wxStaticBoxSizer(int orient, wxWindow *win, const wxString& s) : wxBoxSizer(orient), m_staticBox(new wxStaticBox(win, wxID_ANY, s)) { + // same as above + m_staticBox->SetContainingSizer(this); +} + +wxStaticBoxSizer::~wxStaticBoxSizer() +{ + delete m_staticBox; } static void GetStaticBoxBorders( wxStaticBox *box, @@ -1747,6 +2307,20 @@ void wxStaticBoxSizer::ShowItems( bool show ) wxBoxSizer::ShowItems( show ); } +bool wxStaticBoxSizer::Detach( wxWindow *window ) +{ + // avoid deleting m_staticBox in our dtor if it's being detached from the + // sizer (which can happen because it's being already destroyed for + // example) + if ( window == m_staticBox ) + { + m_staticBox = NULL; + return true; + } + + return wxSizer::Detach( window ); +} + #endif // wxUSE_STATBOX #if wxUSE_BUTTON @@ -1790,6 +2364,7 @@ void wxStdDialogButtonSizer::AddButton(wxButton *mybutton) m_buttonNegative = mybutton; break; case wxID_CANCEL: + case wxID_CLOSE: m_buttonCancel = mybutton; break; case wxID_HELP: @@ -1849,7 +2424,8 @@ void wxStdDialogButtonSizer::Realize() if (m_buttonAffirmative->GetId() == wxID_SAVE){ // these buttons have set labels under Mac so we should use them m_buttonAffirmative->SetLabel(_("Save")); - m_buttonNegative->SetLabel(_("Don't Save")); + if (m_buttonNegative) + m_buttonNegative->SetLabel(_("Don't Save")); } } @@ -1867,15 +2443,17 @@ void wxStdDialogButtonSizer::Realize() Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3); } + // according to HIG, in explicit apply windows the order is: + // [ Help Apply Cancel OK ] + if (m_buttonApply) + Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3); + if (m_buttonCancel){ Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3); // Cancel or help should be default // m_buttonCancel->SetDefaultButton(); } - if (m_buttonApply) - Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3); - if (m_buttonAffirmative) Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6); #elif defined(__WXMSW__) @@ -1931,84 +2509,3 @@ void wxStdDialogButtonSizer::Realize() } #endif // wxUSE_BUTTON - -#if WXWIN_COMPATIBILITY_2_4 - -// ---------------------------------------------------------------------------- -// wxNotebookSizer -// ---------------------------------------------------------------------------- - -#if wxUSE_BOOKCTRL -IMPLEMENT_CLASS(wxBookCtrlSizer, wxSizer) -#if wxUSE_NOTEBOOK -IMPLEMENT_CLASS(wxNotebookSizer, wxBookCtrlSizer) -#endif // wxUSE_NOTEBOOK -#endif // wxUSE_BOOKCTRL - -#if wxUSE_BOOKCTRL - -#if WXWIN_COMPATIBILITY_2_6 - -wxBookCtrlSizer::wxBookCtrlSizer(wxBookCtrlBase *bookctrl) - : m_bookctrl(bookctrl) -{ - wxASSERT_MSG( bookctrl, wxT("wxBookCtrlSizer needs a control") ); -} - -#endif // WXWIN_COMPATIBILITY_2_6 - -void wxBookCtrlSizer::RecalcSizes() -{ - m_bookctrl->SetSize( m_position.x, m_position.y, m_size.x, m_size.y ); -} - -wxSize wxBookCtrlSizer::CalcMin() -{ - wxSize sizeBorder = m_bookctrl->CalcSizeFromPage(wxSize(0,0)); - - sizeBorder.x += 5; - sizeBorder.y += 5; - - if ( m_bookctrl->GetPageCount() == 0 ) - { - return wxSize(sizeBorder.x + 10, sizeBorder.y + 10); - } - - int maxX = 0; - int maxY = 0; - - wxWindowList::compatibility_iterator - node = m_bookctrl->GetChildren().GetFirst(); - while (node) - { - wxWindow *item = node->GetData(); - wxSizer *itemsizer = item->GetSizer(); - - if (itemsizer) - { - wxSize subsize( itemsizer->CalcMin() ); - - if (subsize.x > maxX) - maxX = subsize.x; - if (subsize.y > maxY) - maxY = subsize.y; - } - - node = node->GetNext(); - } - - return wxSize( maxX, maxY ) + sizeBorder; -} - -#if wxUSE_NOTEBOOK - -wxNotebookSizer::wxNotebookSizer(wxNotebook *nb) -{ - wxASSERT_MSG( nb, wxT("wxNotebookSizer needs a control") ); - m_bookctrl = nb; -} - -#endif // wxUSE_NOTEBOOOK -#endif // wxUSE_BOOKCTRL - -#endif // WXWIN_COMPATIBILITY_2_4