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
10 // Copyright: (c) Robin Dunn
11 // Licence: wxWindows licence
12 /////////////////////////////////////////////////////////////////////////////
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
21 #include "wx/gbsizer.h"
23 //---------------------------------------------------------------------------
25 IMPLEMENT_DYNAMIC_CLASS(wxGBSizerItem
, wxSizerItem
)
26 IMPLEMENT_CLASS(wxGridBagSizer
, wxFlexGridSizer
)
28 const wxGBSpan wxDefaultSpan
;
30 //---------------------------------------------------------------------------
32 //---------------------------------------------------------------------------
34 wxGBSizerItem::wxGBSizerItem( int width
,
36 const wxGBPosition
& pos
,
41 : wxSizerItem(width
, height
, 0, flag
, border
, userData
),
49 wxGBSizerItem::wxGBSizerItem( wxWindow
*window
,
50 const wxGBPosition
& pos
,
55 : wxSizerItem(window
, 0, flag
, border
, userData
),
63 wxGBSizerItem::wxGBSizerItem( wxSizer
*sizer
,
64 const wxGBPosition
& pos
,
69 : wxSizerItem(sizer
, 0, flag
, border
, userData
),
76 wxGBSizerItem::wxGBSizerItem()
83 //---------------------------------------------------------------------------
86 void wxGBSizerItem::GetPos(int& row
, int& col
) const
92 void wxGBSizerItem::GetSpan(int& rowspan
, int& colspan
) const
94 rowspan
= m_span
.GetRowspan();
95 colspan
= m_span
.GetColspan();
99 bool wxGBSizerItem::SetPos( const wxGBPosition
& pos
)
103 wxCHECK_MSG( !m_gbsizer
->CheckForIntersection(pos
, m_span
, this), false,
104 wxT("An item is already at that position") );
110 bool wxGBSizerItem::SetSpan( const wxGBSpan
& span
)
114 wxCHECK_MSG( !m_gbsizer
->CheckForIntersection(m_pos
, span
, this), false,
115 wxT("An item is already at that position") );
122 inline bool InRange(int val
, int min
, int max
)
124 return (val
>= min
&& val
<= max
);
127 bool wxGBSizerItem::Intersects(const wxGBSizerItem
& other
)
129 return Intersects(other
.GetPos(), other
.GetSpan());
132 bool wxGBSizerItem::Intersects(const wxGBPosition
& pos
, const wxGBSpan
& span
)
135 int row
, col
, endrow
, endcol
;
136 int otherrow
, othercol
, otherendrow
, otherendcol
;
139 GetEndPos(endrow
, endcol
);
141 otherrow
= pos
.GetRow();
142 othercol
= pos
.GetCol();
143 otherendrow
= otherrow
+ span
.GetRowspan() - 1;
144 otherendcol
= othercol
+ span
.GetColspan() - 1;
146 // is the other item's start or end in the range of this one?
147 if (( InRange(otherrow
, row
, endrow
) && InRange(othercol
, col
, endcol
) ) ||
148 ( InRange(otherendrow
, row
, endrow
) && InRange(otherendcol
, col
, endcol
) ))
151 // is this item's start or end in the range of the other one?
152 if (( InRange(row
, otherrow
, otherendrow
) && InRange(col
, othercol
, otherendcol
) ) ||
153 ( InRange(endrow
, otherrow
, otherendrow
) && InRange(endcol
, othercol
, otherendcol
) ))
160 void wxGBSizerItem::GetEndPos(int& row
, int& col
)
162 row
= m_pos
.GetRow() + m_span
.GetRowspan() - 1;
163 col
= m_pos
.GetCol() + m_span
.GetColspan() - 1;
167 //---------------------------------------------------------------------------
169 //---------------------------------------------------------------------------
171 wxGridBagSizer::wxGridBagSizer(int vgap
, int hgap
)
172 : wxFlexGridSizer(1, vgap
, hgap
),
173 m_emptyCellSize(10,20)
179 wxSizerItem
* wxGridBagSizer::Add( wxWindow
*window
,
180 const wxGBPosition
& pos
, const wxGBSpan
& span
,
181 int flag
, int border
, wxObject
* userData
)
183 wxGBSizerItem
* item
= new wxGBSizerItem(window
, pos
, span
, flag
, border
, userData
);
193 wxSizerItem
* wxGridBagSizer::Add( wxSizer
*sizer
,
194 const wxGBPosition
& pos
, const wxGBSpan
& span
,
195 int flag
, int border
, wxObject
* userData
)
197 wxGBSizerItem
* item
= new wxGBSizerItem(sizer
, pos
, span
, flag
, border
, userData
);
207 wxSizerItem
* wxGridBagSizer::Add( int width
, int height
,
208 const wxGBPosition
& pos
, const wxGBSpan
& span
,
209 int flag
, int border
, wxObject
* userData
)
211 wxGBSizerItem
* item
= new wxGBSizerItem(width
, height
, pos
, span
, flag
, border
, userData
);
221 wxSizerItem
* wxGridBagSizer::Add( wxGBSizerItem
*item
)
223 wxCHECK_MSG( !CheckForIntersection(item
), NULL
,
224 wxT("An item is already at that position") );
225 m_children
.Append(item
);
226 item
->SetGBSizer(this);
227 if ( item
->GetWindow() )
228 item
->GetWindow()->SetContainingSizer( this );
230 // extend the number of rows/columns of the underlying wxFlexGridSizer if
233 item
->GetEndPos(row
, col
);
237 if ( row
> GetRows() )
239 if ( col
> GetCols() )
247 //---------------------------------------------------------------------------
249 wxSize
wxGridBagSizer::GetCellSize(int row
, int col
) const
251 wxCHECK_MSG( (row
< m_rows
) && (col
< m_cols
),
253 wxT("Invalid cell."));
254 return wxSize( m_colWidths
[col
], m_rowHeights
[row
] );
258 wxGBPosition
wxGridBagSizer::GetItemPosition(wxWindow
*window
)
260 wxGBPosition
badpos(-1,-1);
261 wxGBSizerItem
* item
= FindItem(window
);
262 wxCHECK_MSG(item
, badpos
, wxT("Failed to find item."));
263 return item
->GetPos();
267 wxGBPosition
wxGridBagSizer::GetItemPosition(wxSizer
*sizer
)
269 wxGBPosition
badpos(-1,-1);
270 wxGBSizerItem
* item
= FindItem(sizer
);
271 wxCHECK_MSG(item
, badpos
, wxT("Failed to find item."));
272 return item
->GetPos();
276 wxGBPosition
wxGridBagSizer::GetItemPosition(size_t index
)
278 wxGBPosition
badpos(-1,-1);
279 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
280 wxCHECK_MSG( node
, badpos
, wxT("Failed to find item.") );
281 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
282 return item
->GetPos();
287 bool wxGridBagSizer::SetItemPosition(wxWindow
*window
, const wxGBPosition
& pos
)
289 wxGBSizerItem
* item
= FindItem(window
);
290 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
291 return item
->SetPos(pos
);
295 bool wxGridBagSizer::SetItemPosition(wxSizer
*sizer
, const wxGBPosition
& pos
)
297 wxGBSizerItem
* item
= FindItem(sizer
);
298 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
299 return item
->SetPos(pos
);
303 bool wxGridBagSizer::SetItemPosition(size_t index
, const wxGBPosition
& pos
)
305 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
306 wxCHECK_MSG( node
, false, wxT("Failed to find item.") );
307 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
308 return item
->SetPos(pos
);
313 wxGBSpan
wxGridBagSizer::GetItemSpan(wxWindow
*window
)
315 wxGBSizerItem
* item
= FindItem(window
);
316 wxCHECK_MSG( item
, wxGBSpan::Invalid(), wxT("Failed to find item.") );
317 return item
->GetSpan();
321 wxGBSpan
wxGridBagSizer::GetItemSpan(wxSizer
*sizer
)
323 wxGBSizerItem
* item
= FindItem(sizer
);
324 wxCHECK_MSG( item
, wxGBSpan::Invalid(), wxT("Failed to find item.") );
325 return item
->GetSpan();
329 wxGBSpan
wxGridBagSizer::GetItemSpan(size_t index
)
331 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
332 wxCHECK_MSG( node
, wxGBSpan::Invalid(), wxT("Failed to find item.") );
333 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
334 return item
->GetSpan();
339 bool wxGridBagSizer::SetItemSpan(wxWindow
*window
, const wxGBSpan
& span
)
341 wxGBSizerItem
* item
= FindItem(window
);
342 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
343 return item
->SetSpan(span
);
347 bool wxGridBagSizer::SetItemSpan(wxSizer
*sizer
, const wxGBSpan
& span
)
349 wxGBSizerItem
* item
= FindItem(sizer
);
350 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
351 return item
->SetSpan(span
);
355 bool wxGridBagSizer::SetItemSpan(size_t index
, const wxGBSpan
& span
)
357 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
358 wxCHECK_MSG( node
, false, wxT("Failed to find item.") );
359 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
360 return item
->SetSpan(span
);
366 wxGBSizerItem
* wxGridBagSizer::FindItem(wxWindow
* window
)
368 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
371 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
372 if ( item
->GetWindow() == window
)
374 node
= node
->GetNext();
380 wxGBSizerItem
* wxGridBagSizer::FindItem(wxSizer
* sizer
)
382 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
385 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
386 if ( item
->GetSizer() == sizer
)
388 node
= node
->GetNext();
396 wxGBSizerItem
* wxGridBagSizer::FindItemAtPosition(const wxGBPosition
& pos
)
398 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
401 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
402 if ( item
->Intersects(pos
, wxDefaultSpan
) )
404 node
= node
->GetNext();
412 wxGBSizerItem
* wxGridBagSizer::FindItemAtPoint(const wxPoint
& pt
)
414 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
417 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
418 wxRect
rect(item
->GetPosition(), item
->GetSize());
419 rect
.Inflate(m_hgap
, m_vgap
);
420 if ( rect
.Contains(pt
) )
422 node
= node
->GetNext();
430 wxGBSizerItem
* wxGridBagSizer::FindItemWithData(const wxObject
* userData
)
432 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
435 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
436 if ( item
->GetUserData() == userData
)
438 node
= node
->GetNext();
446 //---------------------------------------------------------------------------
448 // Figure out what all the min row heights and col widths are, and calculate
449 // min size from that.
450 wxSize
wxGridBagSizer::CalcMin()
454 if (m_children
.GetCount() == 0)
455 return m_emptyCellSize
;
457 m_rowHeights
.Empty();
460 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
463 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
464 if ( item
->IsShown() )
466 int row
, col
, endrow
, endcol
;
468 item
->GetPos(row
, col
);
469 item
->GetEndPos(endrow
, endcol
);
471 // fill heights and widths up to this item if needed
472 while ( (int)m_rowHeights
.GetCount() <= endrow
)
473 m_rowHeights
.Add(m_emptyCellSize
.GetHeight());
474 while ( (int)m_colWidths
.GetCount() <= endcol
)
475 m_colWidths
.Add(m_emptyCellSize
.GetWidth());
477 // See if this item increases the size of its row(s) or col(s)
478 wxSize
size(item
->CalcMin());
479 for (idx
=row
; idx
<= endrow
; idx
++)
480 m_rowHeights
[idx
] = wxMax(m_rowHeights
[idx
], size
.GetHeight() / (endrow
-row
+1));
481 for (idx
=col
; idx
<= endcol
; idx
++)
482 m_colWidths
[idx
] = wxMax(m_colWidths
[idx
], size
.GetWidth() / (endcol
-col
+1));
484 node
= node
->GetNext();
488 AdjustForFlexDirection();
490 // Now traverse the heights and widths arrays calcing the totals, including gaps
492 m_cols
= m_colWidths
.GetCount();
493 for (idx
=0; idx
< m_cols
; idx
++)
494 width
+= m_colWidths
[idx
] + ( idx
== m_cols
-1 ? 0 : m_hgap
);
497 m_rows
= m_rowHeights
.GetCount();
498 for (idx
=0; idx
< m_rows
; idx
++)
499 height
+= m_rowHeights
[idx
] + ( idx
== m_rows
-1 ? 0 : m_vgap
);
501 m_calculatedMinSize
= wxSize(width
, height
);
502 return m_calculatedMinSize
;
507 void wxGridBagSizer::RecalcSizes()
509 if (m_children
.GetCount() == 0)
512 wxPoint
pt( GetPosition() );
513 wxSize
sz( GetSize() );
515 m_rows
= m_rowHeights
.GetCount();
516 m_cols
= m_colWidths
.GetCount();
517 int idx
, width
, height
;
519 AdjustForGrowables(sz
);
521 // Find the start positions on the window of the rows and columns
523 rowpos
.Add(0, m_rows
);
525 for (idx
=0; idx
< m_rows
; idx
++)
527 height
= m_rowHeights
[idx
] + m_vgap
;
533 colpos
.Add(0, m_cols
);
535 for (idx
=0; idx
< m_cols
; idx
++)
537 width
= m_colWidths
[idx
] + m_hgap
;
543 // Now iterate the children, setting each child's dimensions
544 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
547 int row
, col
, endrow
, endcol
;
548 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
550 if ( item
->IsShown() )
552 item
->GetPos(row
, col
);
553 item
->GetEndPos(endrow
, endcol
);
556 for(idx
=row
; idx
<= endrow
; idx
++)
557 height
+= m_rowHeights
[idx
];
558 height
+= (endrow
- row
) * m_vgap
; // add a vgap for every row spanned
561 for (idx
=col
; idx
<= endcol
; idx
++)
562 width
+= m_colWidths
[idx
];
563 width
+= (endcol
- col
) * m_hgap
; // add a hgap for every col spanned
565 SetItemBounds(item
, colpos
[col
], rowpos
[row
], width
, height
);
568 node
= node
->GetNext();
573 // Sometimes CalcMin can result in some rows or cols having too much space in
574 // them because as it traverses the items it makes some assumptions when
575 // items span to other cells. But those assumptions can become invalid later
576 // on when other items are fitted into the same rows or columns that the
577 // spanning item occupies. This method tries to find those situations and
579 void wxGridBagSizer::AdjustForOverflow()
583 for (row
=0; row
<(int)m_rowHeights
.GetCount(); row
++)
585 int rowExtra
=INT_MAX
;
586 int rowHeight
= m_rowHeights
[row
];
587 for (col
=0; col
<(int)m_colWidths
.GetCount(); col
++)
589 wxGBPosition
pos(row
,col
);
590 wxGBSizerItem
* item
= FindItemAtPosition(pos
);
591 if ( !item
|| !item
->IsShown() )
595 item
->GetEndPos(endrow
, endcol
);
597 // If the item starts in this position and doesn't span rows, then
598 // just look at the whole item height
599 if ( item
->GetPos() == pos
&& endrow
== row
)
601 int itemHeight
= item
->CalcMin().GetHeight();
602 rowExtra
= wxMin(rowExtra
, rowHeight
- itemHeight
);
606 // Otherwise, only look at spanning items if they end on this row
609 // first deduct the portions of the item that are on prior rows
610 int itemHeight
= item
->CalcMin().GetHeight();
611 for (int r
=item
->GetPos().GetRow(); r
<row
; r
++)
612 itemHeight
-= (m_rowHeights
[r
] + GetHGap());
614 if ( itemHeight
< 0 )
617 // and check how much is left
618 rowExtra
= wxMin(rowExtra
, rowHeight
- itemHeight
);
621 if ( rowExtra
&& rowExtra
!= INT_MAX
)
622 m_rowHeights
[row
] -= rowExtra
;
625 // Now do the same thing for columns
626 for (col
=0; col
<(int)m_colWidths
.GetCount(); col
++)
628 int colExtra
=INT_MAX
;
629 int colWidth
= m_colWidths
[col
];
630 for (row
=0; row
<(int)m_rowHeights
.GetCount(); row
++)
632 wxGBPosition
pos(row
,col
);
633 wxGBSizerItem
* item
= FindItemAtPosition(pos
);
634 if ( !item
|| !item
->IsShown() )
638 item
->GetEndPos(endrow
, endcol
);
640 if ( item
->GetPos() == pos
&& endcol
== col
)
642 int itemWidth
= item
->CalcMin().GetWidth();
643 colExtra
= wxMin(colExtra
, colWidth
- itemWidth
);
649 int itemWidth
= item
->CalcMin().GetWidth();
650 for (int c
=item
->GetPos().GetCol(); c
<col
; c
++)
651 itemWidth
-= (m_colWidths
[c
] + GetVGap());
656 colExtra
= wxMin(colExtra
, colWidth
- itemWidth
);
659 if ( colExtra
&& colExtra
!= INT_MAX
)
660 m_colWidths
[col
] -= colExtra
;
666 //---------------------------------------------------------------------------
668 bool wxGridBagSizer::CheckForIntersection(wxGBSizerItem
* item
, wxGBSizerItem
* excludeItem
)
670 return CheckForIntersection(item
->GetPos(), item
->GetSpan(), excludeItem
);
673 bool wxGridBagSizer::CheckForIntersection(const wxGBPosition
& pos
, const wxGBSpan
& span
, wxGBSizerItem
* excludeItem
)
675 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
678 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
679 node
= node
->GetNext();
681 if ( excludeItem
&& item
== excludeItem
)
684 if ( item
->Intersects(pos
, span
) )
692 // Assumes a 10x10 grid, and returns the first empty cell found. This is
693 // really stupid but it is only used by the Add methods that match the base
694 // class virtuals, which should normally not be used anyway...
695 wxGBPosition
wxGridBagSizer::FindEmptyCell()
699 for (row
=0; row
<10; row
++)
700 for (col
=0; col
<10; col
++)
702 wxGBPosition
pos(row
, col
);
703 if ( !CheckForIntersection(pos
, wxDefaultSpan
) )
706 return wxGBPosition(-1, -1);
710 //---------------------------------------------------------------------------
712 // The Add base class virtuals should not be used with this class, but
713 // we'll try to make them automatically select a location for the item
716 wxSizerItem
* wxGridBagSizer::Add( wxWindow
*window
, int, int flag
, int border
, wxObject
* userData
)
718 return Add(window
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
721 wxSizerItem
* wxGridBagSizer::Add( wxSizer
*sizer
, int, int flag
, int border
, wxObject
* userData
)
723 return Add(sizer
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
726 wxSizerItem
* wxGridBagSizer::Add( int width
, int height
, int, int flag
, int border
, wxObject
* userData
)
728 return Add(width
, height
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
733 // The Insert nad Prepend base class virtuals that are not appropriate for
734 // this class and should not be used. Their implementation in this class
737 wxSizerItem
* wxGridBagSizer::Add( wxSizerItem
* )
739 wxFAIL_MSG(wxT("Invalid Add form called."));
743 wxSizerItem
* wxGridBagSizer::Prepend( wxWindow
*, int, int, int, wxObject
* )
745 wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
749 wxSizerItem
* wxGridBagSizer::Prepend( wxSizer
*, int, int, int, wxObject
* )
751 wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
755 wxSizerItem
* wxGridBagSizer::Prepend( int, int, int, int, int, wxObject
* )
757 wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
761 wxSizerItem
* wxGridBagSizer::Prepend( wxSizerItem
* )
763 wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer."));
768 wxSizerItem
* wxGridBagSizer::Insert( size_t, wxWindow
*, int, int, int, wxObject
* )
770 wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
774 wxSizerItem
* wxGridBagSizer::Insert( size_t, wxSizer
*, int, int, int, wxObject
* )
776 wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
780 wxSizerItem
* wxGridBagSizer::Insert( size_t, int, int, int, int, int, wxObject
* )
782 wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
786 wxSizerItem
* wxGridBagSizer::Insert( size_t, wxSizerItem
* )
788 wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer."));
793 //---------------------------------------------------------------------------
794 //---------------------------------------------------------------------------