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