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