]> git.saurik.com Git - wxWidgets.git/blob - src/msw/regconf.cpp
derive wxConfig classes from wxObject and add wxRTTI macros to them (patch 1587607)
[wxWidgets.git] / src / msw / regconf.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/regconf.cpp
3 // Purpose:
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 27.04.98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_CONFIG
20
21 #include "wx/config.h"
22
23 #ifndef WX_PRECOMP
24 #include "wx/string.h"
25 #include "wx/intl.h"
26 #include "wx/log.h"
27 #include "wx/event.h"
28 #include "wx/app.h"
29 #endif //WX_PRECOMP
30
31 #include "wx/msw/registry.h"
32 #include "wx/msw/regconf.h"
33
34 // ----------------------------------------------------------------------------
35 // constants
36 // ----------------------------------------------------------------------------
37
38 // we put our data in HKLM\SOFTWARE_KEY\appname
39 #define SOFTWARE_KEY wxString(wxT("Software\\"))
40
41 // ----------------------------------------------------------------------------
42 // global functions
43 // ----------------------------------------------------------------------------
44
45 // get the value if the key is opened and it exists
46 bool TryGetValue(const wxRegKey& key, const wxString& str, wxString& strVal)
47 {
48 return key.IsOpened() && key.HasValue(str) && key.QueryValue(str, strVal);
49 }
50
51 bool TryGetValue(const wxRegKey& key, const wxString& str, long *plVal)
52 {
53 return key.IsOpened() && key.HasValue(str) && key.QueryValue(str, plVal);
54 }
55
56 // ============================================================================
57 // implementation
58 // ============================================================================
59
60 // ----------------------------------------------------------------------------
61 // ctor/dtor
62 // ----------------------------------------------------------------------------
63 IMPLEMENT_ABSTRACT_CLASS(wxRegConfig, wxConfigBase)
64
65 // create the config object which stores its data under HKCU\vendor\app and, if
66 // style & wxCONFIG_USE_GLOBAL_FILE, under HKLM\vendor\app
67 wxRegConfig::wxRegConfig(const wxString& appName, const wxString& vendorName,
68 const wxString& strLocal, const wxString& strGlobal,
69 long style)
70 : wxConfigBase(appName, vendorName, strLocal, strGlobal, style)
71 {
72 wxString strRoot;
73
74 bool bDoUseGlobal = (style & wxCONFIG_USE_GLOBAL_FILE) != 0;
75
76 // the convention is to put the programs keys under <vendor>\<appname>
77 // (but it can be overriden by specifying the pathes explicitly in strLocal
78 // and/or strGlobal)
79 if ( strLocal.empty() || (strGlobal.empty() && bDoUseGlobal) )
80 {
81 if ( vendorName.empty() )
82 {
83 if ( wxTheApp )
84 strRoot = wxTheApp->GetVendorName();
85 }
86 else
87 {
88 strRoot = vendorName;
89 }
90
91 // no '\\' needed if no vendor name
92 if ( !strRoot.empty() )
93 {
94 strRoot += '\\';
95 }
96
97 if ( appName.empty() )
98 {
99 wxCHECK_RET( wxTheApp, wxT("No application name in wxRegConfig ctor!") );
100 strRoot << wxTheApp->GetAppName();
101 }
102 else
103 {
104 strRoot << appName;
105 }
106 }
107 //else: we don't need to do all the complicated stuff above
108
109 wxString str = strLocal.empty() ? strRoot : strLocal;
110
111 // as we're going to change the name of these keys fairly often and as
112 // there are only few of wxRegConfig objects (usually 1), we can allow
113 // ourselves to be generous and spend some memory to significantly improve
114 // performance of SetPath()
115 static const size_t MEMORY_PREALLOC = 512;
116
117 m_keyLocalRoot.ReserveMemoryForName(MEMORY_PREALLOC);
118 m_keyLocal.ReserveMemoryForName(MEMORY_PREALLOC);
119
120 m_keyLocalRoot.SetName(wxRegKey::HKCU, SOFTWARE_KEY + str);
121 m_keyLocal.SetName(m_keyLocalRoot, wxEmptyString);
122
123 if ( bDoUseGlobal )
124 {
125 str = strGlobal.empty() ? strRoot : strGlobal;
126
127 m_keyGlobalRoot.ReserveMemoryForName(MEMORY_PREALLOC);
128 m_keyGlobal.ReserveMemoryForName(MEMORY_PREALLOC);
129
130 m_keyGlobalRoot.SetName(wxRegKey::HKLM, SOFTWARE_KEY + str);
131 m_keyGlobal.SetName(m_keyGlobalRoot, wxEmptyString);
132 }
133
134 // Create() will Open() if key already exists
135 m_keyLocalRoot.Create();
136
137 // as it's the same key, Open() shouldn't fail (i.e. no need for Create())
138 m_keyLocal.Open();
139
140 // OTOH, this key may perfectly not exist, so suppress error messages the call
141 // to Open() might generate
142 if ( bDoUseGlobal )
143 {
144 wxLogNull nolog;
145 m_keyGlobalRoot.Open(wxRegKey::Read);
146 m_keyGlobal.Open(wxRegKey::Read);
147 }
148 }
149
150 // ----------------------------------------------------------------------------
151 // path management
152 // ----------------------------------------------------------------------------
153
154 // this function is called a *lot* of times (as I learned after seeing from
155 // profiler output that it is called ~12000 times from Mahogany start up code!)
156 // so it is important to optimize it - in particular, avoid using generic
157 // string functions here and do everything manually because it is faster
158 //
159 // I still kept the old version to be able to check that the optimized code has
160 // the same output as the non optimized version.
161 void wxRegConfig::SetPath(const wxString& strPath)
162 {
163 // remember the old path
164 wxString strOldPath = m_strPath;
165
166 #ifdef WX_DEBUG_SET_PATH // non optimized version kept here for testing
167 wxString m_strPathAlt;
168
169 {
170 wxArrayString aParts;
171
172 // because GetPath() returns "" when we're at root, we must understand
173 // empty string as "/"
174 if ( strPath.empty() || (strPath[0] == wxCONFIG_PATH_SEPARATOR) ) {
175 // absolute path
176 wxSplitPath(aParts, strPath);
177 }
178 else {
179 // relative path, combine with current one
180 wxString strFullPath = GetPath();
181 strFullPath << wxCONFIG_PATH_SEPARATOR << strPath;
182 wxSplitPath(aParts, strFullPath);
183 }
184
185 // recombine path parts in one variable
186 wxString strRegPath;
187 m_strPathAlt.Empty();
188 for ( size_t n = 0; n < aParts.Count(); n++ ) {
189 strRegPath << '\\' << aParts[n];
190 m_strPathAlt << wxCONFIG_PATH_SEPARATOR << aParts[n];
191 }
192 }
193 #endif // 0
194
195 // check for the most common case first
196 if ( strPath.empty() )
197 {
198 m_strPath = wxCONFIG_PATH_SEPARATOR;
199 }
200 else // not root
201 {
202 // construct the full path
203 wxString strFullPath;
204 if ( strPath[0u] == wxCONFIG_PATH_SEPARATOR )
205 {
206 // absolute path
207 strFullPath = strPath;
208 }
209 else // relative path
210 {
211 strFullPath.reserve(2*m_strPath.length());
212
213 strFullPath << m_strPath;
214 if ( strFullPath.Len() == 0 ||
215 strFullPath.Last() != wxCONFIG_PATH_SEPARATOR )
216 strFullPath << wxCONFIG_PATH_SEPARATOR;
217 strFullPath << strPath;
218 }
219
220 // simplify it: we need to handle ".." here
221
222 // count the total number of slashes we have to know if we can go upper
223 size_t totalSlashes = 0;
224
225 // position of the last slash to be able to backtrack to it quickly if
226 // needed, but we set it to -1 if we don't have a valid position
227 //
228 // we only remember the last position which means that we handle ".."
229 // quite efficiently but not "../.." - however the latter should be
230 // much more rare, so it is probably ok
231 int posLastSlash = -1;
232
233 const wxChar *src = strFullPath.c_str();
234 size_t len = strFullPath.length();
235 const wxChar *end = src + len;
236
237 wxStringBufferLength buf(m_strPath, len);
238 wxChar *dst = buf;
239 wxChar *start = dst;
240
241 for ( ; src < end; src++, dst++ )
242 {
243 if ( *src == wxCONFIG_PATH_SEPARATOR )
244 {
245 // check for "/.."
246
247 // note that we don't have to check for src < end here as
248 // *end == 0 so can't be '.'
249 if ( src[1] == _T('.') && src[2] == _T('.') &&
250 (src + 3 == end || src[3] == wxCONFIG_PATH_SEPARATOR) )
251 {
252 if ( !totalSlashes )
253 {
254 wxLogWarning(_("'%s' has extra '..', ignored."),
255 strFullPath.c_str());
256 }
257 else // return to the previous path component
258 {
259 // do we already have its position?
260 if ( posLastSlash == -1 )
261 {
262 // no, find it: note that we are sure to have one
263 // because totalSlashes > 0 so we don't have to
264 // check the boundary condition below
265
266 // this is more efficient than strrchr()
267 dst--;
268 while ( *dst != wxCONFIG_PATH_SEPARATOR )
269 {
270 dst--;
271 }
272 }
273 else // the position of last slash was stored
274 {
275 // go directly there
276 dst = start + posLastSlash;
277
278 // invalidate posLastSlash
279 posLastSlash = -1;
280 }
281
282 // we must have found a slash one way or another!
283 wxASSERT_MSG( *dst == wxCONFIG_PATH_SEPARATOR,
284 _T("error in wxRegConfig::SetPath") );
285
286 // stay at the same position
287 dst--;
288
289 // we killed one
290 totalSlashes--;
291 }
292
293 // skip both dots
294 src += 2;
295 }
296 else // not "/.."
297 {
298 if ( (dst == start) || (dst[-1] != wxCONFIG_PATH_SEPARATOR) )
299 {
300 *dst = wxCONFIG_PATH_SEPARATOR;
301
302 posLastSlash = dst - start;
303
304 totalSlashes++;
305 }
306 else // previous char was a slash too
307 {
308 // squeeze several subsequent slashes into one: i.e.
309 // just ignore this one
310 dst--;
311 }
312 }
313 }
314 else // normal character
315 {
316 // just copy
317 *dst = *src;
318 }
319 }
320
321 // NUL terminate the string
322 if ( dst[-1] == wxCONFIG_PATH_SEPARATOR && (dst != start + 1) )
323 {
324 // if it has a trailing slash we remove it unless it is the only
325 // string character
326 dst--;
327 }
328
329 *dst = _T('\0');
330 buf.SetLength(dst - start);
331 }
332
333 #ifdef WX_DEBUG_SET_PATH
334 wxASSERT( m_strPath == m_strPathAlt );
335 #endif
336
337 if ( m_strPath == strOldPath )
338 return;
339
340 // registry APIs want backslashes instead of slashes
341 wxString strRegPath;
342 if ( !m_strPath.empty() )
343 {
344 size_t len = m_strPath.length();
345
346 const wxChar *src = m_strPath.c_str();
347 wxStringBufferLength buf(strRegPath, len);
348 wxChar *dst = buf;
349
350 const wxChar *end = src + len;
351 for ( ; src < end; src++, dst++ )
352 {
353 if ( *src == wxCONFIG_PATH_SEPARATOR )
354 *dst = _T('\\');
355 else
356 *dst = *src;
357 }
358
359 buf.SetLength(len);
360 }
361
362 // this is not needed any longer as we don't create keys unnecessarily any
363 // more (now it is done on demand, i.e. only when they're going to contain
364 // something)
365 #if 0
366 // as we create the registry key when SetPath(key) is done, we can be left
367 // with plenty of empty keys if this was only done to try to read some
368 // value which, in fact, doesn't exist - to prevent this from happening we
369 // automatically delete the old key if it was empty
370 if ( m_keyLocal.Exists() && LocalKey().IsEmpty() )
371 {
372 m_keyLocal.DeleteSelf();
373 }
374 #endif // 0
375
376 // change current key(s)
377 m_keyLocal.SetName(m_keyLocalRoot, strRegPath);
378
379 if ( GetStyle() & wxCONFIG_USE_GLOBAL_FILE )
380 {
381 m_keyGlobal.SetName(m_keyGlobalRoot, strRegPath);
382
383 wxLogNull nolog;
384 m_keyGlobal.Open(wxRegKey::Read);
385 }
386 }
387
388 // ----------------------------------------------------------------------------
389 // enumeration (works only with current group)
390 // ----------------------------------------------------------------------------
391
392 /*
393 We want to enumerate all local keys/values after the global ones, but, of
394 course, we don't want to repeat a key which appears locally as well as
395 globally twice.
396
397 We use the 15th bit of lIndex for distinction between global and local.
398 */
399
400 #define LOCAL_MASK 0x8000
401 #define IS_LOCAL_INDEX(l) (((l) & LOCAL_MASK) != 0)
402
403 bool wxRegConfig::GetFirstGroup(wxString& str, long& lIndex) const
404 {
405 lIndex = 0;
406 return GetNextGroup(str, lIndex);
407 }
408
409 bool wxRegConfig::GetNextGroup(wxString& str, long& lIndex) const
410 {
411 // are we already enumerating local entries?
412 if ( m_keyGlobal.IsOpened() && !IS_LOCAL_INDEX(lIndex) ) {
413 // try to find a global entry which doesn't appear locally
414 while ( m_keyGlobal.GetNextKey(str, lIndex) ) {
415 if ( !m_keyLocal.Exists() || !LocalKey().HasSubKey(str) ) {
416 // ok, found one - return it
417 return true;
418 }
419 }
420
421 // no more global entries
422 lIndex |= LOCAL_MASK;
423 }
424
425 // if we don't have the key at all, don't try to enumerate anything under it
426 if ( !m_keyLocal.Exists() )
427 return false;
428
429 // much easier with local entries: get the next one we find
430 // (don't forget to clear our flag bit and set it again later)
431 lIndex &= ~LOCAL_MASK;
432 bool bOk = LocalKey().GetNextKey(str, lIndex);
433 lIndex |= LOCAL_MASK;
434
435 return bOk;
436 }
437
438 bool wxRegConfig::GetFirstEntry(wxString& str, long& lIndex) const
439 {
440 lIndex = 0;
441 return GetNextEntry(str, lIndex);
442 }
443
444 bool wxRegConfig::GetNextEntry(wxString& str, long& lIndex) const
445 {
446 // are we already enumerating local entries?
447 if ( m_keyGlobal.IsOpened() && !IS_LOCAL_INDEX(lIndex) ) {
448 // try to find a global entry which doesn't appear locally
449 while ( m_keyGlobal.GetNextValue(str, lIndex) ) {
450 if ( !m_keyLocal.Exists() || !LocalKey().HasValue(str) ) {
451 // ok, found one - return it
452 return true;
453 }
454 }
455
456 // no more global entries
457 lIndex |= LOCAL_MASK;
458 }
459
460 // if we don't have the key at all, don't try to enumerate anything under it
461 if ( !m_keyLocal.Exists() )
462 return false;
463
464 // much easier with local entries: get the next one we find
465 // (don't forget to clear our flag bit and set it again later)
466 lIndex &= ~LOCAL_MASK;
467 bool bOk = LocalKey().GetNextValue(str, lIndex);
468 lIndex |= LOCAL_MASK;
469
470 return bOk;
471 }
472
473 size_t wxRegConfig::GetNumberOfEntries(bool WXUNUSED(bRecursive)) const
474 {
475 size_t nEntries = 0;
476
477 // dummy vars
478 wxString str;
479 long l;
480 bool bCont = ((wxRegConfig*)this)->GetFirstEntry(str, l);
481 while ( bCont ) {
482 nEntries++;
483
484 bCont = ((wxRegConfig*)this)->GetNextEntry(str, l);
485 }
486
487 return nEntries;
488 }
489
490 size_t wxRegConfig::GetNumberOfGroups(bool WXUNUSED(bRecursive)) const
491 {
492 size_t nGroups = 0;
493
494 // dummy vars
495 wxString str;
496 long l;
497 bool bCont = ((wxRegConfig*)this)->GetFirstGroup(str, l);
498 while ( bCont ) {
499 nGroups++;
500
501 bCont = ((wxRegConfig*)this)->GetNextGroup(str, l);
502 }
503
504 return nGroups;
505 }
506
507 // ----------------------------------------------------------------------------
508 // tests for existence
509 // ----------------------------------------------------------------------------
510
511 bool wxRegConfig::HasGroup(const wxString& key) const
512 {
513 wxConfigPathChanger path(this, key);
514
515 wxString strName(path.Name());
516
517 return (m_keyLocal.Exists() && LocalKey().HasSubKey(strName)) ||
518 m_keyGlobal.HasSubKey(strName);
519 }
520
521 bool wxRegConfig::HasEntry(const wxString& key) const
522 {
523 wxConfigPathChanger path(this, key);
524
525 wxString strName(path.Name());
526
527 return (m_keyLocal.Exists() && LocalKey().HasValue(strName)) ||
528 m_keyGlobal.HasValue(strName);
529 }
530
531 wxConfigBase::EntryType wxRegConfig::GetEntryType(const wxString& key) const
532 {
533 wxConfigPathChanger path(this, key);
534
535 wxString strName(path.Name());
536
537 bool isNumeric;
538 if ( m_keyLocal.Exists() && LocalKey().HasValue(strName) )
539 isNumeric = m_keyLocal.IsNumericValue(strName);
540 else if ( m_keyGlobal.HasValue(strName) )
541 isNumeric = m_keyGlobal.IsNumericValue(strName);
542 else
543 return wxConfigBase::Type_Unknown;
544
545 return isNumeric ? wxConfigBase::Type_Integer : wxConfigBase::Type_String;
546 }
547
548 // ----------------------------------------------------------------------------
549 // reading/writing
550 // ----------------------------------------------------------------------------
551
552 bool wxRegConfig::DoReadString(const wxString& key, wxString *pStr) const
553 {
554 wxCHECK_MSG( pStr, false, _T("wxRegConfig::Read(): NULL param") );
555
556 wxConfigPathChanger path(this, key);
557
558 bool bQueryGlobal = true;
559
560 // if immutable key exists in global key we must check that it's not
561 // overriden by the local key with the same name
562 if ( IsImmutable(path.Name()) ) {
563 if ( TryGetValue(m_keyGlobal, path.Name(), *pStr) ) {
564 if ( m_keyLocal.Exists() && LocalKey().HasValue(path.Name()) ) {
565 wxLogWarning(wxT("User value for immutable key '%s' ignored."),
566 path.Name().c_str());
567 }
568
569 return true;
570 }
571 else {
572 // don't waste time - it's not there anyhow
573 bQueryGlobal = false;
574 }
575 }
576
577 // first try local key
578 if ( (m_keyLocal.Exists() && TryGetValue(LocalKey(), path.Name(), *pStr)) ||
579 (bQueryGlobal && TryGetValue(m_keyGlobal, path.Name(), *pStr)) ) {
580 return true;
581 }
582
583 return false;
584 }
585
586 // this exactly reproduces the string version above except for ExpandEnvVars(),
587 // we really should avoid this code duplication somehow...
588
589 bool wxRegConfig::DoReadLong(const wxString& key, long *plResult) const
590 {
591 wxCHECK_MSG( plResult, false, _T("wxRegConfig::Read(): NULL param") );
592
593 wxConfigPathChanger path(this, key);
594
595 bool bQueryGlobal = true;
596
597 // if immutable key exists in global key we must check that it's not
598 // overriden by the local key with the same name
599 if ( IsImmutable(path.Name()) ) {
600 if ( TryGetValue(m_keyGlobal, path.Name(), plResult) ) {
601 if ( m_keyLocal.Exists() && LocalKey().HasValue(path.Name()) ) {
602 wxLogWarning(wxT("User value for immutable key '%s' ignored."),
603 path.Name().c_str());
604 }
605
606 return true;
607 }
608 else {
609 // don't waste time - it's not there anyhow
610 bQueryGlobal = false;
611 }
612 }
613
614 // first try local key
615 if ( (m_keyLocal.Exists() && TryGetValue(LocalKey(), path.Name(), plResult)) ||
616 (bQueryGlobal && TryGetValue(m_keyGlobal, path.Name(), plResult)) ) {
617 return true;
618 }
619
620 return false;
621 }
622
623 bool wxRegConfig::DoWriteString(const wxString& key, const wxString& szValue)
624 {
625 wxConfigPathChanger path(this, key);
626
627 if ( IsImmutable(path.Name()) ) {
628 wxLogError(wxT("Can't change immutable entry '%s'."), path.Name().c_str());
629 return false;
630 }
631
632 return LocalKey().SetValue(path.Name(), szValue);
633 }
634
635 bool wxRegConfig::DoWriteLong(const wxString& key, long lValue)
636 {
637 wxConfigPathChanger path(this, key);
638
639 if ( IsImmutable(path.Name()) ) {
640 wxLogError(wxT("Can't change immutable entry '%s'."), path.Name().c_str());
641 return false;
642 }
643
644 return LocalKey().SetValue(path.Name(), lValue);
645 }
646
647 // ----------------------------------------------------------------------------
648 // renaming
649 // ----------------------------------------------------------------------------
650
651 bool wxRegConfig::RenameEntry(const wxString& oldName, const wxString& newName)
652 {
653 // check that the old entry exists...
654 if ( !HasEntry(oldName) )
655 return false;
656
657 // and that the new one doesn't
658 if ( HasEntry(newName) )
659 return false;
660
661 return m_keyLocal.RenameValue(oldName, newName);
662 }
663
664 bool wxRegConfig::RenameGroup(const wxString& oldName, const wxString& newName)
665 {
666 // check that the old group exists...
667 if ( !HasGroup(oldName) )
668 return false;
669
670 // and that the new one doesn't
671 if ( HasGroup(newName) )
672 return false;
673
674 return wxRegKey(m_keyLocal, oldName).Rename(newName);
675 }
676
677 // ----------------------------------------------------------------------------
678 // deleting
679 // ----------------------------------------------------------------------------
680
681 bool wxRegConfig::DeleteEntry(const wxString& value, bool bGroupIfEmptyAlso)
682 {
683 wxConfigPathChanger path(this, value);
684
685 if ( m_keyLocal.Exists() ) {
686 if ( !m_keyLocal.DeleteValue(path.Name()) )
687 return false;
688
689 if ( bGroupIfEmptyAlso && m_keyLocal.IsEmpty() ) {
690 wxString strKey = GetPath().AfterLast(wxCONFIG_PATH_SEPARATOR);
691 SetPath(_T("..")); // changes m_keyLocal
692 return LocalKey().DeleteKey(strKey);
693 }
694 }
695
696 return true;
697 }
698
699 bool wxRegConfig::DeleteGroup(const wxString& key)
700 {
701 wxConfigPathChanger path(this, RemoveTrailingSeparator(key));
702
703 if ( !m_keyLocal.Exists() )
704 {
705 // nothing to do
706 return true;
707 }
708
709 if ( !LocalKey().DeleteKey(path.Name()) )
710 return false;
711
712 path.UpdateIfDeleted();
713
714 return true;
715 }
716
717 bool wxRegConfig::DeleteAll()
718 {
719 m_keyLocal.Close();
720 m_keyGlobal.Close();
721
722 bool bOk = m_keyLocalRoot.DeleteSelf();
723
724 // make sure that we opened m_keyGlobalRoot and so it has a reasonable name:
725 // otherwise we will delete HKEY_CLASSES_ROOT recursively
726 if ( bOk && m_keyGlobalRoot.IsOpened() )
727 bOk = m_keyGlobalRoot.DeleteSelf();
728
729 return bOk;
730 }
731
732 #endif // wxUSE_CONFIG