]> git.saurik.com Git - wxWidgets.git/blame - src/common/sizer.cpp
Have to implement wxPGProperty::DeleteChildren() differently so that it will work...
[wxWidgets.git] / src / common / sizer.cpp
CommitLineData
5279a24d 1/////////////////////////////////////////////////////////////////////////////
ca3e85cf 2// Name: src/common/sizer.cpp
1044a386 3// Purpose: provide new wxSizer class for layout
aa5973ee
JS
4// Author: Robert Roebling and Robin Dunn, contributions by
5// Dirk Holtwick, Ron Lee
566d84a7 6// Modified by: Ron Lee
0c0d686f 7// Created:
5279a24d 8// RCS-ID: $Id$
aa5973ee 9// Copyright: (c) Robin Dunn, Robert Roebling
65571936 10// Licence: wxWindows licence
5279a24d
RR
11/////////////////////////////////////////////////////////////////////////////
12
77671fd2
VZ
13// For compilers that support precompilation, includes "wx.h".
14#include "wx/wxprec.h"
15
16#ifdef __BORLANDC__
17 #pragma hdrstop
18#endif
19
ed2fbeb8 20#include "wx/sizer.h"
df44dced 21#include "wx/private/flagscheck.h"
ed2fbeb8 22
0f769aab
WS
23#ifndef WX_PRECOMP
24 #include "wx/string.h"
25 #include "wx/intl.h"
2fb18bf4 26 #include "wx/math.h"
de6185e2 27 #include "wx/utils.h"
9eddec69 28 #include "wx/settings.h"
ca8d899f 29 #include "wx/button.h"
876cd6f7 30 #include "wx/statbox.h"
4107600f 31 #include "wx/toplevel.h"
0f769aab
WS
32#endif // WX_PRECOMP
33
b7bc9d80 34#include "wx/display.h"
c54b92d3 35#include "wx/listimpl.cpp"
88a7a4e1 36
5279a24d 37
0c0d686f
RD
38//---------------------------------------------------------------------------
39
9cbee2ce
RL
40IMPLEMENT_CLASS(wxSizerItem, wxObject)
41IMPLEMENT_CLASS(wxSizer, wxObject)
42IMPLEMENT_CLASS(wxGridSizer, wxSizer)
43IMPLEMENT_CLASS(wxFlexGridSizer, wxGridSizer)
44IMPLEMENT_CLASS(wxBoxSizer, wxSizer)
1e6feb95 45#if wxUSE_STATBOX
9cbee2ce 46IMPLEMENT_CLASS(wxStaticBoxSizer, wxBoxSizer)
1e6feb95 47#endif
974c2a59 48#if wxUSE_BUTTON
acf2ac37 49IMPLEMENT_CLASS(wxStdDialogButtonSizer, wxBoxSizer)
974c2a59 50#endif
0c0d686f 51
259c43f6 52WX_DEFINE_EXPORTED_LIST( wxSizerItemList )
12a3f227 53
066f1b7a 54/*
8b2bac62
WS
55 TODO PROPERTIES
56 sizeritem
57 object
58 object_ref
59 minsize
60 option
61 flag
62 border
066f1b7a 63 spacer
8b2bac62
WS
64 option
65 flag
66 borfder
67 boxsizer
68 orient
066f1b7a 69 staticboxsizer
8b2bac62
WS
70 orient
71 label
72 gridsizer
73 rows
74 cols
75 vgap
76 hgap
77 flexgridsizer
78 rows
79 cols
80 vgap
81 hgap
82 growablerows
83 growablecols
066f1b7a
SC
84 minsize
85*/
ccbc8038 86
50c06297 87// ----------------------------------------------------------------------------
3417c2cd 88// wxSizerItem
50c06297 89// ----------------------------------------------------------------------------
ccbc8038 90
df44dced
VS
91// check for flags conflicts
92static const int SIZER_FLAGS_MASK =
93 wxADD_FLAG(wxCENTRE,
94 wxADD_FLAG(wxHORIZONTAL,
95 wxADD_FLAG(wxVERTICAL,
96 wxADD_FLAG(wxLEFT,
97 wxADD_FLAG(wxRIGHT,
98 wxADD_FLAG(wxUP,
99 wxADD_FLAG(wxDOWN,
100 wxADD_FLAG(wxALIGN_NOT,
101 wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL,
102 wxADD_FLAG(wxALIGN_RIGHT,
103 wxADD_FLAG(wxALIGN_BOTTOM,
104 wxADD_FLAG(wxALIGN_CENTER_VERTICAL,
105 wxADD_FLAG(wxFIXED_MINSIZE,
106 wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN,
107 wxADD_FLAG(wxSTRETCH_NOT,
108 wxADD_FLAG(wxSHRINK,
109 wxADD_FLAG(wxGROW,
110 wxADD_FLAG(wxSHAPED,
111 0))))))))))))))))));
112
113#define ASSERT_VALID_SIZER_FLAGS(f) wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK)
114
115
ccbc8038
VZ
116void wxSizerItem::Init(const wxSizerFlags& flags)
117{
118 Init();
119
120 m_proportion = flags.GetProportion();
121 m_flag = flags.GetFlags();
122 m_border = flags.GetBorderInPixels();
df44dced
VS
123
124 ASSERT_VALID_SIZER_FLAGS( m_flag );
ccbc8038
VZ
125}
126
50c06297
VZ
127wxSizerItem::wxSizerItem()
128{
129 Init();
130
131 m_proportion = 0;
132 m_border = 0;
133 m_flag = 0;
86909f4c 134 m_id = wxID_NONE;
50c06297
VZ
135}
136
137// window item
4dd10327 138void wxSizerItem::DoSetWindow(wxWindow *window)
50c06297 139{
9a83f860 140 wxCHECK_RET( window, wxT("NULL window in wxSizerItem::SetWindow()") );
50c06297
VZ
141
142 m_kind = Item_Window;
143 m_window = window;
144
145 // window doesn't become smaller than its initial size, whatever happens
ba763a45 146 m_minSize = window->GetSize();
8b2bac62 147
50c06297
VZ
148 if ( m_flag & wxFIXED_MINSIZE )
149 window->SetMinSize(m_minSize);
150
36461f58 151 // aspect ratio calculated from initial size
50c06297
VZ
152 SetRatio(m_minSize);
153}
36461f58 154
50c06297
VZ
155wxSizerItem::wxSizerItem(wxWindow *window,
156 int proportion,
157 int flag,
158 int border,
159 wxObject* userData)
4dd10327
VZ
160 : m_kind(Item_None),
161 m_proportion(proportion),
50c06297
VZ
162 m_border(border),
163 m_flag(flag),
86909f4c 164 m_id(wxID_NONE),
50c06297
VZ
165 m_userData(userData)
166{
df44dced
VS
167 ASSERT_VALID_SIZER_FLAGS( m_flag );
168
4dd10327 169 DoSetWindow(window);
5279a24d
RR
170}
171
50c06297 172// sizer item
4dd10327 173void wxSizerItem::DoSetSizer(wxSizer *sizer)
5279a24d 174{
50c06297
VZ
175 m_kind = Item_Sizer;
176 m_sizer = sizer;
5279a24d
RR
177}
178
50c06297
VZ
179wxSizerItem::wxSizerItem(wxSizer *sizer,
180 int proportion,
181 int flag,
182 int border,
183 wxObject* userData)
4dd10327
VZ
184 : m_kind(Item_None),
185 m_sizer(NULL),
186 m_proportion(proportion),
50c06297
VZ
187 m_border(border),
188 m_flag(flag),
86909f4c 189 m_id(wxID_NONE),
50c06297
VZ
190 m_ratio(0.0),
191 m_userData(userData)
20b35a69 192{
df44dced
VS
193 ASSERT_VALID_SIZER_FLAGS( m_flag );
194
4dd10327 195 DoSetSizer(sizer);
ccbc8038 196
50c06297
VZ
197 // m_minSize is set later
198}
199
200// spacer item
4dd10327 201void wxSizerItem::DoSetSpacer(const wxSize& size)
50c06297
VZ
202{
203 m_kind = Item_Spacer;
204 m_spacer = new wxSizerSpacer(size);
205 m_minSize = size;
206 SetRatio(size);
207}
208
209wxSizerItem::wxSizerItem(int width,
210 int height,
211 int proportion,
212 int flag,
213 int border,
214 wxObject* userData)
4dd10327
VZ
215 : m_kind(Item_None),
216 m_sizer(NULL),
217 m_minSize(width, height), // minimal size is the initial size
50c06297
VZ
218 m_proportion(proportion),
219 m_border(border),
220 m_flag(flag),
86909f4c 221 m_id(wxID_NONE),
50c06297
VZ
222 m_userData(userData)
223{
df44dced
VS
224 ASSERT_VALID_SIZER_FLAGS( m_flag );
225
4dd10327 226 DoSetSpacer(wxSize(width, height));
20b35a69
RD
227}
228
0c0d686f
RD
229wxSizerItem::~wxSizerItem()
230{
f91e8382 231 delete m_userData;
4dd10327
VZ
232 Free();
233}
f91e8382 234
4dd10327
VZ
235void wxSizerItem::Free()
236{
50c06297 237 switch ( m_kind )
f91e8382 238 {
50c06297
VZ
239 case Item_None:
240 break;
241
242 case Item_Window:
243 m_window->SetContainingSizer(NULL);
244 break;
245
246 case Item_Sizer:
247 delete m_sizer;
248 break;
249
250 case Item_Spacer:
251 delete m_spacer;
252 break;
253
254 case Item_Max:
255 default:
9a83f860 256 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
f91e8382 257 }
4dd10327
VZ
258
259 m_kind = Item_None;
0c0d686f
RD
260}
261
50c06297
VZ
262wxSize wxSizerItem::GetSpacer() const
263{
264 wxSize size;
265 if ( m_kind == Item_Spacer )
266 size = m_spacer->GetSize();
267
268 return size;
269}
270
0c0d686f 271
9cbee2ce 272wxSize wxSizerItem::GetSize() const
5279a24d 273{
d597fcb7 274 wxSize ret;
50c06297
VZ
275 switch ( m_kind )
276 {
277 case Item_None:
278 break;
279
280 case Item_Window:
281 ret = m_window->GetSize();
282 break;
283
284 case Item_Sizer:
285 ret = m_sizer->GetSize();
286 break;
287
288 case Item_Spacer:
289 ret = m_spacer->GetSize();
290 break;
291
292 case Item_Max:
293 default:
9a83f860 294 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
50c06297 295 }
0c0d686f 296
d597fcb7
RR
297 if (m_flag & wxWEST)
298 ret.x += m_border;
299 if (m_flag & wxEAST)
300 ret.x += m_border;
301 if (m_flag & wxNORTH)
302 ret.y += m_border;
303 if (m_flag & wxSOUTH)
304 ret.y += m_border;
0c0d686f 305
d597fcb7 306 return ret;
5279a24d
RR
307}
308
15f7c305
RR
309bool wxSizerItem::InformFirstDirection(int direction, int size, int availableOtherDir)
310{
3d2085a4
VZ
311 // The size that come here will be including borders. Child items should get it
312 // without borders.
313 if( size>0 )
314 {
315 if( direction==wxHORIZONTAL )
316 {
317 if (m_flag & wxWEST)
318 size -= m_border;
319 if (m_flag & wxEAST)
320 size -= m_border;
321 }
322 else if( direction==wxVERTICAL )
323 {
324 if (m_flag & wxNORTH)
325 size -= m_border;
326 if (m_flag & wxSOUTH)
327 size -= m_border;
328 }
329 }
330
15f7c305
RR
331 bool didUse = false;
332 // Pass the information along to the held object
333 if (IsSizer())
334 {
335 didUse = GetSizer()->InformFirstDirection(direction,size,availableOtherDir);
336 if (didUse)
3d2085a4 337 m_minSize = GetSizer()->CalcMin();
15f7c305
RR
338 }
339 else if (IsWindow())
340 {
341 didUse = GetWindow()->InformFirstDirection(direction,size,availableOtherDir);
342 if (didUse)
343 m_minSize = m_window->GetEffectiveMinSize();
3d2085a4
VZ
344
345 // This information is useful for items with wxSHAPED flag, since
346 // we can request an optimal min size for such an item. Even if
347 // we overwrite the m_minSize member here, we can read it back from
348 // the owned window (happens automatically).
349 if( (m_flag & wxSHAPED) && (m_flag & wxEXPAND) && direction )
350 {
351 if( !wxIsNullDouble(m_ratio) )
352 {
9a83f860 353 wxCHECK_MSG( (m_proportion==0), false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
3d2085a4
VZ
354 if( direction==wxHORIZONTAL && !wxIsNullDouble(m_ratio) )
355 {
356 // Clip size so that we don't take too much
357 if( availableOtherDir>=0 && int(size/m_ratio)-m_minSize.y>availableOtherDir )
358 size = int((availableOtherDir+m_minSize.y)*m_ratio);
359 m_minSize = wxSize(size,int(size/m_ratio));
360 }
361 else if( direction==wxVERTICAL )
362 {
363 // Clip size so that we don't take too much
364 if( availableOtherDir>=0 && int(size*m_ratio)-m_minSize.x>availableOtherDir )
365 size = int((availableOtherDir+m_minSize.x)/m_ratio);
366 m_minSize = wxSize(int(size*m_ratio),size);
367 }
368 didUse = true;
369 }
370 }
371 }
372
15f7c305
RR
373 return didUse;
374}
375
3417c2cd 376wxSize wxSizerItem::CalcMin()
c62ac5b6 377{
3417c2cd 378 if (IsSizer())
be2577e4 379 {
ba763a45 380 m_minSize = m_sizer->GetMinSize();
d13d8d4e 381
be2577e4
RD
382 // if we have to preserve aspect ratio _AND_ this is
383 // the first-time calculation, consider ret to be initial size
2fb18bf4 384 if ( (m_flag & wxSHAPED) && wxIsNullDouble(m_ratio) )
36461f58 385 SetRatio(m_minSize);
be2577e4 386 }
ba763a45 387 else if ( IsWindow() )
d13d8d4e 388 {
ba763a45
RD
389 // Since the size of the window may change during runtime, we
390 // should use the current minimal/best size.
170acdc9 391 m_minSize = m_window->GetEffectiveMinSize();
d13d8d4e 392 }
0c0d686f 393
ba763a45
RD
394 return GetMinSizeWithBorder();
395}
396
397wxSize wxSizerItem::GetMinSizeWithBorder() const
398{
399 wxSize ret = m_minSize;
400
d597fcb7
RR
401 if (m_flag & wxWEST)
402 ret.x += m_border;
403 if (m_flag & wxEAST)
404 ret.x += m_border;
405 if (m_flag & wxNORTH)
406 ret.y += m_border;
407 if (m_flag & wxSOUTH)
408 ret.y += m_border;
8b2bac62 409
d597fcb7 410 return ret;
c62ac5b6
RR
411}
412
ba763a45 413
fbfb8bcc 414void wxSizerItem::SetDimension( const wxPoint& pos_, const wxSize& size_ )
c62ac5b6 415{
fbfb8bcc
VZ
416 wxPoint pos = pos_;
417 wxSize size = size_;
cdddaeea 418 if (m_flag & wxSHAPED)
d597fcb7 419 {
be2577e4
RD
420 // adjust aspect ratio
421 int rwidth = (int) (size.y * m_ratio);
cdddaeea
VZ
422 if (rwidth > size.x)
423 {
be2577e4
RD
424 // fit horizontally
425 int rheight = (int) (size.x / m_ratio);
426 // add vertical space
427 if (m_flag & wxALIGN_CENTER_VERTICAL)
428 pos.y += (size.y - rheight) / 2;
429 else if (m_flag & wxALIGN_BOTTOM)
430 pos.y += (size.y - rheight);
431 // use reduced dimensions
432 size.y =rheight;
cdddaeea
VZ
433 }
434 else if (rwidth < size.x)
435 {
be2577e4
RD
436 // add horizontal space
437 if (m_flag & wxALIGN_CENTER_HORIZONTAL)
438 pos.x += (size.x - rwidth) / 2;
439 else if (m_flag & wxALIGN_RIGHT)
440 pos.x += (size.x - rwidth);
441 size.x = rwidth;
442 }
443 }
33ac7e6f 444
cdddaeea
VZ
445 // This is what GetPosition() returns. Since we calculate
446 // borders afterwards, GetPosition() will be the left/top
447 // corner of the surrounding border.
448 m_pos = pos;
449
450 if (m_flag & wxWEST)
451 {
452 pos.x += m_border;
453 size.x -= m_border;
454 }
455 if (m_flag & wxEAST)
456 {
457 size.x -= m_border;
458 }
459 if (m_flag & wxNORTH)
460 {
461 pos.y += m_border;
462 size.y -= m_border;
463 }
464 if (m_flag & wxSOUTH)
465 {
466 size.y -= m_border;
467 }
0c0d686f 468
003b64a7
PC
469 if (size.x < 0)
470 size.x = 0;
471 if (size.y < 0)
472 size.y = 0;
473
50c06297 474 m_rect = wxRect(pos, size);
0c0d686f 475
50c06297
VZ
476 switch ( m_kind )
477 {
478 case Item_None:
9a83f860 479 wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
50c06297
VZ
480 break;
481
482 case Item_Window:
e47e063a 483 {
7499628f
RR
484 // Use wxSIZE_FORCE_EVENT here since a sizer item might
485 // have changed alignment or some other property which would
486 // not change the size of the window. In such a case, no
487 // wxSizeEvent would normally be generated and thus the
488 // control wouldn't get layed out correctly here.
c084a1ac 489#if 1
50c06297 490 m_window->SetSize(pos.x, pos.y, size.x, size.y,
e47e063a 491 wxSIZE_ALLOW_MINUS_ONE|wxSIZE_FORCE_EVENT );
491bb4ba
RR
492#else
493 m_window->SetSize(pos.x, pos.y, size.x, size.y,
494 wxSIZE_ALLOW_MINUS_ONE );
495#endif
50c06297 496 break;
e47e063a 497 }
50c06297 498 case Item_Sizer:
49dcc246 499 m_sizer->SetDimension(pos, size);
50c06297
VZ
500 break;
501
502 case Item_Spacer:
503 m_spacer->SetSize(size);
504 break;
505
506 case Item_Max:
507 default:
9a83f860 508 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
50c06297 509 }
c62ac5b6
RR
510}
511
84f7908b
RR
512void wxSizerItem::DeleteWindows()
513{
50c06297 514 switch ( m_kind )
77aa9abd 515 {
50c06297
VZ
516 case Item_None:
517 case Item_Spacer:
518 break;
519
520 case Item_Window:
caa2490c
RN
521 //We are deleting the window from this sizer - normally
522 //the window destroys the sizer associated with it,
523 //which might destroy this, which we don't want
524 m_window->SetContainingSizer(NULL);
50c06297 525 m_window->Destroy();
caa2490c
RN
526 //Putting this after the switch will result in a spacer
527 //not being deleted properly on destruction
902725ee 528 m_kind = Item_None;
50c06297
VZ
529 break;
530
531 case Item_Sizer:
532 m_sizer->DeleteWindows();
533 break;
534
535 case Item_Max:
536 default:
9a83f860 537 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
77aa9abd 538 }
be90c029 539
84f7908b
RR
540}
541
50c06297 542void wxSizerItem::Show( bool show )
5279a24d 543{
50c06297
VZ
544 switch ( m_kind )
545 {
546 case Item_None:
9a83f860 547 wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
50c06297 548 break;
5279a24d 549
50c06297
VZ
550 case Item_Window:
551 m_window->Show(show);
552 break;
5279a24d 553
50c06297 554 case Item_Sizer:
3a5910cb 555 m_sizer->Show(show);
50c06297
VZ
556 break;
557
558 case Item_Spacer:
559 m_spacer->Show(show);
560 break;
561
562 case Item_Max:
563 default:
9a83f860 564 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
50c06297 565 }
5279a24d
RR
566}
567
50c06297 568bool wxSizerItem::IsShown() const
12a3f227 569{
0ba6faae
VS
570 if ( m_flag & wxRESERVE_SPACE_EVEN_IF_HIDDEN )
571 return true;
572
50c06297
VZ
573 switch ( m_kind )
574 {
575 case Item_None:
f1662177
VZ
576 // we may be called from CalcMin(), just return false so that we're
577 // not used
50c06297
VZ
578 break;
579
580 case Item_Window:
581 return m_window->IsShown();
12a3f227 582
50c06297 583 case Item_Sizer:
f303d69f
VZ
584 // arbitrarily decide that if at least one of our elements is
585 // shown, so are we (this arbitrariness is the reason for
586 // deprecating this function)
587 {
29392c81
JS
588 // Some apps (such as dialog editors) depend on an empty sizer still
589 // being laid out correctly and reporting the correct size and position.
590 if (m_sizer->GetChildren().GetCount() == 0)
591 return true;
592
f303d69f
VZ
593 for ( wxSizerItemList::compatibility_iterator
594 node = m_sizer->GetChildren().GetFirst();
595 node;
596 node = node->GetNext() )
597 {
598 if ( node->GetData()->IsShown() )
599 return true;
600 }
601 }
602 return false;
12a3f227 603
50c06297
VZ
604 case Item_Spacer:
605 return m_spacer->IsShown();
606
607 case Item_Max:
608 default:
9a83f860 609 wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
50c06297
VZ
610 }
611
612 return false;
12a3f227
RL
613}
614
ca3e85cf 615#if WXWIN_COMPATIBILITY_2_6
12a3f227
RL
616void wxSizerItem::SetOption( int option )
617{
618 SetProportion( option );
619}
620
621int wxSizerItem::GetOption() const
622{
623 return GetProportion();
624}
ca3e85cf 625#endif // WXWIN_COMPATIBILITY_2_6
12a3f227
RL
626
627
5279a24d 628//---------------------------------------------------------------------------
3417c2cd 629// wxSizer
5279a24d
RR
630//---------------------------------------------------------------------------
631
3417c2cd 632wxSizer::~wxSizer()
5279a24d 633{
222ed1d6 634 WX_CLEAR_LIST(wxSizerItemList, m_children);
5279a24d 635}
0c0d686f 636
e556b612 637wxSizerItem* wxSizer::DoInsert( size_t index, wxSizerItem *item )
12a3f227
RL
638{
639 m_children.Insert( index, item );
0c0d686f 640
50c06297 641 if ( item->GetWindow() )
12a3f227 642 item->GetWindow()->SetContainingSizer( this );
56eee37f 643
e6cfcc0d
JS
644 if ( item->GetSizer() )
645 item->GetSizer()->SetContainingWindow( m_containingWindow );
646
56eee37f 647 return item;
12a3f227
RL
648}
649
e8cfff87
VZ
650void wxSizer::SetContainingWindow(wxWindow *win)
651{
652 if ( win == m_containingWindow )
653 return;
654
655 m_containingWindow = win;
656
657 // set the same window for all nested sizers as well, they also are in the
658 // same window
659 for ( wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
660 node;
661 node = node->GetNext() )
662 {
663 wxSizerItem *const item = node->GetData();
664 wxSizer *const sizer = item->GetSizer();
665
666 if ( sizer )
667 {
668 sizer->SetContainingWindow(win);
669 }
670 }
671}
672
ca3e85cf 673#if WXWIN_COMPATIBILITY_2_6
12a3f227
RL
674bool wxSizer::Remove( wxWindow *window )
675{
676 return Detach( window );
42b4e99e 677}
ca3e85cf 678#endif // WXWIN_COMPATIBILITY_2_6
42b4e99e
RR
679
680bool wxSizer::Remove( wxSizer *sizer )
681{
9a83f860 682 wxASSERT_MSG( sizer, wxT("Removing NULL sizer") );
0c0d686f 683
222ed1d6 684 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
42b4e99e
RR
685 while (node)
686 {
12a3f227
RL
687 wxSizerItem *item = node->GetData();
688
3ca6a5f0 689 if (item->GetSizer() == sizer)
222ed1d6
MB
690 {
691 delete item;
692 m_children.Erase( node );
693 return true;
694 }
12a3f227
RL
695
696 node = node->GetNext();
42b4e99e 697 }
0c0d686f 698
e0d8fb45 699 return false;
42b4e99e
RR
700}
701
e0d8fb45 702bool wxSizer::Remove( int index )
42b4e99e 703{
e0d8fb45
VZ
704 wxCHECK_MSG( index >= 0 && (size_t)index < m_children.GetCount(),
705 false,
9a83f860 706 wxT("Remove index is out of range") );
0c0d686f 707
222ed1d6 708 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
0c0d686f 709
9a83f860 710 wxCHECK_MSG( node, false, wxT("Failed to find child node") );
12a3f227 711
a50cf60e 712 delete node->GetData();
222ed1d6 713 m_children.Erase( node );
a50cf60e 714
222ed1d6 715 return true;
42b4e99e 716}
0c0d686f 717
00976fe5
RL
718bool wxSizer::Detach( wxSizer *sizer )
719{
9a83f860 720 wxASSERT_MSG( sizer, wxT("Detaching NULL sizer") );
00976fe5 721
222ed1d6 722 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
00976fe5
RL
723 while (node)
724 {
12a3f227
RL
725 wxSizerItem *item = node->GetData();
726
00976fe5
RL
727 if (item->GetSizer() == sizer)
728 {
96fdbb60 729 item->DetachSizer();
89c20ac1 730 delete item;
222ed1d6
MB
731 m_children.Erase( node );
732 return true;
12a3f227
RL
733 }
734 node = node->GetNext();
735 }
736
e0d8fb45 737 return false;
12a3f227
RL
738}
739
740bool wxSizer::Detach( wxWindow *window )
741{
9a83f860 742 wxASSERT_MSG( window, wxT("Detaching NULL window") );
12a3f227 743
222ed1d6 744 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
12a3f227
RL
745 while (node)
746 {
747 wxSizerItem *item = node->GetData();
748
749 if (item->GetWindow() == window)
750 {
89c20ac1 751 delete item;
222ed1d6
MB
752 m_children.Erase( node );
753 return true;
00976fe5 754 }
12a3f227 755 node = node->GetNext();
00976fe5
RL
756 }
757
e0d8fb45 758 return false;
00976fe5
RL
759}
760
e0d8fb45 761bool wxSizer::Detach( int index )
00976fe5 762{
e0d8fb45
VZ
763 wxCHECK_MSG( index >= 0 && (size_t)index < m_children.GetCount(),
764 false,
9a83f860 765 wxT("Detach index is out of range") );
12a3f227 766
222ed1d6 767 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
00976fe5 768
9a83f860 769 wxCHECK_MSG( node, false, wxT("Failed to find child node") );
00976fe5 770
e0d8fb45 771 wxSizerItem *item = node->GetData();
9cbee2ce 772
50c06297 773 if ( item->IsSizer() )
9cbee2ce 774 item->DetachSizer();
12a3f227 775
89c20ac1 776 delete item;
222ed1d6
MB
777 m_children.Erase( node );
778 return true;
00976fe5
RL
779}
780
eae0338f
RR
781bool wxSizer::Replace( wxWindow *oldwin, wxWindow *newwin, bool recursive )
782{
9a83f860
VZ
783 wxASSERT_MSG( oldwin, wxT("Replacing NULL window") );
784 wxASSERT_MSG( newwin, wxT("Replacing with NULL window") );
eae0338f
RR
785
786 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
787 while (node)
788 {
789 wxSizerItem *item = node->GetData();
790
791 if (item->GetWindow() == oldwin)
792 {
a50cf60e 793 item->AssignWindow(newwin);
eae0338f
RR
794 newwin->SetContainingSizer( this );
795 return true;
796 }
797 else if (recursive && item->IsSizer())
798 {
799 if (item->GetSizer()->Replace( oldwin, newwin, true ))
800 return true;
801 }
e8cfff87 802
eae0338f
RR
803 node = node->GetNext();
804 }
805
806 return false;
807}
808
809bool wxSizer::Replace( wxSizer *oldsz, wxSizer *newsz, bool recursive )
810{
9a83f860
VZ
811 wxASSERT_MSG( oldsz, wxT("Replacing NULL sizer") );
812 wxASSERT_MSG( newsz, wxT("Replacing with NULL sizer") );
eae0338f
RR
813
814 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
815 while (node)
816 {
817 wxSizerItem *item = node->GetData();
818
819 if (item->GetSizer() == oldsz)
820 {
a50cf60e 821 item->AssignSizer(newsz);
eae0338f
RR
822 return true;
823 }
824 else if (recursive && item->IsSizer())
825 {
826 if (item->GetSizer()->Replace( oldsz, newsz, true ))
827 return true;
e8cfff87
VZ
828 }
829
eae0338f
RR
830 node = node->GetNext();
831 }
832
833 return false;
834}
835
836bool wxSizer::Replace( size_t old, wxSizerItem *newitem )
837{
9a83f860
VZ
838 wxCHECK_MSG( old < m_children.GetCount(), false, wxT("Replace index is out of range") );
839 wxASSERT_MSG( newitem, wxT("Replacing with NULL item") );
eae0338f
RR
840
841 wxSizerItemList::compatibility_iterator node = m_children.Item( old );
842
9a83f860 843 wxCHECK_MSG( node, false, wxT("Failed to find child node") );
eae0338f
RR
844
845 wxSizerItem *item = node->GetData();
846 node->SetData(newitem);
e8cfff87 847 delete item;
eae0338f
RR
848
849 return true;
850}
851
84f7908b
RR
852void wxSizer::Clear( bool delete_windows )
853{
be90c029 854 // First clear the ContainingSizer pointers
222ed1d6 855 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
be90c029
RD
856 while (node)
857 {
12a3f227
RL
858 wxSizerItem *item = node->GetData();
859
be90c029 860 if (item->IsWindow())
12a3f227
RL
861 item->GetWindow()->SetContainingSizer( NULL );
862 node = node->GetNext();
be90c029
RD
863 }
864
865 // Destroy the windows if needed
84f7908b
RR
866 if (delete_windows)
867 DeleteWindows();
be90c029
RD
868
869 // Now empty the list
222ed1d6 870 WX_CLEAR_LIST(wxSizerItemList, m_children);
84f7908b
RR
871}
872
873void wxSizer::DeleteWindows()
874{
222ed1d6 875 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
84f7908b
RR
876 while (node)
877 {
12a3f227
RL
878 wxSizerItem *item = node->GetData();
879
84f7908b 880 item->DeleteWindows();
12a3f227 881 node = node->GetNext();
84f7908b
RR
882 }
883}
884
32013b47 885wxSize wxSizer::ComputeFittingClientSize(wxWindow *window)
5279a24d 886{
32013b47
VS
887 wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" );
888
98018a4b 889 // take the min size by default and limit it by max size
02dc0099 890 wxSize size = GetMinClientSize(window);
7e7bc14b 891 wxSize sizeMax;
98018a4b
VZ
892
893 wxTopLevelWindow *tlw = wxDynamicCast(window, wxTopLevelWindow);
894 if ( tlw )
895 {
896 // hack for small screen devices where TLWs are always full screen
897 if ( tlw->IsAlwaysMaximized() )
898 {
32013b47 899 return tlw->GetClientSize();
98018a4b 900 }
7e7bc14b 901
c30199bf
RR
902 // limit the window to the size of the display it is on
903 int disp = wxDisplay::GetFromWindow(window);
904 if ( disp == wxNOT_FOUND )
98018a4b 905 {
c30199bf
RR
906 // or, if we don't know which one it is, of the main one
907 disp = 0;
98018a4b 908 }
9ef2e675 909
7e7bc14b
VS
910 sizeMax = wxDisplay(disp).GetClientArea().GetSize();
911
c30199bf 912 // space for decorations and toolbars etc.
77fe7204 913 sizeMax = tlw->WindowToClientSize(sizeMax);
c30199bf
RR
914 }
915 else
916 {
7e7bc14b 917 sizeMax = GetMaxClientSize(window);
c30199bf 918 }
7e7bc14b
VS
919
920 if ( sizeMax.x != wxDefaultCoord && size.x > sizeMax.x )
921 size.x = sizeMax.x;
922 if ( sizeMax.y != wxDefaultCoord && size.y > sizeMax.y )
923 size.y = sizeMax.y;
924
32013b47
VS
925 return size;
926}
927
928wxSize wxSizer::ComputeFittingWindowSize(wxWindow *window)
929{
930 wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" );
931
932 return window->ClientToWindowSize(ComputeFittingClientSize(window));
933}
934
935wxSize wxSizer::Fit( wxWindow *window )
936{
937 wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" );
938
7e7bc14b 939 // set client size
32013b47 940 window->SetClientSize(ComputeFittingClientSize(window));
7e7bc14b
VS
941
942 // return entire size
943 return window->GetSize();
5279a24d
RR
944}
945
566d84a7
RL
946void wxSizer::FitInside( wxWindow *window )
947{
948 wxSize size;
949 if (window->IsTopLevel())
950 size = VirtualFitSize( window );
951 else
952 size = GetMinClientSize( window );
953
954 window->SetVirtualSize( size );
955}
956
3417c2cd 957void wxSizer::Layout()
c62ac5b6 958{
ba763a45
RD
959 // (re)calculates minimums needed for each item and other preparations
960 // for layout
42b4e99e 961 CalcMin();
ba763a45
RD
962
963 // Applies the layout and repositions/resizes the items
c62ac5b6
RR
964 RecalcSizes();
965}
966
3417c2cd 967void wxSizer::SetSizeHints( wxWindow *window )
5279a24d 968{
34c3ffca
RL
969 // Preserve the window's max size hints, but set the
970 // lower bound according to the sizer calculations.
971
428c07c0
VS
972 // This is equivalent to calling Fit(), except that we need to set
973 // the size hints _in between_ the two steps performed by Fit
974 // (1. ComputeFittingClientSize, 2. SetClientSize). That's because
975 // otherwise SetClientSize() could have no effect if there already are
976 // size hints in effect that forbid requested client size.
e5251d4f 977
428c07c0
VS
978 const wxSize clientSize = ComputeFittingClientSize(window);
979
980 window->SetMinClientSize(clientSize);
981 window->SetClientSize(clientSize);
5279a24d
RR
982}
983
f944aec0 984#if WXWIN_COMPATIBILITY_2_8
566d84a7
RL
985void wxSizer::SetVirtualSizeHints( wxWindow *window )
986{
566d84a7 987 FitInside( window );
566d84a7 988}
f944aec0 989#endif // WXWIN_COMPATIBILITY_2_8
566d84a7 990
e11d436b
SC
991// TODO on mac we need a function that determines how much free space this
992// min size contains, in order to make sure that we have 20 pixels of free
993// space around the controls
9cbee2ce 994wxSize wxSizer::GetMaxClientSize( wxWindow *window ) const
566d84a7 995{
77fe7204 996 return window->WindowToClientSize(window->GetMaxSize());
566d84a7
RL
997}
998
1b0674f7 999wxSize wxSizer::GetMinClientSize( wxWindow *WXUNUSED(window) )
566d84a7
RL
1000{
1001 return GetMinSize(); // Already returns client size.
1002}
1003
1004wxSize wxSizer::VirtualFitSize( wxWindow *window )
1005{
1006 wxSize size = GetMinClientSize( window );
1007 wxSize sizeMax = GetMaxClientSize( window );
1008
1009 // Limit the size if sizeMax != wxDefaultSize
1010
d775fa82 1011 if ( size.x > sizeMax.x && sizeMax.x != wxDefaultCoord )
566d84a7 1012 size.x = sizeMax.x;
d775fa82 1013 if ( size.y > sizeMax.y && sizeMax.y != wxDefaultCoord )
566d84a7
RL
1014 size.y = sizeMax.y;
1015
1016 return size;
1017}
1018
f6bcfd97 1019wxSize wxSizer::GetMinSize()
3ca6a5f0 1020{
f6bcfd97
BP
1021 wxSize ret( CalcMin() );
1022 if (ret.x < m_minSize.x) ret.x = m_minSize.x;
1023 if (ret.y < m_minSize.y) ret.y = m_minSize.y;
3ca6a5f0 1024 return ret;
f6bcfd97
BP
1025}
1026
1027void wxSizer::DoSetMinSize( int width, int height )
1028{
1029 m_minSize.x = width;
1030 m_minSize.y = height;
1031}
1032
1033bool wxSizer::DoSetItemMinSize( wxWindow *window, int width, int height )
1034{
9a83f860 1035 wxASSERT_MSG( window, wxT("SetMinSize for NULL window") );
12a3f227
RL
1036
1037 // Is it our immediate child?
f6bcfd97 1038
222ed1d6 1039 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
f6bcfd97
BP
1040 while (node)
1041 {
12a3f227
RL
1042 wxSizerItem *item = node->GetData();
1043
3ca6a5f0
BP
1044 if (item->GetWindow() == window)
1045 {
1eba2193 1046 item->SetMinSize( width, height );
e0d8fb45 1047 return true;
3ca6a5f0 1048 }
12a3f227 1049 node = node->GetNext();
f6bcfd97
BP
1050 }
1051
12a3f227
RL
1052 // No? Search any subsizers we own then
1053
1054 node = m_children.GetFirst();
f6bcfd97
BP
1055 while (node)
1056 {
12a3f227
RL
1057 wxSizerItem *item = node->GetData();
1058
1059 if ( item->GetSizer() &&
1060 item->GetSizer()->DoSetItemMinSize( window, width, height ) )
3ca6a5f0 1061 {
12a3f227 1062 // A child sizer found the requested windw, exit.
e0d8fb45 1063 return true;
3ca6a5f0 1064 }
12a3f227 1065 node = node->GetNext();
f6bcfd97
BP
1066 }
1067
e0d8fb45 1068 return false;
f6bcfd97
BP
1069}
1070
1071bool wxSizer::DoSetItemMinSize( wxSizer *sizer, int width, int height )
1072{
9a83f860 1073 wxASSERT_MSG( sizer, wxT("SetMinSize for NULL sizer") );
f6bcfd97 1074
12a3f227
RL
1075 // Is it our immediate child?
1076
222ed1d6 1077 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
f6bcfd97
BP
1078 while (node)
1079 {
12a3f227
RL
1080 wxSizerItem *item = node->GetData();
1081
3ca6a5f0
BP
1082 if (item->GetSizer() == sizer)
1083 {
f6bcfd97 1084 item->GetSizer()->DoSetMinSize( width, height );
e0d8fb45 1085 return true;
3ca6a5f0 1086 }
12a3f227 1087 node = node->GetNext();
f6bcfd97
BP
1088 }
1089
12a3f227
RL
1090 // No? Search any subsizers we own then
1091
1092 node = m_children.GetFirst();
f6bcfd97
BP
1093 while (node)
1094 {
12a3f227
RL
1095 wxSizerItem *item = node->GetData();
1096
1097 if ( item->GetSizer() &&
1098 item->GetSizer()->DoSetItemMinSize( sizer, width, height ) )
3ca6a5f0 1099 {
12a3f227 1100 // A child found the requested sizer, exit.
e0d8fb45 1101 return true;
3ca6a5f0 1102 }
12a3f227 1103 node = node->GetNext();
f6bcfd97
BP
1104 }
1105
e0d8fb45 1106 return false;
f6bcfd97
BP
1107}
1108
12a3f227 1109bool wxSizer::DoSetItemMinSize( size_t index, int width, int height )
f6bcfd97 1110{
222ed1d6 1111 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
12a3f227 1112
9a83f860 1113 wxCHECK_MSG( node, false, wxT("Failed to find child node") );
12a3f227
RL
1114
1115 wxSizerItem *item = node->GetData();
f6bcfd97 1116
f6bcfd97
BP
1117 if (item->GetSizer())
1118 {
0ca5105b 1119 // Sizers contains the minimal size in them, if not calculated ...
f6bcfd97
BP
1120 item->GetSizer()->DoSetMinSize( width, height );
1121 }
1122 else
1123 {
ba763a45 1124 // ... but the minimal size of spacers and windows is stored via the item
1eba2193 1125 item->SetMinSize( width, height );
f6bcfd97
BP
1126 }
1127
e0d8fb45 1128 return true;
f6bcfd97
BP
1129}
1130
9f13661f 1131wxSizerItem* wxSizer::GetItem( wxWindow *window, bool recursive )
2b5f62a0 1132{
9a83f860 1133 wxASSERT_MSG( window, wxT("GetItem for NULL window") );
12a3f227 1134
222ed1d6 1135 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
2b5f62a0
VZ
1136 while (node)
1137 {
12a3f227 1138 wxSizerItem *item = node->GetData();
2b5f62a0 1139
12a3f227 1140 if (item->GetWindow() == window)
2b5f62a0 1141 {
9f13661f 1142 return item;
2b5f62a0 1143 }
8b2bac62
WS
1144 else if (recursive && item->IsSizer())
1145 {
9f13661f
WS
1146 wxSizerItem *subitem = item->GetSizer()->GetItem( window, true );
1147 if (subitem)
1148 return subitem;
8b2bac62
WS
1149 }
1150
12a3f227 1151 node = node->GetNext();
2b5f62a0 1152 }
8b2bac62 1153
9f13661f 1154 return NULL;
2b5f62a0
VZ
1155}
1156
9f13661f 1157wxSizerItem* wxSizer::GetItem( wxSizer *sizer, bool recursive )
2b5f62a0 1158{
9a83f860 1159 wxASSERT_MSG( sizer, wxT("GetItem for NULL sizer") );
12a3f227 1160
222ed1d6 1161 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
2b5f62a0
VZ
1162 while (node)
1163 {
9f13661f 1164 wxSizerItem *item = node->GetData();
2b5f62a0 1165
12a3f227 1166 if (item->GetSizer() == sizer)
2b5f62a0 1167 {
9f13661f 1168 return item;
2b5f62a0 1169 }
8b2bac62
WS
1170 else if (recursive && item->IsSizer())
1171 {
9f13661f
WS
1172 wxSizerItem *subitem = item->GetSizer()->GetItem( sizer, true );
1173 if (subitem)
1174 return subitem;
8b2bac62
WS
1175 }
1176
12a3f227 1177 node = node->GetNext();
2b5f62a0 1178 }
8b2bac62 1179
9f13661f
WS
1180 return NULL;
1181}
1182
1183wxSizerItem* wxSizer::GetItem( size_t index )
1184{
1185 wxCHECK_MSG( index < m_children.GetCount(),
1186 NULL,
9a83f860 1187 wxT("GetItem index is out of range") );
9f13661f
WS
1188
1189 return m_children.Item( index )->GetData();
1190}
1191
86909f4c
VZ
1192wxSizerItem* wxSizer::GetItemById( int id, bool recursive )
1193{
1194 // This gets a sizer item by the id of the sizer item
1195 // and NOT the id of a window if the item is a window.
1196
1197 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1198 while (node)
1199 {
1200 wxSizerItem *item = node->GetData();
1201
1202 if (item->GetId() == id)
1203 {
1204 return item;
1205 }
1206 else if (recursive && item->IsSizer())
1207 {
1208 wxSizerItem *subitem = item->GetSizer()->GetItemById( id, true );
1209 if (subitem)
1210 return subitem;
1211 }
1212
1213 node = node->GetNext();
1214 }
1215
1216 return NULL;
1217}
1218
9f13661f
WS
1219bool wxSizer::Show( wxWindow *window, bool show, bool recursive )
1220{
1221 wxSizerItem *item = GetItem( window, recursive );
1222
1223 if ( item )
1224 {
1225 item->Show( show );
1226 return true;
1227 }
1228
1229 return false;
1230}
1231
1232bool wxSizer::Show( wxSizer *sizer, bool show, bool recursive )
1233{
1234 wxSizerItem *item = GetItem( sizer, recursive );
1235
1236 if ( item )
1237 {
1238 item->Show( show );
1239 return true;
1240 }
1241
8b2bac62 1242 return false;
2b5f62a0
VZ
1243}
1244
8b2bac62 1245bool wxSizer::Show( size_t index, bool show)
2b5f62a0 1246{
9f13661f 1247 wxSizerItem *item = GetItem( index );
2b5f62a0 1248
9f13661f
WS
1249 if ( item )
1250 {
1251 item->Show( show );
1252 return true;
1253 }
8b2bac62 1254
9f13661f 1255 return false;
12a3f227 1256}
2b5f62a0 1257
12a3f227
RL
1258void wxSizer::ShowItems( bool show )
1259{
222ed1d6 1260 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
12a3f227
RL
1261 while (node)
1262 {
1263 node->GetData()->Show( show );
1264 node = node->GetNext();
2b5f62a0
VZ
1265 }
1266}
1267
9cbee2ce 1268bool wxSizer::IsShown( wxWindow *window ) const
2b5f62a0 1269{
222ed1d6 1270 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
2b5f62a0
VZ
1271 while (node)
1272 {
12a3f227 1273 wxSizerItem *item = node->GetData();
dc259b79 1274
12a3f227 1275 if (item->GetWindow() == window)
2b5f62a0
VZ
1276 {
1277 return item->IsShown();
1278 }
12a3f227 1279 node = node->GetNext();
2b5f62a0
VZ
1280 }
1281
9a83f860 1282 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
12a3f227 1283
e0d8fb45 1284 return false;
2b5f62a0
VZ
1285}
1286
9cbee2ce 1287bool wxSizer::IsShown( wxSizer *sizer ) const
2b5f62a0 1288{
222ed1d6 1289 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
2b5f62a0
VZ
1290 while (node)
1291 {
12a3f227 1292 wxSizerItem *item = node->GetData();
2b5f62a0 1293
12a3f227 1294 if (item->GetSizer() == sizer)
2b5f62a0
VZ
1295 {
1296 return item->IsShown();
1297 }
12a3f227 1298 node = node->GetNext();
2b5f62a0
VZ
1299 }
1300
9a83f860 1301 wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
12a3f227 1302
e0d8fb45 1303 return false;
2b5f62a0
VZ
1304}
1305
9cbee2ce 1306bool wxSizer::IsShown( size_t index ) const
12a3f227
RL
1307{
1308 wxCHECK_MSG( index < m_children.GetCount(),
e0d8fb45 1309 false,
9a83f860 1310 wxT("IsShown index is out of range") );
12a3f227
RL
1311
1312 return m_children.Item( index )->GetData()->IsShown();
1313}
1314
1315
f6bcfd97
BP
1316//---------------------------------------------------------------------------
1317// wxGridSizer
1318//---------------------------------------------------------------------------
1319
4a00e77c
VZ
1320wxGridSizer::wxGridSizer( int cols, int vgap, int hgap )
1321 : m_rows( cols == 0 ? 1 : 0 ),
7e6edd27
VZ
1322 m_cols( cols ),
1323 m_vgap( vgap ),
1324 m_hgap( hgap )
1325{
1326}
1327
4a00e77c
VZ
1328wxGridSizer::wxGridSizer( int cols, const wxSize& gap )
1329 : m_rows( cols == 0 ? 1 : 0 ),
7e6edd27
VZ
1330 m_cols( cols ),
1331 m_vgap( gap.GetHeight() ),
1332 m_hgap( gap.GetWidth() )
f6bcfd97 1333{
f6bcfd97
BP
1334}
1335
4a00e77c
VZ
1336wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
1337 : m_rows( rows || cols ? rows : 1 ),
7e6edd27
VZ
1338 m_cols( cols ),
1339 m_vgap( vgap ),
1340 m_hgap( hgap )
1341{
1342}
1343
4a00e77c
VZ
1344wxGridSizer::wxGridSizer( int rows, int cols, const wxSize& gap )
1345 : m_rows( rows || cols ? rows : 1 ),
7e6edd27
VZ
1346 m_cols( cols ),
1347 m_vgap( gap.GetHeight() ),
1348 m_hgap( gap.GetWidth() )
f6bcfd97 1349{
f6bcfd97
BP
1350}
1351
e556b612 1352wxSizerItem *wxGridSizer::DoInsert(size_t index, wxSizerItem *item)
8b492f67
VZ
1353{
1354 // if only the number of columns or the number of rows is specified for a
1355 // sizer, arbitrarily many items can be added to it but if both of them are
1356 // fixed, then the sizer can't have more than that many items -- check for
1357 // this here to ensure that we detect errors as soon as possible
1358 if ( m_cols && m_rows )
1359 {
2d5fccc6
VZ
1360 const int nitems = m_children.GetCount();
1361 if ( nitems == m_cols*m_rows )
8b492f67 1362 {
2d5fccc6
VZ
1363 wxFAIL_MSG(
1364 wxString::Format(
1365 "too many items (%d > %d*%d) in grid sizer (maybe you "
1366 "should omit the number of either rows or columns?)",
1367 nitems + 1, m_cols, m_rows)
1368 );
8b492f67
VZ
1369
1370 // additionally, continuing to use the specified number of columns
1371 // and rows is not a good idea as callers of CalcRowsCols() expect
0274a797 1372 // that all sizer items can fit into m_cols-/m_rows-sized arrays
8b492f67
VZ
1373 // which is not the case if there are too many items and results in
1374 // crashes, so let it compute the number of rows automatically by
1375 // forgetting the (wrong) number of rows specified (this also has a
1376 // nice side effect of giving only one assert even if there are
1377 // many more items than allowed in this sizer)
1378 m_rows = 0;
1379 }
1380 }
1381
e556b612 1382 return wxSizer::DoInsert(index, item);
8b492f67
VZ
1383}
1384
0ca5105b 1385int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const
f6bcfd97 1386{
5fa51d09 1387 const int nitems = m_children.GetCount();
2283800b 1388
0274a797
VZ
1389 ncols = GetEffectiveColsCount();
1390 nrows = GetEffectiveRowsCount();
f6bcfd97 1391
0274a797
VZ
1392 // Since Insert() checks for overpopulation, the following
1393 // should only assert if the grid was shrunk via SetRows() / SetCols()
1394 wxASSERT_MSG( nitems <= ncols*nrows, "logic error in wxGridSizer" );
0ca5105b
VZ
1395
1396 return nitems;
1397}
1398
1399void wxGridSizer::RecalcSizes()
1400{
1401 int nitems, nrows, ncols;
1402 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
1403 return;
f6bcfd97
BP
1404
1405 wxSize sz( GetSize() );
1406 wxPoint pt( GetPosition() );
3ca6a5f0
BP
1407
1408 int w = (sz.x - (ncols - 1) * m_hgap) / ncols;
1409 int h = (sz.y - (nrows - 1) * m_vgap) / nrows;
f6bcfd97
BP
1410
1411 int x = pt.x;
1412 for (int c = 0; c < ncols; c++)
1413 {
1414 int y = pt.y;
1415 for (int r = 0; r < nrows; r++)
1416 {
1417 int i = r * ncols + c;
1418 if (i < nitems)
1419 {
222ed1d6 1420 wxSizerItemList::compatibility_iterator node = m_children.Item( i );
12a3f227 1421
9a83f860 1422 wxASSERT_MSG( node, wxT("Failed to find SizerItemList node") );
3ca6a5f0 1423
12a3f227 1424 SetItemBounds( node->GetData(), x, y, w, h);
f6bcfd97
BP
1425 }
1426 y = y + h + m_vgap;
1427 }
1428 x = x + w + m_hgap;
1429 }
1430}
1431
1432wxSize wxGridSizer::CalcMin()
1433{
196be0f1
JS
1434 int nrows, ncols;
1435 if ( CalcRowsCols(nrows, ncols) == 0 )
b3f1734f 1436 return wxSize();
f6bcfd97 1437
4f469fb5 1438 // Find the max width and height for any component
f6bcfd97
BP
1439 int w = 0;
1440 int h = 0;
3ca6a5f0 1441
222ed1d6 1442 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
f6bcfd97
BP
1443 while (node)
1444 {
12a3f227
RL
1445 wxSizerItem *item = node->GetData();
1446 wxSize sz( item->CalcMin() );
1447
f6bcfd97
BP
1448 w = wxMax( w, sz.x );
1449 h = wxMax( h, sz.y );
3ca6a5f0 1450
12a3f227 1451 node = node->GetNext();
f6bcfd97 1452 }
3ca6a5f0 1453
3d2085a4 1454 // In case we have a nested sizer with a two step algo , give it
15f7c305
RR
1455 // a chance to adjust to that (we give it width component)
1456 node = m_children.GetFirst();
1457 bool didChangeMinSize = false;
1458 while (node)
1459 {
1460 wxSizerItem *item = node->GetData();
1461 didChangeMinSize |= item->InformFirstDirection( wxHORIZONTAL, w, -1 );
3d2085a4 1462
15f7c305
RR
1463 node = node->GetNext();
1464 }
3d2085a4 1465
15f7c305
RR
1466 // And redo iteration in case min size changed
1467 if( didChangeMinSize )
1468 {
1469 node = m_children.GetFirst();
1470 w = h = 0;
1471 while (node)
1472 {
1473 wxSizerItem *item = node->GetData();
1474 wxSize sz( item->GetMinSizeWithBorder() );
1475
1476 w = wxMax( w, sz.x );
1477 h = wxMax( h, sz.y );
1478
1479 node = node->GetNext();
3d2085a4 1480 }
15f7c305 1481 }
3d2085a4 1482
12a3f227
RL
1483 return wxSize( ncols * w + (ncols-1) * m_hgap,
1484 nrows * h + (nrows-1) * m_vgap );
f6bcfd97
BP
1485}
1486
1487void wxGridSizer::SetItemBounds( wxSizerItem *item, int x, int y, int w, int h )
1488{
1489 wxPoint pt( x,y );
8b2bac62 1490 wxSize sz( item->GetMinSizeWithBorder() );
f6bcfd97
BP
1491 int flag = item->GetFlag();
1492
1493 if ((flag & wxEXPAND) || (flag & wxSHAPED))
1494 {
1495 sz = wxSize(w, h);
1496 }
1497 else
1498 {
1499 if (flag & wxALIGN_CENTER_HORIZONTAL)
1500 {
559b747d 1501 pt.x = x + (w - sz.x) / 2;
f6bcfd97
BP
1502 }
1503 else if (flag & wxALIGN_RIGHT)
1504 {
559b747d 1505 pt.x = x + (w - sz.x);
f6bcfd97 1506 }
3ca6a5f0 1507
f6bcfd97
BP
1508 if (flag & wxALIGN_CENTER_VERTICAL)
1509 {
559b747d 1510 pt.y = y + (h - sz.y) / 2;
f6bcfd97
BP
1511 }
1512 else if (flag & wxALIGN_BOTTOM)
1513 {
559b747d 1514 pt.y = y + (h - sz.y);
f6bcfd97
BP
1515 }
1516 }
3ca6a5f0 1517
f6bcfd97
BP
1518 item->SetDimension(pt, sz);
1519}
1520
1521//---------------------------------------------------------------------------
1522// wxFlexGridSizer
1523//---------------------------------------------------------------------------
1524
4a00e77c
VZ
1525wxFlexGridSizer::wxFlexGridSizer( int cols, int vgap, int hgap )
1526 : wxGridSizer( cols, vgap, hgap ),
1527 m_flexDirection(wxBOTH),
1528 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1529{
1530}
1531
1532wxFlexGridSizer::wxFlexGridSizer( int cols, const wxSize& gap )
1533 : wxGridSizer( cols, gap ),
1534 m_flexDirection(wxBOTH),
1535 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1536{
1537}
1538
f6bcfd97 1539wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, int vgap, int hgap )
5d76f462
VZ
1540 : wxGridSizer( rows, cols, vgap, hgap ),
1541 m_flexDirection(wxBOTH),
1542 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
3ca6a5f0 1543{
f6bcfd97
BP
1544}
1545
4a00e77c
VZ
1546wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, const wxSize& gap )
1547 : wxGridSizer( rows, cols, gap ),
5d76f462
VZ
1548 m_flexDirection(wxBOTH),
1549 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
3ca6a5f0 1550{
f6bcfd97 1551}
3ca6a5f0 1552
f6bcfd97
BP
1553wxFlexGridSizer::~wxFlexGridSizer()
1554{
f6bcfd97
BP
1555}
1556
1557void wxFlexGridSizer::RecalcSizes()
1558{
74ab5f5b
VZ
1559 int nrows, ncols;
1560 if ( !CalcRowsCols(nrows, ncols) )
f6bcfd97
BP
1561 return;
1562
97800f66
VZ
1563 const wxPoint pt(GetPosition());
1564 const wxSize sz(GetSize());
3ca6a5f0 1565
97800f66 1566 AdjustForGrowables(sz);
f6bcfd97 1567
97800f66 1568 wxSizerItemList::const_iterator i = m_children.begin();
cc67d082
VZ
1569 const wxSizerItemList::const_iterator end = m_children.end();
1570
97800f66
VZ
1571 int y = 0;
1572 for ( int r = 0; r < nrows; r++ )
f6bcfd97 1573 {
97800f66 1574 if ( m_rowHeights[r] == -1 )
f6bcfd97 1575 {
97800f66
VZ
1576 // this row is entirely hidden, skip it
1577 for ( int c = 0; c < ncols; c++ )
cc67d082
VZ
1578 {
1579 if ( i == end )
1580 return;
1581
97800f66 1582 ++i;
cc67d082 1583 }
12a3f227 1584
97800f66
VZ
1585 continue;
1586 }
3ca6a5f0 1587
97800f66
VZ
1588 const int hrow = m_rowHeights[r];
1589 int h = sz.y - y; // max remaining height, don't overflow it
1590 if ( hrow < h )
1591 h = hrow;
3ca6a5f0 1592
97800f66 1593 int x = 0;
cc67d082 1594 for ( int c = 0; c < ncols && i != end; c++, ++i )
97800f66
VZ
1595 {
1596 const int wcol = m_colWidths[c];
1597
1598 if ( wcol == -1 )
1599 continue;
1600
97800f66
VZ
1601 int w = sz.x - x; // max possible value, ensure we don't overflow
1602 if ( wcol < w )
1603 w = wcol;
1604
1605 SetItemBounds(*i, pt.x + x, pt.y + y, w, h);
1606
1607 x += wcol + m_hgap;
f6bcfd97 1608 }
97800f66 1609
cc67d082
VZ
1610 if ( i == end )
1611 return;
1612
97800f66 1613 y += hrow + m_vgap;
f6bcfd97
BP
1614 }
1615}
1616
97800f66
VZ
1617// helper function used in CalcMin() to sum up the sizes of non-hidden items
1618static int SumArraySizes(const wxArrayInt& sizes, int gap)
1619{
1620 // Sum total minimum size, including gaps between rows/columns.
1621 // -1 is used as a magic number meaning empty row/column.
1622 int total = 0;
1623
1624 const size_t count = sizes.size();
1625 for ( size_t n = 0; n < count; n++ )
1626 {
1627 if ( sizes[n] != -1 )
1628 {
1629 if ( total )
1630 total += gap; // separate from the previous column
1631
1632 total += sizes[n];
1633 }
1634 }
1635
1636 return total;
1637}
1638
15f7c305 1639void wxFlexGridSizer::FindWidthsAndHeights(int nrows, int ncols)
f6bcfd97 1640{
97800f66 1641 // We have to recalculate the sizes in case the item minimum size has
395a82b1
VZ
1642 // changed since the previous layout, or the item has been hidden using
1643 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1644 // dimension of the row/column will be -1, indicating that the column
1645 // itself is hidden.
97800f66
VZ
1646 m_rowHeights.assign(nrows, -1);
1647 m_colWidths.assign(ncols, -1);
1648
1649 // n is the index of the item in left-to-right top-to-bottom order
1650 size_t n = 0;
1651 for ( wxSizerItemList::iterator i = m_children.begin();
1652 i != m_children.end();
1653 ++i, ++n )
f6bcfd97 1654 {
97800f66 1655 wxSizerItem * const item = *i;
55f9f0cb
VZ
1656 if ( item->IsShown() )
1657 {
3d2085a4 1658 // NOTE: Not doing the calculation here, this is just
15f7c305
RR
1659 // for finding max values.
1660 const wxSize sz(item->GetMinSizeWithBorder());
12a3f227 1661
97800f66
VZ
1662 const int row = n / ncols;
1663 const int col = n % ncols;
3ca6a5f0 1664
97800f66
VZ
1665 if ( sz.y > m_rowHeights[row] )
1666 m_rowHeights[row] = sz.y;
1667 if ( sz.x > m_colWidths[col] )
1668 m_colWidths[col] = sz.x;
1669 }
f6bcfd97 1670 }
3ca6a5f0 1671
20b35a69 1672 AdjustForFlexDirection();
8b2bac62 1673
97800f66
VZ
1674 m_calculatedMinSize = wxSize(SumArraySizes(m_colWidths, m_hgap),
1675 SumArraySizes(m_rowHeights, m_vgap));
15f7c305
RR
1676}
1677
1678wxSize wxFlexGridSizer::CalcMin()
1679{
1680 int nrows,
1681 ncols;
1682
1683 // Number of rows/columns can change as items are added or removed.
1684 if ( !CalcRowsCols(nrows, ncols) )
1685 return wxSize();
1686
1687
1688 // We have to recalculate the sizes in case the item minimum size has
1689 // changed since the previous layout, or the item has been hidden using
1690 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1691 // dimension of the row/column will be -1, indicating that the column
1692 // itself is hidden.
1693 m_rowHeights.assign(nrows, -1);
1694 m_colWidths.assign(ncols, -1);
1695
15f7c305
RR
1696 for ( wxSizerItemList::iterator i = m_children.begin();
1697 i != m_children.end();
b7bc9d80 1698 ++i)
15f7c305
RR
1699 {
1700 wxSizerItem * const item = *i;
1701 if ( item->IsShown() )
1702 {
1703 item->CalcMin();
1704 }
1705 }
1706
3d2085a4 1707 // The stage of looking for max values in each row/column has been
15f7c305
RR
1708 // made a separate function, since it's reused in AdjustForGrowables.
1709 FindWidthsAndHeights(nrows,ncols);
97800f66 1710
ba763a45 1711 return m_calculatedMinSize;
20b35a69
RD
1712}
1713
1714void wxFlexGridSizer::AdjustForFlexDirection()
1715{
1716 // the logic in CalcMin works when we resize flexibly in both directions
1717 // but maybe this is not the case
5d76f462
VZ
1718 if ( m_flexDirection != wxBOTH )
1719 {
1720 // select the array corresponding to the direction in which we do *not*
1721 // resize flexibly
1722 wxArrayInt& array = m_flexDirection == wxVERTICAL ? m_colWidths
1723 : m_rowHeights;
1724
4a10ea8b 1725 const size_t count = array.GetCount();
5d76f462
VZ
1726
1727 // find the largest value in this array
4a10ea8b
MW
1728 size_t n;
1729 int largest = 0;
1730
5d76f462
VZ
1731 for ( n = 0; n < count; ++n )
1732 {
1733 if ( array[n] > largest )
1734 largest = array[n];
1735 }
1736
1737 // and now fill it with the largest value
1738 for ( n = 0; n < count; ++n )
1739 {
b9a325a1
VZ
1740 // don't touch hidden rows
1741 if ( array[n] != -1 )
1742 array[n] = largest;
5d76f462
VZ
1743 }
1744 }
8b2bac62 1745}
5d76f462 1746
97800f66
VZ
1747// helper of AdjustForGrowables() which is called for rows/columns separately
1748//
1749// parameters:
1750// delta: the extra space, we do nothing unless it's positive
1751// growable: indices or growable rows/cols in sizes array
1752// sizes: the height/widths of rows/cols to adjust
1753// proportions: proportions of the growable rows/cols or NULL if they all
1754// should be assumed to have proportion of 1
1755static void
1756DoAdjustForGrowables(int delta,
1757 const wxArrayInt& growable,
1758 wxArrayInt& sizes,
1759 const wxArrayInt *proportions)
1760{
1761 if ( delta <= 0 )
1762 return;
3ca6a5f0 1763
97800f66
VZ
1764 // total sum of proportions of all non-hidden rows
1765 int sum_proportions = 0;
8b2bac62 1766
97800f66
VZ
1767 // number of currently shown growable rows
1768 int num = 0;
3ca6a5f0 1769
97800f66
VZ
1770 const int max_idx = sizes.size();
1771
1772 const size_t count = growable.size();
1773 size_t idx;
1774 for ( idx = 0; idx < count; idx++ )
20b35a69 1775 {
97800f66
VZ
1776 // Since the number of rows/columns can change as items are
1777 // inserted/deleted, we need to verify at runtime that the
1778 // requested growable rows/columns are still valid.
1779 if ( growable[idx] >= max_idx )
1780 continue;
1781
1782 // If all items in a row/column are hidden, that row/column will
1783 // have a dimension of -1. This causes the row/column to be
1784 // hidden completely.
1785 if ( sizes[growable[idx]] == -1 )
1786 continue;
1787
1788 if ( proportions )
1789 sum_proportions += (*proportions)[idx];
1790
1791 num++;
20b35a69
RD
1792 }
1793
97800f66
VZ
1794 if ( !num )
1795 return;
1796
1797 // the remaining extra free space, adjusted during each iteration
1798 for ( idx = 0; idx < count; idx++ )
20b35a69 1799 {
97800f66
VZ
1800 if ( growable[idx] >= max_idx )
1801 continue;
8b2bac62 1802
97800f66
VZ
1803 if ( sizes[ growable[idx] ] == -1 )
1804 continue;
20b35a69 1805
97800f66
VZ
1806 int cur_delta;
1807 if ( sum_proportions == 0 )
20b35a69 1808 {
97800f66
VZ
1809 // no growable rows -- divide extra space evenly among all
1810 cur_delta = delta/num;
1811 num--;
20b35a69 1812 }
97800f66
VZ
1813 else // allocate extra space proportionally
1814 {
1815 const int cur_prop = (*proportions)[idx];
1816 cur_delta = (delta*cur_prop)/sum_proportions;
1817 sum_proportions -= cur_prop;
1818 }
1819
1820 sizes[growable[idx]] += cur_delta;
1821 delta -= cur_delta;
20b35a69 1822 }
97800f66
VZ
1823}
1824
1825void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz)
1826{
4b6a582b 1827#if wxDEBUG_LEVEL
4e4e06e3
VZ
1828 // by the time this function is called, the sizer should be already fully
1829 // initialized and hence the number of its columns and rows is known and we
1830 // can check that all indices in m_growableCols/Rows are valid (see also
1831 // comments in AddGrowableCol/Row())
1832 if ( !m_rows || !m_cols )
1833 {
4e4e06e3
VZ
1834 if ( !m_rows )
1835 {
0274a797
VZ
1836 int nrows = CalcRows();
1837
4e4e06e3
VZ
1838 for ( size_t n = 0; n < m_growableRows.size(); n++ )
1839 {
1840 wxASSERT_MSG( m_growableRows[n] < nrows,
1841 "invalid growable row index" );
1842 }
1843 }
1844
1845 if ( !m_cols )
1846 {
0274a797
VZ
1847 int ncols = CalcCols();
1848
4e4e06e3
VZ
1849 for ( size_t n = 0; n < m_growableCols.size(); n++ )
1850 {
1851 wxASSERT_MSG( m_growableCols[n] < ncols,
1852 "invalid growable column index" );
1853 }
1854 }
1855 }
4b6a582b 1856#endif // wxDEBUG_LEVEL
4e4e06e3
VZ
1857
1858
15f7c305 1859 if ( (m_flexDirection & wxHORIZONTAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
97800f66 1860 {
97800f66
VZ
1861 DoAdjustForGrowables
1862 (
15f7c305
RR
1863 sz.x - m_calculatedMinSize.x,
1864 m_growableCols,
1865 m_colWidths,
1866 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
97800f66
VZ
1867 : NULL
1868 );
3d2085a4
VZ
1869
1870 // This gives nested objects that benefit from knowing one size
1871 // component in advance the chance to use that.
15f7c305 1872 bool didAdjustMinSize = false;
3d2085a4 1873
15f7c305 1874 // Iterate over all items and inform about column width
0274a797
VZ
1875 const int ncols = GetEffectiveColsCount();
1876 int col = 0;
15f7c305
RR
1877 for ( wxSizerItemList::iterator i = m_children.begin();
1878 i != m_children.end();
0274a797 1879 ++i )
15f7c305 1880 {
15f7c305 1881 didAdjustMinSize |= (*i)->InformFirstDirection(wxHORIZONTAL, m_colWidths[col], sz.y - m_calculatedMinSize.y);
0274a797
VZ
1882 if ( ++col == ncols )
1883 col = 0;
b7bc9d80 1884 }
97800f66 1885
3d2085a4 1886 // Only redo if info was actually used
15f7c305 1887 if( didAdjustMinSize )
b7bc9d80
PC
1888 {
1889 DoAdjustForGrowables
1890 (
1891 sz.x - m_calculatedMinSize.x,
1892 m_growableCols,
1893 m_colWidths,
1894 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
1895 : NULL
1896 );
1897 }
20b35a69 1898 }
f6bcfd97 1899
15f7c305
RR
1900 if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
1901 {
1902 // pass NULL instead of proportions if the grow mode is ALL as we
1903 // should treat all rows as having proportion of 1 then
1904 DoAdjustForGrowables
1905 (
1906 sz.y - m_calculatedMinSize.y,
1907 m_growableRows,
1908 m_rowHeights,
1909 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableRowsProportions
1910 : NULL
1911 );
1912 }
1913}
1914
67ef83eb
VZ
1915bool wxFlexGridSizer::IsRowGrowable( size_t idx )
1916{
1917 return m_growableRows.Index( idx ) != wxNOT_FOUND;
1918}
1919
1920bool wxFlexGridSizer::IsColGrowable( size_t idx )
1921{
1922 return m_growableCols.Index( idx ) != wxNOT_FOUND;
1923}
20b35a69 1924
e8800dcf 1925void wxFlexGridSizer::AddGrowableRow( size_t idx, int proportion )
f6bcfd97 1926{
ef52f19e 1927 wxASSERT_MSG( !IsRowGrowable( idx ),
67ef83eb 1928 "AddGrowableRow() called for growable row" );
4e4e06e3
VZ
1929
1930 // notice that we intentionally don't check the index validity here in (the
1931 // common) case when the number of rows was not specified in the ctor -- in
1932 // this case it will be computed only later, when all items are added to
1933 // the sizer, and the check will be done in AdjustForGrowables()
1934 wxCHECK_RET( !m_rows || idx < (size_t)m_rows, "invalid row index" );
1935
f6bcfd97 1936 m_growableRows.Add( idx );
e8800dcf 1937 m_growableRowsProportions.Add( proportion );
f6bcfd97
BP
1938}
1939
e8800dcf 1940void wxFlexGridSizer::AddGrowableCol( size_t idx, int proportion )
f6bcfd97 1941{
ef52f19e 1942 wxASSERT_MSG( !IsColGrowable( idx ),
67ef83eb 1943 "AddGrowableCol() called for growable column" );
4e4e06e3
VZ
1944
1945 // see comment in AddGrowableRow(): although it's less common to omit the
1946 // specification of the number of columns, it still can also happen
0274a797 1947 wxCHECK_RET( !m_cols || idx < (size_t)m_cols, "invalid column index" );
4e4e06e3 1948
f6bcfd97 1949 m_growableCols.Add( idx );
e8800dcf 1950 m_growableColsProportions.Add( proportion );
f6bcfd97
BP
1951}
1952
ca243008
VZ
1953// helper function for RemoveGrowableCol/Row()
1954static void
1955DoRemoveFromArrays(size_t idx, wxArrayInt& items, wxArrayInt& proportions)
1956{
1957 const size_t count = items.size();
1958 for ( size_t n = 0; n < count; n++ )
1959 {
1960 if ( (size_t)items[n] == idx )
1961 {
1962 items.RemoveAt(n);
1963 proportions.RemoveAt(n);
1964 return;
1965 }
1966 }
1967
9a83f860 1968 wxFAIL_MSG( wxT("column/row is already not growable") );
ca243008
VZ
1969}
1970
8d2474f4 1971void wxFlexGridSizer::RemoveGrowableCol( size_t idx )
f6bcfd97 1972{
ca243008
VZ
1973 DoRemoveFromArrays(idx, m_growableCols, m_growableColsProportions);
1974}
1975
1976void wxFlexGridSizer::RemoveGrowableRow( size_t idx )
1977{
1978 DoRemoveFromArrays(idx, m_growableRows, m_growableRowsProportions);
f6bcfd97
BP
1979}
1980
c62ac5b6 1981//---------------------------------------------------------------------------
92afa2b1 1982// wxBoxSizer
61d514bb
RR
1983//---------------------------------------------------------------------------
1984
1a2df6a7
VZ
1985wxSizerItem *wxBoxSizer::AddSpacer(int size)
1986{
1987 return IsVertical() ? Add(0, size) : Add(size, 0);
1988}
1989
92afa2b1 1990void wxBoxSizer::RecalcSizes()
61d514bb 1991{
89064717 1992 if ( m_children.empty() )
61d514bb 1993 return;
0c0d686f 1994
3d2085a4 1995 const wxCoord totalMinorSize = GetSizeInMinorDir(m_size);
15f7c305 1996
89064717
VZ
1997 // the amount of free space which we should redistribute among the
1998 // stretchable items (i.e. those with non zero proportion)
3d2085a4
VZ
1999 int delta = GetSizeInMajorDir(m_size) - GetSizeInMajorDir(m_minSize);
2000
0c0d686f 2001
15f7c305
RR
2002 // Inform child items about the size in minor direction, that can
2003 // change how much free space we have in major dir and how to distribute it.
2004 int majorMinSum = 0;
82287aae
CE
2005 wxSizerItemList::const_iterator i ;
2006 for ( i = m_children.begin();
15f7c305
RR
2007 i != m_children.end();
2008 ++i )
2009 {
2010 wxSizerItem * const item = *i;
2011
2012 if ( !item->IsShown() )
2013 continue;
2014
2015 wxSize szMinPrev = item->GetMinSizeWithBorder();
2016 item->InformFirstDirection(m_orient^wxBOTH,totalMinorSize,delta);
2017 wxSize szMin = item->GetMinSizeWithBorder();
3d2085a4 2018 int deltaChange = GetSizeInMajorDir(szMin-szMinPrev);
15f7c305
RR
2019 if( deltaChange )
2020 {
3d2085a4
VZ
2021 // Since we passed available space along to the item, it should not
2022 // take too much, so delta should not become negative.
2023 delta -= deltaChange;
15f7c305 2024 }
3d2085a4 2025 majorMinSum += GetSizeInMajorDir(item->GetMinSizeWithBorder());
15f7c305
RR
2026 }
2027 // And update our min size
2028 SizeInMajorDir(m_minSize) = majorMinSum;
2029
2030
2031 // might have a new delta now
3d2085a4
VZ
2032 delta = GetSizeInMajorDir(m_size) - GetSizeInMajorDir(m_minSize);
2033
89064717
VZ
2034 // the position at which we put the next child
2035 wxPoint pt(m_position);
12a3f227 2036
7fca7a73 2037 int totalProportion = m_totalProportion;
82287aae 2038 for ( i = m_children.begin();
89064717
VZ
2039 i != m_children.end();
2040 ++i )
2041 {
2042 wxSizerItem * const item = *i;
2b5f62a0 2043
89064717
VZ
2044 if ( !item->IsShown() )
2045 continue;
3d2085a4
VZ
2046
2047 const wxSize sizeThis(item->GetMinSizeWithBorder());
2b5f62a0 2048
89064717 2049 // adjust the size in the major direction using the proportion
3d2085a4 2050 wxCoord majorSize = GetSizeInMajorDir(sizeThis);
ec074193
VZ
2051
2052 // if there is not enough space, don't try to distribute negative space
2053 // among the children, this would result in overlapping windows which
2054 // we don't want
2055 if ( delta > 0 )
89064717 2056 {
ec074193
VZ
2057 const int propItem = item->GetProportion();
2058 if ( propItem )
2059 {
2060 const int deltaItem = (delta * propItem) / totalProportion;
7fca7a73 2061
ec074193 2062 majorSize += deltaItem;
7fca7a73 2063
ec074193
VZ
2064 delta -= deltaItem;
2065 totalProportion -= propItem;
2066 }
89064717 2067 }
2b5f62a0 2068
2b5f62a0 2069
89064717
VZ
2070 // apply the alignment in the minor direction
2071 wxPoint posChild(pt);
2b5f62a0 2072
3d2085a4 2073 wxCoord minorSize = GetSizeInMinorDir(sizeThis);
89064717
VZ
2074 const int flag = item->GetFlag();
2075 if ( flag & (wxEXPAND | wxSHAPED) )
2076 {
2077 minorSize = totalMinorSize;
2078 }
2079 else if ( flag & (IsVertical() ? wxALIGN_RIGHT : wxALIGN_BOTTOM) )
2080 {
2081 PosInMinorDir(posChild) += totalMinorSize - minorSize;
2082 }
2083 // NB: wxCENTRE is used here only for backwards compatibility,
2084 // wxALIGN_CENTRE should be used in new code
1a27a87d 2085 else if ( flag & (wxCENTER | (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL : wxALIGN_CENTRE_VERTICAL)))
89064717
VZ
2086 {
2087 PosInMinorDir(posChild) += (totalMinorSize - minorSize) / 2;
2088 }
978af864 2089
2b5f62a0 2090
89064717
VZ
2091 // apply RTL adjustment for horizontal sizers:
2092 if ( !IsVertical() && m_containingWindow )
2093 {
2094 posChild.x = m_containingWindow->AdjustForLayoutDirection
2095 (
2096 posChild.x,
2097 majorSize,
2098 m_size.x
2099 );
3ca6a5f0
BP
2100 }
2101
89064717
VZ
2102 // finally set size of this child and advance to the next one
2103 item->SetDimension(posChild, SizeFromMajorMinor(majorSize, minorSize));
2104
2105 PosInMajorDir(pt) += majorSize;
61d514bb
RR
2106 }
2107}
2108
92afa2b1 2109wxSize wxBoxSizer::CalcMin()
61d514bb 2110{
89064717
VZ
2111 m_totalProportion = 0;
2112 m_minSize = wxSize(0, 0);
0c0d686f 2113
89064717
VZ
2114 // calculate the minimal sizes for all items and count sum of proportions
2115 for ( wxSizerItemList::const_iterator i = m_children.begin();
2116 i != m_children.end();
2117 ++i )
85e5cfc9 2118 {
89064717 2119 wxSizerItem * const item = *i;
12a3f227 2120
89064717
VZ
2121 if ( !item->IsShown() )
2122 continue;
12a3f227 2123
3d2085a4
VZ
2124 const wxSize sizeMinThis = item->CalcMin();
2125 SizeInMajorDir(m_minSize) += GetSizeInMajorDir(sizeMinThis);
2126 if ( GetSizeInMinorDir(sizeMinThis) > GetSizeInMinorDir(m_minSize) )
2127 SizeInMinorDir(m_minSize) = GetSizeInMinorDir(sizeMinThis);
85e5cfc9 2128
89064717 2129 m_totalProportion += item->GetProportion();
61d514bb 2130 }
0c0d686f 2131
89064717 2132 return m_minSize;
61d514bb 2133}
27ea1d8a
RR
2134
2135//---------------------------------------------------------------------------
2136// wxStaticBoxSizer
2137//---------------------------------------------------------------------------
2138
1e6feb95
VZ
2139#if wxUSE_STATBOX
2140
27ea1d8a 2141wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient )
e978011a
VZ
2142 : wxBoxSizer( orient ),
2143 m_staticBox( box )
27ea1d8a 2144{
223d09f6 2145 wxASSERT_MSG( box, wxT("wxStaticBoxSizer needs a static box") );
e978011a
VZ
2146
2147 // do this so that our Detach() is called if the static box is destroyed
2148 // before we are
2149 m_staticBox->SetContainingSizer(this);
27ea1d8a 2150}
0c0d686f 2151
6c1635b5
VZ
2152wxStaticBoxSizer::wxStaticBoxSizer(int orient, wxWindow *win, const wxString& s)
2153 : wxBoxSizer(orient),
2154 m_staticBox(new wxStaticBox(win, wxID_ANY, s))
2155{
e978011a
VZ
2156 // same as above
2157 m_staticBox->SetContainingSizer(this);
6c1635b5
VZ
2158}
2159
649cfca1
VZ
2160wxStaticBoxSizer::~wxStaticBoxSizer()
2161{
2162 delete m_staticBox;
2163}
2164
27ea1d8a
RR
2165void wxStaticBoxSizer::RecalcSizes()
2166{
84028727 2167 int top_border, other_border;
2d5fccc6 2168 m_staticBox->GetBordersForSizer(&top_border, &other_border);
27ea1d8a
RR
2169
2170 m_staticBox->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
0c0d686f 2171
27ea1d8a
RR
2172 wxSize old_size( m_size );
2173 m_size.x -= 2*other_border;
2174 m_size.y -= top_border + other_border;
0c0d686f 2175
2003d003 2176 wxPoint old_pos( m_position );
f25f6620
FM
2177 if (m_staticBox->GetChildren().GetCount() > 0)
2178 {
2003d003 2179#if defined( __WXGTK20__ )
f25f6620
FM
2180 // if the wxStaticBox has created a wxPizza to contain its children
2181 // (see wxStaticBox::AddChild) then we need to place the items it contains
03647350 2182 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
f25f6620
FM
2183 // to the top-left corner of the staticbox:
2184 m_position.x = m_position.y = 0;
2003d003
FM
2185#else
2186 // if the wxStaticBox has childrens, then these windows must be placed
03647350 2187 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2003d003
FM
2188 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2189 // to keep in count the static borders here!):
2190 m_position.x = other_border;
2191 m_position.y = top_border;
f25f6620 2192#endif
2003d003
FM
2193 }
2194 else
2195 {
2196 // the windows contained in the staticbox have been created as siblings of the
2197 // staticbox (this is the "old" way of staticbox contents creation); in this
2198 // case we need to position them with coordinates relative to our common parent
2199 m_position.x += other_border;
2200 m_position.y += top_border;
2201 }
f25f6620 2202
27ea1d8a 2203 wxBoxSizer::RecalcSizes();
0c0d686f 2204
27ea1d8a
RR
2205 m_position = old_pos;
2206 m_size = old_size;
2207}
2208
2209wxSize wxStaticBoxSizer::CalcMin()
2210{
84028727 2211 int top_border, other_border;
2d5fccc6 2212 m_staticBox->GetBordersForSizer(&top_border, &other_border);
0c0d686f 2213
27ea1d8a 2214 wxSize ret( wxBoxSizer::CalcMin() );
cae31b8b 2215 ret.x += 2*other_border;
997e8e29
VZ
2216
2217 // ensure that we're wide enough to show the static box label (there is no
2218 // need to check for the static box best size in vertical direction though)
2219 const int boxWidth = m_staticBox->GetBestSize().x;
2220 if ( ret.x < boxWidth )
2221 ret.x = boxWidth;
2222
27ea1d8a 2223 ret.y += other_border + top_border;
0c0d686f 2224
27ea1d8a
RR
2225 return ret;
2226}
83edc0a5 2227
eb2a7883
VZ
2228void wxStaticBoxSizer::ShowItems( bool show )
2229{
2230 m_staticBox->Show( show );
2231 wxBoxSizer::ShowItems( show );
2232}
2233
e978011a
VZ
2234bool wxStaticBoxSizer::Detach( wxWindow *window )
2235{
2236 // avoid deleting m_staticBox in our dtor if it's being detached from the
2237 // sizer (which can happen because it's being already destroyed for
2238 // example)
2239 if ( window == m_staticBox )
2240 {
2241 m_staticBox = NULL;
2242 return true;
2243 }
2244
2245 return wxSizer::Detach( window );
2246}
2247
1e6feb95
VZ
2248#endif // wxUSE_STATBOX
2249
9b494057
FM
2250//---------------------------------------------------------------------------
2251// wxStdDialogButtonSizer
2252//---------------------------------------------------------------------------
2253
974c2a59
WS
2254#if wxUSE_BUTTON
2255
acf2ac37
RR
2256wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2257 : wxBoxSizer(wxHORIZONTAL)
2258{
94f53923
JS
2259 // Vertical buttons with lots of space on either side
2260 // looks rubbish on WinCE, so let's not do this for now.
2261 // If we are going to use vertical buttons, we should
2262 // put the sizer to the right of other controls in the dialog,
2263 // and that's beyond the scope of this sizer.
2264#ifndef __WXWINCE__
acf2ac37 2265 bool is_pda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
974c2a59 2266 // If we have a PDA screen, put yes/no button over
acf2ac37
RR
2267 // all other buttons, otherwise on the left side.
2268 if (is_pda)
2269 m_orient = wxVERTICAL;
94f53923 2270#endif
974c2a59 2271
acf2ac37
RR
2272 m_buttonAffirmative = NULL;
2273 m_buttonApply = NULL;
2274 m_buttonNegative = NULL;
2275 m_buttonCancel = NULL;
2276 m_buttonHelp = NULL;
2277}
2278
2279void wxStdDialogButtonSizer::AddButton(wxButton *mybutton)
2280{
2281 switch (mybutton->GetId())
2282 {
2283 case wxID_OK:
2284 case wxID_YES:
2285 case wxID_SAVE:
2286 m_buttonAffirmative = mybutton;
2287 break;
2288 case wxID_APPLY:
2289 m_buttonApply = mybutton;
2290 break;
2291 case wxID_NO:
2292 m_buttonNegative = mybutton;
2293 break;
2294 case wxID_CANCEL:
57d7f988 2295 case wxID_CLOSE:
acf2ac37
RR
2296 m_buttonCancel = mybutton;
2297 break;
2298 case wxID_HELP:
2997ca30 2299 case wxID_CONTEXT_HELP:
acf2ac37
RR
2300 m_buttonHelp = mybutton;
2301 break;
2302 default:
2303 break;
2304 }
2305}
2306
b181a505
RR
2307void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton *button )
2308{
2309 m_buttonAffirmative = button;
2310}
2311
2312void wxStdDialogButtonSizer::SetNegativeButton( wxButton *button )
2313{
2314 m_buttonNegative = button;
2315}
2316
2317void wxStdDialogButtonSizer::SetCancelButton( wxButton *button )
2318{
2319 m_buttonCancel = button;
2320}
2321
718903fe 2322void wxStdDialogButtonSizer::Realize()
acf2ac37
RR
2323{
2324#ifdef __WXMAC__
2325 Add(0, 0, 0, wxLEFT, 6);
2326 if (m_buttonHelp)
974c2a59
WS
2327 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2328
acf2ac37
RR
2329 if (m_buttonNegative){
2330 // HIG POLICE BULLETIN - destructive buttons need extra padding
2331 // 24 pixels on either side
2332 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 12);
2333 }
974c2a59 2334
acf2ac37 2335 // extra whitespace between help/negative and cancel/ok buttons
974c2a59
WS
2336 Add(0, 0, 1, wxEXPAND, 0);
2337
acf2ac37
RR
2338 if (m_buttonCancel){
2339 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2340 // Cancel or help should be default
2341 // m_buttonCancel->SetDefaultButton();
2342 }
974c2a59
WS
2343
2344 // Ugh, Mac doesn't really have apply dialogs, so I'll just
acf2ac37
RR
2345 // figure the best place is between Cancel and OK
2346 if (m_buttonApply)
2347 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
974c2a59 2348
acf2ac37
RR
2349 if (m_buttonAffirmative){
2350 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6);
974c2a59 2351
acf2ac37
RR
2352 if (m_buttonAffirmative->GetId() == wxID_SAVE){
2353 // these buttons have set labels under Mac so we should use them
2354 m_buttonAffirmative->SetLabel(_("Save"));
d9485f89
RD
2355 if (m_buttonNegative)
2356 m_buttonNegative->SetLabel(_("Don't Save"));
acf2ac37
RR
2357 }
2358 }
974c2a59 2359
acf2ac37 2360 // Extra space around and at the right
6143d648 2361 Add(12, 40);
acf2ac37
RR
2362#elif defined(__WXGTK20__)
2363 Add(0, 0, 0, wxLEFT, 9);
2364 if (m_buttonHelp)
974c2a59
WS
2365 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
2366
acf2ac37 2367 // extra whitespace between help and cancel/ok buttons
974c2a59
WS
2368 Add(0, 0, 1, wxEXPAND, 0);
2369
acf2ac37
RR
2370 if (m_buttonNegative){
2371 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
2372 }
974c2a59 2373
e6cfcc0d 2374 // according to HIG, in explicit apply windows the order is:
57d7f988
VZ
2375 // [ Help Apply Cancel OK ]
2376 if (m_buttonApply)
2377 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
2378
acf2ac37
RR
2379 if (m_buttonCancel){
2380 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
2381 // Cancel or help should be default
2382 // m_buttonCancel->SetDefaultButton();
2383 }
974c2a59 2384
acf2ac37
RR
2385 if (m_buttonAffirmative)
2386 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6);
0f884515
JS
2387#elif defined(__WXMSW__)
2388 // Windows
2389
2390 // right-justify buttons
2391 Add(0, 0, 1, wxEXPAND, 0);
2392
2393 if (m_buttonAffirmative){
2394 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(2, 0)).x);
2395 }
2396
2397 if (m_buttonNegative){
2398 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(2, 0)).x);
2399 }
2400
2401 if (m_buttonCancel){
2402 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(2, 0)).x);
2403 }
2404 if (m_buttonApply)
2405 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(2, 0)).x);
2406
2407 if (m_buttonHelp)
2408 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(2, 0)).x);
acf2ac37 2409#else
0f884515 2410 // GTK+1 and any other platform
902725ee 2411
23b1018f 2412 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
acf2ac37 2413 if (m_buttonHelp)
974c2a59
WS
2414 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(4, 0)).x);
2415
acf2ac37 2416 // extra whitespace between help and cancel/ok buttons
974c2a59 2417 Add(0, 0, 1, wxEXPAND, 0);
acf2ac37
RR
2418
2419 if (m_buttonApply)
2420 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(4, 0)).x);
974c2a59 2421
acf2ac37
RR
2422 if (m_buttonAffirmative){
2423 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(4, 0)).x);
2424 }
974c2a59 2425
acf2ac37
RR
2426 if (m_buttonNegative){
2427 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(4, 0)).x);
2428 }
974c2a59 2429
acf2ac37 2430 if (m_buttonCancel){
23b1018f 2431 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(4, 0)).x);
acf2ac37
RR
2432 // Cancel or help should be default
2433 // m_buttonCancel->SetDefaultButton();
2434 }
974c2a59 2435
acf2ac37
RR
2436#endif
2437}
adbf2d73 2438
974c2a59 2439#endif // wxUSE_BUTTON