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 // Copyright:   (c) Robin Dunn, Robert Roebling
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14 
15 
16 #include "wx/sizer.h"
17 #include "wx/private/flagscheck.h"
18 
19 #ifndef WX_PRECOMP
20     #include "wx/string.h"
21     #include "wx/intl.h"
22     #include "wx/math.h"
23     #include "wx/utils.h"
24     #include "wx/settings.h"
25     #include "wx/button.h"
26     #include "wx/statbox.h"
27     #include "wx/toplevel.h"
28     #include "wx/app.h"
29 #endif // WX_PRECOMP
30 
31 #include "wx/display.h"
32 #include "wx/vector.h"
33 #include "wx/listimpl.cpp"
34 #include "wx/private/window.h"
35 #include "wx/scopedptr.h"
36 
37 
38 //---------------------------------------------------------------------------
39 
40 wxIMPLEMENT_CLASS(wxSizerItem, wxObject);
41 wxIMPLEMENT_CLASS(wxSizer, wxObject);
42 wxIMPLEMENT_CLASS(wxGridSizer, wxSizer);
43 wxIMPLEMENT_CLASS(wxFlexGridSizer, wxGridSizer);
44 wxIMPLEMENT_CLASS(wxBoxSizer, wxSizer);
45 #if wxUSE_STATBOX
46 wxIMPLEMENT_CLASS(wxStaticBoxSizer, wxBoxSizer);
47 #endif
48 #if wxUSE_BUTTON
49 wxIMPLEMENT_CLASS(wxStdDialogButtonSizer, wxBoxSizer);
50 #endif
51 
WX_DEFINE_EXPORTED_LIST(wxSizerItemList)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 // wxSizerFlags
89 // ----------------------------------------------------------------------------
90 
91 #ifdef wxNEEDS_BORDER_IN_PX
92 
93 /* static */
94 float wxSizerFlags::DoGetDefaultBorderInPx()
95 {
96     // Hard code 5px as it's the minimal border size between two controls, see
97     // the table at the bottom of
98     // https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx
99     //
100     // Of course, ideal would be to use the appropriate sizes for the borders
101     // between related and unrelated controls, as explained at the above URL,
102     // but we don't have a way to specify this in our API currently.
103     //
104     // We also have to use the DPI for the monitor showing the top window here
105     // as we don't have any associated window -- but, again, without changes
106     // in the API, there is nothing we can do about this.
107     const wxWindow* const win = wxApp::GetMainTopWindow();
108     static wxPrivate::DpiDependentValue<float> s_defaultBorderInPx;
109     if ( s_defaultBorderInPx.HasChanged(win) )
110     {
111         s_defaultBorderInPx.SetAtNewDPI(
112             (float)(5 * (win ? win->GetDPIScaleFactor() : 1.0)));
113     }
114     return s_defaultBorderInPx.Get();
115 }
116 
117 #endif // wxNEEDS_BORDER_IN_PX
118 
119 // ----------------------------------------------------------------------------
120 // wxSizerItem
121 // ----------------------------------------------------------------------------
122 
123 // check for flags conflicts
124 #if wxDEBUG_LEVEL
125 
126 static const int SIZER_FLAGS_MASK =
127     wxADD_FLAG(wxCENTRE,
128     wxADD_FLAG(wxHORIZONTAL,
129     wxADD_FLAG(wxVERTICAL,
130     wxADD_FLAG(wxLEFT,
131     wxADD_FLAG(wxRIGHT,
132     wxADD_FLAG(wxUP,
133     wxADD_FLAG(wxDOWN,
134     wxADD_FLAG(wxALIGN_NOT,
135     wxADD_FLAG(wxALIGN_CENTER_HORIZONTAL,
136     wxADD_FLAG(wxALIGN_RIGHT,
137     wxADD_FLAG(wxALIGN_BOTTOM,
138     wxADD_FLAG(wxALIGN_CENTER_VERTICAL,
139     wxADD_FLAG(wxFIXED_MINSIZE,
140     wxADD_FLAG(wxRESERVE_SPACE_EVEN_IF_HIDDEN,
141     wxADD_FLAG(wxSTRETCH_NOT,
142     wxADD_FLAG(wxSHRINK,
143     wxADD_FLAG(wxGROW,
144     wxADD_FLAG(wxSHAPED,
145     0))))))))))))))))));
146 
147 #endif // wxDEBUG_LEVEL
148 
149 #define ASSERT_INCOMPATIBLE_NOT_USED_IMPL(f, f1, n1, f2, n2) \
150     wxASSERT_MSG(((f) & (f1 | f2)) != (f1 | f2), \
151                  n1 " and " n2 " can't be used together")
152 
153 #define ASSERT_INCOMPATIBLE_NOT_USED(f, f1, f2) \
154     ASSERT_INCOMPATIBLE_NOT_USED_IMPL(f, f1, #f1, f2, #f2)
155 
156 #define ASSERT_VALID_SIZER_FLAGS(f) \
157     wxASSERT_VALID_FLAGS(f, SIZER_FLAGS_MASK); \
158     ASSERT_INCOMPATIBLE_NOT_USED(f, wxALIGN_CENTRE_HORIZONTAL, wxALIGN_RIGHT); \
159     ASSERT_INCOMPATIBLE_NOT_USED(f, wxALIGN_CENTRE_VERTICAL, wxALIGN_BOTTOM)
160 
161 
Init(const wxSizerFlags & flags)162 void wxSizerItem::Init(const wxSizerFlags& flags)
163 {
164     Init();
165 
166     m_proportion = flags.GetProportion();
167     m_flag = flags.GetFlags();
168     m_border = flags.GetBorderInPixels();
169 
170     ASSERT_VALID_SIZER_FLAGS( m_flag );
171 }
172 
wxSizerItem()173 wxSizerItem::wxSizerItem()
174 {
175     Init();
176 
177     m_proportion = 0;
178     m_border = 0;
179     m_flag = 0;
180     m_id = wxID_NONE;
181 }
182 
183 // window item
DoSetWindow(wxWindow * window)184 void wxSizerItem::DoSetWindow(wxWindow *window)
185 {
186     wxCHECK_RET( window, wxT("NULL window in wxSizerItem::SetWindow()") );
187 
188     m_kind = Item_Window;
189     m_window = window;
190 
191     // window doesn't become smaller than its initial size, whatever happens
192     m_minSize = window->GetSize();
193 
194     if ( m_flag & wxFIXED_MINSIZE )
195         window->SetMinSize(m_minSize);
196 
197     // aspect ratio calculated from initial size
198     SetRatio(m_minSize);
199 }
200 
wxSizerItem(wxWindow * window,int proportion,int flag,int border,wxObject * userData)201 wxSizerItem::wxSizerItem(wxWindow *window,
202                          int proportion,
203                          int flag,
204                          int border,
205                          wxObject* userData)
206            : m_kind(Item_None),
207              m_proportion(proportion),
208              m_border(border),
209              m_flag(flag),
210              m_id(wxID_NONE),
211              m_userData(userData)
212 {
213     ASSERT_VALID_SIZER_FLAGS( m_flag );
214 
215     DoSetWindow(window);
216 }
217 
218 // sizer item
DoSetSizer(wxSizer * sizer)219 void wxSizerItem::DoSetSizer(wxSizer *sizer)
220 {
221     m_kind = Item_Sizer;
222     m_sizer = sizer;
223 }
224 
wxSizerItem(wxSizer * sizer,int proportion,int flag,int border,wxObject * userData)225 wxSizerItem::wxSizerItem(wxSizer *sizer,
226                          int proportion,
227                          int flag,
228                          int border,
229                          wxObject* userData)
230            : m_kind(Item_None),
231              m_sizer(NULL),
232              m_proportion(proportion),
233              m_border(border),
234              m_flag(flag),
235              m_id(wxID_NONE),
236              m_ratio(0),
237              m_userData(userData)
238 {
239     ASSERT_VALID_SIZER_FLAGS( m_flag );
240 
241     DoSetSizer(sizer);
242 
243     // m_minSize is set later
244 }
245 
246 // spacer item
DoSetSpacer(const wxSize & size)247 void wxSizerItem::DoSetSpacer(const wxSize& size)
248 {
249     m_kind = Item_Spacer;
250     m_spacer = new wxSizerSpacer(size);
251     m_minSize = size;
252     SetRatio(size);
253 }
254 
AddBorderToSize(const wxSize & size) const255 wxSize wxSizerItem::AddBorderToSize(const wxSize& size) const
256 {
257     wxSize result = size;
258 
259     // Notice that we shouldn't modify the unspecified component(s) of the
260     // size, it's perfectly valid to have either min or max size specified in
261     // one direction only and it shouldn't be applied in the other one then.
262 
263     if ( result.x != wxDefaultCoord )
264     {
265         if (m_flag & wxWEST)
266             result.x += m_border;
267         if (m_flag & wxEAST)
268             result.x += m_border;
269     }
270 
271     if ( result.y != wxDefaultCoord )
272     {
273         if (m_flag & wxNORTH)
274             result.y += m_border;
275         if (m_flag & wxSOUTH)
276             result.y += m_border;
277     }
278 
279     return result;
280 }
281 
wxSizerItem(int width,int height,int proportion,int flag,int border,wxObject * userData)282 wxSizerItem::wxSizerItem(int width,
283                          int height,
284                          int proportion,
285                          int flag,
286                          int border,
287                          wxObject* userData)
288            : m_kind(Item_None),
289              m_sizer(NULL),
290              m_minSize(width, height), // minimal size is the initial size
291              m_proportion(proportion),
292              m_border(border),
293              m_flag(flag),
294              m_id(wxID_NONE),
295              m_userData(userData)
296 {
297     ASSERT_VALID_SIZER_FLAGS( m_flag );
298 
299     DoSetSpacer(wxSize(width, height));
300 }
301 
~wxSizerItem()302 wxSizerItem::~wxSizerItem()
303 {
304     delete m_userData;
305     Free();
306 }
307 
Free()308 void wxSizerItem::Free()
309 {
310     switch ( m_kind )
311     {
312         case Item_None:
313             break;
314 
315         case Item_Window:
316             m_window->SetContainingSizer(NULL);
317             break;
318 
319         case Item_Sizer:
320             delete m_sizer;
321             break;
322 
323         case Item_Spacer:
324             delete m_spacer;
325             break;
326 
327         case Item_Max:
328         default:
329             wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
330     }
331 
332     m_kind = Item_None;
333 }
334 
GetSpacer() const335 wxSize wxSizerItem::GetSpacer() const
336 {
337     wxSize size;
338     if ( m_kind == Item_Spacer )
339         size = m_spacer->GetSize();
340 
341     return size;
342 }
343 
344 
GetSize() const345 wxSize wxSizerItem::GetSize() const
346 {
347     wxSize ret;
348     switch ( m_kind )
349     {
350         case Item_None:
351             break;
352 
353         case Item_Window:
354             ret = m_window->GetSize();
355             break;
356 
357         case Item_Sizer:
358             ret = m_sizer->GetSize();
359             break;
360 
361         case Item_Spacer:
362             ret = m_spacer->GetSize();
363             break;
364 
365         case Item_Max:
366         default:
367             wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
368     }
369 
370     if (m_flag & wxWEST)
371         ret.x += m_border;
372     if (m_flag & wxEAST)
373         ret.x += m_border;
374     if (m_flag & wxNORTH)
375         ret.y += m_border;
376     if (m_flag & wxSOUTH)
377         ret.y += m_border;
378 
379     return ret;
380 }
381 
InformFirstDirection(int direction,int size,int availableOtherDir)382 bool wxSizerItem::InformFirstDirection(int direction, int size, int availableOtherDir)
383 {
384     // The size that come here will be including borders. Child items should get it
385     // without borders.
386     if( size>0 )
387     {
388         if( direction==wxHORIZONTAL )
389         {
390             if (m_flag & wxWEST)
391                 size -= m_border;
392             if (m_flag & wxEAST)
393                 size -= m_border;
394         }
395         else if( direction==wxVERTICAL )
396         {
397             if (m_flag & wxNORTH)
398                 size -= m_border;
399             if (m_flag & wxSOUTH)
400                 size -= m_border;
401         }
402     }
403 
404     bool didUse = false;
405     // Pass the information along to the held object
406     if (IsSizer())
407     {
408         didUse = GetSizer()->InformFirstDirection(direction,size,availableOtherDir);
409         if (didUse)
410             m_minSize = GetSizer()->CalcMin();
411     }
412     else if (IsWindow())
413     {
414         didUse =  GetWindow()->InformFirstDirection(direction,size,availableOtherDir);
415         if (didUse)
416             m_minSize = m_window->GetEffectiveMinSize();
417 
418         // This information is useful for items with wxSHAPED flag, since
419         // we can request an optimal min size for such an item. Even if
420         // we overwrite the m_minSize member here, we can read it back from
421         // the owned window (happens automatically).
422         if( (m_flag & wxSHAPED) && (m_flag & wxEXPAND) && direction )
423         {
424             if ( m_ratio != 0 )
425             {
426                 wxCHECK_MSG( m_proportion==0, false, wxT("Shaped item, non-zero proportion in wxSizerItem::InformFirstDirection()") );
427                 if ( direction == wxHORIZONTAL )
428                 {
429                     // Clip size so that we don't take too much
430                     if( availableOtherDir>=0 && int(size/m_ratio)-m_minSize.y>availableOtherDir )
431                         size = int((availableOtherDir+m_minSize.y)*m_ratio);
432                     m_minSize = wxSize(size,int(size/m_ratio));
433                 }
434                 else if( direction==wxVERTICAL )
435                 {
436                     // Clip size so that we don't take too much
437                     if( availableOtherDir>=0 && int(size*m_ratio)-m_minSize.x>availableOtherDir )
438                         size = int((availableOtherDir+m_minSize.x)/m_ratio);
439                     m_minSize = wxSize(int(size*m_ratio),size);
440                 }
441                 didUse = true;
442             }
443         }
444     }
445 
446     return didUse;
447 }
448 
CalcMin()449 wxSize wxSizerItem::CalcMin()
450 {
451     if (IsSizer())
452     {
453         m_minSize = m_sizer->GetMinSize();
454 
455         // if we have to preserve aspect ratio _AND_ this is
456         // the first-time calculation, consider ret to be initial size
457         if ( (m_flag & wxSHAPED) && m_ratio == 0 )
458             SetRatio(m_minSize);
459     }
460     else if ( IsWindow() )
461     {
462         // Since the size of the window may change during runtime, we
463         // should use the current minimal/best size.
464         m_minSize = m_window->GetEffectiveMinSize();
465     }
466 
467     return GetMinSizeWithBorder();
468 }
469 
GetMinSizeWithBorder() const470 wxSize wxSizerItem::GetMinSizeWithBorder() const
471 {
472     return AddBorderToSize(m_minSize);
473 }
474 
GetMaxSizeWithBorder() const475 wxSize wxSizerItem::GetMaxSizeWithBorder() const
476 {
477     return AddBorderToSize(GetMaxSize());
478 }
479 
SetDimension(const wxPoint & pos_,const wxSize & size_)480 void wxSizerItem::SetDimension( const wxPoint& pos_, const wxSize& size_ )
481 {
482     wxPoint pos = pos_;
483     wxSize size = size_;
484     if (m_flag & wxSHAPED)
485     {
486         // adjust aspect ratio
487         int rwidth = (int) (size.y * m_ratio);
488         if (rwidth > size.x)
489         {
490             // fit horizontally
491             int rheight = (int) (size.x / m_ratio);
492             // add vertical space
493             if (m_flag & wxALIGN_CENTER_VERTICAL)
494                 pos.y += (size.y - rheight) / 2;
495             else if (m_flag & wxALIGN_BOTTOM)
496                 pos.y += (size.y - rheight);
497             // use reduced dimensions
498             size.y =rheight;
499         }
500         else if (rwidth < size.x)
501         {
502             // add horizontal space
503             if (m_flag & wxALIGN_CENTER_HORIZONTAL)
504                 pos.x += (size.x - rwidth) / 2;
505             else if (m_flag & wxALIGN_RIGHT)
506                 pos.x += (size.x - rwidth);
507             size.x = rwidth;
508         }
509     }
510 
511     // This is what GetPosition() returns. Since we calculate
512     // borders afterwards, GetPosition() will be the left/top
513     // corner of the surrounding border.
514     m_pos = pos;
515 
516     if (m_flag & wxWEST)
517     {
518         pos.x += m_border;
519         size.x -= m_border;
520     }
521     if (m_flag & wxEAST)
522     {
523         size.x -= m_border;
524     }
525     if (m_flag & wxNORTH)
526     {
527         pos.y += m_border;
528         size.y -= m_border;
529     }
530     if (m_flag & wxSOUTH)
531     {
532         size.y -= m_border;
533     }
534 
535     if (size.x < 0)
536         size.x = 0;
537     if (size.y < 0)
538         size.y = 0;
539 
540     m_rect = wxRect(pos, size);
541 
542     switch ( m_kind )
543     {
544         case Item_None:
545             wxFAIL_MSG( wxT("can't set size of uninitialized sizer item") );
546             break;
547 
548         case Item_Window:
549         {
550             // Use wxSIZE_FORCE_EVENT here since a sizer item might
551             // have changed alignment or some other property which would
552             // not change the size of the window. In such a case, no
553             // wxSizeEvent would normally be generated and thus the
554             // control wouldn't get laid out correctly here.
555 #if 1
556             m_window->SetSize(pos.x, pos.y, size.x, size.y,
557                               wxSIZE_ALLOW_MINUS_ONE|wxSIZE_FORCE_EVENT );
558 #else
559             m_window->SetSize(pos.x, pos.y, size.x, size.y,
560                               wxSIZE_ALLOW_MINUS_ONE );
561 #endif
562             break;
563         }
564         case Item_Sizer:
565             m_sizer->SetDimension(pos, size);
566             break;
567 
568         case Item_Spacer:
569             m_spacer->SetSize(size);
570             break;
571 
572         case Item_Max:
573         default:
574             wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
575     }
576 }
577 
DeleteWindows()578 void wxSizerItem::DeleteWindows()
579 {
580     switch ( m_kind )
581     {
582         case Item_None:
583         case Item_Spacer:
584             break;
585 
586         case Item_Window:
587             //We are deleting the window from this sizer - normally
588             //the window destroys the sizer associated with it,
589             //which might destroy this, which we don't want
590             m_window->SetContainingSizer(NULL);
591             m_window->Destroy();
592             //Putting this after the switch will result in a spacer
593             //not being deleted properly on destruction
594             m_kind = Item_None;
595             break;
596 
597         case Item_Sizer:
598             m_sizer->DeleteWindows();
599             break;
600 
601         case Item_Max:
602         default:
603             wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
604     }
605 
606 }
607 
Show(bool show)608 void wxSizerItem::Show( bool show )
609 {
610     switch ( m_kind )
611     {
612         case Item_None:
613             wxFAIL_MSG( wxT("can't show uninitialized sizer item") );
614             break;
615 
616         case Item_Window:
617             m_window->Show(show);
618             break;
619 
620         case Item_Sizer:
621             m_sizer->Show(show);
622             break;
623 
624         case Item_Spacer:
625             m_spacer->Show(show);
626             break;
627 
628         case Item_Max:
629         default:
630             wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
631     }
632 }
633 
IsShown() const634 bool wxSizerItem::IsShown() const
635 {
636     if ( m_flag & wxRESERVE_SPACE_EVEN_IF_HIDDEN )
637         return true;
638 
639     switch ( m_kind )
640     {
641         case Item_None:
642             // we may be called from CalcMin(), just return false so that we're
643             // not used
644             break;
645 
646         case Item_Window:
647             return m_window->IsShown();
648 
649         case Item_Sizer:
650             // arbitrarily decide that if at least one of our elements is
651             // shown, so are we (this arbitrariness is the reason for
652             // deprecating this function)
653             return m_sizer->AreAnyItemsShown();
654 
655         case Item_Spacer:
656             return m_spacer->IsShown();
657 
658         case Item_Max:
659         default:
660             wxFAIL_MSG( wxT("unexpected wxSizerItem::m_kind") );
661     }
662 
663     return false;
664 }
665 
666 //---------------------------------------------------------------------------
667 // wxSizer
668 //---------------------------------------------------------------------------
669 
~wxSizer()670 wxSizer::~wxSizer()
671 {
672     WX_CLEAR_LIST(wxSizerItemList, m_children);
673 }
674 
DoInsert(size_t index,wxSizerItem * item)675 wxSizerItem* wxSizer::DoInsert( size_t index, wxSizerItem *item )
676 {
677     // The helper class that solves two problems when
678     // wxWindowBase::SetContainingSizer() throws:
679     // 1. Avoid leaking memory using the scoped pointer to the sizer item.
680     // 2. Disassociate the window from the sizer item to not reset the
681     //    containing sizer for the window in the items destructor.
682     class ContainingSizerGuard
683     {
684     public:
685         explicit ContainingSizerGuard( wxSizerItem *item )
686             : m_item(item)
687         {
688         }
689 
690         ~ContainingSizerGuard()
691         {
692             if ( m_item )
693                 m_item->DetachWindow();
694         }
695 
696         wxSizerItem* Release()
697         {
698             return m_item.release();
699         }
700 
701     private:
702         wxScopedPtr<wxSizerItem> m_item;
703     };
704 
705     ContainingSizerGuard guard( item );
706 
707     if ( item->GetWindow() )
708         item->GetWindow()->SetContainingSizer( this );
709 
710     if ( item->GetSizer() )
711         item->GetSizer()->SetContainingWindow( m_containingWindow );
712 
713     m_children.Insert( index, item );
714 
715     return guard.Release();
716 }
717 
SetContainingWindow(wxWindow * win)718 void wxSizer::SetContainingWindow(wxWindow *win)
719 {
720     if ( win == m_containingWindow )
721         return;
722 
723     m_containingWindow = win;
724 
725     // set the same window for all nested sizers as well, they also are in the
726     // same window
727     for ( wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
728           node;
729           node = node->GetNext() )
730     {
731         wxSizerItem *const item = node->GetData();
732         wxSizer *const sizer = item->GetSizer();
733 
734         if ( sizer )
735         {
736             sizer->SetContainingWindow(win);
737         }
738     }
739 }
740 
Remove(wxSizer * sizer)741 bool wxSizer::Remove( wxSizer *sizer )
742 {
743     wxASSERT_MSG( sizer, wxT("Removing NULL sizer") );
744 
745     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
746     while (node)
747     {
748         wxSizerItem     *item = node->GetData();
749 
750         if (item->GetSizer() == sizer)
751         {
752             delete item;
753             m_children.Erase( node );
754             return true;
755         }
756 
757         node = node->GetNext();
758     }
759 
760     return false;
761 }
762 
Remove(int index)763 bool wxSizer::Remove( int index )
764 {
765     wxCHECK_MSG( index >= 0 && (size_t)index < m_children.GetCount(),
766                  false,
767                  wxT("Remove index is out of range") );
768 
769     wxSizerItemList::compatibility_iterator node = m_children.Item( index );
770 
771     wxCHECK_MSG( node, false, wxT("Failed to find child node") );
772 
773     delete node->GetData();
774     m_children.Erase( node );
775 
776     return true;
777 }
778 
Detach(wxSizer * sizer)779 bool wxSizer::Detach( wxSizer *sizer )
780 {
781     wxASSERT_MSG( sizer, wxT("Detaching NULL sizer") );
782 
783     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
784     while (node)
785     {
786         wxSizerItem     *item = node->GetData();
787 
788         if (item->GetSizer() == sizer)
789         {
790             item->DetachSizer();
791             delete item;
792             m_children.Erase( node );
793             return true;
794         }
795         node = node->GetNext();
796     }
797 
798     return false;
799 }
800 
Detach(wxWindow * window)801 bool wxSizer::Detach( wxWindow *window )
802 {
803     wxASSERT_MSG( window, wxT("Detaching NULL window") );
804 
805     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
806     while (node)
807     {
808         wxSizerItem     *item = node->GetData();
809 
810         if (item->GetWindow() == window)
811         {
812             delete item;
813             m_children.Erase( node );
814             return true;
815         }
816         node = node->GetNext();
817     }
818 
819     return false;
820 }
821 
Detach(int index)822 bool wxSizer::Detach( int index )
823 {
824     wxCHECK_MSG( index >= 0 && (size_t)index < m_children.GetCount(),
825                  false,
826                  wxT("Detach index is out of range") );
827 
828     wxSizerItemList::compatibility_iterator node = m_children.Item( index );
829 
830     wxCHECK_MSG( node, false, wxT("Failed to find child node") );
831 
832     wxSizerItem *item = node->GetData();
833 
834     if ( item->IsSizer() )
835         item->DetachSizer();
836 
837     delete item;
838     m_children.Erase( node );
839     return true;
840 }
841 
Replace(wxWindow * oldwin,wxWindow * newwin,bool recursive)842 bool wxSizer::Replace( wxWindow *oldwin, wxWindow *newwin, bool recursive )
843 {
844     wxASSERT_MSG( oldwin, wxT("Replacing NULL window") );
845     wxASSERT_MSG( newwin, wxT("Replacing with NULL window") );
846 
847     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
848     while (node)
849     {
850         wxSizerItem     *item = node->GetData();
851 
852         if (item->GetWindow() == oldwin)
853         {
854             item->AssignWindow(newwin);
855             newwin->SetContainingSizer( this );
856             return true;
857         }
858         else if (recursive && item->IsSizer())
859         {
860             if (item->GetSizer()->Replace( oldwin, newwin, true ))
861                 return true;
862         }
863 
864         node = node->GetNext();
865     }
866 
867     return false;
868 }
869 
Replace(wxSizer * oldsz,wxSizer * newsz,bool recursive)870 bool wxSizer::Replace( wxSizer *oldsz, wxSizer *newsz, bool recursive )
871 {
872     wxASSERT_MSG( oldsz, wxT("Replacing NULL sizer") );
873     wxASSERT_MSG( newsz, wxT("Replacing with NULL sizer") );
874 
875     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
876     while (node)
877     {
878         wxSizerItem     *item = node->GetData();
879 
880         if (item->GetSizer() == oldsz)
881         {
882             item->AssignSizer(newsz);
883             return true;
884         }
885         else if (recursive && item->IsSizer())
886         {
887             if (item->GetSizer()->Replace( oldsz, newsz, true ))
888                 return true;
889         }
890 
891         node = node->GetNext();
892     }
893 
894     return false;
895 }
896 
Replace(size_t old,wxSizerItem * newitem)897 bool wxSizer::Replace( size_t old, wxSizerItem *newitem )
898 {
899     wxCHECK_MSG( old < m_children.GetCount(), false, wxT("Replace index is out of range") );
900     wxCHECK_MSG( newitem, false, wxT("Replacing with NULL item") );
901 
902     wxSizerItemList::compatibility_iterator node = m_children.Item( old );
903 
904     wxCHECK_MSG( node, false, wxT("Failed to find child node") );
905 
906     wxSizerItem *item = node->GetData();
907     node->SetData(newitem);
908 
909     if (wxWindow* const w = item->GetWindow())
910         w->SetContainingSizer(NULL);
911 
912     delete item;
913 
914     if (wxWindow* const w = newitem->GetWindow())
915         w->SetContainingSizer(this);
916 
917     return true;
918 }
919 
Clear(bool delete_windows)920 void wxSizer::Clear( bool delete_windows )
921 {
922     // First clear the ContainingSizer pointers
923     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
924     while (node)
925     {
926         wxSizerItem     *item = node->GetData();
927 
928         if (item->IsWindow())
929             item->GetWindow()->SetContainingSizer( NULL );
930         node = node->GetNext();
931     }
932 
933     // Destroy the windows if needed
934     if (delete_windows)
935         DeleteWindows();
936 
937     // Now empty the list
938     WX_CLEAR_LIST(wxSizerItemList, m_children);
939 }
940 
DeleteWindows()941 void wxSizer::DeleteWindows()
942 {
943     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
944     while (node)
945     {
946         wxSizerItem     *item = node->GetData();
947 
948         item->DeleteWindows();
949         node = node->GetNext();
950     }
951 }
952 
ComputeFittingClientSize(wxWindow * window)953 wxSize wxSizer::ComputeFittingClientSize(wxWindow *window)
954 {
955     wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" );
956 
957     // take the min size by default and limit it by max size
958     wxSize size = GetMinClientSize(window);
959     wxSize sizeMax;
960 
961     wxTopLevelWindow *tlw = wxDynamicCast(window, wxTopLevelWindow);
962     if ( tlw )
963     {
964         // hack for small screen devices where TLWs are always full screen
965         if ( tlw->IsAlwaysMaximized() )
966         {
967             return tlw->GetClientSize();
968         }
969 
970         // limit the window to the size of the display it is on (or the main
971         // one if the window display can't be determined)
972         sizeMax = wxDisplay(window).GetClientArea().GetSize();
973 
974         // If determining the display size failed, skip the max size checks as
975         // we really don't want to create windows of (0, 0) size.
976         if ( !sizeMax.x || !sizeMax.y )
977             return size;
978 
979         // space for decorations and toolbars etc.
980         sizeMax = tlw->WindowToClientSize(sizeMax);
981     }
982     else
983     {
984         sizeMax = GetMaxClientSize(window);
985     }
986 
987     if ( sizeMax.x != wxDefaultCoord && size.x > sizeMax.x )
988             size.x = sizeMax.x;
989     if ( sizeMax.y != wxDefaultCoord && size.y > sizeMax.y )
990             size.y = sizeMax.y;
991 
992     return size;
993 }
994 
ComputeFittingWindowSize(wxWindow * window)995 wxSize wxSizer::ComputeFittingWindowSize(wxWindow *window)
996 {
997     wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" );
998 
999     return window->ClientToWindowSize(ComputeFittingClientSize(window));
1000 }
1001 
Fit(wxWindow * window)1002 wxSize wxSizer::Fit( wxWindow *window )
1003 {
1004     wxCHECK_MSG( window, wxDefaultSize, "window can't be NULL" );
1005 
1006     // set client size
1007     window->WXSetInitialFittingClientSize(wxSIZE_SET_CURRENT);
1008 
1009     // return entire size
1010     return window->GetSize();
1011 }
1012 
FitInside(wxWindow * window)1013 void wxSizer::FitInside( wxWindow *window )
1014 {
1015     wxSize size;
1016     if (window->IsTopLevel())
1017         size = VirtualFitSize( window );
1018     else
1019         size = GetMinClientSize( window );
1020 
1021     window->SetVirtualSize( size );
1022 }
1023 
RecalcSizes()1024 void wxSizer::RecalcSizes()
1025 {
1026     // It is recommended to override RepositionChildren() in the derived
1027     // classes, but if they don't do it, this method must be overridden.
1028     wxFAIL_MSG( wxS("Must be overridden if RepositionChildren() is not") );
1029 }
1030 
Layout()1031 void wxSizer::Layout()
1032 {
1033     // (re)calculates minimums needed for each item and other preparations
1034     // for layout
1035     const wxSize minSize = CalcMin();
1036 
1037     // Applies the layout and repositions/resizes the items
1038     wxWindow::ChildrenRepositioningGuard repositionGuard(m_containingWindow);
1039 
1040     RepositionChildren(minSize);
1041 }
1042 
SetSizeHints(wxWindow * window)1043 void wxSizer::SetSizeHints( wxWindow *window )
1044 {
1045     // Preserve the window's max size hints, but set the
1046     // lower bound according to the sizer calculations.
1047     window->WXSetInitialFittingClientSize(wxSIZE_SET_CURRENT | wxSIZE_SET_MIN);
1048 }
1049 
1050 #if WXWIN_COMPATIBILITY_2_8
SetVirtualSizeHints(wxWindow * window)1051 void wxSizer::SetVirtualSizeHints( wxWindow *window )
1052 {
1053     FitInside( window );
1054 }
1055 #endif // WXWIN_COMPATIBILITY_2_8
1056 
1057 // TODO on mac we need a function that determines how much free space this
1058 // min size contains, in order to make sure that we have 20 pixels of free
1059 // space around the controls
GetMaxClientSize(wxWindow * window) const1060 wxSize wxSizer::GetMaxClientSize( wxWindow *window ) const
1061 {
1062     return window->WindowToClientSize(window->GetMaxSize());
1063 }
1064 
GetMinClientSize(wxWindow * WXUNUSED (window))1065 wxSize wxSizer::GetMinClientSize( wxWindow *WXUNUSED(window) )
1066 {
1067     return GetMinSize();  // Already returns client size.
1068 }
1069 
VirtualFitSize(wxWindow * window)1070 wxSize wxSizer::VirtualFitSize( wxWindow *window )
1071 {
1072     wxSize size     = GetMinClientSize( window );
1073     wxSize sizeMax  = GetMaxClientSize( window );
1074 
1075     // Limit the size if sizeMax != wxDefaultSize
1076 
1077     if ( size.x > sizeMax.x && sizeMax.x != wxDefaultCoord )
1078         size.x = sizeMax.x;
1079     if ( size.y > sizeMax.y && sizeMax.y != wxDefaultCoord )
1080         size.y = sizeMax.y;
1081 
1082     return size;
1083 }
1084 
GetMinSize()1085 wxSize wxSizer::GetMinSize()
1086 {
1087     wxSize ret( CalcMin() );
1088     if (ret.x < m_minSize.x) ret.x = m_minSize.x;
1089     if (ret.y < m_minSize.y) ret.y = m_minSize.y;
1090     return ret;
1091 }
1092 
DoSetMinSize(int width,int height)1093 void wxSizer::DoSetMinSize( int width, int height )
1094 {
1095     m_minSize.x = width;
1096     m_minSize.y = height;
1097 }
1098 
DoSetItemMinSize(wxWindow * window,int width,int height)1099 bool wxSizer::DoSetItemMinSize( wxWindow *window, int width, int height )
1100 {
1101     wxASSERT_MSG( window, wxT("SetMinSize for NULL window") );
1102 
1103     // Is it our immediate child?
1104 
1105     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1106     while (node)
1107     {
1108         wxSizerItem     *item = node->GetData();
1109 
1110         if (item->GetWindow() == window)
1111         {
1112             item->SetMinSize( width, height );
1113             return true;
1114         }
1115         node = node->GetNext();
1116     }
1117 
1118     // No?  Search any subsizers we own then
1119 
1120     node = m_children.GetFirst();
1121     while (node)
1122     {
1123         wxSizerItem     *item = node->GetData();
1124 
1125         if ( item->GetSizer() &&
1126              item->GetSizer()->DoSetItemMinSize( window, width, height ) )
1127         {
1128             // A child sizer found the requested windw, exit.
1129             return true;
1130         }
1131         node = node->GetNext();
1132     }
1133 
1134     return false;
1135 }
1136 
DoSetItemMinSize(wxSizer * sizer,int width,int height)1137 bool wxSizer::DoSetItemMinSize( wxSizer *sizer, int width, int height )
1138 {
1139     wxASSERT_MSG( sizer, wxT("SetMinSize for NULL sizer") );
1140 
1141     // Is it our immediate child?
1142 
1143     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1144     while (node)
1145     {
1146         wxSizerItem     *item = node->GetData();
1147 
1148         if (item->GetSizer() == sizer)
1149         {
1150             item->GetSizer()->DoSetMinSize( width, height );
1151             return true;
1152         }
1153         node = node->GetNext();
1154     }
1155 
1156     // No?  Search any subsizers we own then
1157 
1158     node = m_children.GetFirst();
1159     while (node)
1160     {
1161         wxSizerItem     *item = node->GetData();
1162 
1163         if ( item->GetSizer() &&
1164              item->GetSizer()->DoSetItemMinSize( sizer, width, height ) )
1165         {
1166             // A child found the requested sizer, exit.
1167             return true;
1168         }
1169         node = node->GetNext();
1170     }
1171 
1172     return false;
1173 }
1174 
DoSetItemMinSize(size_t index,int width,int height)1175 bool wxSizer::DoSetItemMinSize( size_t index, int width, int height )
1176 {
1177     wxSizerItemList::compatibility_iterator node = m_children.Item( index );
1178 
1179     wxCHECK_MSG( node, false, wxT("Failed to find child node") );
1180 
1181     wxSizerItem     *item = node->GetData();
1182 
1183     if (item->GetSizer())
1184     {
1185         // Sizers contains the minimal size in them, if not calculated ...
1186         item->GetSizer()->DoSetMinSize( width, height );
1187     }
1188     else
1189     {
1190         // ... but the minimal size of spacers and windows is stored via the item
1191         item->SetMinSize( width, height );
1192     }
1193 
1194     return true;
1195 }
1196 
GetItem(wxWindow * window,bool recursive)1197 wxSizerItem* wxSizer::GetItem( wxWindow *window, bool recursive )
1198 {
1199     wxASSERT_MSG( window, wxT("GetItem for NULL window") );
1200 
1201     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1202     while (node)
1203     {
1204         wxSizerItem     *item = node->GetData();
1205 
1206         if (item->GetWindow() == window)
1207         {
1208             return item;
1209         }
1210         else if (recursive && item->IsSizer())
1211         {
1212             wxSizerItem *subitem = item->GetSizer()->GetItem( window, true );
1213             if (subitem)
1214                 return subitem;
1215         }
1216 
1217         node = node->GetNext();
1218     }
1219 
1220     return NULL;
1221 }
1222 
GetItem(wxSizer * sizer,bool recursive)1223 wxSizerItem* wxSizer::GetItem( wxSizer *sizer, bool recursive )
1224 {
1225     wxASSERT_MSG( sizer, wxT("GetItem for NULL sizer") );
1226 
1227     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1228     while (node)
1229     {
1230         wxSizerItem *item = node->GetData();
1231 
1232         if (item->GetSizer() == sizer)
1233         {
1234             return item;
1235         }
1236         else if (recursive && item->IsSizer())
1237         {
1238             wxSizerItem *subitem = item->GetSizer()->GetItem( sizer, true );
1239             if (subitem)
1240                 return subitem;
1241         }
1242 
1243         node = node->GetNext();
1244     }
1245 
1246     return NULL;
1247 }
1248 
GetItem(size_t index)1249 wxSizerItem* wxSizer::GetItem( size_t index )
1250 {
1251     wxCHECK_MSG( index < m_children.GetCount(),
1252                  NULL,
1253                  wxT("GetItem index is out of range") );
1254 
1255     return m_children.Item( index )->GetData();
1256 }
1257 
GetItemById(int id,bool recursive)1258 wxSizerItem* wxSizer::GetItemById( int id, bool recursive )
1259 {
1260     // This gets a sizer item by the id of the sizer item
1261     // and NOT the id of a window if the item is a window.
1262 
1263     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1264     while (node)
1265     {
1266         wxSizerItem     *item = node->GetData();
1267 
1268         if (item->GetId() == id)
1269         {
1270             return item;
1271         }
1272         else if (recursive && item->IsSizer())
1273         {
1274             wxSizerItem *subitem = item->GetSizer()->GetItemById( id, true );
1275             if (subitem)
1276                 return subitem;
1277         }
1278 
1279         node = node->GetNext();
1280     }
1281 
1282     return NULL;
1283 }
1284 
Show(wxWindow * window,bool show,bool recursive)1285 bool wxSizer::Show( wxWindow *window, bool show, bool recursive )
1286 {
1287     wxSizerItem *item = GetItem( window, recursive );
1288 
1289     if ( item )
1290     {
1291          item->Show( show );
1292          return true;
1293     }
1294 
1295     return false;
1296 }
1297 
Show(wxSizer * sizer,bool show,bool recursive)1298 bool wxSizer::Show( wxSizer *sizer, bool show, bool recursive )
1299 {
1300     wxSizerItem *item = GetItem( sizer, recursive );
1301 
1302     if ( item )
1303     {
1304          item->Show( show );
1305          return true;
1306     }
1307 
1308     return false;
1309 }
1310 
Show(size_t index,bool show)1311 bool wxSizer::Show( size_t index, bool show)
1312 {
1313     wxSizerItem *item = GetItem( index );
1314 
1315     if ( item )
1316     {
1317          item->Show( show );
1318          return true;
1319     }
1320 
1321     return false;
1322 }
1323 
ShowItems(bool show)1324 void wxSizer::ShowItems( bool show )
1325 {
1326     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1327     while (node)
1328     {
1329         node->GetData()->Show( show );
1330         node = node->GetNext();
1331     }
1332 }
1333 
AreAnyItemsShown() const1334 bool wxSizer::AreAnyItemsShown() const
1335 {
1336     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1337     while (node)
1338     {
1339         if ( node->GetData()->IsShown() )
1340             return true;
1341         node = node->GetNext();
1342     }
1343 
1344     return false;
1345 }
1346 
IsShown(wxWindow * window) const1347 bool wxSizer::IsShown( wxWindow *window ) const
1348 {
1349     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1350     while (node)
1351     {
1352         wxSizerItem     *item = node->GetData();
1353 
1354         if (item->GetWindow() == window)
1355         {
1356             return item->IsShown();
1357         }
1358         node = node->GetNext();
1359     }
1360 
1361     wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1362 
1363     return false;
1364 }
1365 
IsShown(wxSizer * sizer) const1366 bool wxSizer::IsShown( wxSizer *sizer ) const
1367 {
1368     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1369     while (node)
1370     {
1371         wxSizerItem     *item = node->GetData();
1372 
1373         if (item->GetSizer() == sizer)
1374         {
1375             return item->IsShown();
1376         }
1377         node = node->GetNext();
1378     }
1379 
1380     wxFAIL_MSG( wxT("IsShown failed to find sizer item") );
1381 
1382     return false;
1383 }
1384 
IsShown(size_t index) const1385 bool wxSizer::IsShown( size_t index ) const
1386 {
1387     wxCHECK_MSG( index < m_children.GetCount(),
1388                  false,
1389                  wxT("IsShown index is out of range") );
1390 
1391     return m_children.Item( index )->GetData()->IsShown();
1392 }
1393 
1394 
1395 //---------------------------------------------------------------------------
1396 // wxGridSizer
1397 //---------------------------------------------------------------------------
1398 
wxGridSizer(int cols,int vgap,int hgap)1399 wxGridSizer::wxGridSizer( int cols, int vgap, int hgap )
1400     : m_rows( cols == 0 ? 1 : 0 ),
1401       m_cols( cols ),
1402       m_vgap( vgap ),
1403       m_hgap( hgap )
1404 {
1405     wxASSERT(cols >= 0);
1406 }
1407 
wxGridSizer(int cols,const wxSize & gap)1408 wxGridSizer::wxGridSizer( int cols, const wxSize& gap )
1409     : m_rows( cols == 0 ? 1 : 0 ),
1410       m_cols( cols ),
1411       m_vgap( gap.GetHeight() ),
1412       m_hgap( gap.GetWidth() )
1413 {
1414     wxASSERT(cols >= 0);
1415 }
1416 
wxGridSizer(int rows,int cols,int vgap,int hgap)1417 wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
1418     : m_rows( rows || cols ? rows : 1 ),
1419       m_cols( cols ),
1420       m_vgap( vgap ),
1421       m_hgap( hgap )
1422 {
1423     wxASSERT(rows >= 0 && cols >= 0);
1424 }
1425 
wxGridSizer(int rows,int cols,const wxSize & gap)1426 wxGridSizer::wxGridSizer( int rows, int cols, const wxSize& gap )
1427     : m_rows( rows || cols ? rows : 1 ),
1428       m_cols( cols ),
1429       m_vgap( gap.GetHeight() ),
1430       m_hgap( gap.GetWidth() )
1431 {
1432     wxASSERT(rows >= 0 && cols >= 0);
1433 }
1434 
DoInsert(size_t index,wxSizerItem * item)1435 wxSizerItem *wxGridSizer::DoInsert(size_t index, wxSizerItem *item)
1436 {
1437     // Ensure that the item will be deleted in case of exception.
1438     wxScopedPtr<wxSizerItem> scopedItem( item );
1439 
1440     // if only the number of columns or the number of rows is specified for a
1441     // sizer, arbitrarily many items can be added to it but if both of them are
1442     // fixed, then the sizer can't have more than that many items -- check for
1443     // this here to ensure that we detect errors as soon as possible
1444     if ( m_cols && m_rows )
1445     {
1446         const int nitems = m_children.GetCount();
1447         if ( nitems == m_cols*m_rows )
1448         {
1449             wxFAIL_MSG(
1450                 wxString::Format(
1451                     "too many items (%d > %d*%d) in grid sizer (maybe you "
1452                     "should omit the number of either rows or columns?)",
1453                 nitems + 1, m_cols, m_rows)
1454             );
1455 
1456             // additionally, continuing to use the specified number of columns
1457             // and rows is not a good idea as callers of CalcRowsCols() expect
1458             // that all sizer items can fit into m_cols-/m_rows-sized arrays
1459             // which is not the case if there are too many items and results in
1460             // crashes, so let it compute the number of rows automatically by
1461             // forgetting the (wrong) number of rows specified (this also has a
1462             // nice side effect of giving only one assert even if there are
1463             // many more items than allowed in this sizer)
1464             m_rows = 0;
1465         }
1466     }
1467 
1468     const int flags = item->GetFlag();
1469     if ( flags & wxEXPAND )
1470     {
1471         // Check that expansion will happen in at least one of the directions.
1472         wxASSERT_MSG
1473         (
1474             !(flags & (wxALIGN_BOTTOM | wxALIGN_CENTRE_VERTICAL)) ||
1475                 !(flags & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL)),
1476             wxS("wxEXPAND flag will be overridden by alignment flags")
1477         );
1478     }
1479 
1480     return wxSizer::DoInsert( index, scopedItem.release() );
1481 }
1482 
CalcRowsCols(int & nrows,int & ncols) const1483 int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const
1484 {
1485     const int nitems = m_children.GetCount();
1486 
1487     ncols = GetEffectiveColsCount();
1488     nrows = GetEffectiveRowsCount();
1489 
1490     // Since Insert() checks for overpopulation, the following
1491     // should only assert if the grid was shrunk via SetRows() / SetCols()
1492     wxASSERT_MSG( nitems <= ncols*nrows, "logic error in wxGridSizer" );
1493 
1494     return nitems;
1495 }
1496 
RepositionChildren(const wxSize & WXUNUSED (minSize))1497 void wxGridSizer::RepositionChildren(const wxSize& WXUNUSED(minSize))
1498 {
1499     int nitems, nrows, ncols;
1500     if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
1501         return;
1502 
1503     wxSize sz( GetSize() );
1504     wxPoint pt( GetPosition() );
1505 
1506     int w = (sz.x - (ncols - 1) * m_hgap) / ncols;
1507     int h = (sz.y - (nrows - 1) * m_vgap) / nrows;
1508 
1509     int x = pt.x;
1510     for (int c = 0; c < ncols; c++)
1511     {
1512         int y = pt.y;
1513         for (int r = 0; r < nrows; r++)
1514         {
1515             int i = r * ncols + c;
1516             if (i < nitems)
1517             {
1518                 wxSizerItemList::compatibility_iterator node = m_children.Item( i );
1519 
1520                 wxASSERT_MSG( node, wxT("Failed to find SizerItemList node") );
1521 
1522                 SetItemBounds( node->GetData(), x, y, w, h);
1523             }
1524             y = y + h + m_vgap;
1525         }
1526         x = x + w + m_hgap;
1527     }
1528 }
1529 
CalcMin()1530 wxSize wxGridSizer::CalcMin()
1531 {
1532     int nrows, ncols;
1533     if ( CalcRowsCols(nrows, ncols) == 0 )
1534         return wxSize();
1535 
1536     // Find the max width and height for any component
1537     int w = 0;
1538     int h = 0;
1539 
1540     wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
1541     while (node)
1542     {
1543         wxSizerItem     *item = node->GetData();
1544         wxSize           sz( item->CalcMin() );
1545 
1546         w = wxMax( w, sz.x );
1547         h = wxMax( h, sz.y );
1548 
1549         node = node->GetNext();
1550     }
1551 
1552     // In case we have a nested sizer with a two step algo , give it
1553     // a chance to adjust to that (we give it width component)
1554     node = m_children.GetFirst();
1555     bool didChangeMinSize = false;
1556     while (node)
1557     {
1558         wxSizerItem     *item = node->GetData();
1559         didChangeMinSize |= item->InformFirstDirection( wxHORIZONTAL, w, -1 );
1560 
1561         node = node->GetNext();
1562     }
1563 
1564     // And redo iteration in case min size changed
1565     if( didChangeMinSize )
1566     {
1567         node = m_children.GetFirst();
1568         w = h = 0;
1569         while (node)
1570         {
1571             wxSizerItem     *item = node->GetData();
1572             wxSize           sz( item->GetMinSizeWithBorder() );
1573 
1574             w = wxMax( w, sz.x );
1575             h = wxMax( h, sz.y );
1576 
1577             node = node->GetNext();
1578         }
1579     }
1580 
1581     return wxSize( ncols * w + (ncols-1) * m_hgap,
1582                    nrows * h + (nrows-1) * m_vgap );
1583 }
1584 
SetItemBounds(wxSizerItem * item,int x,int y,int w,int h)1585 void wxGridSizer::SetItemBounds( wxSizerItem *item, int x, int y, int w, int h )
1586 {
1587     wxPoint pt( x,y );
1588     wxSize sz( item->GetMinSizeWithBorder() );
1589     int flag = item->GetFlag();
1590 
1591     // wxSHAPED maintains aspect ratio and so always applies to both
1592     // directions.
1593     if ( flag & wxSHAPED )
1594     {
1595         sz = wxSize(w, h);
1596     }
1597     else // Otherwise we handle each direction individually.
1598     {
1599         if (flag & wxALIGN_CENTER_HORIZONTAL)
1600         {
1601             pt.x = x + (w - sz.x) / 2;
1602         }
1603         else if (flag & wxALIGN_RIGHT)
1604         {
1605             pt.x = x + (w - sz.x);
1606         }
1607         else if (flag & wxEXPAND)
1608         {
1609             sz.x = w;
1610         }
1611 
1612         if (flag & wxALIGN_CENTER_VERTICAL)
1613         {
1614             pt.y = y + (h - sz.y) / 2;
1615         }
1616         else if (flag & wxALIGN_BOTTOM)
1617         {
1618             pt.y = y + (h - sz.y);
1619         }
1620         else if ( flag & wxEXPAND )
1621         {
1622             sz.y = h;
1623         }
1624     }
1625 
1626     item->SetDimension(pt, sz);
1627 }
1628 
1629 //---------------------------------------------------------------------------
1630 // wxFlexGridSizer
1631 //---------------------------------------------------------------------------
1632 
wxFlexGridSizer(int cols,int vgap,int hgap)1633 wxFlexGridSizer::wxFlexGridSizer( int cols, int vgap, int hgap )
1634                : wxGridSizer( cols, vgap, hgap ),
1635                  m_flexDirection(wxBOTH),
1636                  m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1637 {
1638 }
1639 
wxFlexGridSizer(int cols,const wxSize & gap)1640 wxFlexGridSizer::wxFlexGridSizer( int cols, const wxSize& gap )
1641                : wxGridSizer( cols, gap ),
1642                  m_flexDirection(wxBOTH),
1643                  m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1644 {
1645 }
1646 
wxFlexGridSizer(int rows,int cols,int vgap,int hgap)1647 wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, int vgap, int hgap )
1648                : wxGridSizer( rows, cols, vgap, hgap ),
1649                  m_flexDirection(wxBOTH),
1650                  m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1651 {
1652 }
1653 
wxFlexGridSizer(int rows,int cols,const wxSize & gap)1654 wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, const wxSize& gap )
1655                : wxGridSizer( rows, cols, gap ),
1656                  m_flexDirection(wxBOTH),
1657                  m_growMode(wxFLEX_GROWMODE_SPECIFIED)
1658 {
1659 }
1660 
~wxFlexGridSizer()1661 wxFlexGridSizer::~wxFlexGridSizer()
1662 {
1663 }
1664 
RepositionChildren(const wxSize & minSize)1665 void wxFlexGridSizer::RepositionChildren(const wxSize& minSize)
1666 {
1667     int nrows, ncols;
1668     if ( !CalcRowsCols(nrows, ncols) )
1669         return;
1670 
1671     const wxPoint pt(GetPosition());
1672     const wxSize sz(GetSize());
1673 
1674     AdjustForGrowables(sz, minSize);
1675 
1676     wxSizerItemList::const_iterator i = m_children.begin();
1677     const wxSizerItemList::const_iterator end = m_children.end();
1678 
1679     int y = 0;
1680     for ( int r = 0; r < nrows; r++ )
1681     {
1682         if ( m_rowHeights[r] == -1 )
1683         {
1684             // this row is entirely hidden, skip it
1685             for ( int c = 0; c < ncols; c++ )
1686             {
1687                 if ( i == end )
1688                     return;
1689 
1690                 ++i;
1691             }
1692 
1693             continue;
1694         }
1695 
1696         const int hrow = m_rowHeights[r];
1697         int h = sz.y - y; // max remaining height, don't overflow it
1698         if ( hrow < h )
1699             h = hrow;
1700 
1701         int x = 0;
1702         for ( int c = 0; c < ncols && i != end; c++, ++i )
1703         {
1704             const int wcol = m_colWidths[c];
1705 
1706             if ( wcol == -1 )
1707                 continue;
1708 
1709             int w = sz.x - x; // max possible value, ensure we don't overflow
1710             if ( wcol < w )
1711                 w = wcol;
1712 
1713             SetItemBounds(*i, pt.x + x, pt.y + y, w, h);
1714 
1715             x += wcol + m_hgap;
1716         }
1717 
1718         if ( i == end )
1719             return;
1720 
1721         y += hrow + m_vgap;
1722     }
1723 }
1724 
1725 // helper function used in CalcMin() to sum up the sizes of non-hidden items
SumArraySizes(const wxArrayInt & sizes,int gap)1726 static int SumArraySizes(const wxArrayInt& sizes, int gap)
1727 {
1728     // Sum total minimum size, including gaps between rows/columns.
1729     // -1 is used as a magic number meaning empty row/column.
1730     int total = 0;
1731 
1732     const size_t count = sizes.size();
1733     for ( size_t n = 0; n < count; n++ )
1734     {
1735         if ( sizes[n] != -1 )
1736         {
1737             if ( total )
1738                 total += gap; // separate from the previous column
1739 
1740             total += sizes[n];
1741         }
1742     }
1743 
1744     return total;
1745 }
1746 
FindWidthsAndHeights(int WXUNUSED (nrows),int ncols)1747 wxSize wxFlexGridSizer::FindWidthsAndHeights(int WXUNUSED(nrows), int ncols)
1748 {
1749     // n is the index of the item in left-to-right top-to-bottom order
1750     size_t n = 0;
1751     for ( wxSizerItemList::iterator i = m_children.begin();
1752           i != m_children.end();
1753           ++i, ++n )
1754     {
1755         wxSizerItem * const item = *i;
1756         if ( item->IsShown() )
1757         {
1758             // NOTE: Not doing the calculation here, this is just
1759             // for finding max values.
1760             const wxSize sz(item->GetMinSizeWithBorder());
1761 
1762             const int row = n / ncols;
1763             const int col = n % ncols;
1764 
1765             if ( sz.y > m_rowHeights[row] )
1766                 m_rowHeights[row] = sz.y;
1767             if ( sz.x > m_colWidths[col] )
1768                 m_colWidths[col] = sz.x;
1769         }
1770     }
1771 
1772     AdjustForFlexDirection();
1773 
1774     return wxSize(SumArraySizes(m_colWidths, m_hgap),
1775                   SumArraySizes(m_rowHeights, m_vgap));
1776 }
1777 
CalcMin()1778 wxSize wxFlexGridSizer::CalcMin()
1779 {
1780     int nrows,
1781         ncols;
1782 
1783     // Number of rows/columns can change as items are added or removed.
1784     if ( !CalcRowsCols(nrows, ncols) )
1785         return wxSize();
1786 
1787 
1788     // We have to recalculate the sizes in case the item minimum size has
1789     // changed since the previous layout, or the item has been hidden using
1790     // wxSizer::Show(). If all the items in a row/column are hidden, the final
1791     // dimension of the row/column will be -1, indicating that the column
1792     // itself is hidden.
1793     m_rowHeights.assign(nrows, -1);
1794     m_colWidths.assign(ncols, -1);
1795 
1796     for ( wxSizerItemList::iterator i = m_children.begin();
1797           i != m_children.end();
1798           ++i)
1799     {
1800         wxSizerItem * const item = *i;
1801         if ( item->IsShown() )
1802         {
1803             item->CalcMin();
1804         }
1805     }
1806 
1807     return FindWidthsAndHeights(nrows,ncols);
1808 }
1809 
AdjustForFlexDirection()1810 void wxFlexGridSizer::AdjustForFlexDirection()
1811 {
1812     // the logic in CalcMin works when we resize flexibly in both directions
1813     // but maybe this is not the case
1814     if ( m_flexDirection != wxBOTH )
1815     {
1816         // select the array corresponding to the direction in which we do *not*
1817         // resize flexibly
1818         wxArrayInt& array = m_flexDirection == wxVERTICAL ? m_colWidths
1819                                                           : m_rowHeights;
1820 
1821         const size_t count = array.GetCount();
1822 
1823         // find the largest value in this array
1824         size_t n;
1825         int largest = 0;
1826 
1827         for ( n = 0; n < count; ++n )
1828         {
1829             if ( array[n] > largest )
1830                 largest = array[n];
1831         }
1832 
1833         // and now fill it with the largest value
1834         for ( n = 0; n < count; ++n )
1835         {
1836             // don't touch hidden rows
1837             if ( array[n] != -1 )
1838                 array[n] = largest;
1839         }
1840     }
1841 }
1842 
1843 // helper of AdjustForGrowables() which is called for rows/columns separately
1844 //
1845 // parameters:
1846 //      delta: the extra space, we do nothing unless it's positive
1847 //      growable: indices or growable rows/cols in sizes array
1848 //      sizes: the height/widths of rows/cols to adjust
1849 //      proportions: proportions of the growable rows/cols or NULL if they all
1850 //                   should be assumed to have proportion of 1
1851 static void
DoAdjustForGrowables(int delta,const wxArrayInt & growable,wxArrayInt & sizes,const wxArrayInt * proportions)1852 DoAdjustForGrowables(int delta,
1853                      const wxArrayInt& growable,
1854                      wxArrayInt& sizes,
1855                      const wxArrayInt *proportions)
1856 {
1857     if ( delta <= 0 )
1858         return;
1859 
1860     // total sum of proportions of all non-hidden rows
1861     int sum_proportions = 0;
1862 
1863     // number of currently shown growable rows
1864     int num = 0;
1865 
1866     const int max_idx = sizes.size();
1867 
1868     const size_t count = growable.size();
1869     size_t idx;
1870     for ( idx = 0; idx < count; idx++ )
1871     {
1872         // Since the number of rows/columns can change as items are
1873         // inserted/deleted, we need to verify at runtime that the
1874         // requested growable rows/columns are still valid.
1875         if ( growable[idx] >= max_idx )
1876             continue;
1877 
1878         // If all items in a row/column are hidden, that row/column will
1879         // have a dimension of -1.  This causes the row/column to be
1880         // hidden completely.
1881         if ( sizes[growable[idx]] == -1 )
1882             continue;
1883 
1884         if ( proportions )
1885             sum_proportions += (*proportions)[idx];
1886 
1887         num++;
1888     }
1889 
1890     if ( !num )
1891         return;
1892 
1893     // the remaining extra free space, adjusted during each iteration
1894     for ( idx = 0; idx < count; idx++ )
1895     {
1896         if ( growable[idx] >= max_idx )
1897             continue;
1898 
1899         if ( sizes[ growable[idx] ] == -1 )
1900             continue;
1901 
1902         int cur_delta;
1903         if ( sum_proportions == 0 )
1904         {
1905             // no growable rows -- divide extra space evenly among all
1906             cur_delta = delta/num;
1907             num--;
1908         }
1909         else // allocate extra space proportionally
1910         {
1911             const int cur_prop = (*proportions)[idx];
1912             cur_delta = (delta*cur_prop)/sum_proportions;
1913             sum_proportions -= cur_prop;
1914         }
1915 
1916         sizes[growable[idx]] += cur_delta;
1917         delta -= cur_delta;
1918     }
1919 }
1920 
AdjustForGrowables(const wxSize & sz,const wxSize & minSize)1921 void wxFlexGridSizer::AdjustForGrowables(const wxSize& sz, const wxSize& minSize)
1922 {
1923 #if wxDEBUG_LEVEL
1924     // by the time this function is called, the sizer should be already fully
1925     // initialized and hence the number of its columns and rows is known and we
1926     // can check that all indices in m_growableCols/Rows are valid (see also
1927     // comments in AddGrowableCol/Row())
1928     if ( !m_rows || !m_cols )
1929     {
1930         if ( !m_rows )
1931         {
1932             int nrows = CalcRows();
1933 
1934             for ( size_t n = 0; n < m_growableRows.size(); n++ )
1935             {
1936                 wxASSERT_MSG( m_growableRows[n] < nrows,
1937                               "invalid growable row index" );
1938             }
1939         }
1940 
1941         if ( !m_cols )
1942         {
1943             int ncols = CalcCols();
1944 
1945             for ( size_t n = 0; n < m_growableCols.size(); n++ )
1946             {
1947                 wxASSERT_MSG( m_growableCols[n] < ncols,
1948                               "invalid growable column index" );
1949             }
1950         }
1951     }
1952 #endif // wxDEBUG_LEVEL
1953 
1954 
1955     if ( (m_flexDirection & wxHORIZONTAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
1956     {
1957         DoAdjustForGrowables
1958         (
1959             sz.x - minSize.x,
1960             m_growableCols,
1961             m_colWidths,
1962             m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
1963                                                     : NULL
1964         );
1965 
1966         // This gives nested objects that benefit from knowing one size
1967         // component in advance the chance to use that.
1968         bool didAdjustMinSize = false;
1969 
1970         // Iterate over all items and inform about column width
1971         const int ncols = GetEffectiveColsCount();
1972         int col = 0;
1973         for ( wxSizerItemList::iterator i = m_children.begin();
1974               i != m_children.end();
1975               ++i )
1976         {
1977             didAdjustMinSize |= (*i)->InformFirstDirection(wxHORIZONTAL, m_colWidths[col], sz.y - minSize.y);
1978             if ( ++col == ncols )
1979                 col = 0;
1980         }
1981 
1982         // Only redo if info was actually used
1983         if( didAdjustMinSize )
1984         {
1985             DoAdjustForGrowables
1986             (
1987                 sz.x - minSize.x,
1988                 m_growableCols,
1989                 m_colWidths,
1990                 m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableColsProportions
1991                                                         : NULL
1992             );
1993         }
1994     }
1995 
1996     if ( (m_flexDirection & wxVERTICAL) || (m_growMode != wxFLEX_GROWMODE_NONE) )
1997     {
1998         // pass NULL instead of proportions if the grow mode is ALL as we
1999         // should treat all rows as having proportion of 1 then
2000         DoAdjustForGrowables
2001         (
2002             sz.y - minSize.y,
2003             m_growableRows,
2004             m_rowHeights,
2005             m_growMode == wxFLEX_GROWMODE_SPECIFIED ? &m_growableRowsProportions
2006                                                     : NULL
2007         );
2008     }
2009 }
2010 
IsRowGrowable(size_t idx)2011 bool wxFlexGridSizer::IsRowGrowable( size_t idx )
2012 {
2013     return m_growableRows.Index( idx ) != wxNOT_FOUND;
2014 }
2015 
IsColGrowable(size_t idx)2016 bool wxFlexGridSizer::IsColGrowable( size_t idx )
2017 {
2018     return m_growableCols.Index( idx ) != wxNOT_FOUND;
2019 }
2020 
AddGrowableRow(size_t idx,int proportion)2021 void wxFlexGridSizer::AddGrowableRow( size_t idx, int proportion )
2022 {
2023     wxASSERT_MSG( !IsRowGrowable( idx ),
2024                   "AddGrowableRow() called for growable row" );
2025 
2026     // notice that we intentionally don't check the index validity here in (the
2027     // common) case when the number of rows was not specified in the ctor -- in
2028     // this case it will be computed only later, when all items are added to
2029     // the sizer, and the check will be done in AdjustForGrowables()
2030     wxCHECK_RET( !m_rows || idx < (size_t)m_rows, "invalid row index" );
2031 
2032     m_growableRows.Add( idx );
2033     m_growableRowsProportions.Add( proportion );
2034 }
2035 
AddGrowableCol(size_t idx,int proportion)2036 void wxFlexGridSizer::AddGrowableCol( size_t idx, int proportion )
2037 {
2038     wxASSERT_MSG( !IsColGrowable( idx ),
2039                   "AddGrowableCol() called for growable column" );
2040 
2041     // see comment in AddGrowableRow(): although it's less common to omit the
2042     // specification of the number of columns, it still can also happen
2043     wxCHECK_RET( !m_cols || idx < (size_t)m_cols, "invalid column index" );
2044 
2045     m_growableCols.Add( idx );
2046     m_growableColsProportions.Add( proportion );
2047 }
2048 
2049 // helper function for RemoveGrowableCol/Row()
2050 static void
DoRemoveFromArrays(size_t idx,wxArrayInt & items,wxArrayInt & proportions)2051 DoRemoveFromArrays(size_t idx, wxArrayInt& items, wxArrayInt& proportions)
2052 {
2053     const size_t count = items.size();
2054     for ( size_t n = 0; n < count; n++ )
2055     {
2056         if ( (size_t)items[n] == idx )
2057         {
2058             items.RemoveAt(n);
2059             proportions.RemoveAt(n);
2060             return;
2061         }
2062     }
2063 
2064     wxFAIL_MSG( wxT("column/row is already not growable") );
2065 }
2066 
RemoveGrowableCol(size_t idx)2067 void wxFlexGridSizer::RemoveGrowableCol( size_t idx )
2068 {
2069     DoRemoveFromArrays(idx, m_growableCols, m_growableColsProportions);
2070 }
2071 
RemoveGrowableRow(size_t idx)2072 void wxFlexGridSizer::RemoveGrowableRow( size_t idx )
2073 {
2074     DoRemoveFromArrays(idx, m_growableRows, m_growableRowsProportions);
2075 }
2076 
2077 //---------------------------------------------------------------------------
2078 // wxBoxSizer
2079 //---------------------------------------------------------------------------
2080 
DoInsert(size_t index,wxSizerItem * item)2081 wxSizerItem *wxBoxSizer::DoInsert(size_t index, wxSizerItem *item)
2082 {
2083     const int flags = item->GetFlag();
2084     if ( IsVertical() )
2085     {
2086         wxASSERT_MSG
2087         (
2088             !(flags & wxALIGN_BOTTOM),
2089             wxS("Vertical alignment flags are ignored in vertical sizers")
2090         );
2091 
2092         // We need to accept wxALIGN_CENTRE_VERTICAL when it is combined with
2093         // wxALIGN_CENTRE_HORIZONTAL because this is known as wxALIGN_CENTRE
2094         // and we accept it historically in wxSizer API.
2095         if ( !(flags & wxALIGN_CENTRE_HORIZONTAL) )
2096         {
2097             wxASSERT_MSG
2098             (
2099                 !(flags & wxALIGN_CENTRE_VERTICAL),
2100                 wxS("Vertical alignment flags are ignored in vertical sizers")
2101             );
2102         }
2103 
2104         // Note that using alignment with wxEXPAND can make sense if wxSHAPED
2105         // is also used, as the item doesn't necessarily fully expand in the
2106         // other direction in this case.
2107         if ( (flags & wxEXPAND) && !(flags & wxSHAPED) )
2108         {
2109             wxASSERT_MSG
2110             (
2111                 !(flags & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL)),
2112                 wxS("Horizontal alignment flags are ignored with wxEXPAND")
2113             );
2114         }
2115     }
2116     else // horizontal
2117     {
2118         wxASSERT_MSG
2119         (
2120             !(flags & wxALIGN_RIGHT),
2121             wxS("Horizontal alignment flags are ignored in horizontal sizers")
2122         );
2123 
2124         if ( !(flags & wxALIGN_CENTRE_VERTICAL) )
2125         {
2126             wxASSERT_MSG
2127             (
2128                 !(flags & wxALIGN_CENTRE_HORIZONTAL),
2129                 wxS("Horizontal alignment flags are ignored in horizontal sizers")
2130             );
2131         }
2132 
2133         if ( (flags & wxEXPAND) && !(flags & wxSHAPED) )
2134         {
2135             wxASSERT_MSG(
2136                 !(flags & (wxALIGN_BOTTOM | wxALIGN_CENTRE_VERTICAL)),
2137                 wxS("Vertical alignment flags are ignored with wxEXPAND")
2138             );
2139         }
2140     }
2141 
2142     return wxSizer::DoInsert(index, item);
2143 }
2144 
AddSpacer(int size)2145 wxSizerItem *wxBoxSizer::AddSpacer(int size)
2146 {
2147     return IsVertical() ? Add(0, size) : Add(size, 0);
2148 }
2149 
2150 namespace
2151 {
2152 
2153 /*
2154     Helper of RepositionChildren(): checks if there is enough remaining space
2155     for the min size of the given item and returns its min size or the entire
2156     remaining space depending on which one is greater.
2157 
2158     This function updates the remaining space parameter to account for the size
2159     effectively allocated to the item.
2160  */
2161 int
GetMinOrRemainingSize(int orient,const wxSizerItem * item,int * remainingSpace_)2162 GetMinOrRemainingSize(int orient, const wxSizerItem *item, int *remainingSpace_)
2163 {
2164     int& remainingSpace = *remainingSpace_;
2165 
2166     wxCoord size;
2167     if ( remainingSpace > 0 )
2168     {
2169         const wxSize sizeMin = item->GetMinSizeWithBorder();
2170         size = orient == wxHORIZONTAL ? sizeMin.x : sizeMin.y;
2171 
2172         if ( size >= remainingSpace )
2173         {
2174             // truncate the item to fit in the remaining space, this is better
2175             // than showing it only partially in general, even if both choices
2176             // are bad -- but there is nothing else we can do
2177             size = remainingSpace;
2178         }
2179 
2180         remainingSpace -= size;
2181     }
2182     else // no remaining space
2183     {
2184         // no space at all left, no need to even query the item for its min
2185         // size as we can't give it to it anyhow
2186         size = 0;
2187     }
2188 
2189     return size;
2190 }
2191 
2192 } // anonymous namespace
2193 
RepositionChildren(const wxSize & minSize)2194 void wxBoxSizer::RepositionChildren(const wxSize& minSize)
2195 {
2196     if ( m_children.empty() )
2197         return;
2198 
2199     const wxCoord totalMinorSize = GetSizeInMinorDir(m_size);
2200     const wxCoord totalMajorSize = GetSizeInMajorDir(m_size);
2201 
2202     // the amount of free space which we should redistribute among the
2203     // stretchable items (i.e. those with non zero proportion)
2204     int delta = totalMajorSize - GetSizeInMajorDir(minSize);
2205 
2206     // declare loop variables used below:
2207     wxSizerItemList::const_iterator i;  // iterator in m_children list
2208     unsigned n = 0;                     // item index in majorSizes array
2209 
2210 
2211     // First, inform item about the available size in minor direction as this
2212     // can change their size in the major direction. Also compute the number of
2213     // visible items and sum of their min sizes in major direction.
2214 
2215     int minMajorSize = 0;
2216     for ( i = m_children.begin(); i != m_children.end(); ++i )
2217     {
2218         wxSizerItem * const item = *i;
2219 
2220         if ( !item->IsShown() )
2221             continue;
2222 
2223         wxSize szMinPrev = item->GetMinSizeWithBorder();
2224         item->InformFirstDirection(m_orient^wxBOTH,totalMinorSize,delta);
2225         wxSize szMin = item->GetMinSizeWithBorder();
2226         int deltaChange = GetSizeInMajorDir(szMin-szMinPrev);
2227         if( deltaChange )
2228         {
2229             // Since we passed available space along to the item, it should not
2230             // take too much, so delta should not become negative.
2231             delta -= deltaChange;
2232         }
2233         minMajorSize += GetSizeInMajorDir(item->GetMinSizeWithBorder());
2234     }
2235 
2236 
2237     // space and sum of proportions for the remaining items, both may change
2238     // below
2239     wxCoord remaining = totalMajorSize;
2240     int totalProportion = m_totalProportion;
2241 
2242     // size of the (visible) items in major direction, -1 means "not fixed yet"
2243     wxVector<int> majorSizes(GetItemCount(), wxDefaultCoord);
2244 
2245 
2246     // Check for the degenerated case when we don't have enough space for even
2247     // the min sizes of all the items: in this case we really can't do much
2248     // more than to allocate the min size to as many of fixed size items as
2249     // possible (on the assumption that variable size items such as text zones
2250     // or list boxes may use scrollbars to show their content even if their
2251     // size is less than min size but that fixed size items such as buttons
2252     // will suffer even more if we don't give them their min size)
2253     if ( totalMajorSize < minMajorSize )
2254     {
2255         // Second degenerated case pass: allocate min size to all fixed size
2256         // items.
2257         for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
2258         {
2259             wxSizerItem * const item = *i;
2260 
2261             if ( !item->IsShown() )
2262                 continue;
2263 
2264             // deal with fixed size items only during this pass
2265             if ( item->GetProportion() )
2266                 continue;
2267 
2268             majorSizes[n] = GetMinOrRemainingSize(m_orient, item, &remaining);
2269         }
2270 
2271 
2272         // Third degenerated case pass: allocate min size to all the remaining,
2273         // i.e. non-fixed size, items.
2274         for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
2275         {
2276             wxSizerItem * const item = *i;
2277 
2278             if ( !item->IsShown() )
2279                 continue;
2280 
2281             // we've already dealt with fixed size items above
2282             if ( !item->GetProportion() )
2283                 continue;
2284 
2285             majorSizes[n] = GetMinOrRemainingSize(m_orient, item, &remaining);
2286         }
2287     }
2288     else // we do have enough space to give at least min sizes to all items
2289     {
2290         // Second and maybe more passes in the non-degenerated case: deal with
2291         // fixed size items and items whose min size is greater than what we
2292         // would allocate to them taking their proportion into account. For
2293         // both of them, we will just use their min size, but for the latter we
2294         // also need to reexamine all the items as the items which fitted
2295         // before we adjusted their size upwards might not fit any more. This
2296         // does make for a quadratic algorithm but it's not obvious how to
2297         // avoid it and hopefully it's not a huge problem in practice as the
2298         // sizers don't have many items usually (and, of course, the algorithm
2299         // still reduces into a linear one if there is enough space for all the
2300         // min sizes).
2301         bool nonFixedSpaceChanged = false;
2302         for ( i = m_children.begin(), n = 0; ; ++i, ++n )
2303         {
2304             if ( nonFixedSpaceChanged )
2305             {
2306                 i = m_children.begin();
2307                 n = 0;
2308                 nonFixedSpaceChanged = false;
2309             }
2310 
2311             // check for the end of the loop only after the check above as
2312             // otherwise we wouldn't do another pass if the last child resulted
2313             // in non fixed space reduction
2314             if ( i == m_children.end() )
2315                 break;
2316 
2317             wxSizerItem * const item = *i;
2318 
2319             if ( !item->IsShown() )
2320                 continue;
2321 
2322             // don't check the item which we had already dealt with during a
2323             // previous pass (this is more than an optimization, the code
2324             // wouldn't work correctly if we kept adjusting for the same item
2325             // over and over again)
2326             if ( majorSizes[n] != wxDefaultCoord )
2327                 continue;
2328 
2329             wxCoord minMajor = GetSizeInMajorDir(item->GetMinSizeWithBorder());
2330 
2331             // it doesn't make sense for min size to be negative but right now
2332             // it's possible to create e.g. a spacer with (-1, 10) as size and
2333             // people do it in their code apparently (see #11842) so ensure
2334             // that we don't use this -1 as real min size as it conflicts with
2335             // the meaning we use for it here and negative min sizes just don't
2336             // make sense anyhow (which is why it might be a better idea to
2337             // deal with them at wxSizerItem level in the future but for now
2338             // this is the minimal fix for the bug)
2339             if ( minMajor < 0 )
2340                 minMajor = 0;
2341 
2342             const int propItem = item->GetProportion();
2343             if ( propItem )
2344             {
2345                 // is the desired size of this item big enough?
2346                 if ( (remaining*propItem)/totalProportion >= minMajor )
2347                 {
2348                     // yes, it is, we'll determine the real size of this
2349                     // item later, for now just leave it as wxDefaultCoord
2350                     continue;
2351                 }
2352 
2353                 // the proportion of this item won't count, it has
2354                 // effectively become fixed
2355                 totalProportion -= propItem;
2356             }
2357 
2358             // we can already allocate space for this item
2359             majorSizes[n] = minMajor;
2360 
2361             // change the amount of the space remaining to the other items,
2362             // as this can result in not being able to satisfy their
2363             // proportions any more we will need to redo another loop
2364             // iteration
2365             remaining -= minMajor;
2366 
2367             nonFixedSpaceChanged = true;
2368         }
2369 
2370         // Similar to the previous loop, but dealing with items whose max size
2371         // is less than what we would allocate to them taking their proportion
2372         // into account.
2373         nonFixedSpaceChanged = false;
2374         for ( i = m_children.begin(), n = 0; ; ++i, ++n )
2375         {
2376             if ( nonFixedSpaceChanged )
2377             {
2378                 i = m_children.begin();
2379                 n = 0;
2380                 nonFixedSpaceChanged = false;
2381             }
2382 
2383             // check for the end of the loop only after the check above as
2384             // otherwise we wouldn't do another pass if the last child resulted
2385             // in non fixed space reduction
2386             if ( i == m_children.end() )
2387                 break;
2388 
2389             wxSizerItem * const item = *i;
2390 
2391             if ( !item->IsShown() )
2392                 continue;
2393 
2394             // don't check the item which we had already dealt with during a
2395             // previous pass (this is more than an optimization, the code
2396             // wouldn't work correctly if we kept adjusting for the same item
2397             // over and over again)
2398             if ( majorSizes[n] != wxDefaultCoord )
2399                 continue;
2400 
2401             wxCoord maxMajor = GetSizeInMajorDir(item->GetMaxSizeWithBorder());
2402 
2403             // must be nonzero, fixed-size items were dealt with in the previous loop
2404             const int propItem = item->GetProportion();
2405 
2406             // is the desired size of this item small enough?
2407             if ( maxMajor < 0 ||
2408                     (remaining*propItem)/totalProportion <= maxMajor )
2409             {
2410                 // yes, it is, we'll determine the real size of this
2411                 // item later, for now just leave it as wxDefaultCoord
2412                 continue;
2413             }
2414 
2415             // the proportion of this item won't count, it has
2416             // effectively become fixed
2417             totalProportion -= propItem;
2418 
2419             // we can already allocate space for this item
2420             majorSizes[n] = maxMajor;
2421 
2422             // change the amount of the space remaining to the other items,
2423             // as this can result in not being able to satisfy their
2424             // proportions any more we will need to redo another loop
2425             // iteration
2426             remaining -= maxMajor;
2427 
2428             nonFixedSpaceChanged = true;
2429         }
2430 
2431         // Last by one pass: distribute the remaining space among the non-fixed
2432         // items whose size weren't fixed yet according to their proportions.
2433         for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
2434         {
2435             wxSizerItem * const item = *i;
2436 
2437             if ( !item->IsShown() )
2438                 continue;
2439 
2440             if ( majorSizes[n] == wxDefaultCoord )
2441             {
2442                 const int propItem = item->GetProportion();
2443                 majorSizes[n] = (remaining*propItem)/totalProportion;
2444 
2445                 remaining -= majorSizes[n];
2446                 totalProportion -= propItem;
2447             }
2448         }
2449     }
2450 
2451 
2452     // the position at which we put the next child
2453     wxPoint pt(m_position);
2454 
2455 
2456     // Final pass: finally do position the items correctly using their sizes as
2457     // determined above.
2458     for ( i = m_children.begin(), n = 0; i != m_children.end(); ++i, ++n )
2459     {
2460         wxSizerItem * const item = *i;
2461 
2462         if ( !item->IsShown() )
2463             continue;
2464 
2465         const int majorSize = majorSizes[n];
2466 
2467         const wxSize sizeThis(item->GetMinSizeWithBorder());
2468 
2469         // apply the alignment in the minor direction
2470         wxPoint posChild(pt);
2471 
2472         wxCoord minorSize = GetSizeInMinorDir(sizeThis);
2473         const int flag = item->GetFlag();
2474         if ( (flag & (wxEXPAND | wxSHAPED)) || (minorSize > totalMinorSize) )
2475         {
2476             // occupy all the available space if wxEXPAND was given and also if
2477             // the item is too big to fit -- in this case we truncate it below
2478             // its minimal size which is bad but better than not showing parts
2479             // of the window at all
2480             minorSize = totalMinorSize;
2481 
2482             // do not allow the size in the minor direction to grow beyond the max
2483             // size of the item in the minor direction
2484             const wxCoord maxMinorSize = GetSizeInMinorDir(item->GetMaxSizeWithBorder());
2485             if ( maxMinorSize >= 0 && minorSize > maxMinorSize )
2486                 minorSize = maxMinorSize;
2487         }
2488 
2489         if ( flag & (IsVertical() ? wxALIGN_RIGHT : wxALIGN_BOTTOM) )
2490         {
2491             PosInMinorDir(posChild) += totalMinorSize - minorSize;
2492         }
2493         // NB: wxCENTRE is used here only for backwards compatibility,
2494         //     wxALIGN_CENTRE should be used in new code
2495         else if ( flag & (wxCENTER | (IsVertical() ? wxALIGN_CENTRE_HORIZONTAL
2496                                                    : wxALIGN_CENTRE_VERTICAL)) )
2497         {
2498             PosInMinorDir(posChild) += (totalMinorSize - minorSize) / 2;
2499         }
2500 
2501 
2502         // apply RTL adjustment for horizontal sizers:
2503         if ( !IsVertical() && m_containingWindow )
2504         {
2505             posChild.x = m_containingWindow->AdjustForLayoutDirection
2506                                              (
2507                                                 posChild.x,
2508                                                 majorSize,
2509                                                 m_size.x
2510                                              );
2511         }
2512 
2513         // finally set size of this child and advance to the next one
2514         item->SetDimension(posChild, SizeFromMajorMinor(majorSize, minorSize));
2515 
2516         PosInMajorDir(pt) += majorSize;
2517     }
2518 }
2519 
CalcMin()2520 wxSize wxBoxSizer::CalcMin()
2521 {
2522     m_totalProportion = 0;
2523     wxSize minSize;
2524 
2525     // The minimal size for the sizer should be big enough to allocate its
2526     // element at least its minimal size but also, and this is the non trivial
2527     // part, to respect the children proportion. To satisfy the latter
2528     // condition we must find the greatest min-size-to-proportion ratio for all
2529     // elements with non-zero proportion.
2530     float maxMinSizeToProp = 0;
2531     for ( wxSizerItemList::const_iterator i = m_children.begin();
2532           i != m_children.end();
2533           ++i )
2534     {
2535         wxSizerItem * const item = *i;
2536 
2537         if ( !item->IsShown() )
2538             continue;
2539 
2540         const wxSize sizeMinThis = item->CalcMin();
2541         if ( const int propThis = item->GetProportion() )
2542         {
2543             float minSizeToProp = GetSizeInMajorDir(sizeMinThis);
2544             minSizeToProp /= propThis;
2545 
2546             if ( minSizeToProp > maxMinSizeToProp )
2547                 maxMinSizeToProp = minSizeToProp;
2548 
2549             m_totalProportion += item->GetProportion();
2550         }
2551         else // fixed size item
2552         {
2553             // Just account for its size directly
2554             SizeInMajorDir(minSize) += GetSizeInMajorDir(sizeMinThis);
2555         }
2556 
2557         // In the transversal direction we just need to find the maximum.
2558         if ( GetSizeInMinorDir(sizeMinThis) > GetSizeInMinorDir(minSize) )
2559             SizeInMinorDir(minSize) = GetSizeInMinorDir(sizeMinThis);
2560     }
2561 
2562     // Using the max ratio ensures that the min size is big enough for all
2563     // items to have their min size and satisfy the proportions among them.
2564     SizeInMajorDir(minSize) += (int)(maxMinSizeToProp*m_totalProportion);
2565 
2566     return minSize;
2567 }
2568 
2569 bool
InformFirstDirection(int direction,int size,int availableOtherDir)2570 wxBoxSizer::InformFirstDirection(int direction, int size, int availableOtherDir)
2571 {
2572     // In principle, we could propagate the information about the size in the
2573     // sizer major direction too, but this would require refactoring CalcMin()
2574     // to determine the actual sizes all our items would have with the given
2575     // size and we don't do this yet, so for now handle only the simpler case
2576     // of informing all our items about their size in the orthogonal direction.
2577     if ( direction == GetOrientation() )
2578         return false;
2579 
2580     bool didUse = false;
2581 
2582     for ( wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
2583           node;
2584           node = node->GetNext() )
2585     {
2586         didUse |= node->GetData()->InformFirstDirection(direction,
2587                                                         size,
2588                                                         availableOtherDir);
2589     }
2590 
2591     return didUse;
2592 }
2593 
2594 //---------------------------------------------------------------------------
2595 // wxStaticBoxSizer
2596 //---------------------------------------------------------------------------
2597 
2598 #if wxUSE_STATBOX
2599 
wxStaticBoxSizer(wxStaticBox * box,int orient)2600 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient )
2601     : wxBoxSizer( orient ),
2602       m_staticBox( box )
2603 {
2604     wxASSERT_MSG( box, wxT("wxStaticBoxSizer needs a static box") );
2605 
2606     // do this so that our Detach() is called if the static box is destroyed
2607     // before we are
2608     m_staticBox->SetContainingSizer(this);
2609 }
2610 
wxStaticBoxSizer(int orient,wxWindow * win,const wxString & s)2611 wxStaticBoxSizer::wxStaticBoxSizer(int orient, wxWindow *win, const wxString& s)
2612                 : wxBoxSizer(orient),
2613                   m_staticBox(new wxStaticBox(win, wxID_ANY, s))
2614 {
2615     // same as above
2616     m_staticBox->SetContainingSizer(this);
2617 }
2618 
~wxStaticBoxSizer()2619 wxStaticBoxSizer::~wxStaticBoxSizer()
2620 {
2621     // As an exception to the general rule that sizers own other sizers that
2622     // they contain but not the windows managed by them, this sizer does own
2623     // the static box associated with it (which is not very logical but
2624     // convenient in practice and, most importantly, can't be changed any more
2625     // because of compatibility). However we definitely should not destroy the
2626     // children of this static box when we're being destroyed, as this would be
2627     // unexpected and break the existing code which worked with the windows
2628     // created as siblings of the static box instead of its children in the
2629     // previous wxWidgets versions, so ensure they are left alive.
2630 
2631     if ( m_staticBox )
2632         m_staticBox->WXDestroyWithoutChildren();
2633 }
2634 
RepositionChildren(const wxSize & minSize)2635 void wxStaticBoxSizer::RepositionChildren(const wxSize& minSize)
2636 {
2637     int top_border, other_border;
2638     m_staticBox->GetBordersForSizer(&top_border, &other_border);
2639 
2640     m_staticBox->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
2641 
2642     wxSize old_size( m_size );
2643     m_size.x -= 2*other_border;
2644     m_size.y -= top_border + other_border;
2645 
2646     wxPoint old_pos( m_position );
2647     if (m_staticBox->GetChildren().GetCount() > 0)
2648     {
2649 #if defined( __WXGTK20__ )
2650         // if the wxStaticBox has created a wxPizza to contain its children
2651         // (see wxStaticBox::AddChild) then we need to place the items it contains
2652         // in the base class version called below using coordinates relative
2653         // to the top-left corner of the staticbox:
2654         m_position.x = m_position.y = 0;
2655 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
2656         // the distance from the 'inner' content view to the embedded controls
2657         // this is independent of the title, therefore top_border is not relevant
2658         m_position.x = m_position.y = 10;
2659 #else
2660         // if the wxStaticBox has children, then these windows must be placed
2661         // by the base class version called below using coordinates relative
2662         // to the top-left corner of the staticbox (but unlike wxGTK, we need
2663         // to keep in count the static borders here!):
2664         m_position.x = other_border;
2665         m_position.y = top_border;
2666 #endif
2667     }
2668     else
2669     {
2670         // the windows contained in the staticbox have been created as siblings of the
2671         // staticbox (this is the "old" way of staticbox contents creation); in this
2672         // case we need to position them with coordinates relative to our common parent
2673         m_position.x += other_border;
2674         m_position.y += top_border;
2675     }
2676 
2677     wxBoxSizer::RepositionChildren(minSize);
2678 
2679     m_position = old_pos;
2680     m_size = old_size;
2681 }
2682 
CalcMin()2683 wxSize wxStaticBoxSizer::CalcMin()
2684 {
2685     int top_border, other_border;
2686     m_staticBox->GetBordersForSizer(&top_border, &other_border);
2687 
2688     wxSize ret( wxBoxSizer::CalcMin() );
2689     ret.x += 2*other_border;
2690 
2691     // ensure that we're wide enough to show the static box label (there is no
2692     // need to check for the static box best size in vertical direction though)
2693     const int boxWidth = m_staticBox->GetBestSize().x;
2694     if ( ret.x < boxWidth )
2695         ret.x = boxWidth;
2696 
2697     ret.y += other_border + top_border;
2698 
2699     return ret;
2700 }
2701 
ShowItems(bool show)2702 void wxStaticBoxSizer::ShowItems( bool show )
2703 {
2704     m_staticBox->Show( show );
2705     wxBoxSizer::ShowItems( show );
2706 }
2707 
AreAnyItemsShown() const2708 bool wxStaticBoxSizer::AreAnyItemsShown() const
2709 {
2710     // We don't need to check the status of our child items: if the box is
2711     // shown, this sizer should be considered shown even if all its elements
2712     // are hidden (or, more prosaically, there are no elements at all). And,
2713     // conversely, if the box is hidden then all our items, which are its
2714     // children, are hidden too.
2715     return m_staticBox->IsShown();
2716 }
2717 
Detach(wxWindow * window)2718 bool wxStaticBoxSizer::Detach( wxWindow *window )
2719 {
2720     // avoid deleting m_staticBox in our dtor if it's being detached from the
2721     // sizer (which can happen because it's being already destroyed for
2722     // example)
2723     if ( window == m_staticBox )
2724     {
2725         m_staticBox = NULL;
2726         return true;
2727     }
2728 
2729     return wxSizer::Detach( window );
2730 }
2731 
2732 #endif // wxUSE_STATBOX
2733 
2734 //---------------------------------------------------------------------------
2735 // wxStdDialogButtonSizer
2736 //---------------------------------------------------------------------------
2737 
2738 #if wxUSE_BUTTON
2739 
wxStdDialogButtonSizer()2740 wxStdDialogButtonSizer::wxStdDialogButtonSizer()
2741     : wxBoxSizer(wxHORIZONTAL)
2742 {
2743     bool is_pda = (wxSystemSettings::GetScreenType() <= wxSYS_SCREEN_PDA);
2744     // If we have a PDA screen, put yes/no button over
2745     // all other buttons, otherwise on the left side.
2746     if (is_pda)
2747         m_orient = wxVERTICAL;
2748 
2749     m_buttonAffirmative = NULL;
2750     m_buttonApply = NULL;
2751     m_buttonNegative = NULL;
2752     m_buttonCancel = NULL;
2753     m_buttonHelp = NULL;
2754 }
2755 
AddButton(wxButton * mybutton)2756 void wxStdDialogButtonSizer::AddButton(wxButton *mybutton)
2757 {
2758     switch (mybutton->GetId())
2759     {
2760         case wxID_OK:
2761         case wxID_YES:
2762         case wxID_SAVE:
2763             m_buttonAffirmative = mybutton;
2764             break;
2765         case wxID_APPLY:
2766             m_buttonApply = mybutton;
2767             break;
2768         case wxID_NO:
2769             m_buttonNegative = mybutton;
2770             break;
2771         case wxID_CANCEL:
2772         case wxID_CLOSE:
2773             m_buttonCancel = mybutton;
2774             break;
2775         case wxID_HELP:
2776         case wxID_CONTEXT_HELP:
2777             m_buttonHelp = mybutton;
2778             break;
2779         default:
2780             break;
2781     }
2782 }
2783 
SetAffirmativeButton(wxButton * button)2784 void wxStdDialogButtonSizer::SetAffirmativeButton( wxButton *button )
2785 {
2786     m_buttonAffirmative = button;
2787 }
2788 
SetNegativeButton(wxButton * button)2789 void wxStdDialogButtonSizer::SetNegativeButton( wxButton *button )
2790 {
2791     m_buttonNegative = button;
2792 }
2793 
SetCancelButton(wxButton * button)2794 void wxStdDialogButtonSizer::SetCancelButton( wxButton *button )
2795 {
2796     m_buttonCancel = button;
2797 }
2798 
Realize()2799 void wxStdDialogButtonSizer::Realize()
2800 {
2801     // Helper used to move the most recently added button after the previously
2802     // added one in TAB order.
2803     class TabOrderUpdater
2804     {
2805     public:
2806         TabOrderUpdater()
2807             : m_lastAdded(NULL)
2808         {
2809         }
2810 
2811         void Add(wxButton* btn)
2812         {
2813             if ( m_lastAdded )
2814             {
2815                 // Button should follow previous one in the keyboard navigation
2816                 // order.
2817                 btn->MoveAfterInTabOrder(m_lastAdded);
2818             }
2819 
2820             m_lastAdded = btn;
2821         }
2822 
2823     private:
2824         wxButton* m_lastAdded;
2825     };
2826 
2827     TabOrderUpdater tabOrder;
2828 
2829 #ifdef __WXMAC__
2830         Add(0, 0, 0, wxLEFT, 6);
2831         if (m_buttonHelp)
2832         {
2833             Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2834             tabOrder.Add(m_buttonHelp);
2835         }
2836 
2837         if (m_buttonNegative){
2838             // HIG POLICE BULLETIN - destructive buttons need extra padding
2839             // 24 pixels on either side
2840             Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 12);
2841             tabOrder.Add(m_buttonNegative);
2842         }
2843 
2844         // extra whitespace between help/negative and cancel/ok buttons
2845         Add(0, 0, 1, wxEXPAND, 0);
2846 
2847         if (m_buttonCancel){
2848             Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2849             // Cancel or help should be default
2850             // m_buttonCancel->SetDefaultButton();
2851 
2852             tabOrder.Add(m_buttonCancel);
2853         }
2854 
2855         // Ugh, Mac doesn't really have apply dialogs, so I'll just
2856         // figure the best place is between Cancel and OK
2857         if (m_buttonApply)
2858         {
2859             Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, 6);
2860             tabOrder.Add(m_buttonApply);
2861         }
2862 
2863         if (m_buttonAffirmative){
2864             Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT, 6);
2865 
2866             if (m_buttonAffirmative->GetId() == wxID_SAVE){
2867                 // these buttons have set labels under Mac so we should use them
2868                 m_buttonAffirmative->SetLabel(_("Save"));
2869                 if (m_buttonNegative)
2870                     m_buttonNegative->SetLabel(_("Don't Save"));
2871             }
2872             tabOrder.Add(m_buttonAffirmative);
2873         }
2874 
2875         // Extra space around and at the right
2876         Add(12, 40);
2877 #elif defined(__WXGTK20__)
2878         // http://library.gnome.org/devel/hig-book/stable/windows-alert.html.en
2879         // says that the correct button order is
2880         //
2881         //      [Help]                  [Alternative] [Cancel] [Affirmative]
2882 
2883         // Flags ensuring that margins between the buttons are 6 pixels.
2884         const wxSizerFlags
2885             flagsBtn = wxSizerFlags().Centre().Border(wxLEFT | wxRIGHT, 3);
2886 
2887         // Margin around the entire sizer button should be 12.
2888         AddSpacer(9);
2889 
2890         if (m_buttonHelp)
2891         {
2892             Add(m_buttonHelp, flagsBtn);
2893             tabOrder.Add(m_buttonHelp);
2894         }
2895 
2896         // Align the rest of the buttons to the right.
2897         AddStretchSpacer();
2898 
2899         if (m_buttonNegative)
2900         {
2901             Add(m_buttonNegative, flagsBtn);
2902             tabOrder.Add(m_buttonNegative);
2903         }
2904 
2905         if (m_buttonApply)
2906         {
2907             Add(m_buttonApply, flagsBtn);
2908             tabOrder.Add(m_buttonApply);
2909         }
2910 
2911         if (m_buttonCancel)
2912         {
2913             Add(m_buttonCancel, flagsBtn);
2914             tabOrder.Add(m_buttonCancel);
2915         }
2916 
2917         if (m_buttonAffirmative)
2918         {
2919             Add(m_buttonAffirmative, flagsBtn);
2920             tabOrder.Add(m_buttonAffirmative);
2921         }
2922 
2923         // Ensure that the right margin is 12 as well.
2924         AddSpacer(9);
2925 #elif defined(__WXMSW__)
2926         // Windows
2927 
2928         // right-justify buttons
2929         Add(0, 0, 1, wxEXPAND, 0);
2930 
2931         if (m_buttonAffirmative){
2932             Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(2, 0)).x);
2933             tabOrder.Add(m_buttonAffirmative);
2934         }
2935 
2936         if (m_buttonNegative){
2937             Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(2, 0)).x);
2938             tabOrder.Add(m_buttonNegative);
2939         }
2940 
2941         if (m_buttonCancel){
2942             Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(2, 0)).x);
2943             tabOrder.Add(m_buttonCancel);
2944         }
2945 
2946         if (m_buttonApply)
2947         {
2948             Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(2, 0)).x);
2949             tabOrder.Add(m_buttonApply);
2950         }
2951 
2952         if (m_buttonHelp)
2953         {
2954             Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(2, 0)).x);
2955             tabOrder.Add(m_buttonHelp);
2956         }
2957 #else
2958         // GTK+1 and any other platform
2959 
2960         // Add(0, 0, 0, wxLEFT, 5); // Not sure what this was for but it unbalances the dialog
2961         if (m_buttonHelp)
2962         {
2963             Add((wxWindow*)m_buttonHelp, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonHelp->ConvertDialogToPixels(wxSize(4, 0)).x);
2964             tabOrder.Add(m_buttonHelp);
2965         }
2966 
2967         // extra whitespace between help and cancel/ok buttons
2968         Add(0, 0, 1, wxEXPAND, 0);
2969 
2970         if (m_buttonApply)
2971         {
2972             Add((wxWindow*)m_buttonApply, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonApply->ConvertDialogToPixels(wxSize(4, 0)).x);
2973             tabOrder.Add(m_buttonApply);
2974         }
2975 
2976         if (m_buttonAffirmative){
2977             Add((wxWindow*)m_buttonAffirmative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonAffirmative->ConvertDialogToPixels(wxSize(4, 0)).x);
2978             tabOrder.Add(m_buttonAffirmative);
2979         }
2980 
2981         if (m_buttonNegative){
2982             Add((wxWindow*)m_buttonNegative, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonNegative->ConvertDialogToPixels(wxSize(4, 0)).x);
2983             tabOrder.Add(m_buttonNegative);
2984         }
2985 
2986         if (m_buttonCancel){
2987             Add((wxWindow*)m_buttonCancel, 0, wxALIGN_CENTRE | wxLEFT | wxRIGHT, m_buttonCancel->ConvertDialogToPixels(wxSize(4, 0)).x);
2988             // Cancel or help should be default
2989             // m_buttonCancel->SetDefaultButton();
2990             tabOrder.Add(m_buttonCancel);
2991         }
2992 
2993 #endif
2994 }
2995 
2996 #endif // wxUSE_BUTTON
2997