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