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