]> git.saurik.com Git - wxWidgets.git/blame - src/xrc/xmlres.cpp
fix building with WXWIN_COMPATIBILITY_2_8 == 0
[wxWidgets.git] / src / xrc / xmlres.cpp
CommitLineData
78d14f80 1/////////////////////////////////////////////////////////////////////////////
88a7a4e1 2// Name: src/xrc/xmlres.cpp
b5d6954b 3// Purpose: XRC resources
78d14f80
VS
4// Author: Vaclav Slavik
5// Created: 2000/03/05
78d14f80
VS
6// Copyright: (c) 2000 Vaclav Slavik
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
78d14f80
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#ifdef __BORLANDC__
14 #pragma hdrstop
15#endif
16
621be1ec 17#if wxUSE_XRC
a1e4ec87 18
88a7a4e1
WS
19#include "wx/xrc/xmlres.h"
20
21#ifndef WX_PRECOMP
22 #include "wx/intl.h"
e4db172a 23 #include "wx/log.h"
8e609c82 24 #include "wx/panel.h"
76b49cf4 25 #include "wx/frame.h"
fdf565fe 26 #include "wx/dialog.h"
9eddec69 27 #include "wx/settings.h"
0bca0373 28 #include "wx/bitmap.h"
155ecd4c 29 #include "wx/image.h"
02761f6c 30 #include "wx/module.h"
193d0c93 31 #include "wx/wxcrtvararg.h"
88a7a4e1
WS
32#endif
33
e7a3a5a5 34#ifndef __WXWINCE__
88a7a4e1 35 #include <locale.h>
e7a3a5a5 36#endif
1df61962 37
eb2d0d23 38#include "wx/vector.h"
78d14f80
VS
39#include "wx/wfstream.h"
40#include "wx/filesys.h"
317a0d73 41#include "wx/filename.h"
78d14f80
VS
42#include "wx/tokenzr.h"
43#include "wx/fontenum.h"
78d14f80 44#include "wx/fontmap.h"
af1337b0 45#include "wx/artprov.h"
326462ae 46#include "wx/imaglist.h"
2bb9a404 47#include "wx/dir.h"
34c6bbee 48#include "wx/xml/xml.h"
0526c8cc 49#include "wx/hashset.h"
9fc5a47c 50#include "wx/scopedptr.h"
78d14f80 51
9fc5a47c
VZ
52namespace
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
59wxDateTime 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
f35fdf7e 74
d265ec6b
VZ
75// Assign the given value to the specified entry or add a new value with this
76// name.
3cf2fe54 77static void XRCID_Assign(const wxString& str_id, int value);
d265ec6b 78
eb2d0d23
VS
79class wxXmlResourceDataRecord
80{
81public:
9fc5a47c
VZ
82 // Ctor takes ownership of the document pointer.
83 wxXmlResourceDataRecord(const wxString& File_,
84 wxXmlDocument *Doc_
85 )
86 : File(File_), Doc(Doc_)
87 {
eb2d0d23 88#if wxUSE_DATETIME
9fc5a47c 89 Time = GetXRCFileModTime(File);
eb2d0d23
VS
90#endif
91 }
9fc5a47c 92
eb2d0d23
VS
93 ~wxXmlResourceDataRecord() {delete Doc;}
94
95 wxString File;
96 wxXmlDocument *Doc;
97#if wxUSE_DATETIME
98 wxDateTime Time;
99#endif
9fc5a47c
VZ
100
101 wxDECLARE_NO_COPY_CLASS(wxXmlResourceDataRecord);
eb2d0d23
VS
102};
103
f396e5a7 104class wxXmlResourceDataRecords : public wxVector<wxXmlResourceDataRecord*>
eb2d0d23
VS
105{
106 // this is a class so that it can be forward-declared
107};
78d14f80 108
7a7fa93b 109WX_DECLARE_HASH_SET_PTR(int, wxIntegerHash, wxIntegerEqual, wxHashSetInt);
0526c8cc
VZ
110
111class wxIdRange // Holds data for a particular rangename
112{
113protected:
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
139class wxIdRangeManager
140{
141public:
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;
0526c8cc
VZ
158
159protected:
160 wxIdRange* FindRangeForItem(const wxXmlNode* node,
161 const wxString& item,
162 wxString& value) const;
163 wxVector<wxIdRange*> m_IdRanges;
164
165private:
166 static wxIdRangeManager *ms_instance;
167};
168
23239d94
VZ
169namespace
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
176inline 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
a1eeda0b
VS
183// special XML attribute with name of input file, see GetFileNameFromNode()
184const char *ATTR_INPUT_FILENAME = "__wx:filename";
185
186// helper to get filename corresponding to an XML node
187wxString
1f6ea935 188GetFileNameFromNode(const wxXmlNode *node, const wxXmlResourceDataRecords& files)
a1eeda0b
VS
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
23239d94
VZ
225} // anonymous namespace
226
78d14f80 227
824e8eaa
VS
228wxXmlResource *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
d4a724d4 244wxXmlResource::wxXmlResource(int flags, const wxString& domain)
78d14f80 245{
daa85ee3 246 m_flags = flags;
78d14f80 247 m_version = -1;
eb2d0d23 248 m_data = new wxXmlResourceDataRecords;
d7a80cf5 249 SetDomain(domain);
78d14f80
VS
250}
251
d4a724d4 252wxXmlResource::wxXmlResource(const wxString& filemask, int flags, const wxString& domain)
78d14f80 253{
daa85ee3 254 m_flags = flags;
78d14f80 255 m_version = -1;
eb2d0d23 256 m_data = new wxXmlResourceDataRecords;
d7a80cf5 257 SetDomain(domain);
78d14f80
VS
258 Load(filemask);
259}
260
261wxXmlResource::~wxXmlResource()
262{
263 ClearHandlers();
eb2d0d23 264
f396e5a7
VS
265 for ( wxXmlResourceDataRecords::iterator i = m_data->begin();
266 i != m_data->end(); ++i )
267 {
268 delete *i;
269 }
eb2d0d23 270 delete m_data;
78d14f80
VS
271}
272
d7a80cf5 273void wxXmlResource::SetDomain(const wxString& domain)
d4a724d4 274{
d7a80cf5 275 m_domain = domain;
d4a724d4
RD
276}
277
78d14f80 278
60fd818a
VZ
279/* static */
280wxString 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 */
310bool 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
04ae32cd
VS
319bool wxXmlResource::LoadFile(const wxFileName& file)
320{
159852ae 321#if wxUSE_FILESYSTEM
04ae32cd 322 return Load(wxFileSystem::FileNameToURL(file));
159852ae
VS
323#else
324 return Load(file.GetFullPath());
325#endif
04ae32cd
VS
326}
327
2bb9a404
VS
328bool 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
acd32ffc 344bool wxXmlResource::Load(const wxString& filemask_)
78d14f80 345{
acd32ffc
VS
346 wxString filemask = ConvertFileNameToURL(filemask_);
347
9fc5a47c
VZ
348 bool allOK = true;
349
78d14f80
VS
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
e5bb501b
VZ
358 wxString fnd = wxXmlFindFirst;
359 if ( fnd.empty() )
360 {
361 wxLogError(_("Cannot load resources from '%s'."), filemask);
362 return false;
363 }
364
ec157c8f 365 while (!fnd.empty())
78d14f80
VS
366 {
367#if wxUSE_FILESYSTEM
60fd818a 368 if ( IsArchive(fnd) )
78d14f80 369 {
e5bb501b 370 if ( !Load(fnd + wxT("#zip:*.xrc")) )
9fc5a47c 371 allOK = false;
78d14f80 372 }
60fd818a
VZ
373 else // a single resource URL
374#endif // wxUSE_FILESYSTEM
78d14f80 375 {
9fc5a47c
VZ
376 wxXmlDocument * const doc = DoLoadFile(fnd);
377 if ( !doc )
378 allOK = false;
379 else
380 Data().push_back(new wxXmlResourceDataRecord(fnd, doc));
78d14f80
VS
381 }
382
e5bb501b 383 fnd = wxXmlFindNext;
78d14f80
VS
384 }
385# undef wxXmlFindFirst
386# undef wxXmlFindNext
e5bb501b 387
9fc5a47c 388 return allOK;
78d14f80
VS
389}
390
60fd818a
VZ
391bool wxXmlResource::Unload(const wxString& filename)
392{
393 wxASSERT_MSG( !wxIsWild(filename),
9a83f860 394 wxT("wildcards not supported by wxXmlResource::Unload()") );
60fd818a
VZ
395
396 wxString fnd = ConvertFileNameToURL(filename);
397#if wxUSE_FILESYSTEM
398 const bool isArchive = IsArchive(fnd);
399 if ( isArchive )
9a83f860 400 fnd += wxT("#zip:");
60fd818a
VZ
401#endif // wxUSE_FILESYSTEM
402
403 bool unloaded = false;
eb2d0d23
VS
404 for ( wxXmlResourceDataRecords::iterator i = Data().begin();
405 i != Data().end(); ++i )
60fd818a
VZ
406 {
407#if wxUSE_FILESYSTEM
408 if ( isArchive )
409 {
f396e5a7 410 if ( (*i)->File.StartsWith(fnd) )
60fd818a
VZ
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 {
f396e5a7 417 if ( (*i)->File == fnd )
60fd818a 418 {
f8b1df0b 419 delete *i;
eb2d0d23 420 Data().erase(i);
60fd818a
VZ
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
78d14f80 432
78d14f80
VS
433void wxXmlResource::AddHandler(wxXmlResourceHandler *handler)
434{
a3b9c43b
VZ
435 wxXmlResourceHandlerImpl *impl = new wxXmlResourceHandlerImpl(handler);
436 handler->SetImpl(impl);
eb2d0d23 437 m_handlers.push_back(handler);
78d14f80
VS
438 handler->SetParentResource(this);
439}
440
92e898b0
RD
441void wxXmlResource::InsertHandler(wxXmlResourceHandler *handler)
442{
a3b9c43b
VZ
443 wxXmlResourceHandlerImpl *impl = new wxXmlResourceHandlerImpl(handler);
444 handler->SetImpl(impl);
eb2d0d23 445 m_handlers.insert(m_handlers.begin(), handler);
92e898b0
RD
446 handler->SetParentResource(this);
447}
448
78d14f80
VS
449
450
451void wxXmlResource::ClearHandlers()
452{
eb2d0d23
VS
453 for ( wxVector<wxXmlResourceHandler*>::iterator i = m_handlers.begin();
454 i != m_handlers.end(); ++i )
455 delete *i;
456 m_handlers.clear();
78d14f80
VS
457}
458
459
78d14f80
VS
460wxMenu *wxXmlResource::LoadMenu(const wxString& name)
461{
462 return (wxMenu*)CreateResFromNode(FindResource(name, wxT("wxMenu")), NULL, NULL);
463}
464
465
466
4a1b9596 467wxMenuBar *wxXmlResource::LoadMenuBar(wxWindow *parent, const wxString& name)
78d14f80 468{
4a1b9596 469 return (wxMenuBar*)CreateResFromNode(FindResource(name, wxT("wxMenuBar")), parent, NULL);
78d14f80
VS
470}
471
472
473
4a1b9596 474#if wxUSE_TOOLBAR
78d14f80
VS
475wxToolBar *wxXmlResource::LoadToolBar(wxWindow *parent, const wxString& name)
476{
477 return (wxToolBar*)CreateResFromNode(FindResource(name, wxT("wxToolBar")), parent, NULL);
478}
4a1b9596 479#endif
78d14f80
VS
480
481
482wxDialog *wxXmlResource::LoadDialog(wxWindow *parent, const wxString& name)
483{
4dd75a6a 484 return (wxDialog*)CreateResFromNode(FindResource(name, wxT("wxDialog")), parent, NULL);
78d14f80
VS
485}
486
487bool wxXmlResource::LoadDialog(wxDialog *dlg, wxWindow *parent, const wxString& name)
488{
489 return CreateResFromNode(FindResource(name, wxT("wxDialog")), parent, dlg) != NULL;
490}
491
492
493
494wxPanel *wxXmlResource::LoadPanel(wxWindow *parent, const wxString& name)
495{
496 return (wxPanel*)CreateResFromNode(FindResource(name, wxT("wxPanel")), parent, NULL);
497}
498
499bool wxXmlResource::LoadPanel(wxPanel *panel, wxWindow *parent, const wxString& name)
500{
501 return CreateResFromNode(FindResource(name, wxT("wxPanel")), parent, panel) != NULL;
502}
503
92e898b0
RD
504wxFrame *wxXmlResource::LoadFrame(wxWindow* parent, const wxString& name)
505{
506 return (wxFrame*)CreateResFromNode(FindResource(name, wxT("wxFrame")), parent, NULL);
507}
508
78d14f80
VS
509bool wxXmlResource::LoadFrame(wxFrame* frame, wxWindow *parent, const wxString& name)
510{
511 return CreateResFromNode(FindResource(name, wxT("wxFrame")), parent, frame) != NULL;
512}
513
514wxBitmap 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
524wxIcon 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
92e898b0 534
af0ac990
VZ
535wxObject *
536wxXmlResource::DoLoadObject(wxWindow *parent,
537 const wxString& name,
538 const wxString& classname,
539 bool recursive)
92e898b0 540{
af0ac990
VZ
541 wxXmlNode * const node = FindResource(name, classname, recursive);
542
543 return node ? DoCreateResFromNode(*node, parent, NULL) : NULL;
92e898b0
RD
544}
545
af0ac990
VZ
546bool
547wxXmlResource::DoLoadObject(wxObject *instance,
548 wxWindow *parent,
549 const wxString& name,
550 const wxString& classname,
551 bool recursive)
92e898b0 552{
af0ac990
VZ
553 wxXmlNode * const node = FindResource(name, classname, recursive);
554
555 return node && DoCreateResFromNode(*node, parent, instance) != NULL;
92e898b0
RD
556}
557
558
78d14f80
VS
559bool 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 {
819559b2 567 wxLogError("Cannot find container for unknown control '%s'.", name);
f80ea77b 568 return false;
78d14f80
VS
569 }
570 return control->Reparent(container);
571}
572
573
77b2f9b1 574static void ProcessPlatformProperty(wxXmlNode *node)
78d14f80
VS
575{
576 wxString s;
577 bool isok;
578
579 wxXmlNode *c = node->GetChildren();
580 while (c)
581 {
f80ea77b 582 isok = false;
288b6107 583 if (!c->GetAttribute(wxT("platform"), &s))
f80ea77b 584 isok = true;
78d14f80
VS
585 else
586 {
2b5f62a0 587 wxStringTokenizer tkn(s, wxT(" |"));
78d14f80
VS
588
589 while (tkn.HasMoreTokens())
590 {
591 s = tkn.GetNextToken();
84389518 592#ifdef __WINDOWS__
d003330c
VS
593 if (s == wxT("win")) isok = true;
594#endif
b380439d 595#if defined(__MAC__) || defined(__APPLE__)
d003330c 596 if (s == wxT("mac")) isok = true;
b380439d
RD
597#elif defined(__UNIX__)
598 if (s == wxT("unix")) isok = true;
78d14f80 599#endif
d003330c
VS
600#ifdef __OS2__
601 if (s == wxT("os2")) isok = true;
602#endif
603
604 if (isok)
605 break;
78d14f80
VS
606 }
607 }
608
609 if (isok)
d7b1d73c 610 {
78d14f80 611 ProcessPlatformProperty(c);
d7b1d73c
VS
612 c = c->GetNext();
613 }
78d14f80
VS
614 else
615 {
d7b1d73c 616 wxXmlNode *c2 = c->GetNext();
db59a97c 617 node->RemoveChild(c);
78d14f80 618 delete c;
d7b1d73c 619 c = c2;
78d14f80 620 }
78d14f80
VS
621 }
622}
623
0526c8cc
VZ
624static 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);
78d14f80 643
0526c8cc
VZ
644 // Do any children by recursion, then proceed to the next sibling
645 PreprocessForIdRanges(c);
646 c = c->GetNext();
647 }
648}
78d14f80 649
d614f51b 650bool wxXmlResource::UpdateResources()
78d14f80 651{
d614f51b 652 bool rt = true;
480505bc 653
eb2d0d23
VS
654 for ( wxXmlResourceDataRecords::iterator i = Data().begin();
655 i != Data().end(); ++i )
78d14f80 656 {
f396e5a7
VS
657 wxXmlResourceDataRecord* const rec = *i;
658
9fc5a47c 659 // Check if we need to reload this one.
78d14f80 660
9fc5a47c
VZ
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
78d14f80 677 {
9fc5a47c
VZ
678 // No need to reload, the file wasn't modified since we did it
679 // last.
680 continue;
78d14f80
VS
681 }
682
9fc5a47c
VZ
683 wxXmlDocument * const doc = DoLoadFile(rec->File);
684 if ( !doc )
78d14f80 685 {
9fc5a47c
VZ
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 }
78d14f80 692
9fc5a47c
VZ
693 // Replace the old resource contents with the new one.
694 delete rec->Doc;
695 rec->Doc = doc;
78d14f80 696
9fc5a47c 697 // And, now that we loaded it successfully, update the last load time.
34af0de4 698#if wxUSE_DATETIME
9fc5a47c 699 rec->Time = lastModTime.IsValid() ? lastModTime : wxDateTime::Now();
34af0de4 700#endif // wxUSE_DATETIME
78d14f80 701 }
d614f51b
VS
702
703 return rt;
78d14f80
VS
704}
705
9fc5a47c
VZ
706wxXmlDocument *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
b272b6dc
RD
782wxXmlNode *wxXmlResource::DoFindResource(wxXmlNode *parent,
783 const wxString& name,
784 const wxString& classname,
23239d94 785 bool recursive) const
47793ab8 786{
47793ab8
VS
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 {
23239d94 793 if ( IsObjectNode(node) && node->GetAttribute(wxS("name")) == name )
2b5f62a0 794 {
23239d94
VZ
795 // empty class name matches everything
796 if ( classname.empty() )
2b5f62a0 797 return node;
23239d94
VZ
798
799 wxString cls(node->GetAttribute(wxS("class")));
800
288b6107 801 // object_ref may not have 'class' attribute:
23239d94 802 if (cls.empty() && node->GetName() == wxS("object_ref"))
2b5f62a0 803 {
23239d94 804 wxString refName = node->GetAttribute(wxS("ref"));
2b5f62a0
VZ
805 if (refName.empty())
806 continue;
23239d94
VZ
807
808 const wxXmlNode * const refNode = GetResourceNode(refName);
809 if ( refNode )
810 cls = refNode->GetAttribute(wxS("class"));
2b5f62a0 811 }
23239d94
VZ
812
813 if ( cls == classname )
814 return node;
2b5f62a0 815 }
47793ab8
VS
816 }
817
23239d94 818 // then recurse in child nodes
47793ab8 819 if ( recursive )
23239d94 820 {
47793ab8
VS
821 for (node = parent->GetChildren(); node; node = node->GetNext())
822 {
23239d94 823 if ( IsObjectNode(node) )
47793ab8 824 {
f80ea77b 825 wxXmlNode* found = DoFindResource(node, name, classname, true);
47793ab8
VS
826 if ( found )
827 return found;
828 }
829 }
23239d94 830 }
47793ab8 831
23239d94 832 return NULL;
47793ab8 833}
78d14f80 834
b272b6dc 835wxXmlNode *wxXmlResource::FindResource(const wxString& name,
47793ab8
VS
836 const wxString& classname,
837 bool recursive)
78d14f80 838{
23239d94
VZ
839 wxString path;
840 wxXmlNode * const
841 node = GetResourceNodeAndLocation(name, classname, recursive, &path);
842
843 if ( !node )
844 {
819559b2
VS
845 ReportError
846 (
847 NULL,
848 wxString::Format
849 (
850 "XRC resource \"%s\" (class \"%s\") not found",
851 name, classname
852 )
853 );
23239d94
VZ
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
868wxXmlNode *
869wxXmlResource::GetResourceNodeAndLocation(const wxString& name,
870 const wxString& classname,
871 bool recursive,
872 wxString *path) const
873{
0526c8cc 874 // ensure everything is up-to-date: this is needed to support on-demand
23239d94
VZ
875 // reloading of XRC files
876 const_cast<wxXmlResource *>(this)->UpdateResources();
78d14f80 877
eb2d0d23
VS
878 for ( wxXmlResourceDataRecords::const_iterator f = Data().begin();
879 f != Data().end(); ++f )
78d14f80 880 {
23239d94
VZ
881 wxXmlResourceDataRecord *const rec = *f;
882 wxXmlDocument * const doc = rec->Doc;
883 if ( !doc || !doc->GetRoot() )
47793ab8
VS
884 continue;
885
23239d94
VZ
886 wxXmlNode * const
887 found = DoFindResource(doc->GetRoot(), name, classname, recursive);
47793ab8
VS
888 if ( found )
889 {
23239d94
VZ
890 if ( path )
891 *path = rec->File;
892
47793ab8
VS
893 return found;
894 }
78d14f80
VS
895 }
896
78d14f80
VS
897 return NULL;
898}
899
a1eeda0b
VS
900static void MergeNodesOver(wxXmlNode& dest, wxXmlNode& overwriteWith,
901 const wxString& overwriteFilename)
47793ab8 902{
288b6107 903 // Merge attributes:
a1eeda0b 904 for ( wxXmlAttribute *attr = overwriteWith.GetAttributes();
288b6107 905 attr; attr = attr->GetNext() )
47793ab8 906 {
288b6107
VS
907 wxXmlAttribute *dattr;
908 for (dattr = dest.GetAttributes(); dattr; dattr = dattr->GetNext())
47793ab8 909 {
b272b6dc 910
288b6107 911 if ( dattr->GetName() == attr->GetName() )
47793ab8 912 {
288b6107 913 dattr->SetValue(attr->GetValue());
47793ab8
VS
914 break;
915 }
916 }
78d14f80 917
288b6107
VS
918 if ( !dattr )
919 dest.AddAttribute(attr->GetName(), attr->GetValue());
47793ab8
VS
920 }
921
922 // Merge child nodes:
a1eeda0b 923 for (wxXmlNode* node = overwriteWith.GetChildren(); node; node = node->GetNext())
47793ab8 924 {
288b6107 925 wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
47793ab8
VS
926 wxXmlNode *dnode;
927
928 for (dnode = dest.GetChildren(); dnode; dnode = dnode->GetNext() )
929 {
930 if ( dnode->GetName() == node->GetName() &&
288b6107 931 dnode->GetAttribute(wxT("name"), wxEmptyString) == name &&
47793ab8
VS
932 dnode->GetType() == node->GetType() )
933 {
a1eeda0b 934 MergeNodesOver(*dnode, *node, overwriteFilename);
47793ab8
VS
935 break;
936 }
937 }
938
939 if ( !dnode )
b26a650c 940 {
a1eeda0b
VS
941 wxXmlNode *copyOfNode = new wxXmlNode(*node);
942 // remember referenced object's file, see GetFileNameFromNode()
943 copyOfNode->AddAttribute(ATTR_INPUT_FILENAME, overwriteFilename);
944
b26a650c 945 static const wxChar *AT_END = wxT("end");
288b6107 946 wxString insert_pos = node->GetAttribute(wxT("insert_at"), AT_END);
b26a650c
VZ
947 if ( insert_pos == AT_END )
948 {
a1eeda0b 949 dest.AddChild(copyOfNode);
b26a650c
VZ
950 }
951 else if ( insert_pos == wxT("begin") )
952 {
a1eeda0b 953 dest.InsertChild(copyOfNode, dest.GetChildren());
b26a650c
VZ
954 }
955 }
47793ab8
VS
956 }
957
a1eeda0b
VS
958 if ( dest.GetType() == wxXML_TEXT_NODE && overwriteWith.GetContent().length() )
959 dest.SetContent(overwriteWith.GetContent());
47793ab8 960}
78d14f80 961
af0ac990
VZ
962wxObject *
963wxXmlResource::DoCreateResFromNode(wxXmlNode& node,
964 wxObject *parent,
965 wxObject *instance,
966 wxXmlResourceHandler *handlerToUse)
78d14f80 967{
47793ab8 968 // handling of referenced resource
af0ac990 969 if ( node.GetName() == wxT("object_ref") )
47793ab8 970 {
af0ac990 971 wxString refName = node.GetAttribute(wxT("ref"), wxEmptyString);
f80ea77b 972 wxXmlNode* refNode = FindResource(refName, wxEmptyString, true);
47793ab8
VS
973
974 if ( !refNode )
975 {
819559b2
VS
976 ReportError
977 (
af0ac990 978 &node,
819559b2
VS
979 wxString::Format
980 (
981 "referenced object node with ref=\"%s\" not found",
982 refName
983 )
984 );
47793ab8
VS
985 return NULL;
986 }
987
9445e542
VS
988 const bool hasOnlyRefAttr = node.GetAttributes() != NULL &&
989 node.GetAttributes()->GetNext() == NULL;
990
991 if ( hasOnlyRefAttr && !node.GetChildren() )
44108a07
VS
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
af0ac990 998 return DoCreateResFromNode(*refNode, parent, instance);
44108a07
VS
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);
af0ac990 1008 MergeNodesOver(copy, node, GetFileNameFromNode(&node, Data()));
a1eeda0b 1009
44108a07
VS
1010 // remember referenced object's file, see GetFileNameFromNode()
1011 copy.AddAttribute(ATTR_INPUT_FILENAME,
1012 GetFileNameFromNode(refNode, Data()));
47793ab8 1013
af0ac990 1014 return DoCreateResFromNode(copy, parent, instance);
44108a07 1015 }
47793ab8
VS
1016 }
1017
317a0d73 1018 if (handlerToUse)
b380439d 1019 {
af0ac990 1020 if (handlerToUse->CanHandle(&node))
8e8a4e85 1021 {
af0ac990 1022 return handlerToUse->CreateResource(&node, parent, instance);
8e8a4e85 1023 }
317a0d73 1024 }
af0ac990 1025 else if (node.GetName() == wxT("object"))
b380439d 1026 {
eb2d0d23
VS
1027 for ( wxVector<wxXmlResourceHandler*>::iterator h = m_handlers.begin();
1028 h != m_handlers.end(); ++h )
317a0d73 1029 {
eb2d0d23 1030 wxXmlResourceHandler *handler = *h;
af0ac990
VZ
1031 if (handler->CanHandle(&node))
1032 return handler->CreateResource(&node, parent, instance);
78d14f80 1033 }
78d14f80
VS
1034 }
1035
819559b2
VS
1036 ReportError
1037 (
af0ac990 1038 &node,
819559b2
VS
1039 wxString::Format
1040 (
1041 "no handler found for XML node \"%s\" (class \"%s\")",
af0ac990
VZ
1042 node.GetName(),
1043 node.GetAttribute("class", wxEmptyString)
819559b2
VS
1044 )
1045 );
78d14f80
VS
1046 return NULL;
1047}
1048
0526c8cc
VZ
1049wxIdRange::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
1099void 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
1174void 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 {
d265ec6b
VZ
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);
0526c8cc 1224
e7cad4b7
VZ
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()));
0526c8cc
VZ
1230 }
1231 // and these special ones
d265ec6b
VZ
1232 XRCID_Assign(m_name + "[start]", m_start);
1233 XRCID_Assign(m_name + "[end]", m_end);
0526c8cc
VZ
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
1241wxIdRangeManager *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
1257wxIdRangeManager::~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
1269void 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
1307wxIdRange *
1308wxIdRangeManager::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
1333void
1334wxIdRangeManager::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
1343int 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
1354void 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
78d14f80 1369
eb2d0d23
VS
1370class wxXmlSubclassFactories : public wxVector<wxXmlSubclassFactory*>
1371{
1372 // this is a class so that it can be forward-declared
1373};
2b5f62a0 1374
eb2d0d23 1375wxXmlSubclassFactories *wxXmlResource::ms_subclassFactories = NULL;
2b5f62a0
VZ
1376
1377/*static*/ void wxXmlResource::AddSubclassFactory(wxXmlSubclassFactory *factory)
1378{
1379 if (!ms_subclassFactories)
1380 {
eb2d0d23 1381 ms_subclassFactories = new wxXmlSubclassFactories;
2b5f62a0 1382 }
eb2d0d23 1383 ms_subclassFactories->push_back(factory);
2b5f62a0
VZ
1384}
1385
1386class wxXmlSubclassFactoryCXX : public wxXmlSubclassFactory
1387{
1388public:
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
78d14f80 1404
a3b9c43b
VZ
1405wxXmlResourceHandlerImpl::wxXmlResourceHandlerImpl(wxXmlResourceHandler *handler)
1406 :wxXmlResourceHandlerImplBase(handler)
1407{
1408}
78d14f80 1409
a3b9c43b
VZ
1410wxObject *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
1417wxFileSystem& wxXmlResourceHandlerImpl::GetCurFileSystem()
1418{
1419 return m_handler->m_resource->GetCurFileSystem();
1420}
1421#endif
78d14f80
VS
1422
1423
a3b9c43b 1424wxObject *wxXmlResourceHandlerImpl::CreateResource(wxXmlNode *node, wxObject *parent, wxObject *instance)
78d14f80 1425{
a3b9c43b
VZ
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;
78d14f80 1430
a3b9c43b
VZ
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))
daa85ee3 1434 {
288b6107 1435 wxString subclass = node->GetAttribute(wxT("subclass"), wxEmptyString);
2b5f62a0 1436 if (!subclass.empty())
daa85ee3 1437 {
eb2d0d23
VS
1438 for (wxXmlSubclassFactories::iterator i = wxXmlResource::ms_subclassFactories->begin();
1439 i != wxXmlResource::ms_subclassFactories->end(); ++i)
2b5f62a0 1440 {
a3b9c43b
VZ
1441 m_handler->m_instance = (*i)->Create(subclass);
1442 if (m_handler->m_instance)
2b5f62a0
VZ
1443 break;
1444 }
daa85ee3 1445
a3b9c43b 1446 if (!m_handler->m_instance)
2b5f62a0 1447 {
288b6107 1448 wxString name = node->GetAttribute(wxT("name"), wxEmptyString);
819559b2
VS
1449 ReportError
1450 (
1451 node,
1452 wxString::Format
1453 (
1454 "subclass \"%s\" not found for resource \"%s\", not subclassing",
1455 subclass, name
1456 )
1457 );
2b5f62a0
VZ
1458 }
1459 }
daa85ee3
VS
1460 }
1461
a3b9c43b
VZ
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);
78d14f80 1466
a3b9c43b 1467 wxObject *returned = GetHandler()->DoCreateResource();
78d14f80 1468
a3b9c43b
VZ
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;
78d14f80
VS
1473
1474 return returned;
1475}
1476
a3b9c43b 1477bool wxXmlResourceHandlerImpl::HasParam(const wxString& param)
78d14f80
VS
1478{
1479 return (GetParamNode(param) != NULL);
1480}
1481
1482
a3b9c43b 1483int wxXmlResourceHandlerImpl::GetStyle(const wxString& param, int defaults)
78d14f80
VS
1484{
1485 wxString s = GetParamValue(param);
1486
1487 if (!s) return defaults;
1488
2b5f62a0 1489 wxStringTokenizer tkn(s, wxT("| \t\n"), wxTOKEN_STRTOK);
78d14f80
VS
1490 int style = 0;
1491 int index;
1492 wxString fl;
1493 while (tkn.HasMoreTokens())
1494 {
1495 fl = tkn.GetNextToken();
a3b9c43b 1496 index = m_handler->m_styleNames.Index(fl);
78d14f80 1497 if (index != wxNOT_FOUND)
819559b2 1498 {
a3b9c43b 1499 style |= m_handler->m_styleValues[index];
819559b2 1500 }
78d14f80 1501 else
819559b2
VS
1502 {
1503 ReportParamError
1504 (
1505 param,
1506 wxString::Format("unknown style flag \"%s\"", fl)
1507 );
1508 }
78d14f80
VS
1509 }
1510 return style;
1511}
1512
1513
1514
a3b9c43b 1515wxString wxXmlResourceHandlerImpl::GetText(const wxString& param, bool translate)
78d14f80 1516{
7b56015f
VS
1517 wxXmlNode *parNode = GetParamNode(param);
1518 wxString str1(GetNodeContent(parNode));
78d14f80 1519 wxString str2;
424af7aa
VS
1520
1521 // "\\" wasn't translated to "\" prior to 2.5.3.0:
a3b9c43b 1522 const bool escapeBackslash = (m_handler->m_resource->CompareVersion(2,5,3,0) >= 0);
78d14f80 1523
b272b6dc
RD
1524 // VS: First version of XRC resources used $ instead of & (which is
1525 // illegal in XML), but later I realized that '_' fits this purpose
718cf160 1526 // much better (because &File means "File with F underlined").
a3b9c43b 1527 const wxChar amp_char = (m_handler->m_resource->CompareVersion(2,3,0,1) < 0)
424af7aa 1528 ? '$' : '_';
78d14f80 1529
424af7aa 1530 for ( wxString::const_iterator dt = str1.begin(); dt != str1.end(); ++dt )
78d14f80
VS
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..."):
424af7aa 1534 if ( *dt == amp_char )
78d14f80 1535 {
2148394a 1536 if ( dt+1 == str1.end() || *(++dt) == amp_char )
78d14f80
VS
1537 str2 << amp_char;
1538 else
1539 str2 << wxT('&') << *dt;
1540 }
984c33c9 1541 // Remap \n to CR, \r to LF, \t to TAB, \\ to \:
424af7aa
VS
1542 else if ( *dt == wxT('\\') )
1543 {
1544 switch ( (*(++dt)).GetValue() )
78d14f80 1545 {
984c33c9
VS
1546 case wxT('n'):
1547 str2 << wxT('\n');
1548 break;
e7a3a5a5 1549
984c33c9
VS
1550 case wxT('t'):
1551 str2 << wxT('\t');
1552 break;
e7a3a5a5 1553
984c33c9
VS
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:
424af7aa 1560 if ( escapeBackslash )
984c33c9
VS
1561 {
1562 str2 << wxT('\\');
1563 break;
1564 }
1565 // else fall-through to default: branch below
e7a3a5a5 1566
984c33c9
VS
1567 default:
1568 str2 << wxT('\\') << *dt;
1569 break;
78d14f80 1570 }
424af7aa
VS
1571 }
1572 else
1573 {
1574 str2 << *dt;
1575 }
78d14f80 1576 }
b272b6dc 1577
a3b9c43b 1578 if (m_handler->m_resource->GetFlags() & wxXRC_USE_LOCALE)
7b56015f
VS
1579 {
1580 if (translate && parNode &&
288b6107 1581 parNode->GetAttribute(wxT("translate"), wxEmptyString) != wxT("0"))
7b56015f 1582 {
a3b9c43b 1583 return wxGetTranslation(str2, m_handler->m_resource->GetDomain());
7b56015f
VS
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:
6251e0ea 1592 return wxString(str2.wc_str(wxConvUTF8), wxConvLocal);
7b56015f
VS
1593#endif
1594 }
1595 }
8516a98b
DS
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;
78d14f80
VS
1601}
1602
1603
1604
a3b9c43b 1605long wxXmlResourceHandlerImpl::GetLong(const wxString& param, long defaultv)
78d14f80 1606{
9f5103f1 1607 long value = defaultv;
78d14f80
VS
1608 wxString str1 = GetParamValue(param);
1609
9f5103f1
VZ
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 }
78d14f80
VS
1621
1622 return value;
1623}
e7a3a5a5 1624
a3b9c43b 1625float wxXmlResourceHandlerImpl::GetFloat(const wxString& param, float defaultv)
1df61962 1626{
1d9473d3 1627 wxString str = GetParamValue(param);
1df61962 1628
9edb8fa0
VZ
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
9f5103f1
VZ
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 }
1df61962 1644
17a1ebd1 1645 return wx_truncate_cast(float, value);
1df61962 1646}
78d14f80 1647
af1337b0 1648
a3b9c43b 1649int wxXmlResourceHandlerImpl::GetID()
78d14f80 1650{
13de23f6 1651 return wxXmlResource::GetXRCID(GetName());
78d14f80
VS
1652}
1653
1654
af1337b0 1655
a3b9c43b 1656wxString wxXmlResourceHandlerImpl::GetName()
78d14f80 1657{
a3b9c43b 1658 return m_handler->m_node->GetAttribute(wxT("name"), wxT("-1"));
78d14f80
VS
1659}
1660
1661
1662
a3b9c43b 1663bool wxXmlResourceHandlerImpl::GetBoolAttr(const wxString& attr, bool defaultv)
8758875e
VZ
1664{
1665 wxString v;
a3b9c43b 1666 return m_handler->m_node->GetAttribute(attr, &v) ? v == '1' : defaultv;
8758875e
VZ
1667}
1668
a3b9c43b 1669bool wxXmlResourceHandlerImpl::GetBool(const wxString& param, bool defaultv)
78d14f80 1670{
8758875e 1671 const wxString v = GetParamValue(param);
8516a98b 1672
8758875e 1673 return v.empty() ? defaultv : (v == '1');
78d14f80
VS
1674}
1675
1676
1df61962
VS
1677static wxColour GetSystemColour(const wxString& name)
1678{
1679 if (!name.empty())
1680 {
1681 #define SYSCLR(clr) \
9a83f860 1682 if (name == wxT(#clr)) return wxSystemSettings::GetColour(clr);
1df61962
VS
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}
78d14f80 1725
a3b9c43b 1726wxColour wxXmlResourceHandlerImpl::GetColour(const wxString& param, const wxColour& defaultv)
78d14f80
VS
1727{
1728 wxString v = GetParamValue(param);
984f1d84
VS
1729
1730 if ( v.empty() )
1731 return defaultv;
1732
68b4e4cf 1733 wxColour clr;
1df61962 1734
68b4e4cf
WS
1735 // wxString -> wxColour conversion
1736 if (!clr.Set(v))
78d14f80 1737 {
1df61962
VS
1738 // the colour doesn't use #RRGGBB format, check if it is symbolic
1739 // colour name:
68b4e4cf 1740 clr = GetSystemColour(v);
a1b806b9 1741 if (clr.IsOk())
1df61962 1742 return clr;
e7a3a5a5 1743
819559b2
VS
1744 ReportParamError
1745 (
1746 param,
1747 wxString::Format("incorrect colour specification \"%s\"", v)
1748 );
78d14f80
VS
1749 return wxNullColour;
1750 }
1751
68b4e4cf 1752 return clr;
78d14f80
VS
1753}
1754
1c60f644
VS
1755namespace
1756{
1757
1758// if 'param' has stock_id/stock_client, extracts them and returns true
1759bool 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);
78d14f80 1776
1c60f644
VS
1777 return true;
1778 }
1779 }
1780
1781 return false;
1782}
1783
1784} // anonymous namespace
78d14f80 1785
a3b9c43b 1786wxBitmap wxXmlResourceHandlerImpl::GetBitmap(const wxString& param,
db59a97c
VS
1787 const wxArtClient& defaultArtClient,
1788 wxSize size)
326462ae 1789{
9c1d2aa2
VZ
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);
326462ae
VZ
1805}
1806
a3b9c43b 1807wxBitmap wxXmlResourceHandlerImpl::GetBitmap(const wxXmlNode* node,
326462ae
VZ
1808 const wxArtClient& defaultArtClient,
1809 wxSize size)
78d14f80 1810{
9c1d2aa2
VZ
1811 wxCHECK_MSG( node, wxNullBitmap, "bitmap node can't be NULL" );
1812
db59a97c 1813 /* If the bitmap is specified as stock item, query wxArtProvider for it: */
1c60f644 1814 wxString art_id, art_client;
326462ae 1815 if ( GetStockArtAttrs(node, defaultArtClient,
1c60f644 1816 art_id, art_client) )
af1337b0 1817 {
1c60f644 1818 wxBitmap stockArt(wxArtProvider::GetBitmap(art_id, art_client, size));
a1b806b9 1819 if ( stockArt.IsOk() )
1c60f644 1820 return stockArt;
af1337b0
JS
1821 }
1822
92e898b0 1823 /* ...or load the bitmap from file: */
326462ae 1824 wxString name = GetParamValue(node);
e7a3a5a5 1825 if (name.empty()) return wxNullBitmap;
78d14f80 1826#if wxUSE_FILESYSTEM
4532786e 1827 wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE);
78d14f80
VS
1828 if (fsfile == NULL)
1829 {
819559b2
VS
1830 ReportParamError
1831 (
326462ae 1832 node->GetName(),
819559b2
VS
1833 wxString::Format("cannot open bitmap resource \"%s\"", name)
1834 );
78d14f80
VS
1835 return wxNullBitmap;
1836 }
1837 wxImage img(*(fsfile->GetStream()));
1838 delete fsfile;
1839#else
45f3249b 1840 wxImage img(name);
78d14f80 1841#endif
af1337b0 1842
a1b806b9 1843 if (!img.IsOk())
78d14f80 1844 {
819559b2
VS
1845 ReportParamError
1846 (
326462ae 1847 node->GetName(),
819559b2
VS
1848 wxString::Format("cannot create bitmap from \"%s\"", name)
1849 );
78d14f80
VS
1850 return wxNullBitmap;
1851 }
1852 if (!(size == wxDefaultSize)) img.Rescale(size.x, size.y);
b272b6dc 1853 return wxBitmap(img);
78d14f80
VS
1854}
1855
78d14f80 1856
a3b9c43b 1857wxIcon wxXmlResourceHandlerImpl::GetIcon(const wxString& param,
db59a97c
VS
1858 const wxArtClient& defaultArtClient,
1859 wxSize size)
326462ae 1860{
9c1d2aa2
VZ
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);
326462ae
VZ
1873}
1874
a3b9c43b 1875wxIcon wxXmlResourceHandlerImpl::GetIcon(const wxXmlNode* node,
326462ae
VZ
1876 const wxArtClient& defaultArtClient,
1877 wxSize size)
78d14f80 1878{
78d14f80 1879 wxIcon icon;
326462ae 1880 icon.CopyFromBitmap(GetBitmap(node, defaultArtClient, size));
78d14f80
VS
1881 return icon;
1882}
1883
326462ae 1884
a3b9c43b 1885wxIconBundle wxXmlResourceHandlerImpl::GetIconBundle(const wxString& param,
1c60f644
VS
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}
78d14f80
VS
1931
1932
a3b9c43b 1933wxImageList *wxXmlResourceHandlerImpl::GetImageList(const wxString& param)
326462ae
VZ
1934{
1935 wxXmlNode * const imagelist_node = GetParamNode(param);
1936 if ( !imagelist_node )
1937 return NULL;
1938
a3b9c43b
VZ
1939 wxXmlNode * const oldnode = m_handler->m_node;
1940 m_handler->m_node = imagelist_node;
326462ae 1941
fe97acf0
VZ
1942 // Get the size if we have it, otherwise we will use the size of the first
1943 // list element.
326462ae 1944 wxSize size = GetSize();
326462ae 1945
fe97acf0
VZ
1946 // Start adding images, we'll create the image list when adding the first
1947 // one.
1948 wxImageList * imagelist = NULL;
326462ae
VZ
1949 wxString parambitmap = wxT("bitmap");
1950 if ( HasParam(parambitmap) )
1951 {
a3b9c43b 1952 wxXmlNode *n = m_handler->m_node->GetChildren();
326462ae
VZ
1953 while (n)
1954 {
1955 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == parambitmap)
1956 {
0915bc4d 1957 wxIcon icon = GetIcon(n, wxART_OTHER, size);
fe97acf0
VZ
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.
5bac960d 1965 bool mask = GetBool(wxS("mask"), true);
fe97acf0
VZ
1966
1967 imagelist = new wxImageList(size.x, size.y, mask);
1968 }
1969
326462ae 1970 // add icon instead of bitmap to keep the bitmap mask
fe97acf0 1971 imagelist->Add(icon);
326462ae
VZ
1972 }
1973 n = n->GetNext();
1974 }
1975 }
1976
a3b9c43b 1977 m_handler->m_node = oldnode;
326462ae
VZ
1978 return imagelist;
1979}
1980
a3b9c43b 1981wxXmlNode *wxXmlResourceHandlerImpl::GetParamNode(const wxString& param)
78d14f80 1982{
a3b9c43b 1983 wxCHECK_MSG(m_handler->m_node, NULL, wxT("You can't access handler data before it was initialized!"));
2b5f62a0 1984
a3b9c43b 1985 wxXmlNode *n = m_handler->m_node->GetChildren();
78d14f80
VS
1986
1987 while (n)
1988 {
1989 if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
cffff062
VZ
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
78d14f80 1997 return n;
cffff062 1998 }
78d14f80
VS
1999 n = n->GetNext();
2000 }
2001 return NULL;
2002}
2003
a3b9c43b 2004bool wxXmlResourceHandlerImpl::IsOfClass(wxXmlNode *node, const wxString& classname) const
2d672c46 2005{
8ec22772 2006 return node->GetAttribute(wxT("class")) == classname;
2d672c46
MW
2007}
2008
2009
2010
a3b9c43b 2011wxString wxXmlResourceHandlerImpl::GetNodeContent(const wxXmlNode *node)
78d14f80 2012{
326462ae 2013 const wxXmlNode *n = node;
78d14f80
VS
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
a3b9c43b 2029wxString wxXmlResourceHandlerImpl::GetParamValue(const wxString& param)
78d14f80 2030{
e7a3a5a5 2031 if (param.empty())
a3b9c43b 2032 return GetNodeContent(m_handler->m_node);
78d14f80
VS
2033 else
2034 return GetNodeContent(GetParamNode(param));
2035}
2036
a3b9c43b 2037wxString wxXmlResourceHandlerImpl::GetParamValue(const wxXmlNode* node)
326462ae
VZ
2038{
2039 return GetNodeContent(node);
2040}
78d14f80
VS
2041
2042
a3b9c43b 2043wxSize wxXmlResourceHandlerImpl::GetSize(const wxString& param,
0c00c86f 2044 wxWindow *windowToUse)
78d14f80
VS
2045{
2046 wxString s = GetParamValue(param);
e7a3a5a5 2047 if (s.empty()) s = wxT("-1,-1");
78d14f80 2048 bool is_dlg;
d1f47235 2049 long sx, sy = 0;
78d14f80 2050
88a7a4e1 2051 is_dlg = s[s.length()-1] == wxT('d');
78d14f80
VS
2052 if (is_dlg) s.RemoveLast();
2053
2054 if (!s.BeforeFirst(wxT(',')).ToLong(&sx) ||
2055 !s.AfterLast(wxT(',')).ToLong(&sy))
2056 {
819559b2
VS
2057 ReportParamError
2058 (
2059 param,
2060 wxString::Format("cannot parse coordinates value \"%s\"", s)
2061 );
78d14f80
VS
2062 return wxDefaultSize;
2063 }
2064
2065 if (is_dlg)
2066 {
0c00c86f
VS
2067 if (windowToUse)
2068 {
2069 return wxDLG_UNIT(windowToUse, wxSize(sx, sy));
2070 }
a3b9c43b 2071 else if (m_handler->m_parentAsWindow)
0c00c86f 2072 {
a3b9c43b 2073 return wxDLG_UNIT(m_handler->m_parentAsWindow, wxSize(sx, sy));
0c00c86f 2074 }
78d14f80
VS
2075 else
2076 {
819559b2
VS
2077 ReportParamError
2078 (
2079 param,
2080 "cannot convert dialog units: dialog unknown"
2081 );
78d14f80
VS
2082 return wxDefaultSize;
2083 }
2084 }
8516a98b
DS
2085
2086 return wxSize(sx, sy);
78d14f80
VS
2087}
2088
2089
2090
a3b9c43b 2091wxPoint wxXmlResourceHandlerImpl::GetPosition(const wxString& param)
78d14f80
VS
2092{
2093 wxSize sz = GetSize(param);
2094 return wxPoint(sz.x, sz.y);
2095}
2096
2097
2098
a3b9c43b 2099wxCoord wxXmlResourceHandlerImpl::GetDimension(const wxString& param,
0c00c86f
VS
2100 wxCoord defaultv,
2101 wxWindow *windowToUse)
78d14f80
VS
2102{
2103 wxString s = GetParamValue(param);
e7a3a5a5 2104 if (s.empty()) return defaultv;
78d14f80
VS
2105 bool is_dlg;
2106 long sx;
2107
88a7a4e1 2108 is_dlg = s[s.length()-1] == wxT('d');
78d14f80
VS
2109 if (is_dlg) s.RemoveLast();
2110
2111 if (!s.ToLong(&sx))
2112 {
819559b2
VS
2113 ReportParamError
2114 (
2115 param,
2116 wxString::Format("cannot parse dimension value \"%s\"", s)
2117 );
78d14f80
VS
2118 return defaultv;
2119 }
2120
2121 if (is_dlg)
2122 {
0c00c86f
VS
2123 if (windowToUse)
2124 {
2125 return wxDLG_UNIT(windowToUse, wxSize(sx, 0)).x;
2126 }
a3b9c43b 2127 else if (m_handler->m_parentAsWindow)
0c00c86f 2128 {
a3b9c43b 2129 return wxDLG_UNIT(m_handler->m_parentAsWindow, wxSize(sx, 0)).x;
0c00c86f 2130 }
78d14f80
VS
2131 else
2132 {
819559b2
VS
2133 ReportParamError
2134 (
2135 param,
2136 "cannot convert dialog units: dialog unknown"
2137 );
78d14f80
VS
2138 return defaultv;
2139 }
2140 }
8516a98b
DS
2141
2142 return sx;
78d14f80
VS
2143}
2144
50c20291 2145wxDirection
a3b9c43b 2146wxXmlResourceHandlerImpl::GetDirection(const wxString& param, wxDirection dirDefault)
50c20291
VZ
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}
78d14f80 2179
1df61962
VS
2180// Get system font index using indexname
2181static wxFont GetSystemFont(const wxString& name)
2182{
2183 if (!name.empty())
2184 {
2185 #define SYSFNT(fnt) \
9a83f860 2186 if (name == wxT(#fnt)) return wxSystemSettings::GetFont(fnt);
1df61962
VS
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)
1df61962
VS
2192 SYSFNT(wxSYS_SYSTEM_FIXED_FONT)
2193 SYSFNT(wxSYS_DEFAULT_GUI_FONT)
2194 #undef SYSFNT
2195 }
2196
2197 return wxNullFont;
2198}
78d14f80 2199
a3b9c43b 2200wxFont wxXmlResourceHandlerImpl::GetFont(const wxString& param, wxWindow* parent)
78d14f80
VS
2201{
2202 wxXmlNode *font_node = GetParamNode(param);
2203 if (font_node == NULL)
2204 {
819559b2
VS
2205 ReportError(
2206 wxString::Format("cannot find font node \"%s\"", param));
78d14f80
VS
2207 return wxNullFont;
2208 }
2209
a3b9c43b
VZ
2210 wxXmlNode *oldnode = m_handler->m_node;
2211 m_handler->m_node = font_node;
78d14f80 2212
1df61962 2213 // font attributes:
78d14f80 2214
1df61962 2215 // size
94245f6d 2216 int isize = -1;
1df61962 2217 bool hasSize = HasParam(wxT("size"));
e7a3a5a5 2218 if (hasSize)
94245f6d 2219 isize = GetLong(wxT("size"), -1);
78d14f80 2220
1df61962
VS
2221 // style
2222 int istyle = wxNORMAL;
2223 bool hasStyle = HasParam(wxT("style"));
2224 if (hasStyle)
2225 {
2226 wxString style = GetParamValue(wxT("style"));
e7a3a5a5 2227 if (style == wxT("italic"))
1df61962 2228 istyle = wxITALIC;
e7a3a5a5 2229 else if (style == wxT("slant"))
1df61962 2230 istyle = wxSLANT;
9f5103f1
VZ
2231 else if (style != wxT("normal"))
2232 {
2233 ReportParamError
2234 (
2235 param,
2236 wxString::Format("unknown font style \"%s\"", style)
2237 );
2238 }
1df61962 2239 }
78d14f80 2240
1df61962
VS
2241 // weight
2242 int iweight = wxNORMAL;
2243 bool hasWeight = HasParam(wxT("weight"));
2244 if (hasWeight)
2245 {
2246 wxString weight = GetParamValue(wxT("weight"));
e7a3a5a5 2247 if (weight == wxT("bold"))
1df61962 2248 iweight = wxBOLD;
e7a3a5a5 2249 else if (weight == wxT("light"))
1df61962 2250 iweight = wxLIGHT;
9f5103f1
VZ
2251 else if (weight != wxT("normal"))
2252 {
2253 ReportParamError
2254 (
2255 param,
2256 wxString::Format("unknown font weight \"%s\"", weight)
2257 );
2258 }
1df61962 2259 }
e7a3a5a5 2260
1df61962
VS
2261 // underline
2262 bool hasUnderlined = HasParam(wxT("underlined"));
2263 bool underlined = hasUnderlined ? GetBool(wxT("underlined"), false) : false;
78d14f80 2264
1df61962
VS
2265 // family and facename
2266 int ifamily = wxDEFAULT;
2267 bool hasFamily = HasParam(wxT("family"));
2268 if (hasFamily)
78d14f80 2269 {
1df61962
VS
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;
9f5103f1
VZ
2277 else
2278 {
2279 ReportParamError
2280 (
2281 param,
2282 wxString::Format("unknown font family \"%s\"", family)
2283 );
2284 }
1df61962 2285 }
e7a3a5a5
WS
2286
2287
1df61962
VS
2288 wxString facename;
2289 bool hasFacename = HasParam(wxT("face"));
2290 if (hasFacename)
2291 {
2292 wxString faces = GetParamValue(wxT("face"));
1df61962 2293 wxStringTokenizer tk(faces, wxT(","));
63feebce
VS
2294#if wxUSE_FONTENUM
2295 wxArrayString facenames(wxFontEnumerator::GetFacenames());
1df61962 2296 while (tk.HasMoreTokens())
78d14f80 2297 {
6540132f 2298 int index = facenames.Index(tk.GetNextToken(), false);
1df61962
VS
2299 if (index != wxNOT_FOUND)
2300 {
6540132f 2301 facename = facenames[index];
1df61962
VS
2302 break;
2303 }
78d14f80 2304 }
63feebce
VS
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
78d14f80
VS
2310 }
2311
1df61962
VS
2312 // encoding
2313 wxFontEncoding enc = wxFONTENCODING_DEFAULT;
2314 bool hasEncoding = HasParam(wxT("encoding"));
dc2575ba 2315#if wxUSE_FONTMAP
1df61962
VS
2316 if (hasEncoding)
2317 {
2318 wxString encoding = GetParamValue(wxT("encoding"));
2319 wxFontMapper mapper;
e7a3a5a5 2320 if (!encoding.empty())
1df61962
VS
2321 enc = mapper.CharsetToEncoding(encoding);
2322 if (enc == wxFONTENCODING_SYSTEM)
2323 enc = wxFONTENCODING_DEFAULT;
2324 }
dc2575ba 2325#endif // wxUSE_FONTMAP
78d14f80 2326
45df4bb6
VZ
2327 wxFont font;
2328
1df61962 2329 // is this font based on a system font?
45df4bb6
VZ
2330 if (HasParam(wxT("sysfont")))
2331 {
2332 font = GetSystemFont(GetParamValue(wxT("sysfont")));
9f5103f1
VZ
2333 if (HasParam(wxT("inherit")))
2334 {
2335 ReportParamError
2336 (
2337 param,
2338 "double specification of \"sysfont\" and \"inherit\""
2339 );
2340 }
45df4bb6
VZ
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
9f5103f1
VZ
2348 {
2349 ReportParamError
2350 (
2351 param,
2352 "no parent window specified to derive the font from"
2353 );
2354 }
45df4bb6 2355 }
e7a3a5a5 2356
a1b806b9 2357 if (font.IsOk())
1df61962 2358 {
94245f6d 2359 if (hasSize && isize != -1)
9f5103f1 2360 {
94245f6d 2361 font.SetPointSize(isize);
9f5103f1
VZ
2362 if (HasParam(wxT("relativesize")))
2363 {
2364 ReportParamError
2365 (
2366 param,
2367 "double specification of \"size\" and \"relativesize\""
2368 );
2369 }
2370 }
1df61962 2371 else if (HasParam(wxT("relativesize")))
94245f6d 2372 font.SetPointSize(int(font.GetPointSize() *
1df61962 2373 GetFloat(wxT("relativesize"))));
e7a3a5a5 2374
1df61962 2375 if (hasStyle)
94245f6d 2376 font.SetStyle(istyle);
1df61962 2377 if (hasWeight)
94245f6d 2378 font.SetWeight(iweight);
1df61962 2379 if (hasUnderlined)
94245f6d 2380 font.SetUnderlined(underlined);
1df61962 2381 if (hasFamily)
94245f6d 2382 font.SetFamily(ifamily);
1df61962 2383 if (hasFacename)
94245f6d 2384 font.SetFaceName(facename);
1df61962 2385 if (hasEncoding)
94245f6d
VZ
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);
1df61962 2393 }
8516a98b 2394
a3b9c43b 2395 m_handler->m_node = oldnode;
94245f6d 2396 return font;
78d14f80
VS
2397}
2398
2399
a3b9c43b 2400void wxXmlResourceHandlerImpl::SetupWindow(wxWindow *wnd)
78d14f80
VS
2401{
2402 //FIXME : add cursor
2403
2404 if (HasParam(wxT("exstyle")))
0099f343
JS
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")));
78d14f80
VS
2409 if (HasParam(wxT("bg")))
2410 wnd->SetBackgroundColour(GetColour(wxT("bg")));
a7435c3e
VZ
2411 if (HasParam(wxT("ownbg")))
2412 wnd->SetOwnBackgroundColour(GetColour(wxT("ownbg")));
78d14f80
VS
2413 if (HasParam(wxT("fg")))
2414 wnd->SetForegroundColour(GetColour(wxT("fg")));
a7435c3e
VZ
2415 if (HasParam(wxT("ownfg")))
2416 wnd->SetOwnForegroundColour(GetColour(wxT("ownfg")));
78d14f80 2417 if (GetBool(wxT("enabled"), 1) == 0)
f80ea77b 2418 wnd->Enable(false);
78d14f80
VS
2419 if (GetBool(wxT("focused"), 0) == 1)
2420 wnd->SetFocus();
2421 if (GetBool(wxT("hidden"), 0) == 1)
f80ea77b 2422 wnd->Show(false);
78d14f80
VS
2423#if wxUSE_TOOLTIPS
2424 if (HasParam(wxT("tooltip")))
2425 wnd->SetToolTip(GetText(wxT("tooltip")));
2426#endif
2427 if (HasParam(wxT("font")))
45df4bb6 2428 wnd->SetFont(GetFont(wxT("font"), wnd));
a7435c3e 2429 if (HasParam(wxT("ownfont")))
45df4bb6 2430 wnd->SetOwnFont(GetFont(wxT("ownfont"), wnd));
b23030d6
JS
2431 if (HasParam(wxT("help")))
2432 wnd->SetHelpText(GetText(wxT("help")));
78d14f80
VS
2433}
2434
2435
a3b9c43b 2436void wxXmlResourceHandlerImpl::CreateChildren(wxObject *parent, bool this_hnd_only)
78d14f80 2437{
a3b9c43b 2438 for ( wxXmlNode *n = m_handler->m_node->GetChildren(); n; n = n->GetNext() )
78d14f80 2439 {
23239d94 2440 if ( IsObjectNode(n) )
78d14f80 2441 {
a3b9c43b
VZ
2442 m_handler->m_resource->DoCreateResFromNode(*n, parent, NULL,
2443 this_hnd_only ? this->GetHandler() : NULL);
2444 }
78d14f80
VS
2445 }
2446}
2447
2448
a3b9c43b 2449void wxXmlResourceHandlerImpl::CreateChildrenPrivately(wxObject *parent, wxXmlNode *rootnode)
78d14f80
VS
2450{
2451 wxXmlNode *root;
a3b9c43b 2452 if (rootnode == NULL) root = m_handler->m_node; else root = rootnode;
78d14f80
VS
2453 wxXmlNode *n = root->GetChildren();
2454
2455 while (n)
2456 {
a3b9c43b 2457 if (n->GetType() == wxXML_ELEMENT_NODE && GetHandler()->CanHandle(n))
78d14f80
VS
2458 {
2459 CreateResource(n, parent, NULL);
2460 }
2461 n = n->GetNext();
2462 }
2463}
2464
2465
819559b2
VS
2466//-----------------------------------------------------------------------------
2467// errors reporting
2468//-----------------------------------------------------------------------------
2469
a3b9c43b 2470void wxXmlResourceHandlerImpl::ReportError(const wxString& message)
819559b2 2471{
a3b9c43b 2472 m_handler->m_resource->ReportError(m_handler->m_node, message);
819559b2
VS
2473}
2474
a3b9c43b 2475void wxXmlResourceHandlerImpl::ReportError(wxXmlNode *context,
819559b2
VS
2476 const wxString& message)
2477{
a3b9c43b 2478 m_handler->m_resource->ReportError(context ? context : m_handler->m_node, message);
819559b2
VS
2479}
2480
a3b9c43b 2481void wxXmlResourceHandlerImpl::ReportParamError(const wxString& param,
819559b2
VS
2482 const wxString& message)
2483{
a3b9c43b 2484 m_handler->m_resource->ReportError(GetParamNode(param), message);
819559b2
VS
2485}
2486
1f6ea935 2487void wxXmlResource::ReportError(const wxXmlNode *context, const wxString& message)
819559b2
VS
2488{
2489 if ( !context )
2490 {
2491 DoReportError("", NULL, message);
2492 return;
2493 }
78d14f80 2494
819559b2
VS
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());
78d14f80 2499
819559b2
VS
2500 DoReportError(filename, context, message);
2501}
78d14f80 2502
1f6ea935 2503void wxXmlResource::DoReportError(const wxString& xrcFile, const wxXmlNode *position,
819559b2
VS
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}
78d14f80
VS
2518
2519
819559b2
VS
2520//-----------------------------------------------------------------------------
2521// XRCID implementation
2522//-----------------------------------------------------------------------------
78d14f80 2523
5ed345b7 2524#define XRCID_TABLE_SIZE 1024
78d14f80
VS
2525
2526
5ed345b7 2527struct XRCID_record
78d14f80 2528{
cf2810aa
VZ
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;
c560da98 2533 char *key;
5ed345b7 2534 XRCID_record *next;
78d14f80
VS
2535};
2536
5ed345b7 2537static XRCID_record *XRCID_Records[XRCID_TABLE_SIZE] = {NULL};
78d14f80 2538
d807030e
VZ
2539// Extremely simplistic hash function which probably ought to be replaced with
2540// wxStringHash::stringHash().
2541static inline unsigned XRCIdHash(const char *str_id)
78d14f80 2542{
d807030e 2543 unsigned index = 0;
78d14f80 2544
e03951b7 2545 for (const char *c = str_id; *c != '\0'; c++) index += (unsigned int)*c;
5ed345b7 2546 index %= XRCID_TABLE_SIZE;
78d14f80 2547
d807030e
VZ
2548 return index;
2549}
2550
3cf2fe54 2551static void XRCID_Assign(const wxString& str_id, int value)
d265ec6b 2552{
d7a46c4e 2553 const wxCharBuffer buf_id(str_id.mb_str());
3cf2fe54 2554 const unsigned index = XRCIdHash(buf_id);
d265ec6b
VZ
2555
2556
2557 XRCID_record *oldrec = NULL;
2558 for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
2559 {
3cf2fe54 2560 if (wxStrcmp(rec->key, buf_id) == 0)
d265ec6b
VZ
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
d807030e
VZ
2576static int XRCID_Lookup(const char *str_id, int value_if_not_found = wxID_NONE)
2577{
2578 const unsigned index = XRCIdHash(str_id);
2579
2580
5ed345b7 2581 XRCID_record *oldrec = NULL;
5ed345b7 2582 for (XRCID_record *rec = XRCID_Records[index]; rec; rec = rec->next)
78d14f80 2583 {
00393283 2584 if (wxStrcmp(rec->key, str_id) == 0)
78d14f80
VS
2585 {
2586 return rec->id;
2587 }
78d14f80
VS
2588 oldrec = rec;
2589 }
2590
5ed345b7
VS
2591 XRCID_record **rec_var = (oldrec == NULL) ?
2592 &XRCID_Records[index] : &oldrec->next;
2593 *rec_var = new XRCID_record;
00393283 2594 (*rec_var)->key = wxStrdup(str_id);
78d14f80
VS
2595 (*rec_var)->next = NULL;
2596
c560da98 2597 char *end;
9b2a7469 2598 if (value_if_not_found != wxID_NONE)
13de23f6 2599 (*rec_var)->id = value_if_not_found;
85452d74
VS
2600 else
2601 {
13de23f6
VS
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 {
f35fdf7e 2610 (*rec_var)->id = wxWindowBase::NewControlId();
13de23f6 2611 }
85452d74
VS
2612 }
2613
78d14f80
VS
2614 return (*rec_var)->id;
2615}
2616
a58804ea 2617namespace
78d14f80 2618{
f35fdf7e 2619
a58804ea
VZ
2620// flag indicating whether standard XRC ids were already initialized
2621static bool gs_stdIDsAdded = false;
78d14f80 2622
a58804ea 2623void AddStdXRCID_Records()
13de23f6 2624{
c560da98 2625#define stdID(id) XRCID_Lookup(#id, id)
13de23f6 2626 stdID(-1);
c369d4f1
VS
2627
2628 stdID(wxID_ANY);
2629 stdID(wxID_SEPARATOR);
e7a3a5a5 2630
c369d4f1
VS
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);
e63f19ba 2643 stdID(wxID_PAGE_SETUP);
c369d4f1
VS
2644 stdID(wxID_PREVIEW);
2645 stdID(wxID_ABOUT);
2646 stdID(wxID_HELP_CONTENTS);
9a9c20be
VZ
2647 stdID(wxID_HELP_INDEX),
2648 stdID(wxID_HELP_SEARCH),
c369d4f1
VS
2649 stdID(wxID_HELP_COMMANDS);
2650 stdID(wxID_HELP_PROCEDURES);
2651 stdID(wxID_HELP_CONTEXT);
13de23f6 2652 stdID(wxID_CLOSE_ALL);
c369d4f1 2653 stdID(wxID_PREFERENCES);
9a9c20be 2654
d73195fd 2655 stdID(wxID_EDIT);
c369d4f1
VS
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);
9a9c20be 2667
c369d4f1
VS
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);
9a9c20be
VZ
2676
2677
c369d4f1
VS
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);
9a9c20be
VZ
2687
2688
c369d4f1
VS
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);
9a9c20be 2709
c369d4f1
VS
2710 stdID(wxID_UP);
2711 stdID(wxID_DOWN);
2712 stdID(wxID_HOME);
2713 stdID(wxID_REFRESH);
2714 stdID(wxID_STOP);
2715 stdID(wxID_INDEX);
9a9c20be 2716
c369d4f1
VS
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);
6b1eedc1
VZ
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);
c369d4f1 2750
9a9c20be
VZ
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);
13de23f6
VS
2768#undef stdID
2769}
78d14f80 2770
a58804ea 2771} // anonymous namespace
78d14f80
VS
2772
2773
a58804ea
VZ
2774/*static*/
2775int 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 */
2787wxString 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
2801static 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
2812static 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}
78d14f80
VS
2822
2823
819559b2
VS
2824//-----------------------------------------------------------------------------
2825// module and globals
2826//-----------------------------------------------------------------------------
78d14f80 2827
fd230129
VZ
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
2834static struct wxXRCStaticCleanup
2835{
2836 ~wxXRCStaticCleanup() { CleanXRCID_Records(); }
2837} s_staticCleanup;
2838
78d14f80
VS
2839class wxXmlResourceModule: public wxModule
2840{
2841DECLARE_DYNAMIC_CLASS(wxXmlResourceModule)
2842public:
2843 wxXmlResourceModule() {}
824e8eaa
VS
2844 bool OnInit()
2845 {
2b5f62a0 2846 wxXmlResource::AddSubclassFactory(new wxXmlSubclassFactoryCXX);
f80ea77b 2847 return true;
824e8eaa 2848 }
78d14f80
VS
2849 void OnExit()
2850 {
1542c42e 2851 delete wxXmlResource::Set(NULL);
0526c8cc 2852 delete wxIdRangeManager::Set(NULL);
461932ae 2853 if(wxXmlResource::ms_subclassFactories)
eb2d0d23
VS
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 }
5ed345b7 2862 CleanXRCID_Records();
78d14f80
VS
2863 }
2864};
2865
2866IMPLEMENT_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.
2871void wxXmlInitResourceModule()
2872{
2873 wxModule* module = new wxXmlResourceModule;
78d14f80 2874 wxModule::RegisterModule(module);
58d1949f 2875 wxModule::InitializeModules();
78d14f80 2876}
a1e4ec87
VS
2877
2878#endif // wxUSE_XRC