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