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