source: trunk/eraser/Eraser/Settings.cs @ 2534

Revision 2534, 17.4 KB checked in by lowjoel, 3 years ago (diff)

Removed debug code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Rev URL
Line 
1/*
2 * $Id$
3 * Copyright 2008-2012 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Windows.Forms;
25using System.Text;
26using System.Linq;
27
28using System.IO;
29using System.Globalization;
30using System.Reflection;
31using System.Runtime.InteropServices;
32using System.Runtime.Serialization;
33using System.Runtime.Serialization.Formatters.Binary;
34using Microsoft.Win32;
35
36using Eraser.Util;
37using Eraser.Plugins;
38
39namespace Eraser
40{
41    internal class Settings : PersistentStore
42    {
43        /// <summary>
44        /// Registry-based storage backing for the Settings class.
45        /// </summary>
46        private sealed class RegistrySettings : PersistentStore, IDisposable
47        {
48            /// <summary>
49            /// Constructor.
50            /// </summary>
51            /// <param name="key">The registry key to look for the settings in.</param>
52            public RegistrySettings(RegistryKey key)
53            {
54                if (key == null)
55                    throw new ArgumentNullException("key");
56
57                Key = key;
58            }
59
60            #region IDisposable Members
61
62            ~RegistrySettings()
63            {
64                Dispose(false);
65            }
66
67            public void Dispose()
68            {
69                Dispose(true);
70                GC.SuppressFinalize(this);
71            }
72
73            private void Dispose(bool disposing)
74            {
75                if (Key == null)
76                    return;
77
78                if (disposing)
79                    Key.Close();
80                Key = null;
81            }
82
83            #endregion
84
85            public override T GetValue<T>(string name, T defaultValue)
86            {
87                //Determine the type of T. If it is an IEnumerable or IDictionary, use our
88                //concrete types.
89                Type typeOfT = typeof(T);
90                if (typeOfT.IsInterface)
91                {
92                    //Is it a dictionary?
93                    if (typeOfT.Name == "IDictionary`2")
94                    {
95                        //This is a System.Collections.Generic.IDictionary
96                        Type[] keyValueType = typeOfT.GetGenericArguments();
97
98                        Type settingsDictionary = typeof(SettingsDictionary<,>);
99                        Type typeOfResult = settingsDictionary.MakeGenericType(keyValueType);
100
101                        ConstructorInfo ctor = typeOfResult.GetConstructor(new Type[] {
102                            typeof(PersistentStore), typeof(string) });
103                        return (T)ctor.Invoke(new object[] { this, name });
104                    }
105
106                    //Or an IEnumerable?
107                    else if (typeOfT.Name == "IEnumerable`1")
108                    {
109                        //This is a System.Collections.Generic.IEnumerable
110                        Type[] keyValueType = typeOfT.GetGenericArguments();
111                        return (T)GetList<T>(name, keyValueType[0]);
112                    }
113
114                    //Or an IList<T>, ICollection<T>
115                    else
116                    {
117                        foreach (Type type in typeOfT.GetInterfaces())
118                            if (type.IsGenericType)
119                            {
120                                if (type.GetInterfaces().Any(
121                                        x => x == typeof(System.Collections.IEnumerable)) &&
122                                    type.Name == "IEnumerable`1")
123                                {
124                                    //This is a System.Collections.Generic.IEnumerable
125                                    Type[] keyValueType = typeOfT.GetGenericArguments();
126                                    return (T)GetList<T>(name, keyValueType[0]);
127                                }
128                            }
129                    }
130                }
131
132                return (T)GetScalar(name, defaultValue);
133            }
134
135            private object GetScalar<T>(string name, T defaultValue)
136            {
137                //Get the raw registry value
138                object rawResult = Key.GetValue(name, null);
139                if (rawResult == null)
140                    return defaultValue;
141
142                //Check if it is a serialised object
143                byte[] resultArray = rawResult as byte[];
144                if (resultArray != null)
145                {
146                    using (MemoryStream stream = new MemoryStream(resultArray))
147                        try
148                        {
149                            BinaryFormatter formatter = new BinaryFormatter();
150                            if (typeof(T) != typeof(object))
151                                formatter.Binder = new TypeNameSerializationBinder(typeof(T));
152                            return (T)formatter.Deserialize(stream);
153                        }
154                        catch (InvalidCastException)
155                        {
156                            Key.DeleteValue(name);
157                            MessageBox.Show(S._("Could not load the setting {0}\\{1}. The " +
158                                "setting has been lost.", Key, name),
159                                S._("Eraser"), MessageBoxButtons.OK, MessageBoxIcon.Error,
160                                MessageBoxDefaultButton.Button1,
161                                Localisation.IsRightToLeft(null) ? MessageBoxOptions.RtlReading : 0);
162                        }
163                }
164                else if (typeof(T) == typeof(Guid))
165                {
166                    return new Guid((string)rawResult);
167                }
168                else if (typeof(T).GetInterfaces().Any(x => x == typeof(IConvertible)))
169                {
170                    return Convert.ChangeType(rawResult, typeof(T));
171                }
172                else
173                {
174                    return rawResult;
175                }
176
177                return defaultValue;
178            }
179
180            private object GetList<T>(string name, Type type)
181            {
182                //Make sure that type is either a string or the type can be converted from a
183                //string (via IConvertible)
184                if (type != typeof(string) && !type.GetInterfaces().Any(x => x == typeof(IConvertible)))
185                {
186                    return GetScalar<T>(name, default(T));
187                }
188               
189                Type settingsList = typeof(SettingsList<>);
190                Type typeOfResult = settingsList.MakeGenericType(type);
191
192                //Get the constructor.
193                Type typeOfTArray = typeof(ICollection<>).MakeGenericType(type);
194                ConstructorInfo ctor = typeOfResult.GetConstructor(new Type[] {
195                    typeof(PersistentStore), typeof(string), typeOfTArray });
196
197                //Get the values currently in the registry
198                string[] values = (string[])GetScalar<string[]>(name, null);
199
200                //Convert the values from a string array to the type expected
201                object array = null;
202                if (type == typeof(string))
203                {
204                    array = values;
205                }
206                else
207                {
208                    array = typeof(List<>).MakeGenericType(type).GetConstructor(new Type[0]).
209                        Invoke(new object[0]);
210                    foreach (string item in values)
211                    {
212                        ((System.Collections.IList)array).Add(Convert.ChangeType(item, type));
213                    }
214                }
215               
216                return ctor.Invoke(new object[] { this, name, array });
217            }
218
219            public override void SetValue(string name, object value)
220            {
221                if (value == null)
222                {
223                    Key.DeleteValue(name);
224                }
225                else
226                {
227                    //Determine the type of T. If it is an IEnumerable, store it as a string array
228                    Type typeOfT = value.GetType();
229                    foreach (Type type in typeOfT.GetInterfaces())
230                        if (type.IsGenericType &&
231                            type.Name == "IEnumerable`1" &&
232                            type.GetInterfaces().Any(
233                                x => x == typeof(System.Collections.IEnumerable)))
234                        {
235                            //Check that we know how to convert the item type
236                            Type itemType = type.GetGenericArguments()[0];
237                            string[] registryValue = null;
238                            if (typeOfT == typeof(string[]))
239                            {
240                                registryValue = (string[])value;
241                            }
242                            else if (itemType == typeof(string))
243                            {
244                                registryValue = new List<string>((IEnumerable<string>)value).
245                                    ToArray();
246                            }
247                            else if (itemType.GetInterfaces().Any(x => x == typeof(IConvertible)))
248                            {
249                                List<string> collection = new List<string>();
250                                foreach (object item in (System.Collections.IEnumerable)value)
251                                    collection.Add((string)
252                                        Convert.ChangeType(item, typeof(string)));
253
254                                registryValue = collection.ToArray();
255                            }
256
257                            if (registryValue != null)
258                            {
259                                Key.SetValue(name, registryValue, RegistryValueKind.MultiString);
260                                return;
261                            }
262                        }
263
264                    if (value is bool)
265                        Key.SetValue(name, value, RegistryValueKind.DWord);
266                    else if ((value is int) || (value is uint))
267                        Key.SetValue(name, value, RegistryValueKind.DWord);
268                    else if ((value is long) || (value is ulong))
269                        Key.SetValue(name, value, RegistryValueKind.QWord);
270                    else if ((value is string) || (value is Guid))
271                        Key.SetValue(name, value, RegistryValueKind.String);
272                    else
273                        using (MemoryStream stream = new MemoryStream())
274                        {
275                            new BinaryFormatter().Serialize(stream, value);
276                            Key.SetValue(name, stream.ToArray(), RegistryValueKind.Binary);
277                        }
278                }
279            }
280
281            public override PersistentStore GetSubsection(string subsectionName)
282            {
283                RegistryKey subKey = null;
284
285                try
286                {
287                    //Open the registry key containing the settings
288                    subKey = Key.OpenSubKey(subsectionName, true);
289                    if (subKey == null)
290                        subKey = Key.CreateSubKey(subsectionName);
291
292                    PersistentStore result = new RegistrySettings(subKey);
293                    subKey = null;
294                    return result;
295                }
296                finally
297                {
298                    if (subKey != null)
299                        subKey.Close();
300                }
301            }
302
303            /// <summary>
304            /// The registry key where the data is stored.
305            /// </summary>
306            private RegistryKey Key;
307        }
308
309        private Settings()
310        {
311            RegistryKey eraserKey = null;
312
313            try
314            {
315                //Open the registry key containing the settings
316                eraserKey = Registry.CurrentUser.OpenSubKey(Program.SettingsPath, true);
317                if (eraserKey == null)
318                    eraserKey = Registry.CurrentUser.CreateSubKey(Program.SettingsPath);
319
320                //Return the Settings object.
321                registry = new RegistrySettings(eraserKey);
322                eraserKey = null;
323            }
324            finally
325            {
326                if (eraserKey != null)
327                    eraserKey.Close();
328            }
329        }
330
331        public static Settings Get()
332        {
333            return Instance;
334        }
335
336        public override PersistentStore GetSubsection(string subsectionName)
337        {
338            return registry.GetSubsection(subsectionName);
339        }
340
341        public override T GetValue<T>(string name, T defaultValue)
342        {
343            return registry.GetValue<T>(name, defaultValue);
344        }
345
346        public override void SetValue(string name, object value)
347        {
348            registry.SetValue(name, value);
349        }
350
351        private RegistrySettings registry;
352
353        /// <summary>
354        /// The global Settings instance.
355        /// </summary>
356        private static Settings Instance = new Settings();
357    }
358
359    /// <summary>
360    /// Encapsulates an abstract list that is used to store settings.
361    /// </summary>
362    /// <typeparam name="T">The type of the list element.</typeparam>
363    class SettingsList<T> : IList<T>
364    {
365        public SettingsList(PersistentStore store, string settingName)
366            : this(store, settingName, null)
367        {
368        }
369
370        public SettingsList(PersistentStore store, string settingName, IEnumerable<T> values)
371        {
372            Store = store;
373            SettingName = settingName;
374            List = new List<T>();
375
376            if (values != null)
377                List.AddRange(values);
378        }
379
380        ~SettingsList()
381        {
382            Save();
383        }
384
385        #region IList<T> Members
386
387        public int IndexOf(T item)
388        {
389            return List.IndexOf(item);
390        }
391
392        public void Insert(int index, T item)
393        {
394            List.Insert(index, item);
395            Save();
396        }
397
398        public void RemoveAt(int index)
399        {
400            List.RemoveAt(index);
401            Save();
402        }
403
404        public T this[int index]
405        {
406            get
407            {
408                return List[index];
409            }
410            set
411            {
412                List[index] = value;
413                Save();
414            }
415        }
416
417        #endregion
418
419        #region ICollection<T> Members
420
421        public void Add(T item)
422        {
423            List.Add(item);
424            Save();
425        }
426
427        public void Clear()
428        {
429            List.Clear();
430            Save();
431        }
432
433        public bool Contains(T item)
434        {
435            return List.Contains(item);
436        }
437
438        public void CopyTo(T[] array, int arrayIndex)
439        {
440            List.CopyTo(array, arrayIndex);
441        }
442
443        public int Count
444        {
445            get { return List.Count; }
446        }
447
448        public bool IsReadOnly
449        {
450            get { return false; }
451        }
452
453        public bool Remove(T item)
454        {
455            bool result = List.Remove(item);
456            Save();
457            return result;
458        }
459
460        #endregion
461
462        #region IEnumerable<T> Members
463
464        public IEnumerator<T> GetEnumerator()
465        {
466            return List.GetEnumerator();
467        }
468
469        #endregion
470
471        #region IEnumerable Members
472
473        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
474        {
475            return List.GetEnumerator();
476        }
477
478        #endregion
479
480        /// <summary>
481        /// Saves changes made to the list to the settings manager.
482        /// </summary>
483        private void Save()
484        {
485            Store.SetValue(SettingName, List);
486        }
487
488        /// <summary>
489        /// The settings object storing the settings.
490        /// </summary>
491        private PersistentStore Store;
492
493        /// <summary>
494        /// The name of the setting we are encapsulating.
495        /// </summary>
496        private string SettingName;
497
498        /// <summary>
499        /// The list we are using as scratch.
500        /// </summary>
501        private List<T> List;
502    }
503
504    /// <summary>
505    /// Encapsulates an abstract dictionary that is used to store settings.
506    /// </summary>
507    /// <typeparam name="TKey">The key type of the dictionary.</typeparam>
508    /// <typeparam name="TValue">The value type of the dictionary.</typeparam>
509    class SettingsDictionary<TKey, TValue> : IDictionary<TKey, TValue>
510    {
511        public SettingsDictionary(PersistentStore store, string settingName)
512        {
513            Store = store;
514            SettingName = settingName;
515        }
516
517        #region IDictionary<TKey,TValue> Members
518
519        public void Add(TKey key, TValue value)
520        {
521            KeyStore.SetValue(key.ToString(), value);
522        }
523
524        public bool ContainsKey(TKey key)
525        {
526            TValue outValue;
527            return TryGetValue(key, out outValue);
528        }
529
530        public ICollection<TKey> Keys
531        {
532            get { throw new NotSupportedException(); }
533        }
534
535        public bool Remove(TKey key)
536        {
537            KeyStore.SetValue(key.ToString(), null);
538            return true;
539        }
540
541        public bool TryGetValue(TKey key, out TValue value)
542        {
543            value = KeyStore.GetValue<TValue>(key.ToString());
544            return !value.Equals(default(TValue));
545        }
546
547        public ICollection<TValue> Values
548        {
549            get { throw new NotSupportedException(); }
550        }
551
552        public TValue this[TKey key]
553        {
554            get
555            {
556                return KeyStore.GetValue<TValue>(key.ToString());
557            }
558            set
559            {
560                KeyStore.SetValue(key.ToString(), value);
561            }
562        }
563
564        #endregion
565
566        #region ICollection<KeyValuePair<TKey,TValue>> Members
567
568        public void Add(KeyValuePair<TKey, TValue> item)
569        {
570            Add(item.Key, item.Value);
571        }
572
573        public void Clear()
574        {
575            throw new NotSupportedException();
576        }
577
578        public bool Contains(KeyValuePair<TKey, TValue> item)
579        {
580            TValue outValue;
581            if (TryGetValue(item.Key, out outValue) && item.Equals(outValue))
582                return true;
583
584            return false;
585        }
586
587        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
588        {
589            throw new NotSupportedException();
590        }
591
592        public int Count
593        {
594            get { throw new NotSupportedException(); }
595        }
596
597        public bool IsReadOnly
598        {
599            get { return false; }
600        }
601
602        public bool Remove(KeyValuePair<TKey, TValue> item)
603        {
604            if (Contains(item))
605            {
606                this[item.Key] = default(TValue);
607                return true;
608            }
609
610            return false;
611        }
612
613        #endregion
614
615        #region IEnumerable<KeyValuePair<TKey,TValue>> Members
616
617        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
618        {
619            throw new NotSupportedException();
620        }
621
622        #endregion
623
624        #region IEnumerable Members
625
626        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
627        {
628            throw new NotSupportedException();
629        }
630
631        #endregion
632
633        /// <summary>
634        /// Gets the Persistent Store for this dictionary.
635        /// </summary>
636        private PersistentStore KeyStore
637        {
638            get
639            {
640                return Store.GetSubsection(SettingName);
641            }
642        }
643
644        /// <summary>
645        /// The settings object storing the settings.
646        /// </summary>
647        private PersistentStore Store;
648
649        /// <summary>
650        /// The name of the setting we are encapsulating.
651        /// </summary>
652        private string SettingName;
653    }
654
655    internal class EraserSettings
656    {
657        /// <summary>
658        /// Constructor.
659        /// </summary>
660        private EraserSettings()
661        {
662            settings = Settings.Get();
663        }
664
665        /// <summary>
666        /// Gets the singleton instance of the Eraser UI Settings.
667        /// </summary>
668        /// <returns>The global instance of the Eraser UI settings.</returns>
669        public static EraserSettings Get()
670        {
671            if (instance == null)
672                instance = new EraserSettings();
673            return instance;
674        }
675
676        /// <summary>
677        /// Gets or sets the LCID of the language which the UI should be displayed in.
678        /// </summary>
679        public string Language
680        {
681            get
682            {
683                return settings.GetValue("Language", GetCurrentCulture().Name);
684            }
685            set
686            {
687                settings.SetValue("Language", value);
688            }
689        }
690
691        /// <summary>
692        /// Gets or sets whether the Shell Extension should be loaded into Explorer.
693        /// </summary>
694        public bool IntegrateWithShell
695        {
696            get
697            {
698                return settings.GetValue("IntegrateWithShell", true);
699            }
700            set
701            {
702                settings.SetValue("IntegrateWithShell", value);
703            }
704        }
705
706        /// <summary>
707        /// Gets or sets a value on whether the main frame should be minimised to the
708        /// system notification area.
709        /// </summary>
710        public bool HideWhenMinimised
711        {
712            get
713            {
714                return settings.GetValue("HideWhenMinimised", true);
715            }
716            set
717            {
718                settings.SetValue("HideWhenMinimised", value);
719            }
720        }
721
722        /// <summary>
723        /// Gets ot setts a value whether tasks which were completed successfully
724        /// should be removed by the Eraser client.
725        /// </summary>
726        public bool ClearCompletedTasks
727        {
728            get
729            {
730                return settings.GetValue("ClearCompletedTasks", true);
731            }
732            set
733            {
734                settings.SetValue("ClearCompletedTasks", value);
735            }
736        }
737
738        /// <summary>
739        /// Gets the most specific UI culture with a localisation available, defaulting to English
740        /// if none exist.
741        /// </summary>
742        /// <returns>The CultureInfo of the current UI culture, correct to the top level.</returns>
743        private static CultureInfo GetCurrentCulture()
744        {
745            System.Reflection.Assembly entryAssembly = System.Reflection.Assembly.GetEntryAssembly();
746            CultureInfo culture = CultureInfo.CurrentUICulture;
747            while (culture.Parent != CultureInfo.InvariantCulture &&
748                !Localisation.LocalisationExists(culture, entryAssembly))
749            {
750                culture = culture.Parent;
751            }
752
753            //Default to English if any of our cultures don't exist.
754            if (!Localisation.LocalisationExists(culture, entryAssembly))
755                culture = new CultureInfo("en");
756
757            return culture;
758        }
759
760        /// <summary>
761        /// The data store behind the object.
762        /// </summary>
763        private PersistentStore settings;
764
765        /// <summary>
766        /// The global instance of the settings class.
767        /// </summary>
768        private static EraserSettings instance;
769    }
770}
Note: See TracBrowser for help on using the repository browser.