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