]> git.saurik.com Git - wxWidgets.git/blob - src/common/sizer.cpp
Fixed preview navigation as per ancient bug report
[wxWidgets.git] / src / common / sizer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #ifdef __GNUG__
14 #pragma implementation "sizer.h"
15 #endif
16
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #include "wx/sizer.h"
25 #include "wx/utils.h"
26 #include "wx/statbox.h"
27 #include "wx/notebook.h"
28 #include <wx/listimpl.cpp>
29
30 //---------------------------------------------------------------------------
31
32 IMPLEMENT_CLASS(wxSizerItem, wxObject)
33 IMPLEMENT_CLASS(wxSizer, wxObject)
34 IMPLEMENT_CLASS(wxGridSizer, wxSizer)
35 IMPLEMENT_CLASS(wxFlexGridSizer, wxGridSizer)
36 IMPLEMENT_CLASS(wxBoxSizer, wxSizer)
37 #if wxUSE_STATBOX
38 IMPLEMENT_CLASS(wxStaticBoxSizer, wxBoxSizer)
39 #endif
40 #if wxUSE_NOTEBOOK
41 IMPLEMENT_CLASS(wxNotebookSizer, wxSizer)
42 #endif
43
44 WX_DEFINE_EXPORTED_LIST( wxSizerItemList );
45
46
47 //---------------------------------------------------------------------------
48 // wxSizerItem
49 //---------------------------------------------------------------------------
50
51 wxSizerItem::wxSizerItem( int width, int height, int proportion, int flag, int border, wxObject* userData )
52 : m_window( NULL )
53 , m_sizer( NULL )
54 , m_size( wxSize( width, height ) ) // size is set directly
55 , m_minSize( m_size ) // minimal size is the initial size
56 , m_proportion( proportion )
57 , m_border( border )
58 , m_flag( flag )
59 , m_show( true )
60 , m_userData( userData )
61 {
62 SetRatio( m_size );
63 }
64
65 wxSizerItem::wxSizerItem( wxWindow *window, int proportion, int flag, int border, wxObject* userData )
66 : m_window( window )
67 , m_sizer( NULL )
68 , m_minSize( window->GetSize() ) // minimal size is the initial size
69 , m_proportion( proportion )
70 , m_border( border )
71 , m_flag( flag )
72 , m_show( true )
73 , m_userData( userData )
74 {
75 // aspect ratio calculated from initial size
76 SetRatio( m_minSize );
77
78 // m_size is calculated later
79 }
80
81 wxSizerItem::wxSizerItem( wxSizer *sizer, int proportion, int flag, int border, wxObject* userData )
82 : m_window( NULL )
83 , m_sizer( sizer )
84 , m_proportion( proportion )
85 , m_border( border )
86 , m_flag( flag )
87 , m_show( true )
88 , m_ratio( 0.0 )
89 , m_userData( userData )
90 {
91 // m_minSize is calculated later
92 // m_size is calculated later
93 }
94
95 wxSizerItem::~wxSizerItem()
96 {
97 delete m_userData;
98
99 if ( m_window )
100 {
101 m_window->SetContainingSizer(NULL);
102 }
103 else // we must be a sizer
104 {
105 delete m_sizer;
106 }
107 }
108
109
110 wxSize wxSizerItem::GetSize() const
111 {
112 wxSize ret;
113 if (IsSizer())
114 ret = m_sizer->GetSize();
115 else
116 if (IsWindow())
117 ret = m_window->GetSize();
118 else ret = m_size;
119
120 if (m_flag & wxWEST)
121 ret.x += m_border;
122 if (m_flag & wxEAST)
123 ret.x += m_border;
124 if (m_flag & wxNORTH)
125 ret.y += m_border;
126 if (m_flag & wxSOUTH)
127 ret.y += m_border;
128
129 return ret;
130 }
131
132 wxSize wxSizerItem::CalcMin()
133 {
134 wxSize ret;
135 if (IsSizer())
136 {
137 ret = m_sizer->GetMinSize();
138
139 // if we have to preserve aspect ratio _AND_ this is
140 // the first-time calculation, consider ret to be initial size
141 if ((m_flag & wxSHAPED) && !m_ratio)
142 SetRatio(ret);
143 }
144 else
145 {
146 if ( IsWindow() && (m_flag & wxADJUST_MINSIZE) )
147 {
148 // By user request, keep the minimal size for this item
149 // in sync with the largest of BestSize and any user supplied
150 // minimum size hint. Useful in cases where the item is
151 // changeable -- static text labels, etc.
152 m_minSize = m_window->GetAdjustedBestSize();
153 }
154
155 ret = m_minSize;
156 }
157
158 if (m_flag & wxWEST)
159 ret.x += m_border;
160 if (m_flag & wxEAST)
161 ret.x += m_border;
162 if (m_flag & wxNORTH)
163 ret.y += m_border;
164 if (m_flag & wxSOUTH)
165 ret.y += m_border;
166
167 return ret;
168 }
169
170 void wxSizerItem::SetDimension( wxPoint pos, wxSize size )
171 {
172 if (m_flag & wxSHAPED)
173 {
174 // adjust aspect ratio
175 int rwidth = (int) (size.y * m_ratio);
176 if (rwidth > size.x)
177 {
178 // fit horizontally
179 int rheight = (int) (size.x / m_ratio);
180 // add vertical space
181 if (m_flag & wxALIGN_CENTER_VERTICAL)
182 pos.y += (size.y - rheight) / 2;
183 else if (m_flag & wxALIGN_BOTTOM)
184 pos.y += (size.y - rheight);
185 // use reduced dimensions
186 size.y =rheight;
187 }
188 else if (rwidth < size.x)
189 {
190 // add horizontal space
191 if (m_flag & wxALIGN_CENTER_HORIZONTAL)
192 pos.x += (size.x - rwidth) / 2;
193 else if (m_flag & wxALIGN_RIGHT)
194 pos.x += (size.x - rwidth);
195 size.x = rwidth;
196 }
197 }
198
199 // This is what GetPosition() returns. Since we calculate
200 // borders afterwards, GetPosition() will be the left/top
201 // corner of the surrounding border.
202 m_pos = pos;
203
204 if (m_flag & wxWEST)
205 {
206 pos.x += m_border;
207 size.x -= m_border;
208 }
209 if (m_flag & wxEAST)
210 {
211 size.x -= m_border;
212 }
213 if (m_flag & wxNORTH)
214 {
215 pos.y += m_border;
216 size.y -= m_border;
217 }
218 if (m_flag & wxSOUTH)
219 {
220 size.y -= m_border;
221 }
222
223 if (IsSizer())
224 m_sizer->SetDimension( pos.x, pos.y, size.x, size.y );
225
226 if (IsWindow())
227 m_window->SetSize( pos.x, pos.y, size.x, size.y, wxSIZE_ALLOW_MINUS_ONE );
228
229 m_size = size;
230 }
231
232 void wxSizerItem::DeleteWindows()
233 {
234 if (m_window)
235 m_window->Destroy();
236
237 if (m_sizer)
238 m_sizer->DeleteWindows();
239 }
240
241 bool wxSizerItem::IsWindow() const
242 {
243 return (m_window != NULL);
244 }
245
246 bool wxSizerItem::IsSizer() const
247 {
248 return (m_sizer != NULL);
249 }
250
251 bool wxSizerItem::IsSpacer() const
252 {
253 return (m_window == NULL) && (m_sizer == NULL);
254 }
255
256 void wxSizerItem::Show( bool show )
257 {
258 m_show = show;
259
260 if( IsWindow() )
261 m_window->Show( show );
262 else if( IsSizer() )
263 m_sizer->ShowItems( show );
264
265 // ... nothing else to do to hide/show spacers
266 }
267
268 void wxSizerItem::SetOption( int option )
269 {
270 SetProportion( option );
271 }
272
273 int wxSizerItem::GetOption() const
274 {
275 return GetProportion();
276 }
277
278
279 //---------------------------------------------------------------------------
280 // wxSizer
281 //---------------------------------------------------------------------------
282
283 wxSizer::wxSizer()
284 : m_minSize( wxSize( 0, 0 ) )
285 {
286 }
287
288 wxSizer::~wxSizer()
289 {
290 WX_CLEAR_LIST(wxSizerItemList, m_children);
291 }
292
293 void wxSizer::Add( wxWindow *window, int proportion, int flag, int border, wxObject* userData )
294 {
295 m_children.Append( new wxSizerItem( window, proportion, flag, border, userData ) );
296 window->SetContainingSizer( this );
297 }
298
299 void wxSizer::Add( wxSizer *sizer, int proportion, int flag, int border, wxObject* userData )
300 {
301 m_children.Append( new wxSizerItem( sizer, proportion, flag, border, userData ) );
302 }
303
304 void wxSizer::Add( int width, int height, int proportion, int flag, int border, wxObject* userData )
305 {
306 m_children.Append( new wxSizerItem( width, height, proportion, flag, border, userData ) );
307 }
308
309 void wxSizer::Add( wxSizerItem *item )
310 {
311 m_children.Append( item );
312
313 if( item->GetWindow() )
314 item->GetWindow()->SetContainingSizer( this );
315 }
316
317 void wxSizer::Prepend( wxWindow *window, int proportion, int flag, int border, wxObject* userData )
318 {
319 m_children.Insert( new wxSizerItem( window, proportion, flag, border, userData ) );
320 window->SetContainingSizer( this );
321 }
322
323 void wxSizer::Prepend( wxSizer *sizer, int proportion, int flag, int border, wxObject* userData )
324 {
325 m_children.Insert( new wxSizerItem( sizer, proportion, flag, border, userData ) );
326 }
327
328 void wxSizer::Prepend( int width, int height, int proportion, int flag, int border, wxObject* userData )
329 {
330 m_children.Insert( new wxSizerItem( width, height, proportion, flag, border, userData ) );
331 }
332
333 void wxSizer::Prepend( wxSizerItem *item )
334 {
335 m_children.Insert( item );
336
337 if( item->GetWindow() )
338 item->GetWindow()->SetContainingSizer( this );
339 }
340
341 void wxSizer::Insert( size_t index,
342 wxWindow *window,
343 int proportion,
344 int flag,
345 int border,
346 wxObject* userData )
347 {
348 m_children.Insert( index,
349 new wxSizerItem( window, proportion, flag, border, userData ) );
350 window->SetContainingSizer( this );
351 }
352
353 void wxSizer::Insert( size_t index,
354 wxSizer *sizer,
355 int proportion,
356 int flag,
357 int border,
358 wxObject* userData )
359 {
360 m_children.Insert( index,
361 new wxSizerItem( sizer, proportion, flag, border, userData ) );
362 }
363
364 void wxSizer::Insert( size_t index,
365 int width,
366 int height,
367 int proportion,
368 int flag,
369 int border,
370 wxObject* userData )
371 {
372 m_children.Insert( index,
373 new wxSizerItem( width, height, proportion, flag, border, userData ) );
374 }
375
376 void wxSizer::Insert( size_t index, wxSizerItem *item )
377 {
378 m_children.Insert( index, item );
379
380 if( item->GetWindow() )
381 item->GetWindow()->SetContainingSizer( this );
382 }
383
384 bool wxSizer::Remove( wxWindow *window )
385 {
386 return Detach( window );
387 }
388
389 bool wxSizer::Remove( wxSizer *sizer )
390 {
391 wxASSERT_MSG( sizer, _T("Removing NULL sizer") );
392
393 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
394 while (node)
395 {
396 wxSizerItem *item = node->GetData();
397
398 if (item->GetSizer() == sizer)
399 {
400 delete item;
401 m_children.Erase( node );
402 return true;
403 }
404
405 node = node->GetNext();
406 }
407
408 return false;
409 }
410
411 bool wxSizer::Remove( int index )
412 {
413 wxCHECK_MSG( index >= 0 && (size_t)index < m_children.GetCount(),
414 false,
415 _T("Remove index is out of range") );
416
417 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
418
419 wxCHECK_MSG( node, false, _T("Failed to find child node") );
420
421 wxSizerItem *item = node->GetData();
422
423 if( item->IsWindow() )
424 item->GetWindow()->SetContainingSizer( NULL );
425
426 delete item;
427 m_children.Erase( node );
428 return true;
429 }
430
431 bool wxSizer::Detach( wxSizer *sizer )
432 {
433 wxASSERT_MSG( sizer, _T("Detaching NULL sizer") );
434
435 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
436 while (node)
437 {
438 wxSizerItem *item = node->GetData();
439
440 if (item->GetSizer() == sizer)
441 {
442 item->DetachSizer();
443 delete item;
444 m_children.Erase( node );
445 return true;
446 }
447 node = node->GetNext();
448 }
449
450 return false;
451 }
452
453 bool wxSizer::Detach( wxWindow *window )
454 {
455 wxASSERT_MSG( window, _T("Detaching NULL window") );
456
457 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
458 while (node)
459 {
460 wxSizerItem *item = node->GetData();
461
462 if (item->GetWindow() == window)
463 {
464 item->GetWindow()->SetContainingSizer( NULL );
465 delete item;
466 m_children.Erase( node );
467 return true;
468 }
469 node = node->GetNext();
470 }
471
472 return false;
473 }
474
475 bool wxSizer::Detach( int index )
476 {
477 wxCHECK_MSG( index >= 0 && (size_t)index < m_children.GetCount(),
478 false,
479 _T("Detach index is out of range") );
480
481 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
482
483 wxCHECK_MSG( node, false, _T("Failed to find child node") );
484
485 wxSizerItem *item = node->GetData();
486
487 if( item->IsSizer() )
488 item->DetachSizer();
489 else if( item->IsWindow() )
490 item->GetWindow()->SetContainingSizer( NULL );
491
492 delete item;
493 m_children.Erase( node );
494 return true;
495 }
496
497 void wxSizer::Clear( bool delete_windows )
498 {
499 // First clear the ContainingSizer pointers
500 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
501 while (node)
502 {
503 wxSizerItem *item = node->GetData();
504
505 if (item->IsWindow())
506 item->GetWindow()->SetContainingSizer( NULL );
507 node = node->GetNext();
508 }
509
510 // Destroy the windows if needed
511 if (delete_windows)
512 DeleteWindows();
513
514 // Now empty the list
515 WX_CLEAR_LIST(wxSizerItemList, m_children);
516 }
517
518 void wxSizer::DeleteWindows()
519 {
520 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
521 while (node)
522 {
523 wxSizerItem *item = node->GetData();
524
525 item->DeleteWindows();
526 node = node->GetNext();
527 }
528 }
529
530 wxSize wxSizer::Fit( wxWindow *window )
531 {
532 wxSize size;
533 if (window->IsTopLevel())
534 size = FitSize( window );
535 else
536 size = GetMinWindowSize( window );
537
538 window->SetSize( size );
539
540 return size;
541 }
542
543 void wxSizer::FitInside( wxWindow *window )
544 {
545 wxSize size;
546 if (window->IsTopLevel())
547 size = VirtualFitSize( window );
548 else
549 size = GetMinClientSize( window );
550
551 window->SetVirtualSize( size );
552 }
553
554 void wxSizer::Layout()
555 {
556 CalcMin();
557 RecalcSizes();
558 }
559
560 void wxSizer::SetSizeHints( wxWindow *window )
561 {
562 // Preserve the window's max size hints, but set the
563 // lower bound according to the sizer calculations.
564
565 wxSize size = Fit( window );
566
567 window->SetSizeHints( size.x,
568 size.y,
569 window->GetMaxWidth(),
570 window->GetMaxHeight() );
571 }
572
573 void wxSizer::SetVirtualSizeHints( wxWindow *window )
574 {
575 // Preserve the window's max size hints, but set the
576 // lower bound according to the sizer calculations.
577
578 FitInside( window );
579 wxSize size( window->GetVirtualSize() );
580 window->SetVirtualSizeHints( size.x,
581 size.y,
582 window->GetMaxWidth(),
583 window->GetMaxHeight() );
584 }
585
586 wxSize wxSizer::GetMaxWindowSize( wxWindow *window ) const
587 {
588 return window->GetMaxSize();
589 }
590
591 wxSize wxSizer::GetMinWindowSize( wxWindow *window )
592 {
593 wxSize minSize( GetMinSize() );
594 wxSize size( window->GetSize() );
595 wxSize client_size( window->GetClientSize() );
596
597 return wxSize( minSize.x+size.x-client_size.x,
598 minSize.y+size.y-client_size.y );
599 }
600
601 // Return a window size that will fit within the screens dimensions
602 wxSize wxSizer::FitSize( wxWindow *window )
603 {
604 wxSize size = GetMinWindowSize( window );
605 wxSize sizeMax = GetMaxWindowSize( window );
606
607 // Limit the size if sizeMax != wxDefaultSize
608
609 if ( size.x > sizeMax.x && sizeMax.x != -1 )
610 size.x = sizeMax.x;
611 if ( size.y > sizeMax.y && sizeMax.y != -1 )
612 size.y = sizeMax.y;
613
614 return size;
615 }
616
617 wxSize wxSizer::GetMaxClientSize( wxWindow *window ) const
618 {
619 wxSize maxSize( window->GetMaxSize() );
620
621 if( maxSize != wxDefaultSize )
622 {
623 wxSize size( window->GetSize() );
624 wxSize client_size( window->GetClientSize() );
625
626 return wxSize( maxSize.x + client_size.x - size.x,
627 maxSize.y + client_size.y - size.y );
628 }
629 else
630 return wxDefaultSize;
631 }
632
633 wxSize wxSizer::GetMinClientSize( wxWindow *WXUNUSED(window) )
634 {
635 return GetMinSize(); // Already returns client size.
636 }
637
638 wxSize wxSizer::VirtualFitSize( wxWindow *window )
639 {
640 wxSize size = GetMinClientSize( window );
641 wxSize sizeMax = GetMaxClientSize( window );
642
643 // Limit the size if sizeMax != wxDefaultSize
644
645 if ( size.x > sizeMax.x && sizeMax.x != -1 )
646 size.x = sizeMax.x;
647 if ( size.y > sizeMax.y && sizeMax.y != -1 )
648 size.y = sizeMax.y;
649
650 return size;
651 }
652
653 void wxSizer::SetDimension( int x, int y, int width, int height )
654 {
655 m_position.x = x;
656 m_position.y = y;
657 m_size.x = width;
658 m_size.y = height;
659 Layout();
660 }
661
662 wxSize wxSizer::GetMinSize()
663 {
664 wxSize ret( CalcMin() );
665 if (ret.x < m_minSize.x) ret.x = m_minSize.x;
666 if (ret.y < m_minSize.y) ret.y = m_minSize.y;
667 return ret;
668 }
669
670 void wxSizer::DoSetMinSize( int width, int height )
671 {
672 m_minSize.x = width;
673 m_minSize.y = height;
674 }
675
676 bool wxSizer::DoSetItemMinSize( wxWindow *window, int width, int height )
677 {
678 wxASSERT_MSG( window, _T("SetMinSize for NULL window") );
679
680 // Is it our immediate child?
681
682 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
683 while (node)
684 {
685 wxSizerItem *item = node->GetData();
686
687 if (item->GetWindow() == window)
688 {
689 item->SetInitSize( width, height );
690 return true;
691 }
692 node = node->GetNext();
693 }
694
695 // No? Search any subsizers we own then
696
697 node = m_children.GetFirst();
698 while (node)
699 {
700 wxSizerItem *item = node->GetData();
701
702 if ( item->GetSizer() &&
703 item->GetSizer()->DoSetItemMinSize( window, width, height ) )
704 {
705 // A child sizer found the requested windw, exit.
706 return true;
707 }
708 node = node->GetNext();
709 }
710
711 return false;
712 }
713
714 bool wxSizer::DoSetItemMinSize( wxSizer *sizer, int width, int height )
715 {
716 wxASSERT_MSG( sizer, _T("SetMinSize for NULL sizer") );
717
718 // Is it our immediate child?
719
720 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
721 while (node)
722 {
723 wxSizerItem *item = node->GetData();
724
725 if (item->GetSizer() == sizer)
726 {
727 item->GetSizer()->DoSetMinSize( width, height );
728 return true;
729 }
730 node = node->GetNext();
731 }
732
733 // No? Search any subsizers we own then
734
735 node = m_children.GetFirst();
736 while (node)
737 {
738 wxSizerItem *item = node->GetData();
739
740 if ( item->GetSizer() &&
741 item->GetSizer()->DoSetItemMinSize( sizer, width, height ) )
742 {
743 // A child found the requested sizer, exit.
744 return true;
745 }
746 node = node->GetNext();
747 }
748
749 return false;
750 }
751
752 bool wxSizer::DoSetItemMinSize( size_t index, int width, int height )
753 {
754 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
755
756 wxCHECK_MSG( node, false, _T("Failed to find child node") );
757
758 wxSizerItem *item = node->GetData();
759
760 if (item->GetSizer())
761 {
762 // Sizers contains the minimal size in them, if not calculated ...
763 item->GetSizer()->DoSetMinSize( width, height );
764 }
765 else
766 {
767 // ... but the minimal size of spacers and windows in stored in them
768 item->SetInitSize( width, height );
769 }
770
771 return true;
772 }
773
774 void wxSizer::Show( wxWindow *window, bool show )
775 {
776 wxASSERT_MSG( window, _T("Show for NULL window") );
777
778 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
779 while (node)
780 {
781 wxSizerItem *item = node->GetData();
782
783 if (item->GetWindow() == window)
784 {
785 item->Show( show );
786 break;
787 }
788 node = node->GetNext();
789 }
790 }
791
792 void wxSizer::Show( wxSizer *sizer, bool show )
793 {
794 wxASSERT_MSG( sizer, _T("Show for NULL sizer") );
795
796 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
797 while (node)
798 {
799 wxSizerItem *item = node->GetData();
800
801 if (item->GetSizer() == sizer)
802 {
803 item->Show( show );
804 break;
805 }
806 node = node->GetNext();
807 }
808 }
809
810 void wxSizer::Show( size_t index, bool show )
811 {
812 wxCHECK_RET( index < m_children.GetCount(),
813 _T("Show index is out of range") );
814
815 m_children.Item( index )->GetData()->Show( show );
816 }
817
818 void wxSizer::ShowItems( bool show )
819 {
820 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
821 while (node)
822 {
823 node->GetData()->Show( show );
824 node = node->GetNext();
825 }
826 }
827
828 bool wxSizer::IsShown( wxWindow *window ) const
829 {
830 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
831 while (node)
832 {
833 wxSizerItem *item = node->GetData();
834
835 if (item->GetWindow() == window)
836 {
837 return item->IsShown();
838 }
839 node = node->GetNext();
840 }
841
842 wxFAIL_MSG( _T("IsShown failed to find sizer item") );
843
844 return false;
845 }
846
847 bool wxSizer::IsShown( wxSizer *sizer ) const
848 {
849 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
850 while (node)
851 {
852 wxSizerItem *item = node->GetData();
853
854 if (item->GetSizer() == sizer)
855 {
856 return item->IsShown();
857 }
858 node = node->GetNext();
859 }
860
861 wxFAIL_MSG( _T("IsShown failed to find sizer item") );
862
863 return false;
864 }
865
866 bool wxSizer::IsShown( size_t index ) const
867 {
868 wxCHECK_MSG( index < m_children.GetCount(),
869 false,
870 _T("IsShown index is out of range") );
871
872 return m_children.Item( index )->GetData()->IsShown();
873 }
874
875
876 //---------------------------------------------------------------------------
877 // wxGridSizer
878 //---------------------------------------------------------------------------
879
880 wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
881 : m_rows( rows )
882 , m_cols( cols )
883 , m_vgap( vgap )
884 , m_hgap( hgap )
885 {
886 }
887
888 wxGridSizer::wxGridSizer( int cols, int vgap, int hgap )
889 : m_rows( 0 )
890 , m_cols( cols )
891 , m_vgap( vgap )
892 , m_hgap( hgap )
893 {
894 }
895
896 int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const
897 {
898 int nitems = m_children.GetCount();
899 if ( nitems)
900 {
901 if ( m_cols )
902 {
903 ncols = m_cols;
904 nrows = (nitems + m_cols - 1) / m_cols;
905 }
906 else if ( m_rows )
907 {
908 ncols = (nitems + m_rows - 1) / m_rows;
909 nrows = m_rows;
910 }
911 else // 0 columns, 0 rows?
912 {
913 wxFAIL_MSG( _T("grid sizer must have either rows or columns fixed") );
914
915 nrows = ncols = 0;
916 }
917 }
918
919 return nitems;
920 }
921
922 void wxGridSizer::RecalcSizes()
923 {
924 int nitems, nrows, ncols;
925 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
926 return;
927
928 wxSize sz( GetSize() );
929 wxPoint pt( GetPosition() );
930
931 int w = (sz.x - (ncols - 1) * m_hgap) / ncols;
932 int h = (sz.y - (nrows - 1) * m_vgap) / nrows;
933
934 int x = pt.x;
935 for (int c = 0; c < ncols; c++)
936 {
937 int y = pt.y;
938 for (int r = 0; r < nrows; r++)
939 {
940 int i = r * ncols + c;
941 if (i < nitems)
942 {
943 wxSizerItemList::compatibility_iterator node = m_children.Item( i );
944
945 wxASSERT_MSG( node, _T("Failed to find SizerItemList node") );
946
947 SetItemBounds( node->GetData(), x, y, w, h);
948 }
949 y = y + h + m_vgap;
950 }
951 x = x + w + m_hgap;
952 }
953 }
954
955 wxSize wxGridSizer::CalcMin()
956 {
957 int nitems, nrows, ncols;
958 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
959 return wxSize(10, 10);
960
961 // Find the max width and height for any component
962 int w = 0;
963 int h = 0;
964
965 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
966 while (node)
967 {
968 wxSizerItem *item = node->GetData();
969 wxSize sz( item->CalcMin() );
970
971 w = wxMax( w, sz.x );
972 h = wxMax( h, sz.y );
973
974 node = node->GetNext();
975 }
976
977 return wxSize( ncols * w + (ncols-1) * m_hgap,
978 nrows * h + (nrows-1) * m_vgap );
979 }
980
981 void wxGridSizer::SetItemBounds( wxSizerItem *item, int x, int y, int w, int h )
982 {
983 wxPoint pt( x,y );
984 wxSize sz( item->CalcMin() );
985 int flag = item->GetFlag();
986
987 if ((flag & wxEXPAND) || (flag & wxSHAPED))
988 {
989 sz = wxSize(w, h);
990 }
991 else
992 {
993 if (flag & wxALIGN_CENTER_HORIZONTAL)
994 {
995 pt.x = x + (w - sz.x) / 2;
996 }
997 else if (flag & wxALIGN_RIGHT)
998 {
999 pt.x = x + (w - sz.x);
1000 }
1001
1002 if (flag & wxALIGN_CENTER_VERTICAL)
1003 {
1004 pt.y = y + (h - sz.y) / 2;
1005 }
1006 else if (flag & wxALIGN_BOTTOM)
1007 {
1008 pt.y = y + (h - sz.y);
1009 }
1010 }
1011
1012 item->SetDimension(pt, sz);
1013 }
1014
1015 //---------------------------------------------------------------------------
1016 // wxFlexGridSizer
1017 //---------------------------------------------------------------------------
1018
1019 wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, int vgap, int hgap )
1020 : wxGridSizer( rows, cols, vgap, hgap ),
1021 m_flexDirection(wxBOTH),
1022 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1023 {
1024 }
1025
1026 wxFlexGridSizer::wxFlexGridSizer( int cols, int vgap, int hgap )
1027 : wxGridSizer( cols, vgap, hgap ),
1028 m_flexDirection(wxBOTH),
1029 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1030 {
1031 }
1032
1033 wxFlexGridSizer::~wxFlexGridSizer()
1034 {
1035 }
1036
1037 void wxFlexGridSizer::RecalcSizes()
1038 {
1039 int nitems, nrows, ncols;
1040 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
1041 return;
1042
1043 wxSize sz( GetSize() );
1044 wxSize minsz( CalcMin() );
1045 wxPoint pt( GetPosition() );
1046
1047 // what to do with the rows? by default, resize them proportionally
1048 if ( sz.y > minsz.y && ( (m_flexDirection & wxVERTICAL) || (m_growMode == wxFLEX_GROWMODE_SPECIFIED) ) )
1049 {
1050 int sum_proportions = 0;
1051 int growable_space = 0;
1052 int num = 0;
1053 size_t idx;
1054 for (idx = 0; idx < m_growableRows.GetCount(); idx++)
1055 {
1056 // Since the number of rows/columns can change as items are inserted/deleted, we need
1057 // to verify at runtime that the requested growable rows/columns are still valid.
1058 if (m_growableRows[idx] >= nrows)
1059 continue;
1060 // If all items in a row/column are hidden, that row/column will have a dimension of -1.
1061 // This causes the row/column to be hidden completely.
1062 if (m_rowHeights[ m_growableRows[idx] ] == -1)
1063 continue;
1064 sum_proportions += m_growableRowsProportions[idx];
1065 growable_space += m_rowHeights[ m_growableRows[idx] ];
1066 num++;
1067 }
1068
1069 if (num > 0)
1070 {
1071 for (idx = 0; idx < m_growableRows.GetCount(); idx++)
1072 {
1073 if (m_growableRows[idx] >= nrows )
1074 continue;
1075 if (m_rowHeights[ m_growableRows[idx] ] == -1)
1076 m_rowHeights[ m_growableRows[idx] ] = 0;
1077 else
1078 {
1079 int delta = (sz.y - minsz.y);
1080 if (sum_proportions == 0)
1081 delta = (delta/num) + m_rowHeights[ m_growableRows[idx] ];
1082 else
1083 delta = ((delta+growable_space)*m_growableRowsProportions[idx]) / sum_proportions;
1084 m_rowHeights[ m_growableRows[idx] ] = delta;
1085 }
1086 }
1087 }
1088 }
1089 else if ( (m_growMode == wxFLEX_GROWMODE_ALL) && (sz.y > minsz.y) )
1090 {
1091 // rounding problem?
1092 for ( int row = 0; row < nrows; ++row )
1093 m_rowHeights[ row ] = sz.y / nrows;
1094 }
1095
1096 // the same logic as above but for the columns
1097 if ( sz.x > minsz.x && ( (m_flexDirection & wxHORIZONTAL) || (m_growMode == wxFLEX_GROWMODE_SPECIFIED) ) )
1098 {
1099 int sum_proportions = 0;
1100 int growable_space = 0;
1101 int num = 0;
1102 size_t idx;
1103 for (idx = 0; idx < m_growableCols.GetCount(); idx++)
1104 {
1105 // Since the number of rows/columns can change as items are inserted/deleted, we need
1106 // to verify at runtime that the requested growable rows/columns are still valid.
1107 if (m_growableCols[idx] >= ncols)
1108 continue;
1109 // If all items in a row/column are hidden, that row/column will have a dimension of -1.
1110 // This causes the column to be hidden completely.
1111 if (m_colWidths[ m_growableCols[idx] ] == -1)
1112 continue;
1113 sum_proportions += m_growableColsProportions[idx];
1114 // wtb 5/12/02 bugfix - was m_ColWidths[idx]!!
1115 growable_space += m_colWidths[ m_growableCols[idx] ];
1116 num++;
1117 }
1118
1119 if (num > 0)
1120 {
1121 for (idx = 0; idx < m_growableCols.GetCount(); idx++)
1122 {
1123 if (m_growableCols[idx] >= ncols )
1124 continue;
1125 if (m_colWidths[ m_growableCols[idx] ] == -1)
1126 m_colWidths[ m_growableCols[idx] ] = 0;
1127 else
1128 {
1129 int delta = (sz.x - minsz.x);
1130 if (sum_proportions == 0)
1131 delta = (delta/num) + m_colWidths[ m_growableCols[idx] ];
1132 else
1133 delta = ((delta+growable_space)*m_growableColsProportions[idx])/sum_proportions;
1134 m_colWidths[ m_growableCols[idx] ] = delta;
1135 }
1136 }
1137 }
1138 }
1139 else if ( (m_growMode == wxFLEX_GROWMODE_ALL) && (sz.x > minsz.x) )
1140 {
1141 for ( int col=0; col < ncols; ++col )
1142 m_colWidths[ col ] = sz.x / ncols;
1143 }
1144
1145 sz = wxSize( pt.x + sz.x, pt.y + sz.y );
1146
1147 int x = pt.x;
1148 for (int c = 0; c < ncols; c++)
1149 {
1150 int y = pt.y;
1151 for (int r = 0; r < nrows; r++)
1152 {
1153 int i = r * ncols + c;
1154 if (i < nitems)
1155 {
1156 wxSizerItemList::compatibility_iterator node = m_children.Item( i );
1157
1158 wxASSERT_MSG( node, _T("Failed to find node") );
1159
1160 int w = wxMax( 0, wxMin( m_colWidths[c], sz.x - x ) );
1161 int h = wxMax( 0, wxMin( m_rowHeights[r], sz.y - y ) );
1162
1163 SetItemBounds( node->GetData(), x, y, w, h);
1164 }
1165 y = y + m_rowHeights[r] + m_vgap;
1166 }
1167 x = x + m_colWidths[c] + m_hgap;
1168 }
1169 }
1170
1171 wxSize wxFlexGridSizer::CalcMin()
1172 {
1173 int nrows,
1174 ncols;
1175 size_t i, s;
1176
1177 // Number of rows/columns can change as items are added or removed.
1178 if ( !CalcRowsCols(nrows, ncols) )
1179 return wxSize(10, 10);
1180
1181 m_rowHeights.SetCount(nrows);
1182 m_colWidths.SetCount(ncols);
1183
1184 // We have to recalcuate the sizes in case an item has wxADJUST_MINSIZE, has changed
1185 // minimum size since the previous layout, or has been hidden using wxSizer::Show().
1186 // If all the items in a row/column are hidden, the final dimension of the row/column
1187 // will be -1, indicating that the column itself is hidden.
1188 for( s = m_rowHeights.GetCount(), i = 0; i < s; ++i )
1189 m_rowHeights[ i ] = -1;
1190 for( s = m_colWidths.GetCount(), i = 0; i < s; ++i )
1191 m_colWidths[ i ] = -1;
1192
1193 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1194
1195 i = 0;
1196 while (node)
1197 {
1198 wxSizerItem *item = node->GetData();
1199 if ( item->IsShown() )
1200 {
1201 wxSize sz( item->CalcMin() );
1202 int row = i / ncols;
1203 int col = i % ncols;
1204
1205 m_rowHeights[ row ] = wxMax( wxMax( 0, sz.y ), m_rowHeights[ row ] );
1206 m_colWidths[ col ] = wxMax( wxMax( 0, sz.x ), m_colWidths[ col ] );
1207 }
1208
1209 node = node->GetNext();
1210 i++;
1211 }
1212
1213 // the logic above works when we resize flexibly in both directions but
1214 // maybe this is not the case
1215 if ( m_flexDirection != wxBOTH )
1216 {
1217 // select the array corresponding to the direction in which we do *not*
1218 // resize flexibly
1219 wxArrayInt& array = m_flexDirection == wxVERTICAL ? m_colWidths
1220 : m_rowHeights;
1221
1222 const int count = array.GetCount();
1223
1224 // find the largest value in this array
1225 int n, largest = 0;
1226 for ( n = 0; n < count; ++n )
1227 {
1228 if ( array[n] > largest )
1229 largest = array[n];
1230 }
1231
1232 // and now fill it with the largest value
1233 for ( n = 0; n < count; ++n )
1234 {
1235 array[n] = largest;
1236 }
1237 }
1238
1239 // Sum total minimum size, including gaps between rows/columns.
1240 // -1 is used as a magic number meaning empty column.
1241 int width = 0;
1242 for (int col = 0; col < ncols; col++)
1243 if ( m_colWidths[ col ] != -1 )
1244 width += m_colWidths[ col ] + ( col == ncols-1 ? 0 : m_hgap );
1245
1246 int height = 0;
1247 for (int row = 0; row < nrows; row++)
1248 if ( m_rowHeights[ row ] != -1 )
1249 height += m_rowHeights[ row ] + ( row == nrows-1 ? 0 : m_vgap );
1250
1251 return wxSize( width, height );
1252 }
1253
1254 void wxFlexGridSizer::AddGrowableRow( size_t idx, int proportion )
1255 {
1256 m_growableRows.Add( idx );
1257 m_growableRowsProportions.Add( proportion );
1258 }
1259
1260 void wxFlexGridSizer::RemoveGrowableRow( size_t WXUNUSED(idx) )
1261 {
1262 }
1263
1264 void wxFlexGridSizer::AddGrowableCol( size_t idx, int proportion )
1265 {
1266 m_growableCols.Add( idx );
1267 m_growableColsProportions.Add( proportion );
1268 }
1269
1270 void wxFlexGridSizer::RemoveGrowableCol( size_t WXUNUSED(idx) )
1271 {
1272 }
1273
1274 //---------------------------------------------------------------------------
1275 // wxBoxSizer
1276 //---------------------------------------------------------------------------
1277
1278 wxBoxSizer::wxBoxSizer( int orient )
1279 : m_orient( orient )
1280 {
1281 }
1282
1283 void wxBoxSizer::RecalcSizes()
1284 {
1285 if (m_children.GetCount() == 0)
1286 return;
1287
1288 int delta = 0;
1289 int extra = 0;
1290 if (m_stretchable)
1291 {
1292 if (m_orient == wxHORIZONTAL)
1293 {
1294 delta = (m_size.x - m_fixedWidth) / m_stretchable;
1295 extra = (m_size.x - m_fixedWidth) % m_stretchable;
1296 }
1297 else
1298 {
1299 delta = (m_size.y - m_fixedHeight) / m_stretchable;
1300 extra = (m_size.y - m_fixedHeight) % m_stretchable;
1301 }
1302 }
1303
1304 wxPoint pt( m_position );
1305
1306 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1307 while (node)
1308 {
1309 wxSizerItem *item = node->GetData();
1310
1311 if (item->IsShown())
1312 {
1313 int weight = 1;
1314 if (item->GetProportion())
1315 weight = item->GetProportion();
1316
1317 wxSize size( item->CalcMin() );
1318
1319 if (m_orient == wxVERTICAL)
1320 {
1321 wxCoord height = size.y;
1322 if (item->GetProportion())
1323 {
1324 height = (delta * weight) + extra;
1325 extra = 0; // only the first item will get the remainder as extra size
1326 }
1327
1328 wxPoint child_pos( pt );
1329 wxSize child_size( wxSize( size.x, height) );
1330
1331 if (item->GetFlag() & (wxEXPAND | wxSHAPED))
1332 child_size.x = m_size.x;
1333 else if (item->GetFlag() & wxALIGN_RIGHT)
1334 child_pos.x += m_size.x - size.x;
1335 else if (item->GetFlag() & (wxCENTER | wxALIGN_CENTER_HORIZONTAL))
1336 // XXX wxCENTER is added for backward compatibility;
1337 // wxALIGN_CENTER should be used in new code
1338 child_pos.x += (m_size.x - size.x) / 2;
1339
1340 item->SetDimension( child_pos, child_size );
1341
1342 pt.y += height;
1343 }
1344 else
1345 {
1346 wxCoord width = size.x;
1347 if (item->GetProportion())
1348 {
1349 width = (delta * weight) + extra;
1350 extra = 0; // only the first item will get the remainder as extra size
1351 }
1352
1353 wxPoint child_pos( pt );
1354 wxSize child_size( wxSize(width, size.y) );
1355
1356 if (item->GetFlag() & (wxEXPAND | wxSHAPED))
1357 child_size.y = m_size.y;
1358 else if (item->GetFlag() & wxALIGN_BOTTOM)
1359 child_pos.y += m_size.y - size.y;
1360 else if (item->GetFlag() & (wxCENTER | wxALIGN_CENTER_VERTICAL))
1361 // XXX wxCENTER is added for backward compatibility;
1362 // wxALIGN_CENTER should be used in new code
1363 child_pos.y += (m_size.y - size.y) / 2;
1364
1365 item->SetDimension( child_pos, child_size );
1366
1367 pt.x += width;
1368 }
1369 }
1370
1371 node = node->GetNext();
1372 }
1373 }
1374
1375 wxSize wxBoxSizer::CalcMin()
1376 {
1377 if (m_children.GetCount() == 0)
1378 return wxSize(10,10);
1379
1380 m_stretchable = 0;
1381 m_minWidth = 0;
1382 m_minHeight = 0;
1383 m_fixedWidth = 0;
1384 m_fixedHeight = 0;
1385
1386 // Find how long each stretch unit needs to be
1387 int stretchSize = 1;
1388 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1389
1390 while (node)
1391 {
1392 wxSizerItem *item = node->GetData();
1393
1394 if (item->IsShown() && item->GetProportion() != 0)
1395 {
1396 int stretch = item->GetProportion();
1397 wxSize size( item->CalcMin() );
1398 int sizePerStretch;
1399 // Integer division rounded up is (a + b - 1) / b
1400 if (m_orient == wxHORIZONTAL)
1401 sizePerStretch = ( size.x + stretch - 1 ) / stretch;
1402 else
1403 sizePerStretch = ( size.y + stretch - 1 ) / stretch;
1404 if (sizePerStretch > stretchSize)
1405 stretchSize = sizePerStretch;
1406 }
1407 node = node->GetNext();
1408 }
1409
1410 // Calculate overall minimum size
1411 node = m_children.GetFirst();
1412 while (node)
1413 {
1414 wxSizerItem *item = node->GetData();
1415
1416 if (item->IsShown())
1417 {
1418 m_stretchable += item->GetProportion();
1419
1420 wxSize size( item->CalcMin() );
1421 if (item->GetProportion() != 0)
1422 {
1423 if (m_orient == wxHORIZONTAL)
1424 size.x = stretchSize * item->GetProportion();
1425 else
1426 size.y = stretchSize * item->GetProportion();
1427 }
1428
1429 if (m_orient == wxHORIZONTAL)
1430 {
1431 m_minWidth += size.x;
1432 m_minHeight = wxMax( m_minHeight, size.y );
1433 }
1434 else
1435 {
1436 m_minHeight += size.y;
1437 m_minWidth = wxMax( m_minWidth, size.x );
1438 }
1439
1440 if (item->GetProportion() == 0)
1441 {
1442 if (m_orient == wxVERTICAL)
1443 {
1444 m_fixedHeight += size.y;
1445 m_fixedWidth = wxMax( m_fixedWidth, size.x );
1446 }
1447 else
1448 {
1449 m_fixedWidth += size.x;
1450 m_fixedHeight = wxMax( m_fixedHeight, size.y );
1451 }
1452 }
1453 }
1454 node = node->GetNext();
1455 }
1456
1457 return wxSize( m_minWidth, m_minHeight );
1458 }
1459
1460 //---------------------------------------------------------------------------
1461 // wxStaticBoxSizer
1462 //---------------------------------------------------------------------------
1463
1464 #if wxUSE_STATBOX
1465
1466 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient )
1467 : wxBoxSizer( orient )
1468 , m_staticBox( box )
1469 {
1470 wxASSERT_MSG( box, wxT("wxStaticBoxSizer needs a static box") );
1471 }
1472
1473 static void GetStaticBoxBorders( wxStaticBox *box,
1474 int *borderTop,
1475 int *borderOther)
1476 {
1477 // this has to be done platform by platform as there is no way to
1478 // guess the thickness of a wxStaticBox border
1479 #ifdef __WXCOCOA__
1480 box->GetBordersForSizer(borderTop,borderOther);
1481 #else // __WXCOCOA__
1482 #ifdef __WXGTK__
1483 if ( box->GetLabel().IsEmpty() )
1484 *borderTop = 5;
1485 else
1486 #endif // __WXGTK__
1487 *borderTop = box->GetCharHeight();
1488
1489 *borderOther = 5;
1490 #endif // __WXCOCOA__
1491 }
1492
1493 void wxStaticBoxSizer::RecalcSizes()
1494 {
1495 int top_border, other_border;
1496 GetStaticBoxBorders(m_staticBox, &top_border, &other_border);
1497
1498 m_staticBox->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
1499
1500 wxPoint old_pos( m_position );
1501 m_position.x += other_border;
1502 m_position.y += top_border;
1503 wxSize old_size( m_size );
1504 m_size.x -= 2*other_border;
1505 m_size.y -= top_border + other_border;
1506
1507 wxBoxSizer::RecalcSizes();
1508
1509 m_position = old_pos;
1510 m_size = old_size;
1511 }
1512
1513 wxSize wxStaticBoxSizer::CalcMin()
1514 {
1515 int top_border, other_border;
1516 GetStaticBoxBorders(m_staticBox, &top_border, &other_border);
1517
1518 wxSize ret( wxBoxSizer::CalcMin() );
1519 ret.x += 2*other_border;
1520 ret.y += other_border + top_border;
1521
1522 return ret;
1523 }
1524
1525 #endif // wxUSE_STATBOX
1526
1527 //---------------------------------------------------------------------------
1528 // wxNotebookSizer
1529 //---------------------------------------------------------------------------
1530
1531 #if wxUSE_NOTEBOOK
1532
1533 wxNotebookSizer::wxNotebookSizer( wxNotebook *nb )
1534 : m_notebook( nb )
1535 {
1536 wxASSERT_MSG( nb, wxT("wxNotebookSizer needs a notebook") );
1537 }
1538
1539 void wxNotebookSizer::RecalcSizes()
1540 {
1541 m_notebook->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
1542 }
1543
1544 wxSize wxNotebookSizer::CalcMin()
1545 {
1546 wxSize sizeBorder = m_notebook->CalcSizeFromPage(wxSize(0, 0));
1547
1548 sizeBorder.x += 5;
1549 sizeBorder.y += 5;
1550
1551 if (m_notebook->GetChildren().GetCount() == 0)
1552 {
1553 return wxSize(sizeBorder.x + 10, sizeBorder.y + 10);
1554 }
1555
1556 int maxX = 0;
1557 int maxY = 0;
1558
1559 wxWindowList::compatibility_iterator node = m_notebook->GetChildren().GetFirst();
1560 while (node)
1561 {
1562 wxWindow *item = node->GetData();
1563 wxSizer *itemsizer = item->GetSizer();
1564
1565 if (itemsizer)
1566 {
1567 wxSize subsize( itemsizer->CalcMin() );
1568
1569 if (subsize.x > maxX)
1570 maxX = subsize.x;
1571 if (subsize.y > maxY)
1572 maxY = subsize.y;
1573 }
1574
1575 node = node->GetNext();
1576 }
1577
1578 return wxSize( maxX, maxY ) + sizeBorder;
1579 }
1580
1581 #endif // wxUSE_NOTEBOOK
1582
1583 // vi:sts=4:sw=4:et