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