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