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