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