]> git.saurik.com Git - wxWidgets.git/blob - src/common/gbsizer.cpp
Added wxGridBagSizer, a grid sizer where item positions can be
[wxWidgets.git] / src / common / gbsizer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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
6 //
7 // Author: Robin Dunn
8 // Created: 03-Nov-2003
9 // RCS-ID: $Id$
10 // Copyright: (c) Robin Dunn
11 // Licence: wxWindows licence
12 /////////////////////////////////////////////////////////////////////////////
13
14
15 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
16 #pragma implementation "gbsizer.h"
17 #endif
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #include "wx/gbsizer.h"
27
28 //---------------------------------------------------------------------------
29
30 IMPLEMENT_CLASS(wxGBSizerItem, wxSizerItem)
31 IMPLEMENT_CLASS(wxGridBagSizer, wxFlexGridSizer)
32
33 const wxGBSpan wxDefaultSpan;
34
35 //---------------------------------------------------------------------------
36 // wxGBSizerItem
37 //---------------------------------------------------------------------------
38
39 wxGBSizerItem::wxGBSizerItem( int width,
40 int height,
41 const wxGBPosition& pos,
42 const wxGBSpan& span,
43 int flag,
44 int border,
45 wxObject* userData)
46 : wxSizerItem(width, height, 0, flag, border, userData),
47 m_pos(pos),
48 m_span(span),
49 m_sizer(NULL)
50 {
51 }
52
53
54 wxGBSizerItem::wxGBSizerItem( wxWindow *window,
55 const wxGBPosition& pos,
56 const wxGBSpan& span,
57 int flag,
58 int border,
59 wxObject* userData )
60 : wxSizerItem(window, 0, flag, border, userData),
61 m_pos(pos),
62 m_span(span),
63 m_sizer(NULL)
64 {
65 }
66
67
68 wxGBSizerItem::wxGBSizerItem( wxSizer *sizer,
69 const wxGBPosition& pos,
70 const wxGBSpan& span,
71 int flag,
72 int border,
73 wxObject* userData )
74 : wxSizerItem(sizer, 0, flag, border, userData),
75 m_pos(pos),
76 m_span(span),
77 m_sizer(NULL)
78 {
79 }
80
81
82 //---------------------------------------------------------------------------
83
84
85 void wxGBSizerItem::GetPos(int& row, int& col) const
86 {
87 row = m_pos.GetRow();
88 col = m_pos.GetCol();
89 }
90
91 void wxGBSizerItem::GetSpan(int& rowspan, int& colspan) const
92 {
93 rowspan = m_span.GetRowspan();
94 colspan = m_span.GetColspan();
95 }
96
97
98 bool wxGBSizerItem::SetPos( const wxGBPosition& pos )
99 {
100 if (m_sizer)
101 {
102 wxCHECK_MSG( !m_sizer->CheckForIntersection(pos, m_span, this), false,
103 wxT("An item is already at that position") );
104 }
105 m_pos = pos;
106 return true;
107 }
108
109 bool wxGBSizerItem::SetSpan( const wxGBSpan& span )
110 {
111 if (m_sizer)
112 {
113 wxCHECK_MSG( !m_sizer->CheckForIntersection(m_pos, span, this), false,
114 wxT("An item is already at that position") );
115 }
116 m_span = span;
117 return true;
118 }
119
120
121 inline bool InRange(int val, int min, int max)
122 {
123 return (val >= min && val <= max);
124 }
125
126 bool wxGBSizerItem::Intersects(const wxGBSizerItem& other)
127 {
128 return Intersects(other.GetPos(), other.GetSpan());
129 }
130
131 bool wxGBSizerItem::Intersects(const wxGBPosition& pos, const wxGBSpan& span)
132 {
133
134 int row, col, endrow, endcol;
135 int otherrow, othercol, otherendrow, otherendcol;
136
137 GetPos(row, col);
138 GetEndPos(endrow, endcol);
139
140 otherrow = pos.GetRow();
141 othercol = pos.GetCol();
142 otherendrow = otherrow + span.GetRowspan() - 1;
143 otherendcol = othercol + span.GetColspan() - 1;
144
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) ))
148 return true;
149
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) ))
153 return true;
154
155 return false;
156 }
157
158
159 void wxGBSizerItem::GetEndPos(int& row, int& col)
160 {
161 row = m_pos.GetRow() + m_span.GetRowspan() - 1;
162 col = m_pos.GetCol() + m_span.GetColspan() - 1;
163 }
164
165
166 //---------------------------------------------------------------------------
167 // wxGridBagSizer
168 //---------------------------------------------------------------------------
169
170 wxGridBagSizer::wxGridBagSizer(int vgap, int hgap )
171 : wxFlexGridSizer(1, vgap, hgap),
172 m_emptyCellSize(10,20)
173
174 {
175 }
176
177
178 bool wxGridBagSizer::Add( wxWindow *window,
179 const wxGBPosition& pos, const wxGBSpan& span,
180 int flag, int border, wxObject* userData )
181 {
182 wxGBSizerItem* item = new wxGBSizerItem(window, pos, span, flag, border, userData);
183 if ( Add(item) )
184 return true;
185 else
186 {
187 delete item;
188 return false;
189 }
190 }
191
192 bool wxGridBagSizer::Add( wxSizer *sizer,
193 const wxGBPosition& pos, const wxGBSpan& span,
194 int flag, int border, wxObject* userData )
195 {
196 wxGBSizerItem* item = new wxGBSizerItem(sizer, pos, span, flag, border, userData);
197 if ( Add(item) )
198 return true;
199 else
200 {
201 delete item;
202 return false;
203 }
204 }
205
206 bool wxGridBagSizer::Add( int width, int height,
207 const wxGBPosition& pos, const wxGBSpan& span,
208 int flag, int border, wxObject* userData )
209 {
210 wxGBSizerItem* item = new wxGBSizerItem(width, height, pos, span, flag, border, userData);
211 if ( Add(item) )
212 return true;
213 else
214 {
215 delete item;
216 return false;
217 }
218 }
219
220 bool wxGridBagSizer::Add( wxGBSizerItem *item )
221 {
222 m_children.Append(item);
223 item->SetSizer(this);
224 if ( item->GetWindow() )
225 item->GetWindow()->SetContainingSizer( this );
226
227 return true;
228 }
229
230
231
232 //---------------------------------------------------------------------------
233
234 wxGBPosition wxGridBagSizer::GetItemPosition(wxWindow *window)
235 {
236 wxGBPosition badpos(-1,-1);
237 wxGBSizerItem* item = FindItem(window);
238 wxCHECK_MSG(item, badpos, wxT("Failed to find item."));
239 return item->GetPos();
240 }
241
242
243 wxGBPosition wxGridBagSizer::GetItemPosition(wxSizer *sizer)
244 {
245 wxGBPosition badpos(-1,-1);
246 wxGBSizerItem* item = FindItem(sizer);
247 wxCHECK_MSG(item, badpos, wxT("Failed to find item."));
248 return item->GetPos();
249 }
250
251
252 wxGBPosition wxGridBagSizer::GetItemPosition(size_t index)
253 {
254 wxGBPosition badpos(-1,-1);
255 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
256 wxCHECK_MSG( node, badpos, _T("Failed to find item.") );
257 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
258 return item->GetPos();
259 }
260
261
262
263 bool wxGridBagSizer::SetItemPosition(wxWindow *window, const wxGBPosition& pos)
264 {
265 wxGBSizerItem* item = FindItem(window);
266 wxCHECK_MSG(item, false, wxT("Failed to find item."));
267 return item->SetPos(pos);
268 }
269
270
271 bool wxGridBagSizer::SetItemPosition(wxSizer *sizer, const wxGBPosition& pos)
272 {
273 wxGBSizerItem* item = FindItem(sizer);
274 wxCHECK_MSG(item, false, wxT("Failed to find item."));
275 return item->SetPos(pos);
276 }
277
278
279 bool wxGridBagSizer::SetItemPosition(size_t index, const wxGBPosition& pos)
280 {
281 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
282 wxCHECK_MSG( node, false, _T("Failed to find item.") );
283 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
284 return item->SetPos(pos);
285 }
286
287
288
289 wxGBSpan wxGridBagSizer::GetItemSpan(wxWindow *window)
290 {
291 wxGBSpan badspan(-1,-1);
292 wxGBSizerItem* item = FindItem(window);
293 wxCHECK_MSG( item, badspan, _T("Failed to find item.") );
294 return item->GetSpan();
295 }
296
297
298 wxGBSpan wxGridBagSizer::GetItemSpan(wxSizer *sizer)
299 {
300 wxGBSpan badspan(-1,-1);
301 wxGBSizerItem* item = FindItem(sizer);
302 wxCHECK_MSG( item, badspan, _T("Failed to find item.") );
303 return item->GetSpan();
304 }
305
306
307 wxGBSpan wxGridBagSizer::GetItemSpan(size_t index)
308 {
309 wxGBSpan badspan(-1,-1);
310 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
311 wxCHECK_MSG( node, badspan, _T("Failed to find item.") );
312 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
313 return item->GetSpan();
314 }
315
316
317
318 bool wxGridBagSizer::SetItemSpan(wxWindow *window, const wxGBSpan& span)
319 {
320 wxGBSizerItem* item = FindItem(window);
321 wxCHECK_MSG(item, false, wxT("Failed to find item."));
322 return item->SetSpan(span);
323 }
324
325
326 bool wxGridBagSizer::SetItemSpan(wxSizer *sizer, const wxGBSpan& span)
327 {
328 wxGBSizerItem* item = FindItem(sizer);
329 wxCHECK_MSG(item, false, wxT("Failed to find item."));
330 return item->SetSpan(span);
331 }
332
333
334 bool wxGridBagSizer::SetItemSpan(size_t index, const wxGBSpan& span)
335 {
336 wxSizerItemList::compatibility_iterator node = m_children.Item( index );
337 wxCHECK_MSG( node, false, _T("Failed to find item.") );
338 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
339 return item->SetSpan(span);
340 }
341
342
343
344
345 wxGBSizerItem* wxGridBagSizer::FindItem(wxWindow* window)
346 {
347 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
348 while (node)
349 {
350 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
351 if ( item->GetWindow() == window )
352 return item;
353 node = node->GetNext();
354 }
355 return NULL;
356 }
357
358
359 wxGBSizerItem* wxGridBagSizer::FindItem(wxSizer* sizer)
360 {
361 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
362 while (node)
363 {
364 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
365 if ( item->GetSizer() == sizer )
366 return item;
367 node = node->GetNext();
368 }
369 return NULL;
370 }
371
372
373
374
375 wxGBSizerItem* wxGridBagSizer::FindItemAtPosition(const wxGBPosition& pos)
376 {
377 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
378 while (node)
379 {
380 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
381 if ( item->Intersects(pos, wxDefaultSpan) )
382 return item;
383 node = node->GetNext();
384 }
385 return NULL;
386 }
387
388
389
390
391 wxGBSizerItem* wxGridBagSizer::FindItemWithData(const wxObject* userData)
392 {
393 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
394 while (node)
395 {
396 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
397 if ( item->GetUserData() == userData )
398 return item;
399 node = node->GetNext();
400 }
401 return NULL;
402 }
403
404
405
406
407 //---------------------------------------------------------------------------
408
409 // Figure out what all the min row heights and col widths are, and calculate
410 // min size from that.
411 wxSize wxGridBagSizer::CalcMin()
412 {
413 int idx;
414
415 if (m_children.GetCount() == 0)
416 return m_emptyCellSize;
417
418 m_rowHeights.Empty();
419 m_colWidths.Empty();
420
421 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
422 while (node)
423 {
424 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
425 if ( item->IsShown() )
426 {
427 int row, col, endrow, endcol;
428
429 item->GetPos(row, col);
430 item->GetEndPos(endrow, endcol);
431
432 // fill heights and widths upto this item if needed
433 while ( m_rowHeights.GetCount() <= (size_t)endrow )
434 m_rowHeights.Add(m_emptyCellSize.GetHeight());
435 while ( m_colWidths.GetCount() <= (size_t)endcol )
436 m_colWidths.Add(m_emptyCellSize.GetWidth());
437
438 // See if this item increases the size of its row(s) or col(s)
439 wxSize size(item->CalcMin());
440 for (idx=row; idx <= endrow; idx++)
441 m_rowHeights[idx] = wxMax(m_rowHeights[idx], size.GetHeight() / (endrow-row+1));
442 for (idx=col; idx <= endcol; idx++)
443 m_colWidths[idx] = wxMax(m_colWidths[idx], size.GetWidth() / (endcol-col+1));
444 }
445 node = node->GetNext();
446 }
447
448 AdjustForFlexDirection();
449
450 // Now traverse the heights and widths arrays calcing the totals, including gaps
451 int width = 0;
452 int ncols = m_colWidths.GetCount();
453 for (idx=0; idx < ncols; idx++)
454 width += m_colWidths[idx] + ( idx == ncols-1 ? 0 : m_hgap );
455
456 int height = 0;
457 int nrows = m_rowHeights.GetCount();
458 for (idx=0; idx < nrows; idx++)
459 height += m_rowHeights[idx] + ( idx == nrows-1 ? 0 : m_vgap );
460
461 return wxSize(width, height);
462 }
463
464
465
466 void wxGridBagSizer::RecalcSizes()
467 {
468 if (m_children.GetCount() == 0)
469 return;
470
471 // Calculates minsize and populates m_rowHeights and m_colWidths
472 wxSize minsz( CalcMin() );
473
474 wxPoint pt( GetPosition() );
475 wxSize sz( GetSize() );
476
477 int nrows = m_rowHeights.GetCount();
478 int ncols = m_colWidths.GetCount();
479 int idx, width, height;
480
481 AdjustForGrowables(sz, minsz, nrows, ncols);
482
483 // Find the start positions on the window of the rows and columns
484 wxArrayInt rowpos;
485 rowpos.Add(0, nrows);
486 int y = pt.y;
487 for (idx=0; idx < nrows; idx++)
488 {
489 height = m_rowHeights[idx] + m_vgap;
490 rowpos[idx] = y;
491 y += height;
492 }
493
494 wxArrayInt colpos;
495 colpos.Add(0, ncols);
496 int x = pt.x;
497 for (idx=0; idx < ncols; idx++)
498 {
499 width = m_colWidths[idx] + m_hgap;
500 colpos[idx] = x;
501 x += width;
502 }
503
504
505 // Now iterate the children, setting each child's dimensions
506 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
507 while (node)
508 {
509 int row, col, endrow, endcol;
510 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
511 item->GetPos(row, col);
512 item->GetEndPos(endrow, endcol);
513
514 height = 0;
515 for(idx=row; idx <= endrow; idx++)
516 height += m_rowHeights[idx] + m_vgap;
517
518 width = 0;
519 for (idx=col; idx <= endcol; idx++)
520 width += m_colWidths[idx] + m_hgap;
521
522 SetItemBounds(item, colpos[col], rowpos[row], width, height);
523
524 node = node->GetNext();
525 }
526 }
527
528
529
530 //---------------------------------------------------------------------------
531
532 bool wxGridBagSizer::CheckForIntersection(wxGBSizerItem* item, wxGBSizerItem* excludeItem)
533 {
534 return CheckForIntersection(item->GetPos(), item->GetSpan(), excludeItem);
535 }
536
537 bool wxGridBagSizer::CheckForIntersection(const wxGBPosition& pos, const wxGBSpan& span, wxGBSizerItem* excludeItem)
538 {
539 wxSizerItemList::compatibility_iterator node = m_children.GetFirst();
540 while (node)
541 {
542 wxGBSizerItem* item = (wxGBSizerItem*)node->GetData();
543 node = node->GetNext();
544
545 if ( excludeItem && item == excludeItem )
546 continue;
547
548 if ( item->Intersects(pos, span) )
549 return true;
550
551 }
552 return false;
553 }
554
555
556 // Assumes a 10x10 grid, and returns the first empty cell found. This is
557 // really stupid but it is only used by the Add methods that match the base
558 // class virtuals, which should normally not be used anyway...
559 wxGBPosition wxGridBagSizer::FindEmptyCell()
560 {
561 int row, col;
562
563 for (row=0; row<10; row++)
564 for (col=0; col<10; col++)
565 {
566 wxGBPosition pos(row, col);
567 if ( !CheckForIntersection(pos, wxDefaultSpan) )
568 return pos;
569 }
570 return wxGBPosition(-1, -1);
571 }
572
573
574 //---------------------------------------------------------------------------
575
576 // The Add base class virtuals should not be used with this class, but
577 // we'll try to make them automatically select a location for the item
578 // anyway.
579
580 void wxGridBagSizer::Add( wxWindow *window, int, int flag, int border, wxObject* userData )
581 {
582 Add(window, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
583 }
584
585 void wxGridBagSizer::Add( wxSizer *sizer, int, int flag, int border, wxObject* userData )
586 {
587 Add(sizer, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
588 }
589
590 void wxGridBagSizer::Add( int width, int height, int, int flag, int border, wxObject* userData )
591 {
592 Add(width, height, FindEmptyCell(), wxDefaultSpan, flag, border, userData);
593 }
594
595
596
597 // The Insert nad Prepend base class virtuals that are not appropriate for
598 // this class and should not be used. Their implementation in this class
599 // simply fails.
600
601 void wxGridBagSizer::Add( wxSizerItem *item )
602 { wxFAIL_MSG(wxT("Invalid Add form called.")); }
603
604 void wxGridBagSizer::Prepend( wxWindow *, int, int, int, wxObject* )
605 { wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
606
607 void wxGridBagSizer::Prepend( wxSizer *, int, int, int, wxObject* )
608 { wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
609
610 void wxGridBagSizer::Prepend( int, int, int, int, int, wxObject* )
611 { wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
612
613 void wxGridBagSizer::Prepend( wxSizerItem * )
614 { wxFAIL_MSG(wxT("Prepend should not be used with wxGridBagSizer.")); }
615
616
617 void wxGridBagSizer::Insert( size_t, wxWindow *, int, int, int, wxObject* )
618 { wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
619
620 void wxGridBagSizer::Insert( size_t, wxSizer *, int, int, int, wxObject* )
621 { wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
622
623 void wxGridBagSizer::Insert( size_t, int, int, int, int, int, wxObject* )
624 { wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
625
626 void wxGridBagSizer::Insert( size_t, wxSizerItem * )
627 { wxFAIL_MSG(wxT("Insert should not be used with wxGridBagSizer.")); }
628
629
630 //---------------------------------------------------------------------------
631 //---------------------------------------------------------------------------