]> git.saurik.com Git - wxWidgets.git/blob - src/common/sizer.cpp
bb2ca4fe1635e5ed8bd421a6322593103d454de2
[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 = GetMinWindowSize(window);
841 wxSize sizeMax = GetMaxWindowSize(window);
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 size = tlw->GetSize();
850 }
851 else // normal situation
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 }
864
865 if ( sizeMax.x != wxDefaultCoord && size.x > sizeMax.x )
866 size.x = sizeMax.x;
867 if ( sizeMax.y != wxDefaultCoord && size.y > sizeMax.y )
868 size.y = sizeMax.y;
869
870
871 window->SetSize( size );
872
873 return size;
874 }
875
876 void wxSizer::FitInside( wxWindow *window )
877 {
878 wxSize size;
879 if (window->IsTopLevel())
880 size = VirtualFitSize( window );
881 else
882 size = GetMinClientSize( window );
883
884 window->SetVirtualSize( size );
885 }
886
887 void wxSizer::Layout()
888 {
889 // (re)calculates minimums needed for each item and other preparations
890 // for layout
891 CalcMin();
892
893 // Applies the layout and repositions/resizes the items
894 RecalcSizes();
895 }
896
897 void wxSizer::SetSizeHints( wxWindow *window )
898 {
899 // Preserve the window's max size hints, but set the
900 // lower bound according to the sizer calculations.
901
902 wxSize size = Fit( window );
903
904 window->SetSizeHints( size.x,
905 size.y,
906 window->GetMaxWidth(),
907 window->GetMaxHeight() );
908 }
909
910 void wxSizer::SetVirtualSizeHints( wxWindow *window )
911 {
912 // Preserve the window's max size hints, but set the
913 // lower bound according to the sizer calculations.
914
915 FitInside( window );
916 wxSize size( window->GetVirtualSize() );
917 window->SetVirtualSizeHints( size.x,
918 size.y,
919 window->GetMaxWidth(),
920 window->GetMaxHeight() );
921 }
922
923 wxSize wxSizer::GetMaxWindowSize( wxWindow *window ) const
924 {
925 return window->GetMaxSize();
926 }
927
928 wxSize wxSizer::GetMinWindowSize( wxWindow *window )
929 {
930 wxSize minSize( GetMinSize() );
931 wxSize size( window->GetSize() );
932 wxSize client_size( window->GetClientSize() );
933
934 return wxSize( minSize.x+size.x-client_size.x,
935 minSize.y+size.y-client_size.y );
936 }
937
938 // TODO on mac we need a function that determines how much free space this
939 // min size contains, in order to make sure that we have 20 pixels of free
940 // space around the controls
941 wxSize wxSizer::GetMaxClientSize( wxWindow *window ) const
942 {
943 wxSize maxSize( window->GetMaxSize() );
944
945 if ( maxSize != wxDefaultSize )
946 {
947 wxSize size( window->GetSize() );
948 wxSize client_size( window->GetClientSize() );
949
950 return wxSize( maxSize.x + client_size.x - size.x,
951 maxSize.y + client_size.y - size.y );
952 }
953 else
954 return wxDefaultSize;
955 }
956
957 wxSize wxSizer::GetMinClientSize( wxWindow *WXUNUSED(window) )
958 {
959 return GetMinSize(); // Already returns client size.
960 }
961
962 wxSize wxSizer::VirtualFitSize( wxWindow *window )
963 {
964 wxSize size = GetMinClientSize( window );
965 wxSize sizeMax = GetMaxClientSize( window );
966
967 // Limit the size if sizeMax != wxDefaultSize
968
969 if ( size.x > sizeMax.x && sizeMax.x != wxDefaultCoord )
970 size.x = sizeMax.x;
971 if ( size.y > sizeMax.y && sizeMax.y != wxDefaultCoord )
972 size.y = sizeMax.y;
973
974 return size;
975 }
976
977 void wxSizer::SetDimension( int x, int y, int width, int height )
978 {
979 m_position.x = x;
980 m_position.y = y;
981 m_size.x = width;
982 m_size.y = height;
983 Layout();
984 }
985
986 wxSize wxSizer::GetMinSize()
987 {
988 wxSize ret( CalcMin() );
989 if (ret.x < m_minSize.x) ret.x = m_minSize.x;
990 if (ret.y < m_minSize.y) ret.y = m_minSize.y;
991 return ret;
992 }
993
994 void wxSizer::DoSetMinSize( int width, int height )
995 {
996 m_minSize.x = width;
997 m_minSize.y = height;
998 }
999
1000 bool wxSizer::DoSetItemMinSize( wxWindow *window, int width, int height )
1001 {
1002 wxASSERT_MSG( window, _T("SetMinSize for NULL window") );
1003
1004 // Is it our immediate child?
1005
1006 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1007 while (node)
1008 {
1009 wxSizerItem *item = node->GetData();
1010
1011 if (item->GetWindow() == window)
1012 {
1013 item->SetMinSize( width, height );
1014 return true;
1015 }
1016 node = node->GetNext();
1017 }
1018
1019 // No? Search any subsizers we own then
1020
1021 node = m_children.GetFirst();
1022 while (node)
1023 {
1024 wxSizerItem *item = node->GetData();
1025
1026 if ( item->GetSizer() &&
1027 item->GetSizer()->DoSetItemMinSize( window, width, height ) )
1028 {
1029 // A child sizer found the requested windw, exit.
1030 return true;
1031 }
1032 node = node->GetNext();
1033 }
1034
1035 return false;
1036 }
1037
1038 bool wxSizer::DoSetItemMinSize( wxSizer *sizer, int width, int height )
1039 {
1040 wxASSERT_MSG( sizer, _T("SetMinSize for NULL sizer") );
1041
1042 // Is it our immediate child?
1043
1044 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1045 while (node)
1046 {
1047 wxSizerItem *item = node->GetData();
1048
1049 if (item->GetSizer() == sizer)
1050 {
1051 item->GetSizer()->DoSetMinSize( width, height );
1052 return true;
1053 }
1054 node = node->GetNext();
1055 }
1056
1057 // No? Search any subsizers we own then
1058
1059 node = m_children.GetFirst();
1060 while (node)
1061 {
1062 wxSizerItem *item = node->GetData();
1063
1064 if ( item->GetSizer() &&
1065 item->GetSizer()->DoSetItemMinSize( sizer, width, height ) )
1066 {
1067 // A child found the requested sizer, exit.
1068 return true;
1069 }
1070 node = node->GetNext();
1071 }
1072
1073 return false;
1074 }
1075
1076 bool wxSizer::DoSetItemMinSize( size_t index, int width, int height )
1077 {
1078 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
1079
1080 wxCHECK_MSG( node, false, _T("Failed to find child node") );
1081
1082 wxSizerItem *item = node->GetData();
1083
1084 if (item->GetSizer())
1085 {
1086 // Sizers contains the minimal size in them, if not calculated ...
1087 item->GetSizer()->DoSetMinSize( width, height );
1088 }
1089 else
1090 {
1091 // ... but the minimal size of spacers and windows is stored via the item
1092 item->SetMinSize( width, height );
1093 }
1094
1095 return true;
1096 }
1097
1098 wxSizerItem* wxSizer::GetItem( wxWindow *window, bool recursive )
1099 {
1100 wxASSERT_MSG( window, _T("GetItem for NULL window") );
1101
1102 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1103 while (node)
1104 {
1105 wxSizerItem *item = node->GetData();
1106
1107 if (item->GetWindow() == window)
1108 {
1109 return item;
1110 }
1111 else if (recursive && item->IsSizer())
1112 {
1113 wxSizerItem *subitem = item->GetSizer()->GetItem( window, true );
1114 if (subitem)
1115 return subitem;
1116 }
1117
1118 node = node->GetNext();
1119 }
1120
1121 return NULL;
1122 }
1123
1124 wxSizerItem* wxSizer::GetItem( wxSizer *sizer, bool recursive )
1125 {
1126 wxASSERT_MSG( sizer, _T("GetItem for NULL sizer") );
1127
1128 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1129 while (node)
1130 {
1131 wxSizerItem *item = node->GetData();
1132
1133 if (item->GetSizer() == sizer)
1134 {
1135 return item;
1136 }
1137 else if (recursive && item->IsSizer())
1138 {
1139 wxSizerItem *subitem = item->GetSizer()->GetItem( sizer, true );
1140 if (subitem)
1141 return subitem;
1142 }
1143
1144 node = node->GetNext();
1145 }
1146
1147 return NULL;
1148 }
1149
1150 wxSizerItem* wxSizer::GetItem( size_t index )
1151 {
1152 wxCHECK_MSG( index < m_children.GetCount(),
1153 NULL,
1154 _T("GetItem index is out of range") );
1155
1156 return m_children.Item( index )->GetData();
1157 }
1158
1159 wxSizerItem* wxSizer::GetItemById( int id, bool recursive )
1160 {
1161 // This gets a sizer item by the id of the sizer item
1162 // and NOT the id of a window if the item is a window.
1163
1164 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1165 while (node)
1166 {
1167 wxSizerItem *item = node->GetData();
1168
1169 if (item->GetId() == id)
1170 {
1171 return item;
1172 }
1173 else if (recursive && item->IsSizer())
1174 {
1175 wxSizerItem *subitem = item->GetSizer()->GetItemById( id, true );
1176 if (subitem)
1177 return subitem;
1178 }
1179
1180 node = node->GetNext();
1181 }
1182
1183 return NULL;
1184 }
1185
1186 bool wxSizer::Show( wxWindow *window, bool show, bool recursive )
1187 {
1188 wxSizerItem *item = GetItem( window, recursive );
1189
1190 if ( item )
1191 {
1192 item->Show( show );
1193 return true;
1194 }
1195
1196 return false;
1197 }
1198
1199 bool wxSizer::Show( wxSizer *sizer, bool show, bool recursive )
1200 {
1201 wxSizerItem *item = GetItem( sizer, recursive );
1202
1203 if ( item )
1204 {
1205 item->Show( show );
1206 return true;
1207 }
1208
1209 return false;
1210 }
1211
1212 bool wxSizer::Show( size_t index, bool show)
1213 {
1214 wxSizerItem *item = GetItem( index );
1215
1216 if ( item )
1217 {
1218 item->Show( show );
1219 return true;
1220 }
1221
1222 return false;
1223 }
1224
1225 void wxSizer::ShowItems( bool show )
1226 {
1227 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1228 while (node)
1229 {
1230 node->GetData()->Show( show );
1231 node = node->GetNext();
1232 }
1233 }
1234
1235 bool wxSizer::IsShown( wxWindow *window ) const
1236 {
1237 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1238 while (node)
1239 {
1240 wxSizerItem *item = node->GetData();
1241
1242 if (item->GetWindow() == window)
1243 {
1244 return item->IsShown();
1245 }
1246 node = node->GetNext();
1247 }
1248
1249 wxFAIL_MSG( _T("IsShown failed to find sizer item") );
1250
1251 return false;
1252 }
1253
1254 bool wxSizer::IsShown( wxSizer *sizer ) const
1255 {
1256 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1257 while (node)
1258 {
1259 wxSizerItem *item = node->GetData();
1260
1261 if (item->GetSizer() == sizer)
1262 {
1263 return item->IsShown();
1264 }
1265 node = node->GetNext();
1266 }
1267
1268 wxFAIL_MSG( _T("IsShown failed to find sizer item") );
1269
1270 return false;
1271 }
1272
1273 bool wxSizer::IsShown( size_t index ) const
1274 {
1275 wxCHECK_MSG( index < m_children.GetCount(),
1276 false,
1277 _T("IsShown index is out of range") );
1278
1279 return m_children.Item( index )->GetData()->IsShown();
1280 }
1281
1282
1283 //---------------------------------------------------------------------------
1284 // wxGridSizer
1285 //---------------------------------------------------------------------------
1286
1287 wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
1288 : m_rows( ( cols == 0 && rows == 0 ) ? 1 : rows )
1289 , m_cols( cols )
1290 , m_vgap( vgap )
1291 , m_hgap( hgap )
1292 {
1293 }
1294
1295 wxGridSizer::wxGridSizer( int cols, int vgap, int hgap )
1296 : m_rows( cols == 0 ? 1 : 0 )
1297 , m_cols( cols )
1298 , m_vgap( vgap )
1299 , m_hgap( hgap )
1300 {
1301 }
1302
1303 int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const
1304 {
1305 int nitems = m_children.GetCount();
1306 if ( nitems)
1307 {
1308 if ( m_cols )
1309 {
1310 ncols = m_cols;
1311 nrows = (nitems + m_cols - 1) / m_cols;
1312 }
1313 else if ( m_rows )
1314 {
1315 ncols = (nitems + m_rows - 1) / m_rows;
1316 nrows = m_rows;
1317 }
1318 else // 0 columns, 0 rows?
1319 {
1320 wxFAIL_MSG( _T("grid sizer must have either rows or columns fixed") );
1321
1322 nrows = ncols = 0;
1323 }
1324 }
1325
1326 return nitems;
1327 }
1328
1329 void wxGridSizer::RecalcSizes()
1330 {
1331 int nitems, nrows, ncols;
1332 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
1333 return;
1334
1335 wxSize sz( GetSize() );
1336 wxPoint pt( GetPosition() );
1337
1338 int w = (sz.x - (ncols - 1) * m_hgap) / ncols;
1339 int h = (sz.y - (nrows - 1) * m_vgap) / nrows;
1340
1341 int x = pt.x;
1342 for (int c = 0; c < ncols; c++)
1343 {
1344 int y = pt.y;
1345 for (int r = 0; r < nrows; r++)
1346 {
1347 int i = r * ncols + c;
1348 if (i < nitems)
1349 {
1350 wxSizerItemList::compatibility_iterator node = m_children.Item( i );
1351
1352 wxASSERT_MSG( node, _T("Failed to find SizerItemList node") );
1353
1354 SetItemBounds( node->GetData(), x, y, w, h);
1355 }
1356 y = y + h + m_vgap;
1357 }
1358 x = x + w + m_hgap;
1359 }
1360 }
1361
1362 wxSize wxGridSizer::CalcMin()
1363 {
1364 int nrows, ncols;
1365 if ( CalcRowsCols(nrows, ncols) == 0 )
1366 return wxSize();
1367
1368 // Find the max width and height for any component
1369 int w = 0;
1370 int h = 0;
1371
1372 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1373 while (node)
1374 {
1375 wxSizerItem *item = node->GetData();
1376 wxSize sz( item->CalcMin() );
1377
1378 w = wxMax( w, sz.x );
1379 h = wxMax( h, sz.y );
1380
1381 node = node->GetNext();
1382 }
1383
1384 // In case we have a nested sizer with a two step algo , give it
1385 // a chance to adjust to that (we give it width component)
1386 node = m_children.GetFirst();
1387 bool didChangeMinSize = false;
1388 while (node)
1389 {
1390 wxSizerItem *item = node->GetData();
1391 didChangeMinSize |= item->InformFirstDirection( wxHORIZONTAL, w, -1 );
1392
1393 node = node->GetNext();
1394 }
1395
1396 // And redo iteration in case min size changed
1397 if( didChangeMinSize )
1398 {
1399 node = m_children.GetFirst();
1400 w = h = 0;
1401 while (node)
1402 {
1403 wxSizerItem *item = node->GetData();
1404 wxSize sz( item->GetMinSizeWithBorder() );
1405
1406 w = wxMax( w, sz.x );
1407 h = wxMax( h, sz.y );
1408
1409 node = node->GetNext();
1410 }
1411 }
1412
1413 return wxSize( ncols * w + (ncols-1) * m_hgap,
1414 nrows * h + (nrows-1) * m_vgap );
1415 }
1416
1417 void wxGridSizer::SetItemBounds( wxSizerItem *item, int x, int y, int w, int h )
1418 {
1419 wxPoint pt( x,y );
1420 wxSize sz( item->GetMinSizeWithBorder() );
1421 int flag = item->GetFlag();
1422
1423 if ((flag & wxEXPAND) || (flag & wxSHAPED))
1424 {
1425 sz = wxSize(w, h);
1426 }
1427 else
1428 {
1429 if (flag & wxALIGN_CENTER_HORIZONTAL)
1430 {
1431 pt.x = x + (w - sz.x) / 2;
1432 }
1433 else if (flag & wxALIGN_RIGHT)
1434 {
1435 pt.x = x + (w - sz.x);
1436 }
1437
1438 if (flag & wxALIGN_CENTER_VERTICAL)
1439 {
1440 pt.y = y + (h - sz.y) / 2;
1441 }
1442 else if (flag & wxALIGN_BOTTOM)
1443 {
1444 pt.y = y + (h - sz.y);
1445 }
1446 }
1447
1448 item->SetDimension(pt, sz);
1449 }
1450
1451 //---------------------------------------------------------------------------
1452 // wxFlexGridSizer
1453 //---------------------------------------------------------------------------
1454
1455 wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, int vgap, int hgap )
1456 : wxGridSizer( rows, cols, vgap, hgap ),
1457 m_flexDirection(wxBOTH),
1458 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1459 {
1460 }
1461
1462 wxFlexGridSizer::wxFlexGridSizer( int cols, int vgap, int hgap )
1463 : wxGridSizer( cols, vgap, hgap ),
1464 m_flexDirection(wxBOTH),
1465 m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1466 {
1467 }
1468
1469 wxFlexGridSizer::~wxFlexGridSizer()
1470 {
1471 }
1472
1473 void wxFlexGridSizer::RecalcSizes()
1474 {
1475 int nrows, ncols;
1476 if ( !CalcRowsCols(nrows, ncols) )
1477 return;
1478
1479 const wxPoint pt(GetPosition());
1480 const wxSize sz(GetSize());
1481
1482 AdjustForGrowables(sz);
1483
1484 wxSizerItemList::const_iterator i = m_children.begin();
1485 const wxSizerItemList::const_iterator end = m_children.end();
1486
1487 int y = 0;
1488 for ( int r = 0; r < nrows; r++ )
1489 {
1490 if ( m_rowHeights[r] == -1 )
1491 {
1492 // this row is entirely hidden, skip it
1493 for ( int c = 0; c < ncols; c++ )
1494 {
1495 if ( i == end )
1496 return;
1497
1498 ++i;
1499 }
1500
1501 continue;
1502 }
1503
1504 const int hrow = m_rowHeights[r];
1505 int h = sz.y - y; // max remaining height, don't overflow it
1506 if ( hrow < h )
1507 h = hrow;
1508
1509 int x = 0;
1510 for ( int c = 0; c < ncols && i != end; c++, ++i )
1511 {
1512 const int wcol = m_colWidths[c];
1513
1514 if ( wcol == -1 )
1515 continue;
1516
1517 int w = sz.x - x; // max possible value, ensure we don't overflow
1518 if ( wcol < w )
1519 w = wcol;
1520
1521 SetItemBounds(*i, pt.x + x, pt.y + y, w, h);
1522
1523 x += wcol + m_hgap;
1524 }
1525
1526 if ( i == end )
1527 return;
1528
1529 y += hrow + m_vgap;
1530 }
1531 }
1532
1533 // helper function used in CalcMin() to sum up the sizes of non-hidden items
1534 static int SumArraySizes(const wxArrayInt& sizes, int gap)
1535 {
1536 // Sum total minimum size, including gaps between rows/columns.
1537 // -1 is used as a magic number meaning empty row/column.
1538 int total = 0;
1539
1540 const size_t count = sizes.size();
1541 for ( size_t n = 0; n < count; n++ )
1542 {
1543 if ( sizes[n] != -1 )
1544 {
1545 if ( total )
1546 total += gap; // separate from the previous column
1547
1548 total += sizes[n];
1549 }
1550 }
1551
1552 return total;
1553 }
1554
1555 void wxFlexGridSizer::FindWidthsAndHeights(int nrows, int ncols)
1556 {
1557 // We have to recalculate the sizes in case the item minimum size has
1558 // changed since the previous layout, or the item has been hidden using
1559 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1560 // dimension of the row/column will be -1, indicating that the column
1561 // itself is hidden.
1562 m_rowHeights.assign(nrows, -1);
1563 m_colWidths.assign(ncols, -1);
1564
1565 // n is the index of the item in left-to-right top-to-bottom order
1566 size_t n = 0;
1567 for ( wxSizerItemList::iterator i = m_children.begin();
1568 i != m_children.end();
1569 ++i, ++n )
1570 {
1571 wxSizerItem * const item = *i;
1572 if ( item->IsShown() )
1573 {
1574 // NOTE: Not doing the calculation here, this is just
1575 // for finding max values.
1576 const wxSize sz(item->GetMinSizeWithBorder());
1577
1578 const int row = n / ncols;
1579 const int col = n % ncols;
1580
1581 if ( sz.y > m_rowHeights[row] )
1582 m_rowHeights[row] = sz.y;
1583 if ( sz.x > m_colWidths[col] )
1584 m_colWidths[col] = sz.x;
1585 }
1586 }
1587
1588 AdjustForFlexDirection();
1589
1590 m_calculatedMinSize = wxSize(SumArraySizes(m_colWidths, m_hgap),
1591 SumArraySizes(m_rowHeights, m_vgap));
1592 }
1593
1594 wxSize wxFlexGridSizer::CalcMin()
1595 {
1596 int nrows,
1597 ncols;
1598
1599 // Number of rows/columns can change as items are added or removed.
1600 if ( !CalcRowsCols(nrows, ncols) )
1601 return wxSize();
1602
1603
1604 // We have to recalculate the sizes in case the item minimum size has
1605 // changed since the previous layout, or the item has been hidden using
1606 // wxSizer::Show(). If all the items in a row/column are hidden, the final
1607 // dimension of the row/column will be -1, indicating that the column
1608 // itself is hidden.
1609 m_rowHeights.assign(nrows, -1);
1610 m_colWidths.assign(ncols, -1);
1611
1612 // n is the index of the item in left-to-right top-to-bottom order
1613 size_t n = 0;
1614 for ( wxSizerItemList::iterator i = m_children.begin();
1615 i != m_children.end();
1616 ++i, ++n )
1617 {
1618 wxSizerItem * const item = *i;
1619 if ( item->IsShown() )
1620 {
1621 item->CalcMin();
1622 }
1623 }
1624
1625 // The stage of looking for max values in each row/column has been
1626 // made a separate function, since it's reused in AdjustForGrowables.
1627 FindWidthsAndHeights(nrows,ncols);
1628
1629 return m_calculatedMinSize;
1630 }
1631
1632 void wxFlexGridSizer::AdjustForFlexDirection()
1633 {
1634 // the logic in CalcMin works when we resize flexibly in both directions
1635 // but maybe this is not the case
1636 if ( m_flexDirection != wxBOTH )
1637 {
1638 // select the array corresponding to the direction in which we do *not*
1639 // resize flexibly
1640 wxArrayInt& array = m_flexDirection == wxVERTICAL ? m_colWidths
1641 : m_rowHeights;
1642
1643 const size_t count = array.GetCount();
1644
1645 // find the largest value in this array
1646 size_t n;
1647 int largest = 0;
1648
1649 for ( n = 0; n < count; ++n )
1650 {
1651 if ( array[n] > largest )
1652 largest = array[n];
1653 }
1654
1655 // and now fill it with the largest value
1656 for ( n = 0; n < count; ++n )
1657 {
1658 // don't touch hidden rows
1659 if ( array[n] != -1 )
1660 array[n] = largest;
1661 }
1662 }
1663 }
1664
1665 // helper of AdjustForGrowables() which is called for rows/columns separately
1666 //
1667 // parameters:
1668 // delta: the extra space, we do nothing unless it's positive
1669 // growable: indices or growable rows/cols in sizes array
1670 // sizes: the height/widths of rows/cols to adjust
1671 // proportions: proportions of the growable rows/cols or NULL if they all
1672 // should be assumed to have proportion of 1
1673 static void
1674 DoAdjustForGrowables(int delta,
1675 const wxArrayInt& growable,
1676 wxArrayInt& sizes,
1677 const wxArrayInt *proportions)
1678 {
1679 if ( delta <= 0 )
1680 return;
1681
1682 // total sum of proportions of all non-hidden rows
1683 int sum_proportions = 0;
1684
1685 // number of currently shown growable rows
1686 int num = 0;
1687
1688 const int max_idx = sizes.size();
1689
1690 const size_t count = growable.size();
1691 size_t idx;
1692 for ( idx = 0; idx < count; idx++ )
1693 {
1694 // Since the number of rows/columns can change as items are
1695 // inserted/deleted, we need to verify at runtime that the
1696 // requested growable rows/columns are still valid.
1697 if ( growable[idx] >= max_idx )
1698 continue;
1699
1700 // If all items in a row/column are hidden, that row/column will
1701 // have a dimension of -1. This causes the row/column to be
1702 // hidden completely.
1703 if ( sizes[growable[idx]] == -1 )
1704 continue;
1705
1706 if ( proportions )
1707 sum_proportions += (*proportions)[idx];
1708
1709 num++;
1710 }
1711
1712 if ( !num )
1713 return;
1714
1715 // the remaining extra free space, adjusted during each iteration
1716 for ( idx = 0; idx < count; idx++ )
1717 {
1718 if ( growable[idx] >= max_idx )
1719 continue;
1720
1721 if ( sizes[ growable[idx] ] == -1 )
1722 continue;
1723
1724 int cur_delta;
1725 if ( sum_proportions == 0 )
1726 {
1727 // no growable rows -- divide extra space evenly among all
1728 cur_delta = delta/num;
1729 num--;
1730 }
1731 else // allocate extra space proportionally
1732 {
1733 const int cur_prop = (*proportions)[idx];
1734 cur_delta = (delta*cur_prop)/sum_proportions;
1735 sum_proportions -= cur_prop;
1736 }
1737
1738 sizes[growable[idx]] += cur_delta;
1739 delta -= cur_delta;
1740 }
1741 }
1742
1743 void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz)
1744 {
1745 if ( (m_flexDirection & wxHORIZONTAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
1746 {
1747 DoAdjustForGrowables
1748 (
1749 sz.x - m_calculatedMinSize.x,
1750 m_growableCols,
1751 m_colWidths,
1752 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
1753 : NULL
1754 );
1755
1756 // This gives nested objects that benefit from knowing one size
1757 // component in advance the chance to use that.
1758 bool didAdjustMinSize = false;
1759 int nrows, ncols;
1760 CalcRowsCols(nrows, ncols);
1761
1762 // Iterate over all items and inform about column width
1763 size_t n = 0;
1764 for ( wxSizerItemList::iterator i = m_children.begin();
1765 i != m_children.end();
1766 ++i, ++n )
1767 {
1768 const int col = n % ncols;
1769 didAdjustMinSize |= (*i)->InformFirstDirection(wxHORIZONTAL, m_colWidths[col], sz.y - m_calculatedMinSize.y);
1770 }
1771
1772 // Only redo if info was actually used
1773 if( didAdjustMinSize )
1774 {
1775 DoAdjustForGrowables
1776 (
1777 sz.x - m_calculatedMinSize.x,
1778 m_growableCols,
1779 m_colWidths,
1780 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
1781 : NULL
1782 );
1783 }
1784 }
1785
1786 if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
1787 {
1788 // pass NULL instead of proportions if the grow mode is ALL as we
1789 // should treat all rows as having proportion of 1 then
1790 DoAdjustForGrowables
1791 (
1792 sz.y - m_calculatedMinSize.y,
1793 m_growableRows,
1794 m_rowHeights,
1795 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableRowsProportions
1796 : NULL
1797 );
1798 }
1799 }
1800
1801
1802 void wxFlexGridSizer::AddGrowableRow( size_t idx, int proportion )
1803 {
1804 m_growableRows.Add( idx );
1805 m_growableRowsProportions.Add( proportion );
1806 }
1807
1808 void wxFlexGridSizer::AddGrowableCol( size_t idx, int proportion )
1809 {
1810 m_growableCols.Add( idx );
1811 m_growableColsProportions.Add( proportion );
1812 }
1813
1814 // helper function for RemoveGrowableCol/Row()
1815 static void
1816 DoRemoveFromArrays(size_t idx, wxArrayInt& items, wxArrayInt& proportions)
1817 {
1818 const size_t count = items.size();
1819 for ( size_t n = 0; n < count; n++ )
1820 {
1821 if ( (size_t)items[n] == idx )
1822 {
1823 items.RemoveAt(n);
1824 proportions.RemoveAt(n);
1825 return;
1826 }
1827 }
1828
1829 wxFAIL_MSG( _T("column/row is already not growable") );
1830 }
1831
1832 void wxFlexGridSizer::RemoveGrowableCol( size_t idx )
1833 {
1834 DoRemoveFromArrays(idx, m_growableCols, m_growableColsProportions);
1835 }
1836
1837 void wxFlexGridSizer::RemoveGrowableRow( size_t idx )
1838 {
1839 DoRemoveFromArrays(idx, m_growableRows, m_growableRowsProportions);
1840 }
1841
1842 //---------------------------------------------------------------------------
1843 // wxBoxSizer
1844 //---------------------------------------------------------------------------
1845
1846 void wxBoxSizer::RecalcSizes()
1847 {
1848 if ( m_children.empty() )
1849 return;
1850
1851 const wxCoord totalMinorSize = SizeInMinorDir(m_size);
1852
1853 // the amount of free space which we should redistribute among the
1854 // stretchable items (i.e. those with non zero proportion)
1855 int delta = SizeInMajorDir(m_size) - SizeInMajorDir(m_minSize);
1856
1857
1858 // Inform child items about the size in minor direction, that can
1859 // change how much free space we have in major dir and how to distribute it.
1860 int majorMinSum = 0;
1861 wxSizerItemList::const_iterator i ;
1862 for ( i = m_children.begin();
1863 i != m_children.end();
1864 ++i )
1865 {
1866 wxSizerItem * const item = *i;
1867
1868 if ( !item->IsShown() )
1869 continue;
1870
1871 wxSize szMinPrev = item->GetMinSizeWithBorder();
1872 item->InformFirstDirection(m_orient^wxBOTH,totalMinorSize,delta);
1873 wxSize szMin = item->GetMinSizeWithBorder();
1874 int deltaChange = SizeInMajorDir(szMin-szMinPrev);
1875 if( deltaChange )
1876 {
1877 // Since we passed available space along to the item, it should not
1878 // take too much ,so delat should not become negative.
1879 delta -= deltaChange;
1880 }
1881 majorMinSum += SizeInMajorDir(item->GetMinSizeWithBorder());
1882 }
1883 // And update our min size
1884 SizeInMajorDir(m_minSize) = majorMinSum;
1885
1886
1887 // might have a new delta now
1888 delta = SizeInMajorDir(m_size) - SizeInMajorDir(m_minSize);
1889
1890 // the position at which we put the next child
1891 wxPoint pt(m_position);
1892
1893 int totalProportion = m_totalProportion;
1894 for ( i = m_children.begin();
1895 i != m_children.end();
1896 ++i )
1897 {
1898 wxSizerItem * const item = *i;
1899
1900 if ( !item->IsShown() )
1901 continue;
1902
1903 #ifndef __DMC__
1904 // DMC doesn't distinguish between
1905 // int SizeInMajorDir(const wxSize& sz) const
1906 // and int& SizeInMajorDir(wxSize& sz)
1907 const
1908 #endif
1909 wxSize sizeThis(item->GetMinSizeWithBorder());
1910
1911 // adjust the size in the major direction using the proportion
1912 wxCoord majorSize = SizeInMajorDir(sizeThis);
1913 const int propItem = item->GetProportion();
1914 if ( propItem )
1915 {
1916 const int deltaItem = (delta * propItem) / totalProportion;
1917
1918 majorSize += deltaItem;
1919
1920 delta -= deltaItem;
1921 totalProportion -= propItem;
1922 }
1923
1924
1925 // apply the alignment in the minor direction
1926 wxPoint posChild(pt);
1927
1928 wxCoord minorSize = SizeInMinorDir(sizeThis);
1929 const int flag = item->GetFlag();
1930 if ( flag & (wxEXPAND | wxSHAPED) )
1931 {
1932 minorSize = totalMinorSize;
1933 }
1934 else if ( flag & (IsVertical() ? wxALIGN_RIGHT : wxALIGN_BOTTOM) )
1935 {
1936 PosInMinorDir(posChild) += totalMinorSize - minorSize;
1937 }
1938 // NB: wxCENTRE is used here only for backwards compatibility,
1939 // wxALIGN_CENTRE should be used in new code
1940 else if ( flag & (wxCENTER | wxALIGN_CENTRE) )
1941 {
1942 PosInMinorDir(posChild) += (totalMinorSize - minorSize) / 2;
1943 }
1944
1945
1946 // apply RTL adjustment for horizontal sizers:
1947 if ( !IsVertical() && m_containingWindow )
1948 {
1949 posChild.x = m_containingWindow->AdjustForLayoutDirection
1950 (
1951 posChild.x,
1952 majorSize,
1953 m_size.x
1954 );
1955 }
1956
1957 // finally set size of this child and advance to the next one
1958 item->SetDimension(posChild, SizeFromMajorMinor(majorSize, minorSize));
1959
1960 PosInMajorDir(pt) += majorSize;
1961 }
1962 }
1963
1964 wxSize wxBoxSizer::CalcMin()
1965 {
1966 m_totalProportion = 0;
1967 m_minSize = wxSize(0, 0);
1968
1969 // calculate the minimal sizes for all items and count sum of proportions
1970 for ( wxSizerItemList::const_iterator i = m_children.begin();
1971 i != m_children.end();
1972 ++i )
1973 {
1974 wxSizerItem * const item = *i;
1975
1976 if ( !item->IsShown() )
1977 continue;
1978 #ifndef __DMC__
1979 const // see __DMC__ above
1980 #endif
1981 wxSize sizeMinThis = item->CalcMin();
1982
1983 SizeInMajorDir(m_minSize) += SizeInMajorDir(sizeMinThis);
1984 if ( SizeInMinorDir(sizeMinThis) > SizeInMinorDir(m_minSize) )
1985 SizeInMinorDir(m_minSize) = SizeInMinorDir(sizeMinThis);
1986
1987 m_totalProportion += item->GetProportion();
1988 }
1989
1990 return m_minSize;
1991 }
1992
1993 //---------------------------------------------------------------------------
1994 // wxWrapSizer
1995 //---------------------------------------------------------------------------
1996
1997 #define wxDEFAULT_PROPORTION_LAST 1000000
1998
1999 // User data to hold old proportion for last item on line
2000 // (which might be extended)
2001 struct wxPropHolder : public wxObject
2002 {
2003 wxPropHolder( ) : m_item(0), m_propOld(0) { }
2004 void Init( wxSizerItem *item, int propOld ) { m_item=item; m_propOld=propOld; }
2005
2006 wxSizerItem *m_item;
2007 int m_propOld;
2008 };
2009
2010 IMPLEMENT_DYNAMIC_CLASS(wxWrapSizer, wxBoxSizer);
2011
2012 wxWrapSizer::wxWrapSizer( int orient, int flags )
2013 : wxBoxSizer(orient),
2014 m_prim_size_last( -1 ),
2015 m_rows(orient^wxBOTH),
2016 m_flags(flags)
2017 {
2018 }
2019
2020 wxWrapSizer::~wxWrapSizer()
2021 {
2022 // Have to clear grand child items so that they're not deleted twice
2023 for( int ix=m_rows.GetChildren().GetCount()-1; ix>=0; ix-- )
2024 {
2025 wxSizer *psz = m_rows.GetItem((size_t)ix)->GetSizer();
2026 wxSizerItemList &sl = psz->GetChildren();
2027 while( sl.GetLast() )
2028 sl.Erase( sl.GetLast() );
2029 }
2030 }
2031
2032
2033 bool wxWrapSizer::InformFirstDirection( int direction, int size, int WXUNUSED(availableOtherDir) )
2034 {
2035 if( !direction )
2036 {
2037 // Better to keep value, then CalcMin will work better
2038 //m_prim_size_last = -1;
2039 return false;
2040 }
2041 if( direction==m_orient )
2042 {
2043 // The direction is same as our primary, so we can make use of it
2044 m_prim_size_last = size;
2045 return true;
2046 }
2047 else
2048 return false;
2049 }
2050
2051
2052 void wxWrapSizer::AdjustPropLastItem(wxSizer *psz, wxSizerItem *itemLast)
2053 {
2054 wxSizerItem *psi = m_rows.GetItem(psz);
2055 wxASSERT(psi);
2056 wxPropHolder *pph = (wxPropHolder*)psi->GetUserData();
2057 if ( !pph )
2058 psi->SetUserData( pph=new wxPropHolder );
2059
2060 pph->Init( itemLast, itemLast->GetProportion() );
2061 itemLast->SetProportion( wxDEFAULT_PROPORTION_LAST );
2062 }
2063
2064 void wxWrapSizer::RecalcSizes()
2065 {
2066 wxASSERT( m_orient&wxBOTH );
2067 if (m_children.GetCount() == 0)
2068 return;
2069
2070 // What we do here is to put our items into child box sizers,
2071 // as many of them as we have lines.
2072
2073 // Empty all items in all rows in owned sizer.
2074 // We have to access the list directly, since we don't want to
2075 // destroy the wxSizerItems.
2076 for( int ix=m_rows.GetChildren().GetCount()-1; ix>=0; ix-- ){
2077 wxSizerItem *psi = m_rows.GetItem( (size_t)ix );
2078
2079 // Restore proportion for last item on line (if item has not been deleted)
2080 wxPropHolder *pph = (wxPropHolder*)psi->GetUserData();
2081 if( pph && GetChildren().Find(pph->m_item) )
2082 pph->m_item->SetProportion(pph->m_propOld);
2083
2084 wxSizer *psz = psi->GetSizer();
2085 wxASSERT( psz );
2086 wxSizerItemList &sl = psz->GetChildren();
2087 while( sl.GetLast() )
2088 sl.Erase( sl.GetLast() );
2089 }
2090
2091 int lineSumMajor = 0;
2092 int majorSize = SizeInMajorDir(m_size);
2093
2094 // Make sure we have at least one child sizer
2095 m_n_line = 1;
2096 if( !m_rows.GetChildren().GetCount() )
2097 m_rows.Add( new wxBoxSizer(GetOrientation()), 1, wxEXPAND );
2098
2099 // The sizer where to insert items in
2100 wxSizer *psz = m_rows.GetItem((size_t)0)->GetSizer();
2101 wxASSERT( psz );
2102
2103 // Now put our child items into child sizers instead
2104 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
2105 wxSizerItem *item = NULL, *itemLast=NULL;
2106 while (node)
2107 {
2108 item = node->GetData();
2109 if ( item->IsShown() )
2110 {
2111 wxSize minSz = item->GetMinSize();
2112 int minSzMajor = SizeInMajorDir(minSz);
2113
2114 // More space on this line?
2115 if( !lineSumMajor || lineSumMajor+minSzMajor<=majorSize )
2116 {
2117 lineSumMajor += minSzMajor;
2118 }
2119 else
2120 {
2121 lineSumMajor = minSzMajor;
2122 // Get a new empty sizer to insert into
2123 if( (int)m_rows.GetChildren().GetCount()<=m_n_line )
2124 m_rows.Add( new wxBoxSizer(GetOrientation()), 1, wxEXPAND );
2125
2126 // If we have extend-last-on-each-line mode, then do so now
2127 // Note: We must store old proportion value then.
2128 if( m_flags&wxEXTEND_LAST_ON_EACH_LINE )
2129 AdjustPropLastItem(psz,itemLast);
2130
2131 // The sizer where to insert items in
2132 psz = m_rows.GetItem(m_n_line++)->GetSizer();
2133 }
2134 itemLast = item;
2135 psz->Add( item );
2136 // If item is a window, it now has a pointer to the child sizer,
2137 // which is wrong. Set it to point to us.
2138 if( item->GetWindow() )
2139 item->GetWindow()->SetContainingSizer( this );
2140 }
2141 node = node->GetNext();
2142 }
2143
2144 // If we have extend-last-on-each-line mode, then do so now
2145 if( m_flags&wxEXTEND_LAST_ON_EACH_LINE )
2146 AdjustPropLastItem(psz,itemLast);
2147
2148 // If we have more sizers than lines, remove them
2149 while( (int)m_rows.GetChildren().GetCount()>m_n_line )
2150 m_rows.Remove( m_n_line );
2151
2152 // Now do layout on row sizer
2153 m_rows.SetDimension( m_position.x, m_position.y, m_size.x, m_size.y );
2154
2155 // Remember this to next time (will be overridden by InformFirstDirection if used)
2156 m_prim_size_last = SizeInMajorDir(m_size);
2157 }
2158
2159
2160 wxSize wxWrapSizer::CalcMin()
2161 {
2162 if (m_children.GetCount() == 0)
2163 return wxSize();
2164
2165 // Algorithm for calculating min size: (assuming horizontal orientation)
2166 // X: Max width of all members
2167 // Y: Based on last X, calculate how many lines needed
2168 // First time around, assume all items fits on one line
2169
2170 int maxMajor = 0;
2171 int minorSum = 0;
2172 int lineMaxMinor = 0;
2173 int lineSumMajor = 0;
2174 m_n_line = 0;
2175
2176 // precalc item minsizes and fit on lines (preliminary)
2177 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
2178 while (node)
2179 {
2180 wxSizerItem *item = node->GetData();
2181 if ( item->IsShown() )
2182 {
2183 wxSize minSz = item->CalcMin();
2184 int szMajor = SizeInMajorDir(minSz);
2185 int szMinor = SizeInMinorDir(minSz);
2186 if( szMajor>maxMajor ) maxMajor = szMajor;
2187 // More space on this line?
2188 if( m_prim_size_last<0 || !lineSumMajor ||
2189 lineSumMajor+szMajor<=m_prim_size_last )
2190 {
2191 lineSumMajor += szMajor;
2192 if( szMinor>lineMaxMinor )
2193 lineMaxMinor = szMinor;
2194 }
2195 else
2196 {
2197 minorSum += lineMaxMinor; // Add height of highest item on last line
2198 m_n_line++;
2199 lineMaxMinor = szMinor;
2200 lineSumMajor = szMajor;
2201 }
2202 }
2203 node = node->GetNext();
2204 }
2205 minorSum += lineMaxMinor; // Add height of highest item on last line
2206
2207 m_minSize = SizeFromMajorMinor(maxMajor, minorSum);
2208 return m_minSize;
2209 }
2210
2211 //---------------------------------------------------------------------------
2212 // wxStaticBoxSizer
2213 //---------------------------------------------------------------------------
2214
2215 #if wxUSE_STATBOX
2216
2217 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient )
2218 : wxBoxSizer( orient ),
2219 m_staticBox( box )
2220 {
2221 wxASSERT_MSG( box, wxT("wxStaticBoxSizer needs a static box") );
2222
2223 // do this so that our Detach() is called if the static box is destroyed
2224 // before we are
2225 m_staticBox->SetContainingSizer(this);
2226 }
2227
2228 wxStaticBoxSizer::wxStaticBoxSizer(int orient, wxWindow *win, const wxString& s)
2229 : wxBoxSizer(orient),
2230 m_staticBox(new wxStaticBox(win, wxID_ANY, s))
2231 {
2232 // same as above
2233 m_staticBox->SetContainingSizer(this);
2234 }
2235
2236 wxStaticBoxSizer::~wxStaticBoxSizer()
2237 {
2238 delete m_staticBox;
2239 }
2240
2241 static void GetStaticBoxBorders( wxStaticBox *box,
2242 int *borderTop,
2243 int *borderOther)
2244 {
2245 // this has to be done platform by platform as there is no way to
2246 // guess the thickness of a wxStaticBox border
2247 box->GetBordersForSizer(borderTop, borderOther);
2248 }
2249
2250 void wxStaticBoxSizer::RecalcSizes()
2251 {
2252 int top_border, other_border;
2253 GetStaticBoxBorders(m_staticBox, &top_border, &other_border);
2254
2255 m_staticBox->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
2256
2257 wxPoint old_pos( m_position );
2258 m_position.x += other_border;
2259 m_position.y += top_border;
2260 wxSize old_size( m_size );
2261 m_size.x -= 2*other_border;
2262 m_size.y -= top_border + other_border;
2263
2264 wxBoxSizer::RecalcSizes();
2265
2266 m_position = old_pos;
2267 m_size = old_size;
2268 }
2269
2270 wxSize wxStaticBoxSizer::CalcMin()
2271 {
2272 int top_border, other_border;
2273 GetStaticBoxBorders(m_staticBox, &top_border, &other_border);
2274
2275 wxSize ret( wxBoxSizer::CalcMin() );
2276 ret.x += 2*other_border;
2277 ret.y += other_border + top_border;
2278
2279 return ret;
2280 }
2281
2282 void wxStaticBoxSizer::ShowItems( bool show )
2283 {
2284 m_staticBox->Show( show );
2285 wxBoxSizer::ShowItems( show );
2286 }
2287
2288 bool wxStaticBoxSizer::Detach( wxWindow *window )
2289 {
2290 // avoid deleting m_staticBox in our dtor if it's being detached from the
2291 // sizer (which can happen because it's being already destroyed for
2292 // example)
2293 if ( window == m_staticBox )
2294 {
2295 m_staticBox = NULL;
2296 return true;
2297 }
2298
2299 return wxSizer::Detach( window );
2300 }
2301
2302 #endif // wxUSE_STATBOX
2303
2304 #if wxUSE_BUTTON
2305
2306 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2307 : wxBoxSizer(wxHORIZONTAL)
2308 {
2309 // Vertical buttons with lots of space on either side
2310 // looks rubbish on WinCE, so let's not do this for now.
2311 // If we are going to use vertical buttons, we should
2312 // put the sizer to the right of other controls in the dialog,
2313 // and that's beyond the scope of this sizer.
2314 #ifndef __WXWINCE__
2315 bool is_pda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
2316 // If we have a PDA screen, put yes/no button over
2317 // all other buttons, otherwise on the left side.
2318 if (is_pda)
2319 m_orient = wxVERTICAL;
2320 #endif
2321
2322 m_buttonAffirmative = NULL;
2323 m_buttonApply = NULL;
2324 m_buttonNegative = NULL;
2325 m_buttonCancel = NULL;
2326 m_buttonHelp = NULL;
2327 }
2328
2329 void wxStdDialogButtonSizer::AddButton(wxButton *mybutton)
2330 {
2331 switch (mybutton->GetId())
2332 {
2333 case wxID_OK:
2334 case wxID_YES:
2335 case wxID_SAVE:
2336 m_buttonAffirmative = mybutton;
2337 break;
2338 case wxID_APPLY:
2339 m_buttonApply = mybutton;
2340 break;
2341 case wxID_NO:
2342 m_buttonNegative = mybutton;
2343 break;
2344 case wxID_CANCEL:
2345 case wxID_CLOSE:
2346 m_buttonCancel = mybutton;
2347 break;
2348 case wxID_HELP:
2349 case wxID_CONTEXT_HELP:
2350 m_buttonHelp = mybutton;
2351 break;
2352 default:
2353 break;
2354 }
2355 }
2356
2357 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton *button )
2358 {
2359 m_buttonAffirmative = button;
2360 }
2361
2362 void wxStdDialogButtonSizer::SetNegativeButton( wxButton *button )
2363 {
2364 m_buttonNegative = button;
2365 }
2366
2367 void wxStdDialogButtonSizer::SetCancelButton( wxButton *button )
2368 {
2369 m_buttonCancel = button;
2370 }
2371
2372 void wxStdDialogButtonSizer::Realize()
2373 {
2374 #ifdef __WXMAC__
2375 Add(0, 0, 0, wxLEFT, 6);
2376 if (m_buttonHelp)
2377 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2378
2379 if (m_buttonNegative){
2380 // HIG POLICE BULLETIN - destructive buttons need extra padding
2381 // 24 pixels on either side
2382 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 12);
2383 }
2384
2385 // extra whitespace between help/negative and cancel/ok buttons
2386 Add(0, 0, 1, wxEXPAND, 0);
2387
2388 if (m_buttonCancel){
2389 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2390 // Cancel or help should be default
2391 // m_buttonCancel->SetDefaultButton();
2392 }
2393
2394 // Ugh, Mac doesn't really have apply dialogs, so I'll just
2395 // figure the best place is between Cancel and OK
2396 if (m_buttonApply)
2397 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2398
2399 if (m_buttonAffirmative){
2400 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6);
2401
2402 if (m_buttonAffirmative->GetId() == wxID_SAVE){
2403 // these buttons have set labels under Mac so we should use them
2404 m_buttonAffirmative->SetLabel(_("Save"));
2405 if (m_buttonNegative)
2406 m_buttonNegative->SetLabel(_("Don't Save"));
2407 }
2408 }
2409
2410 // Extra space around and at the right
2411 Add(12, 24);
2412 #elif defined(__WXGTK20__)
2413 Add(0, 0, 0, wxLEFT, 9);
2414 if (m_buttonHelp)
2415 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
2416
2417 // extra whitespace between help and cancel/ok buttons
2418 Add(0, 0, 1, wxEXPAND, 0);
2419
2420 if (m_buttonNegative){
2421 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
2422 }
2423
2424 // according to HIG, in explicit apply windows the order is:
2425 // [ Help Apply Cancel OK ]
2426 if (m_buttonApply)
2427 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
2428
2429 if (m_buttonCancel){
2430 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 3);
2431 // Cancel or help should be default
2432 // m_buttonCancel->SetDefaultButton();
2433 }
2434
2435 if (m_buttonAffirmative)
2436 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6);
2437 #elif defined(__WXMSW__)
2438 // Windows
2439
2440 // right-justify buttons
2441 Add(0, 0, 1, wxEXPAND, 0);
2442
2443 if (m_buttonAffirmative){
2444 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(2, 0)).x);
2445 }
2446
2447 if (m_buttonNegative){
2448 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(2, 0)).x);
2449 }
2450
2451 if (m_buttonCancel){
2452 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(2, 0)).x);
2453 }
2454 if (m_buttonApply)
2455 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(2, 0)).x);
2456
2457 if (m_buttonHelp)
2458 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(2, 0)).x);
2459 #else
2460 // GTK+1 and any other platform
2461
2462 // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2463 if (m_buttonHelp)
2464 Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(4, 0)).x);
2465
2466 // extra whitespace between help and cancel/ok buttons
2467 Add(0, 0, 1, wxEXPAND, 0);
2468
2469 if (m_buttonApply)
2470 Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(4, 0)).x);
2471
2472 if (m_buttonAffirmative){
2473 Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(4, 0)).x);
2474 }
2475
2476 if (m_buttonNegative){
2477 Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(4, 0)).x);
2478 }
2479
2480 if (m_buttonCancel){
2481 Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(4, 0)).x);
2482 // Cancel or help should be default
2483 // m_buttonCancel->SetDefaultButton();
2484 }
2485
2486 #endif
2487 }
2488
2489 #endif // wxUSE_BUTTON