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