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