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