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