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