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