]> git.saurik.com Git - wxWidgets.git/blame - src/common/sizer.cpp
Add some asserts to ensure cols and rows are >= 0 to avoid crashes
[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{
d22e07bf 1322 wxASSERT(cols >= 0);
7e6edd27
VZ
1323}
1324
4a00e77c
VZ
1325wxGridSizer::wxGridSizer( int cols, const wxSize& gap )
1326 : m_rows( cols == 0 ? 1 : 0 ),
7e6edd27
VZ
1327 m_cols( cols ),
1328 m_vgap( gap.GetHeight() ),
1329 m_hgap( gap.GetWidth() )
f6bcfd97 1330{
d22e07bf 1331 wxASSERT(cols >= 0);
f6bcfd97
BP
1332}
1333
4a00e77c
VZ
1334wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
1335 : m_rows( rows || cols ? rows : 1 ),
7e6edd27
VZ
1336 m_cols( cols ),
1337 m_vgap( vgap ),
1338 m_hgap( hgap )
1339{
d22e07bf 1340 wxASSERT(rows >= 0 && cols >= 0);
7e6edd27
VZ
1341}
1342
4a00e77c
VZ
1343wxGridSizer::wxGridSizer( int rows, int cols, const wxSize& gap )
1344 : m_rows( rows || cols ? rows : 1 ),
7e6edd27
VZ
1345 m_cols( cols ),
1346 m_vgap( gap.GetHeight() ),
1347 m_hgap( gap.GetWidth() )
f6bcfd97 1348{
d22e07bf 1349 wxASSERT(rows >= 0 && cols >= 0);
f6bcfd97
BP
1350}
1351
e556b612 1352wxSizerItem *wxGridSizer::DoInsert(size_t index, wxSizerItem *item)
8b492f67
VZ
1353{
1354 // if only the number of columns or the number of rows is specified for a
1355 // sizer, arbitrarily many items can be added to it but if both of them are
1356 // fixed, then the sizer can't have more than that many items -- check for
1357 // this here to ensure that we detect errors as soon as possible
1358 if ( m_cols && m_rows )
1359 {
2d5fccc6
VZ
1360 const int nitems = m_children.GetCount();
1361 if ( nitems == m_cols*m_rows )
8b492f67 1362 {
2d5fccc6
VZ
1363 wxFAIL_MSG(
1364 wxString::Format(
1365 "too many items (%d > %d*%d) in grid sizer (maybe you "
1366 "should omit the number of either rows or columns?)",
1367 nitems + 1, m_cols, m_rows)
1368 );
8b492f67
VZ
1369
1370 // additionally, continuing to use the specified number of columns
1371 // and rows is not a good idea as callers of CalcRowsCols() expect
0274a797 1372 // that all sizer items can fit into m_cols-/m_rows-sized arrays
8b492f67
VZ
1373 // which is not the case if there are too many items and results in
1374 // crashes, so let it compute the number of rows automatically by
1375 // forgetting the (wrong) number of rows specified (this also has a
1376 // nice side effect of giving only one assert even if there are
1377 // many more items than allowed in this sizer)
1378 m_rows = 0;
1379 }
1380 }
1381
e556b612 1382 return wxSizer::DoInsert(index, item);
8b492f67
VZ
1383}
1384
0ca5105b 1385int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const
f6bcfd97 1386{
5fa51d09 1387 const int nitems = m_children.GetCount();
2283800b 1388
0274a797
VZ
1389 ncols = GetEffectiveColsCount();
1390 nrows = GetEffectiveRowsCount();
f6bcfd97 1391
0274a797
VZ
1392 // Since Insert() checks for overpopulation, the following
1393 // should only assert if the grid was shrunk via SetRows() / SetCols()
1394 wxASSERT_MSG( nitems <= ncols*nrows, "logic error in wxGridSizer" );
0ca5105b
VZ
1395
1396 return nitems;
1397}
1398
1399void wxGridSizer::RecalcSizes()
1400{
1401 int nitems, nrows, ncols;
1402 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
1403 return;
f6bcfd97
BP
1404
1405 wxSize sz( GetSize() );
1406 wxPoint pt( GetPosition() );
3ca6a5f0
BP
1407
1408 int w = (sz.x - (ncols - 1) * m_hgap) / ncols;
1409 int h = (sz.y - (nrows - 1) * m_vgap) / nrows;
f6bcfd97
BP
1410
1411 int x = pt.x;
1412 for (int c = 0; c < ncols; c++)
1413 {
1414 int y = pt.y;
1415 for (int r = 0; r < nrows; r++)
1416 {
1417 int i = r * ncols + c;
1418 if (i < nitems)
1419 {
222ed1d6 1420 wxSizerItemList::compatibility_iterator node = m_children.Item( i );
12a3f227 1421
9a83f860 1422 wxASSERT_MSG( node, wxT("Failed to find SizerItemList node") );
3ca6a5f0 1423
12a3f227 1424 SetItemBounds( node->GetData(), x, y, w, h);
f6bcfd97
BP
1425 }
1426 y = y + h + m_vgap;
1427 }
1428 x = x + w + m_hgap;
1429 }
1430}
1431
1432wxSize wxGridSizer::CalcMin()
1433{
196be0f1
JS
1434 int nrows, ncols;
1435 if ( CalcRowsCols(nrows, ncols) == 0 )
b3f1734f 1436 return wxSize();
f6bcfd97 1437
4f469fb5 1438 // Find the max width and height for any component
f6bcfd97
BP
1439 int w = 0;
1440 int h = 0;
3ca6a5f0 1441
222ed1d6 1442 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
f6bcfd97
BP
1443 while (node)
1444 {
12a3f227
RL
1445 wxSizerItem *item = node->GetData();
1446 wxSize sz( item->CalcMin() );
1447
f6bcfd97
BP
1448 w = wxMax( w, sz.x );
1449 h = wxMax( h, sz.y );
3ca6a5f0 1450
12a3f227 1451 node = node->GetNext();
f6bcfd97 1452 }
3ca6a5f0 1453
3d2085a4 1454 // In case we have a nested sizer with a two step algo , give it
15f7c305
RR
1455 // a chance to adjust to that (we give it width component)
1456 node = m_children.GetFirst();
1457 bool didChangeMinSize = false;
1458 while (node)
1459 {
1460 wxSizerItem *item = node->GetData();
1461 didChangeMinSize |= item->InformFirstDirection( wxHORIZONTAL, w, -1 );
3d2085a4 1462
15f7c305
RR
1463 node = node->GetNext();
1464 }
3d2085a4 1465
15f7c305
RR
1466 // And redo iteration in case min size changed
1467 if( didChangeMinSize )
1468 {
1469 node = m_children.GetFirst();
1470 w = h = 0;
1471 while (node)
1472 {
1473 wxSizerItem *item = node->GetData();
1474 wxSize sz( item->GetMinSizeWithBorder() );
1475
1476 w = wxMax( w, sz.x );
1477 h = wxMax( h, sz.y );
1478
1479 node = node->GetNext();
3d2085a4 1480 }
15f7c305 1481 }
3d2085a4 1482
12a3f227
RL
1483 return wxSize( ncols * w + (ncols-1) * m_hgap,
1484 nrows * h + (nrows-1) * m_vgap );
f6bcfd97
BP
1485}
1486
1487void wxGridSizer::SetItemBounds( wxSizerItem *item, int x, int y, int w, int h )
1488{
1489 wxPoint pt( x,y );
8b2bac62 1490 wxSize sz( item->GetMinSizeWithBorder() );
f6bcfd97
BP
1491 int flag = item->GetFlag();
1492
1493 if ((flag & wxEXPAND) || (flag & wxSHAPED))
1494 {
1495 sz = wxSize(w, h);
1496 }
1497 else
1498 {
1499 if (flag & wxALIGN_CENTER_HORIZONTAL)
1500 {
559b747d 1501 pt.x = x + (w - sz.x) / 2;
f6bcfd97
BP
1502 }
1503 else if (flag & wxALIGN_RIGHT)
1504 {
559b747d 1505 pt.x = x + (w - sz.x);
f6bcfd97 1506 }
3ca6a5f0 1507
f6bcfd97
BP
1508 if (flag & wxALIGN_CENTER_VERTICAL)
1509 {
559b747d 1510 pt.y = y + (h - sz.y) / 2;
f6bcfd97
BP
1511 }
1512 else if (flag & wxALIGN_BOTTOM)
1513 {
559b747d 1514 pt.y = y + (h - sz.y);
f6bcfd97
BP
1515 }
1516 }
3ca6a5f0 1517
f6bcfd97
BP
1518 item->SetDimension(pt, sz);
1519}
1520
1521//---------------------------------------------------------------------------
1522// wxFlexGridSizer
1523//---------------------------------------------------------------------------
1524
4a00e77c
VZ
1525wxFlexGridSizer::wxFlexGridSizer( int cols, int vgap, int hgap )
1526 : wxGridSizer( cols, vgap, hgap ),
1527 m_flexDirection(wxBOTH),
1528 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1529{
1530}
1531
1532wxFlexGridSizer::wxFlexGridSizer( int cols, const wxSize& gap )
1533 : wxGridSizer( cols, gap ),
1534 m_flexDirection(wxBOTH),
1535 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1536{
1537}
1538
f6bcfd97 1539wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, int vgap, int hgap )
5d76f462
VZ
1540 : wxGridSizer( rows, cols, vgap, hgap ),
1541 m_flexDirection(wxBOTH),
1542 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
3ca6a5f0 1543{
f6bcfd97
BP
1544}
1545
4a00e77c
VZ
1546wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, const wxSize& gap )
1547 : wxGridSizer( rows, cols, gap ),
5d76f462
VZ
1548 m_flexDirection(wxBOTH),
1549 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
3ca6a5f0 1550{
f6bcfd97 1551}
3ca6a5f0 1552
f6bcfd97
BP
1553wxFlexGridSizer::~wxFlexGridSizer()
1554{
f6bcfd97
BP
1555}
1556
1557void wxFlexGridSizer::RecalcSizes()
1558{
74ab5f5b
VZ
1559 int nrows, ncols;
1560 if ( !CalcRowsCols(nrows, ncols) )
f6bcfd97
BP
1561 return;
1562
97800f66
VZ
1563 const wxPoint pt(GetPosition());
1564 const wxSize sz(GetSize());
3ca6a5f0 1565
97800f66 1566 AdjustForGrowables(sz);
f6bcfd97 1567
97800f66 1568 wxSizerItemList::const_iterator i = m_children.begin();
cc67d082
VZ
1569 const wxSizerItemList::const_iterator end = m_children.end();
1570
97800f66
VZ
1571 int y = 0;
1572 for ( int r = 0; r < nrows; r++ )
f6bcfd97 1573 {
97800f66 1574 if ( m_rowHeights[r] == -1 )
f6bcfd97 1575 {
97800f66
VZ
1576 // this row is entirely hidden, skip it
1577 for ( int c = 0; c < ncols; c++ )
cc67d082
VZ
1578 {
1579 if ( i == end )
1580 return;
1581
97800f66 1582 ++i;
cc67d082 1583 }
12a3f227 1584
97800f66
VZ
1585 continue;
1586 }
3ca6a5f0 1587
97800f66
VZ
1588 const int hrow = m_rowHeights[r];
1589 int h = sz.y - y; // max remaining height, don't overflow it
1590 if ( hrow < h )
1591 h = hrow;
3ca6a5f0 1592
97800f66 1593 int x = 0;
cc67d082 1594 for ( int c = 0; c < ncols && i != end; c++, ++i )
97800f66
VZ
1595 {
1596 const int wcol = m_colWidths[c];
1597
1598 if ( wcol == -1 )
1599 continue;
1600
97800f66
VZ
1601 int w = sz.x - x; // max possible value, ensure we don't overflow
1602 if ( wcol < w )
1603 w = wcol;
1604
1605 SetItemBounds(*i, pt.x + x, pt.y + y, w, h);
1606
1607 x += wcol + m_hgap;
f6bcfd97 1608 }
97800f66 1609
cc67d082
VZ
1610 if ( i == end )
1611 return;
1612
97800f66 1613 y += hrow + m_vgap;
f6bcfd97
BP
1614 }
1615}
1616
97800f66
VZ
1617// helper function used in CalcMin() to sum up the sizes of non-hidden items
1618static int SumArraySizes(const wxArrayInt& sizes, int gap)
1619{
1620 // Sum total minimum size, including gaps between rows/columns.
1621 // -1 is used as a magic number meaning empty row/column.
1622 int total = 0;
1623
1624 const size_t count = sizes.size();
1625 for ( size_t n = 0; n < count; n++ )
1626 {
1627 if ( sizes[n] != -1 )
1628 {
1629 if ( total )
1630 total += gap; // separate from the previous column
1631
1632 total += sizes[n];
1633 }
1634 }
1635
1636 return total;
1637}
1638
15f7c305 1639void wxFlexGridSizer::FindWidthsAndHeights(int nrows, int ncols)
f6bcfd97 1640{
97800f66 1641 // We have to recalculate the sizes in case the item minimum size has
395a82b1
VZ
1642 // changed since the previous layout, or the item has been hidden using
1643 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1644 // dimension of the row/column will be -1, indicating that the column
1645 // itself is hidden.
97800f66
VZ
1646 m_rowHeights.assign(nrows, -1);
1647 m_colWidths.assign(ncols, -1);
1648
1649 // n is the index of the item in left-to-right top-to-bottom order
1650 size_t n = 0;
1651 for ( wxSizerItemList::iterator i = m_children.begin();
1652 i != m_children.end();
1653 ++i, ++n )
f6bcfd97 1654 {
97800f66 1655 wxSizerItem * const item = *i;
55f9f0cb
VZ
1656 if ( item->IsShown() )
1657 {
3d2085a4 1658 // NOTE: Not doing the calculation here, this is just
15f7c305
RR
1659 // for finding max values.
1660 const wxSize sz(item->GetMinSizeWithBorder());
12a3f227 1661
97800f66
VZ
1662 const int row = n / ncols;
1663 const int col = n % ncols;
3ca6a5f0 1664
97800f66
VZ
1665 if ( sz.y > m_rowHeights[row] )
1666 m_rowHeights[row] = sz.y;
1667 if ( sz.x > m_colWidths[col] )
1668 m_colWidths[col] = sz.x;
1669 }
f6bcfd97 1670 }
3ca6a5f0 1671
20b35a69 1672 AdjustForFlexDirection();
8b2bac62 1673
97800f66
VZ
1674 m_calculatedMinSize = wxSize(SumArraySizes(m_colWidths, m_hgap),
1675 SumArraySizes(m_rowHeights, m_vgap));
15f7c305
RR
1676}
1677
1678wxSize wxFlexGridSizer::CalcMin()
1679{
1680 int nrows,
1681 ncols;
1682
1683 // Number of rows/columns can change as items are added or removed.
1684 if ( !CalcRowsCols(nrows, ncols) )
1685 return wxSize();
1686
1687
1688 // We have to recalculate the sizes in case the item minimum size has
1689 // changed since the previous layout, or the item has been hidden using
1690 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1691 // dimension of the row/column will be -1, indicating that the column
1692 // itself is hidden.
1693 m_rowHeights.assign(nrows, -1);
1694 m_colWidths.assign(ncols, -1);
1695
15f7c305
RR
1696 for ( wxSizerItemList::iterator i = m_children.begin();
1697 i != m_children.end();
b7bc9d80 1698 ++i)
15f7c305
RR
1699 {
1700 wxSizerItem * const item = *i;
1701 if ( item->IsShown() )
1702 {
1703 item->CalcMin();
1704 }
1705 }
1706
3d2085a4 1707 // The stage of looking for max values in each row/column has been
15f7c305
RR
1708 // made a separate function, since it's reused in AdjustForGrowables.
1709 FindWidthsAndHeights(nrows,ncols);
97800f66 1710
ba763a45 1711 return m_calculatedMinSize;
20b35a69
RD
1712}
1713
1714void wxFlexGridSizer::AdjustForFlexDirection()
1715{
1716 // the logic in CalcMin works when we resize flexibly in both directions
1717 // but maybe this is not the case
5d76f462
VZ
1718 if ( m_flexDirection != wxBOTH )
1719 {
1720 // select the array corresponding to the direction in which we do *not*
1721 // resize flexibly
1722 wxArrayInt& array = m_flexDirection == wxVERTICAL ? m_colWidths
1723 : m_rowHeights;
1724
4a10ea8b 1725 const size_t count = array.GetCount();
5d76f462
VZ
1726
1727 // find the largest value in this array
4a10ea8b
MW
1728 size_t n;
1729 int largest = 0;
1730
5d76f462
VZ
1731 for ( n = 0; n < count; ++n )
1732 {
1733 if ( array[n] > largest )
1734 largest = array[n];
1735 }
1736
1737 // and now fill it with the largest value
1738 for ( n = 0; n < count; ++n )
1739 {
b9a325a1
VZ
1740 // don't touch hidden rows
1741 if ( array[n] != -1 )
1742 array[n] = largest;
5d76f462
VZ
1743 }
1744 }
8b2bac62 1745}
5d76f462 1746
97800f66
VZ
1747// helper of AdjustForGrowables() which is called for rows/columns separately
1748//
1749// parameters:
1750// delta: the extra space, we do nothing unless it's positive
1751// growable: indices or growable rows/cols in sizes array
1752// sizes: the height/widths of rows/cols to adjust
1753// proportions: proportions of the growable rows/cols or NULL if they all
1754// should be assumed to have proportion of 1
1755static void
1756DoAdjustForGrowables(int delta,
1757 const wxArrayInt& growable,
1758 wxArrayInt& sizes,
1759 const wxArrayInt *proportions)
1760{
1761 if ( delta <= 0 )
1762 return;
3ca6a5f0 1763
97800f66
VZ
1764 // total sum of proportions of all non-hidden rows
1765 int sum_proportions = 0;
8b2bac62 1766
97800f66
VZ
1767 // number of currently shown growable rows
1768 int num = 0;
3ca6a5f0 1769
97800f66
VZ
1770 const int max_idx = sizes.size();
1771
1772 const size_t count = growable.size();
1773 size_t idx;
1774 for ( idx = 0; idx < count; idx++ )
20b35a69 1775 {
97800f66
VZ
1776 // Since the number of rows/columns can change as items are
1777 // inserted/deleted, we need to verify at runtime that the
1778 // requested growable rows/columns are still valid.
1779 if ( growable[idx] >= max_idx )
1780 continue;
1781
1782 // If all items in a row/column are hidden, that row/column will
1783 // have a dimension of -1. This causes the row/column to be
1784 // hidden completely.
1785 if ( sizes[growable[idx]] == -1 )
1786 continue;
1787
1788 if ( proportions )
1789 sum_proportions += (*proportions)[idx];
1790
1791 num++;
20b35a69
RD
1792 }
1793
97800f66
VZ
1794 if ( !num )
1795 return;
1796
1797 // the remaining extra free space, adjusted during each iteration
1798 for ( idx = 0; idx < count; idx++ )
20b35a69 1799 {
97800f66
VZ
1800 if ( growable[idx] >= max_idx )
1801 continue;
8b2bac62 1802
97800f66
VZ
1803 if ( sizes[ growable[idx] ] == -1 )
1804 continue;
20b35a69 1805
97800f66
VZ
1806 int cur_delta;
1807 if ( sum_proportions == 0 )
20b35a69 1808 {
97800f66
VZ
1809 // no growable rows -- divide extra space evenly among all
1810 cur_delta = delta/num;
1811 num--;
20b35a69 1812 }
97800f66
VZ
1813 else // allocate extra space proportionally
1814 {
1815 const int cur_prop = (*proportions)[idx];
1816 cur_delta = (delta*cur_prop)/sum_proportions;
1817 sum_proportions -= cur_prop;
1818 }
1819
1820 sizes[growable[idx]] += cur_delta;
1821 delta -= cur_delta;
20b35a69 1822 }
97800f66
VZ
1823}
1824
1825void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz)
1826{
4b6a582b 1827#if wxDEBUG_LEVEL
4e4e06e3
VZ
1828 // by the time this function is called, the sizer should be already fully
1829 // initialized and hence the number of its columns and rows is known and we
1830 // can check that all indices in m_growableCols/Rows are valid (see also
1831 // comments in AddGrowableCol/Row())
1832 if ( !m_rows || !m_cols )
1833 {
4e4e06e3
VZ
1834 if ( !m_rows )
1835 {
0274a797
VZ
1836 int nrows = CalcRows();
1837
4e4e06e3
VZ
1838 for ( size_t n = 0; n < m_growableRows.size(); n++ )
1839 {
1840 wxASSERT_MSG( m_growableRows[n] < nrows,
1841 "invalid growable row index" );
1842 }
1843 }
1844
1845 if ( !m_cols )
1846 {
0274a797
VZ
1847 int ncols = CalcCols();
1848
4e4e06e3
VZ
1849 for ( size_t n = 0; n < m_growableCols.size(); n++ )
1850 {
1851 wxASSERT_MSG( m_growableCols[n] < ncols,
1852 "invalid growable column index" );
1853 }
1854 }
1855 }
4b6a582b 1856#endif // wxDEBUG_LEVEL
4e4e06e3
VZ
1857
1858
15f7c305 1859 if ( (m_flexDirection & wxHORIZONTAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
97800f66 1860 {
97800f66
VZ
1861 DoAdjustForGrowables
1862 (
15f7c305
RR
1863 sz.x - m_calculatedMinSize.x,
1864 m_growableCols,
1865 m_colWidths,
1866 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
97800f66
VZ
1867 : NULL
1868 );
3d2085a4
VZ
1869
1870 // This gives nested objects that benefit from knowing one size
1871 // component in advance the chance to use that.
15f7c305 1872 bool didAdjustMinSize = false;
3d2085a4 1873
15f7c305 1874 // Iterate over all items and inform about column width
0274a797
VZ
1875 const int ncols = GetEffectiveColsCount();
1876 int col = 0;
15f7c305
RR
1877 for ( wxSizerItemList::iterator i = m_children.begin();
1878 i != m_children.end();
0274a797 1879 ++i )
15f7c305 1880 {
15f7c305 1881 didAdjustMinSize |= (*i)->InformFirstDirection(wxHORIZONTAL, m_colWidths[col], sz.y - m_calculatedMinSize.y);
0274a797
VZ
1882 if ( ++col == ncols )
1883 col = 0;
b7bc9d80 1884 }
97800f66 1885
3d2085a4 1886 // Only redo if info was actually used
15f7c305 1887 if( didAdjustMinSize )
b7bc9d80
PC
1888 {
1889 DoAdjustForGrowables
1890 (
1891 sz.x - m_calculatedMinSize.x,
1892 m_growableCols,
1893 m_colWidths,
1894 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
1895 : NULL
1896 );
1897 }
20b35a69 1898 }
f6bcfd97 1899
15f7c305
RR
1900 if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
1901 {
1902 // pass NULL instead of proportions if the grow mode is ALL as we
1903 // should treat all rows as having proportion of 1 then
1904 DoAdjustForGrowables
1905 (
1906 sz.y - m_calculatedMinSize.y,
1907 m_growableRows,
1908 m_rowHeights,
1909 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableRowsProportions
1910 : NULL
1911 );
1912 }
1913}
1914
67ef83eb
VZ
1915bool wxFlexGridSizer::IsRowGrowable( size_t idx )
1916{
1917 return m_growableRows.Index( idx ) != wxNOT_FOUND;
1918}
1919
1920bool wxFlexGridSizer::IsColGrowable( size_t idx )
1921{
1922 return m_growableCols.Index( idx ) != wxNOT_FOUND;
1923}
20b35a69 1924
e8800dcf 1925void wxFlexGridSizer::AddGrowableRow( size_t idx, int proportion )
f6bcfd97 1926{
ef52f19e 1927 wxASSERT_MSG( !IsRowGrowable( idx ),
67ef83eb 1928 "AddGrowableRow() called for growable row" );
4e4e06e3
VZ
1929
1930 // notice that we intentionally don't check the index validity here in (the
1931 // common) case when the number of rows was not specified in the ctor -- in
1932 // this case it will be computed only later, when all items are added to
1933 // the sizer, and the check will be done in AdjustForGrowables()
1934 wxCHECK_RET( !m_rows || idx < (size_t)m_rows, "invalid row index" );
1935
f6bcfd97 1936 m_growableRows.Add( idx );
e8800dcf 1937 m_growableRowsProportions.Add( proportion );
f6bcfd97
BP
1938}
1939
e8800dcf 1940void wxFlexGridSizer::AddGrowableCol( size_t idx, int proportion )
f6bcfd97 1941{
ef52f19e 1942 wxASSERT_MSG( !IsColGrowable( idx ),
67ef83eb 1943 "AddGrowableCol() called for growable column" );
4e4e06e3
VZ
1944
1945 // see comment in AddGrowableRow(): although it's less common to omit the
1946 // specification of the number of columns, it still can also happen
0274a797 1947 wxCHECK_RET( !m_cols || idx < (size_t)m_cols, "invalid column index" );
4e4e06e3 1948
f6bcfd97 1949 m_growableCols.Add( idx );
e8800dcf 1950 m_growableColsProportions.Add( proportion );
f6bcfd97
BP
1951}
1952
ca243008
VZ
1953// helper function for RemoveGrowableCol/Row()
1954static void
1955DoRemoveFromArrays(size_t idx, wxArrayInt& items, wxArrayInt& proportions)
1956{
1957 const size_t count = items.size();
1958 for ( size_t n = 0; n < count; n++ )
1959 {
1960 if ( (size_t)items[n] == idx )
1961 {
1962 items.RemoveAt(n);
1963 proportions.RemoveAt(n);
1964 return;
1965 }
1966 }
1967
9a83f860 1968 wxFAIL_MSG( wxT("column/row is already not growable") );
ca243008
VZ
1969}
1970
8d2474f4 1971void wxFlexGridSizer::RemoveGrowableCol( size_t idx )
f6bcfd97 1972{
ca243008
VZ
1973 DoRemoveFromArrays(idx, m_growableCols, m_growableColsProportions);
1974}
1975
1976void wxFlexGridSizer::RemoveGrowableRow( size_t idx )
1977{
1978 DoRemoveFromArrays(idx, m_growableRows, m_growableRowsProportions);
f6bcfd97
BP
1979}
1980
c62ac5b6 1981//---------------------------------------------------------------------------
92afa2b1 1982// wxBoxSizer
61d514bb
RR
1983//---------------------------------------------------------------------------
1984
1a2df6a7
VZ
1985wxSizerItem *wxBoxSizer::AddSpacer(int size)
1986{
1987 return IsVertical() ? Add(0, size) : Add(size, 0);
1988}
1989
729f53d4
VZ
1990namespace
1991{
1992
1993/*
1994 Helper of RecalcSizes(): checks if there is enough remaining space for the
1995 min size of the given item and returns its min size or the entire remaining
1996 space depending on which one is greater.
1997
1998 This function updates the remaining space parameter to account for the size
1999 effectively allocated to the item.
2000 */
2001int
2002GetMinOrRemainingSize(int orient, const wxSizerItem *item, int *remainingSpace_)
2003{
2004 int& remainingSpace = *remainingSpace_;
2005
2006 wxCoord size;
2007 if ( remainingSpace > 0 )
2008 {
2009 const wxSize sizeMin = item->GetMinSizeWithBorder();
2010 size = orient == wxHORIZONTAL ? sizeMin.x : sizeMin.y;
2011
2012 if ( size >= remainingSpace )
2013 {
2014 // truncate the item to fit in the remaining space, this is better
2015 // than showing it only partially in general, even if both choices
2016 // are bad -- but there is nothing else we can do
2017 size = remainingSpace;
2018 }
2019
2020 remainingSpace -= size;
2021 }
2022 else // no remaining space
2023 {
2024 // no space at all left, no need to even query the item for its min
2025 // size as we can't give it to it anyhow
2026 size = 0;
2027 }
2028
2029 return size;
2030}
2031
2032} // anonymous namespace
2033
92afa2b1 2034void wxBoxSizer::RecalcSizes()
61d514bb 2035{
89064717 2036 if ( m_children.empty() )
61d514bb 2037 return;
0c0d686f 2038
3d2085a4 2039 const wxCoord totalMinorSize = GetSizeInMinorDir(m_size);
26022721 2040 const wxCoord totalMajorSize = GetSizeInMajorDir(m_size);
15f7c305 2041
89064717
VZ
2042 // the amount of free space which we should redistribute among the
2043 // stretchable items (i.e. those with non zero proportion)
26022721 2044 int delta = totalMajorSize - GetSizeInMajorDir(m_minSize);
3d2085a4 2045
729f53d4
VZ
2046 // declare loop variables used below:
2047 wxSizerItemList::const_iterator i; // iterator in m_children list
2048 unsigned n = 0; // item index in majorSizes array
0c0d686f 2049
729f53d4
VZ
2050
2051 // First, inform item about the available size in minor direction as this
2052 // can change their size in the major direction. Also compute the number of
2053 // visible items and sum of their min sizes in major direction.
2054
2055 int minMajorSize = 0;
2056 for ( i = m_children.begin(); i != m_children.end(); ++i )
15f7c305
RR
2057 {
2058 wxSizerItem * const item = *i;
2059
2060 if ( !item->IsShown() )
2061 continue;
2062
2063 wxSize szMinPrev = item->GetMinSizeWithBorder();
2064 item->InformFirstDirection(m_orient^wxBOTH,totalMinorSize,delta);
2065 wxSize szMin = item->GetMinSizeWithBorder();
3d2085a4 2066 int deltaChange = GetSizeInMajorDir(szMin-szMinPrev);
15f7c305
RR
2067 if( deltaChange )
2068 {
3d2085a4
VZ
2069 // Since we passed available space along to the item, it should not
2070 // take too much, so delta should not become negative.
2071 delta -= deltaChange;
15f7c305 2072 }
729f53d4 2073 minMajorSize += GetSizeInMajorDir(item->GetMinSizeWithBorder());
15f7c305 2074 }
15f7c305 2075
729f53d4
VZ
2076 // update our min size and delta which may have changed
2077 SizeInMajorDir(m_minSize) = minMajorSize;
2078 delta = totalMajorSize - minMajorSize;
15f7c305 2079
3d2085a4 2080
729f53d4
VZ
2081 // space and sum of proportions for the remaining items, both may change
2082 // below
2083 wxCoord remaining = totalMajorSize;
2084 int totalProportion = m_totalProportion;
12a3f227 2085
729f53d4
VZ
2086 // size of the (visible) items in major direction, -1 means "not fixed yet"
2087 wxVector<int> majorSizes(GetItemCount(), wxDefaultCoord);
26022721 2088
729f53d4
VZ
2089
2090 // Check for the degenerated case when we don't have enough space for even
2091 // the min sizes of all the items: in this case we really can't do much
2092 // more than to to allocate the min size to as many of fixed size items as
2093 // possible (on the assumption that variable size items such as text zones
2094 // or list boxes may use scrollbars to show their content even if their
2095 // size is less than min size but that fixed size items such as buttons
2096 // will suffer even more if we don't give them their min size)
2097 if ( totalMajorSize < minMajorSize )
89064717 2098 {
729f53d4
VZ
2099 // Second degenerated case pass: allocate min size to all fixed size
2100 // items.
2101 for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
2102 {
2103 wxSizerItem * const item = *i;
2b5f62a0 2104
729f53d4
VZ
2105 if ( !item->IsShown() )
2106 continue;
3d2085a4 2107
729f53d4
VZ
2108 // deal with fixed size items only during this pass
2109 if ( item->GetProportion() )
2110 continue;
2111
2112 majorSizes[n] = GetMinOrRemainingSize(m_orient, item, &remaining);
2113 }
2114
2115
2116 // Third degenerated case pass: allocate min size to all the remaining,
2117 // i.e. non-fixed size, items.
2118 for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
2119 {
2120 wxSizerItem * const item = *i;
2121
2122 if ( !item->IsShown() )
2123 continue;
2b5f62a0 2124
729f53d4
VZ
2125 // we've already dealt with fixed size items above
2126 if ( !item->GetProportion() )
2127 continue;
ec074193 2128
729f53d4
VZ
2129 majorSizes[n] = GetMinOrRemainingSize(m_orient, item, &remaining);
2130 }
2131 }
2132 else // we do have enough space to give at least min sizes to all items
2133 {
2134 // Second and maybe more passes in the non-degenerated case: deal with
2135 // fixed size items and items whose min size is greater than what we
2136 // would allocate to them taking their proportion into account. For
2137 // both of them, we will just use their min size, but for the latter we
2138 // also need to reexamine all the items as the items which fitted
2139 // before we adjusted their size upwards might not fit any more. This
2140 // does make for a quadratic algorithm but it's not obvious how to
2141 // avoid it and hopefully it's not a huge problem in practice as the
2142 // sizers don't have many items usually (and, of course, the algorithm
2143 // still reduces into a linear one if there is enough space for all the
2144 // min sizes).
2145 bool nonFixedSpaceChanged = false;
2146 for ( i = m_children.begin(), n = 0; ; ++i, ++n )
89064717 2147 {
729f53d4
VZ
2148 if ( nonFixedSpaceChanged )
2149 {
2150 i = m_children.begin();
2151 n = 0;
2152 nonFixedSpaceChanged = false;
2153 }
2154
2155 // check for the end of the loop only after the check above as
2156 // otherwise we wouldn't do another pass if the last child resulted
2157 // in non fixed space reduction
2158 if ( i == m_children.end() )
2159 break;
2160
2161 wxSizerItem * const item = *i;
2162
2163 if ( !item->IsShown() )
2164 continue;
2165
2166 // don't check the item which we had already dealt with during a
2167 // previous pass (this is more than an optimization, the code
2168 // wouldn't work correctly if we kept adjusting for the same item
2169 // over and over again)
2170 if ( majorSizes[n] != wxDefaultCoord )
2171 continue;
2172
75738bb6
VZ
2173 wxCoord minMajor = GetSizeInMajorDir(item->GetMinSizeWithBorder());
2174
2175 // it doesn't make sense for min size to be negative but right now
2176 // it's possible to create e.g. a spacer with (-1, 10) as size and
2177 // people do it in their code apparently (see #11842) so ensure
2178 // that we don't use this -1 as real min size as it conflicts with
2179 // the meaning we use for it here and negative min sizes just don't
2180 // make sense anyhow (which is why it might be a better idea to
2181 // deal with them at wxSizerItem level in the future but for now
2182 // this is the minimal fix for the bug)
2183 if ( minMajor < 0 )
2184 minMajor = 0;
2185
ec074193
VZ
2186 const int propItem = item->GetProportion();
2187 if ( propItem )
2188 {
729f53d4
VZ
2189 // is the desired size of this item big enough?
2190 if ( (remaining*propItem)/totalProportion >= minMajor )
2191 {
2192 // yes, it is, we'll determine the real size of this
2193 // item later, for now just leave it as wxDefaultCoord
2194 continue;
2195 }
7fca7a73 2196
729f53d4
VZ
2197 // the proportion of this item won't count, it has
2198 // effectively become fixed
ec074193
VZ
2199 totalProportion -= propItem;
2200 }
729f53d4
VZ
2201
2202 // we can already allocate space for this item
2203 majorSizes[n] = minMajor;
2204
2205 // change the amount of the space remaining to the other items,
2206 // as this can result in not being able to satisfy their
2207 // proportions any more we will need to redo another loop
2208 // iteration
2209 remaining -= minMajor;
2210
2211 nonFixedSpaceChanged = true;
89064717 2212 }
729f53d4
VZ
2213
2214
2215 // Last by one pass: distribute the remaining space among the non-fixed
2216 // items whose size weren't fixed yet according to their proportions.
2217 for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
26022721 2218 {
729f53d4
VZ
2219 wxSizerItem * const item = *i;
2220
2221 if ( !item->IsShown() )
2222 continue;
2223
2224 if ( majorSizes[n] == wxDefaultCoord )
2225 {
2226 const int propItem = item->GetProportion();
2227 majorSizes[n] = (remaining*propItem)/totalProportion;
2228
2229 remaining -= majorSizes[n];
2230 totalProportion -= propItem;
2231 }
26022721 2232 }
729f53d4
VZ
2233 }
2234
2b5f62a0 2235
729f53d4
VZ
2236 // the position at which we put the next child
2237 wxPoint pt(m_position);
2238
2239
2240 // Final pass: finally do position the items correctly using their sizes as
2241 // determined above.
2242 for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
2243 {
2244 wxSizerItem * const item = *i;
2245
2246 if ( !item->IsShown() )
2247 continue;
2248
2249 const int majorSize = majorSizes[n];
2250
2251 const wxSize sizeThis(item->GetMinSizeWithBorder());
2b5f62a0 2252
89064717
VZ
2253 // apply the alignment in the minor direction
2254 wxPoint posChild(pt);
2b5f62a0 2255
3d2085a4 2256 wxCoord minorSize = GetSizeInMinorDir(sizeThis);
89064717 2257 const int flag = item->GetFlag();
a71aeb24 2258 if ( (flag & (wxEXPAND | wxSHAPED)) || (minorSize > totalMinorSize) )
89064717 2259 {
a71aeb24
VZ
2260 // occupy all the available space if wxEXPAND was given and also if
2261 // the item is too big to fit -- in this case we truncate it below
2262 // its minimal size which is bad but better than not showing parts
2263 // of the window at all
89064717
VZ
2264 minorSize = totalMinorSize;
2265 }
2266 else if ( flag & (IsVertical() ? wxALIGN_RIGHT : wxALIGN_BOTTOM) )
2267 {
2268 PosInMinorDir(posChild) += totalMinorSize - minorSize;
2269 }
2270 // NB: wxCENTRE is used here only for backwards compatibility,
2271 // wxALIGN_CENTRE should be used in new code
a71aeb24
VZ
2272 else if ( flag & (wxCENTER | (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2273 : wxALIGN_CENTRE_VERTICAL)) )
89064717
VZ
2274 {
2275 PosInMinorDir(posChild) += (totalMinorSize - minorSize) / 2;
2276 }
978af864 2277
2b5f62a0 2278
89064717
VZ
2279 // apply RTL adjustment for horizontal sizers:
2280 if ( !IsVertical() && m_containingWindow )
2281 {
2282 posChild.x = m_containingWindow->AdjustForLayoutDirection
2283 (
2284 posChild.x,
2285 majorSize,
2286 m_size.x
2287 );
3ca6a5f0
BP
2288 }
2289
89064717
VZ
2290 // finally set size of this child and advance to the next one
2291 item->SetDimension(posChild, SizeFromMajorMinor(majorSize, minorSize));
2292
2293 PosInMajorDir(pt) += majorSize;
61d514bb
RR
2294 }
2295}
2296
92afa2b1 2297wxSize wxBoxSizer::CalcMin()
61d514bb 2298{
89064717
VZ
2299 m_totalProportion = 0;
2300 m_minSize = wxSize(0, 0);
0c0d686f 2301
f27d62bf
VZ
2302 // The minimal size for the sizer should be big enough to allocate its
2303 // element at least its minimal size but also, and this is the non trivial
2304 // part, to respect the children proportion. To satisfy the latter
2305 // condition we must find the greatest min-size-to-proportion ratio for all
2306 // elements with non-zero proportion.
2307 float maxMinSizeToProp = 0.;
89064717
VZ
2308 for ( wxSizerItemList::const_iterator i = m_children.begin();
2309 i != m_children.end();
2310 ++i )
85e5cfc9 2311 {
89064717 2312 wxSizerItem * const item = *i;
12a3f227 2313
89064717
VZ
2314 if ( !item->IsShown() )
2315 continue;
12a3f227 2316
3d2085a4 2317 const wxSize sizeMinThis = item->CalcMin();
f27d62bf
VZ
2318 if ( const int propThis = item->GetProportion() )
2319 {
2320 float minSizeToProp = GetSizeInMajorDir(sizeMinThis);
2321 minSizeToProp /= propThis;
2322
2323 if ( minSizeToProp > maxMinSizeToProp )
2324 maxMinSizeToProp = minSizeToProp;
2325
2326 m_totalProportion += item->GetProportion();
2327 }
2328 else // fixed size item
2329 {
2330 // Just account for its size directly
2331 SizeInMajorDir(m_minSize) += GetSizeInMajorDir(sizeMinThis);
2332 }
2333
2334 // In the transversal direction we just need to find the maximum.
3d2085a4
VZ
2335 if ( GetSizeInMinorDir(sizeMinThis) > GetSizeInMinorDir(m_minSize) )
2336 SizeInMinorDir(m_minSize) = GetSizeInMinorDir(sizeMinThis);
61d514bb 2337 }
0c0d686f 2338
f27d62bf
VZ
2339 // Using the max ratio ensures that the min size is big enough for all
2340 // items to have their min size and satisfy the proportions among them.
4a49fa24 2341 SizeInMajorDir(m_minSize) += (int)(maxMinSizeToProp*m_totalProportion);
f27d62bf 2342
89064717 2343 return m_minSize;
61d514bb 2344}
27ea1d8a
RR
2345
2346//---------------------------------------------------------------------------
2347// wxStaticBoxSizer
2348//---------------------------------------------------------------------------
2349
1e6feb95
VZ
2350#if wxUSE_STATBOX
2351
27ea1d8a 2352wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient )
e978011a
VZ
2353 : wxBoxSizer( orient ),
2354 m_staticBox( box )
27ea1d8a 2355{
223d09f6 2356 wxASSERT_MSG( box, wxT("wxStaticBoxSizer needs a static box") );
e978011a
VZ
2357
2358 // do this so that our Detach() is called if the static box is destroyed
2359 // before we are
2360 m_staticBox->SetContainingSizer(this);
27ea1d8a 2361}
0c0d686f 2362
6c1635b5
VZ
2363wxStaticBoxSizer::wxStaticBoxSizer(int orient, wxWindow *win, const wxString& s)
2364 : wxBoxSizer(orient),
2365 m_staticBox(new wxStaticBox(win, wxID_ANY, s))
2366{
e978011a
VZ
2367 // same as above
2368 m_staticBox->SetContainingSizer(this);
6c1635b5
VZ
2369}
2370
649cfca1
VZ
2371wxStaticBoxSizer::~wxStaticBoxSizer()
2372{
2373 delete m_staticBox;
2374}
2375
27ea1d8a
RR
2376void wxStaticBoxSizer::RecalcSizes()
2377{
84028727 2378 int top_border, other_border;
2d5fccc6 2379 m_staticBox->GetBordersForSizer(&top_border, &other_border);
27ea1d8a
RR
2380
2381 m_staticBox->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
0c0d686f 2382
27ea1d8a
RR
2383 wxSize old_size( m_size );
2384 m_size.x -= 2*other_border;
2385 m_size.y -= top_border + other_border;
0c0d686f 2386
2003d003 2387 wxPoint old_pos( m_position );
f25f6620
FM
2388 if (m_staticBox->GetChildren().GetCount() > 0)
2389 {
2003d003 2390#if defined( __WXGTK20__ )
f25f6620
FM
2391 // if the wxStaticBox has created a wxPizza to contain its children
2392 // (see wxStaticBox::AddChild) then we need to place the items it contains
03647350 2393 // in the wxBoxSizer::RecalcSizes() call below using coordinates relative
f25f6620
FM
2394 // to the top-left corner of the staticbox:
2395 m_position.x = m_position.y = 0;
2003d003
FM
2396#else
2397 // if the wxStaticBox has childrens, then these windows must be placed
03647350 2398 // by the wxBoxSizer::RecalcSizes() call below using coordinates relative
2003d003
FM
2399 // to the top-left corner of the staticbox (but unlike wxGTK, we need
2400 // to keep in count the static borders here!):
2401 m_position.x = other_border;
2402 m_position.y = top_border;
f25f6620 2403#endif
2003d003
FM
2404 }
2405 else
2406 {
2407 // the windows contained in the staticbox have been created as siblings of the
2408 // staticbox (this is the "old" way of staticbox contents creation); in this
2409 // case we need to position them with coordinates relative to our common parent
2410 m_position.x += other_border;
2411 m_position.y += top_border;
2412 }
f25f6620 2413
27ea1d8a 2414 wxBoxSizer::RecalcSizes();
0c0d686f 2415
27ea1d8a
RR
2416 m_position = old_pos;
2417 m_size = old_size;
2418}
2419
2420wxSize wxStaticBoxSizer::CalcMin()
2421{
84028727 2422 int top_border, other_border;
2d5fccc6 2423 m_staticBox->GetBordersForSizer(&top_border, &other_border);
0c0d686f 2424
27ea1d8a 2425 wxSize ret( wxBoxSizer::CalcMin() );
cae31b8b 2426 ret.x += 2*other_border;
997e8e29
VZ
2427
2428 // ensure that we're wide enough to show the static box label (there is no
2429 // need to check for the static box best size in vertical direction though)
2430 const int boxWidth = m_staticBox->GetBestSize().x;
2431 if ( ret.x < boxWidth )
2432 ret.x = boxWidth;
2433
27ea1d8a 2434 ret.y += other_border + top_border;
0c0d686f 2435
27ea1d8a
RR
2436 return ret;
2437}
83edc0a5 2438
eb2a7883
VZ
2439void wxStaticBoxSizer::ShowItems( bool show )
2440{
2441 m_staticBox->Show( show );
2442 wxBoxSizer::ShowItems( show );
2443}
2444
e978011a
VZ
2445bool wxStaticBoxSizer::Detach( wxWindow *window )
2446{
2447 // avoid deleting m_staticBox in our dtor if it's being detached from the
2448 // sizer (which can happen because it's being already destroyed for
2449 // example)
2450 if ( window == m_staticBox )
2451 {
2452 m_staticBox = NULL;
2453 return true;
2454 }
2455
2456 return wxSizer::Detach( window );
2457}
2458
1e6feb95
VZ
2459#endif // wxUSE_STATBOX
2460
9b494057
FM
2461//---------------------------------------------------------------------------
2462// wxStdDialogButtonSizer
2463//---------------------------------------------------------------------------
2464
974c2a59
WS
2465#if wxUSE_BUTTON
2466
acf2ac37
RR
2467wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2468 : wxBoxSizer(wxHORIZONTAL)
2469{
94f53923
JS
2470 // Vertical buttons with lots of space on either side
2471 // looks rubbish on WinCE, so let's not do this for now.
2472 // If we are going to use vertical buttons, we should
2473 // put the sizer to the right of other controls in the dialog,
2474 // and that's beyond the scope of this sizer.
2475#ifndef __WXWINCE__
acf2ac37 2476 bool is_pda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
974c2a59 2477 // If we have a PDA screen, put yes/no button over
acf2ac37
RR
2478 // all other buttons, otherwise on the left side.
2479 if (is_pda)
2480 m_orient = wxVERTICAL;
94f53923 2481#endif
974c2a59 2482
acf2ac37
RR
2483 m_buttonAffirmative = NULL;
2484 m_buttonApply = NULL;
2485 m_buttonNegative = NULL;
2486 m_buttonCancel = NULL;
2487 m_buttonHelp = NULL;
2488}
2489
2490void wxStdDialogButtonSizer::AddButton(wxButton *mybutton)
2491{
2492 switch (mybutton->GetId())
2493 {
2494 case wxID_OK:
2495 case wxID_YES:
2496 case wxID_SAVE:
2497 m_buttonAffirmative = mybutton;
2498 break;
2499 case wxID_APPLY:
2500 m_buttonApply = mybutton;
2501 break;
2502 case wxID_NO:
2503 m_buttonNegative = mybutton;
2504 break;
2505 case wxID_CANCEL:
57d7f988 2506 case wxID_CLOSE:
acf2ac37
RR
2507 m_buttonCancel = mybutton;
2508 break;
2509 case wxID_HELP:
2997ca30 2510 case wxID_CONTEXT_HELP:
acf2ac37
RR
2511 m_buttonHelp = mybutton;
2512 break;
2513 default:
2514 break;
2515 }
2516}
2517
b181a505
RR
2518void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton *button )
2519{
2520 m_buttonAffirmative = button;
2521}
2522
2523void wxStdDialogButtonSizer::SetNegativeButton( wxButton *button )
2524{
2525 m_buttonNegative = button;
2526}
2527
2528void wxStdDialogButtonSizer::SetCancelButton( wxButton *button )
2529{
2530 m_buttonCancel = button;
2531}
2532
718903fe 2533void wxStdDialogButtonSizer::Realize()
acf2ac37
RR
2534{
2535#ifdef __WXMAC__
2536 Add(0, 0, 0, wxLEFT, 6);
2537 if (m_buttonHelp)
974c2a59
WS
2538 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2539
acf2ac37
RR
2540 if (m_buttonNegative){
2541 // HIG POLICE BULLETIN - destructive buttons need extra padding
2542 // 24 pixels on either side
2543 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 12);
2544 }
974c2a59 2545
acf2ac37 2546 // extra whitespace between help/negative and cancel/ok buttons
974c2a59
WS
2547 Add(0, 0, 1, wxEXPAND, 0);
2548
acf2ac37
RR
2549 if (m_buttonCancel){
2550 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2551 // Cancel or help should be default
2552 // m_buttonCancel->SetDefaultButton();
2553 }
974c2a59
WS
2554
2555 // Ugh, Mac doesn't really have apply dialogs, so I'll just
acf2ac37
RR
2556 // figure the best place is between Cancel and OK
2557 if (m_buttonApply)
2558 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
974c2a59 2559
acf2ac37
RR
2560 if (m_buttonAffirmative){
2561 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6);
974c2a59 2562
acf2ac37
RR
2563 if (m_buttonAffirmative->GetId() == wxID_SAVE){
2564 // these buttons have set labels under Mac so we should use them
2565 m_buttonAffirmative->SetLabel(_("Save"));
d9485f89
RD
2566 if (m_buttonNegative)
2567 m_buttonNegative->SetLabel(_("Don't Save"));
acf2ac37
RR
2568 }
2569 }
974c2a59 2570
acf2ac37 2571 // Extra space around and at the right
6143d648 2572 Add(12, 40);
acf2ac37 2573#elif defined(__WXGTK20__)
4c624eb1 2574 // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
5d3056f1 2575 // says that the correct button order is
4c624eb1
VZ
2576 //
2577 // [Help] [Alternative] [Cancel] [Affirmative]
4c624eb1
VZ
2578
2579 // Flags ensuring that margins between the buttons are 6 pixels.
2580 const wxSizerFlags
2581 flagsBtn = wxSizerFlags().Centre().Border(wxLEFT | wxRIGHT, 3);
2582
2583 // Margin around the entire sizer button should be 12.
2584 AddSpacer(9);
2585
acf2ac37 2586 if (m_buttonHelp)
4c624eb1 2587 Add(m_buttonHelp, flagsBtn);
974c2a59 2588
a01d6442 2589 // Align the rest of the buttons to the right.
4c624eb1 2590 AddStretchSpacer();
974c2a59 2591
5d3056f1 2592 if (m_buttonNegative)
4c624eb1 2593 Add(m_buttonNegative, flagsBtn);
a01d6442 2594
5d3056f1
VZ
2595 if (m_buttonApply)
2596 Add(m_buttonApply, flagsBtn);
2afba7c4 2597
5d3056f1
VZ
2598 if (m_buttonCancel)
2599 Add(m_buttonCancel, flagsBtn);
2afba7c4 2600
5d3056f1
VZ
2601 if (m_buttonAffirmative)
2602 Add(m_buttonAffirmative, flagsBtn);
974c2a59 2603
4c624eb1
VZ
2604 // Ensure that the right margin is 12 as well.
2605 AddSpacer(9);
0f884515
JS
2606#elif defined(__WXMSW__)
2607 // Windows
2608
2609 // right-justify buttons
2610 Add(0, 0, 1, wxEXPAND, 0);
2611
2612 if (m_buttonAffirmative){
2613 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(2, 0)).x);
2614 }
2615
2616 if (m_buttonNegative){
2617 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(2, 0)).x);
2618 }
2619
2620 if (m_buttonCancel){
2621 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(2, 0)).x);
2622 }
2623 if (m_buttonApply)
2624 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(2, 0)).x);
2625
2626 if (m_buttonHelp)
2627 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(2, 0)).x);
acf2ac37 2628#else
0f884515 2629 // GTK+1 and any other platform
902725ee 2630
23b1018f 2631 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
acf2ac37 2632 if (m_buttonHelp)
974c2a59
WS
2633 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(4, 0)).x);
2634
acf2ac37 2635 // extra whitespace between help and cancel/ok buttons
974c2a59 2636 Add(0, 0, 1, wxEXPAND, 0);
acf2ac37
RR
2637
2638 if (m_buttonApply)
2639 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(4, 0)).x);
974c2a59 2640
acf2ac37
RR
2641 if (m_buttonAffirmative){
2642 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(4, 0)).x);
2643 }
974c2a59 2644
acf2ac37
RR
2645 if (m_buttonNegative){
2646 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(4, 0)).x);
2647 }
974c2a59 2648
acf2ac37 2649 if (m_buttonCancel){
23b1018f 2650 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(4, 0)).x);
acf2ac37
RR
2651 // Cancel or help should be default
2652 // m_buttonCancel->SetDefaultButton();
2653 }
974c2a59 2654
acf2ac37
RR
2655#endif
2656}
adbf2d73 2657
974c2a59 2658#endif // wxUSE_BUTTON