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