Files
wxWidgets/src/common/sizer.cpp
Dimitri Schoolwerth 4c51a665c6 Fixed various typos.
Applied patch by snowleopard2 fixing a bunch of typos such as misspellings and double words in the documentation. Combined the patch with some local queued typos waiting to be committed as well as adding new typo fixes inspired by the patch.

Function names with American spelling were not changed nor was third-party code touched. The only code changes involve some changes in strings that are translated ("Can not" -> "Cannot").

Closes #13063 (again).

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@67280 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2011-03-22 14:17:38 +00:00

2666 lines
74 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/sizer.cpp
// Purpose: provide new wxSizer class for layout
// Author: Robert Roebling and Robin Dunn, contributions by
// Dirk Holtwick, Ron Lee
// Modified by: Ron Lee
// Created:
// RCS-ID: $Id$
// Copyright: (c) Robin Dunn, Robert Roebling
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#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/display.h"
#include "wx/vector.h"
#include "wx/listimpl.cpp"
//---------------------------------------------------------------------------
IMPLEMENT_CLASS(wxSizerItem, wxObject)
IMPLEMENT_CLASS(wxSizer, wxObject)
IMPLEMENT_CLASS(wxGridSizer, wxSizer)
IMPLEMENT_CLASS(wxFlexGridSizer, wxGridSizer)
IMPLEMENT_CLASS(wxBoxSizer, wxSizer)
#if wxUSE_STATBOX
IMPLEMENT_CLASS(wxStaticBoxSizer, wxBoxSizer)
#endif
#if wxUSE_BUTTON
IMPLEMENT_CLASS(wxStdDialogButtonSizer, wxBoxSizer)
#endif
WX_DEFINE_EXPORTED_LIST( wxSizerItemList )
/*
TODO PROPERTIES
sizeritem
object
object_ref
minsize
option
flag
border
spacer
option
flag
borfder
boxsizer
orient
staticboxsizer
orient
label
gridsizer
rows
cols
vgap
hgap
flexgridsizer
rows
cols
vgap
hgap
growablerows
growablecols
minsize
*/
// ----------------------------------------------------------------------------
// 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();
m_proportion = flags.GetProportion();
m_flag = flags.GetFlags();
m_border = flags.GetBorderInPixels();
ASSERT_VALID_SIZER_FLAGS( m_flag );
}
wxSizerItem::wxSizerItem()
{
Init();
m_proportion = 0;
m_border = 0;
m_flag = 0;
m_id = wxID_NONE;
}
// window item
void wxSizerItem::DoSetWindow(wxWindow *window)
{
wxCHECK_RET( window, wxT("NULL window in wxSizerItem::SetWindow()") );
m_kind = Item_Window;
m_window = window;
// window doesn't become smaller than its initial size, whatever happens
m_minSize = window->GetSize();
if ( m_flag & wxFIXED_MINSIZE )
window->SetMinSize(m_minSize);
// aspect ratio calculated from initial size
SetRatio(m_minSize);
}
wxSizerItem::wxSizerItem(wxWindow *window,
int proportion,
int flag,
int border,
wxObject* userData)
: m_kind(Item_None),
m_proportion(proportion),
m_border(border),
m_flag(flag),
m_id(wxID_NONE),
m_userData(userData)
{
ASSERT_VALID_SIZER_FLAGS( m_flag );
DoSetWindow(window);
}
// sizer item
void wxSizerItem::DoSetSizer(wxSizer *sizer)
{
m_kind = Item_Sizer;
m_sizer = sizer;
}
wxSizerItem::wxSizerItem(wxSizer *sizer,
int proportion,
int flag,
int border,
wxObject* userData)
: 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)
{
ASSERT_VALID_SIZER_FLAGS( m_flag );
DoSetSizer(sizer);
// m_minSize is set later
}
// spacer item
void wxSizerItem::DoSetSpacer(const wxSize& size)
{
m_kind = Item_Spacer;
m_spacer = new wxSizerSpacer(size);
m_minSize = size;
SetRatio(size);
}
wxSizerItem::wxSizerItem(int width,
int height,
int proportion,
int flag,
int border,
wxObject* userData)
: 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)
{
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:
break;
case Item_Window:
m_window->SetContainingSizer(NULL);
break;
case Item_Sizer:
delete m_sizer;
break;
case Item_Spacer:
delete m_spacer;
break;
case Item_Max:
default:
wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
}
m_kind = Item_None;
}
wxSize wxSizerItem::GetSpacer() const
{
wxSize size;
if ( m_kind == Item_Spacer )
size = m_spacer->GetSize();
return size;
}
wxSize wxSizerItem::GetSize() const
{
wxSize ret;
switch ( m_kind )
{
case Item_None:
break;
case Item_Window:
ret = m_window->GetSize();
break;
case Item_Sizer:
ret = m_sizer->GetSize();
break;
case Item_Spacer:
ret = m_spacer->GetSize();
break;
case Item_Max:
default:
wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
}
if (m_flag & wxWEST)
ret.x += m_border;
if (m_flag & wxEAST)
ret.x += m_border;
if (m_flag & wxNORTH)
ret.y += m_border;
if (m_flag & wxSOUTH)
ret.y += m_border;
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, wxT("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())
{
m_minSize = m_sizer->GetMinSize();
// if we have to preserve aspect ratio _AND_ this is
// the first-time calculation, consider ret to be initial size
if ( (m_flag & wxSHAPED) && wxIsNullDouble(m_ratio) )
SetRatio(m_minSize);
}
else if ( IsWindow() )
{
// Since the size of the window may change during runtime, we
// should use the current minimal/best size.
m_minSize = m_window->GetEffectiveMinSize();
}
return GetMinSizeWithBorder();
}
wxSize wxSizerItem::GetMinSizeWithBorder() const
{
wxSize ret = m_minSize;
if (m_flag & wxWEST)
ret.x += m_border;
if (m_flag & wxEAST)
ret.x += m_border;
if (m_flag & wxNORTH)
ret.y += m_border;
if (m_flag & wxSOUTH)
ret.y += m_border;
return ret;
}
void wxSizerItem::SetDimension( const wxPoint& pos_, const wxSize& size_ )
{
wxPoint pos = pos_;
wxSize size = size_;
if (m_flag & wxSHAPED)
{
// adjust aspect ratio
int rwidth = (int) (size.y * m_ratio);
if (rwidth > size.x)
{
// fit horizontally
int rheight = (int) (size.x / m_ratio);
// add vertical space
if (m_flag & wxALIGN_CENTER_VERTICAL)
pos.y += (size.y - rheight) / 2;
else if (m_flag & wxALIGN_BOTTOM)
pos.y += (size.y - rheight);
// use reduced dimensions
size.y =rheight;
}
else if (rwidth < size.x)
{
// add horizontal space
if (m_flag & wxALIGN_CENTER_HORIZONTAL)
pos.x += (size.x - rwidth) / 2;
else if (m_flag & wxALIGN_RIGHT)
pos.x += (size.x - rwidth);
size.x = rwidth;
}
}
// This is what GetPosition() returns. Since we calculate
// borders afterwards, GetPosition() will be the left/top
// corner of the surrounding border.
m_pos = pos;
if (m_flag & wxWEST)
{
pos.x += m_border;
size.x -= m_border;
}
if (m_flag & wxEAST)
{
size.x -= m_border;
}
if (m_flag & wxNORTH)
{
pos.y += m_border;
size.y -= m_border;
}
if (m_flag & wxSOUTH)
{
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 )
{
case Item_None:
wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
break;
case Item_Window:
{
// Use wxSIZE_FORCE_EVENT here since a sizer item might
// have changed alignment or some other property which would
// not change the size of the window. In such a case, no
// wxSizeEvent would normally be generated and thus the
// control wouldn't get layed out correctly here.
#if 1
m_window->SetSize(pos.x, pos.y, size.x, size.y,
wxSIZE_ALLOW_MINUS_ONE|wxSIZE_FORCE_EVENT );
#else
m_window->SetSize(pos.x, pos.y, size.x, size.y,
wxSIZE_ALLOW_MINUS_ONE );
#endif
break;
}
case Item_Sizer:
m_sizer->SetDimension(pos, size);
break;
case Item_Spacer:
m_spacer->SetSize(size);
break;
case Item_Max:
default:
wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
}
}
void wxSizerItem::DeleteWindows()
{
switch ( m_kind )
{
case Item_None:
case Item_Spacer:
break;
case Item_Window:
//We are deleting the window from this sizer - normally
//the window destroys the sizer associated with it,
//which might destroy this, which we don't want
m_window->SetContainingSizer(NULL);
m_window->Destroy();
//Putting this after the switch will result in a spacer
//not being deleted properly on destruction
m_kind = Item_None;
break;
case Item_Sizer:
m_sizer->DeleteWindows();
break;
case Item_Max:
default:
wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
}
}
void wxSizerItem::Show( bool show )
{
switch ( m_kind )
{
case Item_None:
wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
break;
case Item_Window:
m_window->Show(show);
break;
case Item_Sizer:
m_sizer->Show(show);
break;
case Item_Spacer:
m_spacer->Show(show);
break;
case Item_Max:
default:
wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
}
}
bool wxSizerItem::IsShown() const
{
if ( m_flag & wxRESERVE_SPACE_EVEN_IF_HIDDEN )
return true;
switch ( m_kind )
{
case Item_None:
// we may be called from CalcMin(), just return false so that we're
// not used
break;
case Item_Window:
return m_window->IsShown();
case Item_Sizer:
{
// arbitrarily decide that if at least one of our elements is
// shown, so are we (this arbitrariness is the reason for
// deprecating this function)
for ( wxSizerItemList::compatibility_iterator
node = m_sizer->GetChildren().GetFirst();
node;
node = node->GetNext() )
{
if ( node->GetData()->IsShown() )
return true;
}
return false;
}
case Item_Spacer:
return m_spacer->IsShown();
case Item_Max:
default:
wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
}
return false;
}
#if WXWIN_COMPATIBILITY_2_6
void wxSizerItem::SetOption( int option )
{
SetProportion( option );
}
int wxSizerItem::GetOption() const
{
return GetProportion();
}
#endif // WXWIN_COMPATIBILITY_2_6
//---------------------------------------------------------------------------
// wxSizer
//---------------------------------------------------------------------------
wxSizer::~wxSizer()
{
WX_CLEAR_LIST(wxSizerItemList, m_children);
}
wxSizerItem* wxSizer::DoInsert( size_t index, wxSizerItem *item )
{
m_children.Insert( index, 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 )
{
return Detach( window );
}
#endif // WXWIN_COMPATIBILITY_2_6
bool wxSizer::Remove( wxSizer *sizer )
{
wxASSERT_MSG( sizer, wxT("Removing NULL sizer") );
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetSizer() == sizer)
{
delete item;
m_children.Erase( node );
return true;
}
node = node->GetNext();
}
return false;
}
bool wxSizer::Remove( int index )
{
wxCHECK_MSG( index >= 0 && (size_t)index < m_children.GetCount(),
false,
wxT("Remove index is out of range") );
wxSizerItemList::compatibility_iterator node = m_children.Item( index );
wxCHECK_MSG( node, false, wxT("Failed to find child node") );
delete node->GetData();
m_children.Erase( node );
return true;
}
bool wxSizer::Detach( wxSizer *sizer )
{
wxASSERT_MSG( sizer, wxT("Detaching NULL sizer") );
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetSizer() == sizer)
{
item->DetachSizer();
delete item;
m_children.Erase( node );
return true;
}
node = node->GetNext();
}
return false;
}
bool wxSizer::Detach( wxWindow *window )
{
wxASSERT_MSG( window, wxT("Detaching NULL window") );
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetWindow() == window)
{
delete item;
m_children.Erase( node );
return true;
}
node = node->GetNext();
}
return false;
}
bool wxSizer::Detach( int index )
{
wxCHECK_MSG( index >= 0 && (size_t)index < m_children.GetCount(),
false,
wxT("Detach index is out of range") );
wxSizerItemList::compatibility_iterator node = m_children.Item( index );
wxCHECK_MSG( node, false, wxT("Failed to find child node") );
wxSizerItem *item = node->GetData();
if ( item->IsSizer() )
item->DetachSizer();
delete item;
m_children.Erase( node );
return true;
}
bool wxSizer::Replace( wxWindow *oldwin, wxWindow *newwin, bool recursive )
{
wxASSERT_MSG( oldwin, wxT("Replacing NULL window") );
wxASSERT_MSG( newwin, wxT("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, wxT("Replacing NULL sizer") );
wxASSERT_MSG( newsz, wxT("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, wxT("Replace index is out of range") );
wxASSERT_MSG( newitem, wxT("Replacing with NULL item") );
wxSizerItemList::compatibility_iterator node = m_children.Item( old );
wxCHECK_MSG( node, false, wxT("Failed to find child node") );
wxSizerItem *item = node->GetData();
node->SetData(newitem);
if (item->IsWindow() && item->GetWindow())
item->GetWindow()->SetContainingSizer(NULL);
delete item;
return true;
}
void wxSizer::Clear( bool delete_windows )
{
// First clear the ContainingSizer pointers
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->IsWindow())
item->GetWindow()->SetContainingSizer( NULL );
node = node->GetNext();
}
// Destroy the windows if needed
if (delete_windows)
DeleteWindows();
// Now empty the list
WX_CLEAR_LIST(wxSizerItemList, m_children);
}
void wxSizer::DeleteWindows()
{
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
item->DeleteWindows();
node = node->GetNext();
}
}
wxSize wxSizer::ComputeFittingClientSize(wxWindow *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();
// 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;
if (window->IsTopLevel())
size = VirtualFitSize( window );
else
size = GetMinClientSize( window );
window->SetVirtualSize( size );
}
void wxSizer::Layout()
{
// (re)calculates minimums needed for each item and other preparations
// for layout
CalcMin();
// Applies the layout and repositions/resizes the items
RecalcSizes();
}
void wxSizer::SetSizeHints( wxWindow *window )
{
// Preserve the window's max size hints, but set the
// lower bound according to the sizer calculations.
// 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->SetMinClientSize(clientSize);
window->SetClientSize(clientSize);
}
#if WXWIN_COMPATIBILITY_2_8
void wxSizer::SetVirtualSizeHints( wxWindow *window )
{
FitInside( window );
}
#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
wxSize wxSizer::GetMaxClientSize( wxWindow *window ) const
{
return window->WindowToClientSize(window->GetMaxSize());
}
wxSize wxSizer::GetMinClientSize( wxWindow *WXUNUSED(window) )
{
return GetMinSize(); // Already returns client size.
}
wxSize wxSizer::VirtualFitSize( wxWindow *window )
{
wxSize size = GetMinClientSize( window );
wxSize sizeMax = GetMaxClientSize( 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::GetMinSize()
{
wxSize ret( CalcMin() );
if (ret.x < m_minSize.x) ret.x = m_minSize.x;
if (ret.y < m_minSize.y) ret.y = m_minSize.y;
return ret;
}
void wxSizer::DoSetMinSize( int width, int height )
{
m_minSize.x = width;
m_minSize.y = height;
}
bool wxSizer::DoSetItemMinSize( wxWindow *window, int width, int height )
{
wxASSERT_MSG( window, wxT("SetMinSize for NULL window") );
// Is it our immediate child?
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetWindow() == window)
{
item->SetMinSize( width, height );
return true;
}
node = node->GetNext();
}
// No? Search any subsizers we own then
node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if ( item->GetSizer() &&
item->GetSizer()->DoSetItemMinSize( window, width, height ) )
{
// A child sizer found the requested windw, exit.
return true;
}
node = node->GetNext();
}
return false;
}
bool wxSizer::DoSetItemMinSize( wxSizer *sizer, int width, int height )
{
wxASSERT_MSG( sizer, wxT("SetMinSize for NULL sizer") );
// Is it our immediate child?
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetSizer() == sizer)
{
item->GetSizer()->DoSetMinSize( width, height );
return true;
}
node = node->GetNext();
}
// No? Search any subsizers we own then
node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if ( item->GetSizer() &&
item->GetSizer()->DoSetItemMinSize( sizer, width, height ) )
{
// A child found the requested sizer, exit.
return true;
}
node = node->GetNext();
}
return false;
}
bool wxSizer::DoSetItemMinSize( size_t index, int width, int height )
{
wxSizerItemList::compatibility_iterator node = m_children.Item( index );
wxCHECK_MSG( node, false, wxT("Failed to find child node") );
wxSizerItem *item = node->GetData();
if (item->GetSizer())
{
// Sizers contains the minimal size in them, if not calculated ...
item->GetSizer()->DoSetMinSize( width, height );
}
else
{
// ... but the minimal size of spacers and windows is stored via the item
item->SetMinSize( width, height );
}
return true;
}
wxSizerItem* wxSizer::GetItem( wxWindow *window, bool recursive )
{
wxASSERT_MSG( window, wxT("GetItem for NULL window") );
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetWindow() == window)
{
return item;
}
else if (recursive && item->IsSizer())
{
wxSizerItem *subitem = item->GetSizer()->GetItem( window, true );
if (subitem)
return subitem;
}
node = node->GetNext();
}
return NULL;
}
wxSizerItem* wxSizer::GetItem( wxSizer *sizer, bool recursive )
{
wxASSERT_MSG( sizer, wxT("GetItem for NULL sizer") );
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetSizer() == sizer)
{
return item;
}
else if (recursive && item->IsSizer())
{
wxSizerItem *subitem = item->GetSizer()->GetItem( sizer, true );
if (subitem)
return subitem;
}
node = node->GetNext();
}
return NULL;
}
wxSizerItem* wxSizer::GetItem( size_t index )
{
wxCHECK_MSG( index < m_children.GetCount(),
NULL,
wxT("GetItem index is out of range") );
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 );
if ( item )
{
item->Show( show );
return true;
}
return false;
}
bool wxSizer::Show( wxSizer *sizer, bool show, bool recursive )
{
wxSizerItem *item = GetItem( sizer, recursive );
if ( item )
{
item->Show( show );
return true;
}
return false;
}
bool wxSizer::Show( size_t index, bool show)
{
wxSizerItem *item = GetItem( index );
if ( item )
{
item->Show( show );
return true;
}
return false;
}
void wxSizer::ShowItems( bool show )
{
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
node->GetData()->Show( show );
node = node->GetNext();
}
}
bool wxSizer::IsShown( wxWindow *window ) const
{
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetWindow() == window)
{
return item->IsShown();
}
node = node->GetNext();
}
wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
return false;
}
bool wxSizer::IsShown( wxSizer *sizer ) const
{
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
if (item->GetSizer() == sizer)
{
return item->IsShown();
}
node = node->GetNext();
}
wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
return false;
}
bool wxSizer::IsShown( size_t index ) const
{
wxCHECK_MSG( index < m_children.GetCount(),
false,
wxT("IsShown index is out of range") );
return m_children.Item( index )->GetData()->IsShown();
}
//---------------------------------------------------------------------------
// wxGridSizer
//---------------------------------------------------------------------------
wxGridSizer::wxGridSizer( int cols, int vgap, int hgap )
: m_rows( cols == 0 ? 1 : 0 ),
m_cols( cols ),
m_vgap( vgap ),
m_hgap( hgap )
{
wxASSERT(cols >= 0);
}
wxGridSizer::wxGridSizer( int cols, const wxSize& gap )
: m_rows( cols == 0 ? 1 : 0 ),
m_cols( cols ),
m_vgap( gap.GetHeight() ),
m_hgap( gap.GetWidth() )
{
wxASSERT(cols >= 0);
}
wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
: m_rows( rows || cols ? rows : 1 ),
m_cols( cols ),
m_vgap( vgap ),
m_hgap( hgap )
{
wxASSERT(rows >= 0 && cols >= 0);
}
wxGridSizer::wxGridSizer( int rows, int cols, const wxSize& gap )
: m_rows( rows || cols ? rows : 1 ),
m_cols( cols ),
m_vgap( gap.GetHeight() ),
m_hgap( gap.GetWidth() )
{
wxASSERT(rows >= 0 && cols >= 0);
}
wxSizerItem *wxGridSizer::DoInsert(size_t index, wxSizerItem *item)
{
// if only the number of columns or the number of rows is specified for a
// sizer, arbitrarily many items can be added to it but if both of them are
// fixed, then the sizer can't have more than that many items -- check for
// this here to ensure that we detect errors as soon as possible
if ( m_cols && m_rows )
{
const int nitems = m_children.GetCount();
if ( nitems == m_cols*m_rows )
{
wxFAIL_MSG(
wxString::Format(
"too many items (%d > %d*%d) in grid sizer (maybe you "
"should omit the number of either rows or columns?)",
nitems + 1, m_cols, m_rows)
);
// additionally, continuing to use the specified number of columns
// and rows is not a good idea as callers of CalcRowsCols() expect
// that all sizer items can fit into m_cols-/m_rows-sized arrays
// which is not the case if there are too many items and results in
// crashes, so let it compute the number of rows automatically by
// forgetting the (wrong) number of rows specified (this also has a
// nice side effect of giving only one assert even if there are
// many more items than allowed in this sizer)
m_rows = 0;
}
}
return wxSizer::DoInsert(index, item);
}
int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const
{
const int nitems = m_children.GetCount();
ncols = GetEffectiveColsCount();
nrows = GetEffectiveRowsCount();
// Since Insert() checks for overpopulation, the following
// should only assert if the grid was shrunk via SetRows() / SetCols()
wxASSERT_MSG( nitems <= ncols*nrows, "logic error in wxGridSizer" );
return nitems;
}
void wxGridSizer::RecalcSizes()
{
int nitems, nrows, ncols;
if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
return;
wxSize sz( GetSize() );
wxPoint pt( GetPosition() );
int w = (sz.x - (ncols - 1) * m_hgap) / ncols;
int h = (sz.y - (nrows - 1) * m_vgap) / nrows;
int x = pt.x;
for (int c = 0; c < ncols; c++)
{
int y = pt.y;
for (int r = 0; r < nrows; r++)
{
int i = r * ncols + c;
if (i < nitems)
{
wxSizerItemList::compatibility_iterator node = m_children.Item( i );
wxASSERT_MSG( node, wxT("Failed to find SizerItemList node") );
SetItemBounds( node->GetData(), x, y, w, h);
}
y = y + h + m_vgap;
}
x = x + w + m_hgap;
}
}
wxSize wxGridSizer::CalcMin()
{
int nrows, ncols;
if ( CalcRowsCols(nrows, ncols) == 0 )
return wxSize();
// Find the max width and height for any component
int w = 0;
int h = 0;
wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
while (node)
{
wxSizerItem *item = node->GetData();
wxSize sz( item->CalcMin() );
w = wxMax( w, sz.x );
h = wxMax( h, sz.y );
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 );
}
void wxGridSizer::SetItemBounds( wxSizerItem *item, int x, int y, int w, int h )
{
wxPoint pt( x,y );
wxSize sz( item->GetMinSizeWithBorder() );
int flag = item->GetFlag();
if ((flag & wxEXPAND) || (flag & wxSHAPED))
{
sz = wxSize(w, h);
}
else
{
if (flag & wxALIGN_CENTER_HORIZONTAL)
{
pt.x = x + (w - sz.x) / 2;
}
else if (flag & wxALIGN_RIGHT)
{
pt.x = x + (w - sz.x);
}
if (flag & wxALIGN_CENTER_VERTICAL)
{
pt.y = y + (h - sz.y) / 2;
}
else if (flag & wxALIGN_BOTTOM)
{
pt.y = y + (h - sz.y);
}
}
item->SetDimension(pt, sz);
}
//---------------------------------------------------------------------------
// wxFlexGridSizer
//---------------------------------------------------------------------------
wxFlexGridSizer::wxFlexGridSizer( int cols, int vgap, int hgap )
: wxGridSizer( cols, vgap, hgap ),
m_flexDirection(wxBOTH),
m_growMode(wxFLEX_GROWMODE_SPECIFIED)
{
}
wxFlexGridSizer::wxFlexGridSizer( int cols, const wxSize& gap )
: wxGridSizer( cols, gap ),
m_flexDirection(wxBOTH),
m_growMode(wxFLEX_GROWMODE_SPECIFIED)
{
}
wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, int vgap, int hgap )
: wxGridSizer( rows, cols, vgap, hgap ),
m_flexDirection(wxBOTH),
m_growMode(wxFLEX_GROWMODE_SPECIFIED)
{
}
wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, const wxSize& gap )
: wxGridSizer( rows, cols, gap ),
m_flexDirection(wxBOTH),
m_growMode(wxFLEX_GROWMODE_SPECIFIED)
{
}
wxFlexGridSizer::~wxFlexGridSizer()
{
}
void wxFlexGridSizer::RecalcSizes()
{
int nrows, ncols;
if ( !CalcRowsCols(nrows, ncols) )
return;
const wxPoint pt(GetPosition());
const wxSize sz(GetSize());
AdjustForGrowables(sz);
wxSizerItemList::const_iterator i = m_children.begin();
const wxSizerItemList::const_iterator end = m_children.end();
int y = 0;
for ( int r = 0; r < nrows; r++ )
{
if ( m_rowHeights[r] == -1 )
{
// this row is entirely hidden, skip it
for ( int c = 0; c < ncols; c++ )
{
if ( i == end )
return;
++i;
}
continue;
}
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 ( 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;
// Number of rows/columns can change as items are added or removed.
if ( !CalcRowsCols(nrows, ncols) )
return wxSize();
// 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);
for ( wxSizerItemList::iterator i = m_children.begin();
i != m_children.end();
++i)
{
wxSizerItem * const item = *i;
if ( item->IsShown() )
{
item->CalcMin();
}
}
// 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);
return m_calculatedMinSize;
}
void wxFlexGridSizer::AdjustForFlexDirection()
{
// the logic in CalcMin works when we resize flexibly in both directions
// but maybe this is not the case
if ( m_flexDirection != wxBOTH )
{
// select the array corresponding to the direction in which we do *not*
// resize flexibly
wxArrayInt& array = m_flexDirection == wxVERTICAL ? m_colWidths
: m_rowHeights;
const size_t count = array.GetCount();
// find the largest value in this array
size_t n;
int largest = 0;
for ( n = 0; n < count; ++n )
{
if ( array[n] > largest )
largest = array[n];
}
// and now fill it with the largest value
for ( n = 0; n < count; ++n )
{
// 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;
// total sum of proportions of all non-hidden rows
int sum_proportions = 0;
// 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++ )
{
// 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++;
}
if ( !num )
return;
// the remaining extra free space, adjusted during each iteration
for ( idx = 0; idx < count; idx++ )
{
if ( growable[idx] >= max_idx )
continue;
if ( sizes[ growable[idx] ] == -1 )
continue;
int cur_delta;
if ( sum_proportions == 0 )
{
// 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 wxDEBUG_LEVEL
// by the time this function is called, the sizer should be already fully
// initialized and hence the number of its columns and rows is known and we
// can check that all indices in m_growableCols/Rows are valid (see also
// comments in AddGrowableCol/Row())
if ( !m_rows || !m_cols )
{
if ( !m_rows )
{
int nrows = CalcRows();
for ( size_t n = 0; n < m_growableRows.size(); n++ )
{
wxASSERT_MSG( m_growableRows[n] < nrows,
"invalid growable row index" );
}
}
if ( !m_cols )
{
int ncols = CalcCols();
for ( size_t n = 0; n < m_growableCols.size(); n++ )
{
wxASSERT_MSG( m_growableCols[n] < ncols,
"invalid growable column index" );
}
}
}
#endif // wxDEBUG_LEVEL
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;
// Iterate over all items and inform about column width
const int ncols = GetEffectiveColsCount();
int col = 0;
for ( wxSizerItemList::iterator i = m_children.begin();
i != m_children.end();
++i )
{
didAdjustMinSize |= (*i)->InformFirstDirection(wxHORIZONTAL, m_colWidths[col], sz.y - m_calculatedMinSize.y);
if ( ++col == ncols )
col = 0;
}
// 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
);
}
}
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
);
}
}
bool wxFlexGridSizer::IsRowGrowable( size_t idx )
{
return m_growableRows.Index( idx ) != wxNOT_FOUND;
}
bool wxFlexGridSizer::IsColGrowable( size_t idx )
{
return m_growableCols.Index( idx ) != wxNOT_FOUND;
}
void wxFlexGridSizer::AddGrowableRow( size_t idx, int proportion )
{
wxASSERT_MSG( !IsRowGrowable( idx ),
"AddGrowableRow() called for growable row" );
// notice that we intentionally don't check the index validity here in (the
// common) case when the number of rows was not specified in the ctor -- in
// this case it will be computed only later, when all items are added to
// the sizer, and the check will be done in AdjustForGrowables()
wxCHECK_RET( !m_rows || idx < (size_t)m_rows, "invalid row index" );
m_growableRows.Add( idx );
m_growableRowsProportions.Add( proportion );
}
void wxFlexGridSizer::AddGrowableCol( size_t idx, int proportion )
{
wxASSERT_MSG( !IsColGrowable( idx ),
"AddGrowableCol() called for growable column" );
// see comment in AddGrowableRow(): although it's less common to omit the
// specification of the number of columns, it still can also happen
wxCHECK_RET( !m_cols || idx < (size_t)m_cols, "invalid column index" );
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( wxT("column/row is already not growable") );
}
void wxFlexGridSizer::RemoveGrowableCol( size_t idx )
{
DoRemoveFromArrays(idx, m_growableCols, m_growableColsProportions);
}
void wxFlexGridSizer::RemoveGrowableRow( size_t idx )
{
DoRemoveFromArrays(idx, m_growableRows, m_growableRowsProportions);
}
//---------------------------------------------------------------------------
// wxBoxSizer
//---------------------------------------------------------------------------
wxSizerItem *wxBoxSizer::AddSpacer(int size)
{
return IsVertical() ? Add(0, size) : Add(size, 0);
}
namespace
{
/*
Helper of RecalcSizes(): checks if there is enough remaining space for the
min size of the given item and returns its min size or the entire remaining
space depending on which one is greater.
This function updates the remaining space parameter to account for the size
effectively allocated to the item.
*/
int
GetMinOrRemainingSize(int orient, const wxSizerItem *item, int *remainingSpace_)
{
int& remainingSpace = *remainingSpace_;
wxCoord size;
if ( remainingSpace > 0 )
{
const wxSize sizeMin = item->GetMinSizeWithBorder();
size = orient == wxHORIZONTAL ? sizeMin.x : sizeMin.y;
if ( size >= remainingSpace )
{
// truncate the item to fit in the remaining space, this is better
// than showing it only partially in general, even if both choices
// are bad -- but there is nothing else we can do
size = remainingSpace;
}
remainingSpace -= size;
}
else // no remaining space
{
// no space at all left, no need to even query the item for its min
// size as we can't give it to it anyhow
size = 0;
}
return size;
}
} // anonymous namespace
void wxBoxSizer::RecalcSizes()
{
if ( m_children.empty() )
return;
const wxCoord totalMinorSize = GetSizeInMinorDir(m_size);
const wxCoord totalMajorSize = GetSizeInMajorDir(m_size);
// the amount of free space which we should redistribute among the
// stretchable items (i.e. those with non zero proportion)
int delta = totalMajorSize - GetSizeInMajorDir(m_minSize);
// declare loop variables used below:
wxSizerItemList::const_iterator i; // iterator in m_children list
unsigned n = 0; // item index in majorSizes array
// First, inform item about the available size in minor direction as this
// can change their size in the major direction. Also compute the number of
// visible items and sum of their min sizes in major direction.
int minMajorSize = 0;
for ( i = m_children.begin(); i != m_children.end(); ++i )
{
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;
}
minMajorSize += GetSizeInMajorDir(item->GetMinSizeWithBorder());
}
// update our min size and delta which may have changed
SizeInMajorDir(m_minSize) = minMajorSize;
delta = totalMajorSize - minMajorSize;
// space and sum of proportions for the remaining items, both may change
// below
wxCoord remaining = totalMajorSize;
int totalProportion = m_totalProportion;
// size of the (visible) items in major direction, -1 means "not fixed yet"
wxVector<int> majorSizes(GetItemCount(), wxDefaultCoord);
// Check for the degenerated case when we don't have enough space for even
// the min sizes of all the items: in this case we really can't do much
// more than to allocate the min size to as many of fixed size items as
// possible (on the assumption that variable size items such as text zones
// or list boxes may use scrollbars to show their content even if their
// size is less than min size but that fixed size items such as buttons
// will suffer even more if we don't give them their min size)
if ( totalMajorSize < minMajorSize )
{
// Second degenerated case pass: allocate min size to all fixed size
// items.
for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
{
wxSizerItem * const item = *i;
if ( !item->IsShown() )
continue;
// deal with fixed size items only during this pass
if ( item->GetProportion() )
continue;
majorSizes[n] = GetMinOrRemainingSize(m_orient, item, &remaining);
}
// Third degenerated case pass: allocate min size to all the remaining,
// i.e. non-fixed size, items.
for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
{
wxSizerItem * const item = *i;
if ( !item->IsShown() )
continue;
// we've already dealt with fixed size items above
if ( !item->GetProportion() )
continue;
majorSizes[n] = GetMinOrRemainingSize(m_orient, item, &remaining);
}
}
else // we do have enough space to give at least min sizes to all items
{
// Second and maybe more passes in the non-degenerated case: deal with
// fixed size items and items whose min size is greater than what we
// would allocate to them taking their proportion into account. For
// both of them, we will just use their min size, but for the latter we
// also need to reexamine all the items as the items which fitted
// before we adjusted their size upwards might not fit any more. This
// does make for a quadratic algorithm but it's not obvious how to
// avoid it and hopefully it's not a huge problem in practice as the
// sizers don't have many items usually (and, of course, the algorithm
// still reduces into a linear one if there is enough space for all the
// min sizes).
bool nonFixedSpaceChanged = false;
for ( i = m_children.begin(), n = 0; ; ++i, ++n )
{
if ( nonFixedSpaceChanged )
{
i = m_children.begin();
n = 0;
nonFixedSpaceChanged = false;
}
// check for the end of the loop only after the check above as
// otherwise we wouldn't do another pass if the last child resulted
// in non fixed space reduction
if ( i == m_children.end() )
break;
wxSizerItem * const item = *i;
if ( !item->IsShown() )
continue;
// don't check the item which we had already dealt with during a
// previous pass (this is more than an optimization, the code
// wouldn't work correctly if we kept adjusting for the same item
// over and over again)
if ( majorSizes[n] != wxDefaultCoord )
continue;
wxCoord minMajor = GetSizeInMajorDir(item->GetMinSizeWithBorder());
// it doesn't make sense for min size to be negative but right now
// it's possible to create e.g. a spacer with (-1, 10) as size and
// people do it in their code apparently (see #11842) so ensure
// that we don't use this -1 as real min size as it conflicts with
// the meaning we use for it here and negative min sizes just don't
// make sense anyhow (which is why it might be a better idea to
// deal with them at wxSizerItem level in the future but for now
// this is the minimal fix for the bug)
if ( minMajor < 0 )
minMajor = 0;
const int propItem = item->GetProportion();
if ( propItem )
{
// is the desired size of this item big enough?
if ( (remaining*propItem)/totalProportion >= minMajor )
{
// yes, it is, we'll determine the real size of this
// item later, for now just leave it as wxDefaultCoord
continue;
}
// the proportion of this item won't count, it has
// effectively become fixed
totalProportion -= propItem;
}
// we can already allocate space for this item
majorSizes[n] = minMajor;
// change the amount of the space remaining to the other items,
// as this can result in not being able to satisfy their
// proportions any more we will need to redo another loop
// iteration
remaining -= minMajor;
nonFixedSpaceChanged = true;
}
// Last by one pass: distribute the remaining space among the non-fixed
// items whose size weren't fixed yet according to their proportions.
for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
{
wxSizerItem * const item = *i;
if ( !item->IsShown() )
continue;
if ( majorSizes[n] == wxDefaultCoord )
{
const int propItem = item->GetProportion();
majorSizes[n] = (remaining*propItem)/totalProportion;
remaining -= majorSizes[n];
totalProportion -= propItem;
}
}
}
// the position at which we put the next child
wxPoint pt(m_position);
// Final pass: finally do position the items correctly using their sizes as
// determined above.
for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
{
wxSizerItem * const item = *i;
if ( !item->IsShown() )
continue;
const int majorSize = majorSizes[n];
const wxSize sizeThis(item->GetMinSizeWithBorder());
// 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) )
{
// occupy all the available space if wxEXPAND was given and also if
// the item is too big to fit -- in this case we truncate it below
// its minimal size which is bad but better than not showing parts
// of the window at all
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 | (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
: wxALIGN_CENTRE_VERTICAL)) )
{
PosInMinorDir(posChild) += (totalMinorSize - minorSize) / 2;
}
// 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()
{
m_totalProportion = 0;
m_minSize = wxSize(0, 0);
// The minimal size for the sizer should be big enough to allocate its
// element at least its minimal size but also, and this is the non trivial
// part, to respect the children proportion. To satisfy the latter
// condition we must find the greatest min-size-to-proportion ratio for all
// elements with non-zero proportion.
float maxMinSizeToProp = 0.;
for ( wxSizerItemList::const_iterator i = m_children.begin();
i != m_children.end();
++i )
{
wxSizerItem * const item = *i;
if ( !item->IsShown() )
continue;
const wxSize sizeMinThis = item->CalcMin();
if ( const int propThis = item->GetProportion() )
{
float minSizeToProp = GetSizeInMajorDir(sizeMinThis);
minSizeToProp /= propThis;
if ( minSizeToProp > maxMinSizeToProp )
maxMinSizeToProp = minSizeToProp;
m_totalProportion += item->GetProportion();
}
else // fixed size item
{
// Just account for its size directly
SizeInMajorDir(m_minSize) += GetSizeInMajorDir(sizeMinThis);
}
// In the transversal direction we just need to find the maximum.
if ( GetSizeInMinorDir(sizeMinThis) > GetSizeInMinorDir(m_minSize) )
SizeInMinorDir(m_minSize) = GetSizeInMinorDir(sizeMinThis);
}
// Using the max ratio ensures that the min size is big enough for all
// items to have their min size and satisfy the proportions among them.
SizeInMajorDir(m_minSize) += (int)(maxMinSizeToProp*m_totalProportion);
return m_minSize;
}
//---------------------------------------------------------------------------
// wxStaticBoxSizer
//---------------------------------------------------------------------------
#if wxUSE_STATBOX
wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient )
: 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;
}
void wxStaticBoxSizer::RecalcSizes()
{
int top_border, other_border;
m_staticBox->GetBordersForSizer(&top_border, &other_border);
m_staticBox->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
wxSize old_size( m_size );
m_size.x -= 2*other_border;
m_size.y -= top_border + other_border;
wxPoint old_pos( m_position );
if (m_staticBox->GetChildren().GetCount() > 0)
{
#if defined( __WXGTK20__ )
// if the wxStaticBox has created a wxPizza to contain its children
// (see wxStaticBox::AddChild) then we need to place the items it contains
// in the wxBoxSizer::RecalcSizes() call below using coordinates relative
// to the top-left corner of the staticbox:
m_position.x = m_position.y = 0;
#elif defined(__WXOSX__) && wxOSX_USE_COCOA
// the distance from the 'inner' content view to the embedded controls
m_position.x = m_position.y = 10;
#else
// if the wxStaticBox has childrens, then these windows must be placed
// by the wxBoxSizer::RecalcSizes() call below using coordinates relative
// to the top-left corner of the staticbox (but unlike wxGTK, we need
// to keep in count the static borders here!):
m_position.x = other_border;
m_position.y = top_border;
#endif
}
else
{
// the windows contained in the staticbox have been created as siblings of the
// staticbox (this is the "old" way of staticbox contents creation); in this
// case we need to position them with coordinates relative to our common parent
m_position.x += other_border;
m_position.y += top_border;
}
wxBoxSizer::RecalcSizes();
m_position = old_pos;
m_size = old_size;
}
wxSize wxStaticBoxSizer::CalcMin()
{
int top_border, other_border;
m_staticBox->GetBordersForSizer(&top_border, &other_border);
wxSize ret( wxBoxSizer::CalcMin() );
ret.x += 2*other_border;
// ensure that we're wide enough to show the static box label (there is no
// need to check for the static box best size in vertical direction though)
const int boxWidth = m_staticBox->GetBestSize().x;
if ( ret.x < boxWidth )
ret.x = boxWidth;
ret.y += other_border + top_border;
return ret;
}
void wxStaticBoxSizer::ShowItems( bool show )
{
m_staticBox->Show( 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
//---------------------------------------------------------------------------
// wxStdDialogButtonSizer
//---------------------------------------------------------------------------
#if wxUSE_BUTTON
wxStdDialogButtonSizer::wxStdDialogButtonSizer()
: wxBoxSizer(wxHORIZONTAL)
{
// Vertical buttons with lots of space on either side
// looks rubbish on WinCE, so let's not do this for now.
// If we are going to use vertical buttons, we should
// put the sizer to the right of other controls in the dialog,
// and that's beyond the scope of this sizer.
#ifndef __WXWINCE__
bool is_pda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
// If we have a PDA screen, put yes/no button over
// all other buttons, otherwise on the left side.
if (is_pda)
m_orient = wxVERTICAL;
#endif
m_buttonAffirmative = NULL;
m_buttonApply = NULL;
m_buttonNegative = NULL;
m_buttonCancel = NULL;
m_buttonHelp = NULL;
}
void wxStdDialogButtonSizer::AddButton(wxButton *mybutton)
{
switch (mybutton->GetId())
{
case wxID_OK:
case wxID_YES:
case wxID_SAVE:
m_buttonAffirmative = mybutton;
break;
case wxID_APPLY:
m_buttonApply = mybutton;
break;
case wxID_NO:
m_buttonNegative = mybutton;
break;
case wxID_CANCEL:
case wxID_CLOSE:
m_buttonCancel = mybutton;
break;
case wxID_HELP:
case wxID_CONTEXT_HELP:
m_buttonHelp = mybutton;
break;
default:
break;
}
}
void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton *button )
{
m_buttonAffirmative = button;
}
void wxStdDialogButtonSizer::SetNegativeButton( wxButton *button )
{
m_buttonNegative = button;
}
void wxStdDialogButtonSizer::SetCancelButton( wxButton *button )
{
m_buttonCancel = button;
}
void wxStdDialogButtonSizer::Realize()
{
#ifdef __WXMAC__
Add(0, 0, 0, wxLEFT, 6);
if (m_buttonHelp)
Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
if (m_buttonNegative){
// HIG POLICE BULLETIN - destructive buttons need extra padding
// 24 pixels on either side
Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 12);
}
// extra whitespace between help/negative and cancel/ok buttons
Add(0, 0, 1, wxEXPAND, 0);
if (m_buttonCancel){
Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
// Cancel or help should be default
// m_buttonCancel->SetDefaultButton();
}
// Ugh, Mac doesn't really have apply dialogs, so I'll just
// figure the best place is between Cancel and OK
if (m_buttonApply)
Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
if (m_buttonAffirmative){
Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6);
if (m_buttonAffirmative->GetId() == wxID_SAVE){
// these buttons have set labels under Mac so we should use them
m_buttonAffirmative->SetLabel(_("Save"));
if (m_buttonNegative)
m_buttonNegative->SetLabel(_("Don't Save"));
}
}
// Extra space around and at the right
Add(12, 40);
#elif defined(__WXGTK20__)
// http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
// says that the correct button order is
//
// [Help] [Alternative] [Cancel] [Affirmative]
// Flags ensuring that margins between the buttons are 6 pixels.
const wxSizerFlags
flagsBtn = wxSizerFlags().Centre().Border(wxLEFT | wxRIGHT, 3);
// Margin around the entire sizer button should be 12.
AddSpacer(9);
if (m_buttonHelp)
Add(m_buttonHelp, flagsBtn);
// Align the rest of the buttons to the right.
AddStretchSpacer();
if (m_buttonNegative)
Add(m_buttonNegative, flagsBtn);
if (m_buttonApply)
Add(m_buttonApply, flagsBtn);
if (m_buttonCancel)
Add(m_buttonCancel, flagsBtn);
if (m_buttonAffirmative)
Add(m_buttonAffirmative, flagsBtn);
// Ensure that the right margin is 12 as well.
AddSpacer(9);
#elif defined(__WXMSW__)
// Windows
// right-justify buttons
Add(0, 0, 1, wxEXPAND, 0);
if (m_buttonAffirmative){
Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(2, 0)).x);
}
if (m_buttonNegative){
Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(2, 0)).x);
}
if (m_buttonCancel){
Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(2, 0)).x);
}
if (m_buttonApply)
Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(2, 0)).x);
if (m_buttonHelp)
Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(2, 0)).x);
#else
// GTK+1 and any other platform
// Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
if (m_buttonHelp)
Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(4, 0)).x);
// extra whitespace between help and cancel/ok buttons
Add(0, 0, 1, wxEXPAND, 0);
if (m_buttonApply)
Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(4, 0)).x);
if (m_buttonAffirmative){
Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(4, 0)).x);
}
if (m_buttonNegative){
Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(4, 0)).x);
}
if (m_buttonCancel){
Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(4, 0)).x);
// Cancel or help should be default
// m_buttonCancel->SetDefaultButton();
}
#endif
}
#endif // wxUSE_BUTTON