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