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