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