Fix crash in XRC ID range support code.
[wxWidgets.git] / src / xrc / xmlres.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/xrc/xmlres.cpp
3 // Purpose: XRC resources
4 // Author: Vaclav Slavik
5 // Created: 2000/03/05
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/xmlres.h"
21
22 #ifndef WX_PRECOMP
23 #include "wx/intl.h"
24 #include "wx/log.h"
25 #include "wx/panel.h"
26 #include "wx/frame.h"
27 #include "wx/dialog.h"
28 #include "wx/settings.h"
29 #include "wx/bitmap.h"
30 #include "wx/image.h"
31 #include "wx/module.h"
32 #include "wx/wxcrtvararg.h"
33 #endif
34
35 #ifndef __WXWINCE__
36 #include <locale.h>
37 #endif
38
39 #include "wx/vector.h"
40 #include "wx/wfstream.h"
41 #include "wx/filesys.h"
42 #include "wx/filename.h"
43 #include "wx/tokenzr.h"
44 #include "wx/fontenum.h"
45 #include "wx/fontmap.h"
46 #include "wx/artprov.h"
47 #include "wx/imaglist.h"
48 #include "wx/dir.h"
49 #include "wx/xml/xml.h"
50 #include "wx/hashset.h"
51
52
53 class wxXmlResourceDataRecord
54 {
55 public:
56 wxXmlResourceDataRecord() : Doc(NULL) {
57 #if wxUSE_DATETIME
58 Time = wxDateTime::Now();
59 #endif
60 }
61 ~wxXmlResourceDataRecord() {delete Doc;}
62
63 wxString File;
64 wxXmlDocument *Doc;
65 #if wxUSE_DATETIME
66 wxDateTime Time;
67 #endif
68 };
69
70 class wxXmlResourceDataRecords : public wxVector<wxXmlResourceDataRecord*>
71 {
72 // this is a class so that it can be forward-declared
73 };
74
75 WX_DECLARE_HASH_SET(int, wxIntegerHash, wxIntegerEqual, wxHashSetInt);
76
77 class wxIdRange // Holds data for a particular rangename
78 {
79 protected:
80 wxIdRange(const wxXmlNode* node,
81 const wxString& rname,
82 const wxString& startno,
83 const wxString& rsize);
84
85 // Note the existence of an item within the range
86 void NoteItem(const wxXmlNode* node, const wxString& item);
87
88 // The manager is telling us that it's finished adding items
89 void Finalise(const wxXmlNode* node);
90
91 wxString GetName() const { return m_name; }
92 bool IsFinalised() const { return m_finalised; }
93
94 const wxString m_name;
95 int m_start;
96 int m_end;
97 unsigned int m_size;
98 bool m_item_end_found;
99 bool m_finalised;
100 wxHashSetInt m_indices;
101
102 friend class wxIdRangeManager;
103 };
104
105 class wxIdRangeManager
106 {
107 public:
108 ~wxIdRangeManager();
109 // Gets the global resources object or creates one if none exists.
110 static wxIdRangeManager *Get();
111
112 // Sets the global resources object and returns a pointer to the previous
113 // one (may be NULL).
114 static wxIdRangeManager *Set(wxIdRangeManager *res);
115
116 // Create a new IDrange from this node
117 void AddRange(const wxXmlNode* node);
118 // Tell the IdRange that this item exists, and should be pre-allocated an ID
119 void NotifyRangeOfItem(const wxXmlNode* node, const wxString& item) const;
120 // Tells all IDranges that they're now complete, and can create their IDs
121 void FinaliseRanges(const wxXmlNode* node) const;
122 // Searches for a known IdRange matching 'name', returning its index or -1
123 int Find(const wxString& rangename) const;
124 // Removes, if it exists, an entry from the XRCID table. Used in id-ranges
125 // to replace defunct or statically-initialised entries with current values
126 static void RemoveXRCIDEntry(const wxString& idstr);
127
128 protected:
129 wxIdRange* FindRangeForItem(const wxXmlNode* node,
130 const wxString& item,
131 wxString& value) const;
132 wxVector<wxIdRange*> m_IdRanges;
133
134 private:
135 static wxIdRangeManager *ms_instance;
136 };
137
138 namespace
139 {
140
141 // helper used by DoFindResource() and elsewhere: returns true if this is an
142 // object or object_ref node
143 //
144 // node must be non-NULL
145 inline bool IsObjectNode(wxXmlNode *node)
146 {
147 return node->GetType() == wxXML_ELEMENT_NODE &&
148 (node->GetName() == wxS("object") ||
149 node->GetName() == wxS("object_ref"));
150 }
151
152 // special XML attribute with name of input file, see GetFileNameFromNode()
153 const char *ATTR_INPUT_FILENAME = "__wx:filename";
154
155 // helper to get filename corresponding to an XML node
156 wxString
157 GetFileNameFromNode(const wxXmlNode *node, const wxXmlResourceDataRecords& files)
158 {
159 // this loop does two things: it looks for ATTR_INPUT_FILENAME among
160 // parents and if it isn't used, it finds the root of the XML tree 'node'
161 // is in
162 for ( ;; )
163 {
164 // in some rare cases (specifically, when an <object_ref> is used, see
165 // wxXmlResource::CreateResFromNode() and MergeNodesOver()), we work
166 // with XML nodes that are not rooted in any document from 'files'
167 // (because a new node was created by CreateResFromNode() to merge the
168 // content of <object_ref> and the referenced <object>); in that case,
169 // we hack around the problem by putting the information about input
170 // file into a custom attribute
171 if ( node->HasAttribute(ATTR_INPUT_FILENAME) )
172 return node->GetAttribute(ATTR_INPUT_FILENAME);
173
174 if ( !node->GetParent() )
175 break; // we found the root of this XML tree
176
177 node = node->GetParent();
178 }
179
180 // NB: 'node' now points to the root of XML document
181
182 for ( wxXmlResourceDataRecords::const_iterator i = files.begin();
183 i != files.end(); ++i )
184 {
185 if ( (*i)->Doc->GetRoot() == node )
186 {
187 return (*i)->File;
188 }
189 }
190
191 return wxEmptyString; // not found
192 }
193
194 } // anonymous namespace
195
196
197 wxXmlResource *wxXmlResource::ms_instance = NULL;
198
199 /*static*/ wxXmlResource *wxXmlResource::Get()
200 {
201 if ( !ms_instance )
202 ms_instance = new wxXmlResource;
203 return ms_instance;
204 }
205
206 /*static*/ wxXmlResource *wxXmlResource::Set(wxXmlResource *res)
207 {
208 wxXmlResource *old = ms_instance;
209 ms_instance = res;
210 return old;
211 }
212
213 wxXmlResource::wxXmlResource(int flags, const wxString& domain)
214 {
215 m_flags = flags;
216 m_version = -1;
217 m_data = new wxXmlResourceDataRecords;
218 SetDomain(domain);
219 }
220
221 wxXmlResource::wxXmlResource(const wxString& filemask, int flags, const wxString& domain)
222 {
223 m_flags = flags;
224 m_version = -1;
225 m_data = new wxXmlResourceDataRecords;
226 SetDomain(domain);
227 Load(filemask);
228 }
229
230 wxXmlResource::~wxXmlResource()
231 {
232 ClearHandlers();
233
234 for ( wxXmlResourceDataRecords::iterator i = m_data->begin();
235 i != m_data->end(); ++i )
236 {
237 delete *i;
238 }
239 delete m_data;
240 }
241
242 void wxXmlResource::SetDomain(const wxString& domain)
243 {
244 m_domain = domain;
245 }
246
247
248 /* static */
249 wxString wxXmlResource::ConvertFileNameToURL(const wxString& filename)
250 {
251 wxString fnd(filename);
252
253 // NB: as Load() and Unload() accept both filenames and URLs (should
254 // probably be changed to filenames only, but embedded resources
255 // currently rely on its ability to handle URLs - FIXME) we need to
256 // determine whether found name is filename and not URL and this is the
257 // fastest/simplest way to do it
258 if (wxFileName::FileExists(fnd))
259 {
260 // Make the name absolute filename, because the app may
261 // change working directory later:
262 wxFileName fn(fnd);
263 if (fn.IsRelative())
264 {
265 fn.MakeAbsolute();
266 fnd = fn.GetFullPath();
267 }
268 #if wxUSE_FILESYSTEM
269 fnd = wxFileSystem::FileNameToURL(fnd);
270 #endif
271 }
272
273 return fnd;
274 }
275
276 #if wxUSE_FILESYSTEM
277
278 /* static */
279 bool wxXmlResource::IsArchive(const wxString& filename)
280 {
281 const wxString fnd = filename.Lower();
282
283 return fnd.Matches(wxT("*.zip")) || fnd.Matches(wxT("*.xrs"));
284 }
285
286 #endif // wxUSE_FILESYSTEM
287
288 bool wxXmlResource::LoadFile(const wxFileName& file)
289 {
290 #if wxUSE_FILESYSTEM
291 return Load(wxFileSystem::FileNameToURL(file));
292 #else
293 return Load(file.GetFullPath());
294 #endif
295 }
296
297 bool wxXmlResource::LoadAllFiles(const wxString& dirname)
298 {
299 bool ok = true;
300 wxArrayString files;
301
302 wxDir::GetAllFiles(dirname, &files, "*.xrc");
303
304 for ( wxArrayString::const_iterator i = files.begin(); i != files.end(); ++i )
305 {
306 if ( !LoadFile(*i) )
307 ok = false;
308 }
309
310 return ok;
311 }
312
313 bool wxXmlResource::Load(const wxString& filemask_)
314 {
315 wxString filemask = ConvertFileNameToURL(filemask_);
316
317 #if wxUSE_FILESYSTEM
318 wxFileSystem fsys;
319 # define wxXmlFindFirst fsys.FindFirst(filemask, wxFILE)
320 # define wxXmlFindNext fsys.FindNext()
321 #else
322 # define wxXmlFindFirst wxFindFirstFile(filemask, wxFILE)
323 # define wxXmlFindNext wxFindNextFile()
324 #endif
325 wxString fnd = wxXmlFindFirst;
326 if ( fnd.empty() )
327 {
328 wxLogError(_("Cannot load resources from '%s'."), filemask);
329 return false;
330 }
331
332 while (!fnd.empty())
333 {
334 #if wxUSE_FILESYSTEM
335 if ( IsArchive(fnd) )
336 {
337 if ( !Load(fnd + wxT("#zip:*.xrc")) )
338 return false;
339 }
340 else // a single resource URL
341 #endif // wxUSE_FILESYSTEM
342 {
343 wxXmlResourceDataRecord *drec = new wxXmlResourceDataRecord;
344 drec->File = fnd;
345 Data().push_back(drec);
346 }
347
348 fnd = wxXmlFindNext;
349 }
350 # undef wxXmlFindFirst
351 # undef wxXmlFindNext
352
353 return UpdateResources();
354 }
355
356 bool wxXmlResource::Unload(const wxString& filename)
357 {
358 wxASSERT_MSG( !wxIsWild(filename),
359 wxT("wildcards not supported by wxXmlResource::Unload()") );
360
361 wxString fnd = ConvertFileNameToURL(filename);
362 #if wxUSE_FILESYSTEM
363 const bool isArchive = IsArchive(fnd);
364 if ( isArchive )
365 fnd += wxT("#zip:");
366 #endif // wxUSE_FILESYSTEM
367
368 bool unloaded = false;
369 for ( wxXmlResourceDataRecords::iterator i = Data().begin();
370 i != Data().end(); ++i )
371 {
372 #if wxUSE_FILESYSTEM
373 if ( isArchive )
374 {
375 if ( (*i)->File.StartsWith(fnd) )
376 unloaded = true;
377 // don't break from the loop, we can have other matching files
378 }
379 else // a single resource URL
380 #endif // wxUSE_FILESYSTEM
381 {
382 if ( (*i)->File == fnd )
383 {
384 delete *i;
385 Data().erase(i);
386 unloaded = true;
387
388 // no sense in continuing, there is only one file with this URL
389 break;
390 }
391 }
392 }
393
394 return unloaded;
395 }
396
397
398 IMPLEMENT_ABSTRACT_CLASS(wxXmlResourceHandler, wxObject)
399
400 void wxXmlResource::AddHandler(wxXmlResourceHandler *handler)
401 {
402 m_handlers.push_back(handler);
403 handler->SetParentResource(this);
404 }
405
406 void wxXmlResource::InsertHandler(wxXmlResourceHandler *handler)
407 {
408 m_handlers.insert(m_handlers.begin(), handler);
409 handler->SetParentResource(this);
410 }
411
412
413
414 void wxXmlResource::ClearHandlers()
415 {
416 for ( wxVector<wxXmlResourceHandler*>::iterator i = m_handlers.begin();
417 i != m_handlers.end(); ++i )
418 delete *i;
419 m_handlers.clear();
420 }
421
422
423 wxMenu *wxXmlResource::LoadMenu(const wxString& name)
424 {
425 return (wxMenu*)CreateResFromNode(FindResource(name, wxT("wxMenu")), NULL, NULL);
426 }
427
428
429
430 wxMenuBar *wxXmlResource::LoadMenuBar(wxWindow *parent, const wxString& name)
431 {
432 return (wxMenuBar*)CreateResFromNode(FindResource(name, wxT("wxMenuBar")), parent, NULL);
433 }
434
435
436
437 #if wxUSE_TOOLBAR
438 wxToolBar *wxXmlResource::LoadToolBar(wxWindow *parent, const wxString& name)
439 {
440 return (wxToolBar*)CreateResFromNode(FindResource(name, wxT("wxToolBar")), parent, NULL);
441 }
442 #endif
443
444
445 wxDialog *wxXmlResource::LoadDialog(wxWindow *parent, const wxString& name)
446 {
447 return (wxDialog*)CreateResFromNode(FindResource(name, wxT("wxDialog")), parent, NULL);
448 }
449
450 bool wxXmlResource::LoadDialog(wxDialog *dlg, wxWindow *parent, const wxString& name)
451 {
452 return CreateResFromNode(FindResource(name, wxT("wxDialog")), parent, dlg) != NULL;
453 }
454
455
456
457 wxPanel *wxXmlResource::LoadPanel(wxWindow *parent, const wxString& name)
458 {
459 return (wxPanel*)CreateResFromNode(FindResource(name, wxT("wxPanel")), parent, NULL);
460 }
461
462 bool wxXmlResource::LoadPanel(wxPanel *panel, wxWindow *parent, const wxString& name)
463 {
464 return CreateResFromNode(FindResource(name, wxT("wxPanel")), parent, panel) != NULL;
465 }
466
467 wxFrame *wxXmlResource::LoadFrame(wxWindow* parent, const wxString& name)
468 {
469 return (wxFrame*)CreateResFromNode(FindResource(name, wxT("wxFrame")), parent, NULL);
470 }
471
472 bool wxXmlResource::LoadFrame(wxFrame* frame, wxWindow *parent, const wxString& name)
473 {
474 return CreateResFromNode(FindResource(name, wxT("wxFrame")), parent, frame) != NULL;
475 }
476
477 wxBitmap wxXmlResource::LoadBitmap(const wxString& name)
478 {
479 wxBitmap *bmp = (wxBitmap*)CreateResFromNode(
480 FindResource(name, wxT("wxBitmap")), NULL, NULL);
481 wxBitmap rt;
482
483 if (bmp) { rt = *bmp; delete bmp; }
484 return rt;
485 }
486
487 wxIcon wxXmlResource::LoadIcon(const wxString& name)
488 {
489 wxIcon *icon = (wxIcon*)CreateResFromNode(
490 FindResource(name, wxT("wxIcon")), NULL, NULL);
491 wxIcon rt;
492
493 if (icon) { rt = *icon; delete icon; }
494 return rt;
495 }
496
497
498 wxObject *
499 wxXmlResource::DoLoadObject(wxWindow *parent,
500 const wxString& name,
501 const wxString& classname,
502 bool recursive)
503 {
504 wxXmlNode * const node = FindResource(name, classname, recursive);
505
506 return node ? DoCreateResFromNode(*node, parent, NULL) : NULL;
507 }
508
509 bool
510 wxXmlResource::DoLoadObject(wxObject *instance,
511 wxWindow *parent,
512 const wxString& name,
513 const wxString& classname,
514 bool recursive)
515 {
516 wxXmlNode * const node = FindResource(name, classname, recursive);
517
518 return node && DoCreateResFromNode(*node, parent, instance) != NULL;
519 }
520
521
522 bool wxXmlResource::AttachUnknownControl(const wxString& name,
523 wxWindow *control, wxWindow *parent)
524 {
525 if (parent == NULL)
526 parent = control->GetParent();
527 wxWindow *container = parent->FindWindow(name + wxT("_container"));
528 if (!container)
529 {
530 wxLogError("Cannot find container for unknown control '%s'.", name);
531 return false;
532 }
533 return control->Reparent(container);
534 }
535
536
537 static void ProcessPlatformProperty(wxXmlNode *node)
538 {
539 wxString s;
540 bool isok;
541
542 wxXmlNode *c = node->GetChildren();
543 while (c)
544 {
545 isok = false;
546 if (!c->GetAttribute(wxT("platform"), &s))
547 isok = true;
548 else
549 {
550 wxStringTokenizer tkn(s, wxT(" |"));
551
552 while (tkn.HasMoreTokens())
553 {
554 s = tkn.GetNextToken();
555 #ifdef __WINDOWS__
556 if (s == wxT("win")) isok = true;
557 #endif
558 #if defined(__MAC__) || defined(__APPLE__)
559 if (s == wxT("mac")) isok = true;
560 #elif defined(__UNIX__)
561 if (s == wxT("unix")) isok = true;
562 #endif
563 #ifdef __OS2__
564 if (s == wxT("os2")) isok = true;
565 #endif
566
567 if (isok)
568 break;
569 }
570 }
571
572 if (isok)
573 {
574 ProcessPlatformProperty(c);
575 c = c->GetNext();
576 }
577 else
578 {
579 wxXmlNode *c2 = c->GetNext();
580 node->RemoveChild(c);
581 delete c;
582 c = c2;
583 }
584 }
585 }
586
587 static void PreprocessForIdRanges(wxXmlNode *rootnode)
588 {
589 // First go through the top level, looking for the names of ID ranges
590 // as processing items is a lot easier if names are already known
591 wxXmlNode *c = rootnode->GetChildren();
592 while (c)
593 {
594 if (c->GetName() == wxT("ids-range"))
595 wxIdRangeManager::Get()->AddRange(c);
596 c = c->GetNext();
597 }
598
599 // Next, examine every 'name' for the '[' that denotes an ID in a range
600 c = rootnode->GetChildren();
601 while (c)
602 {
603 wxString name = c->GetAttribute(wxT("name"));
604 if (name.find('[') != wxString::npos)
605 wxIdRangeManager::Get()->NotifyRangeOfItem(rootnode, name);
606
607 // Do any children by recursion, then proceed to the next sibling
608 PreprocessForIdRanges(c);
609 c = c->GetNext();
610 }
611 }
612
613 bool wxXmlResource::UpdateResources()
614 {
615 bool rt = true;
616 bool modif;
617 # if wxUSE_FILESYSTEM
618 wxFSFile *file = NULL;
619 wxUnusedVar(file);
620 wxFileSystem fsys;
621 # endif
622
623 wxString encoding(wxT("UTF-8"));
624 #if !wxUSE_UNICODE && wxUSE_INTL
625 if ( (GetFlags() & wxXRC_USE_LOCALE) == 0 )
626 {
627 // In case we are not using wxLocale to translate strings, convert the
628 // strings GUI's charset. This must not be done when wxXRC_USE_LOCALE
629 // is on, because it could break wxGetTranslation lookup.
630 encoding = wxLocale::GetSystemEncodingName();
631 }
632 #endif
633
634 for ( wxXmlResourceDataRecords::iterator i = Data().begin();
635 i != Data().end(); ++i )
636 {
637 wxXmlResourceDataRecord* const rec = *i;
638
639 modif = (rec->Doc == NULL);
640
641 if (!modif && !(m_flags & wxXRC_NO_RELOADING))
642 {
643 # if wxUSE_FILESYSTEM
644 file = fsys.OpenFile(rec->File);
645 # if wxUSE_DATETIME
646 modif = file && file->GetModificationTime() > rec->Time;
647 # else // wxUSE_DATETIME
648 modif = true;
649 # endif // wxUSE_DATETIME
650 if (!file)
651 {
652 wxLogError(_("Cannot open file '%s'."), rec->File);
653 rt = false;
654 }
655 wxDELETE(file);
656 wxUnusedVar(file);
657 # else // wxUSE_FILESYSTEM
658 # if wxUSE_DATETIME
659 modif = wxDateTime(wxFileModificationTime(rec->File)) > rec->Time;
660 # else // wxUSE_DATETIME
661 modif = true;
662 # endif // wxUSE_DATETIME
663 # endif // wxUSE_FILESYSTEM
664 }
665
666 if (modif)
667 {
668 wxLogTrace(wxT("xrc"), wxT("opening file '%s'"), rec->File);
669
670 wxInputStream *stream = NULL;
671
672 # if wxUSE_FILESYSTEM
673 file = fsys.OpenFile(rec->File);
674 if (file)
675 stream = file->GetStream();
676 # else
677 stream = new wxFileInputStream(rec->File);
678 # endif
679
680 if (stream)
681 {
682 delete rec->Doc;
683 rec->Doc = new wxXmlDocument;
684 }
685 if (!stream || !stream->IsOk() || !rec->Doc->Load(*stream, encoding))
686 {
687 wxLogError(_("Cannot load resources from file '%s'."),
688 rec->File);
689 wxDELETE(rec->Doc);
690 rt = false;
691 }
692 else if (rec->Doc->GetRoot()->GetName() != wxT("resource"))
693 {
694 ReportError
695 (
696 rec->Doc->GetRoot(),
697 "invalid XRC resource, doesn't have root node <resource>"
698 );
699 wxDELETE(rec->Doc);
700 rt = false;
701 }
702 else
703 {
704 long version;
705 int v1, v2, v3, v4;
706 wxString verstr = rec->Doc->GetRoot()->GetAttribute(
707 wxT("version"), wxT("0.0.0.0"));
708 if (wxSscanf(verstr.c_str(), wxT("%i.%i.%i.%i"),
709 &v1, &v2, &v3, &v4) == 4)
710 version = v1*256*256*256+v2*256*256+v3*256+v4;
711 else
712 version = 0;
713 if (m_version == -1)
714 m_version = version;
715 if (m_version != version)
716 {
717 wxLogError("Resource files must have same version number.");
718 rt = false;
719 }
720
721 ProcessPlatformProperty(rec->Doc->GetRoot());
722 PreprocessForIdRanges(rec->Doc->GetRoot());
723 wxIdRangeManager::Get()->FinaliseRanges(rec->Doc->GetRoot());
724 #if wxUSE_DATETIME
725 #if wxUSE_FILESYSTEM
726 rec->Time = file->GetModificationTime();
727 #else // wxUSE_FILESYSTEM
728 rec->Time = wxDateTime(wxFileModificationTime(rec->File));
729 #endif // wxUSE_FILESYSTEM
730 #endif // wxUSE_DATETIME
731 }
732
733 # if wxUSE_FILESYSTEM
734 wxDELETE(file);
735 wxUnusedVar(file);
736 # else
737 wxDELETE(stream);
738 # endif
739 }
740 }
741
742 return rt;
743 }
744
745 wxXmlNode *wxXmlResource::DoFindResource(wxXmlNode *parent,
746 const wxString& name,
747 const wxString& classname,
748 bool recursive) const
749 {
750 wxXmlNode *node;
751
752 // first search for match at the top-level nodes (as this is
753 // where the resource is most commonly looked for):
754 for (node = parent->GetChildren(); node; node = node->GetNext())
755 {
756 if ( IsObjectNode(node) && node->GetAttribute(wxS("name")) == name )
757 {
758 // empty class name matches everything
759 if ( classname.empty() )
760 return node;
761
762 wxString cls(node->GetAttribute(wxS("class")));
763
764 // object_ref may not have 'class' attribute:
765 if (cls.empty() && node->GetName() == wxS("object_ref"))
766 {
767 wxString refName = node->GetAttribute(wxS("ref"));
768 if (refName.empty())
769 continue;
770
771 const wxXmlNode * const refNode = GetResourceNode(refName);
772 if ( refNode )
773 cls = refNode->GetAttribute(wxS("class"));
774 }
775
776 if ( cls == classname )
777 return node;
778 }
779 }
780
781 // then recurse in child nodes
782 if ( recursive )
783 {
784 for (node = parent->GetChildren(); node; node = node->GetNext())
785 {
786 if ( IsObjectNode(node) )
787 {
788 wxXmlNode* found = DoFindResource(node, name, classname, true);
789 if ( found )
790 return found;
791 }
792 }
793 }
794
795 return NULL;
796 }
797
798 wxXmlNode *wxXmlResource::FindResource(const wxString& name,
799 const wxString& classname,
800 bool recursive)
801 {
802 wxString path;
803 wxXmlNode * const
804 node = GetResourceNodeAndLocation(name, classname, recursive, &path);
805
806 if ( !node )
807 {
808 ReportError
809 (
810 NULL,
811 wxString::Format
812 (
813 "XRC resource \"%s\" (class \"%s\") not found",
814 name, classname
815 )
816 );
817 }
818 #if wxUSE_FILESYSTEM
819 else // node was found
820 {
821 // ensure that relative paths work correctly when loading this node
822 // (which should happen as soon as we return as FindResource() result
823 // is always passed to CreateResFromNode())
824 m_curFileSystem.ChangePathTo(path);
825 }
826 #endif // wxUSE_FILESYSTEM
827
828 return node;
829 }
830
831 wxXmlNode *
832 wxXmlResource::GetResourceNodeAndLocation(const wxString& name,
833 const wxString& classname,
834 bool recursive,
835 wxString *path) const
836 {
837 // ensure everything is up-to-date: this is needed to support on-demand
838 // reloading of XRC files
839 const_cast<wxXmlResource *>(this)->UpdateResources();
840
841 for ( wxXmlResourceDataRecords::const_iterator f = Data().begin();
842 f != Data().end(); ++f )
843 {
844 wxXmlResourceDataRecord *const rec = *f;
845 wxXmlDocument * const doc = rec->Doc;
846 if ( !doc || !doc->GetRoot() )
847 continue;
848
849 wxXmlNode * const
850 found = DoFindResource(doc->GetRoot(), name, classname, recursive);
851 if ( found )
852 {
853 if ( path )
854 *path = rec->File;
855
856 return found;
857 }
858 }
859
860 return NULL;
861 }
862
863 static void MergeNodesOver(wxXmlNode& dest, wxXmlNode& overwriteWith,
864 const wxString& overwriteFilename)
865 {
866 // Merge attributes:
867 for ( wxXmlAttribute *attr = overwriteWith.GetAttributes();
868 attr; attr = attr->GetNext() )
869 {
870 wxXmlAttribute *dattr;
871 for (dattr = dest.GetAttributes(); dattr; dattr = dattr->GetNext())
872 {
873
874 if ( dattr->GetName() == attr->GetName() )
875 {
876 dattr->SetValue(attr->GetValue());
877 break;
878 }
879 }
880
881 if ( !dattr )
882 dest.AddAttribute(attr->GetName(), attr->GetValue());
883 }
884
885 // Merge child nodes:
886 for (wxXmlNode* node = overwriteWith.GetChildren(); node; node = node->GetNext())
887 {
888 wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
889 wxXmlNode *dnode;
890
891 for (dnode = dest.GetChildren(); dnode; dnode = dnode->GetNext() )
892 {
893 if ( dnode->GetName() == node->GetName() &&
894 dnode->GetAttribute(wxT("name"), wxEmptyString) == name &&
895 dnode->GetType() == node->GetType() )
896 {
897 MergeNodesOver(*dnode, *node, overwriteFilename);
898 break;
899 }
900 }
901
902 if ( !dnode )
903 {
904 wxXmlNode *copyOfNode = new wxXmlNode(*node);
905 // remember referenced object's file, see GetFileNameFromNode()
906 copyOfNode->AddAttribute(ATTR_INPUT_FILENAME, overwriteFilename);
907
908 static const wxChar *AT_END = wxT("end");
909 wxString insert_pos = node->GetAttribute(wxT("insert_at"), AT_END);
910 if ( insert_pos == AT_END )
911 {
912 dest.AddChild(copyOfNode);
913 }
914 else if ( insert_pos == wxT("begin") )
915 {
916 dest.InsertChild(copyOfNode, dest.GetChildren());
917 }
918 }
919 }
920
921 if ( dest.GetType() == wxXML_TEXT_NODE && overwriteWith.GetContent().length() )
922 dest.SetContent(overwriteWith.GetContent());
923 }
924
925 wxObject *
926 wxXmlResource::DoCreateResFromNode(wxXmlNode& node,
927 wxObject *parent,
928 wxObject *instance,
929 wxXmlResourceHandler *handlerToUse)
930 {
931 // handling of referenced resource
932 if ( node.GetName() == wxT("object_ref") )
933 {
934 wxString refName = node.GetAttribute(wxT("ref"), wxEmptyString);
935 wxXmlNode* refNode = FindResource(refName, wxEmptyString, true);
936
937 if ( !refNode )
938 {
939 ReportError
940 (
941 &node,
942 wxString::Format
943 (
944 "referenced object node with ref=\"%s\" not found",
945 refName
946 )
947 );
948 return NULL;
949 }
950
951 if ( !node.GetChildren() )
952 {
953 // In the typical, simple case, <object_ref> is used to link
954 // to another node and doesn't have any content of its own that
955 // would overwrite linked object's properties. In this case,
956 // we can simply create the resource from linked node.
957
958 return DoCreateResFromNode(*refNode, parent, instance);
959 }
960 else
961 {
962 // In the more complicated (but rare) case, <object_ref> has
963 // subnodes that partially overwrite content of the referenced
964 // object. In this case, we need to merge both XML trees and
965 // load the resource from result of the merge.
966
967 wxXmlNode copy(*refNode);
968 MergeNodesOver(copy, node, GetFileNameFromNode(&node, Data()));
969
970 // remember referenced object's file, see GetFileNameFromNode()
971 copy.AddAttribute(ATTR_INPUT_FILENAME,
972 GetFileNameFromNode(refNode, Data()));
973
974 return DoCreateResFromNode(copy, parent, instance);
975 }
976 }
977
978 if (handlerToUse)
979 {
980 if (handlerToUse->CanHandle(&node))
981 {
982 return handlerToUse->CreateResource(&node, parent, instance);
983 }
984 }
985 else if (node.GetName() == wxT("object"))
986 {
987 for ( wxVector<wxXmlResourceHandler*>::iterator h = m_handlers.begin();
988 h != m_handlers.end(); ++h )
989 {
990 wxXmlResourceHandler *handler = *h;
991 if (handler->CanHandle(&node))
992 return handler->CreateResource(&node, parent, instance);
993 }
994 }
995
996 ReportError
997 (
998 &node,
999 wxString::Format
1000 (
1001 "no handler found for XML node \"%s\" (class \"%s\")",
1002 node.GetName(),
1003 node.GetAttribute("class", wxEmptyString)
1004 )
1005 );
1006 return NULL;
1007 }
1008
1009 wxIdRange::wxIdRange(const wxXmlNode* node,
1010 const wxString& rname,
1011 const wxString& startno,
1012 const wxString& rsize)
1013 : m_name(rname),
1014 m_start(0),
1015 m_size(0),
1016 m_item_end_found(0),
1017 m_finalised(0)
1018 {
1019 long l;
1020 if ( startno.ToLong(&l) )
1021 {
1022 if ( l >= 0 )
1023 {
1024 m_start = l;
1025 }
1026 else
1027 {
1028 wxXmlResource::Get()->ReportError
1029 (
1030 node,
1031 "a negative id-range start parameter was given"
1032 );
1033 }
1034 }
1035 else
1036 {
1037 wxXmlResource::Get()->ReportError
1038 (
1039 node,
1040 "the id-range start parameter was malformed"
1041 );
1042 }
1043
1044 unsigned long ul;
1045 if ( rsize.ToULong(&ul) )
1046 {
1047 m_size = ul;
1048 }
1049 else
1050 {
1051 wxXmlResource::Get()->ReportError
1052 (
1053 node,
1054 "the id-range size parameter was malformed"
1055 );
1056 }
1057 }
1058
1059 void wxIdRange::NoteItem(const wxXmlNode* node, const wxString& item)
1060 {
1061 // Nothing gets added here, but the existence of each item is noted
1062 // thus getting an accurate count. 'item' will be either an integer e.g.
1063 // [0] [123]: will eventually create an XRCID as start+integer or [start]
1064 // or [end] which are synonyms for [0] or [range_size-1] respectively.
1065 wxString content(item.Mid(1, item.length()-2));
1066
1067 // Check that basename+item wasn't foo[]
1068 if (content.empty())
1069 {
1070 wxXmlResource::Get()->ReportError(node, "an empty id-range item found");
1071 return;
1072 }
1073
1074 if (content=="start")
1075 {
1076 // "start" means [0], so store that in the set
1077 if (m_indices.count(0) == 0)
1078 {
1079 m_indices.insert(0);
1080 }
1081 else
1082 {
1083 wxXmlResource::Get()->ReportError
1084 (
1085 node,
1086 "duplicate id-range item found"
1087 );
1088 }
1089 }
1090 else if (content=="end")
1091 {
1092 // We can't yet be certain which XRCID this will be equivalent to, so
1093 // just note that there's an item with this name, in case we need to
1094 // inc the range size
1095 m_item_end_found = true;
1096 }
1097 else
1098 {
1099 // Anything else will be an integer, or rubbish
1100 unsigned long l;
1101 if ( content.ToULong(&l) )
1102 {
1103 if (m_indices.count(l) == 0)
1104 {
1105 m_indices.insert(l);
1106 // Check that this item wouldn't fall outside the current range
1107 // extent
1108 if (l >= m_size)
1109 {
1110 m_size = l + 1;
1111 }
1112 }
1113 else
1114 {
1115 wxXmlResource::Get()->ReportError
1116 (
1117 node,
1118 "duplicate id-range item found"
1119 );
1120 }
1121
1122 }
1123 else
1124 {
1125 wxXmlResource::Get()->ReportError
1126 (
1127 node,
1128 "an id-range item had a malformed index"
1129 );
1130 }
1131 }
1132 }
1133
1134 void wxIdRange::Finalise(const wxXmlNode* node)
1135 {
1136 wxCHECK_RET( !IsFinalised(),
1137 "Trying to finalise an already-finalised range" );
1138
1139 // Now we know about all the items, we can get an accurate range size
1140 // Expand any requested range-size if there were more items than would fit
1141 m_size = wxMax(m_size, m_indices.size());
1142
1143 // If an item is explicitly called foo[end], ensure it won't clash with
1144 // another item
1145 if ( m_item_end_found && m_indices.count(m_size-1) )
1146 ++m_size;
1147 if (m_size == 0)
1148 {
1149 // This will happen if someone creates a range but no items in this xrc
1150 // file Report the error and abort, but don't finalise, in case items
1151 // appear later
1152 wxXmlResource::Get()->ReportError
1153 (
1154 node,
1155 "trying to create an empty id-range"
1156 );
1157 return;
1158 }
1159
1160 if (m_start==0)
1161 {
1162 // This is the usual case, where the user didn't specify a start ID
1163 // So get the range using NewControlId().
1164 //
1165 // NB: negative numbers, but NewControlId already returns the most
1166 // negative
1167 m_start = wxWindow::NewControlId(m_size);
1168 wxCHECK_RET( m_start != wxID_NONE,
1169 "insufficient IDs available to create range" );
1170 m_end = m_start + m_size - 1;
1171 }
1172 else
1173 {
1174 // The user already specified a start value, which must be positive
1175 m_end = m_start + m_size - 1;
1176 }
1177
1178 // Create the XRCIDs
1179 for (int i=m_start; i <= m_end; ++i)
1180 {
1181 // First clear any pre-existing XRCID
1182 // Necessary for wxXmlResource::Unload() followed by Load()
1183 wxIdRangeManager::RemoveXRCIDEntry(
1184 m_name + wxString::Format("[%i]", i-m_start));
1185
1186 // Use the second parameter of GetXRCID to force it to take the value i
1187 wxXmlResource::GetXRCID(m_name + wxString::Format("[%i]", i-m_start), i);
1188 wxLogTrace("xrcrange",
1189 "integer = %i %s now returns %i",
1190 i,
1191 m_name + wxString::Format("[%i]", i-m_start),
1192 XRCID((m_name + wxString::Format("[%i]", i-m_start)).mb_str()));
1193 }
1194 // and these special ones
1195 wxIdRangeManager::RemoveXRCIDEntry(m_name + "[start]");
1196 wxXmlResource::GetXRCID(m_name + "[start]", m_start);
1197 wxIdRangeManager::RemoveXRCIDEntry(m_name + "[end]");
1198 wxXmlResource::GetXRCID(m_name + "[end]", m_end);
1199 wxLogTrace("xrcrange","%s[start] = %i %s[end] = %i",
1200 m_name.mb_str(),XRCID(wxString(m_name+"[start]").mb_str()),
1201 m_name.mb_str(),XRCID(wxString(m_name+"[end]").mb_str()));
1202
1203 m_finalised = true;
1204 }
1205
1206 wxIdRangeManager *wxIdRangeManager::ms_instance = NULL;
1207
1208 /*static*/ wxIdRangeManager *wxIdRangeManager::Get()
1209 {
1210 if ( !ms_instance )
1211 ms_instance = new wxIdRangeManager;
1212 return ms_instance;
1213 }
1214
1215 /*static*/ wxIdRangeManager *wxIdRangeManager::Set(wxIdRangeManager *res)
1216 {
1217 wxIdRangeManager *old = ms_instance;
1218 ms_instance = res;
1219 return old;
1220 }
1221
1222 wxIdRangeManager::~wxIdRangeManager()
1223 {
1224 for ( wxVector<wxIdRange*>::iterator i = m_IdRanges.begin();
1225 i != m_IdRanges.end(); ++i )
1226 {
1227 delete *i;
1228 }
1229 m_IdRanges.clear();
1230
1231 delete ms_instance;
1232 }
1233
1234 void wxIdRangeManager::AddRange(const wxXmlNode* node)
1235 {
1236 wxString name = node->GetAttribute("name");
1237 wxString start = node->GetAttribute("start", "0");
1238 wxString size = node->GetAttribute("size", "0");
1239 if (name.empty())
1240 {
1241 wxXmlResource::Get()->ReportError
1242 (
1243 node,
1244 "xrc file contains an id-range without a name"
1245 );
1246 return;
1247 }
1248
1249 int index = Find(name);
1250 if (index == wxNOT_FOUND)
1251 {
1252 wxLogTrace("xrcrange",
1253 "Adding ID range, name=%s start=%s size=%s",
1254 name, start, size);
1255
1256 m_IdRanges.push_back(new wxIdRange(node, name, start, size));
1257 }
1258 else
1259 {
1260 // There was already a range with this name. Let's hope this is
1261 // from an Unload()/(re)Load(), not an unintentional duplication
1262 wxLogTrace("xrcrange",
1263 "Replacing ID range, name=%s start=%s size=%s",
1264 name, start, size);
1265
1266 wxIdRange* oldrange = m_IdRanges.at(index);
1267 m_IdRanges.at(index) = new wxIdRange(node, name, start, size);
1268 delete oldrange;
1269 }
1270 }
1271
1272 wxIdRange *
1273 wxIdRangeManager::FindRangeForItem(const wxXmlNode* node,
1274 const wxString& item,
1275 wxString& value) const
1276 {
1277 wxString basename = item.BeforeFirst('[');
1278 wxCHECK_MSG( !basename.empty(), NULL,
1279 "an id-range item without a range name" );
1280
1281 int index = Find(basename);
1282 if (index == wxNOT_FOUND)
1283 {
1284 // Don't assert just because we've found an unexpected foo[123]
1285 // Someone might just want such a name, nothing to do with ranges
1286 return NULL;
1287 }
1288
1289 value = item.Mid(basename.Len());
1290 if (value.at(value.length()-1)==']')
1291 {
1292 return m_IdRanges.at(index);
1293 }
1294 wxXmlResource::Get()->ReportError(node, "a malformed id-range item");
1295 return NULL;
1296 }
1297
1298 void
1299 wxIdRangeManager::NotifyRangeOfItem(const wxXmlNode* node,
1300 const wxString& item) const
1301 {
1302 wxString value;
1303 wxIdRange* range = FindRangeForItem(node, item, value);
1304 if (range)
1305 range->NoteItem(node, value);
1306 }
1307
1308 int wxIdRangeManager::Find(const wxString& rangename) const
1309 {
1310 for ( int i=0; i < (int)m_IdRanges.size(); ++i )
1311 {
1312 if (m_IdRanges.at(i)->GetName() == rangename)
1313 return i;
1314 }
1315
1316 return wxNOT_FOUND;
1317 }
1318
1319 void wxIdRangeManager::FinaliseRanges(const wxXmlNode* node) const
1320 {
1321 for ( wxVector<wxIdRange*>::const_iterator i = m_IdRanges.begin();
1322 i != m_IdRanges.end(); ++i )
1323 {
1324 // Check if this range has already been finalised. Quite possible,
1325 // as FinaliseRanges() gets called for each .xrc file loaded
1326 if (!(*i)->IsFinalised())
1327 {
1328 wxLogTrace("xrcrange", "Finalising ID range %s", (*i)->GetName());
1329 (*i)->Finalise(node);
1330 }
1331 }
1332 }
1333
1334
1335 class wxXmlSubclassFactories : public wxVector<wxXmlSubclassFactory*>
1336 {
1337 // this is a class so that it can be forward-declared
1338 };
1339
1340 wxXmlSubclassFactories *wxXmlResource::ms_subclassFactories = NULL;
1341
1342 /*static*/ void wxXmlResource::AddSubclassFactory(wxXmlSubclassFactory *factory)
1343 {
1344 if (!ms_subclassFactories)
1345 {
1346 ms_subclassFactories = new wxXmlSubclassFactories;
1347 }
1348 ms_subclassFactories->push_back(factory);
1349 }
1350
1351 class wxXmlSubclassFactoryCXX : public wxXmlSubclassFactory
1352 {
1353 public:
1354 ~wxXmlSubclassFactoryCXX() {}
1355
1356 wxObject *Create(const wxString& className)
1357 {
1358 wxClassInfo* classInfo = wxClassInfo::FindClass(className);
1359
1360 if (classInfo)
1361 return classInfo->CreateObject();
1362 else
1363 return NULL;
1364 }
1365 };
1366
1367
1368
1369
1370 wxXmlResourceHandler::wxXmlResourceHandler()
1371 : m_node(NULL), m_parent(NULL), m_instance(NULL),
1372 m_parentAsWindow(NULL)
1373 {}
1374
1375
1376
1377 wxObject *wxXmlResourceHandler::CreateResource(wxXmlNode *node, wxObject *parent, wxObject *instance)
1378 {
1379 wxXmlNode *myNode = m_node;
1380 wxString myClass = m_class;
1381 wxObject *myParent = m_parent, *myInstance = m_instance;
1382 wxWindow *myParentAW = m_parentAsWindow;
1383
1384 m_instance = instance;
1385 if (!m_instance && node->HasAttribute(wxT("subclass")) &&
1386 !(m_resource->GetFlags() & wxXRC_NO_SUBCLASSING))
1387 {
1388 wxString subclass = node->GetAttribute(wxT("subclass"), wxEmptyString);
1389 if (!subclass.empty())
1390 {
1391 for (wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
1392 i != wxXmlResource::ms_subclassFactories->end(); ++i)
1393 {
1394 m_instance = (*i)->Create(subclass);
1395 if (m_instance)
1396 break;
1397 }
1398
1399 if (!m_instance)
1400 {
1401 wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
1402 ReportError
1403 (
1404 node,
1405 wxString::Format
1406 (
1407 "subclass \"%s\" not found for resource \"%s\", not subclassing",
1408 subclass, name
1409 )
1410 );
1411 }
1412 }
1413 }
1414
1415 m_node = node;
1416 m_class = node->GetAttribute(wxT("class"), wxEmptyString);
1417 m_parent = parent;
1418 m_parentAsWindow = wxDynamicCast(m_parent, wxWindow);
1419
1420 wxObject *returned = DoCreateResource();
1421
1422 m_node = myNode;
1423 m_class = myClass;
1424 m_parent = myParent; m_parentAsWindow = myParentAW;
1425 m_instance = myInstance;
1426
1427 return returned;
1428 }
1429
1430
1431 void wxXmlResourceHandler::AddStyle(const wxString& name, int value)
1432 {
1433 m_styleNames.Add(name);
1434 m_styleValues.Add(value);
1435 }
1436
1437
1438
1439 void wxXmlResourceHandler::AddWindowStyles()
1440 {
1441 XRC_ADD_STYLE(wxCLIP_CHILDREN);
1442
1443 // the border styles all have the old and new names, recognize both for now
1444 XRC_ADD_STYLE(wxSIMPLE_BORDER); XRC_ADD_STYLE(wxBORDER_SIMPLE);
1445 XRC_ADD_STYLE(wxSUNKEN_BORDER); XRC_ADD_STYLE(wxBORDER_SUNKEN);
1446 XRC_ADD_STYLE(wxDOUBLE_BORDER); XRC_ADD_STYLE(wxBORDER_DOUBLE); // deprecated
1447 XRC_ADD_STYLE(wxBORDER_THEME);
1448 XRC_ADD_STYLE(wxRAISED_BORDER); XRC_ADD_STYLE(wxBORDER_RAISED);
1449 XRC_ADD_STYLE(wxSTATIC_BORDER); XRC_ADD_STYLE(wxBORDER_STATIC);
1450 XRC_ADD_STYLE(wxNO_BORDER); XRC_ADD_STYLE(wxBORDER_NONE);
1451
1452 XRC_ADD_STYLE(wxTRANSPARENT_WINDOW);
1453 XRC_ADD_STYLE(wxWANTS_CHARS);
1454 XRC_ADD_STYLE(wxTAB_TRAVERSAL);
1455 XRC_ADD_STYLE(wxNO_FULL_REPAINT_ON_RESIZE);
1456 XRC_ADD_STYLE(wxFULL_REPAINT_ON_RESIZE);
1457 XRC_ADD_STYLE(wxALWAYS_SHOW_SB);
1458 XRC_ADD_STYLE(wxWS_EX_BLOCK_EVENTS);
1459 XRC_ADD_STYLE(wxWS_EX_VALIDATE_RECURSIVELY);
1460 }
1461
1462
1463
1464 bool wxXmlResourceHandler::HasParam(const wxString& param)
1465 {
1466 return (GetParamNode(param) != NULL);
1467 }
1468
1469
1470 int wxXmlResourceHandler::GetStyle(const wxString& param, int defaults)
1471 {
1472 wxString s = GetParamValue(param);
1473
1474 if (!s) return defaults;
1475
1476 wxStringTokenizer tkn(s, wxT("| \t\n"), wxTOKEN_STRTOK);
1477 int style = 0;
1478 int index;
1479 wxString fl;
1480 while (tkn.HasMoreTokens())
1481 {
1482 fl = tkn.GetNextToken();
1483 index = m_styleNames.Index(fl);
1484 if (index != wxNOT_FOUND)
1485 {
1486 style |= m_styleValues[index];
1487 }
1488 else
1489 {
1490 ReportParamError
1491 (
1492 param,
1493 wxString::Format("unknown style flag \"%s\"", fl)
1494 );
1495 }
1496 }
1497 return style;
1498 }
1499
1500
1501
1502 wxString wxXmlResourceHandler::GetText(const wxString& param, bool translate)
1503 {
1504 wxXmlNode *parNode = GetParamNode(param);
1505 wxString str1(GetNodeContent(parNode));
1506 wxString str2;
1507
1508 // "\\" wasn't translated to "\" prior to 2.5.3.0:
1509 const bool escapeBackslash = (m_resource->CompareVersion(2,5,3,0) >= 0);
1510
1511 // VS: First version of XRC resources used $ instead of & (which is
1512 // illegal in XML), but later I realized that '_' fits this purpose
1513 // much better (because &File means "File with F underlined").
1514 const wxChar amp_char = (m_resource->CompareVersion(2,3,0,1) < 0)
1515 ? '$' : '_';
1516
1517 for ( wxString::const_iterator dt = str1.begin(); dt != str1.end(); ++dt )
1518 {
1519 // Remap amp_char to &, map double amp_char to amp_char (for things
1520 // like "&File..." -- this is illegal in XML, so we use "_File..."):
1521 if ( *dt == amp_char )
1522 {
1523 if ( *(++dt) == amp_char )
1524 str2 << amp_char;
1525 else
1526 str2 << wxT('&') << *dt;
1527 }
1528 // Remap \n to CR, \r to LF, \t to TAB, \\ to \:
1529 else if ( *dt == wxT('\\') )
1530 {
1531 switch ( (*(++dt)).GetValue() )
1532 {
1533 case wxT('n'):
1534 str2 << wxT('\n');
1535 break;
1536
1537 case wxT('t'):
1538 str2 << wxT('\t');
1539 break;
1540
1541 case wxT('r'):
1542 str2 << wxT('\r');
1543 break;
1544
1545 case wxT('\\') :
1546 // "\\" wasn't translated to "\" prior to 2.5.3.0:
1547 if ( escapeBackslash )
1548 {
1549 str2 << wxT('\\');
1550 break;
1551 }
1552 // else fall-through to default: branch below
1553
1554 default:
1555 str2 << wxT('\\') << *dt;
1556 break;
1557 }
1558 }
1559 else
1560 {
1561 str2 << *dt;
1562 }
1563 }
1564
1565 if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
1566 {
1567 if (translate && parNode &&
1568 parNode->GetAttribute(wxT("translate"), wxEmptyString) != wxT("0"))
1569 {
1570 return wxGetTranslation(str2, m_resource->GetDomain());
1571 }
1572 else
1573 {
1574 #if wxUSE_UNICODE
1575 return str2;
1576 #else
1577 // The string is internally stored as UTF-8, we have to convert
1578 // it into system's default encoding so that it can be displayed:
1579 return wxString(str2.wc_str(wxConvUTF8), wxConvLocal);
1580 #endif
1581 }
1582 }
1583
1584 // If wxXRC_USE_LOCALE is not set, then the string is already in
1585 // system's default encoding in ANSI build, so we don't have to
1586 // do anything special here.
1587 return str2;
1588 }
1589
1590
1591
1592 long wxXmlResourceHandler::GetLong(const wxString& param, long defaultv)
1593 {
1594 long value;
1595 wxString str1 = GetParamValue(param);
1596
1597 if (!str1.ToLong(&value))
1598 value = defaultv;
1599
1600 return value;
1601 }
1602
1603 float wxXmlResourceHandler::GetFloat(const wxString& param, float defaultv)
1604 {
1605 wxString str = GetParamValue(param);
1606
1607 // strings in XRC always use C locale so make sure to use the
1608 // locale-independent wxString::ToCDouble() and not ToDouble() which uses
1609 // the current locale with a potentially different decimal point character
1610 double value;
1611 if (!str.ToCDouble(&value))
1612 value = defaultv;
1613
1614 return wx_truncate_cast(float, value);
1615 }
1616
1617
1618 int wxXmlResourceHandler::GetID()
1619 {
1620 return wxXmlResource::GetXRCID(GetName());
1621 }
1622
1623
1624
1625 wxString wxXmlResourceHandler::GetName()
1626 {
1627 return m_node->GetAttribute(wxT("name"), wxT("-1"));
1628 }
1629
1630
1631
1632 bool wxXmlResourceHandler::GetBoolAttr(const wxString& attr, bool defaultv)
1633 {
1634 wxString v;
1635 return m_node->GetAttribute(attr, &v) ? v == '1' : defaultv;
1636 }
1637
1638 bool wxXmlResourceHandler::GetBool(const wxString& param, bool defaultv)
1639 {
1640 const wxString v = GetParamValue(param);
1641
1642 return v.empty() ? defaultv : (v == '1');
1643 }
1644
1645
1646 static wxColour GetSystemColour(const wxString& name)
1647 {
1648 if (!name.empty())
1649 {
1650 #define SYSCLR(clr) \
1651 if (name == wxT(#clr)) return wxSystemSettings::GetColour(clr);
1652 SYSCLR(wxSYS_COLOUR_SCROLLBAR)
1653 SYSCLR(wxSYS_COLOUR_BACKGROUND)
1654 SYSCLR(wxSYS_COLOUR_DESKTOP)
1655 SYSCLR(wxSYS_COLOUR_ACTIVECAPTION)
1656 SYSCLR(wxSYS_COLOUR_INACTIVECAPTION)
1657 SYSCLR(wxSYS_COLOUR_MENU)
1658 SYSCLR(wxSYS_COLOUR_WINDOW)
1659 SYSCLR(wxSYS_COLOUR_WINDOWFRAME)
1660 SYSCLR(wxSYS_COLOUR_MENUTEXT)
1661 SYSCLR(wxSYS_COLOUR_WINDOWTEXT)
1662 SYSCLR(wxSYS_COLOUR_CAPTIONTEXT)
1663 SYSCLR(wxSYS_COLOUR_ACTIVEBORDER)
1664 SYSCLR(wxSYS_COLOUR_INACTIVEBORDER)
1665 SYSCLR(wxSYS_COLOUR_APPWORKSPACE)
1666 SYSCLR(wxSYS_COLOUR_HIGHLIGHT)
1667 SYSCLR(wxSYS_COLOUR_HIGHLIGHTTEXT)
1668 SYSCLR(wxSYS_COLOUR_BTNFACE)
1669 SYSCLR(wxSYS_COLOUR_3DFACE)
1670 SYSCLR(wxSYS_COLOUR_BTNSHADOW)
1671 SYSCLR(wxSYS_COLOUR_3DSHADOW)
1672 SYSCLR(wxSYS_COLOUR_GRAYTEXT)
1673 SYSCLR(wxSYS_COLOUR_BTNTEXT)
1674 SYSCLR(wxSYS_COLOUR_INACTIVECAPTIONTEXT)
1675 SYSCLR(wxSYS_COLOUR_BTNHIGHLIGHT)
1676 SYSCLR(wxSYS_COLOUR_BTNHILIGHT)
1677 SYSCLR(wxSYS_COLOUR_3DHIGHLIGHT)
1678 SYSCLR(wxSYS_COLOUR_3DHILIGHT)
1679 SYSCLR(wxSYS_COLOUR_3DDKSHADOW)
1680 SYSCLR(wxSYS_COLOUR_3DLIGHT)
1681 SYSCLR(wxSYS_COLOUR_INFOTEXT)
1682 SYSCLR(wxSYS_COLOUR_INFOBK)
1683 SYSCLR(wxSYS_COLOUR_LISTBOX)
1684 SYSCLR(wxSYS_COLOUR_HOTLIGHT)
1685 SYSCLR(wxSYS_COLOUR_GRADIENTACTIVECAPTION)
1686 SYSCLR(wxSYS_COLOUR_GRADIENTINACTIVECAPTION)
1687 SYSCLR(wxSYS_COLOUR_MENUHILIGHT)
1688 SYSCLR(wxSYS_COLOUR_MENUBAR)
1689 #undef SYSCLR
1690 }
1691
1692 return wxNullColour;
1693 }
1694
1695 wxColour wxXmlResourceHandler::GetColour(const wxString& param, const wxColour& defaultv)
1696 {
1697 wxString v = GetParamValue(param);
1698
1699 if ( v.empty() )
1700 return defaultv;
1701
1702 wxColour clr;
1703
1704 // wxString -> wxColour conversion
1705 if (!clr.Set(v))
1706 {
1707 // the colour doesn't use #RRGGBB format, check if it is symbolic
1708 // colour name:
1709 clr = GetSystemColour(v);
1710 if (clr.Ok())
1711 return clr;
1712
1713 ReportParamError
1714 (
1715 param,
1716 wxString::Format("incorrect colour specification \"%s\"", v)
1717 );
1718 return wxNullColour;
1719 }
1720
1721 return clr;
1722 }
1723
1724 namespace
1725 {
1726
1727 // if 'param' has stock_id/stock_client, extracts them and returns true
1728 bool GetStockArtAttrs(const wxXmlNode *paramNode,
1729 const wxString& defaultArtClient,
1730 wxString& art_id, wxString& art_client)
1731 {
1732 if ( paramNode )
1733 {
1734 art_id = paramNode->GetAttribute("stock_id", "");
1735
1736 if ( !art_id.empty() )
1737 {
1738 art_id = wxART_MAKE_ART_ID_FROM_STR(art_id);
1739
1740 art_client = paramNode->GetAttribute("stock_client", "");
1741 if ( art_client.empty() )
1742 art_client = defaultArtClient;
1743 else
1744 art_client = wxART_MAKE_CLIENT_ID_FROM_STR(art_client);
1745
1746 return true;
1747 }
1748 }
1749
1750 return false;
1751 }
1752
1753 } // anonymous namespace
1754
1755 wxBitmap wxXmlResourceHandler::GetBitmap(const wxString& param,
1756 const wxArtClient& defaultArtClient,
1757 wxSize size)
1758 {
1759 // it used to be possible to pass an empty string here to indicate that the
1760 // bitmap name should be read from this node itself but this is not
1761 // supported any more because GetBitmap(m_node) can be used directly
1762 // instead
1763 wxASSERT_MSG( !param.empty(), "bitmap parameter name can't be empty" );
1764
1765 const wxXmlNode* const node = GetParamNode(param);
1766
1767 if ( !node )
1768 {
1769 // this is not an error as bitmap parameter could be optional
1770 return wxNullBitmap;
1771 }
1772
1773 return GetBitmap(node, defaultArtClient, size);
1774 }
1775
1776 wxBitmap wxXmlResourceHandler::GetBitmap(const wxXmlNode* node,
1777 const wxArtClient& defaultArtClient,
1778 wxSize size)
1779 {
1780 wxCHECK_MSG( node, wxNullBitmap, "bitmap node can't be NULL" );
1781
1782 /* If the bitmap is specified as stock item, query wxArtProvider for it: */
1783 wxString art_id, art_client;
1784 if ( GetStockArtAttrs(node, defaultArtClient,
1785 art_id, art_client) )
1786 {
1787 wxBitmap stockArt(wxArtProvider::GetBitmap(art_id, art_client, size));
1788 if ( stockArt.Ok() )
1789 return stockArt;
1790 }
1791
1792 /* ...or load the bitmap from file: */
1793 wxString name = GetParamValue(node);
1794 if (name.empty()) return wxNullBitmap;
1795 #if wxUSE_FILESYSTEM
1796 wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
1797 if (fsfile == NULL)
1798 {
1799 ReportParamError
1800 (
1801 node->GetName(),
1802 wxString::Format("cannot open bitmap resource \"%s\"", name)
1803 );
1804 return wxNullBitmap;
1805 }
1806 wxImage img(*(fsfile->GetStream()));
1807 delete fsfile;
1808 #else
1809 wxImage img(name);
1810 #endif
1811
1812 if (!img.Ok())
1813 {
1814 ReportParamError
1815 (
1816 node->GetName(),
1817 wxString::Format("cannot create bitmap from \"%s\"", name)
1818 );
1819 return wxNullBitmap;
1820 }
1821 if (!(size == wxDefaultSize)) img.Rescale(size.x, size.y);
1822 return wxBitmap(img);
1823 }
1824
1825
1826 wxIcon wxXmlResourceHandler::GetIcon(const wxString& param,
1827 const wxArtClient& defaultArtClient,
1828 wxSize size)
1829 {
1830 // see comment in GetBitmap(wxString) overload
1831 wxASSERT_MSG( !param.empty(), "icon parameter name can't be empty" );
1832
1833 const wxXmlNode* const node = GetParamNode(param);
1834
1835 if ( !node )
1836 {
1837 // this is not an error as icon parameter could be optional
1838 return wxIcon();
1839 }
1840
1841 return GetIcon(node, defaultArtClient, size);
1842 }
1843
1844 wxIcon wxXmlResourceHandler::GetIcon(const wxXmlNode* node,
1845 const wxArtClient& defaultArtClient,
1846 wxSize size)
1847 {
1848 wxIcon icon;
1849 icon.CopyFromBitmap(GetBitmap(node, defaultArtClient, size));
1850 return icon;
1851 }
1852
1853
1854 wxIconBundle wxXmlResourceHandler::GetIconBundle(const wxString& param,
1855 const wxArtClient& defaultArtClient)
1856 {
1857 wxString art_id, art_client;
1858 if ( GetStockArtAttrs(GetParamNode(param), defaultArtClient,
1859 art_id, art_client) )
1860 {
1861 wxIconBundle stockArt(wxArtProvider::GetIconBundle(art_id, art_client));
1862 if ( stockArt.IsOk() )
1863 return stockArt;
1864 }
1865
1866 const wxString name = GetParamValue(param);
1867 if ( name.empty() )
1868 return wxNullIconBundle;
1869
1870 #if wxUSE_FILESYSTEM
1871 wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
1872 if ( fsfile == NULL )
1873 {
1874 ReportParamError
1875 (
1876 param,
1877 wxString::Format("cannot open icon resource \"%s\"", name)
1878 );
1879 return wxNullIconBundle;
1880 }
1881
1882 wxIconBundle bundle(*(fsfile->GetStream()));
1883 delete fsfile;
1884 #else
1885 wxIconBundle bundle(name);
1886 #endif
1887
1888 if ( !bundle.IsOk() )
1889 {
1890 ReportParamError
1891 (
1892 param,
1893 wxString::Format("cannot create icon from \"%s\"", name)
1894 );
1895 return wxNullIconBundle;
1896 }
1897
1898 return bundle;
1899 }
1900
1901
1902 wxImageList *wxXmlResourceHandler::GetImageList(const wxString& param)
1903 {
1904 wxXmlNode * const imagelist_node = GetParamNode(param);
1905 if ( !imagelist_node )
1906 return NULL;
1907
1908 wxXmlNode * const oldnode = m_node;
1909 m_node = imagelist_node;
1910
1911 // Get the size if we have it, otherwise we will use the size of the first
1912 // list element.
1913 wxSize size = GetSize();
1914
1915 // Start adding images, we'll create the image list when adding the first
1916 // one.
1917 wxImageList * imagelist = NULL;
1918 wxString parambitmap = wxT("bitmap");
1919 if ( HasParam(parambitmap) )
1920 {
1921 wxXmlNode *n = m_node->GetChildren();
1922 while (n)
1923 {
1924 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == parambitmap)
1925 {
1926 wxIcon icon = GetIcon(n);
1927 if ( !imagelist )
1928 {
1929 // We need the real image list size to create it.
1930 if ( size == wxDefaultSize )
1931 size = icon.GetSize();
1932
1933 // We use the mask by default.
1934 bool mask = !HasParam(wxS("mask")) || GetBool(wxS("mask"));
1935
1936 imagelist = new wxImageList(size.x, size.y, mask);
1937 }
1938
1939 // add icon instead of bitmap to keep the bitmap mask
1940 imagelist->Add(icon);
1941 }
1942 n = n->GetNext();
1943 }
1944 }
1945
1946 m_node = oldnode;
1947 return imagelist;
1948 }
1949
1950 wxXmlNode *wxXmlResourceHandler::GetParamNode(const wxString& param)
1951 {
1952 wxCHECK_MSG(m_node, NULL, wxT("You can't access handler data before it was initialized!"));
1953
1954 wxXmlNode *n = m_node->GetChildren();
1955
1956 while (n)
1957 {
1958 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
1959 {
1960 // TODO: check that there are no other properties/parameters with
1961 // the same name and log an error if there are (can't do this
1962 // right now as I'm not sure if it's not going to break code
1963 // using this function in unintentional way (i.e. for
1964 // accessing other things than properties), for example
1965 // wxBitmapComboBoxXmlHandler almost surely does
1966 return n;
1967 }
1968 n = n->GetNext();
1969 }
1970 return NULL;
1971 }
1972
1973 /* static */
1974 bool wxXmlResourceHandler::IsOfClass(wxXmlNode *node, const wxString& classname)
1975 {
1976 return node->GetAttribute(wxT("class")) == classname;
1977 }
1978
1979
1980
1981 wxString wxXmlResourceHandler::GetNodeContent(const wxXmlNode *node)
1982 {
1983 const wxXmlNode *n = node;
1984 if (n == NULL) return wxEmptyString;
1985 n = n->GetChildren();
1986
1987 while (n)
1988 {
1989 if (n->GetType() == wxXML_TEXT_NODE ||
1990 n->GetType() == wxXML_CDATA_SECTION_NODE)
1991 return n->GetContent();
1992 n = n->GetNext();
1993 }
1994 return wxEmptyString;
1995 }
1996
1997
1998
1999 wxString wxXmlResourceHandler::GetParamValue(const wxString& param)
2000 {
2001 if (param.empty())
2002 return GetNodeContent(m_node);
2003 else
2004 return GetNodeContent(GetParamNode(param));
2005 }
2006
2007 wxString wxXmlResourceHandler::GetParamValue(const wxXmlNode* node)
2008 {
2009 return GetNodeContent(node);
2010 }
2011
2012
2013 wxSize wxXmlResourceHandler::GetSize(const wxString& param,
2014 wxWindow *windowToUse)
2015 {
2016 wxString s = GetParamValue(param);
2017 if (s.empty()) s = wxT("-1,-1");
2018 bool is_dlg;
2019 long sx, sy = 0;
2020
2021 is_dlg = s[s.length()-1] == wxT('d');
2022 if (is_dlg) s.RemoveLast();
2023
2024 if (!s.BeforeFirst(wxT(',')).ToLong(&sx) ||
2025 !s.AfterLast(wxT(',')).ToLong(&sy))
2026 {
2027 ReportParamError
2028 (
2029 param,
2030 wxString::Format("cannot parse coordinates value \"%s\"", s)
2031 );
2032 return wxDefaultSize;
2033 }
2034
2035 if (is_dlg)
2036 {
2037 if (windowToUse)
2038 {
2039 return wxDLG_UNIT(windowToUse, wxSize(sx, sy));
2040 }
2041 else if (m_parentAsWindow)
2042 {
2043 return wxDLG_UNIT(m_parentAsWindow, wxSize(sx, sy));
2044 }
2045 else
2046 {
2047 ReportParamError
2048 (
2049 param,
2050 "cannot convert dialog units: dialog unknown"
2051 );
2052 return wxDefaultSize;
2053 }
2054 }
2055
2056 return wxSize(sx, sy);
2057 }
2058
2059
2060
2061 wxPoint wxXmlResourceHandler::GetPosition(const wxString& param)
2062 {
2063 wxSize sz = GetSize(param);
2064 return wxPoint(sz.x, sz.y);
2065 }
2066
2067
2068
2069 wxCoord wxXmlResourceHandler::GetDimension(const wxString& param,
2070 wxCoord defaultv,
2071 wxWindow *windowToUse)
2072 {
2073 wxString s = GetParamValue(param);
2074 if (s.empty()) return defaultv;
2075 bool is_dlg;
2076 long sx;
2077
2078 is_dlg = s[s.length()-1] == wxT('d');
2079 if (is_dlg) s.RemoveLast();
2080
2081 if (!s.ToLong(&sx))
2082 {
2083 ReportParamError
2084 (
2085 param,
2086 wxString::Format("cannot parse dimension value \"%s\"", s)
2087 );
2088 return defaultv;
2089 }
2090
2091 if (is_dlg)
2092 {
2093 if (windowToUse)
2094 {
2095 return wxDLG_UNIT(windowToUse, wxSize(sx, 0)).x;
2096 }
2097 else if (m_parentAsWindow)
2098 {
2099 return wxDLG_UNIT(m_parentAsWindow, wxSize(sx, 0)).x;
2100 }
2101 else
2102 {
2103 ReportParamError
2104 (
2105 param,
2106 "cannot convert dialog units: dialog unknown"
2107 );
2108 return defaultv;
2109 }
2110 }
2111
2112 return sx;
2113 }
2114
2115
2116 // Get system font index using indexname
2117 static wxFont GetSystemFont(const wxString& name)
2118 {
2119 if (!name.empty())
2120 {
2121 #define SYSFNT(fnt) \
2122 if (name == wxT(#fnt)) return wxSystemSettings::GetFont(fnt);
2123 SYSFNT(wxSYS_OEM_FIXED_FONT)
2124 SYSFNT(wxSYS_ANSI_FIXED_FONT)
2125 SYSFNT(wxSYS_ANSI_VAR_FONT)
2126 SYSFNT(wxSYS_SYSTEM_FONT)
2127 SYSFNT(wxSYS_DEVICE_DEFAULT_FONT)
2128 SYSFNT(wxSYS_SYSTEM_FIXED_FONT)
2129 SYSFNT(wxSYS_DEFAULT_GUI_FONT)
2130 #undef SYSFNT
2131 }
2132
2133 return wxNullFont;
2134 }
2135
2136 wxFont wxXmlResourceHandler::GetFont(const wxString& param)
2137 {
2138 wxXmlNode *font_node = GetParamNode(param);
2139 if (font_node == NULL)
2140 {
2141 ReportError(
2142 wxString::Format("cannot find font node \"%s\"", param));
2143 return wxNullFont;
2144 }
2145
2146 wxXmlNode *oldnode = m_node;
2147 m_node = font_node;
2148
2149 // font attributes:
2150
2151 // size
2152 int isize = -1;
2153 bool hasSize = HasParam(wxT("size"));
2154 if (hasSize)
2155 isize = GetLong(wxT("size"), -1);
2156
2157 // style
2158 int istyle = wxNORMAL;
2159 bool hasStyle = HasParam(wxT("style"));
2160 if (hasStyle)
2161 {
2162 wxString style = GetParamValue(wxT("style"));
2163 if (style == wxT("italic"))
2164 istyle = wxITALIC;
2165 else if (style == wxT("slant"))
2166 istyle = wxSLANT;
2167 }
2168
2169 // weight
2170 int iweight = wxNORMAL;
2171 bool hasWeight = HasParam(wxT("weight"));
2172 if (hasWeight)
2173 {
2174 wxString weight = GetParamValue(wxT("weight"));
2175 if (weight == wxT("bold"))
2176 iweight = wxBOLD;
2177 else if (weight == wxT("light"))
2178 iweight = wxLIGHT;
2179 }
2180
2181 // underline
2182 bool hasUnderlined = HasParam(wxT("underlined"));
2183 bool underlined = hasUnderlined ? GetBool(wxT("underlined"), false) : false;
2184
2185 // family and facename
2186 int ifamily = wxDEFAULT;
2187 bool hasFamily = HasParam(wxT("family"));
2188 if (hasFamily)
2189 {
2190 wxString family = GetParamValue(wxT("family"));
2191 if (family == wxT("decorative")) ifamily = wxDECORATIVE;
2192 else if (family == wxT("roman")) ifamily = wxROMAN;
2193 else if (family == wxT("script")) ifamily = wxSCRIPT;
2194 else if (family == wxT("swiss")) ifamily = wxSWISS;
2195 else if (family == wxT("modern")) ifamily = wxMODERN;
2196 else if (family == wxT("teletype")) ifamily = wxTELETYPE;
2197 }
2198
2199
2200 wxString facename;
2201 bool hasFacename = HasParam(wxT("face"));
2202 if (hasFacename)
2203 {
2204 wxString faces = GetParamValue(wxT("face"));
2205 wxStringTokenizer tk(faces, wxT(","));
2206 #if wxUSE_FONTENUM
2207 wxArrayString facenames(wxFontEnumerator::GetFacenames());
2208 while (tk.HasMoreTokens())
2209 {
2210 int index = facenames.Index(tk.GetNextToken(), false);
2211 if (index != wxNOT_FOUND)
2212 {
2213 facename = facenames[index];
2214 break;
2215 }
2216 }
2217 #else // !wxUSE_FONTENUM
2218 // just use the first face name if we can't check its availability:
2219 if (tk.HasMoreTokens())
2220 facename = tk.GetNextToken();
2221 #endif // wxUSE_FONTENUM/!wxUSE_FONTENUM
2222 }
2223
2224 // encoding
2225 wxFontEncoding enc = wxFONTENCODING_DEFAULT;
2226 bool hasEncoding = HasParam(wxT("encoding"));
2227 #if wxUSE_FONTMAP
2228 if (hasEncoding)
2229 {
2230 wxString encoding = GetParamValue(wxT("encoding"));
2231 wxFontMapper mapper;
2232 if (!encoding.empty())
2233 enc = mapper.CharsetToEncoding(encoding);
2234 if (enc == wxFONTENCODING_SYSTEM)
2235 enc = wxFONTENCODING_DEFAULT;
2236 }
2237 #endif // wxUSE_FONTMAP
2238
2239 // is this font based on a system font?
2240 wxFont font = GetSystemFont(GetParamValue(wxT("sysfont")));
2241
2242 if (font.Ok())
2243 {
2244 if (hasSize && isize != -1)
2245 font.SetPointSize(isize);
2246 else if (HasParam(wxT("relativesize")))
2247 font.SetPointSize(int(font.GetPointSize() *
2248 GetFloat(wxT("relativesize"))));
2249
2250 if (hasStyle)
2251 font.SetStyle(istyle);
2252 if (hasWeight)
2253 font.SetWeight(iweight);
2254 if (hasUnderlined)
2255 font.SetUnderlined(underlined);
2256 if (hasFamily)
2257 font.SetFamily(ifamily);
2258 if (hasFacename)
2259 font.SetFaceName(facename);
2260 if (hasEncoding)
2261 font.SetDefaultEncoding(enc);
2262 }
2263 else // not based on system font
2264 {
2265 font = wxFont(isize == -1 ? wxNORMAL_FONT->GetPointSize() : isize,
2266 ifamily, istyle, iweight,
2267 underlined, facename, enc);
2268 }
2269
2270 m_node = oldnode;
2271 return font;
2272 }
2273
2274
2275 void wxXmlResourceHandler::SetupWindow(wxWindow *wnd)
2276 {
2277 //FIXME : add cursor
2278
2279 if (HasParam(wxT("exstyle")))
2280 // Have to OR it with existing style, since
2281 // some implementations (e.g. wxGTK) use the extra style
2282 // during creation
2283 wnd->SetExtraStyle(wnd->GetExtraStyle() | GetStyle(wxT("exstyle")));
2284 if (HasParam(wxT("bg")))
2285 wnd->SetBackgroundColour(GetColour(wxT("bg")));
2286 if (HasParam(wxT("ownbg")))
2287 wnd->SetOwnBackgroundColour(GetColour(wxT("ownbg")));
2288 if (HasParam(wxT("fg")))
2289 wnd->SetForegroundColour(GetColour(wxT("fg")));
2290 if (HasParam(wxT("ownfg")))
2291 wnd->SetOwnForegroundColour(GetColour(wxT("ownfg")));
2292 if (GetBool(wxT("enabled"), 1) == 0)
2293 wnd->Enable(false);
2294 if (GetBool(wxT("focused"), 0) == 1)
2295 wnd->SetFocus();
2296 if (GetBool(wxT("hidden"), 0) == 1)
2297 wnd->Show(false);
2298 #if wxUSE_TOOLTIPS
2299 if (HasParam(wxT("tooltip")))
2300 wnd->SetToolTip(GetText(wxT("tooltip")));
2301 #endif
2302 if (HasParam(wxT("font")))
2303 wnd->SetFont(GetFont(wxT("font")));
2304 if (HasParam(wxT("ownfont")))
2305 wnd->SetOwnFont(GetFont(wxT("ownfont")));
2306 if (HasParam(wxT("help")))
2307 wnd->SetHelpText(GetText(wxT("help")));
2308 }
2309
2310
2311 void wxXmlResourceHandler::CreateChildren(wxObject *parent, bool this_hnd_only)
2312 {
2313 for ( wxXmlNode *n = m_node->GetChildren(); n; n = n->GetNext() )
2314 {
2315 if ( IsObjectNode(n) )
2316 {
2317 m_resource->DoCreateResFromNode(*n, parent, NULL,
2318 this_hnd_only ? this : NULL);
2319 }
2320 }
2321 }
2322
2323
2324 void wxXmlResourceHandler::CreateChildrenPrivately(wxObject *parent, wxXmlNode *rootnode)
2325 {
2326 wxXmlNode *root;
2327 if (rootnode == NULL) root = m_node; else root = rootnode;
2328 wxXmlNode *n = root->GetChildren();
2329
2330 while (n)
2331 {
2332 if (n->GetType() == wxXML_ELEMENT_NODE && CanHandle(n))
2333 {
2334 CreateResource(n, parent, NULL);
2335 }
2336 n = n->GetNext();
2337 }
2338 }
2339
2340
2341 //-----------------------------------------------------------------------------
2342 // errors reporting
2343 //-----------------------------------------------------------------------------
2344
2345 void wxXmlResourceHandler::ReportError(const wxString& message)
2346 {
2347 m_resource->ReportError(m_node, message);
2348 }
2349
2350 void wxXmlResourceHandler::ReportError(wxXmlNode *context,
2351 const wxString& message)
2352 {
2353 m_resource->ReportError(context ? context : m_node, message);
2354 }
2355
2356 void wxXmlResourceHandler::ReportParamError(const wxString& param,
2357 const wxString& message)
2358 {
2359 m_resource->ReportError(GetParamNode(param), message);
2360 }
2361
2362 void wxXmlResource::ReportError(const wxXmlNode *context, const wxString& message)
2363 {
2364 if ( !context )
2365 {
2366 DoReportError("", NULL, message);
2367 return;
2368 }
2369
2370 // We need to find out the file that 'context' is part of. Performance of
2371 // this code is not critical, so we simply find the root XML node and
2372 // compare it with all loaded XRC files.
2373 const wxString filename = GetFileNameFromNode(context, Data());
2374
2375 DoReportError(filename, context, message);
2376 }
2377
2378 void wxXmlResource::DoReportError(const wxString& xrcFile, const wxXmlNode *position,
2379 const wxString& message)
2380 {
2381 const int line = position ? position->GetLineNumber() : -1;
2382
2383 wxString loc;
2384 if ( !xrcFile.empty() )
2385 loc = xrcFile + ':';
2386 if ( line != -1 )
2387 loc += wxString::Format("%d:", line);
2388 if ( !loc.empty() )
2389 loc += ' ';
2390
2391 wxLogError("XRC error: %s%s", loc, message);
2392 }
2393
2394
2395 //-----------------------------------------------------------------------------
2396 // XRCID implementation
2397 //-----------------------------------------------------------------------------
2398
2399 #define XRCID_TABLE_SIZE 1024
2400
2401
2402 struct XRCID_record
2403 {
2404 /* Hold the id so that once an id is allocated for a name, it
2405 does not get created again by NewControlId at least
2406 until we are done with it */
2407 wxWindowIDRef id;
2408 char *key;
2409 XRCID_record *next;
2410 };
2411
2412 static XRCID_record *XRCID_Records[XRCID_TABLE_SIZE] = {NULL};
2413
2414 // Extremely simplistic hash function which probably ought to be replaced with
2415 // wxStringHash::stringHash().
2416 static inline unsigned XRCIdHash(const char *str_id)
2417 {
2418 unsigned index = 0;
2419
2420 for (const char *c = str_id; *c != '\0'; c++) index += (unsigned int)*c;
2421 index %= XRCID_TABLE_SIZE;
2422
2423 return index;
2424 }
2425
2426 static int XRCID_Lookup(const char *str_id, int value_if_not_found = wxID_NONE)
2427 {
2428 const unsigned index = XRCIdHash(str_id);
2429
2430
2431 XRCID_record *oldrec = NULL;
2432 for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
2433 {
2434 if (wxStrcmp(rec->key, str_id) == 0)
2435 {
2436 return rec->id;
2437 }
2438 oldrec = rec;
2439 }
2440
2441 XRCID_record **rec_var = (oldrec == NULL) ?
2442 &XRCID_Records[index] : &oldrec->next;
2443 *rec_var = new XRCID_record;
2444 (*rec_var)->key = wxStrdup(str_id);
2445 (*rec_var)->next = NULL;
2446
2447 char *end;
2448 if (value_if_not_found != wxID_NONE)
2449 (*rec_var)->id = value_if_not_found;
2450 else
2451 {
2452 int asint = wxStrtol(str_id, &end, 10);
2453 if (*str_id && *end == 0)
2454 {
2455 // if str_id was integer, keep it verbosely:
2456 (*rec_var)->id = asint;
2457 }
2458 else
2459 {
2460 (*rec_var)->id = wxWindowBase::NewControlId();
2461 }
2462 }
2463
2464 return (*rec_var)->id;
2465 }
2466
2467 namespace
2468 {
2469
2470 // flag indicating whether standard XRC ids were already initialized
2471 static bool gs_stdIDsAdded = false;
2472
2473 void AddStdXRCID_Records()
2474 {
2475 #define stdID(id) XRCID_Lookup(#id, id)
2476 stdID(-1);
2477
2478 stdID(wxID_ANY);
2479 stdID(wxID_SEPARATOR);
2480
2481 stdID(wxID_OPEN);
2482 stdID(wxID_CLOSE);
2483 stdID(wxID_NEW);
2484 stdID(wxID_SAVE);
2485 stdID(wxID_SAVEAS);
2486 stdID(wxID_REVERT);
2487 stdID(wxID_EXIT);
2488 stdID(wxID_UNDO);
2489 stdID(wxID_REDO);
2490 stdID(wxID_HELP);
2491 stdID(wxID_PRINT);
2492 stdID(wxID_PRINT_SETUP);
2493 stdID(wxID_PAGE_SETUP);
2494 stdID(wxID_PREVIEW);
2495 stdID(wxID_ABOUT);
2496 stdID(wxID_HELP_CONTENTS);
2497 stdID(wxID_HELP_COMMANDS);
2498 stdID(wxID_HELP_PROCEDURES);
2499 stdID(wxID_HELP_CONTEXT);
2500 stdID(wxID_CLOSE_ALL);
2501 stdID(wxID_PREFERENCES);
2502 stdID(wxID_EDIT);
2503 stdID(wxID_CUT);
2504 stdID(wxID_COPY);
2505 stdID(wxID_PASTE);
2506 stdID(wxID_CLEAR);
2507 stdID(wxID_FIND);
2508 stdID(wxID_DUPLICATE);
2509 stdID(wxID_SELECTALL);
2510 stdID(wxID_DELETE);
2511 stdID(wxID_REPLACE);
2512 stdID(wxID_REPLACE_ALL);
2513 stdID(wxID_PROPERTIES);
2514 stdID(wxID_VIEW_DETAILS);
2515 stdID(wxID_VIEW_LARGEICONS);
2516 stdID(wxID_VIEW_SMALLICONS);
2517 stdID(wxID_VIEW_LIST);
2518 stdID(wxID_VIEW_SORTDATE);
2519 stdID(wxID_VIEW_SORTNAME);
2520 stdID(wxID_VIEW_SORTSIZE);
2521 stdID(wxID_VIEW_SORTTYPE);
2522 stdID(wxID_FILE1);
2523 stdID(wxID_FILE2);
2524 stdID(wxID_FILE3);
2525 stdID(wxID_FILE4);
2526 stdID(wxID_FILE5);
2527 stdID(wxID_FILE6);
2528 stdID(wxID_FILE7);
2529 stdID(wxID_FILE8);
2530 stdID(wxID_FILE9);
2531 stdID(wxID_OK);
2532 stdID(wxID_CANCEL);
2533 stdID(wxID_APPLY);
2534 stdID(wxID_YES);
2535 stdID(wxID_NO);
2536 stdID(wxID_STATIC);
2537 stdID(wxID_FORWARD);
2538 stdID(wxID_BACKWARD);
2539 stdID(wxID_DEFAULT);
2540 stdID(wxID_MORE);
2541 stdID(wxID_SETUP);
2542 stdID(wxID_RESET);
2543 stdID(wxID_CONTEXT_HELP);
2544 stdID(wxID_YESTOALL);
2545 stdID(wxID_NOTOALL);
2546 stdID(wxID_ABORT);
2547 stdID(wxID_RETRY);
2548 stdID(wxID_IGNORE);
2549 stdID(wxID_ADD);
2550 stdID(wxID_REMOVE);
2551 stdID(wxID_UP);
2552 stdID(wxID_DOWN);
2553 stdID(wxID_HOME);
2554 stdID(wxID_REFRESH);
2555 stdID(wxID_STOP);
2556 stdID(wxID_INDEX);
2557 stdID(wxID_BOLD);
2558 stdID(wxID_ITALIC);
2559 stdID(wxID_JUSTIFY_CENTER);
2560 stdID(wxID_JUSTIFY_FILL);
2561 stdID(wxID_JUSTIFY_RIGHT);
2562 stdID(wxID_JUSTIFY_LEFT);
2563 stdID(wxID_UNDERLINE);
2564 stdID(wxID_INDENT);
2565 stdID(wxID_UNINDENT);
2566 stdID(wxID_ZOOM_100);
2567 stdID(wxID_ZOOM_FIT);
2568 stdID(wxID_ZOOM_IN);
2569 stdID(wxID_ZOOM_OUT);
2570 stdID(wxID_UNDELETE);
2571 stdID(wxID_REVERT_TO_SAVED);
2572 stdID(wxID_SYSTEM_MENU);
2573 stdID(wxID_CLOSE_FRAME);
2574 stdID(wxID_MOVE_FRAME);
2575 stdID(wxID_RESIZE_FRAME);
2576 stdID(wxID_MAXIMIZE_FRAME);
2577 stdID(wxID_ICONIZE_FRAME);
2578 stdID(wxID_RESTORE_FRAME);
2579 stdID(wxID_CDROM);
2580 stdID(wxID_CONVERT);
2581 stdID(wxID_EXECUTE);
2582 stdID(wxID_FLOPPY);
2583 stdID(wxID_HARDDISK);
2584 stdID(wxID_BOTTOM);
2585 stdID(wxID_FIRST);
2586 stdID(wxID_LAST);
2587 stdID(wxID_TOP);
2588 stdID(wxID_INFO);
2589 stdID(wxID_JUMP_TO);
2590 stdID(wxID_NETWORK);
2591 stdID(wxID_SELECT_COLOR);
2592 stdID(wxID_SELECT_FONT);
2593 stdID(wxID_SORT_ASCENDING);
2594 stdID(wxID_SORT_DESCENDING);
2595 stdID(wxID_SPELL_CHECK);
2596 stdID(wxID_STRIKETHROUGH);
2597
2598 #undef stdID
2599 }
2600
2601 } // anonymous namespace
2602
2603
2604 /*static*/
2605 int wxXmlResource::DoGetXRCID(const char *str_id, int value_if_not_found)
2606 {
2607 if ( !gs_stdIDsAdded )
2608 {
2609 gs_stdIDsAdded = true;
2610 AddStdXRCID_Records();
2611 }
2612
2613 return XRCID_Lookup(str_id, value_if_not_found);
2614 }
2615
2616 /* static */
2617 wxString wxXmlResource::FindXRCIDById(int numId)
2618 {
2619 for ( int i = 0; i < XRCID_TABLE_SIZE; i++ )
2620 {
2621 for ( XRCID_record *rec = XRCID_Records[i]; rec; rec = rec->next )
2622 {
2623 if ( rec->id == numId )
2624 return wxString(rec->key);
2625 }
2626 }
2627
2628 return wxString();
2629 }
2630
2631 /* static */
2632 void wxIdRangeManager::RemoveXRCIDEntry(const wxString& idstr)
2633 {
2634 const char *str_id = idstr.mb_str();
2635
2636 const unsigned index = XRCIdHash(str_id);
2637
2638 XRCID_record **p_previousrec = &XRCID_Records[index];
2639 for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
2640 {
2641 if (wxStrcmp(rec->key, str_id) == 0)
2642 {
2643 // Found the item to be removed so delete its record; but first
2644 // remove it from the linked list.
2645 *p_previousrec = rec->next;
2646 free(rec->key);
2647 delete rec;
2648 return;
2649 }
2650
2651 p_previousrec = &rec->next;
2652 }
2653 }
2654
2655 static void CleanXRCID_Record(XRCID_record *rec)
2656 {
2657 if (rec)
2658 {
2659 CleanXRCID_Record(rec->next);
2660
2661 free(rec->key);
2662 delete rec;
2663 }
2664 }
2665
2666 static void CleanXRCID_Records()
2667 {
2668 for (int i = 0; i < XRCID_TABLE_SIZE; i++)
2669 {
2670 CleanXRCID_Record(XRCID_Records[i]);
2671 XRCID_Records[i] = NULL;
2672 }
2673
2674 gs_stdIDsAdded = false;
2675 }
2676
2677
2678 //-----------------------------------------------------------------------------
2679 // module and globals
2680 //-----------------------------------------------------------------------------
2681
2682 // normally we would do the cleanup from wxXmlResourceModule::OnExit() but it
2683 // can happen that some XRC records have been created because of the use of
2684 // XRCID() in event tables, which happens during static objects initialization,
2685 // but then the application initialization failed and so the wx modules were
2686 // neither initialized nor cleaned up -- this static object does the cleanup in
2687 // this case
2688 static struct wxXRCStaticCleanup
2689 {
2690 ~wxXRCStaticCleanup() { CleanXRCID_Records(); }
2691 } s_staticCleanup;
2692
2693 class wxXmlResourceModule: public wxModule
2694 {
2695 DECLARE_DYNAMIC_CLASS(wxXmlResourceModule)
2696 public:
2697 wxXmlResourceModule() {}
2698 bool OnInit()
2699 {
2700 wxXmlResource::AddSubclassFactory(new wxXmlSubclassFactoryCXX);
2701 return true;
2702 }
2703 void OnExit()
2704 {
2705 delete wxXmlResource::Set(NULL);
2706 delete wxIdRangeManager::Set(NULL);
2707 if(wxXmlResource::ms_subclassFactories)
2708 {
2709 for ( wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
2710 i != wxXmlResource::ms_subclassFactories->end(); ++i )
2711 {
2712 delete *i;
2713 }
2714 wxDELETE(wxXmlResource::ms_subclassFactories);
2715 }
2716 CleanXRCID_Records();
2717 }
2718 };
2719
2720 IMPLEMENT_DYNAMIC_CLASS(wxXmlResourceModule, wxModule)
2721
2722
2723 // When wxXml is loaded dynamically after the application is already running
2724 // then the built-in module system won't pick this one up. Add it manually.
2725 void wxXmlInitResourceModule()
2726 {
2727 wxModule* module = new wxXmlResourceModule;
2728 module->Init();
2729 wxModule::RegisterModule(module);
2730 }
2731
2732 #endif // wxUSE_XRC