1 /////////////////////////////////////////////////////////////////////////////
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 /////////////////////////////////////////////////////////////////////////////
15 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
16 #pragma implementation "gbsizer.h"
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
26 #include "wx/gbsizer.h"
28 //---------------------------------------------------------------------------
30 IMPLEMENT_DYNAMIC_CLASS(wxGBSizerItem
, wxSizerItem
)
31 IMPLEMENT_CLASS(wxGridBagSizer
, wxFlexGridSizer
)
33 const wxGBSpan wxDefaultSpan
;
35 //---------------------------------------------------------------------------
37 //---------------------------------------------------------------------------
39 wxGBSizerItem::wxGBSizerItem( int width
,
41 const wxGBPosition
& pos
,
46 : wxSizerItem(width
, height
, 0, flag
, border
, userData
),
54 wxGBSizerItem::wxGBSizerItem( wxWindow
*window
,
55 const wxGBPosition
& pos
,
60 : wxSizerItem(window
, 0, flag
, border
, userData
),
68 wxGBSizerItem::wxGBSizerItem( wxSizer
*sizer
,
69 const wxGBPosition
& pos
,
74 : wxSizerItem(sizer
, 0, flag
, border
, userData
),
81 wxGBSizerItem::wxGBSizerItem()
89 //---------------------------------------------------------------------------
92 void wxGBSizerItem::GetPos(int& row
, int& col
) const
98 void wxGBSizerItem::GetSpan(int& rowspan
, int& colspan
) const
100 rowspan
= m_span
.GetRowspan();
101 colspan
= m_span
.GetColspan();
105 bool wxGBSizerItem::SetPos( const wxGBPosition
& pos
)
109 wxCHECK_MSG( !m_gbsizer
->CheckForIntersection(pos
, m_span
, this), false,
110 wxT("An item is already at that position") );
116 bool wxGBSizerItem::SetSpan( const wxGBSpan
& span
)
120 wxCHECK_MSG( !m_gbsizer
->CheckForIntersection(m_pos
, span
, this), false,
121 wxT("An item is already at that position") );
128 inline bool InRange(int val
, int min
, int max
)
130 return (val
>= min
&& val
<= max
);
133 bool wxGBSizerItem::Intersects(const wxGBSizerItem
& other
)
135 return Intersects(other
.GetPos(), other
.GetSpan());
138 bool wxGBSizerItem::Intersects(const wxGBPosition
& pos
, const wxGBSpan
& span
)
141 int row
, col
, endrow
, endcol
;
142 int otherrow
, othercol
, otherendrow
, otherendcol
;
145 GetEndPos(endrow
, endcol
);
147 otherrow
= pos
.GetRow();
148 othercol
= pos
.GetCol();
149 otherendrow
= otherrow
+ span
.GetRowspan() - 1;
150 otherendcol
= othercol
+ span
.GetColspan() - 1;
152 // is the other item's start or end in the range of this one?
153 if (( InRange(otherrow
, row
, endrow
) && InRange(othercol
, col
, endcol
) ) ||
154 ( InRange(otherendrow
, row
, endrow
) && InRange(otherendcol
, col
, endcol
) ))
157 // is this item's start or end in the range of the other one?
158 if (( InRange(row
, otherrow
, otherendrow
) && InRange(col
, othercol
, otherendcol
) ) ||
159 ( InRange(endrow
, otherrow
, otherendrow
) && InRange(endcol
, othercol
, otherendcol
) ))
166 void wxGBSizerItem::GetEndPos(int& row
, int& col
)
168 row
= m_pos
.GetRow() + m_span
.GetRowspan() - 1;
169 col
= m_pos
.GetCol() + m_span
.GetColspan() - 1;
173 //---------------------------------------------------------------------------
175 //---------------------------------------------------------------------------
177 wxGridBagSizer::wxGridBagSizer(int vgap
, int hgap
)
178 : wxFlexGridSizer(1, vgap
, hgap
),
179 m_emptyCellSize(10,20)
185 bool wxGridBagSizer::Add( wxWindow
*window
,
186 const wxGBPosition
& pos
, const wxGBSpan
& span
,
187 int flag
, int border
, wxObject
* userData
)
189 wxGBSizerItem
* item
= new wxGBSizerItem(window
, pos
, span
, flag
, border
, userData
);
199 bool wxGridBagSizer::Add( wxSizer
*sizer
,
200 const wxGBPosition
& pos
, const wxGBSpan
& span
,
201 int flag
, int border
, wxObject
* userData
)
203 wxGBSizerItem
* item
= new wxGBSizerItem(sizer
, pos
, span
, flag
, border
, userData
);
213 bool wxGridBagSizer::Add( int width
, int height
,
214 const wxGBPosition
& pos
, const wxGBSpan
& span
,
215 int flag
, int border
, wxObject
* userData
)
217 wxGBSizerItem
* item
= new wxGBSizerItem(width
, height
, pos
, span
, flag
, border
, userData
);
227 bool wxGridBagSizer::Add( wxGBSizerItem
*item
)
229 wxCHECK_MSG( !CheckForIntersection(item
), false,
230 wxT("An item is already at that position") );
231 m_children
.Append(item
);
232 item
->SetGBSizer(this);
233 if ( item
->GetWindow() )
234 item
->GetWindow()->SetContainingSizer( this );
241 //---------------------------------------------------------------------------
243 wxSize
wxGridBagSizer::GetCellSize(int row
, int col
) const
245 wxCHECK_MSG( (row
< m_rows
) && (col
< m_cols
),
247 wxT("Invalid cell."));
248 return wxSize( m_colWidths
[col
], m_rowHeights
[row
] );
252 wxGBPosition
wxGridBagSizer::GetItemPosition(wxWindow
*window
)
254 wxGBPosition
badpos(-1,-1);
255 wxGBSizerItem
* item
= FindItem(window
);
256 wxCHECK_MSG(item
, badpos
, wxT("Failed to find item."));
257 return item
->GetPos();
261 wxGBPosition
wxGridBagSizer::GetItemPosition(wxSizer
*sizer
)
263 wxGBPosition
badpos(-1,-1);
264 wxGBSizerItem
* item
= FindItem(sizer
);
265 wxCHECK_MSG(item
, badpos
, wxT("Failed to find item."));
266 return item
->GetPos();
270 wxGBPosition
wxGridBagSizer::GetItemPosition(size_t index
)
272 wxGBPosition
badpos(-1,-1);
273 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
274 wxCHECK_MSG( node
, badpos
, _T("Failed to find item.") );
275 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
276 return item
->GetPos();
281 bool wxGridBagSizer::SetItemPosition(wxWindow
*window
, const wxGBPosition
& pos
)
283 wxGBSizerItem
* item
= FindItem(window
);
284 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
285 return item
->SetPos(pos
);
289 bool wxGridBagSizer::SetItemPosition(wxSizer
*sizer
, const wxGBPosition
& pos
)
291 wxGBSizerItem
* item
= FindItem(sizer
);
292 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
293 return item
->SetPos(pos
);
297 bool wxGridBagSizer::SetItemPosition(size_t index
, const wxGBPosition
& pos
)
299 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
300 wxCHECK_MSG( node
, false, _T("Failed to find item.") );
301 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
302 return item
->SetPos(pos
);
307 wxGBSpan
wxGridBagSizer::GetItemSpan(wxWindow
*window
)
309 wxGBSpan
badspan(-1,-1);
310 wxGBSizerItem
* item
= FindItem(window
);
311 wxCHECK_MSG( item
, badspan
, _T("Failed to find item.") );
312 return item
->GetSpan();
316 wxGBSpan
wxGridBagSizer::GetItemSpan(wxSizer
*sizer
)
318 wxGBSpan
badspan(-1,-1);
319 wxGBSizerItem
* item
= FindItem(sizer
);
320 wxCHECK_MSG( item
, badspan
, _T("Failed to find item.") );
321 return item
->GetSpan();
325 wxGBSpan
wxGridBagSizer::GetItemSpan(size_t index
)
327 wxGBSpan
badspan(-1,-1);
328 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
329 wxCHECK_MSG( node
, badspan
, _T("Failed to find item.") );
330 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
331 return item
->GetSpan();
336 bool wxGridBagSizer::SetItemSpan(wxWindow
*window
, const wxGBSpan
& span
)
338 wxGBSizerItem
* item
= FindItem(window
);
339 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
340 return item
->SetSpan(span
);
344 bool wxGridBagSizer::SetItemSpan(wxSizer
*sizer
, const wxGBSpan
& span
)
346 wxGBSizerItem
* item
= FindItem(sizer
);
347 wxCHECK_MSG(item
, false, wxT("Failed to find item."));
348 return item
->SetSpan(span
);
352 bool wxGridBagSizer::SetItemSpan(size_t index
, const wxGBSpan
& span
)
354 wxSizerItemList::compatibility_iterator node
= m_children
.Item( index
);
355 wxCHECK_MSG( node
, false, _T("Failed to find item.") );
356 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
357 return item
->SetSpan(span
);
363 wxGBSizerItem
* wxGridBagSizer::FindItem(wxWindow
* window
)
365 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
368 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
369 if ( item
->GetWindow() == window
)
371 node
= node
->GetNext();
377 wxGBSizerItem
* wxGridBagSizer::FindItem(wxSizer
* sizer
)
379 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
382 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
383 if ( item
->GetSizer() == sizer
)
385 node
= node
->GetNext();
393 wxGBSizerItem
* wxGridBagSizer::FindItemAtPosition(const wxGBPosition
& pos
)
395 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
398 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
399 if ( item
->Intersects(pos
, wxDefaultSpan
) )
401 node
= node
->GetNext();
409 wxGBSizerItem
* wxGridBagSizer::FindItemAtPoint(const wxPoint
& pt
)
411 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
414 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
415 wxRect
rect(item
->GetPosition(), item
->GetSize());
416 rect
.Inflate(m_hgap
, m_vgap
);
417 if ( rect
.Inside(pt
) )
419 node
= node
->GetNext();
427 wxGBSizerItem
* wxGridBagSizer::FindItemWithData(const wxObject
* userData
)
429 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
432 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
433 if ( item
->GetUserData() == userData
)
435 node
= node
->GetNext();
443 //---------------------------------------------------------------------------
445 // Figure out what all the min row heights and col widths are, and calculate
446 // min size from that.
447 wxSize
wxGridBagSizer::CalcMin()
451 if (m_children
.GetCount() == 0)
452 return m_emptyCellSize
;
454 m_rowHeights
.Empty();
457 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
460 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
461 if ( item
->IsShown() )
463 int row
, col
, endrow
, endcol
;
465 item
->GetPos(row
, col
);
466 item
->GetEndPos(endrow
, endcol
);
468 // fill heights and widths upto this item if needed
469 while ( m_rowHeights
.GetCount() <= (size_t)endrow
)
470 m_rowHeights
.Add(m_emptyCellSize
.GetHeight());
471 while ( m_colWidths
.GetCount() <= (size_t)endcol
)
472 m_colWidths
.Add(m_emptyCellSize
.GetWidth());
474 // See if this item increases the size of its row(s) or col(s)
475 wxSize
size(item
->CalcMin());
476 for (idx
=row
; idx
<= endrow
; idx
++)
477 m_rowHeights
[idx
] = wxMax(m_rowHeights
[idx
], size
.GetHeight() / (endrow
-row
+1));
478 for (idx
=col
; idx
<= endcol
; idx
++)
479 m_colWidths
[idx
] = wxMax(m_colWidths
[idx
], size
.GetWidth() / (endcol
-col
+1));
481 node
= node
->GetNext();
484 AdjustForFlexDirection();
486 // Now traverse the heights and widths arrays calcing the totals, including gaps
488 m_cols
= m_colWidths
.GetCount();
489 for (idx
=0; idx
< m_cols
; idx
++)
490 width
+= m_colWidths
[idx
] + ( idx
== m_cols
-1 ? 0 : m_hgap
);
493 m_rows
= m_rowHeights
.GetCount();
494 for (idx
=0; idx
< m_rows
; idx
++)
495 height
+= m_rowHeights
[idx
] + ( idx
== m_rows
-1 ? 0 : m_vgap
);
497 m_calculatedMinSize
= wxSize(width
, height
);
498 return m_calculatedMinSize
;
503 void wxGridBagSizer::RecalcSizes()
505 if (m_children
.GetCount() == 0)
508 wxPoint
pt( GetPosition() );
509 wxSize
sz( GetSize() );
511 m_rows
= m_rowHeights
.GetCount();
512 m_cols
= m_colWidths
.GetCount();
513 int idx
, width
, height
;
515 AdjustForGrowables(sz
, m_calculatedMinSize
, m_rows
, m_cols
);
517 // Find the start positions on the window of the rows and columns
519 rowpos
.Add(0, m_rows
);
521 for (idx
=0; idx
< m_rows
; idx
++)
523 height
= m_rowHeights
[idx
] + m_vgap
;
529 colpos
.Add(0, m_cols
);
531 for (idx
=0; idx
< m_cols
; idx
++)
533 width
= m_colWidths
[idx
] + m_hgap
;
539 // Now iterate the children, setting each child's dimensions
540 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
543 int row
, col
, endrow
, endcol
;
544 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
545 item
->GetPos(row
, col
);
546 item
->GetEndPos(endrow
, endcol
);
549 for(idx
=row
; idx
<= endrow
; idx
++)
550 height
+= m_rowHeights
[idx
];
551 height
+= (endrow
- row
) * m_vgap
; // add a vgap for every row spanned
554 for (idx
=col
; idx
<= endcol
; idx
++)
555 width
+= m_colWidths
[idx
];
556 width
+= (endcol
- col
) * m_hgap
; // add a hgap for every col spanned
558 SetItemBounds(item
, colpos
[col
], rowpos
[row
], width
, height
);
560 node
= node
->GetNext();
566 //---------------------------------------------------------------------------
568 bool wxGridBagSizer::CheckForIntersection(wxGBSizerItem
* item
, wxGBSizerItem
* excludeItem
)
570 return CheckForIntersection(item
->GetPos(), item
->GetSpan(), excludeItem
);
573 bool wxGridBagSizer::CheckForIntersection(const wxGBPosition
& pos
, const wxGBSpan
& span
, wxGBSizerItem
* excludeItem
)
575 wxSizerItemList::compatibility_iterator node
= m_children
.GetFirst();
578 wxGBSizerItem
* item
= (wxGBSizerItem
*)node
->GetData();
579 node
= node
->GetNext();
581 if ( excludeItem
&& item
== excludeItem
)
584 if ( item
->Intersects(pos
, span
) )
592 // Assumes a 10x10 grid, and returns the first empty cell found. This is
593 // really stupid but it is only used by the Add methods that match the base
594 // class virtuals, which should normally not be used anyway...
595 wxGBPosition
wxGridBagSizer::FindEmptyCell()
599 for (row
=0; row
<10; row
++)
600 for (col
=0; col
<10; col
++)
602 wxGBPosition
pos(row
, col
);
603 if ( !CheckForIntersection(pos
, wxDefaultSpan
) )
606 return wxGBPosition(-1, -1);
610 //---------------------------------------------------------------------------
612 // The Add base class virtuals should not be used with this class, but
613 // we'll try to make them automatically select a location for the item
616 void wxGridBagSizer::Add( wxWindow
*window
, int, int flag
, int border
, wxObject
* userData
)
618 Add(window
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
621 void wxGridBagSizer::Add( wxSizer
*sizer
, int, int flag
, int border
, wxObject
* userData
)
623 Add(sizer
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
626 void wxGridBagSizer::Add( int width
, int height
, int, int flag
, int border
, wxObject
* userData
)
628 Add(width
, height
, FindEmptyCell(), wxDefaultSpan
, flag
, border
, userData
);
633 // The Insert nad Prepend base class virtuals that are not appropriate for
634 // this class and should not be used. Their implementation in this class
637 void wxGridBagSizer::Add( wxSizerItem
* )
638 { wxFAIL_MSG(wxT("Invalid Add form called.")); }
640 void wxGridBagSizer::Prepend( wxWindow
*, int, int, int, wxObject
* )
641 { wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
643 void wxGridBagSizer::Prepend( wxSizer
*, int, int, int, wxObject
* )
644 { wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
646 void wxGridBagSizer::Prepend( int, int, int, int, int, wxObject
* )
647 { wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
649 void wxGridBagSizer::Prepend( wxSizerItem
* )
650 { wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
653 void wxGridBagSizer::Insert( size_t, wxWindow
*, int, int, int, wxObject
* )
654 { wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
656 void wxGridBagSizer::Insert( size_t, wxSizer
*, int, int, int, wxObject
* )
657 { wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
659 void wxGridBagSizer::Insert( size_t, int, int, int, int, int, wxObject
* )
660 { wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
662 void wxGridBagSizer::Insert( size_t, wxSizerItem
* )
663 { wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
666 //---------------------------------------------------------------------------
667 //---------------------------------------------------------------------------