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