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