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