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