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