1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/gbsizer.cpp
3 // Purpose: wxGridBagSizer: A sizer that can lay out items in a grid,
4 // with items at specified cells, and with the option of row
5 // and/or column spanning
8 // Created: 03-Nov-2003
9 // Copyright: (c) Robin Dunn
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
13 // For compilers that support precompilation, includes "wx.h".
14 #include "wx/wxprec.h"
20 #include "wx/gbsizer.h"
22 //---------------------------------------------------------------------------
24 IMPLEMENT_DYNAMIC_CLASS(wxGBSizerItem
, wxSizerItem
)
25 IMPLEMENT_CLASS(wxGridBagSizer
, wxFlexGridSizer
)
27 const wxGBSpan wxDefaultSpan
;
29 //---------------------------------------------------------------------------
31 //---------------------------------------------------------------------------
33 wxGBSizerItem::wxGBSizerItem( int width
,
35 const wxGBPosition
& pos
,
40 : wxSizerItem(width
, height
, 0, flag
, border
, userData
),
48 wxGBSizerItem::wxGBSizerItem( wxWindow
*window
,
49 const wxGBPosition
& pos
,
54 : wxSizerItem(window
, 0, flag
, border
, userData
),
62 wxGBSizerItem::wxGBSizerItem( wxSizer
*sizer
,
63 const wxGBPosition
& pos
,
68 : wxSizerItem(sizer
, 0, flag
, border
, userData
),
75 wxGBSizerItem::wxGBSizerItem()
82 //---------------------------------------------------------------------------
85 void wxGBSizerItem::GetPos(int& row
, int& col
) const
91 void wxGBSizerItem::GetSpan(int& rowspan
, int& colspan
) const
93 rowspan
= m_span
.GetRowspan();
94 colspan
= m_span
.GetColspan();
98 bool wxGBSizerItem::SetPos( const wxGBPosition
& pos
)
102 wxCHECK_MSG( !m_gbsizer
->CheckForIntersection(pos
, m_span
, this), false,
103 wxT("An item is already at that position") );
109 bool wxGBSizerItem::SetSpan( const wxGBSpan
& span
)
113 wxCHECK_MSG( !m_gbsizer
->CheckForIntersection(m_pos
, span
, this), false,
114 wxT("An item is already at that position") );
121 inline bool InRange(int val
, int min
, int max
)
123 return (val
>= min
&& val
<= max
);
126 bool wxGBSizerItem::Intersects(const wxGBSizerItem
& other
)
128 return Intersects(other
.GetPos(), other
.GetSpan());
131 bool wxGBSizerItem::Intersects(const wxGBPosition
& pos
, const wxGBSpan
& span
)
134 int row
, col
, endrow
, endcol
;
135 int otherrow
, othercol
, otherendrow
, otherendcol
;
138 GetEndPos(endrow
, endcol
);
140 otherrow
= pos
.GetRow();
141 othercol
= pos
.GetCol();
142 otherendrow
= otherrow
+ span
.GetRowspan() - 1;
143 otherendcol
= othercol
+ span
.GetColspan() - 1;
145 // is the other item's start or end in the range of this one?
146 if (( InRange(otherrow
, row
, endrow
) && InRange(othercol
, col
, endcol
) ) ||
147 ( InRange(otherendrow
, row
, endrow
) && InRange(otherendcol
, col
, endcol
) ))
150 // is this item's start or end in the range of the other one?
151 if (( InRange(row
, otherrow
, otherendrow
) && InRange(col
, othercol
, otherendcol
) ) ||
152 ( InRange(endrow
, otherrow
, otherendrow
) && InRange(endcol
, othercol
, otherendcol
) ))
159 void wxGBSizerItem::GetEndPos(int& row
, int& col
)
161 row
= m_pos
.GetRow() + m_span
.GetRowspan() - 1;
162 col
= m_pos
.GetCol() + m_span
.GetColspan() - 1;
166 //---------------------------------------------------------------------------
168 //---------------------------------------------------------------------------
170 wxGridBagSizer::wxGridBagSizer(int vgap
, int hgap
)
171 : wxFlexGridSizer(1, vgap
, hgap
),
172 m_emptyCellSize(10,20)
178 wxSizerItem
* wxGridBagSizer::Add( wxWindow
*window
,
179 const wxGBPosition
& pos
, const wxGBSpan
& span
,
180 int flag
, int border
, wxObject
* userData
)
182 wxGBSizerItem
* item
= new wxGBSizerItem(window
, pos
, span
, flag
, border
, userData
);
192 wxSizerItem
* wxGridBagSizer::Add( wxSizer
*sizer
,
193 const wxGBPosition
& pos
, const wxGBSpan
& span
,
194 int flag
, int border
, wxObject
* userData
)
196 wxGBSizerItem
* item
= new wxGBSizerItem(sizer
, pos
, span
, flag
, border
, userData
);
206 wxSizerItem
* wxGridBagSizer::Add( int width
, int height
,
207 const wxGBPosition
& pos
, const wxGBSpan
& span
,
208 int flag
, int border
, wxObject
* userData
)
210 wxGBSizerItem
* item
= new wxGBSizerItem(width
, height
, pos
, span
, flag
, border
, userData
);
220 wxSizerItem
* wxGridBagSizer::Add( wxGBSizerItem
*item
)
222 wxCHECK_MSG( !CheckForIntersection(item
), NULL
,
223 wxT("An item is already at that position") );
224 m_children
.Append(item
);
225 item
->SetGBSizer(this);
226 if ( item
->GetWindow() )
227 item
->GetWindow()->SetContainingSizer( this );
229 // extend the number of rows/columns of the underlying wxFlexGridSizer if
232 item
->GetEndPos(row
, col
);
236 if ( row
> GetRows() )
238 if ( col
> GetCols() )
246 //---------------------------------------------------------------------------
248 wxSize
wxGridBagSizer::GetCellSize(int row
, int col
) const
250 wxCHECK_MSG( (row
< m_rows
) && (col
< m_cols
),
252 wxT("Invalid cell."));
253 return wxSize( m_colWidths
[col
], m_rowHeights
[row
] );
257 wxGBPosition
wxGridBagSizer::GetItemPosition(wxWindow
*window
)
259 wxGBPosition
badpos(-1,-1);
260 wxGBSizerItem
* item
= FindItem(window
);
261 wxCHECK_MSG(item
, badpos
, wxT("Failed to find item."));
262 return item
->GetPos();
266 wxGBPosition
wxGridBagSizer::GetItemPosition(wxSizer
*sizer
)
268 wxGBPosition
badpos(-1,-1);
269 wxGBSizerItem
* item
= FindItem(sizer
);
270 wxCHECK_MSG(item
, badpos
, wxT("Failed to find item."));
271 return item
->GetPos();
275 wxGBPosition
wxGridBagSizer::GetItemPosition(size_t index
)
277 wxGBPosition
badpos(-1,-1);
278 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
279 wxCHECK_MSG( node
, badpos
, wxT("Failed to find item.") );
280 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
281 return item
->GetPos();
286 bool wxGridBagSizer::SetItemPosition(wxWindow
*window
, const wxGBPosition
& pos
)
288 wxGBSizerItem
* item
= FindItem(window
);
289 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
290 return item
->SetPos(pos
);
294 bool wxGridBagSizer::SetItemPosition(wxSizer
*sizer
, const wxGBPosition
& pos
)
296 wxGBSizerItem
* item
= FindItem(sizer
);
297 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
298 return item
->SetPos(pos
);
302 bool wxGridBagSizer::SetItemPosition(size_t index
, const wxGBPosition
& pos
)
304 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
305 wxCHECK_MSG( node
, false, wxT("Failed to find item.") );
306 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
307 return item
->SetPos(pos
);
312 wxGBSpan
wxGridBagSizer::GetItemSpan(wxWindow
*window
)
314 wxGBSizerItem
* item
= FindItem(window
);
315 wxCHECK_MSG( item
, wxGBSpan::Invalid(), wxT("Failed to find item.") );
316 return item
->GetSpan();
320 wxGBSpan
wxGridBagSizer::GetItemSpan(wxSizer
*sizer
)
322 wxGBSizerItem
* item
= FindItem(sizer
);
323 wxCHECK_MSG( item
, wxGBSpan::Invalid(), wxT("Failed to find item.") );
324 return item
->GetSpan();
328 wxGBSpan
wxGridBagSizer::GetItemSpan(size_t index
)
330 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
331 wxCHECK_MSG( node
, wxGBSpan::Invalid(), wxT("Failed to find item.") );
332 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
333 return item
->GetSpan();
338 bool wxGridBagSizer::SetItemSpan(wxWindow
*window
, const wxGBSpan
& span
)
340 wxGBSizerItem
* item
= FindItem(window
);
341 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
342 return item
->SetSpan(span
);
346 bool wxGridBagSizer::SetItemSpan(wxSizer
*sizer
, const wxGBSpan
& span
)
348 wxGBSizerItem
* item
= FindItem(sizer
);
349 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
350 return item
->SetSpan(span
);
354 bool wxGridBagSizer::SetItemSpan(size_t index
, const wxGBSpan
& span
)
356 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
357 wxCHECK_MSG( node
, false, wxT("Failed to find item.") );
358 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
359 return item
->SetSpan(span
);
365 wxGBSizerItem
* wxGridBagSizer::FindItem(wxWindow
* window
)
367 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
370 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
371 if ( item
->GetWindow() == window
)
373 node
= node
->GetNext();
379 wxGBSizerItem
* wxGridBagSizer::FindItem(wxSizer
* sizer
)
381 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
384 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
385 if ( item
->GetSizer() == sizer
)
387 node
= node
->GetNext();
395 wxGBSizerItem
* wxGridBagSizer::FindItemAtPosition(const wxGBPosition
& pos
)
397 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
400 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
401 if ( item
->Intersects(pos
, wxDefaultSpan
) )
403 node
= node
->GetNext();
411 wxGBSizerItem
* wxGridBagSizer::FindItemAtPoint(const wxPoint
& pt
)
413 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
416 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
417 wxRect
rect(item
->GetPosition(), item
->GetSize());
418 rect
.Inflate(m_hgap
, m_vgap
);
419 if ( rect
.Contains(pt
) )
421 node
= node
->GetNext();
429 wxGBSizerItem
* wxGridBagSizer::FindItemWithData(const wxObject
* userData
)
431 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
434 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
435 if ( item
->GetUserData() == userData
)
437 node
= node
->GetNext();
445 //---------------------------------------------------------------------------
447 // Figure out what all the min row heights and col widths are, and calculate
448 // min size from that.
449 wxSize
wxGridBagSizer::CalcMin()
453 if (m_children
.GetCount() == 0)
454 return m_emptyCellSize
;
456 m_rowHeights
.Empty();
459 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
462 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
463 if ( item
->IsShown() )
465 int row
, col
, endrow
, endcol
;
467 item
->GetPos(row
, col
);
468 item
->GetEndPos(endrow
, endcol
);
470 // fill heights and widths up to this item if needed
471 while ( (int)m_rowHeights
.GetCount() <= endrow
)
472 m_rowHeights
.Add(m_emptyCellSize
.GetHeight());
473 while ( (int)m_colWidths
.GetCount() <= endcol
)
474 m_colWidths
.Add(m_emptyCellSize
.GetWidth());
476 // See if this item increases the size of its row(s) or col(s)
477 wxSize
size(item
->CalcMin());
478 for (idx
=row
; idx
<= endrow
; idx
++)
479 m_rowHeights
[idx
] = wxMax(m_rowHeights
[idx
], size
.GetHeight() / (endrow
-row
+1));
480 for (idx
=col
; idx
<= endcol
; idx
++)
481 m_colWidths
[idx
] = wxMax(m_colWidths
[idx
], size
.GetWidth() / (endcol
-col
+1));
483 node
= node
->GetNext();
487 AdjustForFlexDirection();
489 // Now traverse the heights and widths arrays calcing the totals, including gaps
491 m_cols
= m_colWidths
.GetCount();
492 for (idx
=0; idx
< m_cols
; idx
++)
493 width
+= m_colWidths
[idx
] + ( idx
== m_cols
-1 ? 0 : m_hgap
);
496 m_rows
= m_rowHeights
.GetCount();
497 for (idx
=0; idx
< m_rows
; idx
++)
498 height
+= m_rowHeights
[idx
] + ( idx
== m_rows
-1 ? 0 : m_vgap
);
500 m_calculatedMinSize
= wxSize(width
, height
);
501 return m_calculatedMinSize
;
506 void wxGridBagSizer::RecalcSizes()
508 // We can't lay out our elements if we don't have at least a single row and
509 // a single column. Notice that this may happen even if we have some
510 // children but all of them are hidden, so checking for m_children being
511 // non-empty is not enough, see #15475.
512 if ( m_rowHeights
.empty() || m_colWidths
.empty() )
515 wxPoint
pt( GetPosition() );
516 wxSize
sz( GetSize() );
518 m_rows
= m_rowHeights
.GetCount();
519 m_cols
= m_colWidths
.GetCount();
520 int idx
, width
, height
;
522 AdjustForGrowables(sz
);
524 // Find the start positions on the window of the rows and columns
526 rowpos
.Add(0, m_rows
);
528 for (idx
=0; idx
< m_rows
; idx
++)
530 height
= m_rowHeights
[idx
] + m_vgap
;
536 colpos
.Add(0, m_cols
);
538 for (idx
=0; idx
< m_cols
; idx
++)
540 width
= m_colWidths
[idx
] + m_hgap
;
546 // Now iterate the children, setting each child's dimensions
547 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
550 int row
, col
, endrow
, endcol
;
551 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
553 if ( item
->IsShown() )
555 item
->GetPos(row
, col
);
556 item
->GetEndPos(endrow
, endcol
);
559 for(idx
=row
; idx
<= endrow
; idx
++)
560 height
+= m_rowHeights
[idx
];
561 height
+= (endrow
- row
) * m_vgap
; // add a vgap for every row spanned
564 for (idx
=col
; idx
<= endcol
; idx
++)
565 width
+= m_colWidths
[idx
];
566 width
+= (endcol
- col
) * m_hgap
; // add a hgap for every col spanned
568 SetItemBounds(item
, colpos
[col
], rowpos
[row
], width
, height
);
571 node
= node
->GetNext();
576 // Sometimes CalcMin can result in some rows or cols having too much space in
577 // them because as it traverses the items it makes some assumptions when
578 // items span to other cells. But those assumptions can become invalid later
579 // on when other items are fitted into the same rows or columns that the
580 // spanning item occupies. This method tries to find those situations and
582 void wxGridBagSizer::AdjustForOverflow()
586 for (row
=0; row
<(int)m_rowHeights
.GetCount(); row
++)
588 int rowExtra
=INT_MAX
;
589 int rowHeight
= m_rowHeights
[row
];
590 for (col
=0; col
<(int)m_colWidths
.GetCount(); col
++)
592 wxGBPosition
pos(row
,col
);
593 wxGBSizerItem
* item
= FindItemAtPosition(pos
);
594 if ( !item
|| !item
->IsShown() )
598 item
->GetEndPos(endrow
, endcol
);
600 // If the item starts in this position and doesn't span rows, then
601 // just look at the whole item height
602 if ( item
->GetPos() == pos
&& endrow
== row
)
604 int itemHeight
= item
->CalcMin().GetHeight();
605 rowExtra
= wxMin(rowExtra
, rowHeight
- itemHeight
);
609 // Otherwise, only look at spanning items if they end on this row
612 // first deduct the portions of the item that are on prior rows
613 int itemHeight
= item
->CalcMin().GetHeight();
614 for (int r
=item
->GetPos().GetRow(); r
<row
; r
++)
615 itemHeight
-= (m_rowHeights
[r
] + GetHGap());
617 if ( itemHeight
< 0 )
620 // and check how much is left
621 rowExtra
= wxMin(rowExtra
, rowHeight
- itemHeight
);
624 if ( rowExtra
&& rowExtra
!= INT_MAX
)
625 m_rowHeights
[row
] -= rowExtra
;
628 // Now do the same thing for columns
629 for (col
=0; col
<(int)m_colWidths
.GetCount(); col
++)
631 int colExtra
=INT_MAX
;
632 int colWidth
= m_colWidths
[col
];
633 for (row
=0; row
<(int)m_rowHeights
.GetCount(); row
++)
635 wxGBPosition
pos(row
,col
);
636 wxGBSizerItem
* item
= FindItemAtPosition(pos
);
637 if ( !item
|| !item
->IsShown() )
641 item
->GetEndPos(endrow
, endcol
);
643 if ( item
->GetPos() == pos
&& endcol
== col
)
645 int itemWidth
= item
->CalcMin().GetWidth();
646 colExtra
= wxMin(colExtra
, colWidth
- itemWidth
);
652 int itemWidth
= item
->CalcMin().GetWidth();
653 for (int c
=item
->GetPos().GetCol(); c
<col
; c
++)
654 itemWidth
-= (m_colWidths
[c
] + GetVGap());
659 colExtra
= wxMin(colExtra
, colWidth
- itemWidth
);
662 if ( colExtra
&& colExtra
!= INT_MAX
)
663 m_colWidths
[col
] -= colExtra
;
669 //---------------------------------------------------------------------------
671 bool wxGridBagSizer::CheckForIntersection(wxGBSizerItem
* item
, wxGBSizerItem
* excludeItem
)
673 return CheckForIntersection(item
->GetPos(), item
->GetSpan(), excludeItem
);
676 bool wxGridBagSizer::CheckForIntersection(const wxGBPosition
& pos
, const wxGBSpan
& span
, wxGBSizerItem
* excludeItem
)
678 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
681 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
682 node
= node
->GetNext();
684 if ( excludeItem
&& item
== excludeItem
)
687 if ( item
->Intersects(pos
, span
) )
695 // Assumes a 10x10 grid, and returns the first empty cell found. This is
696 // really stupid but it is only used by the Add methods that match the base
697 // class virtuals, which should normally not be used anyway...
698 wxGBPosition
wxGridBagSizer::FindEmptyCell()
702 for (row
=0; row
<10; row
++)
703 for (col
=0; col
<10; col
++)
705 wxGBPosition
pos(row
, col
);
706 if ( !CheckForIntersection(pos
, wxDefaultSpan
) )
709 return wxGBPosition(-1, -1);
713 //---------------------------------------------------------------------------
715 // The Add base class virtuals should not be used with this class, but
716 // we'll try to make them automatically select a location for the item
719 wxSizerItem
* wxGridBagSizer::Add( wxWindow
*window
, int, int flag
, int border
, wxObject
* userData
)
721 return Add(window
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
724 wxSizerItem
* wxGridBagSizer::Add( wxSizer
*sizer
, int, int flag
, int border
, wxObject
* userData
)
726 return Add(sizer
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
729 wxSizerItem
* wxGridBagSizer::Add( int width
, int height
, int, int flag
, int border
, wxObject
* userData
)
731 return Add(width
, height
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
736 // The Insert nad Prepend base class virtuals that are not appropriate for
737 // this class and should not be used. Their implementation in this class
740 wxSizerItem
* wxGridBagSizer::Add( wxSizerItem
* )
742 wxFAIL_MSG(wxT("Invalid Add form called."));
746 wxSizerItem
* wxGridBagSizer::Prepend( wxWindow
*, int, int, int, wxObject
* )
748 wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
752 wxSizerItem
* wxGridBagSizer::Prepend( wxSizer
*, int, int, int, wxObject
* )
754 wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
758 wxSizerItem
* wxGridBagSizer::Prepend( int, int, int, int, int, wxObject
* )
760 wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
764 wxSizerItem
* wxGridBagSizer::Prepend( wxSizerItem
* )
766 wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
771 wxSizerItem
* wxGridBagSizer::Insert( size_t, wxWindow
*, int, int, int, wxObject
* )
773 wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
777 wxSizerItem
* wxGridBagSizer::Insert( size_t, wxSizer
*, int, int, int, wxObject
* )
779 wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
783 wxSizerItem
* wxGridBagSizer::Insert( size_t, int, int, int, int, int, wxObject
* )
785 wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
789 wxSizerItem
* wxGridBagSizer::Insert( size_t, wxSizerItem
* )
791 wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
796 //---------------------------------------------------------------------------
797 //---------------------------------------------------------------------------