]> git.saurik.com Git - wxWidgets.git/blame - src/common/sizer.cpp
fixed buffer overrun when reading CCITT group 4 compressed TIFF files (patch 646603)
[wxWidgets.git] / src / common / sizer.cpp
CommitLineData
5279a24d
RR
1/////////////////////////////////////////////////////////////////////////////
2// Name: sizer.cpp
1044a386 3// Purpose: provide new wxSizer class for layout
5279a24d 4// Author: Robert Roebling and Robin Dunn
566d84a7 5// Modified by: Ron Lee
0c0d686f 6// Created:
5279a24d
RR
7// RCS-ID: $Id$
8// Copyright: (c) Robin Dunn, Dirk Holtwick and Robert Roebling
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
5279a24d 12#ifdef __GNUG__
c62ac5b6 13#pragma implementation "sizer.h"
5279a24d
RR
14#endif
15
77671fd2
VZ
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
20 #pragma hdrstop
21#endif
22
5279a24d 23#include "wx/sizer.h"
61d514bb 24#include "wx/utils.h"
27ea1d8a 25#include "wx/statbox.h"
83edc0a5 26#include "wx/notebook.h"
5279a24d 27
0c0d686f
RD
28//---------------------------------------------------------------------------
29
799ea011
GD
30IMPLEMENT_ABSTRACT_CLASS(wxSizerItem, wxObject)
31IMPLEMENT_ABSTRACT_CLASS(wxSizer, wxObject)
32IMPLEMENT_ABSTRACT_CLASS(wxGridSizer, wxSizer)
33IMPLEMENT_ABSTRACT_CLASS(wxFlexGridSizer, wxGridSizer)
34IMPLEMENT_ABSTRACT_CLASS(wxBoxSizer, wxSizer)
1e6feb95 35#if wxUSE_STATBOX
799ea011 36IMPLEMENT_ABSTRACT_CLASS(wxStaticBoxSizer, wxBoxSizer)
1e6feb95 37#endif
60be2f47 38#if wxUSE_NOTEBOOK
799ea011 39IMPLEMENT_ABSTRACT_CLASS(wxNotebookSizer, wxSizer)
60be2f47 40#endif
0c0d686f 41
5279a24d 42//---------------------------------------------------------------------------
3417c2cd 43// wxSizerItem
5279a24d
RR
44//---------------------------------------------------------------------------
45
0c0d686f 46wxSizerItem::wxSizerItem( int width, int height, int option, int flag, int border, wxObject* userData )
5279a24d
RR
47{
48 m_window = (wxWindow *) NULL;
3417c2cd 49 m_sizer = (wxSizer *) NULL;
d597fcb7
RR
50 m_option = option;
51 m_border = border;
52 m_flag = flag;
2b5f62a0 53 m_show = TRUE; // Cannot be changed
0c0d686f
RD
54 m_userData = userData;
55
d597fcb7 56 // minimal size is the initial size
5279a24d 57 m_minSize.x = width;
c62ac5b6 58 m_minSize.y = height;
0c0d686f 59
be2577e4
RD
60 SetRatio(width, height);
61
d597fcb7
RR
62 // size is set directly
63 m_size = m_minSize;
5279a24d
RR
64}
65
0c0d686f 66wxSizerItem::wxSizerItem( wxWindow *window, int option, int flag, int border, wxObject* userData )
5279a24d
RR
67{
68 m_window = window;
3417c2cd 69 m_sizer = (wxSizer *) NULL;
5279a24d 70 m_option = option;
d597fcb7
RR
71 m_border = border;
72 m_flag = flag;
2b5f62a0 73 m_show = TRUE;
0c0d686f
RD
74 m_userData = userData;
75
d597fcb7
RR
76 // minimal size is the initial size
77 m_minSize = window->GetSize();
0c0d686f 78
be2577e4
RD
79 // aspect ratio calculated from initial size
80 SetRatio(m_minSize);
81
d597fcb7
RR
82 // size is calculated later
83 // m_size = ...
5279a24d
RR
84}
85
0c0d686f 86wxSizerItem::wxSizerItem( wxSizer *sizer, int option, int flag, int border, wxObject* userData )
5279a24d
RR
87{
88 m_window = (wxWindow *) NULL;
89 m_sizer = sizer;
5279a24d 90 m_option = option;
d597fcb7
RR
91 m_border = border;
92 m_flag = flag;
2b5f62a0 93 m_show = TRUE;
0c0d686f
RD
94 m_userData = userData;
95
d597fcb7
RR
96 // minimal size is calculated later
97 // m_minSize = ...
be2577e4 98 m_ratio = 0;
0c0d686f 99
d597fcb7
RR
100 // size is calculated later
101 // m_size = ...
5279a24d
RR
102}
103
0c0d686f
RD
104wxSizerItem::~wxSizerItem()
105{
106 if (m_userData)
107 delete m_userData;
108 if (m_sizer)
109 delete m_sizer;
110}
111
112
3417c2cd 113wxSize wxSizerItem::GetSize()
5279a24d 114{
d597fcb7 115 wxSize ret;
3417c2cd 116 if (IsSizer())
d597fcb7
RR
117 ret = m_sizer->GetSize();
118 else
c62ac5b6 119 if (IsWindow())
d597fcb7
RR
120 ret = m_window->GetSize();
121 else ret = m_size;
0c0d686f 122
d597fcb7
RR
123 if (m_flag & wxWEST)
124 ret.x += m_border;
125 if (m_flag & wxEAST)
126 ret.x += m_border;
127 if (m_flag & wxNORTH)
128 ret.y += m_border;
129 if (m_flag & wxSOUTH)
130 ret.y += m_border;
0c0d686f 131
d597fcb7 132 return ret;
5279a24d
RR
133}
134
3417c2cd 135wxSize wxSizerItem::CalcMin()
c62ac5b6 136{
d597fcb7 137 wxSize ret;
3417c2cd 138 if (IsSizer())
be2577e4 139 {
f6bcfd97 140 ret = m_sizer->GetMinSize();
d13d8d4e 141
be2577e4
RD
142 // if we have to preserve aspect ratio _AND_ this is
143 // the first-time calculation, consider ret to be initial size
d13d8d4e
VZ
144 if ((m_flag & wxSHAPED) && !m_ratio)
145 SetRatio(ret);
be2577e4 146 }
d597fcb7 147 else
d13d8d4e
VZ
148 {
149 if ( IsWindow() && (m_flag & wxADJUST_MINSIZE) )
150 {
2b5f62a0
VZ
151 // By user request, keep the minimal size for this item
152 // in sync with the largest of BestSize and any user supplied
153 // minimum size hint. Useful in cases where the item is
154 // changeable -- static text labels, etc.
155 m_minSize = m_window->GetAdjustedBestSize();
d13d8d4e
VZ
156 }
157
158 ret = m_minSize;
159 }
0c0d686f 160
d597fcb7
RR
161 if (m_flag & wxWEST)
162 ret.x += m_border;
163 if (m_flag & wxEAST)
164 ret.x += m_border;
165 if (m_flag & wxNORTH)
166 ret.y += m_border;
167 if (m_flag & wxSOUTH)
168 ret.y += m_border;
0c0d686f 169
d597fcb7 170 return ret;
c62ac5b6
RR
171}
172
3417c2cd 173void wxSizerItem::SetDimension( wxPoint pos, wxSize size )
c62ac5b6 174{
cdddaeea 175 if (m_flag & wxSHAPED)
d597fcb7 176 {
be2577e4
RD
177 // adjust aspect ratio
178 int rwidth = (int) (size.y * m_ratio);
cdddaeea
VZ
179 if (rwidth > size.x)
180 {
be2577e4
RD
181 // fit horizontally
182 int rheight = (int) (size.x / m_ratio);
183 // add vertical space
184 if (m_flag & wxALIGN_CENTER_VERTICAL)
185 pos.y += (size.y - rheight) / 2;
186 else if (m_flag & wxALIGN_BOTTOM)
187 pos.y += (size.y - rheight);
188 // use reduced dimensions
189 size.y =rheight;
cdddaeea
VZ
190 }
191 else if (rwidth < size.x)
192 {
be2577e4
RD
193 // add horizontal space
194 if (m_flag & wxALIGN_CENTER_HORIZONTAL)
195 pos.x += (size.x - rwidth) / 2;
196 else if (m_flag & wxALIGN_RIGHT)
197 pos.x += (size.x - rwidth);
198 size.x = rwidth;
199 }
200 }
33ac7e6f 201
cdddaeea
VZ
202 // This is what GetPosition() returns. Since we calculate
203 // borders afterwards, GetPosition() will be the left/top
204 // corner of the surrounding border.
205 m_pos = pos;
206
207 if (m_flag & wxWEST)
208 {
209 pos.x += m_border;
210 size.x -= m_border;
211 }
212 if (m_flag & wxEAST)
213 {
214 size.x -= m_border;
215 }
216 if (m_flag & wxNORTH)
217 {
218 pos.y += m_border;
219 size.y -= m_border;
220 }
221 if (m_flag & wxSOUTH)
222 {
223 size.y -= m_border;
224 }
0c0d686f 225
3417c2cd 226 if (IsSizer())
c62ac5b6 227 m_sizer->SetDimension( pos.x, pos.y, size.x, size.y );
0c0d686f 228
c62ac5b6 229 if (IsWindow())
b919f007 230 m_window->SetSize( pos.x, pos.y, size.x, size.y, wxSIZE_ALLOW_MINUS_ONE );
d597fcb7
RR
231
232 m_size = size;
c62ac5b6
RR
233}
234
84f7908b
RR
235void wxSizerItem::DeleteWindows()
236{
237 if (m_window)
238 m_window->Destroy();
be90c029 239
84f7908b
RR
240 if (m_sizer)
241 m_sizer->DeleteWindows();
242}
243
3417c2cd 244bool wxSizerItem::IsWindow()
5279a24d
RR
245{
246 return (m_window != NULL);
247}
248
3417c2cd 249bool wxSizerItem::IsSizer()
5279a24d
RR
250{
251 return (m_sizer != NULL);
252}
253
3417c2cd 254bool wxSizerItem::IsSpacer()
5279a24d
RR
255{
256 return (m_window == NULL) && (m_sizer == NULL);
257}
258
259//---------------------------------------------------------------------------
3417c2cd 260// wxSizer
5279a24d
RR
261//---------------------------------------------------------------------------
262
3417c2cd 263wxSizer::wxSizer()
5279a24d
RR
264{
265 m_children.DeleteContents( TRUE );
f6bcfd97
BP
266 m_minSize.x = 0;
267 m_minSize.y = 0;
5279a24d
RR
268}
269
3417c2cd 270wxSizer::~wxSizer()
5279a24d 271{
be90c029 272 Clear();
5279a24d 273}
0c0d686f
RD
274
275void wxSizer::Add( wxWindow *window, int option, int flag, int border, wxObject* userData )
5279a24d 276{
0c0d686f 277 m_children.Append( new wxSizerItem( window, option, flag, border, userData ) );
be90c029 278 window->SetContainingSizer(this);
5279a24d
RR
279}
280
0c0d686f 281void wxSizer::Add( wxSizer *sizer, int option, int flag, int border, wxObject* userData )
5279a24d 282{
0c0d686f 283 m_children.Append( new wxSizerItem( sizer, option, flag, border, userData ) );
5279a24d
RR
284}
285
0c0d686f 286void wxSizer::Add( int width, int height, int option, int flag, int border, wxObject* userData )
5279a24d 287{
0c0d686f 288 m_children.Append( new wxSizerItem( width, height, option, flag, border, userData ) );
5279a24d
RR
289}
290
0c0d686f 291void wxSizer::Prepend( wxWindow *window, int option, int flag, int border, wxObject* userData )
42b4e99e 292{
0c0d686f 293 m_children.Insert( new wxSizerItem( window, option, flag, border, userData ) );
be90c029 294 window->SetContainingSizer(this);
42b4e99e
RR
295}
296
0c0d686f 297void wxSizer::Prepend( wxSizer *sizer, int option, int flag, int border, wxObject* userData )
42b4e99e 298{
0c0d686f 299 m_children.Insert( new wxSizerItem( sizer, option, flag, border, userData ) );
42b4e99e
RR
300}
301
0c0d686f 302void wxSizer::Prepend( int width, int height, int option, int flag, int border, wxObject* userData )
42b4e99e 303{
0c0d686f 304 m_children.Insert( new wxSizerItem( width, height, option, flag, border, userData ) );
f35aa3da
RR
305}
306
307void wxSizer::Insert( int before, wxWindow *window, int option, int flag, int border, wxObject* userData )
308{
309 m_children.Insert( before, new wxSizerItem( window, option, flag, border, userData ) );
be90c029 310 window->SetContainingSizer(this);
f35aa3da
RR
311}
312
313void wxSizer::Insert( int before, wxSizer *sizer, int option, int flag, int border, wxObject* userData )
314{
315 m_children.Insert( before, new wxSizerItem( sizer, option, flag, border, userData ) );
316}
317
318void wxSizer::Insert( int before, int width, int height, int option, int flag, int border, wxObject* userData )
319{
320 m_children.Insert( before, new wxSizerItem( width, height, option, flag, border, userData ) );
42b4e99e
RR
321}
322
323bool wxSizer::Remove( wxWindow *window )
324{
325 wxASSERT( window );
0c0d686f 326
42b4e99e
RR
327 wxNode *node = m_children.First();
328 while (node)
329 {
330 wxSizerItem *item = (wxSizerItem*)node->Data();
3ca6a5f0
BP
331 if (item->GetWindow() == window)
332 {
be90c029 333 item->GetWindow()->SetContainingSizer(NULL);
42b4e99e 334 m_children.DeleteNode( node );
3ca6a5f0
BP
335 return TRUE;
336 }
42b4e99e
RR
337 node = node->Next();
338 }
0c0d686f 339
42b4e99e
RR
340 return FALSE;
341}
342
343bool wxSizer::Remove( wxSizer *sizer )
344{
345 wxASSERT( sizer );
0c0d686f 346
42b4e99e
RR
347 wxNode *node = m_children.First();
348 while (node)
349 {
350 wxSizerItem *item = (wxSizerItem*)node->Data();
3ca6a5f0
BP
351 if (item->GetSizer() == sizer)
352 {
42b4e99e 353 m_children.DeleteNode( node );
3ca6a5f0
BP
354 return TRUE;
355 }
42b4e99e
RR
356 node = node->Next();
357 }
0c0d686f 358
42b4e99e
RR
359 return FALSE;
360}
361
362bool wxSizer::Remove( int pos )
363{
2b5f62a0
VZ
364 if ((size_t)pos >= m_children.GetCount())
365 return FALSE;
42b4e99e
RR
366 wxNode *node = m_children.Nth( pos );
367 if (!node) return FALSE;
0c0d686f 368
42b4e99e 369 m_children.DeleteNode( node );
0c0d686f 370
42b4e99e
RR
371 return TRUE;
372}
0c0d686f 373
84f7908b
RR
374void wxSizer::Clear( bool delete_windows )
375{
be90c029
RD
376 // First clear the ContainingSizer pointers
377 wxNode *node = m_children.First();
378 while (node)
379 {
380 wxSizerItem *item = (wxSizerItem*)node->Data();
381 if (item->IsWindow())
382 item->GetWindow()->SetContainingSizer(NULL);
383 node = node->Next();
384 }
385
386 // Destroy the windows if needed
84f7908b
RR
387 if (delete_windows)
388 DeleteWindows();
be90c029
RD
389
390 // Now empty the list
84f7908b
RR
391 m_children.Clear();
392}
393
394void wxSizer::DeleteWindows()
395{
396 wxNode *node = m_children.First();
397 while (node)
398 {
399 wxSizerItem *item = (wxSizerItem*)node->Data();
400 item->DeleteWindows();
401 node = node->Next();
402 }
403}
404
e5251d4f 405wxSize wxSizer::Fit( wxWindow *window )
5279a24d 406{
9ef2e675
GT
407 wxSize size;
408 if (window->IsTopLevel())
409 size = FitSize( window );
410 else
411 size = GetMinWindowSize( window );
412
77424cfb 413 window->SetSize( size );
e5251d4f
VZ
414
415 return size;
5279a24d
RR
416}
417
566d84a7
RL
418void wxSizer::FitInside( wxWindow *window )
419{
420 wxSize size;
421 if (window->IsTopLevel())
422 size = VirtualFitSize( window );
423 else
424 size = GetMinClientSize( window );
425
426 window->SetVirtualSize( size );
427}
428
3417c2cd 429void wxSizer::Layout()
c62ac5b6 430{
42b4e99e 431 CalcMin();
c62ac5b6
RR
432 RecalcSizes();
433}
434
3417c2cd 435void wxSizer::SetSizeHints( wxWindow *window )
5279a24d 436{
34c3ffca
RL
437 // Preserve the window's max size hints, but set the
438 // lower bound according to the sizer calculations.
439
e5251d4f
VZ
440 wxSize size = Fit( window );
441
34c3ffca
RL
442 window->SetSizeHints( size.x,
443 size.y,
444 window->GetMaxWidth(),
445 window->GetMaxHeight() );
5279a24d
RR
446}
447
566d84a7
RL
448void wxSizer::SetVirtualSizeHints( wxWindow *window )
449{
450 // Preserve the window's max size hints, but set the
451 // lower bound according to the sizer calculations.
452
453 FitInside( window );
454 wxSize size( window->GetVirtualSize() );
455 window->SetVirtualSizeHints( size.x,
456 size.y,
457 window->GetMaxWidth(),
458 window->GetMaxHeight() );
459}
460
34c3ffca 461wxSize wxSizer::GetMaxWindowSize( wxWindow *window )
65ba4113 462{
34c3ffca 463 return window->GetMaxSize();
65ba4113
GT
464}
465
3417c2cd 466wxSize wxSizer::GetMinWindowSize( wxWindow *window )
5279a24d 467{
77671fd2 468 wxSize minSize( GetMinSize() );
5279a24d
RR
469 wxSize size( window->GetSize() );
470 wxSize client_size( window->GetClientSize() );
77671fd2 471 return wxSize( minSize.x+size.x-client_size.x,
0c0d686f 472 minSize.y+size.y-client_size.y );
5279a24d
RR
473}
474
65ba4113
GT
475// Return a window size that will fit within the screens dimensions
476wxSize wxSizer::FitSize( wxWindow *window )
477{
478 wxSize size = GetMinWindowSize( window );
479 wxSize sizeMax = GetMaxWindowSize( window );
480
34c3ffca
RL
481 // Limit the size if sizeMax != wxDefaultSize
482
483 if ( size.x > sizeMax.x && sizeMax.x != -1 )
65ba4113 484 size.x = sizeMax.x;
34c3ffca 485 if ( size.y > sizeMax.y && sizeMax.y != -1 )
65ba4113
GT
486 size.y = sizeMax.y;
487
488 return size;
489}
490
566d84a7
RL
491wxSize wxSizer::GetMaxClientSize( wxWindow *window )
492{
493 wxSize maxSize( window->GetMaxSize() );
494
495 if( maxSize != wxDefaultSize )
496 {
497 wxSize size( window->GetSize() );
498 wxSize client_size( window->GetClientSize() );
499
500 return wxSize( maxSize.x + client_size.x - size.x,
501 maxSize.y + client_size.y - size.y );
502 }
503 else
504 return wxDefaultSize;
505}
506
1b0674f7 507wxSize wxSizer::GetMinClientSize( wxWindow *WXUNUSED(window) )
566d84a7
RL
508{
509 return GetMinSize(); // Already returns client size.
510}
511
512wxSize wxSizer::VirtualFitSize( wxWindow *window )
513{
514 wxSize size = GetMinClientSize( window );
515 wxSize sizeMax = GetMaxClientSize( window );
516
517 // Limit the size if sizeMax != wxDefaultSize
518
519 if ( size.x > sizeMax.x && sizeMax.x != -1 )
520 size.x = sizeMax.x;
521 if ( size.y > sizeMax.y && sizeMax.y != -1 )
522 size.y = sizeMax.y;
523
524 return size;
525}
526
3417c2cd 527void wxSizer::SetDimension( int x, int y, int width, int height )
5279a24d
RR
528{
529 m_position.x = x;
530 m_position.y = y;
531 m_size.x = width;
532 m_size.y = height;
2b5f62a0 533 Layout();
5279a24d
RR
534}
535
f6bcfd97 536wxSize wxSizer::GetMinSize()
3ca6a5f0 537{
f6bcfd97
BP
538 wxSize ret( CalcMin() );
539 if (ret.x < m_minSize.x) ret.x = m_minSize.x;
540 if (ret.y < m_minSize.y) ret.y = m_minSize.y;
3ca6a5f0 541 return ret;
f6bcfd97
BP
542}
543
544void wxSizer::DoSetMinSize( int width, int height )
545{
546 m_minSize.x = width;
547 m_minSize.y = height;
548}
549
550bool wxSizer::DoSetItemMinSize( wxWindow *window, int width, int height )
551{
552 wxASSERT( window );
553
554 wxNode *node = m_children.First();
555 while (node)
556 {
557 wxSizerItem *item = (wxSizerItem*)node->Data();
3ca6a5f0
BP
558 if (item->GetWindow() == window)
559 {
f6bcfd97 560 item->SetInitSize( width, height );
3ca6a5f0
BP
561 return TRUE;
562 }
f6bcfd97
BP
563 node = node->Next();
564 }
565
566 node = m_children.First();
567 while (node)
568 {
569 wxSizerItem *item = (wxSizerItem*)node->Data();
3ca6a5f0
BP
570 if (item->GetSizer())
571 {
0ca5105b 572 // It's a sizer, so lets search recursively.
f6bcfd97
BP
573 if (item->GetSizer()->DoSetItemMinSize( window, width, height ))
574 {
0ca5105b 575 // A child sizer found the requested windw, exit.
3ca6a5f0 576 return TRUE;
f6bcfd97 577 }
3ca6a5f0 578 }
f6bcfd97
BP
579 node = node->Next();
580 }
581
582 return FALSE;
583}
584
585bool wxSizer::DoSetItemMinSize( wxSizer *sizer, int width, int height )
586{
587 wxASSERT( sizer );
588
589 wxNode *node = m_children.First();
590 while (node)
591 {
592 wxSizerItem *item = (wxSizerItem*)node->Data();
3ca6a5f0
BP
593 if (item->GetSizer() == sizer)
594 {
f6bcfd97 595 item->GetSizer()->DoSetMinSize( width, height );
3ca6a5f0
BP
596 return TRUE;
597 }
f6bcfd97
BP
598 node = node->Next();
599 }
600
601 node = m_children.First();
602 while (node)
603 {
604 wxSizerItem *item = (wxSizerItem*)node->Data();
3ca6a5f0
BP
605 if (item->GetSizer())
606 {
0ca5105b 607 // It's a sizer, so lets search recursively.
f6bcfd97
BP
608 if (item->GetSizer()->DoSetItemMinSize( sizer, width, height ))
609 {
0ca5105b 610 // A child sizer found the requested windw, exit.
3ca6a5f0 611 return TRUE;
f6bcfd97 612 }
3ca6a5f0 613 }
f6bcfd97
BP
614 node = node->Next();
615 }
616
617 return FALSE;
618}
619
620bool wxSizer::DoSetItemMinSize( int pos, int width, int height )
621{
622 wxNode *node = m_children.Nth( pos );
623 if (!node) return FALSE;
624
625 wxSizerItem *item = (wxSizerItem*) node->Data();
626 if (item->GetSizer())
627 {
0ca5105b 628 // Sizers contains the minimal size in them, if not calculated ...
f6bcfd97
BP
629 item->GetSizer()->DoSetMinSize( width, height );
630 }
631 else
632 {
0ca5105b 633 // ... but the minimal size of spacers and windows in stored in them
f6bcfd97
BP
634 item->SetInitSize( width, height );
635 }
636
637 return TRUE;
638}
639
2b5f62a0
VZ
640void wxSizer::Show(wxWindow *window, bool show)
641{
642 wxNode *node = m_children.GetFirst();
643 while (node)
644 {
645 wxSizerItem *item = (wxSizerItem*) node->Data();
646
647 if (item->IsWindow() && item->GetWindow() == window)
648 {
649 item->Show(show);
650 window->Show(show);
651 return;
652 }
653 node = node->Next();
654 }
655}
656
657void wxSizer::Show(wxSizer *sizer, bool show)
658{
659 wxNode *node = m_children.GetFirst();
660 while (node)
661 {
662 wxSizerItem *item = (wxSizerItem*) node->Data();
663
664 if (item->IsSizer() && item->GetSizer() == sizer)
665 {
666 item->Show(show);
667 sizer->ShowItems(show);
668 return;
669 }
670 node = node->Next();
671 }
672}
673
674void wxSizer::ShowItems (bool show)
675{
676 wxNode *node = m_children.GetFirst();
677 while (node)
678 {
679 wxSizerItem *item = (wxSizerItem*) node->Data();
680
681 if (item->IsWindow())
682 item->GetWindow()->Show (show);
683 else if (item->IsSizer())
684 item->GetSizer()->ShowItems (show);
685
686 node = node->Next();
687 }
688}
689
690bool wxSizer::IsShown (wxWindow *window)
691{
692 wxNode *node = m_children.GetFirst();
693 while (node)
694 {
695 wxSizerItem *item = (wxSizerItem*) node->Data();
696
697 if (item->IsWindow() && item->GetWindow() == window)
698 {
699 return item->IsShown();
700 }
701 node = node->Next();
702 }
703
704 return FALSE;
705}
706
707bool wxSizer::IsShown (wxSizer *sizer)
708{
709 wxNode *node = m_children.GetFirst();
710 while (node)
711 {
712 wxSizerItem *item = (wxSizerItem*) node->Data();
713
714 if (item->IsSizer() && item->GetSizer() == sizer)
715 {
716 return item->IsShown();
717 }
718 node = node->Next();
719 }
720
721 return FALSE;
722}
723
f6bcfd97
BP
724//---------------------------------------------------------------------------
725// wxGridSizer
726//---------------------------------------------------------------------------
727
728wxGridSizer::wxGridSizer( int rows, int cols, int vgap, int hgap )
729{
730 m_rows = rows;
731 m_cols = cols;
732 m_vgap = vgap;
733 m_hgap = hgap;
734}
735
736wxGridSizer::wxGridSizer( int cols, int vgap, int hgap )
737{
738 m_rows = 0;
739 m_cols = cols;
740 m_vgap = vgap;
741 m_hgap = hgap;
742}
743
0ca5105b 744int wxGridSizer::CalcRowsCols(int& nrows, int& ncols) const
f6bcfd97 745{
f6bcfd97 746 int nitems = m_children.GetCount();
2b5f62a0 747 if ( nitems)
0ca5105b
VZ
748 {
749 if ( m_cols )
750 {
751 ncols = m_cols;
752 nrows = (nitems + m_cols - 1) / m_cols;
753 }
754 else if ( m_rows )
755 {
756 ncols = (nitems + m_rows - 1) / m_rows;
757 nrows = m_rows;
758 }
759 else // 0 columns, 0 rows?
760 {
761 wxFAIL_MSG( _T("grid sizer must have either rows or columns fixed") );
f6bcfd97 762
0ca5105b
VZ
763 nrows = ncols = 0;
764 }
765 }
766
767 return nitems;
768}
769
770void wxGridSizer::RecalcSizes()
771{
772 int nitems, nrows, ncols;
773 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
774 return;
f6bcfd97
BP
775
776 wxSize sz( GetSize() );
777 wxPoint pt( GetPosition() );
3ca6a5f0
BP
778
779 int w = (sz.x - (ncols - 1) * m_hgap) / ncols;
780 int h = (sz.y - (nrows - 1) * m_vgap) / nrows;
f6bcfd97
BP
781
782 int x = pt.x;
783 for (int c = 0; c < ncols; c++)
784 {
785 int y = pt.y;
786 for (int r = 0; r < nrows; r++)
787 {
788 int i = r * ncols + c;
789 if (i < nitems)
790 {
791 wxNode *node = m_children.Nth( i );
792 wxASSERT( node );
3ca6a5f0 793
f6bcfd97
BP
794 SetItemBounds( (wxSizerItem*) node->Data(), x, y, w, h);
795 }
796 y = y + h + m_vgap;
797 }
798 x = x + w + m_hgap;
799 }
800}
801
802wxSize wxGridSizer::CalcMin()
803{
0ca5105b
VZ
804 int nitems, nrows, ncols;
805 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
806 return wxSize(10, 10);
f6bcfd97 807
4f469fb5 808 // Find the max width and height for any component
f6bcfd97
BP
809 int w = 0;
810 int h = 0;
3ca6a5f0 811
f6bcfd97
BP
812 wxNode *node = m_children.First();
813 while (node)
814 {
815 wxSizerItem *item = (wxSizerItem*)node->Data();
816 wxSize sz( item->CalcMin() );
817 w = wxMax( w, sz.x );
818 h = wxMax( h, sz.y );
3ca6a5f0 819
f6bcfd97
BP
820 node = node->Next();
821 }
3ca6a5f0 822
f6bcfd97
BP
823 return wxSize(ncols * w + (ncols-1) * m_hgap,
824 nrows * h + (nrows-1) * m_vgap);
825}
826
827void wxGridSizer::SetItemBounds( wxSizerItem *item, int x, int y, int w, int h )
828{
829 wxPoint pt( x,y );
830 wxSize sz( item->CalcMin() );
831 int flag = item->GetFlag();
832
833 if ((flag & wxEXPAND) || (flag & wxSHAPED))
834 {
835 sz = wxSize(w, h);
836 }
837 else
838 {
839 if (flag & wxALIGN_CENTER_HORIZONTAL)
840 {
841 pt.x = x + (w - sz.x) / 2;
842 }
843 else if (flag & wxALIGN_RIGHT)
844 {
845 pt.x = x + (w - sz.x);
846 }
3ca6a5f0 847
f6bcfd97
BP
848 if (flag & wxALIGN_CENTER_VERTICAL)
849 {
850 pt.y = y + (h - sz.y) / 2;
851 }
852 else if (flag & wxALIGN_BOTTOM)
853 {
854 pt.y = y + (h - sz.y);
855 }
856 }
3ca6a5f0 857
f6bcfd97
BP
858 item->SetDimension(pt, sz);
859}
860
861//---------------------------------------------------------------------------
862// wxFlexGridSizer
863//---------------------------------------------------------------------------
864
865wxFlexGridSizer::wxFlexGridSizer( int rows, int cols, int vgap, int hgap )
866 : wxGridSizer( rows, cols, vgap, hgap )
3ca6a5f0 867{
f6bcfd97
BP
868 m_rowHeights = (int*) NULL;
869 m_colWidths = (int*) NULL;
870}
871
872wxFlexGridSizer::wxFlexGridSizer( int cols, int vgap, int hgap )
3ca6a5f0
BP
873 : wxGridSizer( cols, vgap, hgap )
874{
f6bcfd97
BP
875 m_rowHeights = (int*) NULL;
876 m_colWidths = (int*) NULL;
877}
3ca6a5f0 878
f6bcfd97
BP
879wxFlexGridSizer::~wxFlexGridSizer()
880{
881 if (m_rowHeights)
882 delete[] m_rowHeights;
883 if (m_colWidths)
884 delete[] m_colWidths;
885}
886
887void wxFlexGridSizer::CreateArrays()
888{
889 if (m_rowHeights)
890 delete[] m_rowHeights;
891 if (m_colWidths)
892 delete[] m_colWidths;
3ca6a5f0 893
0ca5105b
VZ
894 int nitems, nrows, ncols;
895 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
896 {
897 m_rowHeights =
898 m_colWidths = NULL;
899 }
f6bcfd97
BP
900
901 m_rowHeights = new int[nrows];
902 m_colWidths = new int[ncols];
0ca5105b 903
f6bcfd97
BP
904 for (int col = 0; col < ncols; col++)
905 m_colWidths[ col ] = 0;
906 for (int row = 0; row < nrows; row++)
907 m_rowHeights[ row ] = 0;
908}
909
910void wxFlexGridSizer::RecalcSizes()
911{
0ca5105b
VZ
912 int nitems, nrows, ncols;
913 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
f6bcfd97
BP
914 return;
915
f6bcfd97
BP
916 wxSize sz( GetSize() );
917 wxSize minsz( CalcMin() );
918 wxPoint pt( GetPosition() );
919 int delta;
4f469fb5
RR
920 size_t idx,num;
921 wxArrayInt temp;
0ca5105b 922
4f469fb5
RR
923 // Transfer only those rows into temp which exist in the sizer
924 // ignoring the superflouus ones. This prevents a segfault when
925 // calling AddGrowableRow( 3 ) if the sizer only has 2 rows.
926 for (idx = 0; idx < m_growableRows.GetCount(); idx++)
927 if (m_growableRows[idx] < nrows)
928 temp.Add( m_growableRows[idx] );
929 num = temp.GetCount();
930
931 if ((num > 0) && (sz.y > minsz.y))
f6bcfd97 932 {
4f469fb5
RR
933 delta = (sz.y - minsz.y) / num;
934 for (idx = 0; idx < num; idx++)
935 m_rowHeights[ temp[idx] ] += delta;
f6bcfd97 936 }
3ca6a5f0 937
2e9c65bf 938 temp.Empty();
0ca5105b 939 // See above
4f469fb5
RR
940 for (idx = 0; idx < m_growableCols.GetCount(); idx++)
941 if (m_growableCols[idx] < ncols)
942 temp.Add( m_growableCols[idx] );
943 num = temp.GetCount();
0ca5105b 944
4f469fb5 945 if ((num > 0) && (sz.x > minsz.x))
f6bcfd97 946 {
4f469fb5
RR
947 delta = (sz.x - minsz.x) / num;
948 for (idx = 0; idx < num; idx++)
949 m_colWidths[ temp[idx] ] += delta;
f6bcfd97 950 }
3ca6a5f0 951
f6bcfd97
BP
952 sz = wxSize( pt.x + sz.x, pt.y + sz.y );
953
954 int x = pt.x;
955 for (int c = 0; c < ncols; c++)
956 {
957 int y = pt.y;
958 for (int r = 0; r < nrows; r++)
959 {
960 int i = r * ncols + c;
961 if (i < nitems)
962 {
963 wxNode *node = m_children.Nth( i );
964 wxASSERT( node );
3ca6a5f0 965
f6bcfd97
BP
966 int w = wxMax( 0, wxMin( m_colWidths[c], sz.x - x ) );
967 int h = wxMax( 0, wxMin( m_rowHeights[r], sz.y - y ) );
3ca6a5f0 968
f6bcfd97
BP
969 SetItemBounds( (wxSizerItem*) node->Data(), x, y, w, h);
970 }
971 y = y + m_rowHeights[r] + m_vgap;
972 }
973 x = x + m_colWidths[c] + m_hgap;
974 }
975}
976
977wxSize wxFlexGridSizer::CalcMin()
978{
0ca5105b
VZ
979 int nitems, nrows, ncols;
980 if ( (nitems = CalcRowsCols(nrows, ncols)) == 0 )
f6bcfd97
BP
981 return wxSize(10,10);
982
f6bcfd97 983 CreateArrays();
3ca6a5f0 984
f6bcfd97
BP
985 int i = 0;
986 wxNode *node = m_children.First();
987 while (node)
988 {
989 wxSizerItem *item = (wxSizerItem*)node->Data();
990 wxSize sz( item->CalcMin() );
0ca5105b
VZ
991 int row = i / ncols;
992 int col = i % ncols;
f6bcfd97
BP
993 m_rowHeights[ row ] = wxMax( sz.y, m_rowHeights[ row ] );
994 m_colWidths[ col ] = wxMax( sz.x, m_colWidths[ col ] );
3ca6a5f0 995
f6bcfd97
BP
996 node = node->Next();
997 i++;
998 }
3ca6a5f0 999
f6bcfd97 1000 int width = 0;
0ca5105b 1001 for (int col = 0; col < ncols; col++)
f6bcfd97 1002 width += m_colWidths[ col ];
3ca6a5f0 1003
f6bcfd97 1004 int height = 0;
0ca5105b 1005 for (int row = 0; row < nrows; row++)
f6bcfd97 1006 height += m_rowHeights[ row ];
3ca6a5f0 1007
f6bcfd97
BP
1008 return wxSize( width + (ncols-1) * m_hgap,
1009 height + (nrows-1) * m_vgap);
1010}
1011
1012void wxFlexGridSizer::AddGrowableRow( size_t idx )
1013{
1014 m_growableRows.Add( idx );
1015}
1016
3ca6a5f0 1017void wxFlexGridSizer::RemoveGrowableRow( size_t WXUNUSED(idx) )
f6bcfd97
BP
1018{
1019}
1020
1021void wxFlexGridSizer::AddGrowableCol( size_t idx )
1022{
1023 m_growableCols.Add( idx );
1024}
1025
3ca6a5f0 1026void wxFlexGridSizer::RemoveGrowableCol( size_t WXUNUSED(idx) )
f6bcfd97
BP
1027{
1028}
1029
c62ac5b6 1030//---------------------------------------------------------------------------
92afa2b1 1031// wxBoxSizer
61d514bb
RR
1032//---------------------------------------------------------------------------
1033
92afa2b1 1034wxBoxSizer::wxBoxSizer( int orient )
61d514bb
RR
1035{
1036 m_orient = orient;
1037}
1038
92afa2b1 1039void wxBoxSizer::RecalcSizes()
61d514bb
RR
1040{
1041 if (m_children.GetCount() == 0)
61d514bb 1042 return;
0c0d686f 1043
61d514bb
RR
1044 int delta = 0;
1045 int extra = 0;
1046 if (m_stretchable)
1047 {
1048 if (m_orient == wxHORIZONTAL)
1049 {
1050 delta = (m_size.x - m_fixedWidth) / m_stretchable;
1051 extra = (m_size.x - m_fixedWidth) % m_stretchable;
3ca6a5f0
BP
1052 }
1053 else
1054 {
61d514bb
RR
1055 delta = (m_size.y - m_fixedHeight) / m_stretchable;
1056 extra = (m_size.y - m_fixedHeight) % m_stretchable;
3ca6a5f0 1057 }
61d514bb 1058 }
0c0d686f 1059
61d514bb 1060 wxPoint pt( m_position );
0c0d686f 1061
61d514bb
RR
1062 wxNode *node = m_children.GetFirst();
1063 while (node)
1064 {
3417c2cd 1065 wxSizerItem *item = (wxSizerItem*) node->Data();
2b5f62a0 1066 if (item->IsShown())
3ca6a5f0 1067 {
2b5f62a0 1068 int weight = 1;
3ca6a5f0 1069 if (item->GetOption())
2b5f62a0 1070 weight = item->GetOption();
3ca6a5f0 1071
2b5f62a0 1072 wxSize size( item->CalcMin() );
3ca6a5f0 1073
2b5f62a0 1074 if (m_orient == wxVERTICAL)
3ca6a5f0 1075 {
2b5f62a0
VZ
1076 wxCoord height = size.y;
1077 if (item->GetOption())
1078 {
1079 height = (delta * weight) + extra;
1080 extra = 0; // only the first item will get the remainder as extra size
1081 }
1082
1083 wxPoint child_pos( pt );
1084 wxSize child_size( wxSize( size.x, height) );
1085
1086 if (item->GetFlag() & (wxEXPAND | wxSHAPED))
1087 child_size.x = m_size.x;
1088 else if (item->GetFlag() & wxALIGN_RIGHT)
1089 child_pos.x += m_size.x - size.x;
1090 else if (item->GetFlag() & (wxCENTER | wxALIGN_CENTER_HORIZONTAL))
1091 // XXX wxCENTER is added for backward compatibility;
1092 // wxALIGN_CENTER should be used in new code
1093 child_pos.x += (m_size.x - size.x) / 2;
1094
1095 item->SetDimension( child_pos, child_size );
1096
1097 pt.y += height;
1098 }
1099 else
1100 {
1101 wxCoord width = size.x;
1102 if (item->GetOption())
1103 {
1104 width = (delta * weight) + extra;
1105 extra = 0; // only the first item will get the remainder as extra size
1106 }
1107
1108 wxPoint child_pos( pt );
1109 wxSize child_size( wxSize(width, size.y) );
1110
1111 if (item->GetFlag() & (wxEXPAND | wxSHAPED))
1112 child_size.y = m_size.y;
1113 else if (item->GetFlag() & wxALIGN_BOTTOM)
1114 child_pos.y += m_size.y - size.y;
1115 else if (item->GetFlag() & (wxCENTER | wxALIGN_CENTER_VERTICAL))
1116 // XXX wxCENTER is added for backward compatibility;
1117 // wxALIGN_CENTER should be used in new code
1118 child_pos.y += (m_size.y - size.y) / 2;
1119
1120 item->SetDimension( child_pos, child_size );
1121
1122 pt.x += width;
3ca6a5f0 1123 }
3ca6a5f0
BP
1124 }
1125
1126 node = node->Next();
61d514bb
RR
1127 }
1128}
1129
92afa2b1 1130wxSize wxBoxSizer::CalcMin()
61d514bb
RR
1131{
1132 if (m_children.GetCount() == 0)
c7a9fa36 1133 return wxSize(10,10);
0c0d686f 1134
61d514bb
RR
1135 m_stretchable = 0;
1136 m_minWidth = 0;
1137 m_minHeight = 0;
1138 m_fixedWidth = 0;
1139 m_fixedHeight = 0;
0c0d686f 1140
f98de448
RD
1141 // Find how long each stretch unit needs to be
1142 int stretchSize = 1;
61d514bb
RR
1143 wxNode *node = m_children.GetFirst();
1144 while (node)
f98de448
RD
1145 {
1146 wxSizerItem *item = (wxSizerItem*) node->Data();
2b5f62a0 1147 if (item->IsShown() && item->GetOption() != 0)
f98de448
RD
1148 {
1149 int stretch = item->GetOption();
1150 wxSize size( item->CalcMin() );
1151 int sizePerStretch;
1152 // Integer division rounded up is (a + b - 1) / b
1153 if (m_orient == wxHORIZONTAL)
1154 sizePerStretch = ( size.x + stretch - 1 ) / stretch;
1155 else
1156 sizePerStretch = ( size.y + stretch - 1 ) / stretch;
1157 if (sizePerStretch > stretchSize)
1158 stretchSize = sizePerStretch;
1159 }
1160 node = node->Next();
1161 }
4f469fb5
RR
1162 // Calculate overall minimum size
1163 node = m_children.GetFirst();
f98de448 1164 while (node)
61d514bb 1165 {
3417c2cd 1166 wxSizerItem *item = (wxSizerItem*) node->Data();
2b5f62a0 1167 if (item->IsShown())
f98de448 1168 {
2b5f62a0 1169 m_stretchable += item->GetOption();
3ca6a5f0 1170
2b5f62a0
VZ
1171 wxSize size( item->CalcMin() );
1172 if (item->GetOption() != 0)
1173 {
1174 if (m_orient == wxHORIZONTAL)
1175 size.x = stretchSize * item->GetOption();
1176 else
1177 size.y = stretchSize * item->GetOption();
1178 }
3ca6a5f0 1179
2b5f62a0 1180 if (m_orient == wxHORIZONTAL)
3ca6a5f0 1181 {
2b5f62a0
VZ
1182 m_minWidth += size.x;
1183 m_minHeight = wxMax( m_minHeight, size.y );
3ca6a5f0
BP
1184 }
1185 else
33ac7e6f 1186 {
2b5f62a0
VZ
1187 m_minHeight += size.y;
1188 m_minWidth = wxMax( m_minWidth, size.x );
3ca6a5f0 1189 }
3ca6a5f0 1190
2b5f62a0
VZ
1191 if (item->GetOption() == 0)
1192 {
1193 if (m_orient == wxVERTICAL)
1194 {
1195 m_fixedHeight += size.y;
1196 m_fixedWidth = wxMax( m_fixedWidth, size.x );
1197 }
1198 else
1199 {
1200 m_fixedWidth += size.x;
1201 m_fixedHeight = wxMax( m_fixedHeight, size.y );
1202 }
1203 }
1204 }
3ca6a5f0 1205 node = node->Next();
61d514bb 1206 }
0c0d686f 1207
61d514bb
RR
1208 return wxSize( m_minWidth, m_minHeight );
1209}
27ea1d8a
RR
1210
1211//---------------------------------------------------------------------------
1212// wxStaticBoxSizer
1213//---------------------------------------------------------------------------
1214
1e6feb95
VZ
1215#if wxUSE_STATBOX
1216
27ea1d8a 1217wxStaticBoxSizer::wxStaticBoxSizer( wxStaticBox *box, int orient )
84028727 1218 : wxBoxSizer( orient )
27ea1d8a 1219{
223d09f6 1220 wxASSERT_MSG( box, wxT("wxStaticBoxSizer needs a static box") );
0c0d686f 1221
27ea1d8a
RR
1222 m_staticBox = box;
1223}
0c0d686f 1224
84028727
VZ
1225static void GetStaticBoxBorders(wxStaticBox *box,
1226 int *borderTop, int *borderOther)
1227{
1228 // this has to be done platform by platform as there is no way to
1229 // guess the thickness of a wxStaticBox border
1230#ifdef __WXGTK__
1231 if ( box->GetLabel().IsEmpty() )
1232 *borderTop = 5;
1233 else
1234#endif // __WXGTK__
1235 *borderTop = 15;
4f469fb5 1236 (void)box;
84028727
VZ
1237 *borderOther = 5;
1238}
1239
27ea1d8a
RR
1240void wxStaticBoxSizer::RecalcSizes()
1241{
84028727
VZ
1242 int top_border, other_border;
1243 GetStaticBoxBorders(m_staticBox, &top_border, &other_border);
27ea1d8a
RR
1244
1245 m_staticBox->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
0c0d686f 1246
27ea1d8a
RR
1247 wxPoint old_pos( m_position );
1248 m_position.x += other_border;
1249 m_position.y += top_border;
1250 wxSize old_size( m_size );
1251 m_size.x -= 2*other_border;
1252 m_size.y -= top_border + other_border;
0c0d686f 1253
27ea1d8a 1254 wxBoxSizer::RecalcSizes();
0c0d686f 1255
27ea1d8a
RR
1256 m_position = old_pos;
1257 m_size = old_size;
1258}
1259
1260wxSize wxStaticBoxSizer::CalcMin()
1261{
84028727
VZ
1262 int top_border, other_border;
1263 GetStaticBoxBorders(m_staticBox, &top_border, &other_border);
0c0d686f 1264
27ea1d8a 1265 wxSize ret( wxBoxSizer::CalcMin() );
cae31b8b 1266 ret.x += 2*other_border;
27ea1d8a 1267 ret.y += other_border + top_border;
0c0d686f 1268
27ea1d8a
RR
1269 return ret;
1270}
83edc0a5 1271
1e6feb95
VZ
1272#endif // wxUSE_STATBOX
1273
83edc0a5
RR
1274//---------------------------------------------------------------------------
1275// wxNotebookSizer
1276//---------------------------------------------------------------------------
1277
60be2f47
VS
1278#if wxUSE_NOTEBOOK
1279
83edc0a5
RR
1280wxNotebookSizer::wxNotebookSizer( wxNotebook *nb )
1281{
1282 wxASSERT_MSG( nb, wxT("wxNotebookSizer needs a notebook") );
3ca6a5f0 1283
83edc0a5
RR
1284 m_notebook = nb;
1285}
1286
1287void wxNotebookSizer::RecalcSizes()
1288{
1289 m_notebook->SetSize( m_position.x, m_position.y, m_size.x, m_size.y );
1290}
1291
1292wxSize wxNotebookSizer::CalcMin()
1293{
1e6feb95
VZ
1294 wxSize sizeBorder = m_notebook->CalcSizeFromPage(wxSize(0, 0));
1295
1296 sizeBorder.x += 5;
1297 sizeBorder.y += 5;
3ca6a5f0 1298
83edc0a5 1299 if (m_notebook->GetChildren().GetCount() == 0)
1e6feb95
VZ
1300 {
1301 return wxSize(sizeBorder.x + 10, sizeBorder.y + 10);
1302 }
83edc0a5
RR
1303
1304 int maxX = 0;
1305 int maxY = 0;
1306
1307 wxWindowList::Node *node = m_notebook->GetChildren().GetFirst();
1308 while (node)
1309 {
1310 wxWindow *item = node->GetData();
3ca6a5f0
BP
1311 wxSizer *itemsizer = item->GetSizer();
1312
1313 if (itemsizer)
1314 {
83edc0a5 1315 wxSize subsize( itemsizer->CalcMin() );
83edc0a5 1316
1e6feb95
VZ
1317 if (subsize.x > maxX)
1318 maxX = subsize.x;
1319 if (subsize.y > maxY)
1320 maxY = subsize.y;
3ca6a5f0
BP
1321 }
1322
1323 node = node->GetNext();
83edc0a5
RR
1324 }
1325
1e6feb95 1326 return wxSize( maxX, maxY ) + sizeBorder;
83edc0a5
RR
1327}
1328
60be2f47 1329#endif // wxUSE_NOTEBOOK
34c3ffca
RL
1330
1331// vi:sts=4:sw=4:et