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