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