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