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