Factor our hash function used for XRC ids hash map.
[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 char *str_id);
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","integer = %i %s now returns %i", i,
1189 m_name + wxString::Format("[%i]", i-m_start).mb_str(),
1190 XRCID(m_name + wxString::Format("[%i]", i-m_start).mb_str()));
1191 }
1192 // and these special ones
1193 wxIdRangeManager::RemoveXRCIDEntry(m_name + "[start]");
1194 wxXmlResource::GetXRCID(m_name + "[start]", m_start);
1195 wxIdRangeManager::RemoveXRCIDEntry(m_name + "[end]");
1196 wxXmlResource::GetXRCID(m_name + "[end]", m_end);
1197 wxLogTrace("xrcrange","%s[start] = %i %s[end] = %i",
1198 m_name.mb_str(),XRCID(wxString(m_name+"[start]").mb_str()),
1199 m_name.mb_str(),XRCID(wxString(m_name+"[end]").mb_str()));
1200
1201 m_finalised = true;
1202 }
1203
1204 wxIdRangeManager *wxIdRangeManager::ms_instance = NULL;
1205
1206 /*static*/ wxIdRangeManager *wxIdRangeManager::Get()
1207 {
1208 if ( !ms_instance )
1209 ms_instance = new wxIdRangeManager;
1210 return ms_instance;
1211 }
1212
1213 /*static*/ wxIdRangeManager *wxIdRangeManager::Set(wxIdRangeManager *res)
1214 {
1215 wxIdRangeManager *old = ms_instance;
1216 ms_instance = res;
1217 return old;
1218 }
1219
1220 wxIdRangeManager::~wxIdRangeManager()
1221 {
1222 for ( wxVector<wxIdRange*>::iterator i = m_IdRanges.begin();
1223 i != m_IdRanges.end(); ++i )
1224 {
1225 delete *i;
1226 }
1227 m_IdRanges.clear();
1228
1229 delete ms_instance;
1230 }
1231
1232 void wxIdRangeManager::AddRange(const wxXmlNode* node)
1233 {
1234 wxString name = node->GetAttribute("name");
1235 wxString start = node->GetAttribute("start", "0");
1236 wxString size = node->GetAttribute("size", "0");
1237 if (name.empty())
1238 {
1239 wxXmlResource::Get()->ReportError
1240 (
1241 node,
1242 "xrc file contains an id-range without a name"
1243 );
1244 return;
1245 }
1246
1247 int index = Find(name);
1248 if (index == wxNOT_FOUND)
1249 {
1250 wxLogTrace("xrcrange",
1251 "Adding ID range, name=%s start=%s size=%s",
1252 name, start, size);
1253
1254 m_IdRanges.push_back(new wxIdRange(node, name, start, size));
1255 }
1256 else
1257 {
1258 // There was already a range with this name. Let's hope this is
1259 // from an Unload()/(re)Load(), not an unintentional duplication
1260 wxLogTrace("xrcrange",
1261 "Replacing ID range, name=%s start=%s size=%s",
1262 name, start, size);
1263
1264 wxIdRange* oldrange = m_IdRanges.at(index);
1265 m_IdRanges.at(index) = new wxIdRange(node, name, start, size);
1266 delete oldrange;
1267 }
1268 }
1269
1270 wxIdRange *
1271 wxIdRangeManager::FindRangeForItem(const wxXmlNode* node,
1272 const wxString& item,
1273 wxString& value) const
1274 {
1275 wxString basename = item.BeforeFirst('[');
1276 wxCHECK_MSG( !basename.empty(), NULL,
1277 "an id-range item without a range name" );
1278
1279 int index = Find(basename);
1280 if (index == wxNOT_FOUND)
1281 {
1282 // Don't assert just because we've found an unexpected foo[123]
1283 // Someone might just want such a name, nothing to do with ranges
1284 return NULL;
1285 }
1286
1287 value = item.Mid(basename.Len());
1288 if (value.at(value.length()-1)==']')
1289 {
1290 return m_IdRanges.at(index);
1291 }
1292 wxXmlResource::Get()->ReportError(node, "a malformed id-range item");
1293 return NULL;
1294 }
1295
1296 void
1297 wxIdRangeManager::NotifyRangeOfItem(const wxXmlNode* node,
1298 const wxString& item) const
1299 {
1300 wxString value;
1301 wxIdRange* range = FindRangeForItem(node, item, value);
1302 if (range)
1303 range->NoteItem(node, value);
1304 }
1305
1306 int wxIdRangeManager::Find(const wxString& rangename) const
1307 {
1308 for ( int i=0; i < (int)m_IdRanges.size(); ++i )
1309 {
1310 if (m_IdRanges.at(i)->GetName() == rangename)
1311 return i;
1312 }
1313
1314 return wxNOT_FOUND;
1315 }
1316
1317 void wxIdRangeManager::FinaliseRanges(const wxXmlNode* node) const
1318 {
1319 for ( wxVector<wxIdRange*>::const_iterator i = m_IdRanges.begin();
1320 i != m_IdRanges.end(); ++i )
1321 {
1322 // Check if this range has already been finalised. Quite possible,
1323 // as FinaliseRanges() gets called for each .xrc file loaded
1324 if (!(*i)->IsFinalised())
1325 {
1326 wxLogTrace("xrcrange", "Finalising ID range %s", (*i)->GetName());
1327 (*i)->Finalise(node);
1328 }
1329 }
1330 }
1331
1332
1333 class wxXmlSubclassFactories : public wxVector<wxXmlSubclassFactory*>
1334 {
1335 // this is a class so that it can be forward-declared
1336 };
1337
1338 wxXmlSubclassFactories *wxXmlResource::ms_subclassFactories = NULL;
1339
1340 /*static*/ void wxXmlResource::AddSubclassFactory(wxXmlSubclassFactory *factory)
1341 {
1342 if (!ms_subclassFactories)
1343 {
1344 ms_subclassFactories = new wxXmlSubclassFactories;
1345 }
1346 ms_subclassFactories->push_back(factory);
1347 }
1348
1349 class wxXmlSubclassFactoryCXX : public wxXmlSubclassFactory
1350 {
1351 public:
1352 ~wxXmlSubclassFactoryCXX() {}
1353
1354 wxObject *Create(const wxString& className)
1355 {
1356 wxClassInfo* classInfo = wxClassInfo::FindClass(className);
1357
1358 if (classInfo)
1359 return classInfo->CreateObject();
1360 else
1361 return NULL;
1362 }
1363 };
1364
1365
1366
1367
1368 wxXmlResourceHandler::wxXmlResourceHandler()
1369 : m_node(NULL), m_parent(NULL), m_instance(NULL),
1370 m_parentAsWindow(NULL)
1371 {}
1372
1373
1374
1375 wxObject *wxXmlResourceHandler::CreateResource(wxXmlNode *node, wxObject *parent, wxObject *instance)
1376 {
1377 wxXmlNode *myNode = m_node;
1378 wxString myClass = m_class;
1379 wxObject *myParent = m_parent, *myInstance = m_instance;
1380 wxWindow *myParentAW = m_parentAsWindow;
1381
1382 m_instance = instance;
1383 if (!m_instance && node->HasAttribute(wxT("subclass")) &&
1384 !(m_resource->GetFlags() & wxXRC_NO_SUBCLASSING))
1385 {
1386 wxString subclass = node->GetAttribute(wxT("subclass"), wxEmptyString);
1387 if (!subclass.empty())
1388 {
1389 for (wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
1390 i != wxXmlResource::ms_subclassFactories->end(); ++i)
1391 {
1392 m_instance = (*i)->Create(subclass);
1393 if (m_instance)
1394 break;
1395 }
1396
1397 if (!m_instance)
1398 {
1399 wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
1400 ReportError
1401 (
1402 node,
1403 wxString::Format
1404 (
1405 "subclass \"%s\" not found for resource \"%s\", not subclassing",
1406 subclass, name
1407 )
1408 );
1409 }
1410 }
1411 }
1412
1413 m_node = node;
1414 m_class = node->GetAttribute(wxT("class"), wxEmptyString);
1415 m_parent = parent;
1416 m_parentAsWindow = wxDynamicCast(m_parent, wxWindow);
1417
1418 wxObject *returned = DoCreateResource();
1419
1420 m_node = myNode;
1421 m_class = myClass;
1422 m_parent = myParent; m_parentAsWindow = myParentAW;
1423 m_instance = myInstance;
1424
1425 return returned;
1426 }
1427
1428
1429 void wxXmlResourceHandler::AddStyle(const wxString& name, int value)
1430 {
1431 m_styleNames.Add(name);
1432 m_styleValues.Add(value);
1433 }
1434
1435
1436
1437 void wxXmlResourceHandler::AddWindowStyles()
1438 {
1439 XRC_ADD_STYLE(wxCLIP_CHILDREN);
1440
1441 // the border styles all have the old and new names, recognize both for now
1442 XRC_ADD_STYLE(wxSIMPLE_BORDER); XRC_ADD_STYLE(wxBORDER_SIMPLE);
1443 XRC_ADD_STYLE(wxSUNKEN_BORDER); XRC_ADD_STYLE(wxBORDER_SUNKEN);
1444 XRC_ADD_STYLE(wxDOUBLE_BORDER); XRC_ADD_STYLE(wxBORDER_DOUBLE); // deprecated
1445 XRC_ADD_STYLE(wxBORDER_THEME);
1446 XRC_ADD_STYLE(wxRAISED_BORDER); XRC_ADD_STYLE(wxBORDER_RAISED);
1447 XRC_ADD_STYLE(wxSTATIC_BORDER); XRC_ADD_STYLE(wxBORDER_STATIC);
1448 XRC_ADD_STYLE(wxNO_BORDER); XRC_ADD_STYLE(wxBORDER_NONE);
1449
1450 XRC_ADD_STYLE(wxTRANSPARENT_WINDOW);
1451 XRC_ADD_STYLE(wxWANTS_CHARS);
1452 XRC_ADD_STYLE(wxTAB_TRAVERSAL);
1453 XRC_ADD_STYLE(wxNO_FULL_REPAINT_ON_RESIZE);
1454 XRC_ADD_STYLE(wxFULL_REPAINT_ON_RESIZE);
1455 XRC_ADD_STYLE(wxALWAYS_SHOW_SB);
1456 XRC_ADD_STYLE(wxWS_EX_BLOCK_EVENTS);
1457 XRC_ADD_STYLE(wxWS_EX_VALIDATE_RECURSIVELY);
1458 }
1459
1460
1461
1462 bool wxXmlResourceHandler::HasParam(const wxString& param)
1463 {
1464 return (GetParamNode(param) != NULL);
1465 }
1466
1467
1468 int wxXmlResourceHandler::GetStyle(const wxString& param, int defaults)
1469 {
1470 wxString s = GetParamValue(param);
1471
1472 if (!s) return defaults;
1473
1474 wxStringTokenizer tkn(s, wxT("| \t\n"), wxTOKEN_STRTOK);
1475 int style = 0;
1476 int index;
1477 wxString fl;
1478 while (tkn.HasMoreTokens())
1479 {
1480 fl = tkn.GetNextToken();
1481 index = m_styleNames.Index(fl);
1482 if (index != wxNOT_FOUND)
1483 {
1484 style |= m_styleValues[index];
1485 }
1486 else
1487 {
1488 ReportParamError
1489 (
1490 param,
1491 wxString::Format("unknown style flag \"%s\"", fl)
1492 );
1493 }
1494 }
1495 return style;
1496 }
1497
1498
1499
1500 wxString wxXmlResourceHandler::GetText(const wxString& param, bool translate)
1501 {
1502 wxXmlNode *parNode = GetParamNode(param);
1503 wxString str1(GetNodeContent(parNode));
1504 wxString str2;
1505
1506 // "\\" wasn't translated to "\" prior to 2.5.3.0:
1507 const bool escapeBackslash = (m_resource->CompareVersion(2,5,3,0) >= 0);
1508
1509 // VS: First version of XRC resources used $ instead of & (which is
1510 // illegal in XML), but later I realized that '_' fits this purpose
1511 // much better (because &File means "File with F underlined").
1512 const wxChar amp_char = (m_resource->CompareVersion(2,3,0,1) < 0)
1513 ? '$' : '_';
1514
1515 for ( wxString::const_iterator dt = str1.begin(); dt != str1.end(); ++dt )
1516 {
1517 // Remap amp_char to &, map double amp_char to amp_char (for things
1518 // like "&File..." -- this is illegal in XML, so we use "_File..."):
1519 if ( *dt == amp_char )
1520 {
1521 if ( *(++dt) == amp_char )
1522 str2 << amp_char;
1523 else
1524 str2 << wxT('&') << *dt;
1525 }
1526 // Remap \n to CR, \r to LF, \t to TAB, \\ to \:
1527 else if ( *dt == wxT('\\') )
1528 {
1529 switch ( (*(++dt)).GetValue() )
1530 {
1531 case wxT('n'):
1532 str2 << wxT('\n');
1533 break;
1534
1535 case wxT('t'):
1536 str2 << wxT('\t');
1537 break;
1538
1539 case wxT('r'):
1540 str2 << wxT('\r');
1541 break;
1542
1543 case wxT('\\') :
1544 // "\\" wasn't translated to "\" prior to 2.5.3.0:
1545 if ( escapeBackslash )
1546 {
1547 str2 << wxT('\\');
1548 break;
1549 }
1550 // else fall-through to default: branch below
1551
1552 default:
1553 str2 << wxT('\\') << *dt;
1554 break;
1555 }
1556 }
1557 else
1558 {
1559 str2 << *dt;
1560 }
1561 }
1562
1563 if (m_resource->GetFlags() & wxXRC_USE_LOCALE)
1564 {
1565 if (translate && parNode &&
1566 parNode->GetAttribute(wxT("translate"), wxEmptyString) != wxT("0"))
1567 {
1568 return wxGetTranslation(str2, m_resource->GetDomain());
1569 }
1570 else
1571 {
1572 #if wxUSE_UNICODE
1573 return str2;
1574 #else
1575 // The string is internally stored as UTF-8, we have to convert
1576 // it into system's default encoding so that it can be displayed:
1577 return wxString(str2.wc_str(wxConvUTF8), wxConvLocal);
1578 #endif
1579 }
1580 }
1581
1582 // If wxXRC_USE_LOCALE is not set, then the string is already in
1583 // system's default encoding in ANSI build, so we don't have to
1584 // do anything special here.
1585 return str2;
1586 }
1587
1588
1589
1590 long wxXmlResourceHandler::GetLong(const wxString& param, long defaultv)
1591 {
1592 long value;
1593 wxString str1 = GetParamValue(param);
1594
1595 if (!str1.ToLong(&value))
1596 value = defaultv;
1597
1598 return value;
1599 }
1600
1601 float wxXmlResourceHandler::GetFloat(const wxString& param, float defaultv)
1602 {
1603 wxString str = GetParamValue(param);
1604
1605 // strings in XRC always use C locale so make sure to use the
1606 // locale-independent wxString::ToCDouble() and not ToDouble() which uses
1607 // the current locale with a potentially different decimal point character
1608 double value;
1609 if (!str.ToCDouble(&value))
1610 value = defaultv;
1611
1612 return wx_truncate_cast(float, value);
1613 }
1614
1615
1616 int wxXmlResourceHandler::GetID()
1617 {
1618 return wxXmlResource::GetXRCID(GetName());
1619 }
1620
1621
1622
1623 wxString wxXmlResourceHandler::GetName()
1624 {
1625 return m_node->GetAttribute(wxT("name"), wxT("-1"));
1626 }
1627
1628
1629
1630 bool wxXmlResourceHandler::GetBoolAttr(const wxString& attr, bool defaultv)
1631 {
1632 wxString v;
1633 return m_node->GetAttribute(attr, &v) ? v == '1' : defaultv;
1634 }
1635
1636 bool wxXmlResourceHandler::GetBool(const wxString& param, bool defaultv)
1637 {
1638 const wxString v = GetParamValue(param);
1639
1640 return v.empty() ? defaultv : (v == '1');
1641 }
1642
1643
1644 static wxColour GetSystemColour(const wxString& name)
1645 {
1646 if (!name.empty())
1647 {
1648 #define SYSCLR(clr) \
1649 if (name == wxT(#clr)) return wxSystemSettings::GetColour(clr);
1650 SYSCLR(wxSYS_COLOUR_SCROLLBAR)
1651 SYSCLR(wxSYS_COLOUR_BACKGROUND)
1652 SYSCLR(wxSYS_COLOUR_DESKTOP)
1653 SYSCLR(wxSYS_COLOUR_ACTIVECAPTION)
1654 SYSCLR(wxSYS_COLOUR_INACTIVECAPTION)
1655 SYSCLR(wxSYS_COLOUR_MENU)
1656 SYSCLR(wxSYS_COLOUR_WINDOW)
1657 SYSCLR(wxSYS_COLOUR_WINDOWFRAME)
1658 SYSCLR(wxSYS_COLOUR_MENUTEXT)
1659 SYSCLR(wxSYS_COLOUR_WINDOWTEXT)
1660 SYSCLR(wxSYS_COLOUR_CAPTIONTEXT)
1661 SYSCLR(wxSYS_COLOUR_ACTIVEBORDER)
1662 SYSCLR(wxSYS_COLOUR_INACTIVEBORDER)
1663 SYSCLR(wxSYS_COLOUR_APPWORKSPACE)
1664 SYSCLR(wxSYS_COLOUR_HIGHLIGHT)
1665 SYSCLR(wxSYS_COLOUR_HIGHLIGHTTEXT)
1666 SYSCLR(wxSYS_COLOUR_BTNFACE)
1667 SYSCLR(wxSYS_COLOUR_3DFACE)
1668 SYSCLR(wxSYS_COLOUR_BTNSHADOW)
1669 SYSCLR(wxSYS_COLOUR_3DSHADOW)
1670 SYSCLR(wxSYS_COLOUR_GRAYTEXT)
1671 SYSCLR(wxSYS_COLOUR_BTNTEXT)
1672 SYSCLR(wxSYS_COLOUR_INACTIVECAPTIONTEXT)
1673 SYSCLR(wxSYS_COLOUR_BTNHIGHLIGHT)
1674 SYSCLR(wxSYS_COLOUR_BTNHILIGHT)
1675 SYSCLR(wxSYS_COLOUR_3DHIGHLIGHT)
1676 SYSCLR(wxSYS_COLOUR_3DHILIGHT)
1677 SYSCLR(wxSYS_COLOUR_3DDKSHADOW)
1678 SYSCLR(wxSYS_COLOUR_3DLIGHT)
1679 SYSCLR(wxSYS_COLOUR_INFOTEXT)
1680 SYSCLR(wxSYS_COLOUR_INFOBK)
1681 SYSCLR(wxSYS_COLOUR_LISTBOX)
1682 SYSCLR(wxSYS_COLOUR_HOTLIGHT)
1683 SYSCLR(wxSYS_COLOUR_GRADIENTACTIVECAPTION)
1684 SYSCLR(wxSYS_COLOUR_GRADIENTINACTIVECAPTION)
1685 SYSCLR(wxSYS_COLOUR_MENUHILIGHT)
1686 SYSCLR(wxSYS_COLOUR_MENUBAR)
1687 #undef SYSCLR
1688 }
1689
1690 return wxNullColour;
1691 }
1692
1693 wxColour wxXmlResourceHandler::GetColour(const wxString& param, const wxColour& defaultv)
1694 {
1695 wxString v = GetParamValue(param);
1696
1697 if ( v.empty() )
1698 return defaultv;
1699
1700 wxColour clr;
1701
1702 // wxString -> wxColour conversion
1703 if (!clr.Set(v))
1704 {
1705 // the colour doesn't use #RRGGBB format, check if it is symbolic
1706 // colour name:
1707 clr = GetSystemColour(v);
1708 if (clr.Ok())
1709 return clr;
1710
1711 ReportParamError
1712 (
1713 param,
1714 wxString::Format("incorrect colour specification \"%s\"", v)
1715 );
1716 return wxNullColour;
1717 }
1718
1719 return clr;
1720 }
1721
1722 namespace
1723 {
1724
1725 // if 'param' has stock_id/stock_client, extracts them and returns true
1726 bool GetStockArtAttrs(const wxXmlNode *paramNode,
1727 const wxString& defaultArtClient,
1728 wxString& art_id, wxString& art_client)
1729 {
1730 if ( paramNode )
1731 {
1732 art_id = paramNode->GetAttribute("stock_id", "");
1733
1734 if ( !art_id.empty() )
1735 {
1736 art_id = wxART_MAKE_ART_ID_FROM_STR(art_id);
1737
1738 art_client = paramNode->GetAttribute("stock_client", "");
1739 if ( art_client.empty() )
1740 art_client = defaultArtClient;
1741 else
1742 art_client = wxART_MAKE_CLIENT_ID_FROM_STR(art_client);
1743
1744 return true;
1745 }
1746 }
1747
1748 return false;
1749 }
1750
1751 } // anonymous namespace
1752
1753 wxBitmap wxXmlResourceHandler::GetBitmap(const wxString& param,
1754 const wxArtClient& defaultArtClient,
1755 wxSize size)
1756 {
1757 // it used to be possible to pass an empty string here to indicate that the
1758 // bitmap name should be read from this node itself but this is not
1759 // supported any more because GetBitmap(m_node) can be used directly
1760 // instead
1761 wxASSERT_MSG( !param.empty(), "bitmap parameter name can't be empty" );
1762
1763 const wxXmlNode* const node = GetParamNode(param);
1764
1765 if ( !node )
1766 {
1767 // this is not an error as bitmap parameter could be optional
1768 return wxNullBitmap;
1769 }
1770
1771 return GetBitmap(node, defaultArtClient, size);
1772 }
1773
1774 wxBitmap wxXmlResourceHandler::GetBitmap(const wxXmlNode* node,
1775 const wxArtClient& defaultArtClient,
1776 wxSize size)
1777 {
1778 wxCHECK_MSG( node, wxNullBitmap, "bitmap node can't be NULL" );
1779
1780 /* If the bitmap is specified as stock item, query wxArtProvider for it: */
1781 wxString art_id, art_client;
1782 if ( GetStockArtAttrs(node, defaultArtClient,
1783 art_id, art_client) )
1784 {
1785 wxBitmap stockArt(wxArtProvider::GetBitmap(art_id, art_client, size));
1786 if ( stockArt.Ok() )
1787 return stockArt;
1788 }
1789
1790 /* ...or load the bitmap from file: */
1791 wxString name = GetParamValue(node);
1792 if (name.empty()) return wxNullBitmap;
1793 #if wxUSE_FILESYSTEM
1794 wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
1795 if (fsfile == NULL)
1796 {
1797 ReportParamError
1798 (
1799 node->GetName(),
1800 wxString::Format("cannot open bitmap resource \"%s\"", name)
1801 );
1802 return wxNullBitmap;
1803 }
1804 wxImage img(*(fsfile->GetStream()));
1805 delete fsfile;
1806 #else
1807 wxImage img(name);
1808 #endif
1809
1810 if (!img.Ok())
1811 {
1812 ReportParamError
1813 (
1814 node->GetName(),
1815 wxString::Format("cannot create bitmap from \"%s\"", name)
1816 );
1817 return wxNullBitmap;
1818 }
1819 if (!(size == wxDefaultSize)) img.Rescale(size.x, size.y);
1820 return wxBitmap(img);
1821 }
1822
1823
1824 wxIcon wxXmlResourceHandler::GetIcon(const wxString& param,
1825 const wxArtClient& defaultArtClient,
1826 wxSize size)
1827 {
1828 // see comment in GetBitmap(wxString) overload
1829 wxASSERT_MSG( !param.empty(), "icon parameter name can't be empty" );
1830
1831 const wxXmlNode* const node = GetParamNode(param);
1832
1833 if ( !node )
1834 {
1835 // this is not an error as icon parameter could be optional
1836 return wxIcon();
1837 }
1838
1839 return GetIcon(node, defaultArtClient, size);
1840 }
1841
1842 wxIcon wxXmlResourceHandler::GetIcon(const wxXmlNode* node,
1843 const wxArtClient& defaultArtClient,
1844 wxSize size)
1845 {
1846 wxIcon icon;
1847 icon.CopyFromBitmap(GetBitmap(node, defaultArtClient, size));
1848 return icon;
1849 }
1850
1851
1852 wxIconBundle wxXmlResourceHandler::GetIconBundle(const wxString& param,
1853 const wxArtClient& defaultArtClient)
1854 {
1855 wxString art_id, art_client;
1856 if ( GetStockArtAttrs(GetParamNode(param), defaultArtClient,
1857 art_id, art_client) )
1858 {
1859 wxIconBundle stockArt(wxArtProvider::GetIconBundle(art_id, art_client));
1860 if ( stockArt.IsOk() )
1861 return stockArt;
1862 }
1863
1864 const wxString name = GetParamValue(param);
1865 if ( name.empty() )
1866 return wxNullIconBundle;
1867
1868 #if wxUSE_FILESYSTEM
1869 wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
1870 if ( fsfile == NULL )
1871 {
1872 ReportParamError
1873 (
1874 param,
1875 wxString::Format("cannot open icon resource \"%s\"", name)
1876 );
1877 return wxNullIconBundle;
1878 }
1879
1880 wxIconBundle bundle(*(fsfile->GetStream()));
1881 delete fsfile;
1882 #else
1883 wxIconBundle bundle(name);
1884 #endif
1885
1886 if ( !bundle.IsOk() )
1887 {
1888 ReportParamError
1889 (
1890 param,
1891 wxString::Format("cannot create icon from \"%s\"", name)
1892 );
1893 return wxNullIconBundle;
1894 }
1895
1896 return bundle;
1897 }
1898
1899
1900 wxImageList *wxXmlResourceHandler::GetImageList(const wxString& param)
1901 {
1902 wxXmlNode * const imagelist_node = GetParamNode(param);
1903 if ( !imagelist_node )
1904 return NULL;
1905
1906 wxXmlNode * const oldnode = m_node;
1907 m_node = imagelist_node;
1908
1909 // Get the size if we have it, otherwise we will use the size of the first
1910 // list element.
1911 wxSize size = GetSize();
1912
1913 // Start adding images, we'll create the image list when adding the first
1914 // one.
1915 wxImageList * imagelist = NULL;
1916 wxString parambitmap = wxT("bitmap");
1917 if ( HasParam(parambitmap) )
1918 {
1919 wxXmlNode *n = m_node->GetChildren();
1920 while (n)
1921 {
1922 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == parambitmap)
1923 {
1924 wxIcon icon = GetIcon(n);
1925 if ( !imagelist )
1926 {
1927 // We need the real image list size to create it.
1928 if ( size == wxDefaultSize )
1929 size = icon.GetSize();
1930
1931 // We use the mask by default.
1932 bool mask = !HasParam(wxS("mask")) || GetBool(wxS("mask"));
1933
1934 imagelist = new wxImageList(size.x, size.y, mask);
1935 }
1936
1937 // add icon instead of bitmap to keep the bitmap mask
1938 imagelist->Add(icon);
1939 }
1940 n = n->GetNext();
1941 }
1942 }
1943
1944 m_node = oldnode;
1945 return imagelist;
1946 }
1947
1948 wxXmlNode *wxXmlResourceHandler::GetParamNode(const wxString& param)
1949 {
1950 wxCHECK_MSG(m_node, NULL, wxT("You can't access handler data before it was initialized!"));
1951
1952 wxXmlNode *n = m_node->GetChildren();
1953
1954 while (n)
1955 {
1956 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
1957 {
1958 // TODO: check that there are no other properties/parameters with
1959 // the same name and log an error if there are (can't do this
1960 // right now as I'm not sure if it's not going to break code
1961 // using this function in unintentional way (i.e. for
1962 // accessing other things than properties), for example
1963 // wxBitmapComboBoxXmlHandler almost surely does
1964 return n;
1965 }
1966 n = n->GetNext();
1967 }
1968 return NULL;
1969 }
1970
1971 /* static */
1972 bool wxXmlResourceHandler::IsOfClass(wxXmlNode *node, const wxString& classname)
1973 {
1974 return node->GetAttribute(wxT("class")) == classname;
1975 }
1976
1977
1978
1979 wxString wxXmlResourceHandler::GetNodeContent(const wxXmlNode *node)
1980 {
1981 const wxXmlNode *n = node;
1982 if (n == NULL) return wxEmptyString;
1983 n = n->GetChildren();
1984
1985 while (n)
1986 {
1987 if (n->GetType() == wxXML_TEXT_NODE ||
1988 n->GetType() == wxXML_CDATA_SECTION_NODE)
1989 return n->GetContent();
1990 n = n->GetNext();
1991 }
1992 return wxEmptyString;
1993 }
1994
1995
1996
1997 wxString wxXmlResourceHandler::GetParamValue(const wxString& param)
1998 {
1999 if (param.empty())
2000 return GetNodeContent(m_node);
2001 else
2002 return GetNodeContent(GetParamNode(param));
2003 }
2004
2005 wxString wxXmlResourceHandler::GetParamValue(const wxXmlNode* node)
2006 {
2007 return GetNodeContent(node);
2008 }
2009
2010
2011 wxSize wxXmlResourceHandler::GetSize(const wxString& param,
2012 wxWindow *windowToUse)
2013 {
2014 wxString s = GetParamValue(param);
2015 if (s.empty()) s = wxT("-1,-1");
2016 bool is_dlg;
2017 long sx, sy = 0;
2018
2019 is_dlg = s[s.length()-1] == wxT('d');
2020 if (is_dlg) s.RemoveLast();
2021
2022 if (!s.BeforeFirst(wxT(',')).ToLong(&sx) ||
2023 !s.AfterLast(wxT(',')).ToLong(&sy))
2024 {
2025 ReportParamError
2026 (
2027 param,
2028 wxString::Format("cannot parse coordinates value \"%s\"", s)
2029 );
2030 return wxDefaultSize;
2031 }
2032
2033 if (is_dlg)
2034 {
2035 if (windowToUse)
2036 {
2037 return wxDLG_UNIT(windowToUse, wxSize(sx, sy));
2038 }
2039 else if (m_parentAsWindow)
2040 {
2041 return wxDLG_UNIT(m_parentAsWindow, wxSize(sx, sy));
2042 }
2043 else
2044 {
2045 ReportParamError
2046 (
2047 param,
2048 "cannot convert dialog units: dialog unknown"
2049 );
2050 return wxDefaultSize;
2051 }
2052 }
2053
2054 return wxSize(sx, sy);
2055 }
2056
2057
2058
2059 wxPoint wxXmlResourceHandler::GetPosition(const wxString& param)
2060 {
2061 wxSize sz = GetSize(param);
2062 return wxPoint(sz.x, sz.y);
2063 }
2064
2065
2066
2067 wxCoord wxXmlResourceHandler::GetDimension(const wxString& param,
2068 wxCoord defaultv,
2069 wxWindow *windowToUse)
2070 {
2071 wxString s = GetParamValue(param);
2072 if (s.empty()) return defaultv;
2073 bool is_dlg;
2074 long sx;
2075
2076 is_dlg = s[s.length()-1] == wxT('d');
2077 if (is_dlg) s.RemoveLast();
2078
2079 if (!s.ToLong(&sx))
2080 {
2081 ReportParamError
2082 (
2083 param,
2084 wxString::Format("cannot parse dimension value \"%s\"", s)
2085 );
2086 return defaultv;
2087 }
2088
2089 if (is_dlg)
2090 {
2091 if (windowToUse)
2092 {
2093 return wxDLG_UNIT(windowToUse, wxSize(sx, 0)).x;
2094 }
2095 else if (m_parentAsWindow)
2096 {
2097 return wxDLG_UNIT(m_parentAsWindow, wxSize(sx, 0)).x;
2098 }
2099 else
2100 {
2101 ReportParamError
2102 (
2103 param,
2104 "cannot convert dialog units: dialog unknown"
2105 );
2106 return defaultv;
2107 }
2108 }
2109
2110 return sx;
2111 }
2112
2113
2114 // Get system font index using indexname
2115 static wxFont GetSystemFont(const wxString& name)
2116 {
2117 if (!name.empty())
2118 {
2119 #define SYSFNT(fnt) \
2120 if (name == wxT(#fnt)) return wxSystemSettings::GetFont(fnt);
2121 SYSFNT(wxSYS_OEM_FIXED_FONT)
2122 SYSFNT(wxSYS_ANSI_FIXED_FONT)
2123 SYSFNT(wxSYS_ANSI_VAR_FONT)
2124 SYSFNT(wxSYS_SYSTEM_FONT)
2125 SYSFNT(wxSYS_DEVICE_DEFAULT_FONT)
2126 SYSFNT(wxSYS_SYSTEM_FIXED_FONT)
2127 SYSFNT(wxSYS_DEFAULT_GUI_FONT)
2128 #undef SYSFNT
2129 }
2130
2131 return wxNullFont;
2132 }
2133
2134 wxFont wxXmlResourceHandler::GetFont(const wxString& param)
2135 {
2136 wxXmlNode *font_node = GetParamNode(param);
2137 if (font_node == NULL)
2138 {
2139 ReportError(
2140 wxString::Format("cannot find font node \"%s\"", param));
2141 return wxNullFont;
2142 }
2143
2144 wxXmlNode *oldnode = m_node;
2145 m_node = font_node;
2146
2147 // font attributes:
2148
2149 // size
2150 int isize = -1;
2151 bool hasSize = HasParam(wxT("size"));
2152 if (hasSize)
2153 isize = GetLong(wxT("size"), -1);
2154
2155 // style
2156 int istyle = wxNORMAL;
2157 bool hasStyle = HasParam(wxT("style"));
2158 if (hasStyle)
2159 {
2160 wxString style = GetParamValue(wxT("style"));
2161 if (style == wxT("italic"))
2162 istyle = wxITALIC;
2163 else if (style == wxT("slant"))
2164 istyle = wxSLANT;
2165 }
2166
2167 // weight
2168 int iweight = wxNORMAL;
2169 bool hasWeight = HasParam(wxT("weight"));
2170 if (hasWeight)
2171 {
2172 wxString weight = GetParamValue(wxT("weight"));
2173 if (weight == wxT("bold"))
2174 iweight = wxBOLD;
2175 else if (weight == wxT("light"))
2176 iweight = wxLIGHT;
2177 }
2178
2179 // underline
2180 bool hasUnderlined = HasParam(wxT("underlined"));
2181 bool underlined = hasUnderlined ? GetBool(wxT("underlined"), false) : false;
2182
2183 // family and facename
2184 int ifamily = wxDEFAULT;
2185 bool hasFamily = HasParam(wxT("family"));
2186 if (hasFamily)
2187 {
2188 wxString family = GetParamValue(wxT("family"));
2189 if (family == wxT("decorative")) ifamily = wxDECORATIVE;
2190 else if (family == wxT("roman")) ifamily = wxROMAN;
2191 else if (family == wxT("script")) ifamily = wxSCRIPT;
2192 else if (family == wxT("swiss")) ifamily = wxSWISS;
2193 else if (family == wxT("modern")) ifamily = wxMODERN;
2194 else if (family == wxT("teletype")) ifamily = wxTELETYPE;
2195 }
2196
2197
2198 wxString facename;
2199 bool hasFacename = HasParam(wxT("face"));
2200 if (hasFacename)
2201 {
2202 wxString faces = GetParamValue(wxT("face"));
2203 wxStringTokenizer tk(faces, wxT(","));
2204 #if wxUSE_FONTENUM
2205 wxArrayString facenames(wxFontEnumerator::GetFacenames());
2206 while (tk.HasMoreTokens())
2207 {
2208 int index = facenames.Index(tk.GetNextToken(), false);
2209 if (index != wxNOT_FOUND)
2210 {
2211 facename = facenames[index];
2212 break;
2213 }
2214 }
2215 #else // !wxUSE_FONTENUM
2216 // just use the first face name if we can't check its availability:
2217 if (tk.HasMoreTokens())
2218 facename = tk.GetNextToken();
2219 #endif // wxUSE_FONTENUM/!wxUSE_FONTENUM
2220 }
2221
2222 // encoding
2223 wxFontEncoding enc = wxFONTENCODING_DEFAULT;
2224 bool hasEncoding = HasParam(wxT("encoding"));
2225 #if wxUSE_FONTMAP
2226 if (hasEncoding)
2227 {
2228 wxString encoding = GetParamValue(wxT("encoding"));
2229 wxFontMapper mapper;
2230 if (!encoding.empty())
2231 enc = mapper.CharsetToEncoding(encoding);
2232 if (enc == wxFONTENCODING_SYSTEM)
2233 enc = wxFONTENCODING_DEFAULT;
2234 }
2235 #endif // wxUSE_FONTMAP
2236
2237 // is this font based on a system font?
2238 wxFont font = GetSystemFont(GetParamValue(wxT("sysfont")));
2239
2240 if (font.Ok())
2241 {
2242 if (hasSize && isize != -1)
2243 font.SetPointSize(isize);
2244 else if (HasParam(wxT("relativesize")))
2245 font.SetPointSize(int(font.GetPointSize() *
2246 GetFloat(wxT("relativesize"))));
2247
2248 if (hasStyle)
2249 font.SetStyle(istyle);
2250 if (hasWeight)
2251 font.SetWeight(iweight);
2252 if (hasUnderlined)
2253 font.SetUnderlined(underlined);
2254 if (hasFamily)
2255 font.SetFamily(ifamily);
2256 if (hasFacename)
2257 font.SetFaceName(facename);
2258 if (hasEncoding)
2259 font.SetDefaultEncoding(enc);
2260 }
2261 else // not based on system font
2262 {
2263 font = wxFont(isize == -1 ? wxNORMAL_FONT->GetPointSize() : isize,
2264 ifamily, istyle, iweight,
2265 underlined, facename, enc);
2266 }
2267
2268 m_node = oldnode;
2269 return font;
2270 }
2271
2272
2273 void wxXmlResourceHandler::SetupWindow(wxWindow *wnd)
2274 {
2275 //FIXME : add cursor
2276
2277 if (HasParam(wxT("exstyle")))
2278 // Have to OR it with existing style, since
2279 // some implementations (e.g. wxGTK) use the extra style
2280 // during creation
2281 wnd->SetExtraStyle(wnd->GetExtraStyle() | GetStyle(wxT("exstyle")));
2282 if (HasParam(wxT("bg")))
2283 wnd->SetBackgroundColour(GetColour(wxT("bg")));
2284 if (HasParam(wxT("ownbg")))
2285 wnd->SetOwnBackgroundColour(GetColour(wxT("ownbg")));
2286 if (HasParam(wxT("fg")))
2287 wnd->SetForegroundColour(GetColour(wxT("fg")));
2288 if (HasParam(wxT("ownfg")))
2289 wnd->SetOwnForegroundColour(GetColour(wxT("ownfg")));
2290 if (GetBool(wxT("enabled"), 1) == 0)
2291 wnd->Enable(false);
2292 if (GetBool(wxT("focused"), 0) == 1)
2293 wnd->SetFocus();
2294 if (GetBool(wxT("hidden"), 0) == 1)
2295 wnd->Show(false);
2296 #if wxUSE_TOOLTIPS
2297 if (HasParam(wxT("tooltip")))
2298 wnd->SetToolTip(GetText(wxT("tooltip")));
2299 #endif
2300 if (HasParam(wxT("font")))
2301 wnd->SetFont(GetFont(wxT("font")));
2302 if (HasParam(wxT("ownfont")))
2303 wnd->SetOwnFont(GetFont(wxT("ownfont")));
2304 if (HasParam(wxT("help")))
2305 wnd->SetHelpText(GetText(wxT("help")));
2306 }
2307
2308
2309 void wxXmlResourceHandler::CreateChildren(wxObject *parent, bool this_hnd_only)
2310 {
2311 for ( wxXmlNode *n = m_node->GetChildren(); n; n = n->GetNext() )
2312 {
2313 if ( IsObjectNode(n) )
2314 {
2315 m_resource->DoCreateResFromNode(*n, parent, NULL,
2316 this_hnd_only ? this : NULL);
2317 }
2318 }
2319 }
2320
2321
2322 void wxXmlResourceHandler::CreateChildrenPrivately(wxObject *parent, wxXmlNode *rootnode)
2323 {
2324 wxXmlNode *root;
2325 if (rootnode == NULL) root = m_node; else root = rootnode;
2326 wxXmlNode *n = root->GetChildren();
2327
2328 while (n)
2329 {
2330 if (n->GetType() == wxXML_ELEMENT_NODE && CanHandle(n))
2331 {
2332 CreateResource(n, parent, NULL);
2333 }
2334 n = n->GetNext();
2335 }
2336 }
2337
2338
2339 //-----------------------------------------------------------------------------
2340 // errors reporting
2341 //-----------------------------------------------------------------------------
2342
2343 void wxXmlResourceHandler::ReportError(const wxString& message)
2344 {
2345 m_resource->ReportError(m_node, message);
2346 }
2347
2348 void wxXmlResourceHandler::ReportError(wxXmlNode *context,
2349 const wxString& message)
2350 {
2351 m_resource->ReportError(context ? context : m_node, message);
2352 }
2353
2354 void wxXmlResourceHandler::ReportParamError(const wxString& param,
2355 const wxString& message)
2356 {
2357 m_resource->ReportError(GetParamNode(param), message);
2358 }
2359
2360 void wxXmlResource::ReportError(const wxXmlNode *context, const wxString& message)
2361 {
2362 if ( !context )
2363 {
2364 DoReportError("", NULL, message);
2365 return;
2366 }
2367
2368 // We need to find out the file that 'context' is part of. Performance of
2369 // this code is not critical, so we simply find the root XML node and
2370 // compare it with all loaded XRC files.
2371 const wxString filename = GetFileNameFromNode(context, Data());
2372
2373 DoReportError(filename, context, message);
2374 }
2375
2376 void wxXmlResource::DoReportError(const wxString& xrcFile, const wxXmlNode *position,
2377 const wxString& message)
2378 {
2379 const int line = position ? position->GetLineNumber() : -1;
2380
2381 wxString loc;
2382 if ( !xrcFile.empty() )
2383 loc = xrcFile + ':';
2384 if ( line != -1 )
2385 loc += wxString::Format("%d:", line);
2386 if ( !loc.empty() )
2387 loc += ' ';
2388
2389 wxLogError("XRC error: %s%s", loc, message);
2390 }
2391
2392
2393 //-----------------------------------------------------------------------------
2394 // XRCID implementation
2395 //-----------------------------------------------------------------------------
2396
2397 #define XRCID_TABLE_SIZE 1024
2398
2399
2400 struct XRCID_record
2401 {
2402 /* Hold the id so that once an id is allocated for a name, it
2403 does not get created again by NewControlId at least
2404 until we are done with it */
2405 wxWindowIDRef id;
2406 char *key;
2407 XRCID_record *next;
2408 };
2409
2410 static XRCID_record *XRCID_Records[XRCID_TABLE_SIZE] = {NULL};
2411
2412 // Extremely simplistic hash function which probably ought to be replaced with
2413 // wxStringHash::stringHash().
2414 static inline unsigned XRCIdHash(const char *str_id)
2415 {
2416 unsigned index = 0;
2417
2418 for (const char *c = str_id; *c != '\0'; c++) index += (unsigned int)*c;
2419 index %= XRCID_TABLE_SIZE;
2420
2421 return index;
2422 }
2423
2424 static int XRCID_Lookup(const char *str_id, int value_if_not_found = wxID_NONE)
2425 {
2426 const unsigned index = XRCIdHash(str_id);
2427
2428
2429 XRCID_record *oldrec = NULL;
2430 for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
2431 {
2432 if (wxStrcmp(rec->key, str_id) == 0)
2433 {
2434 return rec->id;
2435 }
2436 oldrec = rec;
2437 }
2438
2439 XRCID_record **rec_var = (oldrec == NULL) ?
2440 &XRCID_Records[index] : &oldrec->next;
2441 *rec_var = new XRCID_record;
2442 (*rec_var)->key = wxStrdup(str_id);
2443 (*rec_var)->next = NULL;
2444
2445 char *end;
2446 if (value_if_not_found != wxID_NONE)
2447 (*rec_var)->id = value_if_not_found;
2448 else
2449 {
2450 int asint = wxStrtol(str_id, &end, 10);
2451 if (*str_id && *end == 0)
2452 {
2453 // if str_id was integer, keep it verbosely:
2454 (*rec_var)->id = asint;
2455 }
2456 else
2457 {
2458 (*rec_var)->id = wxWindowBase::NewControlId();
2459 }
2460 }
2461
2462 return (*rec_var)->id;
2463 }
2464
2465 namespace
2466 {
2467
2468 // flag indicating whether standard XRC ids were already initialized
2469 static bool gs_stdIDsAdded = false;
2470
2471 void AddStdXRCID_Records()
2472 {
2473 #define stdID(id) XRCID_Lookup(#id, id)
2474 stdID(-1);
2475
2476 stdID(wxID_ANY);
2477 stdID(wxID_SEPARATOR);
2478
2479 stdID(wxID_OPEN);
2480 stdID(wxID_CLOSE);
2481 stdID(wxID_NEW);
2482 stdID(wxID_SAVE);
2483 stdID(wxID_SAVEAS);
2484 stdID(wxID_REVERT);
2485 stdID(wxID_EXIT);
2486 stdID(wxID_UNDO);
2487 stdID(wxID_REDO);
2488 stdID(wxID_HELP);
2489 stdID(wxID_PRINT);
2490 stdID(wxID_PRINT_SETUP);
2491 stdID(wxID_PAGE_SETUP);
2492 stdID(wxID_PREVIEW);
2493 stdID(wxID_ABOUT);
2494 stdID(wxID_HELP_CONTENTS);
2495 stdID(wxID_HELP_COMMANDS);
2496 stdID(wxID_HELP_PROCEDURES);
2497 stdID(wxID_HELP_CONTEXT);
2498 stdID(wxID_CLOSE_ALL);
2499 stdID(wxID_PREFERENCES);
2500 stdID(wxID_EDIT);
2501 stdID(wxID_CUT);
2502 stdID(wxID_COPY);
2503 stdID(wxID_PASTE);
2504 stdID(wxID_CLEAR);
2505 stdID(wxID_FIND);
2506 stdID(wxID_DUPLICATE);
2507 stdID(wxID_SELECTALL);
2508 stdID(wxID_DELETE);
2509 stdID(wxID_REPLACE);
2510 stdID(wxID_REPLACE_ALL);
2511 stdID(wxID_PROPERTIES);
2512 stdID(wxID_VIEW_DETAILS);
2513 stdID(wxID_VIEW_LARGEICONS);
2514 stdID(wxID_VIEW_SMALLICONS);
2515 stdID(wxID_VIEW_LIST);
2516 stdID(wxID_VIEW_SORTDATE);
2517 stdID(wxID_VIEW_SORTNAME);
2518 stdID(wxID_VIEW_SORTSIZE);
2519 stdID(wxID_VIEW_SORTTYPE);
2520 stdID(wxID_FILE1);
2521 stdID(wxID_FILE2);
2522 stdID(wxID_FILE3);
2523 stdID(wxID_FILE4);
2524 stdID(wxID_FILE5);
2525 stdID(wxID_FILE6);
2526 stdID(wxID_FILE7);
2527 stdID(wxID_FILE8);
2528 stdID(wxID_FILE9);
2529 stdID(wxID_OK);
2530 stdID(wxID_CANCEL);
2531 stdID(wxID_APPLY);
2532 stdID(wxID_YES);
2533 stdID(wxID_NO);
2534 stdID(wxID_STATIC);
2535 stdID(wxID_FORWARD);
2536 stdID(wxID_BACKWARD);
2537 stdID(wxID_DEFAULT);
2538 stdID(wxID_MORE);
2539 stdID(wxID_SETUP);
2540 stdID(wxID_RESET);
2541 stdID(wxID_CONTEXT_HELP);
2542 stdID(wxID_YESTOALL);
2543 stdID(wxID_NOTOALL);
2544 stdID(wxID_ABORT);
2545 stdID(wxID_RETRY);
2546 stdID(wxID_IGNORE);
2547 stdID(wxID_ADD);
2548 stdID(wxID_REMOVE);
2549 stdID(wxID_UP);
2550 stdID(wxID_DOWN);
2551 stdID(wxID_HOME);
2552 stdID(wxID_REFRESH);
2553 stdID(wxID_STOP);
2554 stdID(wxID_INDEX);
2555 stdID(wxID_BOLD);
2556 stdID(wxID_ITALIC);
2557 stdID(wxID_JUSTIFY_CENTER);
2558 stdID(wxID_JUSTIFY_FILL);
2559 stdID(wxID_JUSTIFY_RIGHT);
2560 stdID(wxID_JUSTIFY_LEFT);
2561 stdID(wxID_UNDERLINE);
2562 stdID(wxID_INDENT);
2563 stdID(wxID_UNINDENT);
2564 stdID(wxID_ZOOM_100);
2565 stdID(wxID_ZOOM_FIT);
2566 stdID(wxID_ZOOM_IN);
2567 stdID(wxID_ZOOM_OUT);
2568 stdID(wxID_UNDELETE);
2569 stdID(wxID_REVERT_TO_SAVED);
2570 stdID(wxID_SYSTEM_MENU);
2571 stdID(wxID_CLOSE_FRAME);
2572 stdID(wxID_MOVE_FRAME);
2573 stdID(wxID_RESIZE_FRAME);
2574 stdID(wxID_MAXIMIZE_FRAME);
2575 stdID(wxID_ICONIZE_FRAME);
2576 stdID(wxID_RESTORE_FRAME);
2577 stdID(wxID_CDROM);
2578 stdID(wxID_CONVERT);
2579 stdID(wxID_EXECUTE);
2580 stdID(wxID_FLOPPY);
2581 stdID(wxID_HARDDISK);
2582 stdID(wxID_BOTTOM);
2583 stdID(wxID_FIRST);
2584 stdID(wxID_LAST);
2585 stdID(wxID_TOP);
2586 stdID(wxID_INFO);
2587 stdID(wxID_JUMP_TO);
2588 stdID(wxID_NETWORK);
2589 stdID(wxID_SELECT_COLOR);
2590 stdID(wxID_SELECT_FONT);
2591 stdID(wxID_SORT_ASCENDING);
2592 stdID(wxID_SORT_DESCENDING);
2593 stdID(wxID_SPELL_CHECK);
2594 stdID(wxID_STRIKETHROUGH);
2595
2596 #undef stdID
2597 }
2598
2599 } // anonymous namespace
2600
2601
2602 /*static*/
2603 int wxXmlResource::DoGetXRCID(const char *str_id, int value_if_not_found)
2604 {
2605 if ( !gs_stdIDsAdded )
2606 {
2607 gs_stdIDsAdded = true;
2608 AddStdXRCID_Records();
2609 }
2610
2611 return XRCID_Lookup(str_id, value_if_not_found);
2612 }
2613
2614 /* static */
2615 wxString wxXmlResource::FindXRCIDById(int numId)
2616 {
2617 for ( int i = 0; i < XRCID_TABLE_SIZE; i++ )
2618 {
2619 for ( XRCID_record *rec = XRCID_Records[i]; rec; rec = rec->next )
2620 {
2621 if ( rec->id == numId )
2622 return wxString(rec->key);
2623 }
2624 }
2625
2626 return wxString();
2627 }
2628
2629 /* static */
2630 void wxIdRangeManager::RemoveXRCIDEntry(const char *str_id)
2631 {
2632 const unsigned index = XRCIdHash(str_id);
2633
2634 XRCID_record **p_previousrec = &XRCID_Records[index];
2635 for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
2636 {
2637 if (wxStrcmp(rec->key, str_id) == 0)
2638 {
2639 // Found the item to be removed so delete its record; but first
2640 // replace it in the table with any rec->next (usually == NULL)
2641 (*p_previousrec) = rec->next;
2642 free(rec->key);
2643 delete rec;
2644 return;
2645 }
2646 else
2647 {
2648 p_previousrec = &rec;
2649 }
2650 }
2651 }
2652
2653 static void CleanXRCID_Record(XRCID_record *rec)
2654 {
2655 if (rec)
2656 {
2657 CleanXRCID_Record(rec->next);
2658
2659 free(rec->key);
2660 delete rec;
2661 }
2662 }
2663
2664 static void CleanXRCID_Records()
2665 {
2666 for (int i = 0; i < XRCID_TABLE_SIZE; i++)
2667 {
2668 CleanXRCID_Record(XRCID_Records[i]);
2669 XRCID_Records[i] = NULL;
2670 }
2671
2672 gs_stdIDsAdded = false;
2673 }
2674
2675
2676 //-----------------------------------------------------------------------------
2677 // module and globals
2678 //-----------------------------------------------------------------------------
2679
2680 // normally we would do the cleanup from wxXmlResourceModule::OnExit() but it
2681 // can happen that some XRC records have been created because of the use of
2682 // XRCID() in event tables, which happens during static objects initialization,
2683 // but then the application initialization failed and so the wx modules were
2684 // neither initialized nor cleaned up -- this static object does the cleanup in
2685 // this case
2686 static struct wxXRCStaticCleanup
2687 {
2688 ~wxXRCStaticCleanup() { CleanXRCID_Records(); }
2689 } s_staticCleanup;
2690
2691 class wxXmlResourceModule: public wxModule
2692 {
2693 DECLARE_DYNAMIC_CLASS(wxXmlResourceModule)
2694 public:
2695 wxXmlResourceModule() {}
2696 bool OnInit()
2697 {
2698 wxXmlResource::AddSubclassFactory(new wxXmlSubclassFactoryCXX);
2699 return true;
2700 }
2701 void OnExit()
2702 {
2703 delete wxXmlResource::Set(NULL);
2704 delete wxIdRangeManager::Set(NULL);
2705 if(wxXmlResource::ms_subclassFactories)
2706 {
2707 for ( wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
2708 i != wxXmlResource::ms_subclassFactories->end(); ++i )
2709 {
2710 delete *i;
2711 }
2712 wxDELETE(wxXmlResource::ms_subclassFactories);
2713 }
2714 CleanXRCID_Records();
2715 }
2716 };
2717
2718 IMPLEMENT_DYNAMIC_CLASS(wxXmlResourceModule, wxModule)
2719
2720
2721 // When wxXml is loaded dynamically after the application is already running
2722 // then the built-in module system won't pick this one up. Add it manually.
2723 void wxXmlInitResourceModule()
2724 {
2725 wxModule* module = new wxXmlResourceModule;
2726 module->Init();
2727 wxModule::RegisterModule(module);
2728 }
2729
2730 #endif // wxUSE_XRC