From 4b3f7ba0a5808b591adce137269e6b136cb515be Mon Sep 17 00:00:00 2001 From: RKrom Date: Thu, 22 Mar 2012 12:00:07 +0000 Subject: [PATCH] Refactored the ini code to be more "OO", making it possible to reset a value which was needed to implement an auto-repair for certain settings. This was intiated by Bug #3509754, which hat the reason that somehow the Hotkey name was corrupt. git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@1721 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4 --- Greenshot/Destinations/EmailDestination.cs | 3 + Greenshot/Forms/MainForm.cs | 94 +++-- Greenshot/Helpers/DestinationHelper.cs | 5 +- Greenshot/Helpers/EnvironmentInfo.cs | 2 +- GreenshotPlugin/Core/CoreConfiguration.cs | 9 +- GreenshotPlugin/Core/InterfaceUtils.cs | 3 +- GreenshotPlugin/Core/WindowsHelper.cs | 10 +- GreenshotPlugin/IniFile/IniConfig.cs | 378 +-------------------- GreenshotPlugin/IniFile/IniSection.cs | 85 +++++ GreenshotPlugin/IniFile/IniValue.cs | 358 ++++++++++++++++++- 10 files changed, 507 insertions(+), 440 deletions(-) diff --git a/Greenshot/Destinations/EmailDestination.cs b/Greenshot/Destinations/EmailDestination.cs index 8d9fe0ad9..323fe80f2 100644 --- a/Greenshot/Destinations/EmailDestination.cs +++ b/Greenshot/Destinations/EmailDestination.cs @@ -192,7 +192,10 @@ namespace Greenshot.Destinations { using (Image image = surface.GetImageForExport()) { tmpFile = ImageOutput.SaveNamedTmpFile(image, captureDetails, conf.OutputFileFormat, conf.OutputFileJpegQuality, conf.OutputFileReduceColors); } + } else { + LOG.InfoFormat("Using already available file: {0}", tmpFile); } + // Create a attachment name for the image string attachmentName = captureDetails.Title; if (!string.IsNullOrEmpty(attachmentName)) { diff --git a/Greenshot/Forms/MainForm.cs b/Greenshot/Forms/MainForm.cs index 665dc002c..cf6803b83 100644 --- a/Greenshot/Forms/MainForm.cs +++ b/Greenshot/Forms/MainForm.cs @@ -450,7 +450,45 @@ namespace Greenshot { } base.WndProc(ref m); } - + + /// + /// Helper method to cleanly register a hotkey + /// + /// + /// + /// + /// + /// + private static bool RegisterHotkey(StringBuilder failedKeys, string functionName, string hotkeyString, HotKeyHandler handler) { + Keys modifierKeyCode = HotkeyControl.HotkeyModifiersFromString(hotkeyString); + Keys virtualKeyCode = HotkeyControl.HotkeyFromString(hotkeyString); + if (HotkeyControl.RegisterHotKey(modifierKeyCode, virtualKeyCode, handler) < 0) { + LOG.DebugFormat("Failed to register {0} to hotkey: {1}", functionName, hotkeyString); + if (failedKeys.Length > 0) { + failedKeys.Append(", "); + } + failedKeys.Append(hotkeyString); + return false; + } else { + LOG.DebugFormat("Registered {0} to hotkey: {1}", functionName, hotkeyString); + } + return true; + } + + private static bool RegisterWrapper(StringBuilder failedKeys, string functionName, string configurationKey, HotKeyHandler handler) { + IniValue hotkeyValue = conf.Values[configurationKey]; + try { + return RegisterHotkey(failedKeys, functionName, hotkeyValue.Value.ToString(), handler); + } catch (Exception ex) { + LOG.Warn(ex); + LOG.WarnFormat("Repairing the hotkey for {0}, stored under {1} from '{2}' to '{3}'", functionName, configurationKey, hotkeyValue.Value, hotkeyValue.Attributes.DefaultValue); + // when getting an exception the key wasn't found: reset the hotkey value + hotkeyValue.UseValueOrDefault(null); + hotkeyValue.ContainingIniSection.IsDirty = true; + return RegisterHotkey(failedKeys, functionName, hotkeyValue.Value.ToString(), handler); + } + } + public static void RegisterHotkeys() { if (instance == null) { return; @@ -458,64 +496,20 @@ namespace Greenshot { bool success = true; StringBuilder failedKeys = new StringBuilder(); - // Capture region - if (HotkeyControl.RegisterHotKey(conf.RegionHotkey, new HotKeyHandler(instance.CaptureRegion)) < 0) { - LOG.DebugFormat("Failed to register CaptureRegion to hotkey: {0}", conf.RegionHotkey); + if (!RegisterWrapper(failedKeys, "CaptureRegion", "RegionHotkey", new HotKeyHandler(instance.CaptureRegion))) { success = false; - if(failedKeys.Length > 0) { - failedKeys.Append(", "); - } - failedKeys.Append(conf.RegionHotkey); - } else { - LOG.DebugFormat("Registered CaptureRegion to hotkey: {0}", conf.RegionHotkey); } - - // Capture window - if (HotkeyControl.RegisterHotKey(conf.WindowHotkey, new HotKeyHandler(instance.CaptureWindow)) < 0) { - LOG.DebugFormat("Failed to register CaptureWindow to hotkey: {0}", conf.WindowHotkey); + if (!RegisterWrapper(failedKeys, "CaptureWindow", "WindowHotkey", new HotKeyHandler(instance.CaptureWindow))) { success = false; - if(failedKeys.Length > 0) { - failedKeys.Append(", "); - } - failedKeys.Append(conf.WindowHotkey); - } else { - LOG.DebugFormat("Registered CaptureWindow to hotkey: {0}", conf.WindowHotkey); } - - // Capture fullScreen - if (HotkeyControl.RegisterHotKey(conf.FullscreenHotkey, new HotKeyHandler(instance.CaptureFullScreen)) < 0) { - LOG.DebugFormat("Failed to register CaptureFullScreen to hotkey: {0}", conf.FullscreenHotkey); + if (!RegisterWrapper(failedKeys, "CaptureFullScreen", "FullscreenHotkey", new HotKeyHandler(instance.CaptureFullScreen))) { success = false; - if(failedKeys.Length > 0) { - failedKeys.Append(", "); - } - failedKeys.Append(conf.FullscreenHotkey); - } else { - LOG.DebugFormat("Registered CaptureFullScreen to hotkey: {0}", conf.FullscreenHotkey); } - - // Capture last region - if (HotkeyControl.RegisterHotKey(conf.LastregionHotkey, new HotKeyHandler(instance.CaptureLastRegion)) < 0) { - LOG.DebugFormat("Failed to register CaptureLastRegion to hotkey: {0}", conf.LastregionHotkey); + if (!RegisterWrapper(failedKeys, "CaptureLastRegion", "LastregionHotkey", new HotKeyHandler(instance.CaptureLastRegion))) { success = false; - if(failedKeys.Length > 0) { - failedKeys.Append(", "); - } - failedKeys.Append(conf.LastregionHotkey); - } else { - LOG.DebugFormat("Registered CaptureLastRegion to hotkey: {0}", conf.LastregionHotkey); } - - // Capture IE - if (HotkeyControl.RegisterHotKey(conf.IEHotkey, new HotKeyHandler(instance.CaptureIE)) < 0) { - LOG.DebugFormat("Failed to register CaptureIE to hotkey: {0}", conf.IEHotkey); + if (!RegisterWrapper(failedKeys, "CaptureIE", "IEHotkey", new HotKeyHandler(instance.CaptureIE))) { success = false; - if(failedKeys.Length > 0) { - failedKeys.Append(", "); - } - failedKeys.Append(conf.IEHotkey); - } else { - LOG.DebugFormat("Registered CaptureIE to hotkey: {0}", conf.IEHotkey); } if (!success) { diff --git a/Greenshot/Helpers/DestinationHelper.cs b/Greenshot/Helpers/DestinationHelper.cs index b313a0a4b..6535b8a38 100644 --- a/Greenshot/Helpers/DestinationHelper.cs +++ b/Greenshot/Helpers/DestinationHelper.cs @@ -75,7 +75,10 @@ namespace Greenshot.Helpers { List destinations = new List(); destinations.AddRange(RegisteredDestinations.Values); foreach(IGreenshotPlugin plugin in PluginHelper.instance.Plugins.Values) { - destinations.AddRange(plugin.Destinations()); + var dests = plugin.Destinations(); + if (dests != null) { + destinations.AddRange(dests); + } } destinations.Sort(); return destinations; diff --git a/Greenshot/Helpers/EnvironmentInfo.cs b/Greenshot/Helpers/EnvironmentInfo.cs index 8f3e52f21..68e51a077 100644 --- a/Greenshot/Helpers/EnvironmentInfo.cs +++ b/Greenshot/Helpers/EnvironmentInfo.cs @@ -125,7 +125,7 @@ namespace Greenshot.Helpers { exceptionText.AppendLine(EnvironmentInfo.ExceptionToString(exception)); exceptionText.AppendLine("Configuration dump:"); using (TextWriter writer = new StringWriter(exceptionText)) { - IniConfig.SaveIniSectionToWriter(writer, IniConfig.GetIniSection(), true); + IniConfig.GetIniSection().Write(writer, true); } return exceptionText.ToString(); diff --git a/GreenshotPlugin/Core/CoreConfiguration.cs b/GreenshotPlugin/Core/CoreConfiguration.cs index 8d5c50e3a..1a5d00024 100644 --- a/GreenshotPlugin/Core/CoreConfiguration.cs +++ b/GreenshotPlugin/Core/CoreConfiguration.cs @@ -194,7 +194,8 @@ namespace GreenshotPlugin.Core { if (productName != null && NoDWMCaptureForProduct.Contains(productName.ToLower())) { return false; } - } catch { + } catch (Exception ex) { + LOG.Warn(ex); } } } @@ -214,7 +215,8 @@ namespace GreenshotPlugin.Core { if (productName != null && NoGDICaptureForProduct.Contains(productName.ToLower())) { return false; } - } catch { + } catch (Exception ex) { + LOG.Warn(ex); } } } @@ -250,7 +252,8 @@ namespace GreenshotPlugin.Core { try { Directory.CreateDirectory(pafOutputFilePath); return pafOutputFilePath; - } catch(Exception) { + } catch (Exception ex) { + LOG.Warn(ex); // Problem creating directory, fallback to Desktop } } else { diff --git a/GreenshotPlugin/Core/InterfaceUtils.cs b/GreenshotPlugin/Core/InterfaceUtils.cs index dbb196d1a..dda013442 100644 --- a/GreenshotPlugin/Core/InterfaceUtils.cs +++ b/GreenshotPlugin/Core/InterfaceUtils.cs @@ -48,7 +48,8 @@ namespace GreenshotPlugin.Core { } } } - } catch { + } catch (Exception ex) { + LOG.Warn(ex); } } return list; diff --git a/GreenshotPlugin/Core/WindowsHelper.cs b/GreenshotPlugin/Core/WindowsHelper.cs index 4ed110f3b..cc53b6b38 100644 --- a/GreenshotPlugin/Core/WindowsHelper.cs +++ b/GreenshotPlugin/Core/WindowsHelper.cs @@ -244,7 +244,7 @@ namespace GreenshotPlugin.Core { return iconCache[filename]; } catch (Exception ex) { LOG.WarnFormat("Couldn't get icon for window {0} due to: {1}", Text, ex.Message); - }; + } return null; } } @@ -567,7 +567,9 @@ namespace GreenshotPlugin.Core { if (process != null) { return process; } - } catch {} + } catch (Exception ex) { + LOG.Warn(ex); + } return null; } } @@ -1358,7 +1360,9 @@ namespace GreenshotPlugin.Core { get { try { return "Greenshot".Equals(Process.MainModule.FileVersionInfo.ProductName); - } catch {} + } catch (Exception ex) { + LOG.Warn(ex); + } return false; } } diff --git a/GreenshotPlugin/IniFile/IniConfig.cs b/GreenshotPlugin/IniFile/IniConfig.cs index 9a5eb43ea..20bf1816e 100644 --- a/GreenshotPlugin/IniFile/IniConfig.cs +++ b/GreenshotPlugin/IniFile/IniConfig.cs @@ -221,7 +221,7 @@ namespace Greenshot.IniFile { Read(CreateIniLocation(configName + FIXED_POSTFIX + INI_EXTENSION)); foreach (IniSection section in sectionMap.Values) { - FillIniSection(section); + section.Fill(PropertiesForSection(section)); } } @@ -278,8 +278,7 @@ namespace Greenshot.IniFile { // Store for later save & retrieval sectionMap.Add(sectionName, section); - FillIniSection(section); - //LOG.Debug("Returning newly mapped section " + sectionName); + section.Fill(PropertiesForSection(section)); } if (section.IsDirty) { LOG.DebugFormat("Section {0} is marked dirty, saving!", sectionName); @@ -288,7 +287,7 @@ namespace Greenshot.IniFile { return section; } - private static void FillIniSection(IniSection section) { + public static Dictionary PropertiesForSection(IniSection section) { Type iniSectionType = section.GetType(); string sectionName = getSectionName(iniSectionType); // Get the properties for the section @@ -299,279 +298,7 @@ namespace Greenshot.IniFile { sections.Add(sectionName, new Dictionary()); properties = sections[sectionName]; } - - // Iterate over the members and create IniValueContainers - foreach (FieldInfo fieldInfo in iniSectionType.GetFields()) { - if (Attribute.IsDefined(fieldInfo, typeof(IniPropertyAttribute))) { - if (!section.Values.ContainsKey(fieldInfo.Name)) { - IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)fieldInfo.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0]; - section.Values.Add(fieldInfo.Name, new IniValue(section, fieldInfo, iniPropertyAttribute)); - } - } - } - - foreach (PropertyInfo propertyInfo in iniSectionType.GetProperties()) { - if (Attribute.IsDefined(propertyInfo, typeof(IniPropertyAttribute))) { - if (!section.Values.ContainsKey(propertyInfo.Name)) { - IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)propertyInfo.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0]; - section.Values.Add(propertyInfo.Name, new IniValue(section, propertyInfo, iniPropertyAttribute)); - } - } - } - - foreach (string fieldName in section.Values.Keys) { - IniValue iniValue = section.Values[fieldName]; - string propertyName = iniValue.Attributes.Name; - string propertyDefaultValue = iniValue.Attributes.DefaultValue; - - // Get the value from the ini file, if there is none take the default - if (!properties.ContainsKey(propertyName) && propertyDefaultValue != null) { - // Mark as dirty, we didn't use properties from the file (even defaults from the default file are allowed) - section.IsDirty = true; - //LOG.Debug("Passing default: " + propertyName + "=" + propertyDefaultValue); - } - - // Try to get the field value from the properties or use the default value - object fieldValue = null; - try { - fieldValue = CreateValue(section, sectionName, iniValue); - } catch (Exception) { - //LOG.Warn("Couldn't parse field: " + sectionName + "." + propertyName, e); - } - - // If still no value, e.g. due to an exception, check if the GetDefault delivers a value - if (fieldValue == null) { - // Use GetDefault to fill the field if none is set - fieldValue = section.GetDefault(propertyName); - } - - // Still no value? Log warning - if (fieldValue == null) { - LOG.WarnFormat("Property {0} has no value or default value.", propertyName); - } - - // Set the value - try { - iniValue.Value = fieldValue; - } catch (Exception) { - //LOG.Warn("Couldn't set field: " + sectionName + "." + propertyName, e); - } - } - section.AfterLoad(); - } - - /// - /// Helper method for creating a value - /// - /// IniSection - /// string with name of section - /// IniValue - /// - private static object CreateValue(IniSection section, string sectionName, IniValue iniValue) { - string propertyName = iniValue.Attributes.Name; - string defaultValue = iniValue.Attributes.DefaultValue; - string arraySeparator = iniValue.Attributes.Separator; - // Get the type, or the underlying type for nullables - Type valueType = iniValue.ValueType; - - Dictionary properties = sections[sectionName]; - bool defaultUsed = false; - string propertyValue = null; - object defaultValueFromConfig = null; - if (properties.ContainsKey(propertyName) && properties[propertyName] != null) { - propertyValue = section.PreCheckValue(propertyName, properties[propertyName]); - } else if (defaultValue != null && defaultValue.Trim().Length != 0) { - propertyValue = defaultValue; - defaultUsed = true; - } else { - LOG.DebugFormat("Property {0} has no value or default value, this might be corrected later!", propertyName); - // Check if the developer implemented a default for the property - defaultValueFromConfig = section.GetDefault(propertyName); - if (defaultValueFromConfig != null) { - LOG.DebugFormat("Default for Property {0} implemented!", propertyName); - } else { - if (iniValue.Attributes.ExcludeIfNull) { - return null; - } - } - } - - // Now set the value - if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List<>)) { - object list = Activator.CreateInstance(valueType); - // Logic for List<> - if (propertyValue == null) { - if (defaultValueFromConfig != null) { - return defaultValueFromConfig; - } - return list; - } - string[] arrayValues = propertyValue.Split(new string[] { arraySeparator }, StringSplitOptions.None); - if (arrayValues == null || arrayValues.Length == 0) { - return list; - } - bool addedElements = false; - bool parseProblems = false; - MethodInfo addMethodInfo = valueType.GetMethod("Add"); - - foreach (string arrayValue in arrayValues) { - if (arrayValue != null && arrayValue.Length > 0) { - object newValue = null; - try { - newValue = ConvertValueToValueType(valueType.GetGenericArguments()[0], arrayValue); - } catch (Exception) { - //LOG.Error("Problem converting " + arrayValue + " to type " + fieldType.FullName, e); - parseProblems = true; - } - if (newValue != null) { - addMethodInfo.Invoke(list, new object[] { newValue }); - addedElements = true; - } - } - } - // Try to fallback on a default - if (!addedElements && parseProblems) { - try { - object fallbackValue = ConvertValueToValueType(valueType.GetGenericArguments()[0], defaultValue); - addMethodInfo.Invoke(list, new object[] { fallbackValue }); - return list; - } catch (Exception) { - //LOG.Error("Problem converting " + defaultValue + " to type " + fieldType.FullName, e); - } - } - - return list; - } else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { - // Logic for Dictionary<,> - Type type1 = valueType.GetGenericArguments()[0]; - Type type2 = valueType.GetGenericArguments()[1]; - //LOG.Info(String.Format("Found Dictionary<{0},{1}>", type1.Name, type2.Name)); - object dictionary = Activator.CreateInstance(valueType); - MethodInfo addMethodInfo = valueType.GetMethod("Add"); - bool addedElements = false; - foreach (string key in properties.Keys) { - if (key != null && key.StartsWith(propertyName + ".")) { - // What "key" do we need to store it under? - string subPropertyName = key.Substring(propertyName.Length + 1); - string stringValue = properties[key]; - object newValue1 = null; - object newValue2 = null; - try { - newValue1 = ConvertValueToValueType(type1, subPropertyName); - } catch (Exception) { - //LOG.Error("Problem converting " + subPropertyName + " to type " + type1.FullName, e); - } - try { - newValue2 = ConvertValueToValueType(type2, stringValue); - } catch (Exception) { - //LOG.Error("Problem converting " + stringValue + " to type " + type2.FullName, e); - } - addMethodInfo.Invoke(dictionary, new object[] { newValue1, newValue2 }); - addedElements = true; - } - } - // No need to return something that isn't filled! - if (addedElements) { - return dictionary; - } else if (defaultValueFromConfig != null) { - return defaultValueFromConfig; - } - } if (defaultValueFromConfig != null) { - return defaultValueFromConfig; - } else { - if (valueType.IsGenericType && valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { - // We are dealing with a generic type that is nullable - valueType = Nullable.GetUnderlyingType(valueType); - } - object newValue = null; - try { - newValue = ConvertValueToValueType(valueType, propertyValue); - } catch (Exception) { - newValue = null; - if (!defaultUsed) { - try { - newValue = ConvertValueToValueType(valueType, defaultValue); - } catch (Exception) { - //LOG.Error("Problem converting " + propertyValue + " to type " + fieldType.FullName, e2); - } - } else { - //LOG.Error("Problem converting " + propertyValue + " to type " + fieldType.FullName, e1); - } - } - return newValue; - } - } - - private static object ConvertValueToValueType(Type valueType, string valueString) { - if (valueString == null) { - return null; - } - if (valueType == typeof(string)) { - return valueString; - } - TypeConverter converter = TypeDescriptor.GetConverter(valueType); - //LOG.Debug("No convertor for " + fieldType.ToString()); - if (valueType == typeof(object) && valueString.Length > 0) { - //LOG.Debug("Parsing: " + valueString); - string[] values = valueString.Split(new Char[] { ':' }); - //LOG.Debug("Type: " + values[0]); - //LOG.Debug("Value: " + values[1]); - Type fieldTypeForValue = Type.GetType(values[0], true); - //LOG.Debug("Type after GetType: " + fieldTypeForValue); - return ConvertValueToValueType(fieldTypeForValue, values[1]); - } else if (converter != null) { - return converter.ConvertFromInvariantString(valueString); - } else if (valueType.IsEnum) { - if (valueString.Length > 0) { - try { - return Enum.Parse(valueType, valueString); - } catch (ArgumentException ae) { - //LOG.InfoFormat("Couldn't match {0} to {1}, trying case-insentive match", valueString, fieldType); - foreach (Enum enumValue in Enum.GetValues(valueType)) { - if (enumValue.ToString().Equals(valueString, StringComparison.InvariantCultureIgnoreCase)) { - //LOG.Info("Match found..."); - return enumValue; - } - } - throw ae; - } - } - } - return null; - } - - private static string ConvertValueToString(Type valueType, object valueObject) { - if (valueObject == null) { - // If there is nothing, deliver nothing! - return ""; - } - if (valueType == typeof(object)) { - // object to String, this is the hardest - // Format will be "FQTypename[,Assemblyname]:Value" - - // Get the type so we can call ourselves recursive - Type objectType = valueObject.GetType(); - - // Get the value as string - string ourValue = ConvertValueToString(objectType, valueObject); - - // Get the valuetype as string - string valueTypeName = objectType.FullName; - // Find the assembly name and only append it if it's not already in the fqtypename (like System.Drawing) - string assemblyName = objectType.Assembly.FullName; - // correct assemblyName, this also has version information etc. - if (assemblyName.StartsWith("Green")) { - assemblyName = assemblyName.Substring(0, assemblyName.IndexOf(',')); - } - return String.Format("{0},{1}:{2}", valueTypeName, assemblyName, ourValue); - } else { - TypeConverter converter = TypeDescriptor.GetConverter(valueType); - if (converter != null) { - return converter.ConvertToInvariantString(valueObject); - } - } - // All other types - return valueObject.ToString(); + return properties; } private static string getSectionName(Type iniSectionType) { @@ -589,101 +316,12 @@ namespace Greenshot.IniFile { string iniLocation = CreateIniLocation(configName + INI_EXTENSION); try { SaveInternally(iniLocation); - } catch (Exception) { - //LOG.Error("A problem occured while writing the configuration file to: " + iniLocation, e); + } catch (Exception ex) { + LOG.Error("A problem occured while writing the configuration file to: " + iniLocation); + LOG.Error(ex); } } - public static void SaveIniSectionToWriter(TextWriter writer, IniSection section, bool onlyProperties) { - section.BeforeSave(); - Type classType = section.GetType(); - Attribute[] classAttributes = Attribute.GetCustomAttributes(classType); - foreach (Attribute attribute in classAttributes) { - if (attribute is IniSectionAttribute) { - IniSectionAttribute iniSectionAttribute = (IniSectionAttribute)attribute; - if (!onlyProperties) { - writer.WriteLine("; {0}", iniSectionAttribute.Description); - } - writer.WriteLine("[{0}]", iniSectionAttribute.Name); - - // Iterate over the members and fill them - List members = new List(); - - foreach (FieldInfo fieldInfo in classType.GetFields()) { - members.Add(fieldInfo); - } - foreach (PropertyInfo propertyInfo in classType.GetProperties()) { - members.Add(propertyInfo); - } - - foreach (MemberInfo member in members) { - if (Attribute.IsDefined(member, typeof(IniPropertyAttribute))) { - IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)member.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0]; - object propertyValue; - Type valueType; - if (member is FieldInfo) { - propertyValue = ((FieldInfo)member).GetValue(section); - valueType = ((FieldInfo)member).FieldType; - } else if (member is PropertyInfo) { - propertyValue = ((PropertyInfo)member).GetValue(section, null); - valueType = ((PropertyInfo)member).PropertyType; - } else { - continue; - } - - if (propertyValue == null) { - if (iniPropertyAttribute.ExcludeIfNull) { - continue; - } - propertyValue = iniPropertyAttribute.DefaultValue; - valueType = typeof(string); - } - - if (!onlyProperties) { - writer.WriteLine("; {0}", iniPropertyAttribute.Description); - } - if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List<>)) { - Type specificValueType = valueType.GetGenericArguments()[0]; - writer.Write("{0}=", iniPropertyAttribute.Name); - int listCount = (int)valueType.GetProperty("Count").GetValue(propertyValue, null); - // Loop though generic list - for (int index = 0; index < listCount; index++) { - object item = valueType.GetMethod("get_Item").Invoke(propertyValue, new object[] { index }); - - // Now you have an instance of the item in the generic list - if (index < listCount - 1) { - writer.Write("{0}" + iniPropertyAttribute.Separator, ConvertValueToString(specificValueType, item)); - } else { - writer.Write("{0}", ConvertValueToString(specificValueType, item)); - } - } - writer.WriteLine(); - } else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { - // Handle dictionaries. - Type valueType1 = valueType.GetGenericArguments()[0]; - Type valueType2 = valueType.GetGenericArguments()[1]; - // Get the methods we need to deal with dictionaries. - var keys = valueType.GetProperty("Keys").GetValue(propertyValue, null); - var item = valueType.GetProperty("Item"); - var enumerator = keys.GetType().GetMethod("GetEnumerator").Invoke(keys, null); - var moveNext = enumerator.GetType().GetMethod("MoveNext"); - var current = enumerator.GetType().GetProperty("Current").GetGetMethod(); - // Get all the values. - while ((bool)moveNext.Invoke(enumerator, null)) { - var key = current.Invoke(enumerator, null); - var valueObject = item.GetValue(propertyValue, new object[] { key }); - // Write to ini file! - writer.WriteLine("{0}.{1}={2}", iniPropertyAttribute.Name, ConvertValueToString(valueType1, key), ConvertValueToString(valueType2, valueObject)); - } - } else { - writer.WriteLine("{0}={1}", iniPropertyAttribute.Name, ConvertValueToString(valueType, propertyValue)); - } - } - } - } - section.AfterSave(); - } - } private static void SaveInternally(string iniLocation) { WatchConfigFile(false); @@ -694,7 +332,7 @@ namespace Greenshot.IniFile { } TextWriter writer = new StreamWriter(iniLocation, false, Encoding.UTF8); foreach (IniSection section in sectionMap.Values) { - SaveIniSectionToWriter(writer, section, false); + section.Write(writer, false); // Add empty line after section writer.WriteLine(); section.IsDirty = false; diff --git a/GreenshotPlugin/IniFile/IniSection.cs b/GreenshotPlugin/IniFile/IniSection.cs index 444a3847f..ca7684bd5 100644 --- a/GreenshotPlugin/IniFile/IniSection.cs +++ b/GreenshotPlugin/IniFile/IniSection.cs @@ -20,6 +20,8 @@ */ using System; using System.Collections.Generic; +using System.Reflection; +using System.IO; namespace Greenshot.IniFile { /// @@ -27,16 +29,23 @@ namespace Greenshot.IniFile { /// [Serializable] public abstract class IniSection { + protected static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(IniSection)); + [NonSerialized] private IDictionary values = new Dictionary(); + /// + /// Get the dictionary with all the IniValues + /// public IDictionary Values { get { return values; } } + /// /// Flag to specify if values have been changed + /// public bool IsDirty = false; /// @@ -65,10 +74,86 @@ namespace Greenshot.IniFile { public virtual void AfterLoad() { } + /// + /// This will be called before saving the Section, so we can encrypt passwords etc... + /// public virtual void BeforeSave() { } + /// + /// This will be called before saving the Section, so we can decrypt passwords etc... + /// public virtual void AfterSave() { } + + /// + /// Fill the section with the supplied properties + /// + /// + public void Fill(Dictionary properties) { + Type iniSectionType = this.GetType(); + + // Iterate over the members and create IniValueContainers + foreach (FieldInfo fieldInfo in iniSectionType.GetFields()) { + if (Attribute.IsDefined(fieldInfo, typeof(IniPropertyAttribute))) { + if (!Values.ContainsKey(fieldInfo.Name)) { + IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)fieldInfo.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0]; + Values.Add(fieldInfo.Name, new IniValue(this, fieldInfo, iniPropertyAttribute)); + } + } + } + + foreach (PropertyInfo propertyInfo in iniSectionType.GetProperties()) { + if (Attribute.IsDefined(propertyInfo, typeof(IniPropertyAttribute))) { + if (!Values.ContainsKey(propertyInfo.Name)) { + IniPropertyAttribute iniPropertyAttribute = (IniPropertyAttribute)propertyInfo.GetCustomAttributes(typeof(IniPropertyAttribute), false)[0]; + Values.Add(propertyInfo.Name, new IniValue(this, propertyInfo, iniPropertyAttribute)); + } + } + } + + foreach (string fieldName in Values.Keys) { + IniValue iniValue = Values[fieldName]; + try { + iniValue.SetValueFromProperties(properties); + } catch (Exception ex) { + LOG.Error(ex); + } + } + AfterLoad(); + } + + /// + /// Write the section to the writer + /// + /// + /// + public void Write(TextWriter writer, bool onlyProperties) { + BeforeSave(); + try { + Attribute[] classAttributes = Attribute.GetCustomAttributes(this.GetType()); + IniSectionAttribute iniSectionAttribute = null; + foreach (Attribute attribute in classAttributes) { + if (attribute is IniSectionAttribute) { + iniSectionAttribute = (IniSectionAttribute)attribute; + break; + } + } + if (iniSectionAttribute == null) { + throw new ArgumentException("Section didn't implement the IniSectionAttribute"); + } + + if (!onlyProperties) { + writer.WriteLine("; {0}", iniSectionAttribute.Description); + } + writer.WriteLine("[{0}]", iniSectionAttribute.Name); + + foreach (IniValue value in Values.Values) { + value.Write(writer, onlyProperties); + } + } finally { + AfterSave(); + } + } } } diff --git a/GreenshotPlugin/IniFile/IniValue.cs b/GreenshotPlugin/IniFile/IniValue.cs index 3fd85f080..b079295cf 100644 --- a/GreenshotPlugin/IniFile/IniValue.cs +++ b/GreenshotPlugin/IniFile/IniValue.cs @@ -20,25 +20,29 @@ */ using System; using System.Reflection; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; namespace Greenshot.IniFile { /// /// A container to be able to pass the value from a IniSection around. /// public class IniValue { + private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(IniValue)); private PropertyInfo propertyInfo; private FieldInfo fieldInfo; - private object containingObject; + private IniSection containingIniSection; private IniPropertyAttribute attributes; - public IniValue(object containingObject, PropertyInfo propertyInfo, IniPropertyAttribute iniPropertyAttribute) { - this.containingObject = containingObject; + public IniValue(IniSection containingIniSection, PropertyInfo propertyInfo, IniPropertyAttribute iniPropertyAttribute) { + this.containingIniSection = containingIniSection; this.propertyInfo = propertyInfo; this.attributes = iniPropertyAttribute; } - public IniValue(object containingObject, FieldInfo fieldInfo, IniPropertyAttribute iniPropertyAttribute) { - this.containingObject = containingObject; + public IniValue(IniSection containingIniSection, FieldInfo fieldInfo, IniPropertyAttribute iniPropertyAttribute) { + this.containingIniSection = containingIniSection; this.fieldInfo = fieldInfo; this.attributes = iniPropertyAttribute; } @@ -52,6 +56,12 @@ namespace Greenshot.IniFile { } } } + + public IniSection ContainingIniSection { + get { + return containingIniSection; + } + } public IniPropertyAttribute Attributes { get { @@ -62,28 +72,354 @@ namespace Greenshot.IniFile { public object Value { get { if (propertyInfo == null) { - return fieldInfo.GetValue(containingObject); + return fieldInfo.GetValue(containingIniSection); } else { - return propertyInfo.GetValue(containingObject, null); + return propertyInfo.GetValue(containingIniSection, null); } } set { if (propertyInfo == null) { - fieldInfo.SetValue(containingObject, value); + fieldInfo.SetValue(containingIniSection, value); } else { - propertyInfo.SetValue(containingObject, value, null); + propertyInfo.SetValue(containingIniSection, value, null); } } } public Type ValueType { get { + Type valueType = null; if (propertyInfo == null) { - return fieldInfo.FieldType; + valueType = fieldInfo.FieldType; } else { - return propertyInfo.PropertyType; + valueType = propertyInfo.PropertyType; } + if (valueType.IsGenericType && valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { + // We are dealing with a generic type that is nullable + valueType = Nullable.GetUnderlyingType(valueType); + } + return valueType; } } + + /// + /// Write the value to the text writer + /// + /// TextWriter to write to + /// true if we do not want the comment + public void Write(TextWriter writer, bool onlyProperties) { + object myValue = Value; + Type valueType = ValueType; + if (myValue == null) { + if (attributes.ExcludeIfNull) { + return; + } + if (attributes.DefaultValue != null) { + myValue = attributes.DefaultValue; + valueType = typeof(string); + } else { + myValue = containingIniSection.GetDefault(attributes.Name); + if (myValue != null) { + valueType = myValue.GetType(); + } + } + } + if (myValue == null) { + if (attributes.ExcludeIfNull) { + return; + } + } + if (!onlyProperties) { + writer.WriteLine("; {0}", attributes.Description); + } + if (myValue == null) { + writer.Write("{0}=", attributes.Name); + return; + } + if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(List<>)) { + Type specificValueType = valueType.GetGenericArguments()[0]; + writer.Write("{0}=", attributes.Name); + int listCount = (int)valueType.GetProperty("Count").GetValue(myValue, null); + // Loop though generic list + for (int index = 0; index < listCount; index++) { + object item = valueType.GetMethod("get_Item").Invoke(myValue, new object[] { index }); + + // Now you have an instance of the item in the generic list + if (index < listCount - 1) { + writer.Write("{0}" + attributes.Separator, ConvertValueToString(specificValueType, item)); + } else { + writer.Write("{0}", ConvertValueToString(specificValueType, item)); + } + } + writer.WriteLine(); + } else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { + // Handle dictionaries. + Type valueType1 = valueType.GetGenericArguments()[0]; + Type valueType2 = valueType.GetGenericArguments()[1]; + // Get the methods we need to deal with dictionaries. + var keys = valueType.GetProperty("Keys").GetValue(myValue, null); + var item = valueType.GetProperty("Item"); + var enumerator = keys.GetType().GetMethod("GetEnumerator").Invoke(keys, null); + var moveNext = enumerator.GetType().GetMethod("MoveNext"); + var current = enumerator.GetType().GetProperty("Current").GetGetMethod(); + // Get all the values. + while ((bool)moveNext.Invoke(enumerator, null)) { + var key = current.Invoke(enumerator, null); + var valueObject = item.GetValue(myValue, new object[] { key }); + // Write to ini file! + writer.WriteLine("{0}.{1}={2}", attributes.Name, ConvertValueToString(valueType1, key), ConvertValueToString(valueType2, valueObject)); + } + } else { + writer.WriteLine("{0}={1}", attributes.Name, ConvertValueToString(valueType, myValue)); + } + + } + + /// + /// Set the value to the value in the ini file, or default + /// + /// + public void SetValueFromProperties(Dictionary properties) { + string propertyName = attributes.Name; + string defaultValue = attributes.DefaultValue; + + // Get the value from the ini file, if there is none take the default + if (!properties.ContainsKey(propertyName) && defaultValue != null) { + // Mark as dirty, we didn't use properties from the file (even defaults from the default file are allowed) + containingIniSection.IsDirty = true; + //LOG.Debug("Passing default: " + propertyName + "=" + propertyDefaultValue); + } + + string propertyValue = null; + if (properties.ContainsKey(propertyName) && properties[propertyName] != null) { + propertyValue = containingIniSection.PreCheckValue(propertyName, properties[propertyName]); + } + UseValueOrDefault(propertyValue); + } + + /// + /// This method will set the ini value to the supplied value or use the default if non supplied + /// + /// + public void UseValueOrDefault(string propertyValue) { + Type valueType = ValueType; + string propertyName = attributes.Name; + string defaultValue = attributes.DefaultValue; + bool defaultUsed = false; + object defaultValueFromConfig = containingIniSection.GetDefault(propertyName); + + if (string.IsNullOrEmpty(propertyValue)) { + if (defaultValue != null && defaultValue.Trim().Length != 0) { + propertyValue = defaultValue; + defaultUsed = true; + } else if (defaultValueFromConfig != null) { + LOG.DebugFormat("Default for Property {0} implemented!", propertyName); + } else { + if (attributes.ExcludeIfNull) { + Value = null; + return; + } + LOG.DebugFormat("Property {0} has no value or default value!", propertyName); + } + } + // Now set the value + if (valueType.IsGenericType && ValueType.GetGenericTypeDefinition() == typeof(List<>)) { + string arraySeparator = attributes.Separator; + object list = Activator.CreateInstance(ValueType); + // Logic for List<> + if (propertyValue == null) { + if (defaultValueFromConfig != null) { + Value = defaultValueFromConfig; + return; + } + Value = list; + return; + } + string[] arrayValues = propertyValue.Split(new string[] { arraySeparator }, StringSplitOptions.None); + if (arrayValues == null || arrayValues.Length == 0) { + Value = list; + return; + } + bool addedElements = false; + bool parseProblems = false; + MethodInfo addMethodInfo = valueType.GetMethod("Add"); + + foreach (string arrayValue in arrayValues) { + if (arrayValue != null && arrayValue.Length > 0) { + object newValue = null; + try { + newValue = ConvertStringToValueType(valueType.GetGenericArguments()[0], arrayValue); + } catch (Exception ex) { + LOG.Warn(ex); + //LOG.Error("Problem converting " + arrayValue + " to type " + fieldType.FullName, e); + parseProblems = true; + } + if (newValue != null) { + addMethodInfo.Invoke(list, new object[] { newValue }); + addedElements = true; + } + } + } + // Try to fallback on a default + if (!addedElements && parseProblems) { + try { + object fallbackValue = ConvertStringToValueType(valueType.GetGenericArguments()[0], defaultValue); + addMethodInfo.Invoke(list, new object[] { fallbackValue }); + Value = list; + return; + } catch (Exception ex) { + LOG.Warn(ex); + //LOG.Error("Problem converting " + defaultValue + " to type " + fieldType.FullName, e); + } + } + Value = list; + return; + } else if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { + // Logic for Dictionary<,> + Type type1 = valueType.GetGenericArguments()[0]; + Type type2 = valueType.GetGenericArguments()[1]; + //LOG.Info(String.Format("Found Dictionary<{0},{1}>", type1.Name, type2.Name)); + object dictionary = Activator.CreateInstance(valueType); + MethodInfo addMethodInfo = valueType.GetMethod("Add"); + bool addedElements = false; + Dictionary properties = IniConfig.PropertiesForSection(containingIniSection); + foreach (string key in properties.Keys) { + if (key != null && key.StartsWith(propertyName + ".")) { + // What "key" do we need to store it under? + string subPropertyName = key.Substring(propertyName.Length + 1); + string stringValue = properties[key]; + object newValue1 = null; + object newValue2 = null; + try { + newValue1 = ConvertStringToValueType(type1, subPropertyName); + } catch (Exception ex) { + LOG.Warn(ex); + //LOG.Error("Problem converting " + subPropertyName + " to type " + type1.FullName, e); + } + try { + newValue2 = ConvertStringToValueType(type2, stringValue); + } catch (Exception ex) { + LOG.Warn(ex); + //LOG.Error("Problem converting " + stringValue + " to type " + type2.FullName, e); + } + addMethodInfo.Invoke(dictionary, new object[] { newValue1, newValue2 }); + addedElements = true; + } + } + // No need to return something that isn't filled! + if (addedElements) { + Value = dictionary; + return; + } else if (defaultValueFromConfig != null) { + Value = defaultValueFromConfig; + return; + } + } if (defaultValueFromConfig != null) { + Value = defaultValueFromConfig; + return; + } else { + if (valueType.IsGenericType && valueType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { + // We are dealing with a generic type that is nullable + valueType = Nullable.GetUnderlyingType(valueType); + } + object newValue = null; + try { + newValue = ConvertStringToValueType(valueType, propertyValue); + } catch (Exception ex1) { + newValue = null; + if (!defaultUsed) { + try { + newValue = ConvertStringToValueType(valueType, defaultValue); + } catch (Exception ex2) { + LOG.Warn("Problem converting " + propertyValue + " to type " + valueType.FullName, ex2); + } + } else { + LOG.Warn("Problem converting " + propertyValue + " to type " + valueType.FullName, ex1); + } + } + Value = newValue; + return; + } + } + + /// + /// Helper method for conversion + /// + /// + /// + /// + private static object ConvertStringToValueType(Type valueType, string valueString) { + if (valueString == null) { + return null; + } + if (valueType == typeof(string)) { + return valueString; + } + TypeConverter converter = TypeDescriptor.GetConverter(valueType); + //LOG.Debug("No convertor for " + fieldType.ToString()); + if (valueType == typeof(object) && valueString.Length > 0) { + //LOG.Debug("Parsing: " + valueString); + string[] values = valueString.Split(new Char[] { ':' }); + //LOG.Debug("Type: " + values[0]); + //LOG.Debug("Value: " + values[1]); + Type fieldTypeForValue = Type.GetType(values[0], true); + //LOG.Debug("Type after GetType: " + fieldTypeForValue); + return ConvertStringToValueType(fieldTypeForValue, values[1]); + } else if (converter != null) { + return converter.ConvertFromInvariantString(valueString); + } else if (valueType.IsEnum) { + if (valueString.Length > 0) { + try { + return Enum.Parse(valueType, valueString); + } catch (ArgumentException ae) { + //LOG.InfoFormat("Couldn't match {0} to {1}, trying case-insentive match", valueString, fieldType); + foreach (Enum enumValue in Enum.GetValues(valueType)) { + if (enumValue.ToString().Equals(valueString, StringComparison.InvariantCultureIgnoreCase)) { + //LOG.Info("Match found..."); + return enumValue; + } + } + throw ae; + } + } + } + return null; + } + + private static string ConvertValueToString(Type valueType, object valueObject) { + if (valueObject == null) { + // If there is nothing, deliver nothing! + return ""; + } + if (valueType == typeof(object)) { + // object to String, this is the hardest + // Format will be "FQTypename[,Assemblyname]:Value" + + // Get the type so we can call ourselves recursive + Type objectType = valueObject.GetType(); + + // Get the value as string + string ourValue = ConvertValueToString(objectType, valueObject); + + // Get the valuetype as string + string valueTypeName = objectType.FullName; + // Find the assembly name and only append it if it's not already in the fqtypename (like System.Drawing) + string assemblyName = objectType.Assembly.FullName; + // correct assemblyName, this also has version information etc. + if (assemblyName.StartsWith("Green")) { + assemblyName = assemblyName.Substring(0, assemblyName.IndexOf(',')); + } + return String.Format("{0},{1}:{2}", valueTypeName, assemblyName, ourValue); + } else { + TypeConverter converter = TypeDescriptor.GetConverter(valueType); + if (converter != null) { + return converter.ConvertToInvariantString(valueObject); + } + } + // All other types + return valueObject.ToString(); + } + + } } \ No newline at end of file