]> git.saurik.com Git - wxWidgets.git/blob - src/common/sizer.cpp
::bindInsertParams() was leaving some column bindings uninitialized if the column...
[wxWidgets.git] / src / common / sizer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: sizer.cpp
3 // Purpose: provide new wxSizer class for layout
4 // Author: Robert Roebling and Robin Dunn
5 // Modified by:
6 // Created:
7 // RCS-ID: $Id$
8 // Copyright: (c) Robin Dunn, Dirk Holtwick and Robert Roebling
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "sizer.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #include "wx/sizer.h"
24 #include "wx/utils.h"
25 #include "wx/statbox.h"
26 #include "wx/notebook.h"
27
28 //---------------------------------------------------------------------------
29
30 IMPLEMENT_ABSTRACT_CLASS(wxSizerItem, wxObject);
31 IMPLEMENT_ABSTRACT_CLASS(wxSizer, wxObject);
32 IMPLEMENT_ABSTRACT_CLASS(wxBoxSizer, wxSizer);
33 IMPLEMENT_ABSTRACT_CLASS(wxStaticBoxSizer, wxBoxSizer);
34 #if wxUSE_NOTEBOOK
35 IMPLEMENT_ABSTRACT_CLASS(wxNotebookSizer, wxSizer);
36 #endif
37
38 //---------------------------------------------------------------------------
39 // wxSizerItem
40 //---------------------------------------------------------------------------
41
42 wxSizerItem::wxSizerItem( int width, int height, int option, int flag, int border, wxObject* userData )
43 {
44 m_window = (wxWindow *) NULL;
45 m_sizer = (wxSizer *) NULL;
46 m_option = option;
47 m_border = border;
48 m_flag = flag;
49 m_userData = userData;
50
51 // minimal size is the initial size
52 m_minSize.x = width;
53 m_minSize.y = height;
54
55 SetRatio(width, height);
56
57 // size is set directly
58 m_size = m_minSize;
59 }
60
61 wxSizerItem::wxSizerItem( wxWindow *window, int option, int flag, int border, wxObject* userData )
62 {
63 m_window = window;
64 m_sizer = (wxSizer *) NULL;
65 m_option = option;
66 m_border = border;
67 m_flag = flag;
68 m_userData = userData;
69
70 // minimal size is the initial size
71 m_minSize = window->GetSize();
72
73 // aspect ratio calculated from initial size
74 SetRatio(m_minSize);
75
76 // size is calculated later
77 // m_size = ...
78 }
79
80 wxSizerItem::wxSizerItem( wxSizer *sizer, int option, int flag, int border, wxObject* userData )
81 {
82 m_window = (wxWindow *) NULL;
83 m_sizer = sizer;
84 m_option = option;
85 m_border = border;
86 m_flag = flag;
87 m_userData = userData;
88
89 // minimal size is calculated later
90 // m_minSize = ...
91 m_ratio = 0;
92
93 // size is calculated later
94 // m_size = ...
95 }
96
97 wxSizerItem::~wxSizerItem()
98 {
99 if (m_userData)
100 delete m_userData;
101 if (m_sizer)
102 delete m_sizer;
103 }
104
105
106 wxSize wxSizerItem::GetSize()
107 {
108 wxSize ret;
109 if (IsSizer())
110 ret = m_sizer->GetSize();
111 else
112 if (IsWindow())
113 ret = m_window->GetSize();
114 else ret = m_size;
115
116 if (m_flag & wxWEST)
117 ret.x += m_border;
118 if (m_flag & wxEAST)
119 ret.x += m_border;
120 if (m_flag & wxNORTH)
121 ret.y += m_border;
122 if (m_flag & wxSOUTH)
123 ret.y += m_border;
124
125 return ret;
126 }
127
128 wxSize wxSizerItem::CalcMin()
129 {
130 wxSize ret;
131 if (IsSizer())
132 {
133 ret = m_sizer->CalcMin();
134 // if we have to preserve aspect ratio _AND_ this is
135 // the first-time calculation, consider ret to be initial size
136 if ((m_flag & wxSHAPED) && !m_ratio) SetRatio(ret);
137 }
138
139 /*
140 The minimum size of a window should be the
141 initial size, as saved in m_minSize, not the
142 current size.
143
144 else
145 if (IsWindow())
146 ret = m_window->GetSize();
147 */
148 else ret = m_minSize;
149
150 if (m_flag & wxWEST)
151 ret.x += m_border;
152 if (m_flag & wxEAST)
153 ret.x += m_border;
154 if (m_flag & wxNORTH)
155 ret.y += m_border;
156 if (m_flag & wxSOUTH)
157 ret.y += m_border;
158
159 return ret;
160 }
161
162 void wxSizerItem::SetDimension( wxPoint pos, wxSize size )
163 {
164 if (m_flag & wxWEST)
165 {
166 pos.x += m_border;
167 size.x -= m_border;
168 }
169 if (m_flag & wxEAST)
170 {
171 size.x -= m_border;
172 }
173 if (m_flag & wxNORTH)
174 {
175 pos.y += m_border;
176 size.y -= m_border;
177 }
178 if (m_flag & wxSOUTH)
179 {
180 size.y -= m_border;
181 }
182 if (m_flag & wxSHAPED) {
183 // adjust aspect ratio
184 int rwidth = (int) (size.y * m_ratio);
185 if (rwidth > size.x) {
186 // fit horizontally
187 int rheight = (int) (size.x / m_ratio);
188 // add vertical space
189 if (m_flag & wxALIGN_CENTER_VERTICAL)
190 pos.y += (size.y - rheight) / 2;
191 else if (m_flag & wxALIGN_BOTTOM)
192 pos.y += (size.y - rheight);
193 // use reduced dimensions
194 size.y =rheight;
195 } else if (rwidth < size.x) {
196 // add horizontal space
197 if (m_flag & wxALIGN_CENTER_HORIZONTAL)
198 pos.x += (size.x - rwidth) / 2;
199 else if (m_flag & wxALIGN_RIGHT)
200 pos.x += (size.x - rwidth);
201 size.x = rwidth;
202 }
203 }
204
205 if (IsSizer())
206 m_sizer->SetDimension( pos.x, pos.y, size.x, size.y );
207
208 if (IsWindow())
209 m_window->SetSize( pos.x, pos.y, size.x, size.y, wxSIZE_ALLOW_MINUS_ONE );
210
211 m_size = size;
212 }
213
214 bool wxSizerItem::IsWindow()
215 {
216 return (m_window != NULL);
217 }
218
219 bool wxSizerItem::IsSizer()
220 {
221 return (m_sizer != NULL);
222 }
223
224 bool wxSizerItem::IsSpacer()
225 {
226 return (m_window == NULL) && (m_sizer == NULL);
227 }
228
229 //---------------------------------------------------------------------------
230 // wxSizer
231 //---------------------------------------------------------------------------
232
233 wxSizer::wxSizer()
234 {
235 m_children.DeleteContents( TRUE );
236 }
237
238 wxSizer::~wxSizer()
239 {
240 }
241
242 void wxSizer::Add( wxWindow *window, int option, int flag, int border, wxObject* userData )
243 {
244 m_children.Append( new wxSizerItem( window, option, flag, border, userData ) );
245 }
246
247 void wxSizer::Add( wxSizer *sizer, int option, int flag, int border, wxObject* userData )
248 {
249 m_children.Append( new wxSizerItem( sizer, option, flag, border, userData ) );
250 }
251
252 void wxSizer::Add( int width, int height, int option, int flag, int border, wxObject* userData )
253 {
254 m_children.Append( new wxSizerItem( width, height, option, flag, border, userData ) );
255 }
256
257 void wxSizer::Prepend( wxWindow *window, int option, int flag, int border, wxObject* userData )
258 {
259 m_children.Insert( new wxSizerItem( window, option, flag, border, userData ) );
260 }
261
262 void wxSizer::Prepend( wxSizer *sizer, int option, int flag, int border, wxObject* userData )
263 {
264 m_children.Insert( new wxSizerItem( sizer, option, flag, border, userData ) );
265 }
266
267 void wxSizer::Prepend( int width, int height, int option, int flag, int border, wxObject* userData )
268 {
269 m_children.Insert( new wxSizerItem( width, height, option, flag, border, userData ) );
270 }
271
272 void wxSizer::Insert( int before, wxWindow *window, int option, int flag, int border, wxObject* userData )
273 {
274 m_children.Insert( before, new wxSizerItem( window, option, flag, border, userData ) );
275 }
276
277 void wxSizer::Insert( int before, wxSizer *sizer, int option, int flag, int border, wxObject* userData )
278 {
279 m_children.Insert( before, new wxSizerItem( sizer, option, flag, border, userData ) );
280 }
281
282 void wxSizer::Insert( int before, int width, int height, int option, int flag, int border, wxObject* userData )
283 {
284 m_children.Insert( before, new wxSizerItem( width, height, option, flag, border, userData ) );
285 }
286
287 bool wxSizer::Remove( wxWindow *window )
288 {
289 wxASSERT( window );
290
291 wxNode *node = m_children.First();
292 while (node)
293 {
294 wxSizerItem *item = (wxSizerItem*)node->Data();
295 if (item->GetWindow() == window)
296 {
297 m_children.DeleteNode( node );
298 return TRUE;
299 }
300 node = node->Next();
301 }
302
303 return FALSE;
304 }
305
306 bool wxSizer::Remove( wxSizer *sizer )
307 {
308 wxASSERT( sizer );
309
310 wxNode *node = m_children.First();
311 while (node)
312 {
313 wxSizerItem *item = (wxSizerItem*)node->Data();
314 if (item->GetSizer() == sizer)
315 {
316 m_children.DeleteNode( node );
317 return TRUE;
318 }
319 node = node->Next();
320 }
321
322 return FALSE;
323 }
324
325 bool wxSizer::Remove( int pos )
326 {
327 wxNode *node = m_children.Nth( pos );
328 if (!node) return FALSE;
329
330 m_children.DeleteNode( node );
331
332 return TRUE;
333 }
334
335 void wxSizer::Fit( wxWindow *window )
336 {
337 window->SetSize( GetMinWindowSize( window ) );
338 }
339
340 void wxSizer::Layout()
341 {
342 CalcMin();
343 RecalcSizes();
344 }
345
346 void wxSizer::SetSizeHints( wxWindow *window )
347 {
348 wxSize size( GetMinWindowSize( window ) );
349 window->SetSizeHints( size.x, size.y );
350 }
351
352 wxSize wxSizer::GetMinWindowSize( wxWindow *window )
353 {
354 wxSize minSize( GetMinSize() );
355 wxSize size( window->GetSize() );
356 wxSize client_size( window->GetClientSize() );
357 return wxSize( minSize.x+size.x-client_size.x,
358 minSize.y+size.y-client_size.y );
359 }
360
361 void wxSizer::SetDimension( int x, int y, int width, int height )
362 {
363 m_position.x = x;
364 m_position.y = y;
365 m_size.x = width;
366 m_size.y = height;
367 CalcMin();
368 RecalcSizes();
369 }
370
371 //---------------------------------------------------------------------------
372 // wxBoxSizer
373 //---------------------------------------------------------------------------
374
375 wxBoxSizer::wxBoxSizer( int orient )
376 {
377 m_orient = orient;
378 }
379
380 void wxBoxSizer::RecalcSizes()
381 {
382 if (m_children.GetCount() == 0)
383 return;
384
385 int delta = 0;
386 int extra = 0;
387 if (m_stretchable)
388 {
389 if (m_orient == wxHORIZONTAL)
390 {
391 delta = (m_size.x - m_fixedWidth) / m_stretchable;
392 extra = (m_size.x - m_fixedWidth) % m_stretchable;
393 }
394 else
395 {
396 delta = (m_size.y - m_fixedHeight) / m_stretchable;
397 extra = (m_size.y - m_fixedHeight) % m_stretchable;
398 }
399 }
400
401 wxPoint pt( m_position );
402
403 wxNode *node = m_children.GetFirst();
404 while (node)
405 {
406 wxSizerItem *item = (wxSizerItem*) node->Data();
407
408 int weight = 1;
409 if (item->GetOption())
410 weight = item->GetOption();
411
412 wxSize size( item->CalcMin() );
413
414 if (m_orient == wxVERTICAL)
415 {
416 wxCoord height = size.y;
417 if (item->GetOption())
418 {
419 height = (delta * weight) + extra;
420 extra = 0; // only the first item will get the remainder as extra size
421 }
422
423 wxPoint child_pos( pt );
424 wxSize child_size( wxSize( size.x, height) );
425
426 if (item->GetFlag() & (wxEXPAND | wxSHAPED))
427 child_size.x = m_size.x;
428 else if (item->GetFlag() & wxALIGN_RIGHT)
429 child_pos.x += m_size.x - size.x;
430 else if (item->GetFlag() & (wxCENTER | wxALIGN_CENTER_HORIZONTAL))
431 // XXX wxCENTER is added for backward compatibility;
432 // wxALIGN_CENTER should be used in new code
433 child_pos.x += (m_size.x - size.x) / 2;
434
435 item->SetDimension( child_pos, child_size );
436
437 pt.y += height;
438 }
439 else
440 {
441 wxCoord width = size.x;
442 if (item->GetOption())
443 {
444 width = (delta * weight) + extra;
445 extra = 0; // only the first item will get the remainder as extra size
446 }
447
448 wxPoint child_pos( pt );
449 wxSize child_size( wxSize(width, size.y) );
450
451 if (item->GetFlag() & (wxEXPAND | wxSHAPED))
452 child_size.y = m_size.y;
453 else if (item->GetFlag() & wxALIGN_BOTTOM)
454 child_pos.y += m_size.y - size.y;
455 else if (item->GetFlag() & (wxCENTER | wxALIGN_CENTER_VERTICAL))
456 // XXX wxCENTER is added for backward compatibility;
457 // wxALIGN_CENTER should be used in new code
458 child_pos.y += (m_size.y - size.y) / 2;
459
460 item->SetDimension( child_pos, child_size );
461
462 pt.x += width;
463 }
464
465 node = node->Next();
466 }
467 }
468
469 wxSize wxBoxSizer::CalcMin()
470 {
471 if (m_children.GetCount() == 0)
472 return wxSize(10,10);
473
474 m_stretchable = 0;
475 m_minWidth = 0;
476 m_minHeight = 0;
477 m_fixedWidth = 0;
478 m_fixedHeight = 0;
479
480 wxNode *node = m_children.GetFirst();
481 while (node)
482 {
483 wxSizerItem *item = (wxSizerItem*) node->Data();
484
485 int weight = 1;
486 if (item->GetOption())
487 weight = item->GetOption();
488
489 wxSize size( item->CalcMin() );
490
491 if (m_orient == wxHORIZONTAL)
492 {
493 m_minWidth += (size.x * weight);
494 m_minHeight = wxMax( m_minHeight, size.y );
495 }
496 else
497 {
498 m_minHeight += (size.y * weight);
499 m_minWidth = wxMax( m_minWidth, size.x );
500 }
501
502 if (item->GetOption())
503 {
504 m_stretchable += weight;
505 }
506 else
507 {
508 if (m_orient == wxVERTICAL)
509 {
510 m_fixedHeight += size.y;
511 m_fixedWidth = wxMax( m_fixedWidth, size.x );
512 }
513 else
514 {
515 m_fixedWidth += size.x;
516 m_fixedHeight = wxMax( m_fixedHeight, size.y );
517 }
518 }
519
520 node = node->Next();
521 }
522
523 return wxSize( m_minWidth, m_minHeight );
524 }
525
526 //---------------------------------------------------------------------------
527 // wxStaticBoxSizer
528 //---------------------------------------------------------------------------
529
530 wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient )
531 : wxBoxSizer( orient )
532 {
533 wxASSERT_MSG( box, wxT("wxStaticBoxSizer needs a static box") );
534
535 m_staticBox = box;
536 }
537
538 void wxStaticBoxSizer::RecalcSizes()
539 {
540 // this will have to be done platform by platform
541 // as there is no way to guess the thickness of
542 // a wxStaticBox border
543 int top_border = 15;
544 if (m_staticBox->GetLabel().IsEmpty()) top_border = 5;
545 int other_border = 5;
546
547 m_staticBox->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
548
549 wxPoint old_pos( m_position );
550 m_position.x += other_border;
551 m_position.y += top_border;
552 wxSize old_size( m_size );
553 m_size.x -= 2*other_border;
554 m_size.y -= top_border + other_border;
555
556 wxBoxSizer::RecalcSizes();
557
558 m_position = old_pos;
559 m_size = old_size;
560 }
561
562 wxSize wxStaticBoxSizer::CalcMin()
563 {
564 // This will have to be done platform by platform
565 // as there is no way to guess the thickness of
566 // a wxStaticBox border.
567
568 int top_border = 15;
569 if (m_staticBox->GetLabel().IsEmpty()) top_border = 5;
570 int other_border = 5;
571
572 wxSize ret( wxBoxSizer::CalcMin() );
573 ret.x += 2*other_border;
574 ret.y += other_border + top_border;
575
576 return ret;
577 }
578
579 //---------------------------------------------------------------------------
580 // wxNotebookSizer
581 //---------------------------------------------------------------------------
582
583 #if wxUSE_NOTEBOOK
584
585 wxNotebookSizer::wxNotebookSizer( wxNotebook *nb )
586 {
587 wxASSERT_MSG( nb, wxT("wxNotebookSizer needs a notebook") );
588
589 m_notebook = nb;
590 }
591
592 void wxNotebookSizer::RecalcSizes()
593 {
594 m_notebook->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
595 }
596
597 wxSize wxNotebookSizer::CalcMin()
598 {
599 // This will have to be done platform by platform
600 // as there is no way to guess the thickness of
601 // the wxNotebook tabs and border.
602
603 int borderX = 5;
604 int borderY = 5;
605 if ((m_notebook->HasFlag(wxNB_RIGHT)) ||
606 (m_notebook->HasFlag(wxNB_LEFT)))
607 {
608 borderX += 70; // improvements later..
609 }
610 else
611 {
612 borderY += 35; // improvements later..
613 }
614
615 if (m_notebook->GetChildren().GetCount() == 0)
616 return wxSize(borderX + 10, borderY + 10);
617
618 int maxX = 0;
619 int maxY = 0;
620
621 wxWindowList::Node *node = m_notebook->GetChildren().GetFirst();
622 while (node)
623 {
624 wxWindow *item = node->GetData();
625 wxSizer *itemsizer = item->GetSizer();
626
627 if (itemsizer)
628 {
629 wxSize subsize( itemsizer->CalcMin() );
630
631 if (subsize.x > maxX) maxX = subsize.x;
632 if (subsize.y > maxY) maxY = subsize.y;
633 }
634
635 node = node->GetNext();
636 }
637
638 return wxSize( borderX + maxX, borderY + maxY );
639 }
640
641 #endif // wxUSE_NOTEBOOK