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