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