]> git.saurik.com Git - wxWidgets.git/blob - src/xrc/xh_sizer.cpp
avoiding reentrancy problems and congestion
[wxWidgets.git] / src / xrc / xh_sizer.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/xrc/xh_sizer.cpp
3 // Purpose: XRC resource for wxBoxSizer
4 // Author: Vaclav Slavik
5 // Created: 2000/03/21
6 // RCS-ID: $Id$
7 // Copyright: (c) 2000 Vaclav Slavik
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_XRC
19
20 #include "wx/xrc/xh_sizer.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/log.h"
24 #include "wx/panel.h"
25 #include "wx/statbox.h"
26 #include "wx/sizer.h"
27 #include "wx/frame.h"
28 #include "wx/dialog.h"
29 #include "wx/button.h"
30 #include "wx/scrolwin.h"
31 #endif
32
33 #include "wx/gbsizer.h"
34 #include "wx/wrapsizer.h"
35 #include "wx/notebook.h"
36 #include "wx/tokenzr.h"
37
38 #include "wx/xml/xml.h"
39
40 //-----------------------------------------------------------------------------
41 // wxSizerXmlHandler
42 //-----------------------------------------------------------------------------
43
44 IMPLEMENT_DYNAMIC_CLASS(wxSizerXmlHandler, wxXmlResourceHandler)
45
46 wxSizerXmlHandler::wxSizerXmlHandler()
47 :wxXmlResourceHandler(),
48 m_isInside(false),
49 m_isGBS(false),
50 m_parentSizer(NULL)
51 {
52 XRC_ADD_STYLE(wxHORIZONTAL);
53 XRC_ADD_STYLE(wxVERTICAL);
54
55 // and flags
56 XRC_ADD_STYLE(wxLEFT);
57 XRC_ADD_STYLE(wxRIGHT);
58 XRC_ADD_STYLE(wxTOP);
59 XRC_ADD_STYLE(wxBOTTOM);
60 XRC_ADD_STYLE(wxNORTH);
61 XRC_ADD_STYLE(wxSOUTH);
62 XRC_ADD_STYLE(wxEAST);
63 XRC_ADD_STYLE(wxWEST);
64 XRC_ADD_STYLE(wxALL);
65
66 XRC_ADD_STYLE(wxGROW);
67 XRC_ADD_STYLE(wxEXPAND);
68 XRC_ADD_STYLE(wxSHAPED);
69 XRC_ADD_STYLE(wxSTRETCH_NOT);
70
71 XRC_ADD_STYLE(wxALIGN_CENTER);
72 XRC_ADD_STYLE(wxALIGN_CENTRE);
73 XRC_ADD_STYLE(wxALIGN_LEFT);
74 XRC_ADD_STYLE(wxALIGN_TOP);
75 XRC_ADD_STYLE(wxALIGN_RIGHT);
76 XRC_ADD_STYLE(wxALIGN_BOTTOM);
77 XRC_ADD_STYLE(wxALIGN_CENTER_HORIZONTAL);
78 XRC_ADD_STYLE(wxALIGN_CENTRE_HORIZONTAL);
79 XRC_ADD_STYLE(wxALIGN_CENTER_VERTICAL);
80 XRC_ADD_STYLE(wxALIGN_CENTRE_VERTICAL);
81
82 XRC_ADD_STYLE(wxFIXED_MINSIZE);
83 XRC_ADD_STYLE(wxRESERVE_SPACE_EVEN_IF_HIDDEN);
84
85 // this flag doesn't do anything any more but we can just ignore its
86 // occurrences in the old resource files instead of raising a fuss because
87 // of it
88 AddStyle("wxADJUST_MINSIZE", 0);
89
90 // wxWrapSizer-specific flags
91 XRC_ADD_STYLE(wxEXTEND_LAST_ON_EACH_LINE);
92 XRC_ADD_STYLE(wxREMOVE_LEADING_SPACES);
93 }
94
95
96
97 bool wxSizerXmlHandler::CanHandle(wxXmlNode *node)
98 {
99 return ( (!m_isInside && IsSizerNode(node)) ||
100 (m_isInside && IsOfClass(node, wxT("sizeritem"))) ||
101 (m_isInside && IsOfClass(node, wxT("spacer")))
102 );
103 }
104
105
106 wxObject* wxSizerXmlHandler::DoCreateResource()
107 {
108 if (m_class == wxT("sizeritem"))
109 return Handle_sizeritem();
110
111 else if (m_class == wxT("spacer"))
112 return Handle_spacer();
113
114 else
115 return Handle_sizer();
116 }
117
118
119 wxSizer* wxSizerXmlHandler::DoCreateSizer(const wxString& name)
120 {
121 if (name == wxT("wxBoxSizer"))
122 return Handle_wxBoxSizer();
123 #if wxUSE_STATBOX
124 else if (name == wxT("wxStaticBoxSizer"))
125 return Handle_wxStaticBoxSizer();
126 #endif
127 else if (name == wxT("wxGridSizer"))
128 {
129 if ( !ValidateGridSizerChildren() )
130 return NULL;
131 return Handle_wxGridSizer();
132 }
133 else if (name == wxT("wxFlexGridSizer"))
134 {
135 return Handle_wxFlexGridSizer();
136 }
137 else if (name == wxT("wxGridBagSizer"))
138 {
139 return Handle_wxGridBagSizer();
140 }
141 else if (name == wxT("wxWrapSizer"))
142 {
143 return Handle_wxWrapSizer();
144 }
145
146 ReportError(wxString::Format("unknown sizer class \"%s\"", name));
147 return NULL;
148 }
149
150
151
152 bool wxSizerXmlHandler::IsSizerNode(wxXmlNode *node) const
153 {
154 return (IsOfClass(node, wxT("wxBoxSizer"))) ||
155 (IsOfClass(node, wxT("wxStaticBoxSizer"))) ||
156 (IsOfClass(node, wxT("wxGridSizer"))) ||
157 (IsOfClass(node, wxT("wxFlexGridSizer"))) ||
158 (IsOfClass(node, wxT("wxGridBagSizer"))) ||
159 (IsOfClass(node, wxT("wxWrapSizer")));
160 }
161
162
163 wxObject* wxSizerXmlHandler::Handle_sizeritem()
164 {
165 // find the item to be managed by this sizeritem
166 wxXmlNode *n = GetParamNode(wxT("object"));
167 if ( !n )
168 n = GetParamNode(wxT("object_ref"));
169
170 // did we find one?
171 if (n)
172 {
173 // create a sizer item for it
174 wxSizerItem* sitem = MakeSizerItem();
175
176 // now fetch the item to be managed
177 bool old_gbs = m_isGBS;
178 bool old_ins = m_isInside;
179 wxSizer *old_par = m_parentSizer;
180 m_isInside = false;
181 if (!IsSizerNode(n)) m_parentSizer = NULL;
182 wxObject *item = CreateResFromNode(n, m_parent, NULL);
183 m_isInside = old_ins;
184 m_parentSizer = old_par;
185 m_isGBS = old_gbs;
186
187 // and figure out what type it is
188 wxSizer *sizer = wxDynamicCast(item, wxSizer);
189 wxWindow *wnd = wxDynamicCast(item, wxWindow);
190
191 if (sizer)
192 sitem->AssignSizer(sizer);
193 else if (wnd)
194 sitem->AssignWindow(wnd);
195 else
196 ReportError(n, "unexpected item in sizer");
197
198 // finally, set other wxSizerItem attributes
199 SetSizerItemAttributes(sitem);
200
201 AddSizerItem(sitem);
202 return item;
203 }
204 else /*n == NULL*/
205 {
206 ReportError("no window/sizer/spacer within sizeritem object");
207 return NULL;
208 }
209 }
210
211
212 wxObject* wxSizerXmlHandler::Handle_spacer()
213 {
214 if ( !m_parentSizer )
215 {
216 ReportError("spacer only allowed inside a sizer");
217 return NULL;
218 }
219
220 wxSizerItem* sitem = MakeSizerItem();
221 SetSizerItemAttributes(sitem);
222 sitem->AssignSpacer(GetSize());
223 AddSizerItem(sitem);
224 return NULL;
225 }
226
227
228 wxObject* wxSizerXmlHandler::Handle_sizer()
229 {
230 wxXmlNode *parentNode = m_node->GetParent();
231
232 if ( !m_parentSizer &&
233 (!parentNode || parentNode->GetType() != wxXML_ELEMENT_NODE ||
234 !m_parentAsWindow) )
235 {
236 ReportError("sizer must have a window parent");
237 return NULL;
238 }
239
240 // Create the sizer of the appropriate class.
241 wxSizer * const sizer = DoCreateSizer(m_class);
242
243 // creation of sizer failed for some (already reported) reason, so exit:
244 if ( !sizer )
245 return NULL;
246
247 wxSize minsize = GetSize(wxT("minsize"));
248 if (!(minsize == wxDefaultSize))
249 sizer->SetMinSize(minsize);
250
251 // save state
252 wxSizer *old_par = m_parentSizer;
253 bool old_ins = m_isInside;
254
255 // set new state
256 m_parentSizer = sizer;
257 m_isInside = true;
258 m_isGBS = (m_class == wxT("wxGridBagSizer"));
259
260 wxObject* parent = m_parent;
261 #if wxUSE_STATBOX
262 // wxStaticBoxSizer's child controls should be parented by the box itself,
263 // not its parent.
264 wxStaticBoxSizer* const stsizer = wxDynamicCast(sizer, wxStaticBoxSizer);
265 if ( stsizer )
266 parent = stsizer->GetStaticBox();
267 #endif // wxUSE_STATBOX
268
269 CreateChildren(parent, true/*only this handler*/);
270
271 // set growable rows and cols for sizers which support this
272 if ( wxFlexGridSizer *flexsizer = wxDynamicCast(sizer, wxFlexGridSizer) )
273 {
274 SetFlexibleMode(flexsizer);
275 SetGrowables(flexsizer, wxT("growablerows"), true);
276 SetGrowables(flexsizer, wxT("growablecols"), false);
277 }
278
279 // restore state
280 m_isInside = old_ins;
281 m_parentSizer = old_par;
282
283 if (m_parentSizer == NULL) // setup window:
284 {
285 m_parentAsWindow->SetSizer(sizer);
286
287 wxXmlNode *nd = m_node;
288 m_node = parentNode;
289 if (GetSize() == wxDefaultSize)
290 {
291 if ( wxDynamicCast(m_parentAsWindow, wxScrolledWindow) != NULL )
292 {
293 sizer->FitInside(m_parentAsWindow);
294 }
295 else
296 {
297 sizer->Fit(m_parentAsWindow);
298 }
299 }
300 m_node = nd;
301
302 if (m_parentAsWindow->IsTopLevel())
303 {
304 sizer->SetSizeHints(m_parentAsWindow);
305 }
306 }
307
308 return sizer;
309 }
310
311
312 wxSizer* wxSizerXmlHandler::Handle_wxBoxSizer()
313 {
314 return new wxBoxSizer(GetStyle(wxT("orient"), wxHORIZONTAL));
315 }
316
317 #if wxUSE_STATBOX
318 wxSizer* wxSizerXmlHandler::Handle_wxStaticBoxSizer()
319 {
320 return new wxStaticBoxSizer(
321 new wxStaticBox(m_parentAsWindow,
322 GetID(),
323 GetText(wxT("label")),
324 wxDefaultPosition, wxDefaultSize,
325 0/*style*/,
326 GetName()),
327 GetStyle(wxT("orient"), wxHORIZONTAL));
328 }
329 #endif // wxUSE_STATBOX
330
331 wxSizer* wxSizerXmlHandler::Handle_wxGridSizer()
332 {
333 return new wxGridSizer(GetLong(wxT("rows")), GetLong(wxT("cols")),
334 GetDimension(wxT("vgap")), GetDimension(wxT("hgap")));
335 }
336
337
338 wxFlexGridSizer* wxSizerXmlHandler::Handle_wxFlexGridSizer()
339 {
340 if ( !ValidateGridSizerChildren() )
341 return NULL;
342 return new wxFlexGridSizer(GetLong(wxT("rows")), GetLong(wxT("cols")),
343 GetDimension(wxT("vgap")), GetDimension(wxT("hgap")));
344 }
345
346
347 wxGridBagSizer* wxSizerXmlHandler::Handle_wxGridBagSizer()
348 {
349 if ( !ValidateGridSizerChildren() )
350 return NULL;
351 return new wxGridBagSizer(GetDimension(wxT("vgap")), GetDimension(wxT("hgap")));
352 }
353
354 wxSizer* wxSizerXmlHandler::Handle_wxWrapSizer()
355 {
356 wxWrapSizer *sizer = new wxWrapSizer(GetStyle("orient"), GetStyle("flag"));
357 return sizer;
358 }
359
360
361 bool wxSizerXmlHandler::ValidateGridSizerChildren()
362 {
363 int rows = GetLong("rows");
364 int cols = GetLong("cols");
365
366 if ( rows && cols )
367 {
368 // fixed number of cells, need to verify children count
369 int children = 0;
370 for ( wxXmlNode *n = m_node->GetChildren(); n; n = n->GetNext() )
371 {
372 if ( n->GetType() == wxXML_ELEMENT_NODE &&
373 (n->GetName() == "object" || n->GetName() == "object_ref") )
374 {
375 children++;
376 }
377 }
378
379 if ( children > rows * cols )
380 {
381 ReportError
382 (
383 wxString::Format
384 (
385 "too many children in grid sizer: %d > %d x %d"
386 " (consider omitting the number of rows or columns)",
387 children,
388 cols,
389 rows
390 )
391 );
392 return false;
393 }
394 }
395
396 return true;
397 }
398
399
400 void wxSizerXmlHandler::SetFlexibleMode(wxFlexGridSizer* fsizer)
401 {
402 if (HasParam(wxT("flexibledirection")))
403 {
404 wxString dir = GetParamValue(wxT("flexibledirection"));
405
406 if (dir == wxT("wxVERTICAL"))
407 fsizer->SetFlexibleDirection(wxVERTICAL);
408 else if (dir == wxT("wxHORIZONTAL"))
409 fsizer->SetFlexibleDirection(wxHORIZONTAL);
410 else if (dir == wxT("wxBOTH"))
411 fsizer->SetFlexibleDirection(wxBOTH);
412 else
413 {
414 ReportParamError
415 (
416 wxT("flexibledirection"),
417 wxString::Format("unknown direction \"%s\"", dir)
418 );
419 }
420 }
421
422 if (HasParam(wxT("nonflexiblegrowmode")))
423 {
424 wxString mode = GetParamValue(wxT("nonflexiblegrowmode"));
425
426 if (mode == wxT("wxFLEX_GROWMODE_NONE"))
427 fsizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_NONE);
428 else if (mode == wxT("wxFLEX_GROWMODE_SPECIFIED"))
429 fsizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
430 else if (mode == wxT("wxFLEX_GROWMODE_ALL"))
431 fsizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_ALL);
432 else
433 {
434 ReportParamError
435 (
436 wxT("nonflexiblegrowmode"),
437 wxString::Format("unknown grow mode \"%s\"", mode)
438 );
439 }
440 }
441 }
442
443
444 void wxSizerXmlHandler::SetGrowables(wxFlexGridSizer* sizer,
445 const wxChar* param,
446 bool rows)
447 {
448 int nrows, ncols;
449 sizer->CalcRowsCols(nrows, ncols);
450 const int nslots = rows ? nrows : ncols;
451
452 wxStringTokenizer tkn;
453 tkn.SetString(GetParamValue(param), wxT(","));
454
455 while (tkn.HasMoreTokens())
456 {
457 wxString propStr;
458 wxString idxStr = tkn.GetNextToken().BeforeFirst(wxT(':'), &propStr);
459
460 unsigned long li;
461 if (!idxStr.ToULong(&li))
462 {
463 ReportParamError
464 (
465 param,
466 "value must be a comma-separated list of numbers"
467 );
468 break;
469 }
470
471 unsigned long lp = 0;
472 if (!propStr.empty())
473 {
474 if (!propStr.ToULong(&lp))
475 {
476 ReportParamError
477 (
478 param,
479 "value must be a comma-separated list of numbers"
480 );
481 break;
482 }
483 }
484
485 const int n = static_cast<int>(li);
486 if ( n >= nslots )
487 {
488 ReportParamError
489 (
490 param,
491 wxString::Format
492 (
493 "invalid %s index %d: must be less than %d",
494 rows ? "row" : "column",
495 n,
496 nslots
497 )
498 );
499
500 // ignore incorrect value, still try to process the rest
501 continue;
502 }
503
504 if (rows)
505 sizer->AddGrowableRow(n, static_cast<int>(lp));
506 else
507 sizer->AddGrowableCol(n, static_cast<int>(lp));
508 }
509 }
510
511
512 wxGBPosition wxSizerXmlHandler::GetGBPos(const wxString& param)
513 {
514 wxSize sz = GetSize(param);
515 if (sz.x < 0) sz.x = 0;
516 if (sz.y < 0) sz.y = 0;
517 return wxGBPosition(sz.x, sz.y);
518 }
519
520 wxGBSpan wxSizerXmlHandler::GetGBSpan(const wxString& param)
521 {
522 wxSize sz = GetSize(param);
523 if (sz.x < 1) sz.x = 1;
524 if (sz.y < 1) sz.y = 1;
525 return wxGBSpan(sz.x, sz.y);
526 }
527
528
529
530 wxSizerItem* wxSizerXmlHandler::MakeSizerItem()
531 {
532 if (m_isGBS)
533 return new wxGBSizerItem();
534 else
535 return new wxSizerItem();
536 }
537
538 void wxSizerXmlHandler::SetSizerItemAttributes(wxSizerItem* sitem)
539 {
540 sitem->SetProportion(GetLong(wxT("option"))); // Should this check for "proportion" too?
541 sitem->SetFlag(GetStyle(wxT("flag")));
542 sitem->SetBorder(GetDimension(wxT("border")));
543 wxSize sz = GetSize(wxT("minsize"));
544 if (!(sz == wxDefaultSize))
545 sitem->SetMinSize(sz);
546 sz = GetSize(wxT("ratio"));
547 if (!(sz == wxDefaultSize))
548 sitem->SetRatio(sz);
549
550 if (m_isGBS)
551 {
552 wxGBSizerItem* gbsitem = (wxGBSizerItem*)sitem;
553 gbsitem->SetPos(GetGBPos(wxT("cellpos")));
554 gbsitem->SetSpan(GetGBSpan(wxT("cellspan")));
555 }
556
557 // record the id of the item, if any, for use by XRCSIZERITEM()
558 sitem->SetId(GetID());
559 }
560
561 void wxSizerXmlHandler::AddSizerItem(wxSizerItem* sitem)
562 {
563 if (m_isGBS)
564 ((wxGridBagSizer*)m_parentSizer)->Add((wxGBSizerItem*)sitem);
565 else
566 m_parentSizer->Add(sitem);
567 }
568
569
570
571 //-----------------------------------------------------------------------------
572 // wxStdDialogButtonSizerXmlHandler
573 //-----------------------------------------------------------------------------
574 #if wxUSE_BUTTON
575
576 IMPLEMENT_DYNAMIC_CLASS(wxStdDialogButtonSizerXmlHandler, wxXmlResourceHandler)
577
578 wxStdDialogButtonSizerXmlHandler::wxStdDialogButtonSizerXmlHandler()
579 : m_isInside(false), m_parentSizer(NULL)
580 {
581 }
582
583 wxObject *wxStdDialogButtonSizerXmlHandler::DoCreateResource()
584 {
585 if (m_class == wxT("wxStdDialogButtonSizer"))
586 {
587 wxASSERT( !m_parentSizer );
588
589 wxSizer *s = m_parentSizer = new wxStdDialogButtonSizer;
590 m_isInside = true;
591
592 CreateChildren(m_parent, true/*only this handler*/);
593
594 m_parentSizer->Realize();
595
596 m_isInside = false;
597 m_parentSizer = NULL;
598
599 return s;
600 }
601 else // m_class == "button"
602 {
603 wxASSERT( m_parentSizer );
604
605 // find the item to be managed by this sizeritem
606 wxXmlNode *n = GetParamNode(wxT("object"));
607 if ( !n )
608 n = GetParamNode(wxT("object_ref"));
609
610 // did we find one?
611 if (n)
612 {
613 wxObject *item = CreateResFromNode(n, m_parent, NULL);
614 wxButton *button = wxDynamicCast(item, wxButton);
615
616 if (button)
617 m_parentSizer->AddButton(button);
618 else
619 ReportError(n, "expected wxButton");
620
621 return item;
622 }
623 else /*n == NULL*/
624 {
625 ReportError("no button within wxStdDialogButtonSizer");
626 return NULL;
627 }
628 }
629 }
630
631 bool wxStdDialogButtonSizerXmlHandler::CanHandle(wxXmlNode *node)
632 {
633 return (!m_isInside && IsOfClass(node, wxT("wxStdDialogButtonSizer"))) ||
634 (m_isInside && IsOfClass(node, wxT("button")));
635 }
636 #endif // wxUSE_BUTTON
637
638 #endif // wxUSE_XRC