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