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