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