X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/29392c81aaf80dcabaab135a1eca64ff4c97092e..29fbbf8967e4c4b3174a4bc45a9007b92ecb0de7:/src/common/sizer.cpp diff --git a/src/common/sizer.cpp b/src/common/sizer.cpp index 84473ce9cc..6030fd9f20 100644 --- a/src/common/sizer.cpp +++ b/src/common/sizer.cpp @@ -33,9 +33,6 @@ #include "wx/listimpl.cpp" -#if WXWIN_COMPATIBILITY_2_4 - #include "wx/notebook.h" -#endif //--------------------------------------------------------------------------- @@ -106,12 +103,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()") ); @@ -133,16 +129,18 @@ 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); + DoSetWindow(window); } // sizer item -void wxSizerItem::SetSizer(wxSizer *sizer) +void wxSizerItem::DoSetSizer(wxSizer *sizer) { m_kind = Item_Sizer; m_sizer = sizer; @@ -153,19 +151,22 @@ 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); + 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); @@ -179,19 +180,26 @@ 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); + DoSetSpacer(wxSize(width, height)); } wxSizerItem::~wxSizerItem() { delete m_userData; + Free(); +} +void wxSizerItem::Free() +{ switch ( m_kind ) { case Item_None: @@ -213,6 +221,8 @@ wxSizerItem::~wxSizerItem() default: wxFAIL_MSG( _T("unexpected wxSizerItem::m_kind") ); } + + m_kind = Item_None; } wxSize wxSizerItem::GetSpacer() const @@ -262,6 +272,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()) @@ -516,6 +593,9 @@ 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; } @@ -581,13 +661,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; } @@ -624,7 +700,6 @@ bool wxSizer::Detach( wxWindow *window ) if (item->GetWindow() == window) { - item->GetWindow()->SetContainingSizer( NULL ); delete item; m_children.Erase( node ); return true; @@ -649,8 +724,6 @@ bool wxSizer::Detach( int index ) if ( item->IsSizer() ) item->DetachSizer(); - else if ( item->IsWindow() ) - item->GetWindow()->SetContainingSizer( NULL ); delete item; m_children.Erase( node ); @@ -669,8 +742,7 @@ bool wxSizer::Replace( wxWindow *oldwin, wxWindow *newwin, bool recursive ) if (item->GetWindow() == oldwin) { - item->GetWindow()->SetContainingSizer( NULL ); - item->SetWindow(newwin); + item->AssignWindow(newwin); newwin->SetContainingSizer( this ); return true; } @@ -698,9 +770,7 @@ bool wxSizer::Replace( wxSizer *oldsz, wxSizer *newsz, bool recursive ) if (item->GetSizer() == oldsz) { - wxSizer *old = item->GetSizer(); - item->SetSizer(newsz); - delete old; + item->AssignSizer(newsz); return true; } else if (recursive && item->IsSizer()) @@ -767,8 +837,8 @@ void wxSizer::DeleteWindows() wxSize wxSizer::Fit( wxWindow *window ) { // take the min size by default and limit it by max size - wxSize size = GetMinWindowSize(window); - wxSize sizeMax = GetMaxWindowSize(window); + wxSize size = GetMinClientSize(window); + wxSize sizeMax; wxTopLevelWindow *tlw = wxDynamicCast(window, wxTopLevelWindow); if ( tlw ) @@ -776,31 +846,41 @@ wxSize wxSizer::Fit( wxWindow *window ) // hack for small screen devices where TLWs are always full screen if ( tlw->IsAlwaysMaximized() ) { - size = tlw->GetSize(); + // do nothing + return tlw->GetSize(); } - else // normal situation - { - // 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(); + // 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(); + + // space for decorations and toolbars etc. + wxSize tlw_client_size = tlw->GetClientSize(); + wxSize tlw_size = tlw->GetSize(); + sizeMax.x -= tlw_size.x - tlw_client_size.x; + sizeMax.y -= tlw_size.y - tlw_client_size.y; + } + else + { + sizeMax = GetMaxClientSize(window); } if ( sizeMax.x != wxDefaultCoord && size.x > sizeMax.x ) - size.x = sizeMax.x; + size.x = sizeMax.x; if ( sizeMax.y != wxDefaultCoord && size.y > sizeMax.y ) - size.y = sizeMax.y; - + size.y = sizeMax.y; - window->SetSize( size ); + // set client size + window->SetClientSize( size ); - return size; + // return entire size + return window->GetSize(); } void wxSizer::FitInside( wxWindow *window ) @@ -837,18 +917,12 @@ void wxSizer::SetSizeHints( wxWindow *window ) window->GetMaxHeight() ); } +#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() ); } +#endif // WXWIN_COMPATIBILITY_2_8 wxSize wxSizer::GetMaxWindowSize( wxWindow *window ) const { @@ -1086,6 +1160,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 ); @@ -1284,6 +1385,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 ); } @@ -1346,105 +1476,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; + + ++i; + } - wxASSERT_MSG( node, _T("Failed to find node") ); + continue; + } - int w = wxMax( 0, wxMin( m_colWidths[c], sz.x - x ) ); - int h = wxMax( 0, wxMin( m_rowHeights[r], sz.y - y ) ); + const int hrow = m_rowHeights[r]; + int h = sz.y - y; // max remaining height, don't overflow it + if ( hrow < h ) + h = hrow; - SetItemBounds( node->GetData(), x, y, w, h); - } - if (m_rowHeights[r] != -1) - y = y + m_rowHeights[r] + m_vgap; + 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 ( 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; } - if (m_colWidths[c] != -1) - x = x + m_colWidths[c] + m_hgap; } + + 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(); - 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; } @@ -1474,116 +1659,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; } } } - -void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz, const wxSize& minsz, - int nrows, int ncols) +// 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) { - // 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; + if ( delta <= 0 ) + return; - // 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++; - } + // total sum of proportions of all non-hidden rows + int sum_proportions = 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) ) + // number of currently shown growable rows + int num = 0; + + 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; + } +} + +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 ) + { + DoAdjustForGrowables + ( + sz.x - m_calculatedMinSize.x, + m_growableCols, + m_colWidths, + m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions + : NULL + ); } - else if ( (m_growMode == wxFLEX_GROWMODE_ALL) && (sz.x > minsz.x) ) +} + + if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) ) { - for ( int col=0; col < ncols; ++col ) - m_colWidths[ col ] = sz.x / ncols; + // 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 + ); } } @@ -1632,204 +1847,360 @@ void wxFlexGridSizer::RemoveGrowableRow( size_t idx ) // 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) - { - if (m_orient == wxHORIZONTAL) - delta = m_size.x - m_fixedWidth; - else - delta = m_size.y - m_fixedHeight; - } + const wxCoord totalMinorSize = GetSizeInMinorDir(m_size); - wxPoint pt( m_position ); + // 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); - wxSizerItemList::compatibility_iterator node = m_children.GetFirst(); - while (node) + + // 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 ) { - wxSizerItem *item = node->GetData(); + wxSizerItem * const item = *i; + + if ( !item->IsShown() ) + continue; - if (item->IsShown()) + wxSize szMinPrev = item->GetMinSizeWithBorder(); + item->InformFirstDirection(m_orient^wxBOTH,totalMinorSize,delta); + wxSize szMin = item->GetMinSizeWithBorder(); + int deltaChange = GetSizeInMajorDir(szMin-szMinPrev); + if( deltaChange ) { - wxSize size( item->GetMinSizeWithBorder() ); + // 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; - 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; - } - wxPoint child_pos( pt ); - wxSize child_size( size.x, height ); + // might have a new delta now + delta = GetSizeInMajorDir(m_size) - GetSizeInMajorDir(m_minSize); - 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; + // the position at which we put the next child + wxPoint pt(m_position); - item->SetDimension( child_pos, child_size ); + int totalProportion = m_totalProportion; + for ( i = m_children.begin(); + i != m_children.end(); + ++i ) + { + wxSizerItem * const item = *i; - 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; - } + if ( !item->IsShown() ) + continue; - wxPoint child_pos( pt ); - wxSize child_size( width, size.y ); + const wxSize sizeThis(item->GetMinSizeWithBorder()); - 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; + // 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 ( m_containingWindow ) - { - child_pos.x = m_containingWindow->AdjustForLayoutDirection - ( - child_pos.x, - width, - m_size.x - ); - } + majorSize += deltaItem; - item->SetDimension( child_pos, child_size ); + delta -= deltaItem; + totalProportion -= propItem; + } - pt.x += width; - } + + // apply the alignment in the minor direction + wxPoint posChild(pt); + + 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; } - node = node->GetNext(); + + // apply RTL adjustment for horizontal sizers: + if ( !IsVertical() && m_containingWindow ) + { + posChild.x = m_containingWindow->AdjustForLayoutDirection + ( + posChild.x, + majorSize, + m_size.x + ); + } + + // 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(); - - m_stretchable = 0; - m_minWidth = 0; - m_minHeight = 0; - m_fixedWidth = 0; - m_fixedHeight = 0; + m_totalProportion = 0; + m_minSize = wxSize(0, 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 (m_orient == wxHORIZONTAL) + // 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); +} + + +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; } //--------------------------------------------------------------------------- @@ -1966,6 +2337,7 @@ void wxStdDialogButtonSizer::AddButton(wxButton *mybutton) m_buttonNegative = mybutton; break; case wxID_CANCEL: + case wxID_CLOSE: m_buttonCancel = mybutton; break; case wxID_HELP: @@ -2025,7 +2397,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")); } } @@ -2043,15 +2416,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__) @@ -2107,88 +2482,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 - -#if WXWIN_COMPATIBILITY_2_6 - -wxNotebookSizer::wxNotebookSizer(wxNotebook *nb) -{ - wxASSERT_MSG( nb, wxT("wxNotebookSizer needs a control") ); - m_bookctrl = nb; -} - -#endif // WXWIN_COMPATIBILITY_2_6 - -#endif // wxUSE_NOTEBOOOK -#endif // wxUSE_BOOKCTRL - -#endif // WXWIN_COMPATIBILITY_2_4