diff --git a/mono-addins/AUTHORS b/mono-addins/AUTHORS new file mode 100644 index 00000000..1fa72b11 --- /dev/null +++ b/mono-addins/AUTHORS @@ -0,0 +1,3 @@ + +Lluis Sanchez Gual + diff --git a/mono-addins/COPYING b/mono-addins/COPYING new file mode 100644 index 00000000..a3a03efc --- /dev/null +++ b/mono-addins/COPYING @@ -0,0 +1,22 @@ +The MIT License + +Copyright (C) 2007 Novell, Inc (http://www.novell.com) +Copyright (C) 2012 Xamarin Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/mono-addins/Mono.Addins.CecilReflector/AssemblyInfo.cs b/mono-addins/Mono.Addins.CecilReflector/AssemblyInfo.cs new file mode 100644 index 00000000..d2c22cca --- /dev/null +++ b/mono-addins/Mono.Addins.CecilReflector/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("Mono.Addins.CecilReflector")] +[assembly: AssemblyCopyright("Copyright (C) 2007 Novell, Inc (http://www.novell.com)")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.3.7.0")] diff --git a/mono-addins/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector.csproj b/mono-addins/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector.csproj new file mode 100644 index 00000000..2a615236 --- /dev/null +++ b/mono-addins/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector.csproj @@ -0,0 +1,60 @@ + + + + + Debug + AnyCPU + {42D1CE65-A14B-4218-B787-58AD7AA68513} + Library + Mono.Addins.CecilReflector + Mono.Addins.CecilReflector + True + ..\mono-addins.snk + v4.6 + Mono.Addins.CecilReflector + Lluis Sanchez + https://github.com/mono/mono-addins/blob/master/COPYING + https://github.com/mono/mono-addins + Mono.Addins is a framework for creating extensible applications, and for creating add-ins which extend applications. Mono.Addins.Setup provides an API for managing add-ins, creating add-in packages and publishing add-ins in on-line repositories. + 8.0.30703 + 2.0 + + + + True + full + false + ..\bin + READ_ONLY + prompt + 4 + True + False + + + pdbonly + True + ..\bin + READ_ONLY + prompt + 4 + True + False + true + True + + + + + + + {91DD5A2D-9FE3-4C3C-9253-876141874DAD} + Mono.Addins + + + + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector/Reflector.cs b/mono-addins/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector/Reflector.cs new file mode 100644 index 00000000..37f69137 --- /dev/null +++ b/mono-addins/Mono.Addins.CecilReflector/Mono.Addins.CecilReflector/Reflector.cs @@ -0,0 +1,610 @@ +// Reflector.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +//#define ASSEMBLY_LOAD_STATS + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using Mono.Addins; +using Mono.Addins.Database; +using Mono.Cecil; +using CustomAttribute = Mono.Cecil.CustomAttribute; +using MA = Mono.Addins.Database; +using System.Linq; + +namespace Mono.Addins.CecilReflector +{ + public class Reflector: IAssemblyReflector, IDisposable + { + IAssemblyLocator locator; + Dictionary cachedAssemblies = new Dictionary (); + DefaultAssemblyResolver defaultAssemblyResolver; + + public void Initialize (IAssemblyLocator locator) + { + this.locator = locator; + defaultAssemblyResolver = new DefaultAssemblyResolver (); + defaultAssemblyResolver.ResolveFailure += delegate (object sender, AssemblyNameReference reference) { + var file = locator.GetAssemblyLocation (reference.FullName); + if (file != null) + return LoadAssembly (file, true); + else + return null; + }; + } + + public object[] GetCustomAttributes (object obj, Type type, bool inherit) + { + Mono.Cecil.ICustomAttributeProvider aprov = obj as Mono.Cecil.ICustomAttributeProvider; + if (aprov == null) + return new object [0]; + + ArrayList atts = new ArrayList (); + foreach (CustomAttribute att in aprov.CustomAttributes) { + object catt = ConvertAttribute (att, type); + if (catt != null) + atts.Add (catt); + } + if (inherit && (obj is TypeDefinition)) { + TypeDefinition td = (TypeDefinition) obj; + if (td.BaseType != null && td.BaseType.FullName != "System.Object") { + // The base type may be in an assembly that doesn't reference Mono.Addins, even though it may reference + // other assemblies that do reference Mono.Addins. So the Mono.Addins filter can't be applied here. + TypeDefinition bt = FindTypeDefinition (td.Module.Assembly, td.BaseType, assembliesReferencingMonoAddinsOnly: false); + if (bt != null) + atts.AddRange (GetCustomAttributes (bt, type, true)); + } + } + return atts.ToArray (); + } + + object ConvertAttribute (CustomAttribute att, Type expectedType) + { + Type attype = typeof(IAssemblyReflector).Assembly.GetType (att.Constructor.DeclaringType.FullName); + + if (attype == null || !expectedType.IsAssignableFrom (attype)) + return null; + + object ob; + + if (att.ConstructorArguments.Count > 0) { + object[] cargs = new object [att.ConstructorArguments.Count]; + ArrayList typeParameters = null; + + // Constructor parameters of type System.Type can't be set because types from the assembly + // can't be loaded. The parameter value will be set later using a type name property. + for (int n=0; n GetRawCustomAttributes (object obj, Type type, bool inherit) + { + List atts = new List (); + Mono.Cecil.ICustomAttributeProvider aprov = obj as Mono.Cecil.ICustomAttributeProvider; + if (aprov == null) + return atts; + + foreach (CustomAttribute att in aprov.CustomAttributes) { + // The class of the attribute is always a subclass of a Mono.Addins class + MA.CustomAttribute catt = ConvertToRawAttribute (att, type.FullName, baseIsMonoAddinsType: true); + if (catt != null) + atts.Add (catt); + } + if (inherit && (obj is TypeDefinition)) { + TypeDefinition td = (TypeDefinition) obj; + if (td.BaseType != null && td.BaseType.FullName != "System.Object") { + // The base type may be in an assembly that doesn't reference Mono.Addins, even though it may reference + // other assemblies that do reference Mono.Addins. So the Mono.Addins filter can't be applied here. + TypeDefinition bt = FindTypeDefinition (td.Module.Assembly, td.BaseType, assembliesReferencingMonoAddinsOnly: false); + if (bt != null) + atts.AddRange (GetRawCustomAttributes (bt, type, true)); + } + } + return atts; + } + + MA.CustomAttribute ConvertToRawAttribute (CustomAttribute att, string expectedType, bool baseIsMonoAddinsType) + { + // If the class of the attribute is a subclass of a Mono.Addins class, then the assembly where this + // custom attribute type is defined must reference Mono.Addins. + TypeDefinition attType = FindTypeDefinition (att.Constructor.DeclaringType.Module.Assembly, att.Constructor.DeclaringType, assembliesReferencingMonoAddinsOnly: baseIsMonoAddinsType); + + if (attType == null || !TypeIsAssignableFrom (expectedType, attType, baseIsMonoAddinsType)) + return null; + + MA.CustomAttribute mat = new MA.CustomAttribute (); + mat.TypeName = att.Constructor.DeclaringType.FullName; + + if (att.ConstructorArguments.Count > 0) { + var arguments = att.ConstructorArguments; + + MethodReference constructor = FindConstructor (att, attType); + if (constructor == null) + throw new InvalidOperationException ("Custom attribute constructor not found"); + + for (int n=0; n inheritanceChain = null; + + foreach (Mono.Cecil.CustomAttributeNamedArgument namedArgument in att.Properties) { + string pname = namedArgument.Name; + object val = namedArgument.Argument.Value; + if (val == null) + continue; + + if (inheritanceChain == null) + inheritanceChain = GetInheritanceChain (attType, baseIsMonoAddinsType).ToList (); + + foreach (TypeDefinition td in inheritanceChain) { + PropertyDefinition prop = GetMember (td.Properties, pname); + if (prop == null) + continue; + + NodeAttributeAttribute bat = (NodeAttributeAttribute) GetCustomAttribute (prop, typeof(NodeAttributeAttribute), false); + if (bat != null) { + string name = string.IsNullOrEmpty (bat.Name) ? prop.Name : bat.Name; + mat.Add (name, Convert.ToString (val, System.Globalization.CultureInfo.InvariantCulture)); + } + } + } + + foreach (Mono.Cecil.CustomAttributeNamedArgument namedArgument in att.Fields) { + string pname = namedArgument.Name; + object val = namedArgument.Argument.Value; + if (val == null) + continue; + + if (inheritanceChain == null) + inheritanceChain = GetInheritanceChain (attType, baseIsMonoAddinsType).ToList (); + + foreach (TypeDefinition td in inheritanceChain) { + FieldDefinition field = GetMember (td.Fields, pname); + if (field != null) { + NodeAttributeAttribute bat = (NodeAttributeAttribute) GetCustomAttribute (field, typeof(NodeAttributeAttribute), false); + if (bat != null) { + string name = string.IsNullOrEmpty (bat.Name) ? field.Name : bat.Name; + mat.Add (name, Convert.ToString (val, System.Globalization.CultureInfo.InvariantCulture)); + } + } + } + } + + return mat; + } + + static TMember GetMember (ICollection members, string name) where TMember : class, IMemberDefinition + { + foreach (var member in members) + if (member.Name == name) + return member; + + return null; + } + + IEnumerable GetInheritanceChain (TypeDefinition td, bool baseIsMonoAddinsType) + { + yield return td; + while (td != null && td.BaseType != null && td.BaseType.FullName != "System.Object") { + // If the class we are looking for is a subclass of a Mono.Addins class, then the assembly where this + // class is defined must reference Mono.Addins. + td = FindTypeDefinition (td.Module.Assembly, td.BaseType, assembliesReferencingMonoAddinsOnly: baseIsMonoAddinsType); + if (td != null) + yield return td; + } + } + + MethodReference FindConstructor (CustomAttribute att, TypeDefinition atd) + { + // The constructor provided by CustomAttribute.Constructor is lacking some information, such as the parameter + // name and custom attributes. Since we need the full info, we have to look it up in the declaring type. + + foreach (MethodReference met in atd.Methods) { + if (met.Name != ".ctor") + continue; + + if (met.Parameters.Count == att.Constructor.Parameters.Count) { + for (int n = met.Parameters.Count - 1; n >= 0; n--) { + if (met.Parameters[n].ParameterType.FullName != att.Constructor.Parameters[n].ParameterType.FullName) + break; + if (n == 0) + return met; + } + } + } + return null; + } + + public object LoadAssembly (string file) + { + return LoadAssembly (file, true); + } + + public AssemblyDefinition LoadAssembly (string file, bool cache) + { + AssemblyDefinition adef; + if (cachedAssemblies.TryGetValue (file, out adef)) + return adef; + var rp = new ReaderParameters (ReadingMode.Deferred); + rp.AssemblyResolver = defaultAssemblyResolver; + adef = AssemblyDefinition.ReadAssembly (file, rp); + if (adef != null) { + if (cache) + cachedAssemblies [file] = adef; + // Since the assembly is loaded, we can quickly check now if it references Mono.Addins. + // This information may be useful later on. + if (adef.Name.Name != "Mono.Addins" && !adef.MainModule.AssemblyReferences.Any (r => r.Name == "Mono.Addins")) + assembliesNotReferencingMonoAddins.Add (adef.FullName); + } + +#if ASSEMBLY_LOAD_STATS + loadCounter.TryGetValue (file, out int num); + loadCounter [file] = num + 1; +#endif + return adef; + } + +#if ASSEMBLY_LOAD_STATS + static Dictionary loadCounter = new Dictionary (); +#endif + + public void UnloadAssembly (object assembly) + { + var adef = (AssemblyDefinition)assembly; + cachedAssemblies.Remove (adef.MainModule.FileName); + adef.Dispose (); + } + + bool FoundToNotReferenceMonoAddins (AssemblyNameReference aref) + { + // Quick check to find out if an assembly references Mono.Addins, based only on cached information. + return assembliesNotReferencingMonoAddins.Contains (aref.FullName); + } + + bool CheckHasMonoAddinsReference (AssemblyDefinition adef) + { + // Maybe the assembly is already in the blacklist + if (assembliesNotReferencingMonoAddins.Contains (adef.FullName)) + return false; + + if (adef.Name.Name != "Mono.Addins" && !adef.MainModule.AssemblyReferences.Any (r => r.Name == "Mono.Addins")) { + assembliesNotReferencingMonoAddins.Add (adef.FullName); + return false; + } + return true; + } + + HashSet assembliesNotReferencingMonoAddins = new HashSet (StringComparer.Ordinal); + + public string [] GetResourceNames (object asm) + { + AssemblyDefinition adef = (AssemblyDefinition)asm; + List names = new List (adef.MainModule.Resources.Count); + foreach (Resource res in adef.MainModule.Resources) { + if (res is EmbeddedResource) + names.Add (res.Name); + } + return names.ToArray (); + } + + public System.IO.Stream GetResourceStream (object asm, string resourceName) + { + AssemblyDefinition adef = (AssemblyDefinition) asm; + foreach (Resource res in adef.MainModule.Resources) { + EmbeddedResource r = res as EmbeddedResource; + if (r != null && r.Name == resourceName) + return r.GetResourceStream (); + } + throw new InvalidOperationException ("Resource not found: " + resourceName); + } + + public object LoadAssemblyFromReference (object asmReference) + { + // The scanner only uses this method when looking for an extension node type, which is + // a subclass of a Mono.Addins type, so it must be defined in an assembly that references Mono.Addins. + return LoadAssemblyFromReference ((AssemblyNameReference)asmReference, assembliesReferencingMonoAddinsOnly: true); + } + + AssemblyDefinition LoadAssemblyFromReference (AssemblyNameReference aref, bool assembliesReferencingMonoAddinsOnly) + { + // Fast check for Mono.Addins reference that sometimes will avoid loading the assembly + if (assembliesReferencingMonoAddinsOnly && FoundToNotReferenceMonoAddins (aref)) + return null; + + string loc = locator.GetAssemblyLocation (aref.FullName); + if (loc == null) + return null; + + AssemblyDefinition asm = LoadAssembly (loc, true); + + // Check for Mono.Addins references first, that will update the cache. + + if (!CheckHasMonoAddinsReference (asm) && assembliesReferencingMonoAddinsOnly) { + // We loaded an assembly we are not interested in, so we could unload it now. + // We already cached the information about whether it has a Mono.Addins reference + // or not, so the next we try to load the reference, the check can be done + // without loading. However, empirical tests should that the cache size doesn't + // increase much, while redundant assembly loads are significanly reduced. + //UnloadAssembly (asm); + return null; + } + return asm; + } + + public System.Collections.IEnumerable GetAssemblyTypes (object asm) + { + return ((AssemblyDefinition)asm).MainModule.Types; + } + + public System.Collections.IEnumerable GetAssemblyReferences (object asm) + { + return ((AssemblyDefinition)asm).MainModule.AssemblyReferences; + } + + public object GetType (object asm, string typeName) + { + if (typeName.IndexOf ('`') != -1) { + foreach (TypeDefinition td in ((AssemblyDefinition)asm).MainModule.Types) { + if (td.FullName == typeName) { + return td; + } + } + } + TypeDefinition t = ((AssemblyDefinition)asm).MainModule.GetType (typeName); + if (t != null) { + return t; + } else { + return null; + } + } + + public object GetCustomAttribute (object obj, Type type, bool inherit) + { + foreach (object att in GetCustomAttributes (obj, type, inherit)) + if (type.IsInstanceOfType (att)) + return att; + return null; + } + + public string GetTypeName (object type) + { + return ((TypeDefinition)type).Name; + } + + public string GetTypeFullName (object type) + { + return ((TypeDefinition)type).FullName; + } + + public string GetTypeAssemblyQualifiedName (object type) + { + AssemblyDefinition asm = GetAssemblyDefinition ((TypeDefinition)type); + return ((TypeDefinition)type).FullName + ", " + asm.Name.FullName; + } + + AssemblyDefinition GetAssemblyDefinition (TypeDefinition t) + { + return t.Module.Assembly; + } + + public System.Collections.IEnumerable GetBaseTypeFullNameList (object type) + { + // The base type can be any type, so we can apply the Mono.Addins optimization in this case. + return GetBaseTypeFullNameList ((TypeDefinition)type, baseIsMonoAddinsType: false, includeInterfaces: true); + } + + public System.Collections.IEnumerable GetBaseTypeFullNameList (TypeDefinition type, bool baseIsMonoAddinsType, bool includeInterfaces) + { + AssemblyDefinition asm = GetAssemblyDefinition (type); + + ArrayList list = new ArrayList (); + Hashtable visited = new Hashtable (); + GetBaseTypeFullNameList (visited, list, asm, type, baseIsMonoAddinsType, includeInterfaces); + list.Remove (type.FullName); + return list; + } + + void GetBaseTypeFullNameList (Hashtable visited, ArrayList list, AssemblyDefinition asm, TypeReference tr, bool baseIsMonoAddinsType, bool includeInterfaces) + { + if (tr.FullName == "System.Object" || visited.Contains (tr.FullName)) + return; + + visited [tr.FullName] = tr; + list.Add (tr.FullName); + + TypeDefinition type = FindTypeDefinition (asm, tr, assembliesReferencingMonoAddinsOnly: baseIsMonoAddinsType); + if (type == null) + return; + + asm = GetAssemblyDefinition (type); + + if (type.BaseType != null) + GetBaseTypeFullNameList (visited, list, asm, type.BaseType, baseIsMonoAddinsType, includeInterfaces); + + if (includeInterfaces) { + foreach (InterfaceImplementation ii in type.Interfaces) { + TypeReference interf = ii.InterfaceType; + GetBaseTypeFullNameList (visited, list, asm, interf, baseIsMonoAddinsType, includeInterfaces); + } + } + } + + TypeDefinition FindTypeDefinition (AssemblyDefinition referencer, TypeReference rt, bool assembliesReferencingMonoAddinsOnly) + { + if (rt is TypeDefinition) + return (TypeDefinition)rt; + + string name = rt.FullName; + TypeDefinition td = GetType (referencer, name) as TypeDefinition; + if (td != null) + return td; + int i = name.IndexOf ('<'); + if (i != -1) { + name = name.Substring (0, i); + td = GetType (referencer, name) as TypeDefinition; + if (td != null) + return td; + } + + foreach (AssemblyNameReference aref in referencer.MainModule.AssemblyReferences) { + try { + AssemblyDefinition asm = LoadAssemblyFromReference (aref, assembliesReferencingMonoAddinsOnly); + if (asm == null) + continue; + + td = GetType (asm, name) as TypeDefinition; + if (td != null) + return td; + } catch { + Console.WriteLine ("Could not scan dependency '{0}'. Ignoring for now.", aref.FullName); + } + } + return null; + } + + public bool TypeIsAssignableFrom (object baseType, object type) + { + string baseName = ((TypeDefinition)baseType).FullName; + foreach (string bt in GetBaseTypeFullNameList (type)) + if (bt == baseName) + return true; + return false; + } + + public bool TypeIsAssignableFrom (string baseTypeName, object type, bool baseIsMonoAddinsClass) + { + // If the base is a Mono.Addins class then there is no need to include interfaces when getting + // the base type list (since we are looking for a class, not for an interface). + foreach (string bt in GetBaseTypeFullNameList ((TypeDefinition)type, baseIsMonoAddinsClass, !baseIsMonoAddinsClass)) + if (bt == baseTypeName) + return true; + return false; + } + + public IEnumerable GetFields (object type) + { + return ((TypeDefinition)type).Fields; + } + + public string GetFieldName (object field) + { + return ((FieldDefinition)field).Name; + } + + public string GetFieldTypeFullName (object field) + { + return ((FieldDefinition)field).FieldType.FullName; + } + + public void Dispose () + { + foreach (AssemblyDefinition asm in cachedAssemblies.Values) + asm.Dispose (); + +#if ASSEMBLY_LOAD_STATS + Console.WriteLine ("Total assemblies: " + loadCounter.Count); + Console.WriteLine ("Assembly cache size: {0} ({1}%)", cachedAssemblies.Count, (cachedAssemblies.Count * 100) / loadCounter.Count); + + Console.WriteLine ("Total assembly loads: " + loadCounter.Values.Sum ()); + var redundant = loadCounter.Where (c => c.Value > 1).Select (c => c.Value - 1).Sum (); + Console.WriteLine ("Redundant loads: {0} ({1}%)", redundant, ((redundant * 100) / loadCounter.Count)); +#endif + } + } +} diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Debug/Mono.Addins.CecilReflector.csproj.CoreCompileInputs.cache b/mono-addins/Mono.Addins.CecilReflector/obj/Debug/Mono.Addins.CecilReflector.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..79ebf467 --- /dev/null +++ b/mono-addins/Mono.Addins.CecilReflector/obj/Debug/Mono.Addins.CecilReflector.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +35e0fe8e07ee88ca3f686eaab0c228bcbfb9a913 diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.CecilReflector/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.CecilReflector/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.CecilReflector/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Mono.Addins.CecilReflector.csproj.nuget.g.props b/mono-addins/Mono.Addins.CecilReflector/obj/Mono.Addins.CecilReflector.csproj.nuget.g.props new file mode 100644 index 00000000..e522c5a2 --- /dev/null +++ b/mono-addins/Mono.Addins.CecilReflector/obj/Mono.Addins.CecilReflector.csproj.nuget.g.props @@ -0,0 +1,18 @@ + + + + True + NuGet + D:\opensim\MonoAddins\mono-addins\Mono.Addins.CecilReflector\obj\project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\ld\.nuget\packages\ + PackageReference + 4.9.3 + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Mono.Addins.CecilReflector.csproj.nuget.g.targets b/mono-addins/Mono.Addins.CecilReflector/obj/Mono.Addins.CecilReflector.csproj.nuget.g.targets new file mode 100644 index 00000000..f9da2a43 --- /dev/null +++ b/mono-addins/Mono.Addins.CecilReflector/obj/Mono.Addins.CecilReflector.csproj.nuget.g.targets @@ -0,0 +1,9 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/mono-addins/Mono.Addins.CecilReflector/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 00000000..53ecc9f7 Binary files /dev/null and b/mono-addins/Mono.Addins.CecilReflector/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.CecilReflector/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.CecilReflector/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.CecilReflector/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.CecilReflector/obj/project.assets.json b/mono-addins/Mono.Addins.CecilReflector/obj/project.assets.json new file mode 100644 index 00000000..ed8e5448 --- /dev/null +++ b/mono-addins/Mono.Addins.CecilReflector/obj/project.assets.json @@ -0,0 +1,286 @@ +{ + "version": 3, + "targets": { + ".NETFramework,Version=v4.6": { + "Mono.Cecil/0.10.0-beta6": { + "type": "package", + "compile": { + "lib/net40/Mono.Cecil.Mdb.dll": {}, + "lib/net40/Mono.Cecil.Pdb.dll": {}, + "lib/net40/Mono.Cecil.Rocks.dll": {}, + "lib/net40/Mono.Cecil.dll": {} + }, + "runtime": { + "lib/net40/Mono.Cecil.Mdb.dll": {}, + "lib/net40/Mono.Cecil.Pdb.dll": {}, + "lib/net40/Mono.Cecil.Rocks.dll": {}, + "lib/net40/Mono.Cecil.dll": {} + } + }, + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + }, + "Mono.Addins/1.3.7": { + "type": "project", + "framework": ".NETFramework,Version=v4.6", + "dependencies": { + "NuGet.Build.Packaging": "0.2.0" + }, + "compile": { + "bin/placeholder/Mono.Addins.dll": {} + }, + "runtime": { + "bin/placeholder/Mono.Addins.dll": {} + } + } + }, + ".NETFramework,Version=v4.6/win": { + "Mono.Cecil/0.10.0-beta6": { + "type": "package", + "compile": { + "lib/net40/Mono.Cecil.Mdb.dll": {}, + "lib/net40/Mono.Cecil.Pdb.dll": {}, + "lib/net40/Mono.Cecil.Rocks.dll": {}, + "lib/net40/Mono.Cecil.dll": {} + }, + "runtime": { + "lib/net40/Mono.Cecil.Mdb.dll": {}, + "lib/net40/Mono.Cecil.Pdb.dll": {}, + "lib/net40/Mono.Cecil.Rocks.dll": {}, + "lib/net40/Mono.Cecil.dll": {} + } + }, + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + }, + "Mono.Addins/1.3.7": { + "type": "project", + "framework": ".NETFramework,Version=v4.6", + "dependencies": { + "NuGet.Build.Packaging": "0.2.0" + }, + "compile": { + "bin/placeholder/Mono.Addins.dll": {} + }, + "runtime": { + "bin/placeholder/Mono.Addins.dll": {} + } + } + }, + ".NETFramework,Version=v4.6/win-x64": { + "Mono.Cecil/0.10.0-beta6": { + "type": "package", + "compile": { + "lib/net40/Mono.Cecil.Mdb.dll": {}, + "lib/net40/Mono.Cecil.Pdb.dll": {}, + "lib/net40/Mono.Cecil.Rocks.dll": {}, + "lib/net40/Mono.Cecil.dll": {} + }, + "runtime": { + "lib/net40/Mono.Cecil.Mdb.dll": {}, + "lib/net40/Mono.Cecil.Pdb.dll": {}, + "lib/net40/Mono.Cecil.Rocks.dll": {}, + "lib/net40/Mono.Cecil.dll": {} + } + }, + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + }, + "Mono.Addins/1.3.7": { + "type": "project", + "framework": ".NETFramework,Version=v4.6", + "dependencies": { + "NuGet.Build.Packaging": "0.2.0" + }, + "compile": { + "bin/placeholder/Mono.Addins.dll": {} + }, + "runtime": { + "bin/placeholder/Mono.Addins.dll": {} + } + } + }, + ".NETFramework,Version=v4.6/win-x86": { + "Mono.Cecil/0.10.0-beta6": { + "type": "package", + "compile": { + "lib/net40/Mono.Cecil.Mdb.dll": {}, + "lib/net40/Mono.Cecil.Pdb.dll": {}, + "lib/net40/Mono.Cecil.Rocks.dll": {}, + "lib/net40/Mono.Cecil.dll": {} + }, + "runtime": { + "lib/net40/Mono.Cecil.Mdb.dll": {}, + "lib/net40/Mono.Cecil.Pdb.dll": {}, + "lib/net40/Mono.Cecil.Rocks.dll": {}, + "lib/net40/Mono.Cecil.dll": {} + } + }, + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + }, + "Mono.Addins/1.3.7": { + "type": "project", + "framework": ".NETFramework,Version=v4.6", + "dependencies": { + "NuGet.Build.Packaging": "0.2.0" + }, + "compile": { + "bin/placeholder/Mono.Addins.dll": {} + }, + "runtime": { + "bin/placeholder/Mono.Addins.dll": {} + } + } + } + }, + "libraries": { + "Mono.Cecil/0.10.0-beta6": { + "sha512": "AGOyahYE6CGh3dLz/mz6cnj8hABoJm2KcMRHSHMrW75WHgU5YB+5PSQTVWsmQk9RFC4HAGr5ZzLMFFXsbmTi1w==", + "type": "package", + "path": "mono.cecil/0.10.0-beta6", + "files": [ + "lib/net35/Mono.Cecil.Mdb.dll", + "lib/net35/Mono.Cecil.Pdb.dll", + "lib/net35/Mono.Cecil.Rocks.dll", + "lib/net35/Mono.Cecil.dll", + "lib/net40/Mono.Cecil.Mdb.dll", + "lib/net40/Mono.Cecil.Pdb.dll", + "lib/net40/Mono.Cecil.Rocks.dll", + "lib/net40/Mono.Cecil.dll", + "lib/netstandard1.3/Mono.Cecil.Mdb.dll", + "lib/netstandard1.3/Mono.Cecil.Pdb.dll", + "lib/netstandard1.3/Mono.Cecil.Rocks.dll", + "lib/netstandard1.3/Mono.Cecil.dll", + "mono.cecil.0.10.0-beta6.nupkg.sha512", + "mono.cecil.nuspec" + ] + }, + "NuGet.Build.Packaging/0.2.0": { + "sha512": "iqo7f9c+oA12IcelLjD232BMxdGR2Dzrqk00C8w7NiB1sbXa8YHsZGCJkt6CEQKR5XYqZ/8f3z0kHTBJ0Gua3Q==", + "type": "package", + "path": "nuget.build.packaging/0.2.0", + "files": [ + "build/ApiIntersect.exe", + "build/ApiIntersect.exe.config", + "build/GenerateReferenceAssembly.csproj", + "build/ICSharpCode.Decompiler.dll", + "build/ICSharpCode.NRefactory.CSharp.dll", + "build/ICSharpCode.NRefactory.Cecil.dll", + "build/ICSharpCode.NRefactory.Xml.dll", + "build/ICSharpCode.NRefactory.dll", + "build/Mono.Cecil.Mdb.dll", + "build/Mono.Cecil.Pdb.dll", + "build/Mono.Cecil.Rocks.dll", + "build/Mono.Cecil.dll", + "build/Mono.Options.dll", + "build/NuGet.Build.Packaging.Authoring.props", + "build/NuGet.Build.Packaging.Authoring.targets", + "build/NuGet.Build.Packaging.Compatibility.props", + "build/NuGet.Build.Packaging.CrossTargeting.targets", + "build/NuGet.Build.Packaging.Inference.targets", + "build/NuGet.Build.Packaging.Legacy.props", + "build/NuGet.Build.Packaging.Legacy.targets", + "build/NuGet.Build.Packaging.ReferenceAssembly.targets", + "build/NuGet.Build.Packaging.Tasks.dll", + "build/NuGet.Build.Packaging.Tasks.pdb", + "build/NuGet.Build.Packaging.Version.props", + "build/NuGet.Build.Packaging.props", + "build/NuGet.Build.Packaging.targets", + "nuget.build.packaging.0.2.0.nupkg.sha512", + "nuget.build.packaging.nuspec" + ] + }, + "Mono.Addins/1.3.7": { + "type": "project", + "path": "../Mono.Addins/Mono.Addins.csproj", + "msbuildProject": "../Mono.Addins/Mono.Addins.csproj" + } + }, + "projectFileDependencyGroups": { + ".NETFramework,Version=v4.6": [ + "Mono.Addins >= 1.3.7", + "Mono.Cecil >= 0.10.0-beta6", + "NuGet.Build.Packaging >= 0.2.0" + ] + }, + "packageFolders": { + "C:\\Users\\ld\\.nuget\\packages\\": {} + }, + "project": { + "version": "1.3.7", + "restore": { + "projectUniqueName": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins.CecilReflector\\Mono.Addins.CecilReflector.csproj", + "projectName": "Mono.Addins.CecilReflector", + "projectPath": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins.CecilReflector\\Mono.Addins.CecilReflector.csproj", + "packagesPath": "C:\\Users\\ld\\.nuget\\packages\\", + "outputPath": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins.CecilReflector\\obj\\", + "projectStyle": "PackageReference", + "skipContentFileWrite": true, + "configFilePaths": [ + "C:\\Users\\ld\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net46" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Users\\ld\\AppData\\Local\\Xenko\\NugetDev": {}, + "D:\\xenko\\xenko\\bin\\packages": {}, + "https://api.nuget.org/v3/index.json": {}, + "https://packages.xenko.com/nuget": {} + }, + "frameworks": { + "net46": { + "projectReferences": { + "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins\\Mono.Addins.csproj": { + "projectPath": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins\\Mono.Addins.csproj" + } + } + } + } + }, + "frameworks": { + "net46": { + "dependencies": { + "Mono.Cecil": { + "target": "Package", + "version": "[0.10.0-beta6, )" + }, + "NuGet.Build.Packaging": { + "target": "Package", + "version": "[0.2.0, )" + } + } + } + }, + "runtimes": { + "win": { + "#import": [] + }, + "win-x64": { + "#import": [] + }, + "win-x86": { + "#import": [] + } + } + } +} \ No newline at end of file diff --git a/mono-addins/Mono.Addins.Gui/AssemblyInfo.cs b/mono-addins/Mono.Addins.Gui/AssemblyInfo.cs new file mode 100644 index 00000000..0337dac3 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("Mono.Addins.Gui")] +[assembly: AssemblyCopyright("Copyright (C) 2007 Novell, Inc (http://www.novell.com)")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.3.7.0")] diff --git a/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinInfoView.cs b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinInfoView.cs new file mode 100644 index 00000000..d6142db7 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinInfoView.cs @@ -0,0 +1,270 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal partial class AddinInfoView + { + private global::Gtk.EventBox ebox; + private global::Gtk.VBox vbox6; + private global::Gtk.EventBox boxHeader; + private global::Gtk.HBox hbox2; + private global::Gtk.Image imageHeader; + private global::Gtk.Label labelHeader; + private global::Gtk.VBox vbox3; + private global::Gtk.HBox headerBox; + private global::Gtk.HBox boxTitle; + private global::Gtk.VBox vbox4; + private global::Gtk.Label labelName; + private global::Gtk.Label labelVersion; + private global::Gtk.ScrolledWindow scrolledwindow; + private global::Gtk.EventBox ebox2; + private global::Gtk.VBox vboxDesc; + private global::Gtk.Label labelDesc; + private global::Gtk.HBox hbox3; + private global::Gtk.Button urlButton; + private global::Gtk.EventBox eboxButs; + private global::Gtk.HBox hbox1; + private global::Gtk.Button btnInstall; + private global::Gtk.Button btnUpdate; + private global::Gtk.Button btnDisable; + private global::Gtk.Button btnUninstall; + + protected virtual void Build () + { + Gui.Initialize (this); + // Widget Mono.Addins.Gui.AddinInfoView + BinContainer.Attach (this); + this.Name = "Mono.Addins.Gui.AddinInfoView"; + // Container child Mono.Addins.Gui.AddinInfoView.Gtk.Container+ContainerChild + this.ebox = new global::Gtk.EventBox (); + this.ebox.Name = "ebox"; + // Container child ebox.Gtk.Container+ContainerChild + this.vbox6 = new global::Gtk.VBox (); + this.vbox6.Name = "vbox6"; + // Container child vbox6.Gtk.Box+BoxChild + this.boxHeader = new global::Gtk.EventBox (); + this.boxHeader.Name = "boxHeader"; + // Container child boxHeader.Gtk.Container+ContainerChild + this.hbox2 = new global::Gtk.HBox (); + this.hbox2.Name = "hbox2"; + this.hbox2.Spacing = 6; + // Container child hbox2.Gtk.Box+BoxChild + this.imageHeader = new global::Gtk.Image (); + this.imageHeader.Name = "imageHeader"; + this.imageHeader.Pixbuf = IconLoader.LoadIcon (this, "gtk-dialog-warning", global::Gtk.IconSize.Menu); + this.hbox2.Add (this.imageHeader); + global::Gtk.Box.BoxChild w1 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.imageHeader])); + w1.Position = 0; + w1.Expand = false; + w1.Fill = false; + // Container child hbox2.Gtk.Box+BoxChild + this.labelHeader = new global::Gtk.Label (); + this.labelHeader.WidthRequest = 250; + this.labelHeader.Name = "labelHeader"; + this.labelHeader.Xalign = 0F; + this.labelHeader.LabelProp = global::Mono.Unix.Catalog.GetString ("label1"); + this.labelHeader.Wrap = true; + this.hbox2.Add (this.labelHeader); + global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.labelHeader])); + w2.Position = 1; + w2.Expand = false; + w2.Fill = false; + this.boxHeader.Add (this.hbox2); + this.vbox6.Add (this.boxHeader); + global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox6 [this.boxHeader])); + w4.Position = 0; + w4.Expand = false; + w4.Fill = false; + // Container child vbox6.Gtk.Box+BoxChild + this.vbox3 = new global::Gtk.VBox (); + this.vbox3.Name = "vbox3"; + this.vbox3.Spacing = 6; + this.vbox3.BorderWidth = ((uint)(12)); + // Container child vbox3.Gtk.Box+BoxChild + this.headerBox = new global::Gtk.HBox (); + this.headerBox.Name = "headerBox"; + this.headerBox.Spacing = 6; + // Container child headerBox.Gtk.Box+BoxChild + this.boxTitle = new global::Gtk.HBox (); + this.boxTitle.Name = "boxTitle"; + this.boxTitle.Spacing = 6; + // Container child boxTitle.Gtk.Box+BoxChild + this.vbox4 = new global::Gtk.VBox (); + this.vbox4.Name = "vbox4"; + this.vbox4.Spacing = 3; + // Container child vbox4.Gtk.Box+BoxChild + this.labelName = new global::Gtk.Label (); + this.labelName.WidthRequest = 280; + this.labelName.Name = "labelName"; + this.labelName.Xalign = 0F; + this.labelName.LabelProp = global::Mono.Unix.Catalog.GetString ("Some Addin"); + this.labelName.UseMarkup = true; + this.labelName.Wrap = true; + this.vbox4.Add (this.labelName); + global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox4 [this.labelName])); + w5.Position = 0; + w5.Expand = false; + w5.Fill = false; + // Container child vbox4.Gtk.Box+BoxChild + this.labelVersion = new global::Gtk.Label (); + this.labelVersion.WidthRequest = 280; + this.labelVersion.Name = "labelVersion"; + this.labelVersion.Xalign = 0F; + this.labelVersion.LabelProp = global::Mono.Unix.Catalog.GetString ("Version 2.6"); + this.labelVersion.Wrap = true; + this.vbox4.Add (this.labelVersion); + global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox4 [this.labelVersion])); + w6.Position = 1; + w6.Expand = false; + w6.Fill = false; + this.boxTitle.Add (this.vbox4); + global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.boxTitle [this.vbox4])); + w7.Position = 0; + w7.Expand = false; + w7.Fill = false; + this.headerBox.Add (this.boxTitle); + global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.headerBox [this.boxTitle])); + w8.Position = 0; + this.vbox3.Add (this.headerBox); + global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.headerBox])); + w9.Position = 0; + w9.Expand = false; + w9.Fill = false; + // Container child vbox3.Gtk.Box+BoxChild + this.scrolledwindow = new global::Gtk.ScrolledWindow (); + this.scrolledwindow.CanFocus = true; + this.scrolledwindow.Name = "scrolledwindow"; + this.scrolledwindow.HscrollbarPolicy = ((global::Gtk.PolicyType)(2)); + // Container child scrolledwindow.Gtk.Container+ContainerChild + global::Gtk.Viewport w10 = new global::Gtk.Viewport (); + w10.ShadowType = ((global::Gtk.ShadowType)(0)); + // Container child GtkViewport.Gtk.Container+ContainerChild + this.ebox2 = new global::Gtk.EventBox (); + this.ebox2.Name = "ebox2"; + // Container child ebox2.Gtk.Container+ContainerChild + this.vboxDesc = new global::Gtk.VBox (); + this.vboxDesc.Name = "vboxDesc"; + this.vboxDesc.Spacing = 6; + // Container child vboxDesc.Gtk.Box+BoxChild + this.labelDesc = new global::Gtk.Label (); + this.labelDesc.WidthRequest = 250; + this.labelDesc.Name = "labelDesc"; + this.labelDesc.Xalign = 0F; + this.labelDesc.LabelProp = global::Mono.Unix.Catalog.GetString ("Long description of the extension. Long description of the extension. Long description of the extension. Long description of the extension. Long description of the extension. Long description of the extension. "); + this.labelDesc.Wrap = true; + this.vboxDesc.Add (this.labelDesc); + global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.vboxDesc [this.labelDesc])); + w11.Position = 0; + w11.Expand = false; + w11.Fill = false; + // Container child vboxDesc.Gtk.Box+BoxChild + this.hbox3 = new global::Gtk.HBox (); + this.hbox3.Name = "hbox3"; + this.hbox3.Spacing = 6; + // Container child hbox3.Gtk.Box+BoxChild + this.urlButton = new global::Gtk.Button (); + this.urlButton.CanFocus = true; + this.urlButton.Name = "urlButton"; + this.urlButton.UseUnderline = true; + this.urlButton.Relief = ((global::Gtk.ReliefStyle)(2)); + this.urlButton.Label = global::Mono.Unix.Catalog.GetString ("More information"); + global::Gtk.Image w12 = new global::Gtk.Image (); + w12.Pixbuf = global::Gdk.Pixbuf.LoadFromResource ("web-search-16.png"); + this.urlButton.Image = w12; + this.hbox3.Add (this.urlButton); + global::Gtk.Box.BoxChild w13 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.urlButton])); + w13.Position = 0; + w13.Expand = false; + w13.Fill = false; + this.vboxDesc.Add (this.hbox3); + global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.vboxDesc [this.hbox3])); + w14.PackType = ((global::Gtk.PackType)(1)); + w14.Position = 2; + w14.Expand = false; + w14.Fill = false; + this.ebox2.Add (this.vboxDesc); + w10.Add (this.ebox2); + this.scrolledwindow.Add (w10); + this.vbox3.Add (this.scrolledwindow); + global::Gtk.Box.BoxChild w18 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.scrolledwindow])); + w18.Position = 1; + this.vbox6.Add (this.vbox3); + global::Gtk.Box.BoxChild w19 = ((global::Gtk.Box.BoxChild)(this.vbox6 [this.vbox3])); + w19.Position = 1; + // Container child vbox6.Gtk.Box+BoxChild + this.eboxButs = new global::Gtk.EventBox (); + this.eboxButs.Name = "eboxButs"; + // Container child eboxButs.Gtk.Container+ContainerChild + this.hbox1 = new global::Gtk.HBox (); + this.hbox1.Name = "hbox1"; + this.hbox1.Spacing = 6; + // Container child hbox1.Gtk.Box+BoxChild + this.btnInstall = new global::Gtk.Button (); + this.btnInstall.CanFocus = true; + this.btnInstall.Name = "btnInstall"; + this.btnInstall.UseUnderline = true; + this.btnInstall.Label = global::Mono.Unix.Catalog.GetString ("Install..."); + global::Gtk.Image w20 = new global::Gtk.Image (); + w20.Pixbuf = global::Gdk.Pixbuf.LoadFromResource ("download-16.png"); + this.btnInstall.Image = w20; + this.hbox1.Add (this.btnInstall); + global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.btnInstall])); + w21.Position = 0; + w21.Expand = false; + w21.Fill = false; + // Container child hbox1.Gtk.Box+BoxChild + this.btnUpdate = new global::Gtk.Button (); + this.btnUpdate.CanFocus = true; + this.btnUpdate.Name = "btnUpdate"; + this.btnUpdate.UseUnderline = true; + this.btnUpdate.Label = global::Mono.Unix.Catalog.GetString ("Update"); + global::Gtk.Image w22 = new global::Gtk.Image (); + w22.Pixbuf = global::Gdk.Pixbuf.LoadFromResource ("download-16.png"); + this.btnUpdate.Image = w22; + this.hbox1.Add (this.btnUpdate); + global::Gtk.Box.BoxChild w23 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.btnUpdate])); + w23.Position = 1; + w23.Expand = false; + w23.Fill = false; + // Container child hbox1.Gtk.Box+BoxChild + this.btnDisable = new global::Gtk.Button (); + this.btnDisable.CanFocus = true; + this.btnDisable.Name = "btnDisable"; + this.btnDisable.UseUnderline = true; + this.btnDisable.Label = global::Mono.Unix.Catalog.GetString ("Disable"); + this.hbox1.Add (this.btnDisable); + global::Gtk.Box.BoxChild w24 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.btnDisable])); + w24.Position = 2; + w24.Expand = false; + w24.Fill = false; + // Container child hbox1.Gtk.Box+BoxChild + this.btnUninstall = new global::Gtk.Button (); + this.btnUninstall.CanFocus = true; + this.btnUninstall.Name = "btnUninstall"; + this.btnUninstall.UseUnderline = true; + this.btnUninstall.Label = global::Mono.Unix.Catalog.GetString ("_Uninstall..."); + this.hbox1.Add (this.btnUninstall); + global::Gtk.Box.BoxChild w25 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.btnUninstall])); + w25.Position = 3; + w25.Expand = false; + w25.Fill = false; + this.eboxButs.Add (this.hbox1); + this.vbox6.Add (this.eboxButs); + global::Gtk.Box.BoxChild w27 = ((global::Gtk.Box.BoxChild)(this.vbox6 [this.eboxButs])); + w27.Position = 2; + w27.Expand = false; + w27.Fill = false; + this.ebox.Add (this.vbox6); + this.Add (this.ebox); + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.Hide (); + this.urlButton.Clicked += new global::System.EventHandler (this.OnUrlButtonClicked); + this.btnInstall.Clicked += new global::System.EventHandler (this.OnBtnInstallClicked); + this.btnUpdate.Clicked += new global::System.EventHandler (this.OnBtnUpdateClicked); + this.btnDisable.Clicked += new global::System.EventHandler (this.OnBtnDisableClicked); + this.btnUninstall.Clicked += new global::System.EventHandler (this.OnBtnUninstallClicked); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinInstallerDialog.cs b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinInstallerDialog.cs new file mode 100644 index 00000000..bc3dc3fa --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinInstallerDialog.cs @@ -0,0 +1,128 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal partial class AddinInstallerDialog + { + private global::Gtk.VBox vbox2; + private global::Gtk.Label label1; + private global::Gtk.Label label2; + private global::Gtk.ScrolledWindow scrolledwindow1; + private global::Gtk.Label addinList; + private global::Gtk.ProgressBar progressBar; + private global::Gtk.Button buttonCancel; + private global::Gtk.Button buttonOk; + + protected virtual void Build () + { + Gui.Initialize (this); + // Widget Mono.Addins.Gui.AddinInstallerDialog + this.Name = "Mono.Addins.Gui.AddinInstallerDialog"; + this.Title = global::Mono.Unix.Catalog.GetString ("Extension Manager"); + this.WindowPosition = ((global::Gtk.WindowPosition)(4)); + this.BorderWidth = ((uint)(6)); + // Internal child Mono.Addins.Gui.AddinInstallerDialog.VBox + global::Gtk.VBox w1 = this.VBox; + w1.Name = "dialog1_VBox"; + w1.Spacing = 6; + w1.BorderWidth = ((uint)(2)); + // Container child dialog1_VBox.Gtk.Box+BoxChild + this.vbox2 = new global::Gtk.VBox (); + this.vbox2.Name = "vbox2"; + this.vbox2.Spacing = 6; + this.vbox2.BorderWidth = ((uint)(6)); + // Container child vbox2.Gtk.Box+BoxChild + this.label1 = new global::Gtk.Label (); + this.label1.Name = "label1"; + this.label1.Xalign = 0F; + this.label1.LabelProp = global::Mono.Unix.Catalog.GetString ("Additional extension packages are required to perform this operation."); + this.vbox2.Add (this.label1); + global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.label1])); + w2.Position = 0; + w2.Expand = false; + w2.Fill = false; + // Container child vbox2.Gtk.Box+BoxChild + this.label2 = new global::Gtk.Label (); + this.label2.Name = "label2"; + this.label2.Xalign = 0F; + this.label2.LabelProp = global::Mono.Unix.Catalog.GetString ("The following extension packages will be installed:"); + this.vbox2.Add (this.label2); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.label2])); + w3.Position = 1; + w3.Expand = false; + w3.Fill = false; + // Container child vbox2.Gtk.Box+BoxChild + this.scrolledwindow1 = new global::Gtk.ScrolledWindow (); + this.scrolledwindow1.CanFocus = true; + this.scrolledwindow1.Name = "scrolledwindow1"; + this.scrolledwindow1.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledwindow1.Gtk.Container+ContainerChild + global::Gtk.Viewport w4 = new global::Gtk.Viewport (); + w4.ShadowType = ((global::Gtk.ShadowType)(0)); + // Container child GtkViewport.Gtk.Container+ContainerChild + this.addinList = new global::Gtk.Label (); + this.addinList.Name = "addinList"; + this.addinList.Xpad = 6; + this.addinList.Ypad = 6; + this.addinList.Xalign = 0F; + this.addinList.Yalign = 0F; + this.addinList.LabelProp = "label3"; + w4.Add (this.addinList); + this.scrolledwindow1.Add (w4); + this.vbox2.Add (this.scrolledwindow1); + global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.scrolledwindow1])); + w7.Position = 2; + // Container child vbox2.Gtk.Box+BoxChild + this.progressBar = new global::Gtk.ProgressBar (); + this.progressBar.Name = "progressBar"; + this.progressBar.Text = ""; + this.vbox2.Add (this.progressBar); + global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.progressBar])); + w8.Position = 3; + w8.Expand = false; + w8.Fill = false; + w1.Add (this.vbox2); + global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(w1 [this.vbox2])); + w9.Position = 0; + // Internal child Mono.Addins.Gui.AddinInstallerDialog.ActionArea + global::Gtk.HButtonBox w10 = this.ActionArea; + w10.Name = "dialog1_ActionArea"; + w10.Spacing = 10; + w10.BorderWidth = ((uint)(6)); + w10.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(4)); + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.buttonCancel = new global::Gtk.Button (); + this.buttonCancel.CanDefault = true; + this.buttonCancel.CanFocus = true; + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.UseStock = true; + this.buttonCancel.UseUnderline = true; + this.buttonCancel.Label = "gtk-cancel"; + this.AddActionWidget (this.buttonCancel, -6); + global::Gtk.ButtonBox.ButtonBoxChild w11 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w10 [this.buttonCancel])); + w11.Expand = false; + w11.Fill = false; + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.buttonOk = new global::Gtk.Button (); + this.buttonOk.CanDefault = true; + this.buttonOk.CanFocus = true; + this.buttonOk.Name = "buttonOk"; + this.buttonOk.UseStock = true; + this.buttonOk.UseUnderline = true; + this.buttonOk.Label = "gtk-ok"; + w10.Add (this.buttonOk); + global::Gtk.ButtonBox.ButtonBoxChild w12 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w10 [this.buttonOk])); + w12.Position = 1; + w12.Expand = false; + w12.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.DefaultWidth = 593; + this.DefaultHeight = 433; + this.progressBar.Hide (); + this.Show (); + this.buttonOk.Clicked += new global::System.EventHandler (this.OnButtonOkClicked); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinManagerDialog.cs b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinManagerDialog.cs new file mode 100644 index 00000000..073f95b1 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.AddinManagerDialog.cs @@ -0,0 +1,381 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal partial class AddinManagerDialog + { + private global::Gtk.VBox vbox93; + private global::Gtk.HBox hbox65; + private global::Gtk.HBox hbox72; + private global::Gtk.Notebook notebook; + private global::Gtk.HBox hbox2; + private global::Gtk.VBox vbox6; + private global::Gtk.ScrolledWindow scrolledwindow13; + private global::Gtk.TreeView addinTree; + private global::Gtk.EventBox eventbox2; + private global::Mono.Addins.Gui.AddinInfoView addininfoInstalled; + private global::Gtk.Label label7; + private global::Gtk.HBox boxUpdates; + private global::Gtk.VBox vboxUpdates; + private global::Gtk.EventBox eboxRepoUpdates; + private global::Gtk.HBox hbox67; + private global::Gtk.Label labelUpdates; + private global::Gtk.Button buttonRefreshUpdates; + private global::Gtk.Button buttonUpdateAll; + private global::Gtk.ScrolledWindow scrolledUpdates; + private global::Gtk.TreeView updatesTreeView; + private global::Gtk.EventBox eventbox3; + private global::Mono.Addins.Gui.AddinInfoView addininfoUpdates; + private global::Gtk.Label label4; + private global::Gtk.HBox hbox8; + private global::Gtk.VBox vboxGallery; + private global::Gtk.EventBox eboxRepo; + private global::Gtk.HBox hbox66; + private global::Gtk.Label label112; + private global::Gtk.ComboBox repoCombo; + private global::Gtk.Button buttonRefresh; + private global::Gtk.ScrolledWindow scrolledGallery; + private global::Gtk.TreeView galleryTreeView; + private global::Gtk.EventBox eventbox1; + private global::Mono.Addins.Gui.AddinInfoView addininfoGallery; + private global::Gtk.Label label8; + private global::Gtk.Button buttonInstallFromFile; + private global::Gtk.Button btnClose; + + protected virtual void Build () + { + Gui.Initialize (this); + // Widget Mono.Addins.Gui.AddinManagerDialog + this.Name = "Mono.Addins.Gui.AddinManagerDialog"; + this.Title = global::Mono.Unix.Catalog.GetString ("Extension Manager"); + this.TypeHint = ((global::Gdk.WindowTypeHint)(1)); + this.BorderWidth = ((uint)(6)); + this.DefaultWidth = 700; + this.DefaultHeight = 550; + // Internal child Mono.Addins.Gui.AddinManagerDialog.VBox + global::Gtk.VBox w1 = this.VBox; + w1.Name = "dialog-vbox8"; + w1.Spacing = 3; + w1.BorderWidth = ((uint)(2)); + // Container child dialog-vbox8.Gtk.Box+BoxChild + this.vbox93 = new global::Gtk.VBox (); + this.vbox93.Name = "vbox93"; + this.vbox93.Spacing = 6; + this.vbox93.BorderWidth = ((uint)(6)); + // Container child vbox93.Gtk.Box+BoxChild + this.hbox65 = new global::Gtk.HBox (); + this.hbox65.Name = "hbox65"; + this.hbox65.Spacing = 12; + // Container child hbox65.Gtk.Box+BoxChild + this.hbox72 = new global::Gtk.HBox (); + this.hbox72.Name = "hbox72"; + this.hbox72.Spacing = 12; + // Container child hbox72.Gtk.Box+BoxChild + this.notebook = new global::Gtk.Notebook (); + this.notebook.CanFocus = true; + this.notebook.Name = "notebook"; + this.notebook.CurrentPage = 0; + this.notebook.ShowBorder = false; + // Container child notebook.Gtk.Notebook+NotebookChild + this.hbox2 = new global::Gtk.HBox (); + this.hbox2.Name = "hbox2"; + this.hbox2.Spacing = 9; + this.hbox2.BorderWidth = ((uint)(9)); + // Container child hbox2.Gtk.Box+BoxChild + this.vbox6 = new global::Gtk.VBox (); + this.vbox6.Name = "vbox6"; + this.vbox6.Spacing = 6; + // Container child vbox6.Gtk.Box+BoxChild + this.scrolledwindow13 = new global::Gtk.ScrolledWindow (); + this.scrolledwindow13.CanFocus = true; + this.scrolledwindow13.Name = "scrolledwindow13"; + this.scrolledwindow13.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledwindow13.Gtk.Container+ContainerChild + this.addinTree = new global::Gtk.TreeView (); + this.addinTree.CanFocus = true; + this.addinTree.Name = "addinTree"; + this.scrolledwindow13.Add (this.addinTree); + this.vbox6.Add (this.scrolledwindow13); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox6 [this.scrolledwindow13])); + w3.Position = 0; + this.hbox2.Add (this.vbox6); + global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.vbox6])); + w4.Position = 0; + // Container child hbox2.Gtk.Box+BoxChild + this.eventbox2 = new global::Gtk.EventBox (); + this.eventbox2.Name = "eventbox2"; + // Container child eventbox2.Gtk.Container+ContainerChild + this.addininfoInstalled = new global::Mono.Addins.Gui.AddinInfoView (); + this.addininfoInstalled.Events = ((global::Gdk.EventMask)(256)); + this.addininfoInstalled.Name = "addininfoInstalled"; + this.addininfoInstalled.AllowInstall = false; + this.eventbox2.Add (this.addininfoInstalled); + this.hbox2.Add (this.eventbox2); + global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.hbox2 [this.eventbox2])); + w6.Position = 1; + w6.Expand = false; + w6.Fill = false; + this.notebook.Add (this.hbox2); + // Notebook tab + this.label7 = new global::Gtk.Label (); + this.label7.Name = "label7"; + this.label7.LabelProp = global::Mono.Unix.Catalog.GetString ("Installed"); + this.notebook.SetTabLabel (this.hbox2, this.label7); + this.label7.ShowAll (); + // Container child notebook.Gtk.Notebook+NotebookChild + this.boxUpdates = new global::Gtk.HBox (); + this.boxUpdates.Name = "boxUpdates"; + this.boxUpdates.Spacing = 9; + this.boxUpdates.BorderWidth = ((uint)(9)); + // Container child boxUpdates.Gtk.Box+BoxChild + this.vboxUpdates = new global::Gtk.VBox (); + this.vboxUpdates.Name = "vboxUpdates"; + // Container child vboxUpdates.Gtk.Box+BoxChild + this.eboxRepoUpdates = new global::Gtk.EventBox (); + this.eboxRepoUpdates.Name = "eboxRepoUpdates"; + // Container child eboxRepoUpdates.Gtk.Container+ContainerChild + this.hbox67 = new global::Gtk.HBox (); + this.hbox67.Name = "hbox67"; + this.hbox67.Spacing = 6; + // Container child hbox67.Gtk.Box+BoxChild + this.labelUpdates = new global::Gtk.Label (); + this.labelUpdates.Name = "labelUpdates"; + this.labelUpdates.LabelProp = global::Mono.Unix.Catalog.GetString ("No updates found"); + this.hbox67.Add (this.labelUpdates); + global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.hbox67 [this.labelUpdates])); + w8.Position = 0; + w8.Expand = false; + w8.Fill = false; + // Container child hbox67.Gtk.Box+BoxChild + this.buttonRefreshUpdates = new global::Gtk.Button (); + this.buttonRefreshUpdates.CanFocus = true; + this.buttonRefreshUpdates.Name = "buttonRefreshUpdates"; + this.buttonRefreshUpdates.UseUnderline = true; + this.buttonRefreshUpdates.Relief = ((global::Gtk.ReliefStyle)(2)); + this.buttonRefreshUpdates.Label = global::Mono.Unix.Catalog.GetString ("Refresh"); + global::Gtk.Image w9 = new global::Gtk.Image (); + w9.Pixbuf = IconLoader.LoadIcon (this, "gtk-refresh", global::Gtk.IconSize.Menu); + this.buttonRefreshUpdates.Image = w9; + this.hbox67.Add (this.buttonRefreshUpdates); + global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.hbox67 [this.buttonRefreshUpdates])); + w10.PackType = ((global::Gtk.PackType)(1)); + w10.Position = 1; + w10.Expand = false; + w10.Fill = false; + // Container child hbox67.Gtk.Box+BoxChild + this.buttonUpdateAll = new global::Gtk.Button (); + this.buttonUpdateAll.CanFocus = true; + this.buttonUpdateAll.Name = "buttonUpdateAll"; + this.buttonUpdateAll.UseUnderline = true; + this.buttonUpdateAll.Relief = ((global::Gtk.ReliefStyle)(2)); + this.buttonUpdateAll.Label = global::Mono.Unix.Catalog.GetString ("Update All"); + global::Gtk.Image w11 = new global::Gtk.Image (); + w11.Pixbuf = global::Gdk.Pixbuf.LoadFromResource ("download-16.png"); + this.buttonUpdateAll.Image = w11; + this.hbox67.Add (this.buttonUpdateAll); + global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.hbox67 [this.buttonUpdateAll])); + w12.PackType = ((global::Gtk.PackType)(1)); + w12.Position = 2; + w12.Expand = false; + w12.Fill = false; + this.eboxRepoUpdates.Add (this.hbox67); + this.vboxUpdates.Add (this.eboxRepoUpdates); + global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.vboxUpdates [this.eboxRepoUpdates])); + w14.Position = 0; + w14.Expand = false; + w14.Fill = false; + // Container child vboxUpdates.Gtk.Box+BoxChild + this.scrolledUpdates = new global::Gtk.ScrolledWindow (); + this.scrolledUpdates.CanFocus = true; + this.scrolledUpdates.Name = "scrolledUpdates"; + this.scrolledUpdates.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledUpdates.Gtk.Container+ContainerChild + this.updatesTreeView = new global::Gtk.TreeView (); + this.updatesTreeView.CanFocus = true; + this.updatesTreeView.Name = "updatesTreeView"; + this.scrolledUpdates.Add (this.updatesTreeView); + this.vboxUpdates.Add (this.scrolledUpdates); + global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.vboxUpdates [this.scrolledUpdates])); + w16.Position = 1; + this.boxUpdates.Add (this.vboxUpdates); + global::Gtk.Box.BoxChild w17 = ((global::Gtk.Box.BoxChild)(this.boxUpdates [this.vboxUpdates])); + w17.Position = 0; + // Container child boxUpdates.Gtk.Box+BoxChild + this.eventbox3 = new global::Gtk.EventBox (); + this.eventbox3.Name = "eventbox3"; + // Container child eventbox3.Gtk.Container+ContainerChild + this.addininfoUpdates = new global::Mono.Addins.Gui.AddinInfoView (); + this.addininfoUpdates.Events = ((global::Gdk.EventMask)(256)); + this.addininfoUpdates.Name = "addininfoUpdates"; + this.addininfoUpdates.AllowInstall = false; + this.eventbox3.Add (this.addininfoUpdates); + this.boxUpdates.Add (this.eventbox3); + global::Gtk.Box.BoxChild w19 = ((global::Gtk.Box.BoxChild)(this.boxUpdates [this.eventbox3])); + w19.Position = 1; + w19.Expand = false; + w19.Fill = false; + this.notebook.Add (this.boxUpdates); + global::Gtk.Notebook.NotebookChild w20 = ((global::Gtk.Notebook.NotebookChild)(this.notebook [this.boxUpdates])); + w20.Position = 1; + // Notebook tab + this.label4 = new global::Gtk.Label (); + this.label4.Name = "label4"; + this.label4.LabelProp = global::Mono.Unix.Catalog.GetString ("Updates"); + this.notebook.SetTabLabel (this.boxUpdates, this.label4); + this.label4.ShowAll (); + // Container child notebook.Gtk.Notebook+NotebookChild + this.hbox8 = new global::Gtk.HBox (); + this.hbox8.Name = "hbox8"; + this.hbox8.Spacing = 9; + this.hbox8.BorderWidth = ((uint)(9)); + // Container child hbox8.Gtk.Box+BoxChild + this.vboxGallery = new global::Gtk.VBox (); + this.vboxGallery.Name = "vboxGallery"; + // Container child vboxGallery.Gtk.Box+BoxChild + this.eboxRepo = new global::Gtk.EventBox (); + this.eboxRepo.Name = "eboxRepo"; + // Container child eboxRepo.Gtk.Container+ContainerChild + this.hbox66 = new global::Gtk.HBox (); + this.hbox66.Name = "hbox66"; + this.hbox66.Spacing = 6; + // Container child hbox66.Gtk.Box+BoxChild + this.label112 = new global::Gtk.Label (); + this.label112.Name = "label112"; + this.label112.LabelProp = global::Mono.Unix.Catalog.GetString ("Repository:"); + this.hbox66.Add (this.label112); + global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.hbox66 [this.label112])); + w21.Position = 0; + w21.Expand = false; + w21.Fill = false; + // Container child hbox66.Gtk.Box+BoxChild + this.repoCombo = new global::Gtk.ComboBox (); + this.repoCombo.Name = "repoCombo"; + this.hbox66.Add (this.repoCombo); + global::Gtk.Box.BoxChild w22 = ((global::Gtk.Box.BoxChild)(this.hbox66 [this.repoCombo])); + w22.Position = 1; + // Container child hbox66.Gtk.Box+BoxChild + this.buttonRefresh = new global::Gtk.Button (); + this.buttonRefresh.CanFocus = true; + this.buttonRefresh.Name = "buttonRefresh"; + this.buttonRefresh.UseUnderline = true; + this.buttonRefresh.Relief = ((global::Gtk.ReliefStyle)(2)); + this.buttonRefresh.Label = global::Mono.Unix.Catalog.GetString ("Refresh"); + global::Gtk.Image w23 = new global::Gtk.Image (); + w23.Pixbuf = IconLoader.LoadIcon (this, "gtk-refresh", global::Gtk.IconSize.Menu); + this.buttonRefresh.Image = w23; + this.hbox66.Add (this.buttonRefresh); + global::Gtk.Box.BoxChild w24 = ((global::Gtk.Box.BoxChild)(this.hbox66 [this.buttonRefresh])); + w24.Position = 2; + w24.Expand = false; + w24.Fill = false; + this.eboxRepo.Add (this.hbox66); + this.vboxGallery.Add (this.eboxRepo); + global::Gtk.Box.BoxChild w26 = ((global::Gtk.Box.BoxChild)(this.vboxGallery [this.eboxRepo])); + w26.Position = 0; + w26.Expand = false; + w26.Fill = false; + // Container child vboxGallery.Gtk.Box+BoxChild + this.scrolledGallery = new global::Gtk.ScrolledWindow (); + this.scrolledGallery.CanFocus = true; + this.scrolledGallery.Name = "scrolledGallery"; + this.scrolledGallery.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledGallery.Gtk.Container+ContainerChild + this.galleryTreeView = new global::Gtk.TreeView (); + this.galleryTreeView.CanFocus = true; + this.galleryTreeView.Name = "galleryTreeView"; + this.scrolledGallery.Add (this.galleryTreeView); + this.vboxGallery.Add (this.scrolledGallery); + global::Gtk.Box.BoxChild w28 = ((global::Gtk.Box.BoxChild)(this.vboxGallery [this.scrolledGallery])); + w28.Position = 1; + this.hbox8.Add (this.vboxGallery); + global::Gtk.Box.BoxChild w29 = ((global::Gtk.Box.BoxChild)(this.hbox8 [this.vboxGallery])); + w29.Position = 0; + // Container child hbox8.Gtk.Box+BoxChild + this.eventbox1 = new global::Gtk.EventBox (); + this.eventbox1.Name = "eventbox1"; + // Container child eventbox1.Gtk.Container+ContainerChild + this.addininfoGallery = new global::Mono.Addins.Gui.AddinInfoView (); + this.addininfoGallery.Events = ((global::Gdk.EventMask)(256)); + this.addininfoGallery.Name = "addininfoGallery"; + this.addininfoGallery.AllowInstall = false; + this.eventbox1.Add (this.addininfoGallery); + this.hbox8.Add (this.eventbox1); + global::Gtk.Box.BoxChild w31 = ((global::Gtk.Box.BoxChild)(this.hbox8 [this.eventbox1])); + w31.Position = 1; + w31.Expand = false; + w31.Fill = false; + this.notebook.Add (this.hbox8); + global::Gtk.Notebook.NotebookChild w32 = ((global::Gtk.Notebook.NotebookChild)(this.notebook [this.hbox8])); + w32.Position = 2; + // Notebook tab + this.label8 = new global::Gtk.Label (); + this.label8.Name = "label8"; + this.label8.LabelProp = global::Mono.Unix.Catalog.GetString ("Gallery"); + this.notebook.SetTabLabel (this.hbox8, this.label8); + this.label8.ShowAll (); + this.hbox72.Add (this.notebook); + global::Gtk.Box.BoxChild w33 = ((global::Gtk.Box.BoxChild)(this.hbox72 [this.notebook])); + w33.Position = 0; + this.hbox65.Add (this.hbox72); + global::Gtk.Box.BoxChild w34 = ((global::Gtk.Box.BoxChild)(this.hbox65 [this.hbox72])); + w34.Position = 0; + this.vbox93.Add (this.hbox65); + global::Gtk.Box.BoxChild w35 = ((global::Gtk.Box.BoxChild)(this.vbox93 [this.hbox65])); + w35.Position = 0; + w1.Add (this.vbox93); + global::Gtk.Box.BoxChild w36 = ((global::Gtk.Box.BoxChild)(w1 [this.vbox93])); + w36.Position = 0; + // Internal child Mono.Addins.Gui.AddinManagerDialog.ActionArea + global::Gtk.HButtonBox w37 = this.ActionArea; + w37.Name = "dialog-action_area8"; + w37.Spacing = 6; + w37.BorderWidth = ((uint)(5)); + w37.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(2)); + // Container child dialog-action_area8.Gtk.ButtonBox+ButtonBoxChild + this.buttonInstallFromFile = new global::Gtk.Button (); + this.buttonInstallFromFile.CanFocus = true; + this.buttonInstallFromFile.Name = "buttonInstallFromFile"; + this.buttonInstallFromFile.UseUnderline = true; + this.buttonInstallFromFile.Label = global::Mono.Unix.Catalog.GetString ("Install from file..."); + w37.Add (this.buttonInstallFromFile); + global::Gtk.ButtonBox.ButtonBoxChild w38 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w37 [this.buttonInstallFromFile])); + w38.Expand = false; + w38.Fill = false; + // Container child dialog-action_area8.Gtk.ButtonBox+ButtonBoxChild + this.btnClose = new global::Gtk.Button (); + this.btnClose.CanDefault = true; + this.btnClose.CanFocus = true; + this.btnClose.Name = "btnClose"; + this.btnClose.UseStock = true; + this.btnClose.UseUnderline = true; + this.btnClose.Label = "gtk-close"; + this.AddActionWidget (this.btnClose, -7); + global::Gtk.ButtonBox.ButtonBoxChild w39 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w37 [this.btnClose])); + w39.Position = 1; + w39.Expand = false; + w39.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.Hide (); + this.notebook.SwitchPage += new global::Gtk.SwitchPageHandler (this.OnNotebookSwitchPage); + this.addininfoInstalled.InstallClicked += new global::System.EventHandler (this.OnInstallClicked); + this.addininfoInstalled.UninstallClicked += new global::System.EventHandler (this.OnUninstallClicked); + this.addininfoInstalled.UpdateClicked += new global::System.EventHandler (this.OnUpdateClicked); + this.addininfoInstalled.EnableDisableClicked += new global::System.EventHandler (this.OnEnableDisableClicked); + this.buttonUpdateAll.Clicked += new global::System.EventHandler (this.OnUpdateAll); + this.buttonRefreshUpdates.Clicked += new global::System.EventHandler (this.OnButtonRefreshClicked); + this.addininfoUpdates.InstallClicked += new global::System.EventHandler (this.OnInstallClicked); + this.addininfoUpdates.UninstallClicked += new global::System.EventHandler (this.OnUninstallClicked); + this.addininfoUpdates.UpdateClicked += new global::System.EventHandler (this.OnUpdateClicked); + this.addininfoUpdates.EnableDisableClicked += new global::System.EventHandler (this.OnEnableDisableClicked); + this.repoCombo.Changed += new global::System.EventHandler (this.OnRepoComboChanged); + this.buttonRefresh.Clicked += new global::System.EventHandler (this.OnButtonRefreshClicked); + this.addininfoGallery.InstallClicked += new global::System.EventHandler (this.OnInstallClicked); + this.addininfoGallery.UninstallClicked += new global::System.EventHandler (this.OnUninstallClicked); + this.addininfoGallery.UpdateClicked += new global::System.EventHandler (this.OnUpdateClicked); + this.addininfoGallery.EnableDisableClicked += new global::System.EventHandler (this.OnEnableDisableClicked); + this.buttonInstallFromFile.Clicked += new global::System.EventHandler (this.OnButtonInstallFromFileClicked); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ErrorDialog.cs b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ErrorDialog.cs new file mode 100644 index 00000000..c56949a8 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ErrorDialog.cs @@ -0,0 +1,129 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal partial class ErrorDialog + { + private global::Gtk.HBox hbox59; + private global::Gtk.VBox vbox72; + private global::Gtk.Image icon; + private global::Gtk.VBox vbox73; + private global::Gtk.Label descriptionLabel; + private global::Gtk.Expander expander; + private global::Gtk.ScrolledWindow scrolledwindow10; + private global::Gtk.TextView detailsTextView; + private global::Gtk.Label label102; + private global::Gtk.Button okButton; + + protected virtual void Build () + { + Gui.Initialize (this); + // Widget Mono.Addins.Gui.ErrorDialog + this.Name = "Mono.Addins.Gui.ErrorDialog"; + this.Title = global::Mono.Unix.Catalog.GetString ("Error"); + this.TypeHint = ((global::Gdk.WindowTypeHint)(1)); + this.BorderWidth = ((uint)(6)); + // Internal child Mono.Addins.Gui.ErrorDialog.VBox + global::Gtk.VBox w1 = this.VBox; + w1.Name = "dialog-vbox5"; + w1.Spacing = 6; + // Container child dialog-vbox5.Gtk.Box+BoxChild + this.hbox59 = new global::Gtk.HBox (); + this.hbox59.Name = "hbox59"; + this.hbox59.Spacing = 6; + this.hbox59.BorderWidth = ((uint)(6)); + // Container child hbox59.Gtk.Box+BoxChild + this.vbox72 = new global::Gtk.VBox (); + this.vbox72.Name = "vbox72"; + // Container child vbox72.Gtk.Box+BoxChild + this.icon = new global::Gtk.Image (); + this.icon.Name = "icon"; + this.icon.Pixbuf = IconLoader.LoadIcon (this, "gtk-dialog-error", global::Gtk.IconSize.Dialog); + this.vbox72.Add (this.icon); + global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox72 [this.icon])); + w2.Position = 0; + w2.Expand = false; + w2.Fill = false; + this.hbox59.Add (this.vbox72); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.hbox59 [this.vbox72])); + w3.Position = 0; + w3.Expand = false; + w3.Fill = false; + // Container child hbox59.Gtk.Box+BoxChild + this.vbox73 = new global::Gtk.VBox (); + this.vbox73.Name = "vbox73"; + this.vbox73.Spacing = 12; + // Container child vbox73.Gtk.Box+BoxChild + this.descriptionLabel = new global::Gtk.Label (); + this.descriptionLabel.WidthRequest = 540; + this.descriptionLabel.CanFocus = true; + this.descriptionLabel.Name = "descriptionLabel"; + this.descriptionLabel.Xalign = 0F; + this.descriptionLabel.LabelProp = "An exception has been thrown 1 2 3 4 5 6 7 8 9 10 11 12 13 14"; + this.descriptionLabel.Wrap = true; + this.descriptionLabel.Selectable = true; + this.vbox73.Add (this.descriptionLabel); + global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox73 [this.descriptionLabel])); + w4.Position = 0; + w4.Expand = false; + w4.Fill = false; + // Container child vbox73.Gtk.Box+BoxChild + this.expander = new global::Gtk.Expander (null); + this.expander.CanFocus = true; + this.expander.Name = "expander"; + // Container child expander.Gtk.Container+ContainerChild + this.scrolledwindow10 = new global::Gtk.ScrolledWindow (); + this.scrolledwindow10.CanFocus = true; + this.scrolledwindow10.Name = "scrolledwindow10"; + this.scrolledwindow10.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledwindow10.Gtk.Container+ContainerChild + this.detailsTextView = new global::Gtk.TextView (); + this.detailsTextView.HeightRequest = 250; + this.detailsTextView.CanFocus = true; + this.detailsTextView.Name = "detailsTextView"; + this.detailsTextView.PixelsAboveLines = 2; + this.detailsTextView.PixelsBelowLines = 2; + this.detailsTextView.LeftMargin = 6; + this.detailsTextView.RightMargin = 6; + this.scrolledwindow10.Add (this.detailsTextView); + this.expander.Add (this.scrolledwindow10); + this.label102 = new global::Gtk.Label (); + this.label102.Name = "label102"; + this.label102.LabelProp = global::Mono.Unix.Catalog.GetString ("Details"); + this.expander.LabelWidget = this.label102; + this.vbox73.Add (this.expander); + global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox73 [this.expander])); + w7.Position = 1; + this.hbox59.Add (this.vbox73); + global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.hbox59 [this.vbox73])); + w8.Position = 1; + w1.Add (this.hbox59); + global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(w1 [this.hbox59])); + w9.Position = 0; + // Internal child Mono.Addins.Gui.ErrorDialog.ActionArea + global::Gtk.HButtonBox w10 = this.ActionArea; + w10.Name = "dialog-action_area5"; + w10.Spacing = 10; + w10.BorderWidth = ((uint)(5)); + w10.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(4)); + // Container child dialog-action_area5.Gtk.ButtonBox+ButtonBoxChild + this.okButton = new global::Gtk.Button (); + this.okButton.CanDefault = true; + this.okButton.CanFocus = true; + this.okButton.Name = "okButton"; + this.okButton.UseStock = true; + this.okButton.UseUnderline = true; + this.okButton.Label = "gtk-ok"; + this.AddActionWidget (this.okButton, -5); + global::Gtk.ButtonBox.ButtonBoxChild w11 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w10 [this.okButton])); + w11.Expand = false; + w11.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.DefaultWidth = 632; + this.DefaultHeight = 155; + this.Show (); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.InstallDialog.cs b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.InstallDialog.cs new file mode 100644 index 00000000..a36f27fc --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.InstallDialog.cs @@ -0,0 +1,143 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal partial class InstallDialog + { + private global::Gtk.VBox vbox3; + private global::Gtk.ScrolledWindow scrolledwindow1; + private global::Gtk.VBox vbox4; + private global::Gtk.Label labelInfo; + private global::Gtk.HSeparator insSeparator; + private global::Gtk.VBox boxProgress; + private global::Gtk.Label globalProgressLabel; + private global::Gtk.ProgressBar mainProgressBar; + private global::Gtk.Button buttonCancel; + private global::Gtk.Button buttonOk; + + protected virtual void Build () + { + Gui.Initialize (this); + // Widget Mono.Addins.Gui.InstallDialog + this.Name = "Mono.Addins.Gui.InstallDialog"; + this.WindowPosition = ((global::Gtk.WindowPosition)(4)); + // Internal child Mono.Addins.Gui.InstallDialog.VBox + global::Gtk.VBox w1 = this.VBox; + w1.Name = "dialog1_VBox"; + w1.BorderWidth = ((uint)(2)); + // Container child dialog1_VBox.Gtk.Box+BoxChild + this.vbox3 = new global::Gtk.VBox (); + this.vbox3.Name = "vbox3"; + this.vbox3.Spacing = 9; + this.vbox3.BorderWidth = ((uint)(9)); + // Container child vbox3.Gtk.Box+BoxChild + this.scrolledwindow1 = new global::Gtk.ScrolledWindow (); + this.scrolledwindow1.CanFocus = true; + this.scrolledwindow1.Name = "scrolledwindow1"; + this.scrolledwindow1.VscrollbarPolicy = ((global::Gtk.PolicyType)(2)); + this.scrolledwindow1.HscrollbarPolicy = ((global::Gtk.PolicyType)(2)); + // Container child scrolledwindow1.Gtk.Container+ContainerChild + global::Gtk.Viewport w2 = new global::Gtk.Viewport (); + w2.ShadowType = ((global::Gtk.ShadowType)(0)); + // Container child GtkViewport.Gtk.Container+ContainerChild + this.vbox4 = new global::Gtk.VBox (); + this.vbox4.Name = "vbox4"; + this.vbox4.Spacing = 6; + // Container child vbox4.Gtk.Box+BoxChild + this.labelInfo = new global::Gtk.Label (); + this.labelInfo.WidthRequest = 400; + this.labelInfo.Name = "labelInfo"; + this.labelInfo.Xalign = 0F; + this.labelInfo.Yalign = 0F; + this.labelInfo.LabelProp = global::Mono.Unix.Catalog.GetString ("label3"); + this.labelInfo.Wrap = true; + this.vbox4.Add (this.labelInfo); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox4 [this.labelInfo])); + w3.Position = 0; + w3.Expand = false; + w3.Fill = false; + w2.Add (this.vbox4); + this.scrolledwindow1.Add (w2); + this.vbox3.Add (this.scrolledwindow1); + global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.scrolledwindow1])); + w6.Position = 0; + // Container child vbox3.Gtk.Box+BoxChild + this.insSeparator = new global::Gtk.HSeparator (); + this.insSeparator.Name = "insSeparator"; + this.vbox3.Add (this.insSeparator); + global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.insSeparator])); + w7.Position = 1; + w7.Expand = false; + w7.Fill = false; + // Container child vbox3.Gtk.Box+BoxChild + this.boxProgress = new global::Gtk.VBox (); + this.boxProgress.Name = "boxProgress"; + this.boxProgress.Spacing = 6; + // Container child boxProgress.Gtk.Box+BoxChild + this.globalProgressLabel = new global::Gtk.Label (); + this.globalProgressLabel.Name = "globalProgressLabel"; + this.globalProgressLabel.Xalign = 0F; + this.globalProgressLabel.Ellipsize = ((global::Pango.EllipsizeMode)(3)); + this.boxProgress.Add (this.globalProgressLabel); + global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.boxProgress [this.globalProgressLabel])); + w8.Position = 0; + w8.Expand = false; + w8.Fill = false; + // Container child boxProgress.Gtk.Box+BoxChild + this.mainProgressBar = new global::Gtk.ProgressBar (); + this.mainProgressBar.Name = "mainProgressBar"; + this.boxProgress.Add (this.mainProgressBar); + global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.boxProgress [this.mainProgressBar])); + w9.Position = 1; + w9.Expand = false; + w9.Fill = false; + this.vbox3.Add (this.boxProgress); + global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.boxProgress])); + w10.Position = 2; + w10.Expand = false; + w10.Fill = false; + w1.Add (this.vbox3); + global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(w1 [this.vbox3])); + w11.Position = 0; + // Internal child Mono.Addins.Gui.InstallDialog.ActionArea + global::Gtk.HButtonBox w12 = this.ActionArea; + w12.Name = "dialog1_ActionArea"; + w12.Spacing = 10; + w12.BorderWidth = ((uint)(5)); + w12.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(4)); + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.buttonCancel = new global::Gtk.Button (); + this.buttonCancel.CanDefault = true; + this.buttonCancel.CanFocus = true; + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.UseStock = true; + this.buttonCancel.UseUnderline = true; + this.buttonCancel.Label = "gtk-cancel"; + w12.Add (this.buttonCancel); + global::Gtk.ButtonBox.ButtonBoxChild w13 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w12 [this.buttonCancel])); + w13.Expand = false; + w13.Fill = false; + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.buttonOk = new global::Gtk.Button (); + this.buttonOk.CanDefault = true; + this.buttonOk.CanFocus = true; + this.buttonOk.Name = "buttonOk"; + this.buttonOk.UseUnderline = true; + this.buttonOk.Label = global::Mono.Unix.Catalog.GetString ("Install"); + w12.Add (this.buttonOk); + global::Gtk.ButtonBox.ButtonBoxChild w14 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w12 [this.buttonOk])); + w14.Position = 1; + w14.Expand = false; + w14.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.DefaultWidth = 494; + this.DefaultHeight = 239; + this.insSeparator.Hide (); + this.Hide (); + this.buttonCancel.Clicked += new global::System.EventHandler (this.OnButtonCancelClicked); + this.buttonOk.Clicked += new global::System.EventHandler (this.OnButtonOkClicked); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ManageSitesDialog.cs b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ManageSitesDialog.cs new file mode 100644 index 00000000..b1d5044d --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ManageSitesDialog.cs @@ -0,0 +1,110 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal partial class ManageSitesDialog + { + private global::Gtk.HBox hbox67; + private global::Gtk.ScrolledWindow scrolledwindow17; + private global::Gtk.TreeView repoTree; + private global::Gtk.VBox vbox88; + private global::Gtk.Button btnAdd; + private global::Gtk.Button btnRemove; + private global::Gtk.Button closebutton2; + + protected virtual void Build () + { + Gui.Initialize (this); + // Widget Mono.Addins.Gui.ManageSitesDialog + this.Name = "Mono.Addins.Gui.ManageSitesDialog"; + this.Title = global::Mono.Unix.Catalog.GetString ("Extension Repository Management"); + this.TypeHint = ((global::Gdk.WindowTypeHint)(1)); + this.BorderWidth = ((uint)(6)); + this.DefaultWidth = 600; + this.DefaultHeight = 300; + // Internal child Mono.Addins.Gui.ManageSitesDialog.VBox + global::Gtk.VBox w1 = this.VBox; + w1.Name = "dialog-vbox10"; + w1.Spacing = 6; + // Container child dialog-vbox10.Gtk.Box+BoxChild + this.hbox67 = new global::Gtk.HBox (); + this.hbox67.Name = "hbox67"; + this.hbox67.Spacing = 12; + this.hbox67.BorderWidth = ((uint)(6)); + // Container child hbox67.Gtk.Box+BoxChild + this.scrolledwindow17 = new global::Gtk.ScrolledWindow (); + this.scrolledwindow17.CanFocus = true; + this.scrolledwindow17.Name = "scrolledwindow17"; + this.scrolledwindow17.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledwindow17.Gtk.Container+ContainerChild + this.repoTree = new global::Gtk.TreeView (); + this.repoTree.CanFocus = true; + this.repoTree.Name = "repoTree"; + this.repoTree.HeadersVisible = false; + this.scrolledwindow17.Add (this.repoTree); + this.hbox67.Add (this.scrolledwindow17); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.hbox67 [this.scrolledwindow17])); + w3.Position = 0; + // Container child hbox67.Gtk.Box+BoxChild + this.vbox88 = new global::Gtk.VBox (); + this.vbox88.Name = "vbox88"; + this.vbox88.Spacing = 6; + // Container child vbox88.Gtk.Box+BoxChild + this.btnAdd = new global::Gtk.Button (); + this.btnAdd.CanFocus = true; + this.btnAdd.Name = "btnAdd"; + this.btnAdd.UseStock = true; + this.btnAdd.UseUnderline = true; + this.btnAdd.Label = "gtk-add"; + this.vbox88.Add (this.btnAdd); + global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox88 [this.btnAdd])); + w4.Position = 0; + w4.Expand = false; + w4.Fill = false; + // Container child vbox88.Gtk.Box+BoxChild + this.btnRemove = new global::Gtk.Button (); + this.btnRemove.CanFocus = true; + this.btnRemove.Name = "btnRemove"; + this.btnRemove.UseStock = true; + this.btnRemove.UseUnderline = true; + this.btnRemove.Label = "gtk-delete"; + this.vbox88.Add (this.btnRemove); + global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox88 [this.btnRemove])); + w5.Position = 1; + w5.Expand = false; + w5.Fill = false; + this.hbox67.Add (this.vbox88); + global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.hbox67 [this.vbox88])); + w6.Position = 1; + w6.Expand = false; + w6.Fill = false; + w1.Add (this.hbox67); + global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(w1 [this.hbox67])); + w7.Position = 0; + // Internal child Mono.Addins.Gui.ManageSitesDialog.ActionArea + global::Gtk.HButtonBox w8 = this.ActionArea; + w8.Name = "dialog-action_area10"; + w8.Spacing = 10; + w8.BorderWidth = ((uint)(6)); + w8.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(4)); + // Container child dialog-action_area10.Gtk.ButtonBox+ButtonBoxChild + this.closebutton2 = new global::Gtk.Button (); + this.closebutton2.CanDefault = true; + this.closebutton2.CanFocus = true; + this.closebutton2.Name = "closebutton2"; + this.closebutton2.UseStock = true; + this.closebutton2.UseUnderline = true; + this.closebutton2.Label = "gtk-close"; + this.AddActionWidget (this.closebutton2, -7); + global::Gtk.ButtonBox.ButtonBoxChild w9 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w8 [this.closebutton2])); + w9.Expand = false; + w9.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.Hide (); + this.btnAdd.Clicked += new global::System.EventHandler (this.OnAdd); + this.btnRemove.Clicked += new global::System.EventHandler (this.OnRemove); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.NewSiteDialog.cs b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.NewSiteDialog.cs new file mode 100644 index 00000000..6e797b74 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.NewSiteDialog.cs @@ -0,0 +1,214 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal partial class NewSiteDialog + { + private global::Gtk.VBox vbox89; + private global::Gtk.Label label121; + private global::Gtk.RadioButton btnOnlineRep; + private global::Gtk.HBox hbox68; + private global::Gtk.Label label122; + private global::Gtk.Label label119; + private global::Gtk.Entry urlText; + private global::Gtk.RadioButton btnLocalRep; + private global::Gtk.HBox hbox69; + private global::Gtk.Label label123; + private global::Gtk.Label label120; + private global::Gtk.HBox hbox1; + private global::Gtk.Entry pathEntry; + private global::Gtk.Button buttonBrowse; + private global::Gtk.Button cancelbutton1; + private global::Gtk.Button btnOk; + + protected virtual void Build () + { + Gui.Initialize (this); + // Widget Mono.Addins.Gui.NewSiteDialog + this.Name = "Mono.Addins.Gui.NewSiteDialog"; + this.Title = global::Mono.Unix.Catalog.GetString ("Add New Repository"); + this.TypeHint = ((global::Gdk.WindowTypeHint)(1)); + this.BorderWidth = ((uint)(6)); + this.DefaultWidth = 550; + // Internal child Mono.Addins.Gui.NewSiteDialog.VBox + global::Gtk.VBox w1 = this.VBox; + w1.Name = "dialog-vbox11"; + w1.Spacing = 6; + w1.BorderWidth = ((uint)(2)); + // Container child dialog-vbox11.Gtk.Box+BoxChild + this.vbox89 = new global::Gtk.VBox (); + this.vbox89.Name = "vbox89"; + this.vbox89.Spacing = 6; + this.vbox89.BorderWidth = ((uint)(6)); + // Container child vbox89.Gtk.Box+BoxChild + this.label121 = new global::Gtk.Label (); + this.label121.Name = "label121"; + this.label121.Xalign = 0F; + this.label121.LabelProp = global::Mono.Unix.Catalog.GetString ("Select the location of the repository you want to register:"); + this.vbox89.Add (this.label121); + global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox89 [this.label121])); + w2.Position = 0; + w2.Expand = false; + w2.Fill = false; + // Container child vbox89.Gtk.Box+BoxChild + this.btnOnlineRep = new global::Gtk.RadioButton (global::Mono.Unix.Catalog.GetString ("Register an on-line repository")); + this.btnOnlineRep.CanFocus = true; + this.btnOnlineRep.Name = "btnOnlineRep"; + this.btnOnlineRep.Active = true; + this.btnOnlineRep.DrawIndicator = true; + this.btnOnlineRep.UseUnderline = true; + this.btnOnlineRep.Group = new global::GLib.SList (global::System.IntPtr.Zero); + this.vbox89.Add (this.btnOnlineRep); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox89 [this.btnOnlineRep])); + w3.Position = 1; + w3.Expand = false; + w3.Fill = false; + // Container child vbox89.Gtk.Box+BoxChild + this.hbox68 = new global::Gtk.HBox (); + this.hbox68.Name = "hbox68"; + this.hbox68.Spacing = 6; + // Container child hbox68.Gtk.Box+BoxChild + this.label122 = new global::Gtk.Label (); + this.label122.WidthRequest = 32; + this.label122.Name = "label122"; + this.hbox68.Add (this.label122); + global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.hbox68 [this.label122])); + w4.Position = 0; + w4.Expand = false; + w4.Fill = false; + // Container child hbox68.Gtk.Box+BoxChild + this.label119 = new global::Gtk.Label (); + this.label119.Name = "label119"; + this.label119.LabelProp = global::Mono.Unix.Catalog.GetString ("Url:"); + this.hbox68.Add (this.label119); + global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.hbox68 [this.label119])); + w5.Position = 1; + w5.Expand = false; + w5.Fill = false; + // Container child hbox68.Gtk.Box+BoxChild + this.urlText = new global::Gtk.Entry (); + this.urlText.CanFocus = true; + this.urlText.Name = "urlText"; + this.urlText.IsEditable = true; + this.urlText.InvisibleChar = '●'; + this.hbox68.Add (this.urlText); + global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.hbox68 [this.urlText])); + w6.Position = 2; + this.vbox89.Add (this.hbox68); + global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox89 [this.hbox68])); + w7.Position = 2; + w7.Expand = false; + w7.Fill = false; + // Container child vbox89.Gtk.Box+BoxChild + this.btnLocalRep = new global::Gtk.RadioButton (global::Mono.Unix.Catalog.GetString ("Register a local repository")); + this.btnLocalRep.CanFocus = true; + this.btnLocalRep.Name = "btnLocalRep"; + this.btnLocalRep.DrawIndicator = true; + this.btnLocalRep.UseUnderline = true; + this.btnLocalRep.Group = this.btnOnlineRep.Group; + this.vbox89.Add (this.btnLocalRep); + global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.vbox89 [this.btnLocalRep])); + w8.Position = 3; + w8.Expand = false; + w8.Fill = false; + // Container child vbox89.Gtk.Box+BoxChild + this.hbox69 = new global::Gtk.HBox (); + this.hbox69.Name = "hbox69"; + this.hbox69.Spacing = 6; + // Container child hbox69.Gtk.Box+BoxChild + this.label123 = new global::Gtk.Label (); + this.label123.WidthRequest = 32; + this.label123.Name = "label123"; + this.hbox69.Add (this.label123); + global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.hbox69 [this.label123])); + w9.Position = 0; + w9.Expand = false; + w9.Fill = false; + // Container child hbox69.Gtk.Box+BoxChild + this.label120 = new global::Gtk.Label (); + this.label120.Name = "label120"; + this.label120.LabelProp = global::Mono.Unix.Catalog.GetString ("Path:"); + this.hbox69.Add (this.label120); + global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.hbox69 [this.label120])); + w10.Position = 1; + w10.Expand = false; + w10.Fill = false; + // Container child hbox69.Gtk.Box+BoxChild + this.hbox1 = new global::Gtk.HBox (); + this.hbox1.Name = "hbox1"; + this.hbox1.Spacing = 6; + // Container child hbox1.Gtk.Box+BoxChild + this.pathEntry = new global::Gtk.Entry (); + this.pathEntry.CanFocus = true; + this.pathEntry.Name = "pathEntry"; + this.pathEntry.IsEditable = true; + this.pathEntry.InvisibleChar = '●'; + this.hbox1.Add (this.pathEntry); + global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.pathEntry])); + w11.Position = 0; + // Container child hbox1.Gtk.Box+BoxChild + this.buttonBrowse = new global::Gtk.Button (); + this.buttonBrowse.CanFocus = true; + this.buttonBrowse.Name = "buttonBrowse"; + this.buttonBrowse.UseUnderline = true; + this.buttonBrowse.Label = global::Mono.Unix.Catalog.GetString ("Browse..."); + this.hbox1.Add (this.buttonBrowse); + global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.buttonBrowse])); + w12.Position = 1; + w12.Expand = false; + w12.Fill = false; + this.hbox69.Add (this.hbox1); + global::Gtk.Box.BoxChild w13 = ((global::Gtk.Box.BoxChild)(this.hbox69 [this.hbox1])); + w13.Position = 2; + this.vbox89.Add (this.hbox69); + global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.vbox89 [this.hbox69])); + w14.Position = 4; + w14.Expand = false; + w14.Fill = false; + w1.Add (this.vbox89); + global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(w1 [this.vbox89])); + w15.Position = 0; + // Internal child Mono.Addins.Gui.NewSiteDialog.ActionArea + global::Gtk.HButtonBox w16 = this.ActionArea; + w16.Name = "dialog-action_area11"; + w16.Spacing = 10; + w16.BorderWidth = ((uint)(5)); + w16.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(4)); + // Container child dialog-action_area11.Gtk.ButtonBox+ButtonBoxChild + this.cancelbutton1 = new global::Gtk.Button (); + this.cancelbutton1.CanDefault = true; + this.cancelbutton1.CanFocus = true; + this.cancelbutton1.Name = "cancelbutton1"; + this.cancelbutton1.UseStock = true; + this.cancelbutton1.UseUnderline = true; + this.cancelbutton1.Label = "gtk-cancel"; + this.AddActionWidget (this.cancelbutton1, -6); + global::Gtk.ButtonBox.ButtonBoxChild w17 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w16 [this.cancelbutton1])); + w17.Expand = false; + w17.Fill = false; + // Container child dialog-action_area11.Gtk.ButtonBox+ButtonBoxChild + this.btnOk = new global::Gtk.Button (); + this.btnOk.CanDefault = true; + this.btnOk.CanFocus = true; + this.btnOk.Name = "btnOk"; + this.btnOk.UseStock = true; + this.btnOk.UseUnderline = true; + this.btnOk.Label = "gtk-ok"; + this.AddActionWidget (this.btnOk, -5); + global::Gtk.ButtonBox.ButtonBoxChild w18 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w16 [this.btnOk])); + w18.Position = 1; + w18.Expand = false; + w18.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.DefaultHeight = 249; + this.Hide (); + this.btnOnlineRep.Clicked += new global::System.EventHandler (this.OnOptionClicked); + this.urlText.Changed += new global::System.EventHandler (this.OnUrlTextChanged); + this.btnLocalRep.Clicked += new global::System.EventHandler (this.OnOptionClicked); + this.pathEntry.Changed += new global::System.EventHandler (this.OnPathEntryChanged); + this.buttonBrowse.Clicked += new global::System.EventHandler (this.OnButtonBrowseClicked); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ProgressDialog.cs b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ProgressDialog.cs new file mode 100644 index 00000000..a71a3f49 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/Mono.Addins.Gui.ProgressDialog.cs @@ -0,0 +1,102 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal partial class ProgressDialog + { + private global::Gtk.VBox vbox2; + private global::Gtk.Label labelMessage; + private global::Gtk.ProgressBar progressbar; + private global::Gtk.Expander expander1; + private global::Gtk.ScrolledWindow GtkScrolledWindow; + private global::Gtk.TextView textview; + private global::Gtk.Label GtkLabel1; + private global::Gtk.Button buttonCancel; + + protected virtual void Build () + { + Gui.Initialize (this); + // Widget Mono.Addins.Gui.ProgressDialog + this.Name = "Mono.Addins.Gui.ProgressDialog"; + this.Title = global::Mono.Unix.Catalog.GetString ("Progress"); + this.WindowPosition = ((global::Gtk.WindowPosition)(4)); + this.Modal = true; + // Internal child Mono.Addins.Gui.ProgressDialog.VBox + global::Gtk.VBox w1 = this.VBox; + w1.Name = "dialog1_VBox"; + w1.BorderWidth = ((uint)(2)); + // Container child dialog1_VBox.Gtk.Box+BoxChild + this.vbox2 = new global::Gtk.VBox (); + this.vbox2.Name = "vbox2"; + this.vbox2.Spacing = 6; + this.vbox2.BorderWidth = ((uint)(9)); + // Container child vbox2.Gtk.Box+BoxChild + this.labelMessage = new global::Gtk.Label (); + this.labelMessage.Name = "labelMessage"; + this.labelMessage.Xalign = 0F; + this.vbox2.Add (this.labelMessage); + global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.labelMessage])); + w2.Position = 0; + w2.Expand = false; + w2.Fill = false; + // Container child vbox2.Gtk.Box+BoxChild + this.progressbar = new global::Gtk.ProgressBar (); + this.progressbar.Name = "progressbar"; + this.vbox2.Add (this.progressbar); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.progressbar])); + w3.Position = 1; + w3.Expand = false; + w3.Fill = false; + // Container child vbox2.Gtk.Box+BoxChild + this.expander1 = new global::Gtk.Expander (null); + this.expander1.CanFocus = true; + this.expander1.Name = "expander1"; + // Container child expander1.Gtk.Container+ContainerChild + this.GtkScrolledWindow = new global::Gtk.ScrolledWindow (); + this.GtkScrolledWindow.Name = "GtkScrolledWindow"; + this.GtkScrolledWindow.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child GtkScrolledWindow.Gtk.Container+ContainerChild + this.textview = new global::Gtk.TextView (); + this.textview.CanFocus = true; + this.textview.Name = "textview"; + this.GtkScrolledWindow.Add (this.textview); + this.expander1.Add (this.GtkScrolledWindow); + this.GtkLabel1 = new global::Gtk.Label (); + this.GtkLabel1.Name = "GtkLabel1"; + this.GtkLabel1.LabelProp = global::Mono.Unix.Catalog.GetString ("Details"); + this.GtkLabel1.UseUnderline = true; + this.expander1.LabelWidget = this.GtkLabel1; + this.vbox2.Add (this.expander1); + global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.expander1])); + w6.Position = 2; + w1.Add (this.vbox2); + global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(w1 [this.vbox2])); + w7.Position = 0; + // Internal child Mono.Addins.Gui.ProgressDialog.ActionArea + global::Gtk.HButtonBox w8 = this.ActionArea; + w8.Name = "dialog1_ActionArea"; + w8.Spacing = 6; + w8.BorderWidth = ((uint)(5)); + w8.LayoutStyle = ((global::Gtk.ButtonBoxStyle)(4)); + // Container child dialog1_ActionArea.Gtk.ButtonBox+ButtonBoxChild + this.buttonCancel = new global::Gtk.Button (); + this.buttonCancel.CanDefault = true; + this.buttonCancel.CanFocus = true; + this.buttonCancel.Name = "buttonCancel"; + this.buttonCancel.UseStock = true; + this.buttonCancel.UseUnderline = true; + this.buttonCancel.Label = "gtk-cancel"; + this.AddActionWidget (this.buttonCancel, -6); + global::Gtk.ButtonBox.ButtonBoxChild w9 = ((global::Gtk.ButtonBox.ButtonBoxChild)(w8 [this.buttonCancel])); + w9.Expand = false; + w9.Fill = false; + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.DefaultWidth = 513; + this.DefaultHeight = 156; + this.Hide (); + this.buttonCancel.Clicked += new global::System.EventHandler (this.OnButtonCancelClicked); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Gui/generated.cs b/mono-addins/Mono.Addins.Gui/Gui/generated.cs new file mode 100644 index 00000000..83f4a6c2 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Gui/generated.cs @@ -0,0 +1,135 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Mono.Addins.Gui +{ + internal class Gui + { + private static bool initialized; + + internal static void Initialize(Gtk.Widget iconRenderer) + { + if ((initialized == false)) + { + initialized = true; + } + } + } + + internal class IconLoader + { + public static Gdk.Pixbuf LoadIcon(Gtk.Widget widget, string name, Gtk.IconSize size) + { + Gdk.Pixbuf res = widget.RenderIcon(name, size, null); + if ((res != null)) + { + return res; + } + else + { + int sz; + int sy; + global::Gtk.Icon.SizeLookup(size, out sz, out sy); + try + { + return Gtk.IconTheme.Default.LoadIcon(name, sz, 0); + } + catch (System.Exception) + { + if ((name != "gtk-missing-image")) + { + return IconLoader.LoadIcon(widget, "gtk-missing-image", size); + } + else + { + Gdk.Pixmap pmap = new Gdk.Pixmap(Gdk.Screen.Default.RootWindow, sz, sz); + Gdk.GC gc = new Gdk.GC(pmap); + gc.RgbFgColor = new Gdk.Color(255, 255, 255); + pmap.DrawRectangle(gc, true, 0, 0, sz, sz); + gc.RgbFgColor = new Gdk.Color(0, 0, 0); + pmap.DrawRectangle(gc, false, 0, 0, (sz - 1), (sz - 1)); + gc.SetLineAttributes(3, Gdk.LineStyle.Solid, Gdk.CapStyle.Round, Gdk.JoinStyle.Round); + gc.RgbFgColor = new Gdk.Color(255, 0, 0); + pmap.DrawLine(gc, (sz / 4), (sz / 4), ((sz - 1) + - (sz / 4)), ((sz - 1) + - (sz / 4))); + pmap.DrawLine(gc, ((sz - 1) + - (sz / 4)), (sz / 4), (sz / 4), ((sz - 1) + - (sz / 4))); + return Gdk.Pixbuf.FromDrawable(pmap, pmap.Colormap, 0, 0, 0, 0, sz, sz); + } + } + } + } + } + + internal class BinContainer + { + private Gtk.Widget child; + + private Gtk.UIManager uimanager; + + public static BinContainer Attach(Gtk.Bin bin) + { + BinContainer bc = new BinContainer(); + bin.SizeRequested += new Gtk.SizeRequestedHandler(bc.OnSizeRequested); + bin.SizeAllocated += new Gtk.SizeAllocatedHandler(bc.OnSizeAllocated); + bin.Added += new Gtk.AddedHandler(bc.OnAdded); + return bc; + } + + private void OnSizeRequested(object sender, Gtk.SizeRequestedArgs args) + { + if ((this.child != null)) + { + args.Requisition = this.child.SizeRequest(); + } + } + + private void OnSizeAllocated(object sender, Gtk.SizeAllocatedArgs args) + { + if ((this.child != null)) + { + this.child.Allocation = args.Allocation; + } + } + + private void OnAdded(object sender, Gtk.AddedArgs args) + { + this.child = args.Widget; + } + + public void SetUiManager(Gtk.UIManager uim) + { + this.uimanager = uim; + this.child.Realized += new System.EventHandler(this.OnRealized); + } + + private void OnRealized(object sender, System.EventArgs args) + { + if ((this.uimanager != null)) + { + Gtk.Widget w; + w = this.child.Toplevel; + if (((w != null) + && typeof(Gtk.Window).IsInstanceOfType(w))) + { + ((Gtk.Window)(w)).AddAccelGroup(this.uimanager.AccelGroup); + this.uimanager = null; + } + } + } + } + + internal class ActionGroups + { + public static Gtk.ActionGroup GetActionGroup(System.Type type) + { + return ActionGroups.GetActionGroup(type.FullName); + } + + public static Gtk.ActionGroup GetActionGroup(string name) + { + return null; + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui.csproj b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui.csproj new file mode 100644 index 00000000..52f39982 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui.csproj @@ -0,0 +1,393 @@ + + + + Debug + AnyCPU + 8.0.30703 + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A} + Library + Mono.Addins.Gui + 2.0 + Mono.Addins.Gui + True + ..\mono-addins.snk + v4.6 + + + + True + full + false + ..\bin + prompt + 4 + True + False + True + + + pdbonly + True + ..\bin + prompt + 4 + True + False + True + true + + + + False + + + False + + + False + + + False + + + False + + + + + + + + + {91DD5A2D-9FE3-4C3C-9253-876141874DAD} + Mono.Addins + False + + + {A85C9721-C054-4BD8-A1F3-0227615F0A36} + Mono.Addins.Setup + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + package-x-generic.png + + + package-x-generic_16.png + + + package-x-generic_22.png + + + plugin-avail-16.png + + + plugin-avail-16@2x.png + + + plugin-avail-16~dark.png + + + plugin-avail-16~dark@2x.png + + + plugin-avail-16~sel.png + + + plugin-avail-16~sel@2x.png + + + plugin-avail-16~dark~sel.png + + + plugin-avail-16~dark~sel@2x.png + + + plugin-update-16.png + + + plugin-update-16@2x.png + + + plugin-update-16~dark.png + + + plugin-update-16~dark@2x.png + + + plugin-update-16~sel.png + + + plugin-update-16~sel@2x.png + + + plugin-update-16~dark~sel.png + + + plugin-update-16~dark~sel@2x.png + + + plugin-16.png + + + plugin-16@2x.png + + + plugin-16~dark.png + + + plugin-16~dark@2x.png + + + plugin-16~sel.png + + + plugin-16~sel@2x.png + + + plugin-16~dark~sel.png + + + plugin-16~dark~sel@2x.png + + + plugin-32.png + + + plugin-32@2x.png + + + plugin-32~dark.png + + + plugin-32~dark@2x.png + + + plugin-32~sel.png + + + plugin-32~sel@2x.png + + + plugin-32~dark~sel.png + + + plugin-32~dark~sel@2x.png + + + plugin-avail-32.png + + + plugin-update-32.png + + + plugin-update-32@2x.png + + + plugin-update-32~dark.png + + + plugin-update-32~dark@2x.png + + + plugin-update-32~sel.png + + + plugin-update-32~sel@2x.png + + + plugin-update-32~dark~sel.png + + + plugin-update-32~dark~sel@2x.png + + + plugin-disabled-32.png + + + plugin-disabled-32@2x.png + + + plugin-disabled-32~dark.png + + + plugin-disabled-32~dark@2x.png + + + plugin-disabled-32~sel@2x.png + + + plugin-disabled-32~sel.png + + + plugin-disabled-32~dark~sel@2x.png + + + plugin-disabled-32~dark~sel.png + + + download-16.png + + + download-16@2x.png + + + download-16~dark.png + + + download-16~dark@2x.png + + + plugin-22.png + + + plugin-22@2x.png + + + plugin-22~dark.png + + + plugin-22~dark@2x.png + + + plugin-22~sel.png + + + plugin-22~sel@2x.png + + + plugin-22~dark~sel.png + + + plugin-22~dark~sel@2x.png + + + plugin-update-22.png + + + plugin-update-22@2x.png + + + plugin-update-22~dark.png + + + plugin-update-22~dark@2x.png + + + plugin-update-22~sel.png + + + plugin-update-22~sel@2x.png + + + plugin-update-22~dark~sel.png + + + plugin-update-22~dark~sel@2x.png + + + update-available-overlay-16.png + + + update-available-overlay-16@2x.png + + + update-available-overlay-16~dark.png + + + update-available-overlay-16~dark@2x.png + + + update-available-overlay-16~sel.png + + + update-available-overlay-16~sel@2x.png + + + update-available-overlay-16~dark~sel.png + + + update-available-overlay-16~dark~sel@2x.png + + + update-16.png + + + update-16@2x.png + + + update-16~dark.png + + + update-16~dark@2x.png + + + installed-overlay-16.png + + + installed-overlay-16@2x.png + + + installed-overlay-16~dark.png + + + installed-overlay-16~dark@2x.png + + + installed-overlay-16~sel.png + + + installed-overlay-16~sel@2x.png + + + installed-overlay-16~dark~sel.png + + + installed-overlay-16~dark~sel@2x.png + + + web-search-16.png + + + web-search-16@2x.png + + + web-search-16~dark.png + + + web-search-16~dark@2x.png + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInfoView.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInfoView.cs new file mode 100644 index 00000000..04450378 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInfoView.cs @@ -0,0 +1,449 @@ +// +// AddinInfoView.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.IO; +using System.Collections.Generic; +using Mono.Addins.Setup; +using System.Text; +using Mono.Unix; +using System.Linq; + +namespace Mono.Addins.Gui +{ + [System.ComponentModel.ToolboxItem(true)] + partial class AddinInfoView : Gtk.Bin + { + List selectedEntry = new List (); + List selectedAddin = new List (); + SetupService service; + HeaderBox topHeaderBox; + List previewImages = new List (); + ImageContainer titleIcon; + int titleWidth; + string infoUrl; + + public event EventHandler InstallClicked; + public event EventHandler UninstallClicked; + public event EventHandler UpdateClicked; + public event EventHandler EnableDisableClicked; + + public AddinInfoView () + { + this.Build (); + AllowInstall = true; + titleWidth = labelName.SizeRequest ().Width; + + HeaderBox hb = new HeaderBox (1,1,1,1); + hb.Show (); + hb.Replace (this); + + hb = new HeaderBox (1,0,0,0); + hb.SetPadding (6,6,6,6); + hb.Show (); + hb.GradientBackround = true; + hb.Replace (eboxButs); + + hb = new HeaderBox (0,1,0,0); + hb.SetPadding (6,6,6,6); + hb.Show (); + hb.GradientBackround = true; + hb.Replace (boxHeader); + topHeaderBox = hb; + } + + public void Init (SetupService service) + { + this.service = service; + } + + public bool AllowInstall { get; set; } + + public List SelectedEntries { + get { + return this.selectedEntry; + } + } + + public List SelectedAddins { + get { + return this.selectedAddin; + } + } + + public void ShowAddins (object[] data) + { + selectedEntry.Clear (); + selectedAddin.Clear (); + eboxButs.Visible = true; + topHeaderBox.Hide (); + urlButton.Hide (); + + if (titleIcon != null) { + boxTitle.Remove (titleIcon); + titleIcon.Destroy (); + titleIcon = null; + } + + foreach (var img in previewImages) { + ((Gtk.Container)img.Parent).Remove (img); + img.Destroy (); + } + previewImages.Clear (); + + if (data.Length == 1) { + headerBox.Show (); + ShowAddin (data[0]); + } + else if (data.Length > 1) { + headerBox.Hide (); + StringBuilder sb = new StringBuilder (); + sb.Append (Catalog.GetString ("Multiple selection:\n\n")); + bool allowUpdate = AllowInstall; + bool allowInstall = true; + bool allowUninstall = AllowInstall; + bool allowEnable = true; + bool allowDisable = true; + + foreach (object o in data) { + Addin installed; + if (o is Addin) { + Addin a = (Addin)o; + installed = a; + selectedAddin.Add (a); + sb.Append (a.Name); + } + else { + AddinRepositoryEntry entry = (AddinRepositoryEntry) o; + selectedEntry.Add (entry); + sb.Append (entry.Addin.Name); + installed = AddinManager.Registry.GetAddin (Addin.GetIdName (entry.Addin.Id)); + } + if (installed != null) { + if (GetUpdate (installed) == null) + allowUpdate = false; + allowInstall = false; + if (installed.Enabled) + allowEnable = false; + else + allowDisable = false; + } else + allowEnable = allowDisable = allowUninstall = allowUpdate = false; + + sb.Append ('\n'); + labelDesc.Text = sb.ToString (); + + if (allowEnable) { + btnDisable.Visible = true; + btnDisable.Label = Catalog.GetString ("Enable"); + } else if (allowDisable) { + btnDisable.Visible = true; + btnDisable.Label = Catalog.GetString ("Disable"); + } else + btnDisable.Visible = false; + btnInstall.Visible = allowInstall; + btnUninstall.Visible = allowUninstall; + btnUpdate.Visible = allowUpdate; + } + } + else { + headerBox.Hide (); + btnDisable.Visible = false; + btnInstall.Visible = false; + btnUninstall.Visible = false; + btnUpdate.Visible = false; + eboxButs.Visible = false; + labelDesc.Text = Catalog.GetString ("No selection"); + } + } + + + void ShowAddin (object data) + { + AddinHeader sinfo = null; + Addin installed = null; + AddinHeader updateInfo = null; + string repo = ""; + string downloadSize = null; + + topHeaderBox.Hide (); + + if (data is Addin) { + installed = (Addin) data; + sinfo = SetupService.GetAddinHeader (installed); + var entry = GetUpdate (installed); + if (entry != null) { + updateInfo = entry.Addin; + selectedEntry.Add (entry); + } + foreach (var prop in sinfo.Properties) { + if (prop.Name.StartsWith ("PreviewImage")) + previewImages.Add (new ImageContainer (installed, prop.Value)); + } + string icon32 = sinfo.Properties.GetPropertyValue ("Icon32"); + if (icon32.Length > 0) + titleIcon = new ImageContainer (installed, icon32); + } + else if (data is AddinRepositoryEntry) { + AddinRepositoryEntry entry = (AddinRepositoryEntry) data; + sinfo = entry.Addin; + installed = AddinManager.Registry.GetAddin (Addin.GetIdName (sinfo.Id)); + if (installed != null && Addin.CompareVersions (installed.Version, sinfo.Version) > 0) + updateInfo = sinfo; + selectedEntry.Add (entry); + string rname = !string.IsNullOrEmpty (entry.RepositoryName) ? entry.RepositoryName : entry.RepositoryUrl; + repo = "" + Catalog.GetString ("Available in repository:") + "\n" + GLib.Markup.EscapeText (rname) + "\n\n"; + foreach (var prop in sinfo.Properties) { + if (prop.Name.StartsWith ("PreviewImage")) + previewImages.Add (new ImageContainer (entry, prop.Value)); + } + string icon32 = sinfo.Properties.GetPropertyValue ("Icon32"); + if (icon32.Length > 0) + titleIcon = new ImageContainer (entry, icon32); + int size; + if (int.TryParse (sinfo.Properties.GetPropertyValue ("DownloadSize"), out size)) { + float fs = ((float)size) / 1048576f; + downloadSize = fs.ToString ("0.00 MB"); + } + } else + selectedEntry.Clear (); + + if (installed != null) + selectedAddin.Add (installed); + + string missingDepsTxt = null; + + if (sinfo == null) { + btnDisable.Visible = false; + btnUninstall.Visible = false; + btnUpdate.Visible = false; + } else { + string version; + string newVersion = null; + if (installed != null) { + btnInstall.Visible = false; + btnUpdate.Visible = updateInfo != null && AllowInstall; + btnDisable.Visible = true; + btnDisable.Label = installed.Enabled ? Catalog.GetString ("Disable") : Catalog.GetString ("Enable"); + btnDisable.Visible = installed.Description.CanDisable; + btnUninstall.Visible = installed.Description.CanUninstall; + version = installed.Version; + var missingDeps = Services.GetMissingDependencies (installed); + if (updateInfo != null) { + newVersion = updateInfo.Version; + labelHeader.Markup = "" + Catalog.GetString ("Update available") + ""; +// topHeaderBox.BackgroundColor = new Gdk.Color (0, 132, 208); + imageHeader.Pixbuf = Gdk.Pixbuf.LoadFromResource ("update-16.png"); + topHeaderBox.BackgroundColor = new Gdk.Color (255, 176, 0); + topHeaderBox.Show (); + } + else if (missingDeps.Any ()) { + labelHeader.Markup = "" + Catalog.GetString ("This extension package can't be loaded due to missing dependencies") + ""; + topHeaderBox.BackgroundColor = new Gdk.Color (255, 176, 0); + imageHeader.SetFromStock (Gtk.Stock.DialogWarning, Gtk.IconSize.Menu); + topHeaderBox.Show (); + missingDepsTxt = ""; + foreach (var mdep in missingDeps) { + if (mdep.Found != null) + missingDepsTxt += "\n" + string.Format (Catalog.GetString ("Required: {0} v{1}, found v{2}"), mdep.Addin, mdep.Required, mdep.Found); + else + missingDepsTxt += "\n" + string.Format (Catalog.GetString ("Missing: {0} v{1}"), mdep.Addin, mdep.Required); + } + } + } else { + btnInstall.Visible = AllowInstall; + btnUpdate.Visible = false; + btnDisable.Visible = false; + btnUninstall.Visible = false; + version = sinfo.Version; + } + labelName.Markup = "" + GLib.Markup.EscapeText(sinfo.Name) + ""; + + string ver; + if (newVersion != null) { + ver = "" + Catalog.GetString ("Installed version") + ": " + version + "\n"; + ver += "" + Catalog.GetString ("Repository version") + ": " + newVersion + ""; + } + else + ver = "" + Catalog.GetString ("Version") + " " + version + ""; + + if (downloadSize != null) + ver += "\n" + Catalog.GetString ("Download size") + ": " + downloadSize + ""; + if (missingDepsTxt != null) + ver += "\n\n" + GLib.Markup.EscapeText (Catalog.GetString ("The following dependencies required by this extension package are not available:")) + missingDepsTxt; + labelVersion.Markup = ver; + + string desc = GLib.Markup.EscapeText (sinfo.Description); + labelDesc.Markup = repo + GLib.Markup.EscapeText (desc); + + foreach (var img in previewImages) + vboxDesc.PackStart (img, false, false, 0); + + urlButton.Visible = !string.IsNullOrEmpty (sinfo.Url); + infoUrl = sinfo.Url; + + if (titleIcon != null) { + boxTitle.PackEnd (titleIcon, false, false, 0); + labelName.WidthRequest = titleWidth - 32; + labelVersion.WidthRequest = titleWidth - 32; + } else { + labelName.WidthRequest = titleWidth; + labelVersion.WidthRequest = titleWidth; + } + + if (IsRealized) + SetComponentsBg (); + } + } + + public AddinRepositoryEntry GetUpdate (Addin a) + { + AddinRepositoryEntry[] updates = service.Repositories.GetAvailableAddinUpdates (Addin.GetIdName (a.Id)); + AddinRepositoryEntry best = null; + string bestVersion = a.Version; + foreach (AddinRepositoryEntry e in updates) { + if (Addin.CompareVersions (bestVersion, e.Addin.Version) > 0) { + best = e; + bestVersion = e.Addin.Version; + } + } + return best; + } + + protected virtual void OnBtnInstallClicked (object sender, System.EventArgs e) + { + if (InstallClicked != null) + InstallClicked (this, e); + } + + protected virtual void OnBtnDisableClicked (object sender, System.EventArgs e) + { + if (EnableDisableClicked != null) + EnableDisableClicked (this, e); + } + + protected virtual void OnBtnUpdateClicked (object sender, System.EventArgs e) + { + if (UpdateClicked != null) + UpdateClicked (this, e); + } + + protected virtual void OnBtnUninstallClicked (object sender, System.EventArgs e) + { + if (UninstallClicked != null) + UninstallClicked (this, e); + } + + protected override void OnRealized () + { + base.OnRealized (); + HslColor gcol = ebox.Style.Background (Gtk.StateType.Normal); + gcol.L -= 0.03; + ebox.ModifyBg (Gtk.StateType.Normal, gcol); + ebox2.ModifyBg (Gtk.StateType.Normal, gcol); + scrolledwindow.ModifyBg (Gtk.StateType.Normal, gcol); + SetComponentsBg (); + } + + void SetComponentsBg () + { + HslColor gcol = ebox.Style.Background (Gtk.StateType.Normal); + //gcol.L -= 0.03; + if (titleIcon != null) + titleIcon.ModifyBg (Gtk.StateType.Normal, gcol); + foreach (var i in previewImages) + i.ModifyBg (Gtk.StateType.Normal, gcol); + } + + protected virtual void OnUrlButtonClicked (object sender, System.EventArgs e) + { + System.Diagnostics.Process.Start (infoUrl); + } + } + + class ImageContainer: Gtk.EventBox + { + AddinRepositoryEntry aentry; + IAsyncResult aresult; + Gtk.Image image; + bool destroyed; + + ImageContainer () + { + image = new Gtk.Image (); + Add (image); + image.SetAlignment (0.5f, 0f); + Show (); + } + + public ImageContainer (AddinRepositoryEntry aentry, string fileName): this () + { + this.aentry = aentry; + aresult = aentry.BeginDownloadSupportFile (fileName, ImageDownloaded, null); + } + + public ImageContainer (Addin addin, string fileName): this () + { + string path = System.IO.Path.Combine (addin.Description.BasePath, fileName); + LoadImage (File.OpenRead (path)); + } + + void ImageDownloaded (object state) + { + Gtk.Application.Invoke ((o, args) => { + if (destroyed) + return; + try { + LoadImage (aentry.EndDownloadSupportFile (aresult)); + } catch { + // ignore + } + }); + } + + void LoadImage (Stream s) + { + using (s) { + Gdk.PixbufLoader loader = new Gdk.PixbufLoader (s); + Gdk.Pixbuf pix = image.Pixbuf = loader.Pixbuf; + loader.Dispose (); + if (pix.Width > 250) { + Gdk.Pixbuf spix = pix.ScaleSimple (250, (250 * pix.Height) / pix.Width, Gdk.InterpType.Hyper); + pix.Dispose (); + pix = spix; + } + image.Pixbuf = pix; + image.Show (); + } + } + + protected override void OnDestroyed () + { + destroyed = true; + base.OnDestroyed (); + } + } +} + diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInstaller.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInstaller.cs new file mode 100644 index 00000000..b0826be8 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInstaller.cs @@ -0,0 +1,25 @@ + + +using System; +using Mono.Addins.Setup; +using Mono.Unix; + +namespace Mono.Addins.Gui +{ + public class AddinInstaller: IAddinInstaller + { + public void InstallAddins (AddinRegistry reg, string message, string[] addinIds) + { + AddinInstallerDialog dlg = new AddinInstallerDialog (reg, message, addinIds); + try { + if (dlg.Run () == (int) Gtk.ResponseType.Cancel) + throw new InstallException (Catalog.GetString ("Installation cancelled")); + else if (dlg.ErrMessage != null) + throw new InstallException (dlg.ErrMessage); + } + finally { + dlg.Destroy (); + } + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInstallerDialog.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInstallerDialog.cs new file mode 100644 index 00000000..7dfe2901 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinInstallerDialog.cs @@ -0,0 +1,178 @@ +// +// AddinInstallerDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Threading; +using System.Collections; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using Mono.Unix; + +namespace Mono.Addins.Gui +{ + internal partial class AddinInstallerDialog : Gtk.Dialog, IProgressStatus + { + PackageCollection entries = new PackageCollection (); + string[] addinIds; + bool addinsNotFound; + string errMessage; + SetupService setup; + + public AddinInstallerDialog (AddinRegistry reg, string message, string[] addinIds) + { + this.Build(); + + this.addinIds = addinIds; + setup = new SetupService (reg); + + if (!CheckAddins (true)) + UpdateRepos (); + } + + bool CheckAddins (bool updating) + { + string txt = ""; + entries.Clear (); + bool addinsNotFound = false; + foreach (string id in addinIds) { + string name = Addin.GetIdName (id); + string version = Addin.GetIdVersion (id); + AddinRepositoryEntry[] ares = setup.Repositories.GetAvailableAddin (name, version); + if (ares.Length == 0) { + addinsNotFound = true; + if (updating) + txt += "" + name + " " + version + " (searching extension packages)\n"; + else + txt += "" + name + " " + version + " (not found)\n"; + } else { + entries.Add (Package.FromRepository (ares[0])); + txt += "" + ares[0].Addin.Name + " " + ares[0].Addin.Version + "\n"; + } + } + PackageCollection toUninstall; + DependencyCollection unresolved; + if (!setup.ResolveDependencies (this, entries, out toUninstall, out unresolved)) { + foreach (Dependency dep in unresolved) { + txt += "" + dep.Name + " (not found)\n"; + } + addinsNotFound = true; + } + addinList.Markup = txt; + return !addinsNotFound; + } + + void UpdateRepos () + { + progressBar.Show (); + setup.Repositories.UpdateAllRepositories (this); + progressBar.Hide (); + addinsNotFound = CheckAddins (false); + if (errMessage != null) { + Services.ShowError (null, errMessage, this, true); + errMessage = null; + } + } + + public int LogLevel { + get { + return 1; + } + } + + public bool IsCanceled { + get { + return false; + } + } + + public bool AddinsNotFound { + get { + return addinsNotFound; + } + } + + public string ErrMessage { + get { + return errMessage; + } + } + + public void SetMessage (string msg) + { + progressBar.Text = msg; + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + + public void SetProgress (double progress) + { + progressBar.Fraction = progress; + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + + public void Log (string msg) + { + } + + public void ReportWarning (string message) + { + } + + public void ReportError (string message, System.Exception exception) + { + errMessage = message; + } + + public void Cancel () + { + } + + protected virtual void OnButtonOkClicked (object sender, System.EventArgs e) + { + if (addinsNotFound) { + errMessage = Catalog.GetString ("Some of the required extension packages were not found"); + Respond (Gtk.ResponseType.Ok); + } + else { + errMessage = null; + progressBar.Show (); + progressBar.Fraction = 0; + progressBar.Text = ""; + bool res = setup.Install (this, entries); + if (!res) { + buttonCancel.Sensitive = buttonOk.Sensitive = false; + if (errMessage == null) + errMessage = Catalog.GetString ("Installation failed"); + Services.ShowError (null, errMessage, this, true); + } + } + Respond (Gtk.ResponseType.Ok); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinManagerDialog.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinManagerDialog.cs new file mode 100644 index 00000000..59c8a035 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinManagerDialog.cs @@ -0,0 +1,580 @@ +// +// AddinManagerDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; +using Mono.Addins.Setup; +using Mono.Addins; +using Mono.Unix; +using System.Threading; +using System.Text; +using System.Collections.Generic; +using System.Linq; + +namespace Mono.Addins.Gui +{ + partial class AddinManagerDialog : Dialog, IDisposable + { + AddinTreeWidget tree; + AddinTreeWidget galleryTree; + AddinTreeWidget updatesTree; + + SetupService service = new SetupService (); + ListStore repoStore; + int lastRepoActive; + SearchEntry filterEntry; + Label installedTabLabel; + Label updatesTabLabel; + Label galleryTabLabel; + + static bool firstLoad = true; + + const string AllRepoMarker = "__ALL"; + const string ManageRepoMarker = "__MANAGE"; + + internal bool AllowInstall + { + set { + addininfoInstalled.AllowInstall = value; + addininfoGallery.AllowInstall = value; + addininfoUpdates.AllowInstall = value; + } + } + + public AddinManagerDialog (Window parent) + { + Build (); + TransientFor = parent; + HasSeparator = false; + Services.PlaceDialog (this, parent); + + addininfoInstalled.Init (service); + addininfoGallery.Init (service); + + addinTree.Selection.Mode = SelectionMode.Multiple; + tree = new AddinTreeWidget (addinTree); + addinTree.Selection.Changed += OnSelectionChanged; + tree.VersionVisible = false; + + galleryTreeView.Selection.Mode = SelectionMode.Multiple; + galleryTree = new AddinTreeWidget (galleryTreeView); + galleryTree.VersionVisible = false; + galleryTree.ShowInstalledMarkers = true; + galleryTreeView.Selection.Changed += OnGallerySelectionChanged; + + updatesTreeView.Selection.Mode = SelectionMode.Multiple; + updatesTree = new AddinTreeWidget (updatesTreeView); + updatesTree.VersionVisible = false; + updatesTree.ShowCategories = false; + updatesTree.ShowInstalledMarkers = true; + updatesTreeView.Selection.Changed += OnGallerySelectionChanged; + + repoStore = new ListStore (typeof(string), typeof(string)); + repoCombo.Model = repoStore; + CellRendererText crt = new CellRendererText (); + repoCombo.PackStart (crt, true); + repoCombo.AddAttribute (crt, "text", 0); + repoCombo.RowSeparatorFunc = delegate(TreeModel model, TreeIter iter) { + string val = (string) model.GetValue (iter, 0); + return val == "---"; + }; + + // Make sure the tree has the focus when switching tabs + + vboxUpdates.FocusChain = new Widget [] { scrolledUpdates, eboxRepoUpdates }; + vboxGallery.FocusChain = new Widget [] { scrolledGallery, eboxRepo }; + + // Improve the look of the headers + + HBox tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("plugin-22.png")), false, false, 0); + installedTabLabel = new Label (Catalog.GetString ("Installed")); + tab.PackStart (installedTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (0), tab); + + tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("plugin-update-22.png")), false, false, 0); + updatesTabLabel = new Label (Catalog.GetString ("Updates")); + tab.PackStart (updatesTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (1), tab); + + tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("update-16.png")), false, false, 0); + galleryTabLabel = new Label (Catalog.GetString ("Gallery")); + tab.PackStart (galleryTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (2), tab); + + // Gradient header for the updates and gallery tabs + + HeaderBox hb = new HeaderBox (1, 0, 1, 1); + hb.SetPadding (6,6,6,6); + hb.GradientBackround = true; + hb.Show (); + hb.Replace (eboxRepo); + + hb = new HeaderBox (1, 0, 1, 1); + hb.SetPadding (6,6,6,6); + hb.GradientBackround = true; + hb.Show (); + hb.Replace (eboxRepoUpdates); + + InsertFilterEntry (); + + FillRepos (); + repoCombo.Active = 0; + + LoadAll (); + } + + void InsertFilterEntry () + { + filterEntry = new SearchEntry (); + filterEntry.Entry.SetSizeRequest (200, filterEntry.Entry.SizeRequest ().Height); + filterEntry.SizeAllocated += (o, args) => { + RepositionFilter (); + }; + ActionArea.PackEnd (filterEntry); + var btnCloseBoxChild = ((Box.BoxChild)(ActionArea [btnClose])); + btnCloseBoxChild.Position = 2; + filterEntry.Show (); + + notebook.SizeAllocated += delegate { + RepositionFilter (); + }; + filterEntry.TextChanged += delegate { + tree.SetFilter (filterEntry.Text); + galleryTree.SetFilter (filterEntry.Text); + updatesTree.SetFilter (filterEntry.Text); + LoadAll (); + addinTree.ExpandAll (); + galleryTreeView.ExpandAll (); + }; + RepositionFilter (); + } + + protected override void OnShown () + { + base.OnShown (); + filterEntry.Parent = notebook; + } + + void RepositionFilter () + { + int w = filterEntry.SizeRequest ().Width; + int h = filterEntry.SizeRequest ().Height; + var alloc = notebook.Allocation; + filterEntry.Allocation = new Gdk.Rectangle (alloc.Left + alloc.Width - 1 - w, alloc.Y, w, h); + } + + public override void Dispose () + { + base.Dispose (); + Destroy (); + } + + internal void OnSelectionChanged (object sender, EventArgs args) + { + UpdateAddinInfo (); + } + + internal void OnManageRepos (object sender, EventArgs e) + { + ManageSitesDialog dlg = new ManageSitesDialog (this, service); + try { + dlg.Run (); + } finally { + dlg.Destroy (); + } + } + + void LoadAll () + { + LoadInstalled (); + LoadGallery (); + LoadUpdates (); + UpdateAddinInfo (); + } + + void UpdateAddinInfo () + { + addininfoInstalled.ShowAddins (tree.ActiveAddinsData); + addininfoGallery.ShowAddins (galleryTree.ActiveAddinsData); + addininfoUpdates.ShowAddins (updatesTree.ActiveAddinsData); + } + + void LoadInstalled () + { + object s = tree.SaveStatus (); + + int count = 0; + tree.Clear (); + foreach (Addin ainfo in AddinManager.Registry.GetModules (AddinSearchFlags.IncludeAddins | AddinSearchFlags.LatestVersionsOnly)) { + if (Services.InApplicationNamespace (service, ainfo.Id) && !ainfo.Description.IsHidden) { + AddinHeader ah = SetupService.GetAddinHeader (ainfo); + if (IsFiltered (ah)) + continue; + AddinStatus st = AddinStatus.Installed; + if (!ainfo.Enabled || Services.GetMissingDependencies (ainfo).Any()) + st |= AddinStatus.Disabled; + if (addininfoInstalled.GetUpdate (ainfo) != null) + st |= AddinStatus.HasUpdate; + tree.AddAddin (ah, ainfo, st); + count++; + } + } + + if (count > 0) + tree.RestoreStatus (s); + else + tree.ShowEmptyMessage (); + + UpdateAddinInfo (); + + installedTabLabel.Text = Catalog.GetString ("Installed"); + + if (filterEntry.Text.Length != 0 && count > 0) + installedTabLabel.Text += " (" + count + ")"; + } + + void FillRepos () + { + int i = repoCombo.Active; + repoStore.Clear (); + + repoStore.AppendValues (Catalog.GetString ("All repositories"), AllRepoMarker); + + foreach (AddinRepository rep in service.Repositories.GetRepositories ()) { + if (rep.Enabled) + repoStore.AppendValues (rep.Title, rep.Url); + } + repoStore.AppendValues ("---", ""); + repoStore.AppendValues (Catalog.GetString ("Manage Repositories..."), ManageRepoMarker); + repoCombo.Active = i; + } + + string GetRepoSelection () + { + Gtk.TreeIter iter; + if (!repoCombo.GetActiveIter (out iter)) + return null; + return (string) repoStore.GetValue (iter, 1); + } + + void LoadGallery () + { + object s = galleryTree.SaveStatus (); + + galleryTree.Clear (); + + string rep = GetRepoSelection (); + + AddinRepositoryEntry[] reps; + if (rep == AllRepoMarker) + reps = service.Repositories.GetAvailableAddins (RepositorySearchFlags.LatestVersionsOnly); + else + reps = service.Repositories.GetAvailableAddins (rep, RepositorySearchFlags.LatestVersionsOnly); + + int count = 0; + + foreach (AddinRepositoryEntry arep in reps) + { + if (!Services.InApplicationNamespace (service, arep.Addin.Id)) + continue; + + if (IsFiltered (arep.Addin)) + continue; + + AddinStatus status = AddinStatus.NotInstalled; + + // Find whatever version is installed + Addin sinfo = AddinManager.Registry.GetAddin (Addin.GetIdName (arep.Addin.Id)); + + if (sinfo != null) { + status |= AddinStatus.Installed; + if (!sinfo.Enabled || Services.GetMissingDependencies (sinfo).Any()) + status |= AddinStatus.Disabled; + if (Addin.CompareVersions (sinfo.Version, arep.Addin.Version) > 0) + status |= AddinStatus.HasUpdate; + } + galleryTree.AddAddin (arep.Addin, arep, status); + count++; + } + + if (count > 0) + galleryTree.RestoreStatus (s); + else + galleryTree.ShowEmptyMessage (); + + galleryTabLabel.Text = Catalog.GetString ("Gallery"); + + if (filterEntry.Text.Length != 0 && count > 0) + galleryTabLabel.Text += " (" + count + ")"; + } + + void LoadUpdates () + { + object s = updatesTree.SaveStatus (); + + updatesTree.Clear (); + + AddinRepositoryEntry[] reps; + reps = service.Repositories.GetAvailableAddins (RepositorySearchFlags.LatestVersionsOnly); + + int count = 0; + + foreach (AddinRepositoryEntry arep in reps) + { + if (!Services.InApplicationNamespace (service, arep.Addin.Id)) + continue; + + // Find whatever version is installed + Addin sinfo = AddinManager.Registry.GetAddin (Addin.GetIdName (arep.Addin.Id)); + if (sinfo == null || !sinfo.Enabled || Addin.CompareVersions (sinfo.Version, arep.Addin.Version) <= 0) + continue; + + if (IsFiltered (arep.Addin)) + continue; + + AddinStatus status = AddinStatus.Installed; + if (!sinfo.Enabled || Services.GetMissingDependencies (sinfo).Any()) + status |= AddinStatus.Disabled; + + updatesTree.AddAddin (arep.Addin, arep, status | AddinStatus.HasUpdate); + count++; + } + + labelUpdates.Text = string.Format (Catalog.GetPluralString ("{0} update available", "{0} updates available", count), count); + updatesTabLabel.Text = Catalog.GetString ("Updates"); + if (count > 0) + updatesTabLabel.Text += " (" + count + ")"; + + buttonUpdateAll.Visible = count > 0; + + if (count > 0) + updatesTree.RestoreStatus (s); + else + updatesTree.ShowEmptyMessage (); + } + + bool IsFiltered (AddinHeader ah) + { + if (filterEntry.Text.Length == 0) + return false; + if (ah.Name.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + if (ah.Description.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + if (ah.Id.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + return true; + } + + void ManageSites () + { + ManageSitesDialog dlg = new ManageSitesDialog (this, service); + try { + dlg.Run (); + repoCombo.Active = lastRepoActive; + FillRepos (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnRepoComboChanged (object sender, System.EventArgs e) + { + if (GetRepoSelection () == ManageRepoMarker) + ManageSites (); + else + LoadGallery (); + lastRepoActive = repoCombo.Active; + } + + protected virtual void OnGallerySelectionChanged (object sender, System.EventArgs e) + { + UpdateAddinInfo (); + } + + void UpdateRepositories () + { + ProgressDialog pdlg = new ProgressDialog (this); + if (!firstLoad) + pdlg.Show (); + pdlg.SetMessage (AddinManager.CurrentLocalizer.GetString ("Updating repository")); + bool updateDone = buttonRefresh.Sensitive = false; + + Thread t = new Thread (delegate () { + try { + service.Repositories.UpdateAllRepositories (pdlg); + } finally { + updateDone = true; + } + }); + t.Start (); + while (!updateDone) { + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + Thread.Sleep (50); + } + buttonRefresh.Sensitive = true; + pdlg.Destroy (); + } + + protected virtual void OnButtonRefreshClicked (object sender, System.EventArgs e) + { + UpdateRepositories (); + LoadGallery (); + LoadUpdates (); + } + + protected virtual void OnInstallClicked (object sender, System.EventArgs e) + { + InstallDialog dlg = new InstallDialog (this, service); + try { + List selectedEntry = ((AddinInfoView)sender).SelectedEntries; + dlg.InitForInstall (selectedEntry.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnUninstallClicked (object sender, System.EventArgs e) + { + List selectedAddin = ((AddinInfoView)sender).SelectedAddins; + InstallDialog dlg = new InstallDialog (this, service); + try { + dlg.InitForUninstall (selectedAddin.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) { + LoadAll (); + } + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnUpdateClicked (object sender, System.EventArgs e) + { + List selectedEntry = ((AddinInfoView)sender).SelectedEntries; + InstallDialog dlg = new InstallDialog (this, service); + try { + dlg.InitForInstall (selectedEntry.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnEnableDisableClicked (object sender, System.EventArgs e) + { + try { + foreach (Addin a in ((AddinInfoView)sender).SelectedAddins) { + a.Enabled = !a.Enabled; + } + LoadAll (); + } + catch (Exception ex) { + Services.ShowError (ex, null, this, true); + } + } + + protected virtual void OnUpdateAll (object sender, System.EventArgs e) + { + object[] data = updatesTree.AddinsData; + AddinRepositoryEntry[] entries = new AddinRepositoryEntry [data.Length]; + Array.Copy (data, entries, data.Length); + InstallDialog dlg = new InstallDialog (this, service); + try { + dlg.InitForInstall (entries); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + static string lastFolder; + + protected virtual void OnButtonInstallFromFileClicked (object sender, System.EventArgs e) + { + string[] files; + Gtk.FileChooserDialog dlg = new Gtk.FileChooserDialog (Catalog.GetString ("Install Extension Package"), this, FileChooserAction.Open); + try { + if (lastFolder != null) + dlg.SetCurrentFolder (lastFolder); + else + dlg.SetCurrentFolder (Environment.GetFolderPath (Environment.SpecialFolder.Personal)); + dlg.SelectMultiple = true; + + Gtk.FileFilter f = new Gtk.FileFilter (); + f.AddPattern ("*.mpack"); + f.Name = Catalog.GetString ("Extension packages"); + dlg.AddFilter (f); + + f = new Gtk.FileFilter (); + f.AddPattern ("*"); + f.Name = Catalog.GetString ("All files"); + dlg.AddFilter (f); + + dlg.AddButton (Gtk.Stock.Cancel, ResponseType.Cancel); + dlg.AddButton (Gtk.Stock.Open, ResponseType.Ok); + if (dlg.Run () != (int) Gtk.ResponseType.Ok) + return; + files = dlg.Filenames; + lastFolder = dlg.CurrentFolder; + } finally { + dlg.Destroy (); + } + + InstallDialog idlg = new InstallDialog (this, service); + try { + idlg.InitForInstall (files); + if (idlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + idlg.Destroy (); + } + } + + protected void OnNotebookSwitchPage (object o, SwitchPageArgs args) + { + if (args.PageNum == 2 && firstLoad) { + UpdateRepositories (); + firstLoad = false; + } + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinManagerWindow.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinManagerWindow.cs new file mode 100644 index 00000000..980eed63 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinManagerWindow.cs @@ -0,0 +1,83 @@ +// +// AddinManagerWindow.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins.Gui +{ + public class AddinManagerWindow + { + private static bool mAllowInstall = true; + + public static bool AllowInstall + { + get { return mAllowInstall; } + set { mAllowInstall = value; } + } + + private AddinManagerWindow() + { + } + + private static void InitDialog (AddinManagerDialog dlg) + { + dlg.AllowInstall = AllowInstall; + } + + public static Gtk.Window Show (Gtk.Window parent) + { + AddinManagerDialog dlg = new AddinManagerDialog (parent); + InitDialog (dlg); + dlg.Show (); + return dlg; + } + + public static void Run (Gtk.Window parent) + { + AddinManagerDialog dlg = new AddinManagerDialog (parent); + try { + InitDialog (dlg); + dlg.Run (); + } finally { + dlg.Destroy (); + } + } + + public static int RunToInstallFile (Gtk.Window parent, Setup.SetupService service, string file) + { + var dlg = new InstallDialog (parent, service); + try { + dlg.InitForInstall (new [] { file }); + return dlg.Run (); + } finally { + dlg.Destroy (); + } + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinTreeWidget.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinTreeWidget.cs new file mode 100644 index 00000000..05238fe6 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/AddinTreeWidget.cs @@ -0,0 +1,572 @@ +// +// AddinTreeWidget.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using Gtk; +using Gdk; +using Mono.Addins; +using Mono.Addins.Setup; +using Mono.Unix; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Mono.Addins.Gui +{ + public class AddinTreeWidget + { + protected Gtk.TreeView treeView; + protected Gtk.TreeStore treeStore; + bool allowSelection; + ArrayList selected = new ArrayList (); + Hashtable addinData = new Hashtable (); + TreeViewColumn versionColumn; + string filter; + Dictionary cachedIcons = new Dictionary (); + bool disposed; + + Gdk.Pixbuf iconInstalled; + Gdk.Pixbuf updateOverlay; + Gdk.Pixbuf installedOverlay; + + public event EventHandler SelectionChanged; + + const int ColAddin = 0; + const int ColData = 1; + const int ColName = 2; + const int ColVersion = 3; + const int ColAllowSelection = 4; + const int ColSelected = 5; + const int ColImage = 6; + const int ColShowImage = 7; + + public AddinTreeWidget (Gtk.TreeView treeView) + { + iconInstalled = Gdk.Pixbuf.LoadFromResource ("plugin-32.png"); + updateOverlay = Gdk.Pixbuf.LoadFromResource ("update-available-overlay-16.png"); + installedOverlay = Gdk.Pixbuf.LoadFromResource ("installed-overlay-16.png"); + + this.treeView = treeView; + ArrayList list = new ArrayList (); + AddStoreTypes (list); + Type[] types = (Type[]) list.ToArray (typeof(Type)); + treeStore = new Gtk.TreeStore (types); + treeView.Model = treeStore; + CreateColumns (); + ShowCategories = true; + + treeView.Destroyed += HandleTreeViewDestroyed; + } + + void HandleTreeViewDestroyed (object sender, EventArgs e) + { + disposed = true; + foreach (var px in cachedIcons.Values) + if (px != null) px.Dispose (); + } + + internal void SetFilter (string text) + { + this.filter = text; + } + + internal void ShowEmptyMessage () + { + treeStore.AppendValues (null, null, Catalog.GetString ("No extension packages found"), "", false, false, null, false); + } + + protected virtual void AddStoreTypes (ArrayList list) + { + list.Add (typeof(object)); + list.Add (typeof(object)); + list.Add (typeof(string)); + list.Add (typeof(string)); + list.Add (typeof(bool)); + list.Add (typeof(bool)); + list.Add (typeof (Pixbuf)); + list.Add (typeof(bool)); + } + + protected virtual void CreateColumns () + { + TreeViewColumn col = new TreeViewColumn (); + col.Title = Catalog.GetString ("Extension Package"); + + CellRendererToggle crtog = new CellRendererToggle (); + crtog.Activatable = true; + crtog.Toggled += new ToggledHandler (OnAddinToggled); + col.PackStart (crtog, false); + + CellRendererPixbuf pr = new CellRendererPixbuf (); + col.PackStart (pr, false); + col.AddAttribute (pr, "pixbuf", ColImage); + col.AddAttribute (pr, "visible", ColShowImage); + + CellRendererText crt = new CellRendererText (); + crt.Ellipsize = Pango.EllipsizeMode.End; + col.PackStart (crt, true); + + col.AddAttribute (crt, "markup", ColName); + col.AddAttribute (crtog, "visible", ColAllowSelection); + col.AddAttribute (crtog, "active", ColSelected); + col.Expand = true; + treeView.AppendColumn (col); + + col = new TreeViewColumn (); + col.Title = Catalog.GetString ("Version"); + col.PackStart (crt, true); + col.AddAttribute (crt, "markup", ColVersion); + versionColumn = col; + treeView.AppendColumn (col); + } + + public bool AllowSelection { + get { return allowSelection; } + set { allowSelection = value; } + } + + public bool VersionVisible { + get { + return versionColumn.Visible; + } + set { + versionColumn.Visible = value; + treeView.HeadersVisible = value; + } + } + + public bool ShowCategories { get; set; } + + void OnAddinToggled (object o, ToggledArgs args) + { + TreeIter it; + if (treeStore.GetIter (out it, new TreePath (args.Path))) { + bool sel = !(bool) treeStore.GetValue (it, 5); + treeStore.SetValue (it, 5, sel); + AddinHeader info = (AddinHeader) treeStore.GetValue (it, 0); + if (sel) + selected.Add (info); + else + selected.Remove (info); + + OnSelectionChanged (EventArgs.Empty); + } + } + + protected virtual void OnSelectionChanged (EventArgs e) + { + if (SelectionChanged != null) + SelectionChanged (this, e); + } + + public void Clear () + { + addinData.Clear (); + selected.Clear (); + treeStore.Clear (); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, bool enabled) + { + return AddAddin (info, dataItem, enabled, true); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, bool enabled, bool userDir) + { + return AddAddin (info, dataItem, enabled ? AddinStatus.Installed : AddinStatus.Disabled | AddinStatus.Installed); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, AddinStatus status) + { + addinData [info] = dataItem; + TreeIter iter; + if (ShowCategories) { + TreeIter piter = TreeIter.Zero; + if (info.Category == "") { + string otherCat = Catalog.GetString ("Other"); + piter = FindCategory (otherCat); + } else { + piter = FindCategory (info.Category); + } + iter = treeStore.AppendNode (piter); + } else { + iter = treeStore.AppendNode (); + } + UpdateRow (iter, info, dataItem, status); + return iter; + } + + protected virtual void UpdateRow (TreeIter iter, AddinHeader info, object dataItem, AddinStatus status) + { + bool sel = selected.Contains (info); + + treeStore.SetValue (iter, ColAddin, info); + treeStore.SetValue (iter, ColData, dataItem); + + string name = EscapeWithFilterMarker (info.Name); + if (!string.IsNullOrEmpty (info.Description)) { + string desc = info.Description; + int i = desc.IndexOf ('\n'); + if (i != -1) + desc = desc.Substring (0, i); + name += "\n" + EscapeWithFilterMarker (desc) + ""; + } + + if (status != AddinStatus.Disabled) { + treeStore.SetValue (iter, ColName, name); + treeStore.SetValue (iter, ColVersion, info.Version); + treeStore.SetValue (iter, ColAllowSelection, allowSelection); + } + else { + treeStore.SetValue (iter, ColName, "" + name + ""); + treeStore.SetValue (iter, ColVersion, "" + info.Version + ""); + treeStore.SetValue (iter, ColAllowSelection, false); + } + + treeStore.SetValue (iter, ColShowImage, true); + treeStore.SetValue (iter, ColSelected, sel); + SetRowIcon (iter, info, dataItem, status); + } + + void SetRowIcon (TreeIter it, AddinHeader info, object dataItem, AddinStatus status) + { + string customIcom = info.Properties.GetPropertyValue ("Icon32"); + string iconId = info.Id + " " + info.Version + " " + customIcom; + Gdk.Pixbuf customPix; + + if (customIcom.Length == 0) { + customPix = null; + iconId = "__"; + } + else if (!cachedIcons.TryGetValue (iconId, out customPix)) { + + if (dataItem is Addin) { + string file = Path.Combine (((Addin)dataItem).Description.BasePath, customIcom); + if (File.Exists (file)) { + try { + customPix = new Gdk.Pixbuf (file); + } catch (Exception ex) { + Console.WriteLine (ex); + } + } + cachedIcons [iconId] = customPix; + } + else if (dataItem is AddinRepositoryEntry) { + AddinRepositoryEntry arep = (AddinRepositoryEntry) dataItem; + string tmpId = iconId; + arep.BeginDownloadSupportFile (customIcom, delegate (IAsyncResult res) { + Gtk.Application.Invoke ((o, args) => { + LoadRemoteIcon (it, tmpId, arep, res, info, dataItem, status); + }); + }, null); + iconId = "__"; + } + } + + StoreIcon (it, iconId, customPix, status); + } + + Gdk.Pixbuf GetCachedIcon (string id, string effect, Func pixbufGenerator) + { + Gdk.Pixbuf pix; + if (!cachedIcons.TryGetValue (id + "_" + effect, out pix)) + cachedIcons [id + "_" + effect] = pix = pixbufGenerator (); + return pix; + } + + internal bool ShowInstalledMarkers = false; + + void StoreIcon (TreeIter it, string iconId, Gdk.Pixbuf customPix, AddinStatus status) + { + if (customPix == null) + customPix = iconInstalled; + + if ((status & AddinStatus.Installed) == 0) { + treeStore.SetValue (it, ColImage, customPix); + return; + } else if (ShowInstalledMarkers && (status & AddinStatus.HasUpdate) == 0) { + customPix = GetCachedIcon (iconId, "InstalledOverlay", delegate { return Services.AddIconOverlay (customPix, installedOverlay); }); + iconId = iconId + "_Installed"; + } + + if ((status & AddinStatus.Disabled) != 0) { + customPix = GetCachedIcon (iconId, "Desaturate", delegate { return Services.DesaturateIcon (customPix); }); + iconId = iconId + "_Desaturate"; + } + if ((status & AddinStatus.HasUpdate) != 0) + customPix = GetCachedIcon (iconId, "UpdateOverlay", delegate { return Services.AddIconOverlay (customPix, updateOverlay); }); + + treeStore.SetValue (it, ColImage, customPix); + } + + + void LoadRemoteIcon (TreeIter it, string iconId, AddinRepositoryEntry arep, IAsyncResult res, AddinHeader info, object dataItem, AddinStatus status) + { + if (!disposed && treeStore.IterIsValid (it)) { + Gdk.Pixbuf customPix = null; + try { + Gdk.PixbufLoader loader = new Gdk.PixbufLoader (arep.EndDownloadSupportFile (res)); + customPix = loader.Pixbuf; + } catch (Exception ex) { + Console.WriteLine (ex); + } + cachedIcons [iconId] = customPix; + StoreIcon (it, iconId, customPix, status); + } + } + + string EscapeWithFilterMarker (string txt) + { + if (string.IsNullOrEmpty (filter)) + return GLib.Markup.EscapeText (txt); + + StringBuilder sb = new StringBuilder (); + int last = 0; + int i = txt.IndexOf (filter, StringComparison.CurrentCultureIgnoreCase); + while (i != -1) { + sb.Append (GLib.Markup.EscapeText (txt.Substring (last, i - last))); + sb.Append ("").Append (txt, i, filter.Length).Append (""); + last = i + filter.Length; + i = txt.IndexOf (filter, last, StringComparison.CurrentCultureIgnoreCase); + } + if (last < txt.Length) + sb.Append (GLib.Markup.EscapeText (txt.Substring (last, txt.Length - last))); + return sb.ToString (); + } + + public object GetAddinData (AddinHeader info) + { + return addinData [info]; + } + + public AddinHeader[] GetSelectedAddins () + { + return (AddinHeader[]) selected.ToArray (typeof(AddinHeader)); + } + + TreeIter FindCategory (string namePath) + { + TreeIter iter = TreeIter.Zero; + string[] paths = namePath.Split ('/'); + foreach (string name in paths) { + TreeIter child; + if (!FindCategory (iter, name, out child)) { + if (iter.Equals (TreeIter.Zero)) + iter = treeStore.AppendValues (null, null, name, "", false, false, null, false); + else + iter = treeStore.AppendValues (iter, null, null, name, "", false, false, null, false); + } + else + iter = child; + } + return iter; + } + + bool FindCategory (TreeIter piter, string name, out TreeIter child) + { + if (piter.Equals (TreeIter.Zero)) { + if (!treeStore.GetIterFirst (out child)) + return false; + } + else if (!treeStore.IterChildren (out child, piter)) + return false; + + do { + if (((string) treeStore.GetValue (child, ColName)) == name) { + return true; + } + } while (treeStore.IterNext (ref child)); + + return false; + } + + public AddinHeader ActiveAddin { + get { + AddinHeader[] sel = ActiveAddins; + if (sel.Length > 0) + return sel[0]; + else + return null; + } + } + + public AddinHeader[] ActiveAddins { + get { + List list = new List (); + foreach (TreePath p in treeView.Selection.GetSelectedRows ()) { + TreeIter iter; + treeStore.GetIter (out iter, p); + AddinHeader ah = (AddinHeader) treeStore.GetValue (iter, 0); + if (ah != null) + list.Add (ah); + } + return list.ToArray (); + } + } + + public object ActiveAddinData { + get { + AddinHeader ai = ActiveAddin; + return ai != null ? GetAddinData (ai) : null; + } + } + + public object[] ActiveAddinsData { + get { + List res = new List (); + foreach (AddinHeader ai in ActiveAddins) { + res.Add (GetAddinData (ai)); + } + return res.ToArray (); + } + } + + public object[] AddinsData { + get { + object[] data = new object [addinData.Count]; + addinData.Values.CopyTo (data, 0); + return data; + } + } + + public object SaveStatus () + { + TreeIter iter; + ArrayList list = new ArrayList (); + + // Save the current selection + list.Add (treeView.Selection.GetSelectedRows ()); + + if (!treeStore.GetIterFirst (out iter)) + return null; + + // Save the expand state + do { + SaveStatus (list, iter); + } while (treeStore.IterNext (ref iter)); + + return list; + } + + void SaveStatus (ArrayList list, TreeIter iter) + { + Gtk.TreePath path = treeStore.GetPath (iter); + if (treeView.GetRowExpanded (path)) + list.Add (path); + if (treeStore.IterChildren (out iter, iter)) { + do { + SaveStatus (list, iter); + } while (treeStore.IterNext (ref iter)); + } + } + + public void RestoreStatus (object ob) + { + if (ob == null) + return; + + // The first element is the selection + ArrayList list = (ArrayList) ob; + TreePath[] selpaths = (TreePath[]) list [0]; + list.RemoveAt (0); + + foreach (TreePath path in list) + treeView.ExpandRow (path, false); + + foreach (TreePath p in selpaths) + treeView.Selection.SelectPath (p); + } + + public void SelectAll () + { + TreeIter iter; + + if (!treeStore.GetIterFirst (out iter)) + return; + do { + SelectAll (iter); + } while (treeStore.IterNext (ref iter)); + OnSelectionChanged (EventArgs.Empty); + } + + void SelectAll (TreeIter iter) + { + AddinHeader info = (AddinHeader) treeStore.GetValue (iter, ColAddin); + + if (info != null) { + treeStore.SetValue (iter, ColSelected, true); + if (!selected.Contains (info)) + selected.Add (info); + treeView.ExpandToPath (treeStore.GetPath (iter)); + } else { + if (treeStore.IterChildren (out iter, iter)) { + do { + SelectAll (iter); + } while (treeStore.IterNext (ref iter)); + } + } + } + + public void UnselectAll () + { + TreeIter iter; + if (!treeStore.GetIterFirst (out iter)) + return; + do { + UnselectAll (iter); + } while (treeStore.IterNext (ref iter)); + OnSelectionChanged (EventArgs.Empty); + } + + void UnselectAll (TreeIter iter) + { + AddinHeader info = (AddinHeader) treeStore.GetValue (iter, ColAddin); + if (info != null) { + treeStore.SetValue (iter, ColSelected, false); + selected.Remove (info); + } else { + if (treeStore.IterChildren (out iter, iter)) { + do { + UnselectAll (iter); + } while (treeStore.IterNext (ref iter)); + } + } + } + } + + [Flags] + public enum AddinStatus + { + NotInstalled = 0, + Installed = 1, + Disabled = 2, + HasUpdate = 4 + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ErrorDialog.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ErrorDialog.cs new file mode 100644 index 00000000..ca9fce05 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ErrorDialog.cs @@ -0,0 +1,97 @@ +// +// ErrorDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; + +namespace Mono.Addins.Gui +{ + partial class ErrorDialog : Dialog + { + TextTag tagNoWrap; + TextTag tagWrap; + + public ErrorDialog (Window parent) + { + Build (); + TransientFor = parent; + okButton.Clicked += new EventHandler (OnClose); + expander.Activated += new EventHandler (OnExpanded); + descriptionLabel.ModifyBg (StateType.Normal, new Gdk.Color (255,0,0)); + + tagNoWrap = new TextTag ("nowrap"); + tagNoWrap.WrapMode = WrapMode.None; + detailsTextView.Buffer.TagTable.Add (tagNoWrap); + + tagWrap = new TextTag ("wrap"); + tagWrap.WrapMode = WrapMode.Word; + detailsTextView.Buffer.TagTable.Add (tagWrap); + + expander.Visible = false; + } + + public string Message { + get { return descriptionLabel.Text; } + set { + string message = value; + while (message.EndsWith ("\r") || message.EndsWith ("\n")) + message = message.Substring (0, message.Length - 1); + if (!message.EndsWith (".")) message += "."; + descriptionLabel.Text = message; + } + } + + public void AddDetails (string text, bool wrapped) + { + TextIter it = detailsTextView.Buffer.EndIter; + if (wrapped) + detailsTextView.Buffer.InsertWithTags (ref it, text, tagWrap); + else + detailsTextView.Buffer.InsertWithTags (ref it, text, tagNoWrap); + expander.Visible = true; + } + + void OnClose (object sender, EventArgs args) + { + Destroy (); + } + + void OnExpanded (object sender, EventArgs args) + { + GLib.Timeout.Add (100, new GLib.TimeoutHandler (UpdateSize)); + } + + bool UpdateSize () + { + int w, h; + GetSize (out w, out h); + Resize (w, 1); + return false; + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/HeaderBox.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/HeaderBox.cs new file mode 100644 index 00000000..487da63b --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/HeaderBox.cs @@ -0,0 +1,175 @@ +// +// HeaderBox.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Gtk; + +namespace Mono.Addins.Gui +{ + class HeaderBox: Bin + { + Gtk.Widget child; + int topMargin; + int bottomMargin; + int leftMargin; + int rightMargin; + + int topPadding; + int bottomPadding; + int leftPadding; + int rightPadding; + + bool useCustomColor; + Gdk.Color customColor; + + public HeaderBox () + { + } + + public HeaderBox (int topMargin, int bottomMargin, int leftMargin, int rightMargin) + { + SetMargins (topMargin, bottomMargin, leftMargin, rightMargin); + } + + public void Replace (Gtk.Bin parent) + { + Gtk.Widget c = parent.Child; + parent.Remove (c); + Add (c); + parent.Add (this); + } + + public void SetMargins (int topMargin, int bottomMargin, int leftMargin, int rightMargin) + { + this.topMargin = topMargin; + this.bottomMargin = bottomMargin; + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + } + + public void SetPadding (int topPadding, int bottomPadding, int leftPadding, int rightPadding) + { + this.topPadding = topPadding; + this.bottomPadding = bottomPadding; + this.leftPadding = leftPadding; + this.rightPadding = rightPadding; + } + + public bool GradientBackround { get; set; } + + public Gdk.Color BackgroundColor { + get { return customColor; } + set { customColor = value; useCustomColor = true; } + } + + public void ResetBackgroundColor () + { + useCustomColor = false; + } + + protected override void OnAdded (Widget widget) + { + base.OnAdded (widget); + child = widget; + } + + protected override void OnSizeRequested (ref Requisition requisition) + { + if (child != null) { + requisition = child.SizeRequest (); + requisition.Width += leftMargin + rightMargin + leftPadding + rightPadding; + requisition.Height += topMargin + bottomMargin + topPadding + bottomPadding; + } else { + requisition.Width = 0; + requisition.Height = 0; + } + } + + protected override void OnSizeAllocated (Gdk.Rectangle allocation) + { + base.OnSizeAllocated (allocation); + if (allocation.Width > leftMargin + rightMargin + leftPadding + rightPadding) { + allocation.X += leftMargin + leftPadding; + allocation.Width -= leftMargin + rightMargin + leftPadding + rightPadding; + } + if (allocation.Height > topMargin + bottomMargin + topPadding + bottomPadding) { + allocation.Y += topMargin + topPadding; + allocation.Height -= topMargin + bottomMargin + topPadding + bottomPadding; + } + if (child != null) + child.SizeAllocate (allocation); + } + + protected override bool OnExposeEvent (Gdk.EventExpose evnt) + { + Gdk.Rectangle rect; + + if (GradientBackround) { + rect = new Gdk.Rectangle (Allocation.X, Allocation.Y, Allocation.Width, Allocation.Height); + HslColor gcol = useCustomColor ? customColor : Parent.Style.Background (Gtk.StateType.Normal); + + using (Cairo.Context cr = Gdk.CairoHelper.Create (GdkWindow)) { + cr.NewPath (); + cr.MoveTo (rect.X, rect.Y); + cr.RelLineTo (rect.Width, 0); + cr.RelLineTo (0, rect.Height); + cr.RelLineTo (-rect.Width, 0); + cr.RelLineTo (0, -rect.Height); + cr.ClosePath (); + using (Cairo.Gradient pat = new Cairo.LinearGradient (rect.X, rect.Y, rect.X, rect.Y + rect.Height - 1)) { + Cairo.Color color1 = gcol; + pat.AddColorStop (0, color1); + gcol.L -= 0.1; + if (gcol.L < 0) + gcol.L = 0; + pat.AddColorStop (1, gcol); + cr.Pattern = pat; + cr.FillPreserve (); + } + } + } + + bool res = base.OnExposeEvent (evnt); + + Gdk.GC borderColor = Parent.Style.DarkGC (Gtk.StateType.Normal); + + rect = Allocation; + for (int n=0; n + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using Gtk; + +namespace Mono.Addins.Gui +{ + class HoverImageButton : EventBox + { + private static Gdk.Cursor hand_cursor = new Gdk.Cursor(Gdk.CursorType.Hand1); + + private IconSize icon_size = IconSize.Menu; + private string [] icon_names = { "image-missing", Stock.MissingImage }; + private Gdk.Pixbuf normal_pixbuf; + private Gdk.Pixbuf active_pixbuf; + private Image image; + private bool is_hovering; + private bool is_pressed; + + private bool draw_focus = true; + + private event EventHandler clicked; + + public event EventHandler Clicked { + add { clicked += value; } + remove { clicked -= value; } + } + + public HoverImageButton() + { + CanFocus = true; + + image = new Image(); + image.Show(); + Add(image); + } + + public HoverImageButton(IconSize size, string icon_name) : this(size, new string [] { icon_name }) + { + } + + public HoverImageButton(IconSize size, string [] icon_names) : this() + { + this.icon_size = size; + this.icon_names = icon_names; + } + + public new void Activate() + { + EventHandler handler = clicked; + if(handler != null) { + handler(this, EventArgs.Empty); + } + } + + private bool changing_style = false; + protected override void OnStyleSet(Style previous_style) + { + if(changing_style) { + return; + } + + changing_style = true; + if (normal_pixbuf == null) + LoadPixbufs(); + changing_style = false; + } + + protected override bool OnEnterNotifyEvent(Gdk.EventCrossing evnt) + { + image.GdkWindow.Cursor = hand_cursor; + is_hovering = true; + UpdateImage(); + return base.OnEnterNotifyEvent(evnt); + } + + protected override bool OnLeaveNotifyEvent(Gdk.EventCrossing evnt) + { + is_hovering = false; + UpdateImage(); + return base.OnLeaveNotifyEvent(evnt); + } + + protected override bool OnFocusInEvent(Gdk.EventFocus evnt) + { + bool ret = base.OnFocusInEvent(evnt); + UpdateImage(); + return ret; + } + + protected override bool OnFocusOutEvent(Gdk.EventFocus evnt) + { + bool ret = base.OnFocusOutEvent(evnt); + UpdateImage(); + return ret; + } + + protected override bool OnButtonPressEvent(Gdk.EventButton evnt) + { + if(evnt.Button != 1) { + return base.OnButtonPressEvent(evnt); + } + + HasFocus = true; + is_pressed = true; + QueueDraw(); + + return base.OnButtonPressEvent(evnt); + } + + protected override bool OnButtonReleaseEvent(Gdk.EventButton evnt) + { + if(evnt.Button != 1) { + return base.OnButtonReleaseEvent(evnt); + } + + is_pressed = false; + QueueDraw(); + Activate(); + + return base.OnButtonReleaseEvent(evnt); + } + + protected override bool OnExposeEvent(Gdk.EventExpose evnt) + { + base.OnExposeEvent(evnt); + + PropagateExpose(Child, evnt); + + if(HasFocus && draw_focus) { + Style.PaintFocus(Style, GdkWindow, StateType.Normal, evnt.Area, this, "button", + 0, 0, Allocation.Width, Allocation.Height); + } + + return true; + } + + private void UpdateImage() + { + image.Pixbuf = is_hovering || is_pressed || HasFocus + ? active_pixbuf : normal_pixbuf; + } + + private void LoadPixbufs() + { + int width, height; + Icon.SizeLookup(icon_size, out width, out height); + IconTheme theme = IconTheme.GetForScreen(Screen); + + if(normal_pixbuf != null) { + normal_pixbuf.Dispose(); + normal_pixbuf = null; + } + + if(active_pixbuf != null) { + active_pixbuf.Dispose(); + active_pixbuf = null; + } + + for(int i = 0; i < icon_names.Length; i++) { + try { + normal_pixbuf = RenderIcon(icon_names[i], icon_size, null) + ?? theme.LoadIcon(icon_names[i], width, 0); + active_pixbuf = ColorShiftPixbuf(normal_pixbuf, 30); + break; + } catch { + } + } + + UpdateImage(); + } + + public Gdk.Pixbuf Pixbuf { + get { return this.normal_pixbuf; } + set { + this.normal_pixbuf = value; + active_pixbuf = ColorShiftPixbuf(normal_pixbuf, 30); + UpdateImage(); + } + } + + + private static byte PixelClamp(int val) + { + return (byte)System.Math.Max(0, System.Math.Min(255, val)); + } + + private unsafe Gdk.Pixbuf ColorShiftPixbuf(Gdk.Pixbuf src, byte shift) + { + Gdk.Pixbuf dest = new Gdk.Pixbuf(src.Colorspace, src.HasAlpha, src.BitsPerSample, src.Width, src.Height); + + byte *src_pixels_orig = (byte *)src.Pixels; + byte *dest_pixels_orig = (byte *)dest.Pixels; + + for(int i = 0; i < src.Height; i++) { + byte *src_pixels = src_pixels_orig + i * src.Rowstride; + byte *dest_pixels = dest_pixels_orig + i * dest.Rowstride; + + for(int j = 0; j < src.Width; j++) { + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + + if(src.HasAlpha) { + *(dest_pixels++) = *(src_pixels++); + } + } + } + + return dest; + } + + public string [] IconNames { + get { return icon_names; } + set { + icon_names = value; + LoadPixbufs(); + } + } + + public IconSize IconSize { + get { return icon_size; } + set { + icon_size = value; + LoadPixbufs(); + } + } + + public Image Image { + get { return image; } + } + + public bool DrawFocus { + get { return draw_focus; } + set { + draw_focus = value; + QueueDraw(); + } + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/HslColor.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/HslColor.cs new file mode 100644 index 00000000..0862ffac --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/HslColor.cs @@ -0,0 +1,164 @@ +// +// HslColor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Gdk; + +namespace Mono.Addins.Gui +{ + struct HslColor + { + public double H { + get; + set; + } + + public double S { + get; + set; + } + + public double L { + get; + set; + } + + static Gdk.Color black = new Gdk.Color (0, 0, 0); + public static implicit operator Color (HslColor hsl) + { + if (hsl.L > 1) hsl.L = 1; + if (hsl.L < 0) hsl.L = 0; + if (hsl.H > 1) hsl.H = 1; + if (hsl.H < 0) hsl.H = 0; + if (hsl.S > 1) hsl.S = 1; + if (hsl.S < 0) hsl.S = 0; + + double r = 0, g = 0, b = 0; + + if (hsl.L == 0) + return black; + + if (hsl.S == 0) { + r = g = b = hsl.L; + } else { + double temp2 = hsl.L <= 0.5 ? hsl.L * (1.0 + hsl.S) : hsl.L + hsl.S -(hsl.L * hsl.S); + double temp1 = 2.0 * hsl.L - temp2; + + double[] t3 = new double[] { hsl.H + 1.0 / 3.0, hsl.H, hsl.H - 1.0 / 3.0}; + double[] clr= new double[] { 0, 0, 0}; + for (int i = 0; i < 3; i++) { + if (t3[i] < 0) + t3[i] += 1.0; + if (t3[i] > 1) + t3[i]-=1.0; + if (6.0 * t3[i] < 1.0) + clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0; + else if (2.0 * t3[i] < 1.0) + clr[i] = temp2; + else if (3.0 * t3[i] < 2.0) + clr[i] = (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - t3[i]) * 6.0); + else + clr[i] = temp1; + } + + r = clr[0]; + g = clr[1]; + b = clr[2]; + } + return new Color ((byte)(255 * r), + (byte)(255 * g), + (byte)(255 * b)); + } + + public static Cairo.Color ToCairoColor (Gdk.Color color) + { + return new Cairo.Color ((double)color.Red / ushort.MaxValue, + (double)color.Green / ushort.MaxValue, + (double)color.Blue / ushort.MaxValue); + } + + public static implicit operator Cairo.Color (HslColor hsl) + { + return ToCairoColor ((Gdk.Color)hsl); + } + + public static implicit operator HslColor (Color color) + { + return new HslColor (color); + } + + public HslColor (Color color) : this () + { + double r = color.Red / (double)ushort.MaxValue; + double g = color.Green / (double)ushort.MaxValue; + double b = color.Blue / (double)ushort.MaxValue; + + double v = System.Math.Max (r, g); + v = System.Math.Max (v, b); + + double m = System.Math.Min (r, g); + m = System.Math.Min (m, b); + + this.L = (m + v) / 2.0; + if (this.L <= 0.0) + return; + double vm = v - m; + this.S = vm; + + if (this.S > 0.0) { + this.S /= (this.L <= 0.5) ? (v + m) : (2.0 - v - m); + } else { + return; + } + + double r2 = (v - r) / vm; + double g2 = (v - g) / vm; + double b2 = (v - b) / vm; + + if (r == v) { + this.H = (g == m ? 5.0 + b2 : 1.0 - g2); + } else if (g == v) { + this.H = (b == m ? 1.0 + r2 : 3.0 - b2); + } else { + this.H = (r == m ? 3.0 + g2 : 5.0 - r2); + } + this.H /= 6.0; + } + + public static double Brightness (Gdk.Color c) + { + double r = c.Red / (double)ushort.MaxValue; + double g = c.Green / (double)ushort.MaxValue; + double b = c.Blue / (double)ushort.MaxValue; + return System.Math.Sqrt (r * .241 + g * .691 + b * .068); + } + + public override string ToString () + { + return string.Format ("[HslColor: H={0}, S={1}, L={2}]", H, S, L); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/InstallDialog.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/InstallDialog.cs new file mode 100644 index 00000000..c81f2421 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/InstallDialog.cs @@ -0,0 +1,276 @@ +// +// InstallDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using System.Text; +using Mono.Unix; +using System.Threading; +using System.Linq; +using System.Collections.Generic; + +namespace Mono.Addins.Gui +{ + internal partial class InstallDialog : Gtk.Dialog + { + string[] filesToInstall; + AddinRepositoryEntry[] addinsToInstall; + PackageCollection packagesToInstall; + SetupService service; + Gtk.ResponseType response = Gtk.ResponseType.None; + IEnumerable uninstallIds; + InstallMonitor installMonitor; + bool installing; + const int MaxHeight = 350; + + public InstallDialog (Gtk.Window parent, SetupService service) + { + this.Build (); + this.service = service; + TransientFor = parent; + WindowPosition = Gtk.WindowPosition.CenterOnParent; + Services.PlaceDialog (this, parent); + boxProgress.Visible = false; + Resizable = false; + } + + public void InitForInstall (AddinRepositoryEntry[] addinsToInstall) + { + this.addinsToInstall = addinsToInstall; + FillSummaryPage (); + Services.PlaceDialog (this, TransientFor); + } + + public void InitForInstall (string[] filesToInstall) + { + this.filesToInstall = filesToInstall; + FillSummaryPage (); + Services.PlaceDialog (this, TransientFor); + } + + public void InitForUninstall (Addin[] info) + { + this.uninstallIds = info.Select (a => a.Id); + buttonOk.Label = Catalog.GetString ("Uninstall"); + + HashSet sinfos = new HashSet (); + + StringBuilder sb = new StringBuilder (); + sb.Append ("").Append (Catalog.GetString ("The following packages will be uninstalled:")).Append ("\n\n"); + foreach (var a in info) { + sb.Append (a.Name + "\n\n"); + sinfos.UnionWith (service.GetDependentAddins (a.Id, true)); + } + + if (sinfos.Count > 0) { + sb.Append ("").Append (Catalog.GetString ("There are other extension packages that depend on the previous ones which will also be uninstalled:")).Append ("\n\n"); + foreach (Addin si in sinfos) + sb.Append (si.Description.Name + "\n"); + } + + ShowMessage (sb.ToString ()); + Services.PlaceDialog (this, TransientFor); + } + + void FillSummaryPage () + { + PackageCollection packs = new PackageCollection (); + + if (filesToInstall != null) { + foreach (string file in filesToInstall) { + packs.Add (Package.FromFile (file)); + } + } + else { + foreach (AddinRepositoryEntry arep in addinsToInstall) { + packs.Add (Package.FromRepository (arep)); + } + } + + packagesToInstall = new PackageCollection (packs); + + PackageCollection toUninstall; + DependencyCollection unresolved; + bool res; + + InstallMonitor m = new InstallMonitor (); + res = service.ResolveDependencies (m, packs, out toUninstall, out unresolved); + + StringBuilder sb = new StringBuilder (); + if (!res) { + sb.Append ("").Append (Catalog.GetString ("The selected extension packages can't be installed because there are dependency conflicts.")).Append ("\n"); + foreach (string s in m.Errors) { + sb.Append ("" + s + "\n"); + } + sb.Append ("\n"); + } + + if (m.Warnings.Count != 0) { + foreach (string w in m.Warnings) { + sb.Append ("" + w + "\n"); + } + sb.Append ("\n"); + } + + sb.Append ("").Append (Catalog.GetString ("The following packages will be installed:")).Append ("\n\n"); + foreach (Package p in packs) { + sb.Append (p.Name); + if (!p.SharedInstall) + sb.Append (Catalog.GetString (" (in user directory)")); + sb.Append ("\n"); + } + sb.Append ("\n"); + + if (toUninstall.Count > 0) { + sb.Append ("").Append (Catalog.GetString ("The following packages need to be uninstalled:")).Append ("\n\n"); + foreach (Package p in toUninstall) { + sb.Append (p.Name + "\n"); + } + sb.Append ("\n"); + } + + if (unresolved.Count > 0) { + sb.Append ("").Append (Catalog.GetString ("The following dependencies could not be resolved:")).Append ("\n\n"); + foreach (Dependency p in unresolved) { + sb.Append (p.Name + "\n"); + } + sb.Append ("\n"); + } + buttonOk.Sensitive = res; + ShowMessage (sb.ToString ()); + } + + void ShowMessage (string txt) + { + labelInfo.Markup = txt.TrimEnd ('\n','\t',' '); + if (labelInfo.SizeRequest ().Height > MaxHeight) { + scrolledwindow1.VscrollbarPolicy = Gtk.PolicyType.Automatic; + scrolledwindow1.HeightRequest = MaxHeight; + } + else { + scrolledwindow1.HeightRequest = labelInfo.SizeRequest ().Height; + } + } + + protected virtual void OnButtonOkClicked (object sender, System.EventArgs e) + { + if (response != Gtk.ResponseType.None) { + Respond (response); + return; + } + Install (); + } + + protected virtual void OnButtonCancelClicked (object sender, System.EventArgs e) + { + if (installing) { + if (Services.AskQuestion (Catalog.GetString ("Are you sure you want to cancel the installation?"))) + installMonitor.Cancel (); + } else + Respond (Gtk.ResponseType.Cancel); + } + + void Install () + { + insSeparator.Visible = true; + boxProgress.Visible = true; + buttonOk.Sensitive = false; + + string txt; + string errmessage; + string warnmessage; + + ThreadStart oper; + + if (uninstallIds == null) { + installMonitor = new InstallMonitor (globalProgressLabel, mainProgressBar, Catalog.GetString ("Installing Extension Packages")); + oper = new ThreadStart (RunInstall); + errmessage = Catalog.GetString ("The installation failed!"); + warnmessage = Catalog.GetString ("The installation has completed with warnings."); + } else { + installMonitor = new InstallMonitor (globalProgressLabel, mainProgressBar, Catalog.GetString ("Uninstalling Extension Packages")); + oper = new ThreadStart (RunUninstall); + errmessage = Catalog.GetString ("The uninstallation failed!"); + warnmessage = Catalog.GetString ("The uninstallation has completed with warnings."); + } + + installing = true; + oper (); + installing = false; + + buttonCancel.Visible = false; + buttonOk.Label = Gtk.Stock.Close; + buttonOk.UseStock = true; + + if (installMonitor.Success && installMonitor.Warnings.Count == 0) { + Respond (Gtk.ResponseType.Ok); + return; + } else if (installMonitor.Success) { + txt = "" + warnmessage + "\n\n"; + foreach (string s in installMonitor.Warnings) + txt += GLib.Markup.EscapeText (s) + "\n"; + response = Gtk.ResponseType.Ok; + buttonOk.Sensitive = true; + } else { + buttonCancel.Label = Gtk.Stock.Close; + buttonCancel.UseStock = true; + txt = "" + errmessage + "\n\n"; + foreach (string s in installMonitor.Errors) + txt += GLib.Markup.EscapeText (s) + "\n"; + response = Gtk.ResponseType.Cancel; + buttonOk.Sensitive = true; + } + + ShowMessage (txt); + } + + void RunInstall () + { + try { + if (filesToInstall != null) + service.Install (installMonitor, filesToInstall); + else + service.Install (installMonitor, packagesToInstall); + } catch (Exception ex) { + installMonitor.Errors.Add (ex.Message); + } finally { + installMonitor.Dispose (); + } + } + + void RunUninstall () + { + try { + service.Uninstall (installMonitor, uninstallIds); + } catch (Exception ex) { + installMonitor.Errors.Add (ex.Message); + } finally { + installMonitor.Dispose (); + } + } + } +} + diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/InstallMonitor.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/InstallMonitor.cs new file mode 100644 index 00000000..60f59499 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/InstallMonitor.cs @@ -0,0 +1,133 @@ +// +// AddinInstallDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Text; +using System.Threading; +using System.Collections; +using System.Collections.Specialized; +using System.Diagnostics; +using Mono.Unix; +using Gtk; +using Mono.Addins.Setup; +using Mono.Addins.Description; +namespace Mono.Addins.Gui +{ + class InstallMonitor: IProgressStatus, IDisposable + { + Label progressLabel; + ProgressBar progressBar; + StringCollection errors = new StringCollection (); + StringCollection warnings = new StringCollection (); + bool canceled; + bool done; + string mainOperation; + + public InstallMonitor (Label progressLabel, ProgressBar progressBar, string mainOperation) + { + this.progressLabel = progressLabel; + this.progressBar = progressBar; + this.mainOperation = mainOperation; + } + + public InstallMonitor () + { + } + + public void SetMessage (string msg) + { + if (progressLabel != null) + progressLabel.Markup = "" + GLib.Markup.EscapeText (mainOperation) + "\n" + GLib.Markup.EscapeText (msg); + RunPendingEvents (); + } + + public void SetProgress (double progress) + { + if (progressBar != null) + progressBar.Fraction = progress; + RunPendingEvents (); + } + + public void Log (string msg) + { + Console.WriteLine (msg); + } + + public void ReportWarning (string message) + { + warnings.Add (message); + } + + public void ReportError (string message, Exception exception) + { + errors.Add (message); + } + + public bool IsCanceled { + get { return canceled; } + } + + public StringCollection Errors { + get { return errors; } + } + + public StringCollection Warnings { + get { return warnings; } + } + + public void Cancel () + { + canceled = true; + } + + public int LogLevel { + get { return 1; } + } + + public void Dispose () + { + done = true; + } + + public void WaitForCompleted () + { + while (!done) { + RunPendingEvents (); + Thread.Sleep (50); + } + } + + public bool Success { + get { return errors.Count == 0; } + } + + void RunPendingEvents () + { + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ManageSitesDialog.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ManageSitesDialog.cs new file mode 100644 index 00000000..21d3a9e4 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ManageSitesDialog.cs @@ -0,0 +1,173 @@ +// +// ManageSitesDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; +using Mono.Unix; +using System.Threading; + +using Mono.Addins.Setup; + + +namespace Mono.Addins.Gui +{ + partial class ManageSitesDialog : Dialog + { + ListStore treeStore; + SetupService service; + + public ManageSitesDialog (Gtk.Window parent, SetupService service) + { + Build (); + TransientFor = parent; + Services.PlaceDialog (this, parent); + this.service = service; + treeStore = new Gtk.ListStore (typeof (string), typeof (string), typeof(bool)); + repoTree.Model = treeStore; + repoTree.HeadersVisible = false; + var crt = new Gtk.CellRendererToggle (); + crt.Toggled += HandleRepoToggled; + repoTree.AppendColumn ("", crt, "active", 2); + repoTree.AppendColumn ("", new Gtk.CellRendererText (), "markup", 1); + repoTree.Selection.Changed += new EventHandler(OnSelect); + + AddinRepository[] reps = service.Repositories.GetRepositories (); + foreach (AddinRepository rep in reps) + AppendRepository (rep); + + btnRemove.Sensitive = false; + } + + public override void Dispose () + { + base.Dispose (); + Destroy (); + } + + void AppendRepository (AddinRepository rep) + { + string txt = GLib.Markup.EscapeText (rep.Title) + "\n" + GLib.Markup.EscapeText (rep.Url) + ""; + treeStore.AppendValues (rep.Url, txt, rep.Enabled); + } + + protected void OnAdd (object sender, EventArgs e) + { + NewSiteDialog dlg = new NewSiteDialog (this); + try { + if (dlg.Run ()) { + string url = dlg.Url; + if (!url.StartsWith ("http://") && !url.StartsWith ("https://") && !url.StartsWith ("file://")) { + url = "http://" + url; + } + + try { + new Uri (url); + } catch { + Services.ShowError (null, "Invalid url: " + url, null, true); + } + + if (!service.Repositories.ContainsRepository (url)) { + ProgressDialog pdlg = new ProgressDialog (this); + pdlg.Show (); + pdlg.SetMessage (AddinManager.CurrentLocalizer.GetString ("Registering repository")); + + bool done = false; + AddinRepository rr = null; + Exception error = null; + + ThreadPool.QueueUserWorkItem (delegate { + try { + rr = service.Repositories.RegisterRepository (pdlg, url, true); + } catch (System.Exception ex) { + error = ex; + } finally { + done = true; + } + }); + + while (!done) { + if (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + else + Thread.Sleep (100); + } + + pdlg.Destroy (); + + if (pdlg.HadError) { + if (rr != null) + service.Repositories.RemoveRepository (rr.Url); + return; + } + + if (error != null) { + Services.ShowError (error, "The repository could not be registered", null, true); + return; + } + + AppendRepository (rr); + } + } + } finally { + dlg.Destroy (); + } + } + + protected void OnRemove (object sender, EventArgs e) + { + Gtk.TreeModel foo; + Gtk.TreeIter iter; + if (!repoTree.Selection.GetSelected (out foo, out iter)) + return; + + string rep = (string) treeStore.GetValue (iter, 0); + service.Repositories.RemoveRepository (rep); + + treeStore.Remove (ref iter); + } + + void HandleRepoToggled (object o, ToggledArgs args) + { + Gtk.TreeIter iter; + if (!treeStore.GetIterFromString (out iter, args.Path)) + return; + + bool newVal = !(bool) treeStore.GetValue (iter, 2); + string rep = (string) treeStore.GetValue (iter, 0); + service.Repositories.SetRepositoryEnabled (rep, newVal); + + treeStore.SetValue (iter, 2, newVal); + } + + protected void OnSelect(object sender, EventArgs e) + { + btnRemove.Sensitive = repoTree.Selection.CountSelectedRows() > 0; + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/NewSiteDialog.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/NewSiteDialog.cs new file mode 100644 index 00000000..b10d691f --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/NewSiteDialog.cs @@ -0,0 +1,117 @@ +// +// NewSiteDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; + +namespace Mono.Addins.Gui +{ + partial class NewSiteDialog : Dialog + { + public NewSiteDialog (Gtk.Window parent) + { + Build (); + TransientFor = parent; + Services.PlaceDialog (this, parent); + pathEntry.Sensitive = false; + CheckValues (); + } + + public override void Dispose () + { + base.Dispose (); + Destroy (); + } + + public string Url { + get { + if (btnOnlineRep.Active) + return urlText.Text; + else if (pathEntry.Text.Length > 0) + return "file://" + pathEntry.Text; + else + return string.Empty; + } + } + + void CheckValues () + { + btnOk.Sensitive = (Url != ""); + } + + public new bool Run () + { + ShowAll (); + return ((ResponseType) base.Run ()) == ResponseType.Ok; + } + + protected void OnClose (object sender, EventArgs args) + { + Destroy (); + } + + protected void OnOptionClicked (object sender, EventArgs e) + { + if (btnOnlineRep.Active) { + urlText.Sensitive = true; + pathEntry.Sensitive = false; + } else { + urlText.Sensitive = false; + pathEntry.Sensitive = true; + } + CheckValues (); + } + + protected virtual void OnButtonBrowseClicked(object sender, System.EventArgs e) + { + FileChooserDialog dlg = new FileChooserDialog ("Select Folder", this, FileChooserAction.SelectFolder); + try { + dlg.AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel); + dlg.AddButton (Gtk.Stock.Open, Gtk.ResponseType.Ok); + + dlg.SetFilename (Environment.GetFolderPath (Environment.SpecialFolder.Personal)); + if (dlg.Run () == (int) ResponseType.Ok) { + pathEntry.Text = dlg.Filename; + } + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnPathEntryChanged(object sender, System.EventArgs e) + { + CheckValues (); + } + + protected virtual void OnUrlTextChanged (object sender, System.EventArgs e) + { + CheckValues (); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ProgressDialog.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ProgressDialog.cs new file mode 100644 index 00000000..c58a8a1d --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/ProgressDialog.cs @@ -0,0 +1,112 @@ +// ProgressDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; + +namespace Mono.Addins.Gui +{ + internal partial class ProgressDialog : Gtk.Dialog, IProgressStatus + { + bool cancelled; + bool hadError; + + public ProgressDialog (Gtk.Window parent) + { + this.Build(); + Services.PlaceDialog (this, parent); + } + + public bool IsCanceled { + get { + return cancelled; + } + } + + public int LogLevel { + get { + return 1; + } + } + + public bool HadError { + get { + return hadError; + } + } + + public void SetMessage (string msg) + { + Gtk.Application.Invoke ((o, args) => { + labelMessage.Text = msg; + }); + } + + public void SetProgress (double progress) + { + Gtk.Application.Invoke ((o, args) => { + progressbar.Fraction = progress; + }); + } + + public void Log (string msg) + { + Gtk.Application.Invoke ((o, args) => { + Gtk.TextIter it = textview.Buffer.EndIter; + textview.Buffer.Insert (ref it, msg + "\n"); + }); + } + + public void ReportWarning (string message) + { + Log ("WARNING: " + message); + } + + public void ReportError (string message, Exception exception) + { + Log ("Error: " + message); + if (exception != null) + Log (exception.ToString ()); + Gtk.Application.Invoke ((o, args) => { + Services.ShowError (exception, message, null, true); + }); + hadError = true; + } + + public void Cancel () + { + Gtk.Application.Invoke ((o, args) => { + cancelled = true; + buttonCancel.Sensitive = false; + }); + } + + protected virtual void OnButtonCancelClicked (object sender, System.EventArgs e) + { + Cancel (); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/SearchEntry.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/SearchEntry.cs new file mode 100644 index 00000000..18ee7236 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/SearchEntry.cs @@ -0,0 +1,147 @@ +// +// SearchEntry.cs +// +// Author: +// Aaron Bockover +// Gabriel Burt +// +// Copyright 2007-2010 Novell, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; + +namespace Mono.Addins.Gui +{ + [System.ComponentModel.ToolboxItem(true)] + class SearchEntry : EventBox + { + HBox box = new HBox (); + Gtk.Entry entry = new Gtk.Entry (); + HoverImageButton iconFind; + HoverImageButton iconClean; + const int notifyDelay = 50; + bool notifying; + + public SearchEntry () + { + entry.HasFrame = false; + box.PackStart (entry, true, true, 0); + + iconFind = new HoverImageButton (IconSize.Menu, Gtk.Stock.Find); + box.PackStart (iconFind, false, false, 0); + iconClean = new HoverImageButton (IconSize.Menu, Gtk.Stock.Clear); + box.PackStart (iconClean, false, false, 0); + box.BorderWidth = 1; + + + HeaderBox hbox = new HeaderBox (1,1,1,1); + hbox.Show (); + hbox.Add (box); + Add (hbox); + + UpdateStyle (); + entry.StyleSet += UpdateStyle; + + iconClean.BorderWidth = 1; + iconFind.BorderWidth = 1; + + iconClean.Clicked += delegate { + entry.Text = string.Empty; + }; + + iconFind.Clicked += delegate { + FireSearch (); + }; + + entry.Activated += delegate { + FireSearch (); + }; + + ShowAll (); + UpdateIcon (); + + entry.Changed += delegate { + UpdateIcon (); + FireSearch (); + }; + } + + public event EventHandler TextChanged; + + public Gtk.Entry Entry { + get { + return this.entry; + } + set { + if (entry != null) + entry.StyleSet -= UpdateStyle; + entry = value; + entry.StyleSet += UpdateStyle; + } + } + + public string Text { + get { return entry.Text; } + } + + void UpdateIcon () + { + if (entry.Text.Length > 0) { + iconFind.Hide (); + iconClean.Show (); + } + else { + iconFind.Show (); + iconClean.Hide (); + } + } + + void FireSearch () + { + if (!notifying) { + notifying = true; + GLib.Timeout.Add (notifyDelay, delegate { + notifying = false; + if (TextChanged != null) + TextChanged (this, EventArgs.Empty); + return false; + }); + } + } + + void UpdateStyle (object o = null, StyleSetArgs args = null) + { + if (entry != null) { + ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + iconClean.ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + iconFind.ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + } + } + + protected override void OnRealized () + { + base.OnRealized (); + UpdateStyle (); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/Services.cs b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/Services.cs new file mode 100644 index 00000000..bd90d7bf --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/Mono.Addins.Gui/Services.cs @@ -0,0 +1,155 @@ +// +// Services.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; +using Mono.Unix; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using System.Linq; +using System.Collections.Generic; + +namespace Mono.Addins.Gui +{ + internal class Services + { + public static bool InApplicationNamespace (SetupService service, string id) + { + return service.ApplicationNamespace == null || id.StartsWith (service.ApplicationNamespace + "."); + } + + public static bool AskQuestion (string question) + { + MessageDialog md = new MessageDialog (null, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.YesNo, question); + try { + int response = md.Run (); + return ((ResponseType) response == ResponseType.Yes); + } finally { + md.Destroy (); + } + } + + public static void ShowError (Exception ex, string message, Window parent, bool modal) + { + ErrorDialog dlg = new ErrorDialog (parent); + + if (message == null) { + if (ex != null) + dlg.Message = string.Format (Catalog.GetString ("Exception occurred: {0}"), ex.Message); + else { + dlg.Message = "An unknown error occurred"; + dlg.AddDetails (Environment.StackTrace, false); + } + } else + dlg.Message = message; + + if (ex != null) { + dlg.AddDetails (string.Format (Catalog.GetString ("Exception occurred: {0}"), ex.Message) + "\n\n", true); + dlg.AddDetails (ex.ToString (), false); + } + + if (modal) { + dlg.Run (); + dlg.Destroy (); + } else + dlg.Show (); + } + + public struct MissingDepInfo + { + public string Addin; + public string Required; + public string Found; + } + + public static IEnumerable GetMissingDependencies (Addin addin) + { + IEnumerable allAddins = AddinManager.Registry.GetAddins ().Union (AddinManager.Registry.GetAddinRoots ()); + foreach (var dep in addin.Description.MainModule.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep != null) { + if (!allAddins.Any (a => Addin.GetIdName (a.Id) == Addin.GetIdName (adep.FullAddinId) && a.SupportsVersion (adep.Version))) { + Addin found = allAddins.FirstOrDefault (a => Addin.GetIdName (a.Id) == Addin.GetIdName (adep.FullAddinId)); + yield return new MissingDepInfo () { Addin = Addin.GetIdName (adep.FullAddinId), Required = adep.Version, Found = found != null ? found.Version : null }; + } + } + } + } + + public static Gdk.Pixbuf AddIconOverlay (Gdk.Pixbuf target, Gdk.Pixbuf overlay) + { + Gdk.Pixbuf res = new Gdk.Pixbuf (target.Colorspace, target.HasAlpha, target.BitsPerSample, target.Width, target.Height); + res.Fill (0); + target.CopyArea (0, 0, target.Width, target.Height, res, 0, 0); + overlay.Composite (res, 0, 0, overlay.Width, overlay.Height, 0, 0, 1, 1, Gdk.InterpType.Bilinear, 255); + return res; + } + + public static Gdk.Pixbuf DesaturateIcon (Gdk.Pixbuf source) + { + Gdk.Pixbuf dest = new Gdk.Pixbuf (source.Colorspace, source.HasAlpha, source.BitsPerSample, source.Width, source.Height); + dest.Fill (0); + source.SaturateAndPixelate (dest, 0, false); + return dest; + } + + public static Gdk.Pixbuf FadeIcon (Gdk.Pixbuf source) + { + Gdk.Pixbuf result = source.Copy (); + result.Fill (0); + result = result.AddAlpha (true, 0, 0, 0); + source.Composite (result, 0, 0, source.Width, source.Height, 0, 0, 1, 1, Gdk.InterpType.Bilinear, 128); + return result; + } + + /// + /// Positions a dialog relative to its parent on platforms where default placement is known to be poor. + /// + public static void PlaceDialog (Window child, Window parent) + { + CenterWindow (child, parent); + } + + /// Centers a window relative to its parent. + static void CenterWindow (Window child, Window parent) + { + if (child == null || parent == null) + return; + + child.Child.Show (); + int w, h, winw, winh, x, y, winx, winy; + child.GetSize (out w, out h); + parent.GetSize (out winw, out winh); + parent.GetPosition (out winx, out winy); + x = System.Math.Max (0, (winw - w) /2) + winx; + y = System.Math.Max (0, (winh - h) /2) + winy; + child.Move (x, y); + } + } +} diff --git a/mono-addins/Mono.Addins.Gui/icons/download-16.png b/mono-addins/Mono.Addins.Gui/icons/download-16.png new file mode 100644 index 00000000..0fcdd62c Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/download-16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/download-16@2x.png b/mono-addins/Mono.Addins.Gui/icons/download-16@2x.png new file mode 100644 index 00000000..7cb21ad7 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/download-16@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/download-16~dark.png b/mono-addins/Mono.Addins.Gui/icons/download-16~dark.png new file mode 100644 index 00000000..4547fc97 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/download-16~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/download-16~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/download-16~dark@2x.png new file mode 100644 index 00000000..e34e55ed Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/download-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16.png b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16.png new file mode 100644 index 00000000..99516ff6 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16@2x.png b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16@2x.png new file mode 100644 index 00000000..06dec679 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark.png b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark.png new file mode 100644 index 00000000..25ee0dc1 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark@2x.png new file mode 100644 index 00000000..2d6ff200 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark~sel.png new file mode 100644 index 00000000..c565a5bd Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark~sel@2x.png new file mode 100644 index 00000000..d959c8c4 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~sel.png b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~sel.png new file mode 100644 index 00000000..c565a5bd Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~sel@2x.png new file mode 100644 index 00000000..d959c8c4 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/installed-overlay-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/package-x-generic.png b/mono-addins/Mono.Addins.Gui/icons/package-x-generic.png new file mode 100644 index 00000000..9ea804ac Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/package-x-generic.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/package-x-generic_16.png b/mono-addins/Mono.Addins.Gui/icons/package-x-generic_16.png new file mode 100644 index 00000000..62383b25 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/package-x-generic_16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/package-x-generic_22.png b/mono-addins/Mono.Addins.Gui/icons/package-x-generic_22.png new file mode 100644 index 00000000..fa1711f1 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/package-x-generic_22.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-16.png b/mono-addins/Mono.Addins.Gui/icons/plugin-16.png new file mode 100644 index 00000000..b91883c6 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-16@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-16@2x.png new file mode 100644 index 00000000..9f9c5280 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-16@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark.png new file mode 100644 index 00000000..ceff41f2 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark@2x.png new file mode 100644 index 00000000..a4c6e707 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark~sel.png new file mode 100644 index 00000000..d449be9d Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark~sel@2x.png new file mode 100644 index 00000000..7bdeee00 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-16~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-16~sel.png new file mode 100644 index 00000000..d449be9d Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-16~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-16~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-16~sel@2x.png new file mode 100644 index 00000000..7bdeee00 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-22.png b/mono-addins/Mono.Addins.Gui/icons/plugin-22.png new file mode 100644 index 00000000..98a37fdf Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-22.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-22@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-22@2x.png new file mode 100644 index 00000000..ccc24f77 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-22@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark.png new file mode 100644 index 00000000..21024fd6 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark@2x.png new file mode 100644 index 00000000..46a351e2 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark~sel.png new file mode 100644 index 00000000..0ac0e47b Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark~sel@2x.png new file mode 100644 index 00000000..45e5da32 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-22~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-22~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-22~sel.png new file mode 100644 index 00000000..0ac0e47b Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-22~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-22~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-22~sel@2x.png new file mode 100644 index 00000000..45e5da32 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-22~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-32.png b/mono-addins/Mono.Addins.Gui/icons/plugin-32.png new file mode 100644 index 00000000..9f9c5280 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-32.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-32@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-32@2x.png new file mode 100644 index 00000000..9db0330b Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-32@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark.png new file mode 100644 index 00000000..a4c6e707 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark@2x.png new file mode 100644 index 00000000..29934c06 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark~sel.png new file mode 100644 index 00000000..7bdeee00 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark~sel@2x.png new file mode 100644 index 00000000..cd52532a Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-32~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-32~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-32~sel.png new file mode 100644 index 00000000..7bdeee00 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-32~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-32~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-32~sel@2x.png new file mode 100644 index 00000000..cd52532a Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-32~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16.png new file mode 100644 index 00000000..a77427ff Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16@2x.png new file mode 100644 index 00000000..48c1eda1 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark.png new file mode 100644 index 00000000..8b143c52 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark@2x.png new file mode 100644 index 00000000..5e3d3371 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark~sel.png new file mode 100644 index 00000000..224066c4 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark~sel@2x.png new file mode 100644 index 00000000..f2f7f404 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~sel.png new file mode 100644 index 00000000..224066c4 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~sel@2x.png new file mode 100644 index 00000000..f2f7f404 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32.png new file mode 100644 index 00000000..48c1eda1 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32@2x.png new file mode 100644 index 00000000..dd962b8d Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark.png new file mode 100644 index 00000000..5e3d3371 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark@2x.png new file mode 100644 index 00000000..63e9ac76 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark~sel.png new file mode 100644 index 00000000..f2f7f404 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark~sel@2x.png new file mode 100644 index 00000000..34aaed63 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~sel.png new file mode 100644 index 00000000..f2f7f404 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~sel@2x.png new file mode 100644 index 00000000..34aaed63 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-avail-32~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32.png b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32.png new file mode 100644 index 00000000..19e5bc49 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32@2x.png new file mode 100644 index 00000000..b2ec2a77 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark.png new file mode 100644 index 00000000..90b7212b Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark@2x.png new file mode 100644 index 00000000..a817cac9 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark~sel.png new file mode 100644 index 00000000..b5cc5ef6 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark~sel@2x.png new file mode 100644 index 00000000..603b09b3 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~sel.png new file mode 100644 index 00000000..b5cc5ef6 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~sel@2x.png new file mode 100644 index 00000000..603b09b3 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-disabled-32~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-16.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16.png new file mode 100644 index 00000000..19457650 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-16@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16@2x.png new file mode 100644 index 00000000..e9def363 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark.png new file mode 100644 index 00000000..e1b17e75 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark@2x.png new file mode 100644 index 00000000..fa4b0449 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark~sel.png new file mode 100644 index 00000000..5895d078 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark~sel@2x.png new file mode 100644 index 00000000..bbb9003f Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~sel.png new file mode 100644 index 00000000..5895d078 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~sel@2x.png new file mode 100644 index 00000000..bbb9003f Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-22.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22.png new file mode 100644 index 00000000..dfa61242 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-22@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22@2x.png new file mode 100644 index 00000000..be0273c1 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark.png new file mode 100644 index 00000000..7eaf157f Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark@2x.png new file mode 100644 index 00000000..2a5a7c1a Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark~sel.png new file mode 100644 index 00000000..e9cc9b30 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark~sel@2x.png new file mode 100644 index 00000000..ff60343e Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~sel.png new file mode 100644 index 00000000..e9cc9b30 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~sel@2x.png new file mode 100644 index 00000000..ff60343e Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-22~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-32.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32.png new file mode 100644 index 00000000..e9def363 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-32@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32@2x.png new file mode 100644 index 00000000..875d10c3 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark.png new file mode 100644 index 00000000..fa4b0449 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark@2x.png new file mode 100644 index 00000000..1a51e15e Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark~sel.png new file mode 100644 index 00000000..bbb9003f Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark~sel@2x.png new file mode 100644 index 00000000..14f79ecf Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~sel.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~sel.png new file mode 100644 index 00000000..bbb9003f Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~sel@2x.png new file mode 100644 index 00000000..14f79ecf Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/plugin-update-32~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-16.png b/mono-addins/Mono.Addins.Gui/icons/update-16.png new file mode 100644 index 00000000..97df672b Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-16@2x.png b/mono-addins/Mono.Addins.Gui/icons/update-16@2x.png new file mode 100644 index 00000000..c7cad401 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-16@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-16~dark.png b/mono-addins/Mono.Addins.Gui/icons/update-16~dark.png new file mode 100644 index 00000000..efce32be Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-16~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-16~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/update-16~dark@2x.png new file mode 100644 index 00000000..90636c76 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16.png b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16.png new file mode 100644 index 00000000..fe4899da Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16@2x.png b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16@2x.png new file mode 100644 index 00000000..97c0737f Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark.png b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark.png new file mode 100644 index 00000000..e5080864 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark@2x.png new file mode 100644 index 00000000..c43d5443 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark~sel.png b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark~sel.png new file mode 100644 index 00000000..e01f5f33 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark~sel@2x.png new file mode 100644 index 00000000..de378c1e Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~sel.png b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~sel.png new file mode 100644 index 00000000..e01f5f33 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~sel.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~sel@2x.png b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~sel@2x.png new file mode 100644 index 00000000..de378c1e Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/update-available-overlay-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/web-search-16.png b/mono-addins/Mono.Addins.Gui/icons/web-search-16.png new file mode 100644 index 00000000..7ff800d4 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/web-search-16.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/web-search-16@2x.png b/mono-addins/Mono.Addins.Gui/icons/web-search-16@2x.png new file mode 100644 index 00000000..6fd81ad6 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/web-search-16@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/web-search-16~dark.png b/mono-addins/Mono.Addins.Gui/icons/web-search-16~dark.png new file mode 100644 index 00000000..2ac4184d Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/web-search-16~dark.png differ diff --git a/mono-addins/Mono.Addins.Gui/icons/web-search-16~dark@2x.png b/mono-addins/Mono.Addins.Gui/icons/web-search-16~dark@2x.png new file mode 100644 index 00000000..7a0d0711 Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/icons/web-search-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.Gui/obj/Debug/Mono.Addins.Gui.csproj.CoreCompileInputs.cache b/mono-addins/Mono.Addins.Gui/obj/Debug/Mono.Addins.Gui.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..00eb8414 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/obj/Debug/Mono.Addins.Gui.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +ed98a27d60fbff71e56a32d5535f03b9d95b4782 diff --git a/mono-addins/Mono.Addins.Gui/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.Gui/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Gui/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.Gui/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Gui/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.Gui/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Gui/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/mono-addins/Mono.Addins.Gui/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 00000000..3169fa7d Binary files /dev/null and b/mono-addins/Mono.Addins.Gui/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/mono-addins/Mono.Addins.Gui/obj/Release/Mono.Addins.Gui.csproj.CoreCompileInputs.cache b/mono-addins/Mono.Addins.Gui/obj/Release/Mono.Addins.Gui.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..d848a772 --- /dev/null +++ b/mono-addins/Mono.Addins.Gui/obj/Release/Mono.Addins.Gui.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +d15178a91c449829f95e464297572955ad9f6df7 diff --git a/mono-addins/Mono.Addins.Gui/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.Gui/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Gui/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.Gui/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Gui/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.Gui/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.GuiGtk3/AssemblyInfo.cs b/mono-addins/Mono.Addins.GuiGtk3/AssemblyInfo.cs new file mode 100644 index 00000000..80aa7691 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/AssemblyInfo.cs @@ -0,0 +1,47 @@ +// +// AssemblyInfo.cs +// +// Author: +// Robert Nordan +// +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle ("Mono.Addins.GuiGtk3")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("Robert Nordan")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("1.3.7")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInfoView.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInfoView.cs new file mode 100644 index 00000000..128bdaef --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInfoView.cs @@ -0,0 +1,484 @@ +// +// AddinInfoView.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.IO; +using System.Collections.Generic; +using Mono.Addins.Setup; +using System.Text; +using Mono.Unix; +using System.Linq; +using UI = Gtk.Builder.ObjectAttribute; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + [System.ComponentModel.ToolboxItem(true)] + class AddinInfoView : Gtk.Bin + { + //From UI files + [UI] EventBox eboxButs; + [UI] Label labelName; + [UI] Label labelDesc; + [UI] Button urlButton; + [UI] HBox boxTitle; + [UI] EventBox boxHeader; + [UI] HBox headerBox; + [UI] Button btnDisable; + [UI] Button btnInstall; + [UI] Button btnUninstall; + [UI] Button btnUpdate; + [UI] Label labelHeader; + [UI] Image imageHeader; + [UI] Label labelVersion; + [UI] EventBox ebox; + [UI] EventBox ebox2; + [UI] Box vboxDesc; + [UI] ScrolledWindow scrolledwindow; + + List selectedEntry = new List (); + List selectedAddin = new List (); + SetupService service; + HeaderBox topHeaderBox; + List previewImages = new List (); + ImageContainer titleIcon; + int titleWidth; + string infoUrl; + + public event EventHandler InstallClicked; + public event EventHandler UninstallClicked; + public event EventHandler UpdateClicked; + public event EventHandler EnableDisableClicked; + + public AddinInfoView () + { + Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.AddinInfoView.ui", null); + builder.Autoconnect (this); + Add ((Box) builder.GetObject ("AddinInfoView")); + AllowInstall = true; + titleWidth = labelName.SizeRequest ().Width; + + HeaderBox hb = new HeaderBox (1,1,1,1); + hb.Show (); + hb.Replace (this); + + hb = new HeaderBox (1,0,0,0); + hb.SetPadding (6,6,6,6); + hb.Show (); + hb.GradientBackround = true; + hb.Replace (eboxButs); + + hb = new HeaderBox (0,1,0,0); + hb.SetPadding (6,6,6,6); + hb.Show (); + hb.GradientBackround = true; + hb.Replace (boxHeader); + topHeaderBox = hb; + + //Enable our buttons for clicking + btnDisable.Clicked += OnBtnDisableClicked; + btnInstall.Clicked += OnBtnInstallClicked; + btnUninstall.Clicked += OnBtnUninstallClicked; + btnUpdate.Clicked += OnBtnUpdateClicked; + urlButton.Clicked += OnUrlButtonClicked; + + ShowAll (); + } + + public void Init (SetupService service) + { + this.service = service; + } + + public bool AllowInstall { get; set; } + + public List SelectedEntries { + get { + return this.selectedEntry; + } + } + + public List SelectedAddins { + get { + return this.selectedAddin; + } + } + + public void ShowAddins (object[] data) + { + selectedEntry.Clear (); + selectedAddin.Clear (); + eboxButs.Visible = true; + topHeaderBox.Hide (); + urlButton.Hide (); + + if (titleIcon != null) { + boxTitle.Remove (titleIcon); + titleIcon.Destroy (); + titleIcon = null; + } + + foreach (var img in previewImages) { + ((Gtk.Container)img.Parent).Remove (img); + img.Destroy (); + } + previewImages.Clear (); + + if (data.Length == 1) { + headerBox.Show (); + ShowAddin (data[0]); + } + else if (data.Length > 1) { + headerBox.Hide (); + StringBuilder sb = new StringBuilder (); + sb.Append (Catalog.GetString ("Multiple selection:\n\n")); + bool allowUpdate = AllowInstall; + bool allowInstall = true; + bool allowUninstall = AllowInstall; + bool allowEnable = true; + bool allowDisable = true; + + foreach (object o in data) { + Addin installed; + if (o is Addin) { + Addin a = (Addin)o; + installed = a; + selectedAddin.Add (a); + sb.Append (a.Name); + } + else { + AddinRepositoryEntry entry = (AddinRepositoryEntry) o; + selectedEntry.Add (entry); + sb.Append (entry.Addin.Name); + installed = AddinManager.Registry.GetAddin (Addin.GetIdName (entry.Addin.Id)); + } + if (installed != null) { + if (GetUpdate (installed) == null) + allowUpdate = false; + allowInstall = false; + if (installed.Enabled) + allowEnable = false; + else + allowDisable = false; + } else + allowEnable = allowDisable = allowUninstall = allowUpdate = false; + + sb.Append ('\n'); + labelDesc.Text = sb.ToString (); + + if (allowEnable) { + btnDisable.Visible = true; + btnDisable.Label = Catalog.GetString ("Enable"); + } else if (allowDisable) { + btnDisable.Visible = true; + btnDisable.Label = Catalog.GetString ("Disable"); + } else + btnDisable.Visible = false; + btnInstall.Visible = allowInstall; + btnUninstall.Visible = allowUninstall; + btnUpdate.Visible = allowUpdate; + } + } + else { + headerBox.Hide (); + btnDisable.Visible = false; + btnInstall.Visible = false; + btnUninstall.Visible = false; + btnUpdate.Visible = false; + eboxButs.Visible = false; + labelDesc.Text = Catalog.GetString ("No selection"); + } + } + + + void ShowAddin (object data) + { + AddinHeader sinfo = null; + Addin installed = null; + AddinHeader updateInfo = null; + string repo = ""; + string downloadSize = null; + + topHeaderBox.Hide (); + + if (data is Addin) { + installed = (Addin) data; + sinfo = SetupService.GetAddinHeader (installed); + var entry = GetUpdate (installed); + if (entry != null) { + updateInfo = entry.Addin; + selectedEntry.Add (entry); + } + foreach (var prop in sinfo.Properties) { + if (prop.Name.StartsWith ("PreviewImage")) + previewImages.Add (new ImageContainer (installed, prop.Value)); + } + string icon32 = sinfo.Properties.GetPropertyValue ("Icon32"); + if (icon32.Length > 0) + titleIcon = new ImageContainer (installed, icon32); + } + else if (data is AddinRepositoryEntry) { + AddinRepositoryEntry entry = (AddinRepositoryEntry) data; + sinfo = entry.Addin; + installed = AddinManager.Registry.GetAddin (Addin.GetIdName (sinfo.Id)); + if (installed != null && Addin.CompareVersions (installed.Version, sinfo.Version) > 0) + updateInfo = sinfo; + selectedEntry.Add (entry); + string rname = !string.IsNullOrEmpty (entry.RepositoryName) ? entry.RepositoryName : entry.RepositoryUrl; + repo = "" + Catalog.GetString ("Available in repository:") + "\n" + GLib.Markup.EscapeText (rname) + "\n\n"; + foreach (var prop in sinfo.Properties) { + if (prop.Name.StartsWith ("PreviewImage")) + previewImages.Add (new ImageContainer (entry, prop.Value)); + } + string icon32 = sinfo.Properties.GetPropertyValue ("Icon32"); + if (icon32.Length > 0) + titleIcon = new ImageContainer (entry, icon32); + int size; + if (int.TryParse (sinfo.Properties.GetPropertyValue ("DownloadSize"), out size)) { + float fs = ((float)size) / 1048576f; + downloadSize = fs.ToString ("0.00 MB"); + } + } else + selectedEntry.Clear (); + + if (installed != null) + selectedAddin.Add (installed); + + string missingDepsTxt = null; + + if (sinfo == null) { + btnDisable.Visible = false; + btnUninstall.Visible = false; + btnUpdate.Visible = false; + } else { + string version; + string newVersion = null; + if (installed != null) { + btnInstall.Visible = false; + btnUpdate.Visible = updateInfo != null && AllowInstall; + btnDisable.Visible = true; + btnDisable.Label = installed.Enabled ? Catalog.GetString ("Disable") : Catalog.GetString ("Enable"); + btnDisable.Visible = installed.Description.CanDisable; + btnUninstall.Visible = installed.Description.CanUninstall; + version = installed.Version; + var missingDeps = Services.GetMissingDependencies (installed); + if (updateInfo != null) { + newVersion = updateInfo.Version; + labelHeader.Markup = "" + Catalog.GetString ("Update available") + ""; +// topHeaderBox.BackgroundColor = new Gdk.Color (0, 132, 208); + imageHeader.Pixbuf = Gdk.Pixbuf.LoadFromResource ("update-16.png"); + topHeaderBox.BackgroundColor = new Gdk.Color (255, 176, 0); + topHeaderBox.Show (); + } + else if (missingDeps.Any ()) { + labelHeader.Markup = "" + Catalog.GetString ("This extension package can't be loaded due to missing dependencies") + ""; + topHeaderBox.BackgroundColor = new Gdk.Color (255, 176, 0); + imageHeader.SetFromStock (Gtk.Stock.DialogWarning, Gtk.IconSize.Menu); + topHeaderBox.Show (); + missingDepsTxt = ""; + foreach (var mdep in missingDeps) { + if (mdep.Found != null) + missingDepsTxt += "\n" + string.Format (Catalog.GetString ("Required: {0} v{1}, found v{2}"), mdep.Addin, mdep.Required, mdep.Found); + else + missingDepsTxt += "\n" + string.Format (Catalog.GetString ("Missing: {0} v{1}"), mdep.Addin, mdep.Required); + } + } + } else { + btnInstall.Visible = AllowInstall; + btnUpdate.Visible = false; + btnDisable.Visible = false; + btnUninstall.Visible = false; + version = sinfo.Version; + } + labelName.Markup = "" + GLib.Markup.EscapeText(sinfo.Name) + ""; + + string ver; + if (newVersion != null) { + ver = "" + Catalog.GetString ("Installed version") + ": " + version + "\n"; + ver += "" + Catalog.GetString ("Repository version") + ": " + newVersion + ""; + } + else + ver = "" + Catalog.GetString ("Version") + " " + version + ""; + + if (downloadSize != null) + ver += "\n" + Catalog.GetString ("Download size") + ": " + downloadSize + ""; + if (missingDepsTxt != null) + ver += "\n\n" + GLib.Markup.EscapeText (Catalog.GetString ("The following dependencies required by this extension package are not available:")) + missingDepsTxt; + labelVersion.Markup = ver; + + string desc = GLib.Markup.EscapeText (sinfo.Description); + labelDesc.Markup = repo + GLib.Markup.EscapeText (desc); + + foreach (var img in previewImages) + vboxDesc.PackStart (img, false, false, 0); + + urlButton.Visible = !string.IsNullOrEmpty (sinfo.Url); + infoUrl = sinfo.Url; + + if (titleIcon != null) { + boxTitle.PackEnd (titleIcon, false, false, 0); + labelName.WidthRequest = titleWidth - 32; + labelVersion.WidthRequest = titleWidth - 32; + } else { + labelName.WidthRequest = titleWidth; + labelVersion.WidthRequest = titleWidth; + } + + if (IsRealized) + SetComponentsBg (); + } + } + + public AddinRepositoryEntry GetUpdate (Addin a) + { + AddinRepositoryEntry[] updates = service.Repositories.GetAvailableAddinUpdates (Addin.GetIdName (a.Id)); + AddinRepositoryEntry best = null; + string bestVersion = a.Version; + foreach (AddinRepositoryEntry e in updates) { + if (Addin.CompareVersions (bestVersion, e.Addin.Version) > 0) { + best = e; + bestVersion = e.Addin.Version; + } + } + return best; + } + + protected virtual void OnBtnInstallClicked (object sender, System.EventArgs e) + { + if (InstallClicked != null) + InstallClicked (this, e); + } + + protected virtual void OnBtnDisableClicked (object sender, System.EventArgs e) + { + if (EnableDisableClicked != null) + EnableDisableClicked (this, e); + } + + protected virtual void OnBtnUpdateClicked (object sender, System.EventArgs e) + { + if (UpdateClicked != null) + UpdateClicked (this, e); + } + + protected virtual void OnBtnUninstallClicked (object sender, System.EventArgs e) + { + if (UninstallClicked != null) + UninstallClicked (this, e); + } + + protected override void OnRealized () + { + base.OnRealized (); + HslColor gcol = ebox.Style.Background (Gtk.StateType.Normal); + gcol.L -= 0.03; + ebox.ModifyBg (Gtk.StateType.Normal, gcol); + ebox2.ModifyBg (Gtk.StateType.Normal, gcol); + scrolledwindow.ModifyBg (Gtk.StateType.Normal, gcol); + SetComponentsBg (); + } + + void SetComponentsBg () + { + HslColor gcol = ebox.Style.Background (Gtk.StateType.Normal); + //gcol.L -= 0.03; + if (titleIcon != null) + titleIcon.ModifyBg (Gtk.StateType.Normal, gcol); + foreach (var i in previewImages) + i.ModifyBg (Gtk.StateType.Normal, gcol); + } + + protected virtual void OnUrlButtonClicked (object sender, System.EventArgs e) + { + System.Diagnostics.Process.Start (infoUrl); + } + } + + class ImageContainer: Gtk.EventBox + { + AddinRepositoryEntry aentry; + IAsyncResult aresult; + Gtk.Image image; + bool destroyed; + + ImageContainer () + { + image = new Gtk.Image (); + Add (image); + image.SetAlignment (0.5f, 0f); + Show (); + } + + public ImageContainer (AddinRepositoryEntry aentry, string fileName): this () + { + this.aentry = aentry; + aresult = aentry.BeginDownloadSupportFile (fileName, ImageDownloaded, null); + } + + public ImageContainer (Addin addin, string fileName): this () + { + string path = System.IO.Path.Combine (addin.Description.BasePath, fileName); + LoadImage (File.OpenRead (path)); + } + + void ImageDownloaded (object state) + { + Gtk.Application.Invoke (delegate { + if (destroyed) + return; + try { + LoadImage (aentry.EndDownloadSupportFile (aresult)); + } catch { + // ignore + } + }); + } + + void LoadImage (Stream s) + { + using (s) { + Gdk.PixbufLoader loader = new Gdk.PixbufLoader (s); + Gdk.Pixbuf pix = image.Pixbuf = loader.Pixbuf; + loader.Dispose (); + if (pix.Width > 250) { + Gdk.Pixbuf spix = pix.ScaleSimple (250, (250 * pix.Height) / pix.Width, Gdk.InterpType.Hyper); + pix.Dispose (); + pix = spix; + } + image.Pixbuf = pix; + image.Show (); + } + } + + protected override void OnDestroyed () + { + destroyed = true; + base.OnDestroyed (); + } + } +} + diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstaller.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstaller.cs new file mode 100644 index 00000000..fb1cbc0a --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstaller.cs @@ -0,0 +1,52 @@ +// +// AddinInstaller.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Mono.Addins.Setup; +using Mono.Unix; + +namespace Mono.Addins.GuiGtk3 +{ + public class AddinInstaller: IAddinInstaller + { + public void InstallAddins (AddinRegistry reg, string message, string[] addinIds) + { + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.AddinInstallerDialog.ui", null); + AddinInstallerDialog dlg = new AddinInstallerDialog (reg, message, addinIds, builder, builder.GetObject ("window1").Handle); + try { + if (dlg.Run () == (int) Gtk.ResponseType.Cancel) + throw new InstallException (Catalog.GetString ("Installation cancelled")); + else if (dlg.ErrMessage != null) + throw new InstallException (dlg.ErrMessage); + } + finally { + dlg.Destroy (); + } + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstallerDialog.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstallerDialog.cs new file mode 100644 index 00000000..9aa3bc62 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinInstallerDialog.cs @@ -0,0 +1,189 @@ +// +// AddinInstallerDialog.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Threading; +using System.Collections; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using Mono.Unix; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + internal class AddinInstallerDialog : Gtk.Dialog, IProgressStatus + { + //From UI File + [UI] Label addinList; + [UI] ProgressBar progressBar; + [UI] Button buttonCancel; + [UI] Button buttonOk; + + PackageCollection entries = new PackageCollection (); + string[] addinIds; + bool addinsNotFound; + string errMessage; + SetupService setup; + + public AddinInstallerDialog (AddinRegistry reg, string message, string[] addinIds, + Builder builder, IntPtr handle): base (handle) + { + builder.Autoconnect (this); + + this.addinIds = addinIds; + setup = new SetupService (reg); + + if (!CheckAddins (true)) + UpdateRepos (); + } + + bool CheckAddins (bool updating) + { + string txt = ""; + entries.Clear (); + bool addinsNotFound = false; + foreach (string id in addinIds) { + string name = Addin.GetIdName (id); + string version = Addin.GetIdVersion (id); + AddinRepositoryEntry[] ares = setup.Repositories.GetAvailableAddin (name, version); + if (ares.Length == 0) { + addinsNotFound = true; + if (updating) + txt += "" + name + " " + version + " (searching extension packages)\n"; + else + txt += "" + name + " " + version + " (not found)\n"; + } else { + entries.Add (Package.FromRepository (ares[0])); + txt += "" + ares[0].Addin.Name + " " + ares[0].Addin.Version + "\n"; + } + } + PackageCollection toUninstall; + DependencyCollection unresolved; + if (!setup.ResolveDependencies (this, entries, out toUninstall, out unresolved)) { + foreach (Dependency dep in unresolved) { + txt += "" + dep.Name + " (not found)\n"; + } + addinsNotFound = true; + } + addinList.Markup = txt; + return !addinsNotFound; + } + + void UpdateRepos () + { + progressBar.Show (); + setup.Repositories.UpdateAllRepositories (this); + progressBar.Hide (); + addinsNotFound = CheckAddins (false); + if (errMessage != null) { + Services.ShowError (null, errMessage, this, true); + errMessage = null; + } + } + + public int LogLevel { + get { + return 1; + } + } + + public bool IsCanceled { + get { + return false; + } + } + + public bool AddinsNotFound { + get { + return addinsNotFound; + } + } + + public string ErrMessage { + get { + return errMessage; + } + } + + public void SetMessage (string msg) + { + progressBar.Text = msg; + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + + public void SetProgress (double progress) + { + progressBar.Fraction = progress; + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + + public void Log (string msg) + { + } + + public void ReportWarning (string message) + { + } + + public void ReportError (string message, System.Exception exception) + { + errMessage = message; + } + + public void Cancel () + { + } + + protected virtual void OnButtonOkClicked (object sender, System.EventArgs e) + { + if (addinsNotFound) { + errMessage = Catalog.GetString ("Some of the required extension packages were not found"); + Respond (Gtk.ResponseType.Ok); + } + else { + errMessage = null; + progressBar.Show (); + progressBar.Fraction = 0; + progressBar.Text = ""; + bool res = setup.Install (this, entries); + if (!res) { + buttonCancel.Sensitive = buttonOk.Sensitive = false; + if (errMessage == null) + errMessage = Catalog.GetString ("Installation failed"); + Services.ShowError (null, errMessage, this, true); + } + } + Respond (Gtk.ResponseType.Ok); + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerDialog.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerDialog.cs new file mode 100644 index 00000000..ed1d5644 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerDialog.cs @@ -0,0 +1,621 @@ +// +// AddinManagerDialog.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; +using Mono.Addins.Setup; +using Mono.Addins; +using Mono.Unix; +using System.Threading; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + class AddinManagerDialog : Dialog, IDisposable + { + //Connected from the UI file + [UI] TreeView addinTree; + [UI] TreeView galleryTreeView; + [UI] TreeView updatesTreeView; + [UI] ComboBox repoCombo; + [UI] Box vboxUpdates; + [UI] Box vboxGallery; + [UI] ScrolledWindow scrolledUpdates; + [UI] ScrolledWindow scrolledGallery; + [UI] EventBox eboxRepoUpdates; + [UI] EventBox eboxRepo; + [UI] Label labelUpdates; + [UI] Button buttonUpdateAll; + [UI] Button buttonRefreshUpdates; + [UI] Button buttonRefresh; + [UI] Button buttonInstallFromFile; + [UI] Notebook notebook; + [UI] EventBox eventbox1; + [UI] EventBox eventbox2; + [UI] EventBox eventbox3; + + //Manually fill in from the UI File + AddinInfoView addininfoInstalled; + AddinInfoView addininfoGallery; + AddinInfoView addininfoUpdates; + + AddinTreeWidget tree; + AddinTreeWidget galleryTree; + AddinTreeWidget updatesTree; + + SetupService service = new SetupService (); + ListStore repoStore; + int lastRepoActive; + SearchEntry filterEntry; + Label installedTabLabel; + Label updatesTabLabel; + Label galleryTabLabel; + + const string AllRepoMarker = "__ALL"; + const string ManageRepoMarker = "__MANAGE"; + + internal bool AllowInstall + { + set { + addininfoInstalled.AllowInstall = value; + addininfoGallery.AllowInstall = value; + addininfoUpdates.AllowInstall = value; + } + } + + public AddinManagerDialog (Builder builder, IntPtr handle): base (handle) + { + builder.Autoconnect (this); +// TransientFor = parent; +// HasSeparator = false; + addininfoInstalled = new AddinInfoView (); + addininfoInstalled.InstallClicked += OnInstallClicked; + addininfoInstalled.UninstallClicked += OnUninstallClicked; + addininfoInstalled.UpdateClicked += OnUpdateClicked; + addininfoInstalled.EnableDisableClicked += OnEnableDisableClicked; + eventbox2.Child = addininfoInstalled; + addininfoGallery = new AddinInfoView (); + addininfoGallery.InstallClicked += OnInstallClicked; + addininfoGallery.UninstallClicked += OnUninstallClicked; + addininfoGallery.UpdateClicked += OnUpdateClicked; + addininfoGallery.EnableDisableClicked += OnEnableDisableClicked; + eventbox1.Child = addininfoGallery; + addininfoUpdates = new AddinInfoView (); + addininfoUpdates.InstallClicked += OnInstallClicked; + addininfoUpdates.UninstallClicked += OnUninstallClicked; + addininfoUpdates.UpdateClicked += OnUpdateClicked; + addininfoUpdates.EnableDisableClicked += OnEnableDisableClicked; + eventbox3.Child = addininfoUpdates; + +// Services.PlaceDialog (this, parent); + + addininfoInstalled.Init (service); + addininfoGallery.Init (service); + + addinTree.Selection.Mode = SelectionMode.Multiple; + tree = new AddinTreeWidget (addinTree); + addinTree.Selection.Changed += OnSelectionChanged; + tree.VersionVisible = false; + + galleryTreeView.Selection.Mode = SelectionMode.Multiple; + galleryTree = new AddinTreeWidget (galleryTreeView); + galleryTree.VersionVisible = false; + galleryTree.ShowInstalledMarkers = true; + galleryTreeView.Selection.Changed += OnGallerySelectionChanged; + + updatesTreeView.Selection.Mode = SelectionMode.Multiple; + updatesTree = new AddinTreeWidget (updatesTreeView); + updatesTree.VersionVisible = false; + updatesTree.ShowCategories = false; + updatesTree.ShowInstalledMarkers = true; + updatesTreeView.Selection.Changed += OnGallerySelectionChanged; + + //Wiring more buttons + buttonUpdateAll.Clicked += OnUpdateAll; + buttonRefreshUpdates.Clicked += OnButtonRefreshClicked; + buttonRefresh.Clicked += OnButtonRefreshClicked; + buttonInstallFromFile.Clicked += OnButtonInstallFromFileClicked; + repoCombo.Changed += OnRepoComboChanged; + + repoStore = new ListStore (typeof(string), typeof(string)); + repoCombo.Model = repoStore; + CellRendererText crt = new CellRendererText (); + repoCombo.PackStart (crt, true); + repoCombo.AddAttribute (crt, "text", 0); + repoCombo.RowSeparatorFunc = delegate(ITreeModel model, TreeIter iter) { + string val = (string) model.GetValue (iter, 0); + return val == "---"; + }; + + // Make sure the tree has the focus when switching tabs + + vboxUpdates.FocusChain = new Widget [] { scrolledUpdates, eboxRepoUpdates }; + vboxGallery.FocusChain = new Widget [] { scrolledGallery, eboxRepo }; + + // Improve the look of the headers + + HBox tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("plugin-22.png")), false, false, 0); + installedTabLabel = new Label (Catalog.GetString ("Installed")); + tab.PackStart (installedTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (0), tab); + + tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("plugin-update-22.png")), false, false, 0); + updatesTabLabel = new Label (Catalog.GetString ("Updates")); + tab.PackStart (updatesTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (1), tab); + + tab = new HBox (false, 3); + tab.PackStart (new Image (Gdk.Pixbuf.LoadFromResource ("update-16.png")), false, false, 0); + galleryTabLabel = new Label (Catalog.GetString ("Gallery")); + tab.PackStart (galleryTabLabel, true, true, 0); + tab.BorderWidth = 3; + tab.ShowAll (); + notebook.SetTabLabel (notebook.GetNthPage (2), tab); + + // Gradient header for the updates and gallery tabs + + HeaderBox hb = new HeaderBox (1, 0, 1, 1); + hb.SetPadding (6,6,6,6); + hb.GradientBackround = true; + hb.Show (); + hb.Replace (eboxRepo); + + hb = new HeaderBox (1, 0, 1, 1); + hb.SetPadding (6,6,6,6); + hb.GradientBackround = true; + hb.Show (); + hb.Replace (eboxRepoUpdates); + + InsertFilterEntry (); + + FillRepos (); + repoCombo.Active = 0; + + LoadAll (); + + ShowAll (); + } + + void InsertFilterEntry () + { + filterEntry = new SearchEntry (); + filterEntry.Entry.SetSizeRequest (200, filterEntry.Entry.SizeRequest ().Height); + filterEntry.Show (); + notebook.SizeAllocated += delegate { + RepositionFilter (); + }; + filterEntry.TextChanged += delegate { + tree.SetFilter (filterEntry.Text); + galleryTree.SetFilter (filterEntry.Text); + updatesTree.SetFilter (filterEntry.Text); + LoadAll (); + addinTree.ExpandAll (); + galleryTreeView.ExpandAll (); + }; + RepositionFilter (); + } + + void RepositionFilter () + { + int w = filterEntry.SizeRequest ().Width; + int h = filterEntry.SizeRequest ().Height; + var alloc = notebook.Allocation; + filterEntry.SetAllocation (new Gdk.Rectangle (alloc.Left + alloc.Width - 1 - w, alloc.Y, w, h)); + } + + protected override void OnShown () + { + base.OnShown (); + filterEntry.Parent = notebook; + } + +// public override void Dispose () +// { +// base.Dispose (); +// Destroy (); +// } + + internal void OnSelectionChanged (object sender, EventArgs args) + { + UpdateAddinInfo (); + } + + internal void OnManageRepos (object sender, EventArgs e) + { + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.ManageSitesDialog.ui", null); + ManageSitesDialog dlg = new ManageSitesDialog (service, builder, builder.GetObject ("window1").Handle); + try { + dlg.Run (); + } finally { + dlg.Destroy (); + } + } + + void LoadAll () + { + LoadInstalled (); + LoadGallery (); + LoadUpdates (); + UpdateAddinInfo (); + } + + void UpdateAddinInfo () + { + addininfoInstalled.ShowAddins (tree.ActiveAddinsData); + addininfoGallery.ShowAddins (galleryTree.ActiveAddinsData); + addininfoUpdates.ShowAddins (updatesTree.ActiveAddinsData); + } + + void LoadInstalled () + { + object s = tree.SaveStatus (); + + int count = 0; + tree.Clear (); + foreach (Addin ainfo in AddinManager.Registry.GetModules (AddinSearchFlags.IncludeAddins | AddinSearchFlags.LatestVersionsOnly)) { + if (Services.InApplicationNamespace (service, ainfo.Id) && !ainfo.Description.IsHidden) { + AddinHeader ah = SetupService.GetAddinHeader (ainfo); + if (IsFiltered (ah)) + continue; + AddinStatus st = AddinStatus.Installed; + if (!ainfo.Enabled || Services.GetMissingDependencies (ainfo).Any()) + st |= AddinStatus.Disabled; + if (addininfoInstalled.GetUpdate (ainfo) != null) + st |= AddinStatus.HasUpdate; + tree.AddAddin (ah, ainfo, st); + count++; + } + } + + if (count > 0) + tree.RestoreStatus (s); + else + tree.ShowEmptyMessage (); + + UpdateAddinInfo (); + + installedTabLabel.Text = Catalog.GetString ("Installed"); + + if (filterEntry.Text.Length != 0 && count > 0) + installedTabLabel.Text += " (" + count + ")"; + } + + void FillRepos () + { + int i = repoCombo.Active; + repoStore.Clear (); + + repoStore.AppendValues (Catalog.GetString ("All repositories"), AllRepoMarker); + + foreach (AddinRepository rep in service.Repositories.GetRepositories ()) { + if (rep.Enabled) + repoStore.AppendValues (rep.Title, rep.Url); + } + repoStore.AppendValues ("---", ""); + repoStore.AppendValues (Catalog.GetString ("Manage Repositories..."), ManageRepoMarker); + repoCombo.Active = i; + } + + string GetRepoSelection () + { + Gtk.TreeIter iter; + if (!repoCombo.GetActiveIter (out iter)) + return null; + return (string) repoStore.GetValue (iter, 1); + } + + void LoadGallery () + { + object s = galleryTree.SaveStatus (); + + galleryTree.Clear (); + + string rep = GetRepoSelection (); + + AddinRepositoryEntry[] reps; + if (rep == AllRepoMarker) + reps = service.Repositories.GetAvailableAddins (RepositorySearchFlags.LatestVersionsOnly); + else + reps = service.Repositories.GetAvailableAddins (rep, RepositorySearchFlags.LatestVersionsOnly); + + int count = 0; + + foreach (AddinRepositoryEntry arep in reps) + { + if (!Services.InApplicationNamespace (service, arep.Addin.Id)) + continue; + + if (IsFiltered (arep.Addin)) + continue; + + AddinStatus status = AddinStatus.NotInstalled; + + // Find whatever version is installed + Addin sinfo = AddinManager.Registry.GetAddin (Addin.GetIdName (arep.Addin.Id)); + + if (sinfo != null) { + status |= AddinStatus.Installed; + if (!sinfo.Enabled || Services.GetMissingDependencies (sinfo).Any()) + status |= AddinStatus.Disabled; + if (Addin.CompareVersions (sinfo.Version, arep.Addin.Version) > 0) + status |= AddinStatus.HasUpdate; + } + galleryTree.AddAddin (arep.Addin, arep, status); + count++; + } + + if (count > 0) + galleryTree.RestoreStatus (s); + else + galleryTree.ShowEmptyMessage (); + + galleryTabLabel.Text = Catalog.GetString ("Gallery"); + + if (filterEntry.Text.Length != 0 && count > 0) + galleryTabLabel.Text += " (" + count + ")"; + } + + void LoadUpdates () + { + object s = updatesTree.SaveStatus (); + + updatesTree.Clear (); + + AddinRepositoryEntry[] reps; + reps = service.Repositories.GetAvailableAddins (RepositorySearchFlags.LatestVersionsOnly); + + int count = 0; + + foreach (AddinRepositoryEntry arep in reps) + { + if (!Services.InApplicationNamespace (service, arep.Addin.Id)) + continue; + + // Find whatever version is installed + Addin sinfo = AddinManager.Registry.GetAddin (Addin.GetIdName (arep.Addin.Id)); + if (sinfo == null || !sinfo.Enabled || Addin.CompareVersions (sinfo.Version, arep.Addin.Version) <= 0) + continue; + + if (IsFiltered (arep.Addin)) + continue; + + AddinStatus status = AddinStatus.Installed; + if (!sinfo.Enabled || Services.GetMissingDependencies (sinfo).Any()) + status |= AddinStatus.Disabled; + + updatesTree.AddAddin (arep.Addin, arep, status | AddinStatus.HasUpdate); + count++; + } + + labelUpdates.Text = string.Format (Catalog.GetPluralString ("{0} update available", "{0} updates available", count), count); + updatesTabLabel.Text = Catalog.GetString ("Updates"); + if (count > 0) + updatesTabLabel.Text += " (" + count + ")"; + + buttonUpdateAll.Visible = count > 0; + + if (count > 0) + updatesTree.RestoreStatus (s); + else + updatesTree.ShowEmptyMessage (); + } + + bool IsFiltered (AddinHeader ah) + { + if (filterEntry.Text.Length == 0) + return false; + if (ah.Name.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + if (ah.Description.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + if (ah.Id.IndexOf (filterEntry.Text, StringComparison.CurrentCultureIgnoreCase) != -1) + return false; + return true; + } + + void ManageSites () + { + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.ManageSitesDialog.ui", null); + ManageSitesDialog dlg = new ManageSitesDialog (service, builder, builder.GetObject ("ManageSitesDialog").Handle); + try { + dlg.Run (); + repoCombo.Active = lastRepoActive; + FillRepos (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnRepoComboChanged (object sender, System.EventArgs e) + { + if (GetRepoSelection () == ManageRepoMarker) + ManageSites (); + else + LoadGallery (); + lastRepoActive = repoCombo.Active; + } + + protected virtual void OnGallerySelectionChanged (object sender, System.EventArgs e) + { + UpdateAddinInfo (); + } + + protected virtual void OnButtonRefreshClicked (object sender, System.EventArgs e) + { + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.ProgressDialog.ui", null); + ProgressDialog pdlg = new ProgressDialog (builder, builder.GetObject ("ProgressDialog").Handle); + pdlg.Show (); + pdlg.SetMessage (AddinManager.CurrentLocalizer.GetString ("Updating repository")); + bool updateDone = false; + + Thread t = new Thread (delegate () { + try { + service.Repositories.UpdateAllRepositories (pdlg); + } finally { + updateDone = true; + } + }); + t.Start (); + while (!updateDone) { + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + Thread.Sleep (50); + } + pdlg.Destroy (); + LoadGallery (); + LoadUpdates (); + } + + protected virtual void OnInstallClicked (object sender, System.EventArgs e) + { + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.InstallDialog.ui", null); + InstallDialog dlg = new InstallDialog (service, builder, builder.GetObject ("InstallDialog").Handle); + try { + List selectedEntry = ((AddinInfoView)sender).SelectedEntries; + dlg.InitForInstall (selectedEntry.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnUninstallClicked (object sender, System.EventArgs e) + { + List selectedAddin = ((AddinInfoView)sender).SelectedAddins; + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.InstallDialog.ui", null); + InstallDialog dlg = new InstallDialog (service, builder, builder.GetObject ("InstallDialog").Handle); + try { + dlg.InitForUninstall (selectedAddin.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) { + LoadAll (); + } + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnUpdateClicked (object sender, System.EventArgs e) + { + List selectedEntry = ((AddinInfoView)sender).SelectedEntries; + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.InstallDialog.ui", null); + InstallDialog dlg = new InstallDialog (service, builder, builder.GetObject ("InstallDialog").Handle); + try { + dlg.InitForInstall (selectedEntry.ToArray ()); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnEnableDisableClicked (object sender, System.EventArgs e) + { + try { + foreach (Addin a in ((AddinInfoView)sender).SelectedAddins) { + a.Enabled = !a.Enabled; + } + LoadAll (); + } + catch (Exception ex) { + Services.ShowError (ex, null, this, true); + } + } + + protected virtual void OnUpdateAll (object sender, System.EventArgs e) + { + object[] data = updatesTree.AddinsData; + AddinRepositoryEntry[] entries = new AddinRepositoryEntry [data.Length]; + Array.Copy (data, entries, data.Length); + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.InstallDialog.ui", null); + InstallDialog dlg = new InstallDialog (service, builder, builder.GetObject ("InstallDialog").Handle); + try { + dlg.InitForInstall (entries); + if (dlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + dlg.Destroy (); + } + } + + static string lastFolder; + + protected virtual void OnButtonInstallFromFileClicked (object sender, System.EventArgs e) + { + string[] files; + Gtk.FileChooserDialog dlg = new Gtk.FileChooserDialog (Catalog.GetString ("Install Extension Package"), this, FileChooserAction.Open); + try { + if (lastFolder != null) + dlg.SetCurrentFolder (lastFolder); + else + dlg.SetCurrentFolder (Environment.GetFolderPath (Environment.SpecialFolder.Personal)); + dlg.SelectMultiple = true; + + Gtk.FileFilter f = new Gtk.FileFilter (); + f.AddPattern ("*.mpack"); + f.Name = Catalog.GetString ("Extension packages"); + dlg.AddFilter (f); + + f = new Gtk.FileFilter (); + f.AddPattern ("*"); + f.Name = Catalog.GetString ("All files"); + dlg.AddFilter (f); + + dlg.AddButton (Gtk.Stock.Cancel, ResponseType.Cancel); + dlg.AddButton (Gtk.Stock.Open, ResponseType.Ok); + if (dlg.Run () != (int) Gtk.ResponseType.Ok) + return; + files = dlg.Filenames; + lastFolder = dlg.CurrentFolder; + } finally { + dlg.Destroy (); + } + + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.InstallDialog.ui", null); + InstallDialog idlg = new InstallDialog (service, builder, builder.GetObject ("InstallDialog").Handle); + try { + idlg.InitForInstall (files); + if (idlg.Run () == (int) Gtk.ResponseType.Ok) + LoadAll (); + } finally { + idlg.Destroy (); + } + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerWindow.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerWindow.cs new file mode 100644 index 00000000..d6718645 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinManagerWindow.cs @@ -0,0 +1,78 @@ +// +// AddinManagerWindow.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins.GuiGtk3 +{ + public class AddinManagerWindow + { + private static bool mAllowInstall = true; + + public static bool AllowInstall + { + get { return mAllowInstall; } + set { mAllowInstall = value; } + } + + private AddinManagerWindow() + { + } + + private static void InitDialog (AddinManagerDialog dlg) + { + dlg.AllowInstall = AllowInstall; + } + + public static Gtk.Window Show (Gtk.Window parent) + { + + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.AddinManagerDialog.ui", null); + AddinManagerDialog dlg = new AddinManagerDialog (builder, builder.GetObject ("AddinManagerDialog").Handle); + InitDialog (dlg); + parent.Add (dlg); + dlg.Show (); + return dlg; + } + + public static void Run (Gtk.Window parent) + { + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.AddinManagerDialog.ui", null); + AddinManagerDialog dlg = new AddinManagerDialog (builder, builder.GetObject ("AddinManagerDialog").Handle); + try { + InitDialog (dlg); + dlg.Run (); + } finally { + dlg.Destroy (); + } + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinTreeWidget.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinTreeWidget.cs new file mode 100644 index 00000000..40885500 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/AddinTreeWidget.cs @@ -0,0 +1,574 @@ +// +// AddinTreeWidget.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using Gtk; +using Gdk; +using Mono.Addins; +using Mono.Addins.Setup; +using Mono.Unix; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Mono.Addins.GuiGtk3 +{ + public class AddinTreeWidget + { + protected Gtk.TreeView treeView; + protected Gtk.TreeStore treeStore; + bool allowSelection; + ArrayList selected = new ArrayList (); + Hashtable addinData = new Hashtable (); + TreeViewColumn versionColumn; + string filter; + Dictionary cachedIcons = new Dictionary (); + bool disposed; + + Gdk.Pixbuf iconInstalled; + Gdk.Pixbuf updateOverlay; + Gdk.Pixbuf installedOverlay; + + public event EventHandler SelectionChanged; + + const int ColAddin = 0; + const int ColData = 1; + const int ColName = 2; + const int ColVersion = 3; + const int ColAllowSelection = 4; + const int ColSelected = 5; + const int ColImage = 6; + const int ColShowImage = 7; + + public AddinTreeWidget (Gtk.TreeView treeView) + { + iconInstalled = Gdk.Pixbuf.LoadFromResource ("plugin-32.png"); + updateOverlay = Gdk.Pixbuf.LoadFromResource ("update-available-overlay-16.png"); + installedOverlay = Gdk.Pixbuf.LoadFromResource ("installed-overlay-16.png"); + + this.treeView = treeView; + ArrayList list = new ArrayList (); + AddStoreTypes (list); + Type[] types = (Type[]) list.ToArray (typeof(Type)); + treeStore = new Gtk.TreeStore (types); + treeView.Model = treeStore; + CreateColumns (); + ShowCategories = true; + + treeView.Destroyed += HandleTreeViewDestroyed; + } + + void HandleTreeViewDestroyed (object sender, EventArgs e) + { + disposed = true; + foreach (var px in cachedIcons.Values) + if (px != null) px.Dispose (); + } + + internal void SetFilter (string text) + { + this.filter = text; + } + + internal void ShowEmptyMessage () + { + treeStore.AppendValues (null, null, Catalog.GetString ("No extension packages found"), "", false, false, null, false); + } + + protected virtual void AddStoreTypes (ArrayList list) + { + list.Add (typeof(object)); + list.Add (typeof(object)); + list.Add (typeof(string)); + list.Add (typeof(string)); + list.Add (typeof(bool)); + list.Add (typeof(bool)); + list.Add (typeof (Pixbuf)); + list.Add (typeof(bool)); + } + + protected virtual void CreateColumns () + { + TreeViewColumn col = new TreeViewColumn (); + col.Title = Catalog.GetString ("Extension Package"); + + CellRendererToggle crtog = new CellRendererToggle (); + crtog.Activatable = true; + crtog.Toggled += new ToggledHandler (OnAddinToggled); + col.PackStart (crtog, false); + + CellRendererPixbuf pr = new CellRendererPixbuf (); + col.PackStart (pr, false); + col.AddAttribute (pr, "pixbuf", ColImage); + col.AddAttribute (pr, "visible", ColShowImage); + + CellRendererText crt = new CellRendererText (); + crt.Ellipsize = Pango.EllipsizeMode.End; + col.PackStart (crt, true); + + col.AddAttribute (crt, "markup", ColName); + col.AddAttribute (crtog, "visible", ColAllowSelection); + col.AddAttribute (crtog, "active", ColSelected); + col.Expand = true; + treeView.AppendColumn (col); + + col = new TreeViewColumn (); + col.Title = Catalog.GetString ("Version"); + col.PackStart (crt, true); + col.AddAttribute (crt, "markup", ColVersion); + versionColumn = col; + treeView.AppendColumn (col); + } + + public bool AllowSelection { + get { return allowSelection; } + set { allowSelection = value; } + } + + public bool VersionVisible { + get { + return versionColumn.Visible; + } + set { + versionColumn.Visible = value; + treeView.HeadersVisible = value; + } + } + + public bool ShowCategories { get; set; } + + void OnAddinToggled (object o, ToggledArgs args) + { + TreeIter it; + if (treeStore.GetIter (out it, new TreePath (args.Path))) { + bool sel = !(bool) treeStore.GetValue (it, 5); + treeStore.SetValue (it, 5, sel); + AddinHeader info = (AddinHeader) treeStore.GetValue (it, 0); + if (sel) + selected.Add (info); + else + selected.Remove (info); + + OnSelectionChanged (EventArgs.Empty); + } + } + + protected virtual void OnSelectionChanged (EventArgs e) + { + if (SelectionChanged != null) + SelectionChanged (this, e); + } + + public void Clear () + { + addinData.Clear (); + selected.Clear (); + treeStore.Clear (); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, bool enabled) + { + return AddAddin (info, dataItem, enabled, true); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, bool enabled, bool userDir) + { + return AddAddin (info, dataItem, enabled ? AddinStatus.Installed : AddinStatus.Disabled | AddinStatus.Installed); + } + + public TreeIter AddAddin (AddinHeader info, object dataItem, AddinStatus status) + { + addinData [info] = dataItem; + TreeIter iter; + if (ShowCategories) { + TreeIter piter = TreeIter.Zero; + if (info.Category == "") { + string otherCat = Catalog.GetString ("Other"); + piter = FindCategory (otherCat); + } else { + piter = FindCategory (info.Category); + } + iter = treeStore.AppendNode (piter); + } else { + iter = treeStore.AppendNode (); + } + UpdateRow (iter, info, dataItem, status); + return iter; + } + + protected virtual void UpdateRow (TreeIter iter, AddinHeader info, object dataItem, AddinStatus status) + { + bool sel = selected.Contains (info); + + treeStore.SetValue (iter, ColAddin, info); + treeStore.SetValue (iter, ColData, dataItem); + + string name = EscapeWithFilterMarker (info.Name); + if (!string.IsNullOrEmpty (info.Description)) { + string desc = info.Description; + int i = desc.IndexOf ('\n'); + if (i != -1) + desc = desc.Substring (0, i); + name += "\n" + EscapeWithFilterMarker (desc) + ""; + } + + if (status != AddinStatus.Disabled) { + treeStore.SetValue (iter, ColName, name); + treeStore.SetValue (iter, ColVersion, info.Version); + treeStore.SetValue (iter, ColAllowSelection, allowSelection); + } + else { + treeStore.SetValue (iter, ColName, "" + name + ""); + treeStore.SetValue (iter, ColVersion, "" + info.Version + ""); + treeStore.SetValue (iter, ColAllowSelection, false); + } + + treeStore.SetValue (iter, ColShowImage, true); + treeStore.SetValue (iter, ColSelected, sel); + SetRowIcon (iter, info, dataItem, status); + } + + void SetRowIcon (TreeIter it, AddinHeader info, object dataItem, AddinStatus status) + { + string customIcom = info.Properties.GetPropertyValue ("Icon32"); + string iconId = info.Id + " " + info.Version + " " + customIcom; + Gdk.Pixbuf customPix; + + if (customIcom.Length == 0) { + customPix = null; + iconId = "__"; + } + else if (!cachedIcons.TryGetValue (iconId, out customPix)) { + + if (dataItem is Addin) { + string file = Path.Combine (((Addin)dataItem).Description.BasePath, customIcom); + if (File.Exists (file)) { + try { + customPix = new Gdk.Pixbuf (file); + } catch (Exception ex) { + Console.WriteLine (ex); + } + } + cachedIcons [iconId] = customPix; + } + else if (dataItem is AddinRepositoryEntry) { + AddinRepositoryEntry arep = (AddinRepositoryEntry) dataItem; + string tmpId = iconId; + arep.BeginDownloadSupportFile (customIcom, delegate (IAsyncResult res) { + Gtk.Application.Invoke (delegate { + LoadRemoteIcon (it, tmpId, arep, res, info, dataItem, status); + }); + }, null); + iconId = "__"; + } + } + + StoreIcon (it, iconId, customPix, status); + } + + Gdk.Pixbuf GetCachedIcon (string id, string effect, Func pixbufGenerator) + { + Gdk.Pixbuf pix; + if (!cachedIcons.TryGetValue (id + "_" + effect, out pix)) + cachedIcons [id + "_" + effect] = pix = pixbufGenerator (); + return pix; + } + + internal bool ShowInstalledMarkers = false; + + void StoreIcon (TreeIter it, string iconId, Gdk.Pixbuf customPix, AddinStatus status) + { + if (customPix == null) + customPix = iconInstalled; + + if ((status & AddinStatus.Installed) == 0) { + treeStore.SetValue (it, ColImage, customPix); + return; + } else if (ShowInstalledMarkers && (status & AddinStatus.HasUpdate) == 0) { + customPix = GetCachedIcon (iconId, "InstalledOverlay", delegate { return Services.AddIconOverlay (customPix, installedOverlay); }); + iconId = iconId + "_Installed"; + } + + if ((status & AddinStatus.Disabled) != 0) { + customPix = GetCachedIcon (iconId, "Desaturate", delegate { return Services.DesaturateIcon (customPix); }); + iconId = iconId + "_Desaturate"; + } + if ((status & AddinStatus.HasUpdate) != 0) + customPix = GetCachedIcon (iconId, "UpdateOverlay", delegate { return Services.AddIconOverlay (customPix, updateOverlay); }); + + treeStore.SetValue (it, ColImage, customPix); + } + + + void LoadRemoteIcon (TreeIter it, string iconId, AddinRepositoryEntry arep, IAsyncResult res, AddinHeader info, object dataItem, AddinStatus status) + { + if (!disposed && treeStore.IterIsValid (it)) { + Gdk.Pixbuf customPix = null; + try { + Gdk.PixbufLoader loader = new Gdk.PixbufLoader (arep.EndDownloadSupportFile (res)); + customPix = loader.Pixbuf; + } catch (Exception ex) { + Console.WriteLine (ex); + } + cachedIcons [iconId] = customPix; + StoreIcon (it, iconId, customPix, status); + } + } + + string EscapeWithFilterMarker (string txt) + { + if (string.IsNullOrEmpty (filter)) + return GLib.Markup.EscapeText (txt); + + StringBuilder sb = new StringBuilder (); + int last = 0; + int i = txt.IndexOf (filter, StringComparison.CurrentCultureIgnoreCase); + while (i != -1) { + sb.Append (GLib.Markup.EscapeText (txt.Substring (last, i - last))); + sb.Append ("").Append (txt.Substring (i, filter.Length)).Append (""); + last = i + filter.Length; + i = txt.IndexOf (filter, last, StringComparison.CurrentCultureIgnoreCase); + } + if (last < txt.Length) + sb.Append (GLib.Markup.EscapeText (txt.Substring (last, txt.Length - last))); + return sb.ToString (); + } + + public object GetAddinData (AddinHeader info) + { + return addinData [info]; + } + + public AddinHeader[] GetSelectedAddins () + { + return (AddinHeader[]) selected.ToArray (typeof(AddinHeader)); + } + + TreeIter FindCategory (string namePath) + { + TreeIter iter = TreeIter.Zero; + string[] paths = namePath.Split ('/'); + foreach (string name in paths) { + TreeIter child; + if (!FindCategory (iter, name, out child)) { + if (iter.Equals (TreeIter.Zero)) + iter = treeStore.AppendValues (null, null, name, "", false, false, null, false); + else + iter = treeStore.AppendValues (iter, null, null, name, "", false, false, null, false); + } + else + iter = child; + } + return iter; + } + + bool FindCategory (TreeIter piter, string name, out TreeIter child) + { + if (piter.Equals (TreeIter.Zero)) { + if (!treeStore.GetIterFirst (out child)) + return false; + } + else if (!treeStore.IterChildren (out child, piter)) + return false; + + do { + if (((string) treeStore.GetValue (child, ColName)) == name) { + return true; + } + } while (treeStore.IterNext (ref child)); + + return false; + } + + public AddinHeader ActiveAddin { + get { + AddinHeader[] sel = ActiveAddins; + if (sel.Length > 0) + return sel[0]; + else + return null; + } + } + + public AddinHeader[] ActiveAddins { + get { + List list = new List (); + foreach (TreePath p in treeView.Selection.GetSelectedRows ()) { + TreeIter iter; + treeStore.GetIter (out iter, p); + AddinHeader ah = (AddinHeader) treeStore.GetValue (iter, 0); + if (ah != null) + list.Add (ah); + } + return list.ToArray (); + } + } + + public object ActiveAddinData { + get { + AddinHeader ai = ActiveAddin; + return ai != null ? GetAddinData (ai) : null; + } + } + + public object[] ActiveAddinsData { + get { + List res = new List (); + foreach (AddinHeader ai in ActiveAddins) { + res.Add (GetAddinData (ai)); + } + return res.ToArray (); + } + } + + public object[] AddinsData { + get { + object[] data = new object [addinData.Count]; + addinData.Values.CopyTo (data, 0); + return data; + } + } + + public object SaveStatus () + { + TreeIter iter; + ArrayList list = new ArrayList (); + + // Save the current selection + list.Add (treeView.Selection.GetSelectedRows ()); + + if (!treeStore.GetIterFirst (out iter)) + return null; + + // Save the expand state + do { + SaveStatus (list, iter); + } while (treeStore.IterNext (ref iter)); + + return list; + } + + void SaveStatus (ArrayList list, TreeIter iter) + { + Gtk.TreePath path = treeStore.GetPath (iter); + if (treeView.GetRowExpanded (path)) + list.Add (path); + if (treeStore.IterChildren (out iter, iter)) { + do { + SaveStatus (list, iter); + } while (treeStore.IterNext (ref iter)); + } + } + + public void RestoreStatus (object ob) + { + if (ob == null) + return; + + // The first element is the selection + ArrayList list = (ArrayList) ob; + TreePath[] selpaths = (TreePath[]) list [0]; + list.RemoveAt (0); + + foreach (TreePath path in list) + treeView.ExpandRow (path, false); + + foreach (TreePath p in selpaths) + treeView.Selection.SelectPath (p); + } + + public void SelectAll () + { + TreeIter iter; + + if (!treeStore.GetIterFirst (out iter)) + return; + do { + SelectAll (iter); + } while (treeStore.IterNext (ref iter)); + OnSelectionChanged (EventArgs.Empty); + } + + void SelectAll (TreeIter iter) + { + AddinHeader info = (AddinHeader) treeStore.GetValue (iter, ColAddin); + + if (info != null) { + treeStore.SetValue (iter, ColSelected, true); + if (!selected.Contains (info)) + selected.Add (info); + treeView.ExpandToPath (treeStore.GetPath (iter)); + } else { + if (treeStore.IterChildren (out iter, iter)) { + do { + SelectAll (iter); + } while (treeStore.IterNext (ref iter)); + } + } + } + + public void UnselectAll () + { + TreeIter iter; + if (!treeStore.GetIterFirst (out iter)) + return; + do { + UnselectAll (iter); + } while (treeStore.IterNext (ref iter)); + OnSelectionChanged (EventArgs.Empty); + } + + void UnselectAll (TreeIter iter) + { + AddinHeader info = (AddinHeader) treeStore.GetValue (iter, ColAddin); + if (info != null) { + treeStore.SetValue (iter, ColSelected, false); + selected.Remove (info); + } else { + if (treeStore.IterChildren (out iter, iter)) { + do { + UnselectAll (iter); + } while (treeStore.IterNext (ref iter)); + } + } + } + } + + [Flags] + public enum AddinStatus + { + NotInstalled = 0, + Installed = 1, + Disabled = 2, + HasUpdate = 4 + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ErrorDialog.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ErrorDialog.cs new file mode 100644 index 00000000..685597d8 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ErrorDialog.cs @@ -0,0 +1,108 @@ +// +// ErrorDialog.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + class ErrorDialog : Dialog + { + //From UI File + [UI] Button okButton; + [UI] Expander expander; + [UI] Label descriptionLabel; + [UI] TextView detailsTextView; + + TextTag tagNoWrap; + TextTag tagWrap; + + public ErrorDialog (Builder builder, IntPtr handle): base (handle) + { + builder.Autoconnect (this); +// TransientFor = parent; + okButton.Clicked += new EventHandler (OnClose); + expander.Activated += new EventHandler (OnExpanded); + descriptionLabel.ModifyBg (StateType.Normal, new Gdk.Color (255,0,0)); + + tagNoWrap = new TextTag ("nowrap"); + tagNoWrap.WrapMode = WrapMode.None; + detailsTextView.Buffer.TagTable.Add (tagNoWrap); + + tagWrap = new TextTag ("wrap"); + tagWrap.WrapMode = WrapMode.Word; + detailsTextView.Buffer.TagTable.Add (tagWrap); + + expander.Visible = false; + + ShowAll (); + } + + public string Message { + get { return descriptionLabel.Text; } + set { + string message = value; + while (message.EndsWith ("\r") || message.EndsWith ("\n")) + message = message.Substring (0, message.Length - 1); + if (!message.EndsWith (".")) message += "."; + descriptionLabel.Text = message; + } + } + + public void AddDetails (string text, bool wrapped) + { + TextIter it = detailsTextView.Buffer.EndIter; + if (wrapped) + detailsTextView.Buffer.InsertWithTags (ref it, text, tagWrap); + else + detailsTextView.Buffer.InsertWithTags (ref it, text, tagNoWrap); + expander.Visible = true; + } + + void OnClose (object sender, EventArgs args) + { + Destroy (); + } + + void OnExpanded (object sender, EventArgs args) + { + GLib.Timeout.Add (100, new GLib.TimeoutHandler (UpdateSize)); + } + + bool UpdateSize () + { + int w, h; + GetSize (out w, out h); + Resize (w, 1); + return false; + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HeaderBox.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HeaderBox.cs new file mode 100644 index 00000000..28fb615f --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HeaderBox.cs @@ -0,0 +1,197 @@ +// +// HeaderBox.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + class HeaderBox: Bin + { + Gtk.Widget child; + int topMargin; + int bottomMargin; + int leftMargin; + int rightMargin; + + int topPadding; + int bottomPadding; + int leftPadding; + int rightPadding; + + bool useCustomColor; + Gdk.Color customColor; + + public HeaderBox () + { + } + + public HeaderBox (int topMargin, int bottomMargin, int leftMargin, int rightMargin) + { + SetMargins (topMargin, bottomMargin, leftMargin, rightMargin); + } + + public void Replace (Gtk.Bin parent) + { + Gtk.Widget c = parent.Child; + parent.Remove (c); + Add (c); + parent.Add (this); + } + + public void SetMargins (int topMargin, int bottomMargin, int leftMargin, int rightMargin) + { + this.topMargin = topMargin; + this.bottomMargin = bottomMargin; + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + } + + public void SetPadding (int topPadding, int bottomPadding, int leftPadding, int rightPadding) + { + this.topPadding = topPadding; + this.bottomPadding = bottomPadding; + this.leftPadding = leftPadding; + this.rightPadding = rightPadding; + } + + public bool GradientBackround { get; set; } + + public Gdk.Color BackgroundColor { + get { return customColor; } + set { customColor = value; useCustomColor = true; } + } + + public void ResetBackgroundColor () + { + useCustomColor = false; + } + + protected override void OnAdded (Widget widget) + { + base.OnAdded (widget); + child = widget; + } + +// protected override void OnSizeRequested (ref Requisition requisition) +// { +// if (child != null) { +// requisition = child.SizeRequest (); +// requisition.Width += leftMargin + rightMargin + leftPadding + rightPadding; +// requisition.Height += topMargin + bottomMargin + topPadding + bottomPadding; +// } else { +// requisition.Width = 0; +// requisition.Height = 0; +// } +// } + + public new void GetPreferredWidth (out int width) + { + if (child != null) { + Requisition req = child.SizeRequest (); + req.Width += leftMargin + rightMargin + leftPadding + rightPadding; + width = req.Width; + } else { + width = 0; + } + } + + public new void GetPreferredHeight (out int height) + { + if (child != null) { + Requisition req = child.SizeRequest (); + req.Height += topMargin + bottomMargin + topPadding + bottomPadding; + height = req.Height; + } else { + height = 0; + } + } + + protected override void OnSizeAllocated (Gdk.Rectangle allocation) + { + base.OnSizeAllocated (allocation); + if (allocation.Width > leftMargin + rightMargin + leftPadding + rightPadding) { + allocation.X += leftMargin + leftPadding; + allocation.Width -= leftMargin + rightMargin + leftPadding + rightPadding; + } + if (allocation.Height > topMargin + bottomMargin + topPadding + bottomPadding) { + allocation.Y += topMargin + topPadding; + allocation.Height -= topMargin + bottomMargin + topPadding + bottomPadding; + } + if (child != null) + child.SizeAllocate (allocation); + } + + public new void Draw (Cairo.Context cr) + { + Gdk.Rectangle rect; + + if (GradientBackround) { + rect = new Gdk.Rectangle (Allocation.X, Allocation.Y, Allocation.Width, Allocation.Height); + HslColor gcol = useCustomColor ? customColor : Parent.Style.Background (Gtk.StateType.Normal); + + cr.NewPath (); + cr.MoveTo (rect.X, rect.Y); + cr.RelLineTo (rect.Width, 0); + cr.RelLineTo (0, rect.Height); + cr.RelLineTo (-rect.Width, 0); + cr.RelLineTo (0, -rect.Height); + cr.ClosePath (); + using (Cairo.Gradient pat = new Cairo.LinearGradient (rect.X, rect.Y, rect.X, rect.Y + rect.Height - 1)) { + Cairo.Color color1 = gcol; + pat.AddColorStop (0, color1); + gcol.L -= 0.1; + if (gcol.L < 0) + gcol.L = 0; + pat.AddColorStop (1, gcol); + cr.Pattern = pat; + cr.FillPreserve (); + } + + } + + base.Draw (cr); + //FIXME: Get this drawing properly again! +// Gdk.Color colour = Parent.Style.Dark (Gtk.StateType.Normal); +// cr.SetSourceRGB (colour.Red, colour.Green, colour.Blue); +// +// rect = Allocation; +// for (int n=0; n + ****************************************************************************/ + +/* THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +using System; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + class HoverImageButton : EventBox + { + private static Gdk.Cursor hand_cursor = new Gdk.Cursor(Gdk.CursorType.Hand1); + + private IconSize icon_size = IconSize.Menu; + private string [] icon_names = { "image-missing", Stock.MissingImage }; + private Gdk.Pixbuf normal_pixbuf; + private Gdk.Pixbuf active_pixbuf; + private Image image; + private bool is_hovering; + private bool is_pressed; + + private bool draw_focus = true; + + private event EventHandler clicked; + + public event EventHandler Clicked { + add { clicked += value; } + remove { clicked -= value; } + } + + public HoverImageButton() + { + CanFocus = true; + + image = new Image(); + image.Show(); + Add(image); + } + + public HoverImageButton(IconSize size, string icon_name) : this(size, new string [] { icon_name }) + { + } + + public HoverImageButton(IconSize size, string [] icon_names) : this() + { + this.icon_size = size; + this.icon_names = icon_names; + } + + public new void Activate() + { + EventHandler handler = clicked; + if(handler != null) { + handler(this, EventArgs.Empty); + } + } + + private bool changing_style = false; + protected override void OnStyleSet(Style previous_style) + { + if(changing_style) { + return; + } + + changing_style = true; + if (normal_pixbuf == null) + LoadPixbufs(); + changing_style = false; + } + + protected override bool OnEnterNotifyEvent(Gdk.EventCrossing evnt) + { + image.GdkWindow.Cursor = hand_cursor; + is_hovering = true; + UpdateImage(); + return base.OnEnterNotifyEvent(evnt); + } + + protected override bool OnLeaveNotifyEvent(Gdk.EventCrossing evnt) + { + is_hovering = false; + UpdateImage(); + return base.OnLeaveNotifyEvent(evnt); + } + + protected override bool OnFocusInEvent(Gdk.EventFocus evnt) + { + bool ret = base.OnFocusInEvent(evnt); + UpdateImage(); + return ret; + } + + protected override bool OnFocusOutEvent(Gdk.EventFocus evnt) + { + bool ret = base.OnFocusOutEvent(evnt); + UpdateImage(); + return ret; + } + + protected override bool OnButtonPressEvent(Gdk.EventButton evnt) + { + if(evnt.Button != 1) { + return base.OnButtonPressEvent(evnt); + } + + HasFocus = true; + is_pressed = true; + QueueDraw(); + + return base.OnButtonPressEvent(evnt); + } + + protected override bool OnButtonReleaseEvent(Gdk.EventButton evnt) + { + if(evnt.Button != 1) { + return base.OnButtonReleaseEvent(evnt); + } + + is_pressed = false; + QueueDraw(); + Activate(); + + return base.OnButtonReleaseEvent(evnt); + } + + public new void Draw (Cairo.Context cr) + { + base.Draw (cr); + + PropagateDraw (Child, cr); + + if(HasFocus && draw_focus) { + Style.PaintFocus(Style, cr, StateType.Normal, this, "button", + 0, 0, Allocation.Width, Allocation.Height); + } + } + + private void UpdateImage() + { + image.Pixbuf = is_hovering || is_pressed || HasFocus + ? active_pixbuf : normal_pixbuf; + } + + private void LoadPixbufs() + { + int width, height; + Icon.SizeLookup(icon_size, out width, out height); + IconTheme theme = IconTheme.GetForScreen(Screen); + + if(normal_pixbuf != null) { + normal_pixbuf.Dispose(); + normal_pixbuf = null; + } + + if(active_pixbuf != null) { + active_pixbuf.Dispose(); + active_pixbuf = null; + } + + for(int i = 0; i < icon_names.Length; i++) { + try { + normal_pixbuf = RenderIcon(icon_names[i], icon_size, null) + ?? theme.LoadIcon(icon_names[i], width, 0); + active_pixbuf = ColorShiftPixbuf(normal_pixbuf, 30); + break; + } catch { + } + } + + UpdateImage(); + } + + public Gdk.Pixbuf Pixbuf { + get { return this.normal_pixbuf; } + set { + this.normal_pixbuf = value; + active_pixbuf = ColorShiftPixbuf(normal_pixbuf, 30); + UpdateImage(); + } + } + + + private static byte PixelClamp(int val) + { + return (byte)System.Math.Max(0, System.Math.Min(255, val)); + } + + private unsafe Gdk.Pixbuf ColorShiftPixbuf(Gdk.Pixbuf src, byte shift) + { + Gdk.Pixbuf dest = new Gdk.Pixbuf(src.Colorspace, src.HasAlpha, src.BitsPerSample, src.Width, src.Height); + + byte *src_pixels_orig = (byte *)src.Pixels; + byte *dest_pixels_orig = (byte *)dest.Pixels; + + for(int i = 0; i < src.Height; i++) { + byte *src_pixels = src_pixels_orig + i * src.Rowstride; + byte *dest_pixels = dest_pixels_orig + i * dest.Rowstride; + + for(int j = 0; j < src.Width; j++) { + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + *(dest_pixels++) = PixelClamp(*(src_pixels++) + shift); + + if(src.HasAlpha) { + *(dest_pixels++) = *(src_pixels++); + } + } + } + + return dest; + } + + public string [] IconNames { + get { return icon_names; } + set { + icon_names = value; + LoadPixbufs(); + } + } + + public IconSize IconSize { + get { return icon_size; } + set { + icon_size = value; + LoadPixbufs(); + } + } + + public Image Image { + get { return image; } + } + + public bool DrawFocus { + get { return draw_focus; } + set { + draw_focus = value; + QueueDraw(); + } + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HslColor.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HslColor.cs new file mode 100644 index 00000000..0012d24e --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/HslColor.cs @@ -0,0 +1,164 @@ +// +// HslColor.cs +// +// Author: +// Mike Krüger +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Gdk; + +namespace Mono.Addins.GuiGtk3 +{ + struct HslColor + { + public double H { + get; + set; + } + + public double S { + get; + set; + } + + public double L { + get; + set; + } + + static Gdk.Color black = new Gdk.Color (0, 0, 0); + public static implicit operator Color (HslColor hsl) + { + if (hsl.L > 1) hsl.L = 1; + if (hsl.L < 0) hsl.L = 0; + if (hsl.H > 1) hsl.H = 1; + if (hsl.H < 0) hsl.H = 0; + if (hsl.S > 1) hsl.S = 1; + if (hsl.S < 0) hsl.S = 0; + + double r = 0, g = 0, b = 0; + + if (hsl.L == 0) + return black; + + if (hsl.S == 0) { + r = g = b = hsl.L; + } else { + double temp2 = hsl.L <= 0.5 ? hsl.L * (1.0 + hsl.S) : hsl.L + hsl.S -(hsl.L * hsl.S); + double temp1 = 2.0 * hsl.L - temp2; + + double[] t3 = new double[] { hsl.H + 1.0 / 3.0, hsl.H, hsl.H - 1.0 / 3.0}; + double[] clr= new double[] { 0, 0, 0}; + for (int i = 0; i < 3; i++) { + if (t3[i] < 0) + t3[i] += 1.0; + if (t3[i] > 1) + t3[i]-=1.0; + if (6.0 * t3[i] < 1.0) + clr[i] = temp1 + (temp2 - temp1) * t3[i] * 6.0; + else if (2.0 * t3[i] < 1.0) + clr[i] = temp2; + else if (3.0 * t3[i] < 2.0) + clr[i] = (temp1 + (temp2 - temp1) * ((2.0 / 3.0) - t3[i]) * 6.0); + else + clr[i] = temp1; + } + + r = clr[0]; + g = clr[1]; + b = clr[2]; + } + return new Color ((byte)(255 * r), + (byte)(255 * g), + (byte)(255 * b)); + } + + public static Cairo.Color ToCairoColor (Gdk.Color color) + { + return new Cairo.Color ((double)color.Red / ushort.MaxValue, + (double)color.Green / ushort.MaxValue, + (double)color.Blue / ushort.MaxValue); + } + + public static implicit operator Cairo.Color (HslColor hsl) + { + return ToCairoColor ((Gdk.Color)hsl); + } + + public static implicit operator HslColor (Color color) + { + return new HslColor (color); + } + + public HslColor (Color color) : this () + { + double r = color.Red / (double)ushort.MaxValue; + double g = color.Green / (double)ushort.MaxValue; + double b = color.Blue / (double)ushort.MaxValue; + + double v = System.Math.Max (r, g); + v = System.Math.Max (v, b); + + double m = System.Math.Min (r, g); + m = System.Math.Min (m, b); + + this.L = (m + v) / 2.0; + if (this.L <= 0.0) + return; + double vm = v - m; + this.S = vm; + + if (this.S > 0.0) { + this.S /= (this.L <= 0.5) ? (v + m) : (2.0 - v - m); + } else { + return; + } + + double r2 = (v - r) / vm; + double g2 = (v - g) / vm; + double b2 = (v - b) / vm; + + if (r == v) { + this.H = (g == m ? 5.0 + b2 : 1.0 - g2); + } else if (g == v) { + this.H = (b == m ? 1.0 + r2 : 3.0 - b2); + } else { + this.H = (r == m ? 3.0 + g2 : 5.0 - r2); + } + this.H /= 6.0; + } + + public static double Brightness (Gdk.Color c) + { + double r = c.Red / (double)ushort.MaxValue; + double g = c.Green / (double)ushort.MaxValue; + double b = c.Blue / (double)ushort.MaxValue; + return System.Math.Sqrt (r * .241 + g * .691 + b * .068); + } + + public override string ToString () + { + return string.Format ("[HslColor: H={0}, S={1}, L={2}]", H, S, L); + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallDialog.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallDialog.cs new file mode 100644 index 00000000..da7c556a --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallDialog.cs @@ -0,0 +1,296 @@ +// +// InstallDialog.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using System.Text; +using Mono.Unix; +using System.Threading; +using System.Linq; +using System.Collections.Generic; +using UI = Gtk.Builder.ObjectAttribute; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + internal class InstallDialog : Gtk.Dialog + { + //From UI File + [UI] Box boxProgress; + [UI] Button buttonOk; + [UI] Label labelInfo; + [UI] ScrolledWindow scrolledwindow1; + [UI] HSeparator insSeparator; + [UI] Label globalProgressLabel; + [UI] ProgressBar mainProgressBar; + [UI] Button buttonCancel; + + string[] filesToInstall; + AddinRepositoryEntry[] addinsToInstall; + PackageCollection packagesToInstall; + SetupService service; + Gtk.ResponseType response = Gtk.ResponseType.None; + IEnumerable uninstallIds; + InstallMonitor installMonitor; + bool installing; + const int MaxHeight = 350; + + public InstallDialog (SetupService service, Builder builder, IntPtr handle): base (handle) + { + builder.Autoconnect (this); + this.service = service; +// TransientFor = parent; + WindowPosition = Gtk.WindowPosition.CenterOnParent; +// Services.PlaceDialog (this, parent); + boxProgress.Visible = false; + Resizable = false; + + //Wire Buttons + buttonOk.Clicked += OnButtonOkClicked; + buttonCancel.Clicked += OnButtonCancelClicked; + + ShowAll (); + } + + public void InitForInstall (AddinRepositoryEntry[] addinsToInstall) + { + this.addinsToInstall = addinsToInstall; + FillSummaryPage (); +// Services.PlaceDialog (this, TransientFor); + } + + public void InitForInstall (string[] filesToInstall) + { + this.filesToInstall = filesToInstall; + FillSummaryPage (); +// Services.PlaceDialog (this, TransientFor); + } + + public void InitForUninstall (Addin[] info) + { + this.uninstallIds = info.Select (a => a.Id); + buttonOk.Label = Catalog.GetString ("Uninstall"); + + HashSet sinfos = new HashSet (); + + StringBuilder sb = new StringBuilder (); + sb.Append ("").Append (Catalog.GetString ("The following packages will be uninstalled:")).Append ("\n\n"); + foreach (var a in info) { + sb.Append (a.Name + "\n\n"); + sinfos.UnionWith (service.GetDependentAddins (a.Id, true)); + } + + if (sinfos.Count > 0) { + sb.Append ("").Append (Catalog.GetString ("There are other extension packages that depend on the previous ones which will also be uninstalled:")).Append ("\n\n"); + foreach (Addin si in sinfos) + sb.Append (si.Description.Name + "\n"); + } + + ShowMessage (sb.ToString ()); +// Services.PlaceDialog (this, TransientFor); + } + + void FillSummaryPage () + { + PackageCollection packs = new PackageCollection (); + + if (filesToInstall != null) { + foreach (string file in filesToInstall) { + packs.Add (Package.FromFile (file)); + } + } + else { + foreach (AddinRepositoryEntry arep in addinsToInstall) { + packs.Add (Package.FromRepository (arep)); + } + } + + packagesToInstall = new PackageCollection (packs); + + PackageCollection toUninstall; + DependencyCollection unresolved; + bool res; + + InstallMonitor m = new InstallMonitor (); + res = service.ResolveDependencies (m, packs, out toUninstall, out unresolved); + + StringBuilder sb = new StringBuilder (); + if (!res) { + sb.Append ("").Append (Catalog.GetString ("The selected extension packages can't be installed because there are dependency conflicts.")).Append ("\n"); + foreach (string s in m.Errors) { + sb.Append ("" + s + "\n"); + } + sb.Append ("\n"); + } + + if (m.Warnings.Count != 0) { + foreach (string w in m.Warnings) { + sb.Append ("" + w + "\n"); + } + sb.Append ("\n"); + } + + sb.Append ("").Append (Catalog.GetString ("The following packages will be installed:")).Append ("\n\n"); + foreach (Package p in packs) { + sb.Append (p.Name); + if (!p.SharedInstall) + sb.Append (Catalog.GetString (" (in user directory)")); + sb.Append ("\n"); + } + sb.Append ("\n"); + + if (toUninstall.Count > 0) { + sb.Append ("").Append (Catalog.GetString ("The following packages need to be uninstalled:")).Append ("\n\n"); + foreach (Package p in toUninstall) { + sb.Append (p.Name + "\n"); + } + sb.Append ("\n"); + } + + if (unresolved.Count > 0) { + sb.Append ("").Append (Catalog.GetString ("The following dependencies could not be resolved:")).Append ("\n\n"); + foreach (Dependency p in unresolved) { + sb.Append (p.Name + "\n"); + } + sb.Append ("\n"); + } + buttonOk.Sensitive = res; + ShowMessage (sb.ToString ()); + } + + void ShowMessage (string txt) + { + labelInfo.Markup = txt.TrimEnd ('\n','\t',' '); + if (labelInfo.SizeRequest ().Height > MaxHeight) { + scrolledwindow1.VscrollbarPolicy = Gtk.PolicyType.Automatic; + scrolledwindow1.HeightRequest = MaxHeight; + } + else { + scrolledwindow1.HeightRequest = labelInfo.SizeRequest ().Height; + } + } + + protected virtual void OnButtonOkClicked (object sender, System.EventArgs e) + { + if (response != Gtk.ResponseType.None) { + Respond (response); + return; + } + Install (); + } + + protected virtual void OnButtonCancelClicked (object sender, System.EventArgs e) + { + if (installing) { + if (Services.AskQuestion (Catalog.GetString ("Are you sure you want to cancel the installation?"))) + installMonitor.Cancel (); + } else + Respond (Gtk.ResponseType.Cancel); + } + + void Install () + { + insSeparator.Visible = true; + boxProgress.Visible = true; + buttonOk.Sensitive = false; + + string txt; + string errmessage; + string warnmessage; + + ThreadStart oper; + + if (uninstallIds == null) { + installMonitor = new InstallMonitor (globalProgressLabel, mainProgressBar, Catalog.GetString ("Installing Extension Packages")); + oper = new ThreadStart (RunInstall); + errmessage = Catalog.GetString ("The installation failed!"); + warnmessage = Catalog.GetString ("The installation has completed with warnings."); + } else { + installMonitor = new InstallMonitor (globalProgressLabel, mainProgressBar, Catalog.GetString ("Uninstalling Extension Packages")); + oper = new ThreadStart (RunUninstall); + errmessage = Catalog.GetString ("The uninstallation failed!"); + warnmessage = Catalog.GetString ("The uninstallation has completed with warnings."); + } + + installing = true; + oper (); + installing = false; + + buttonCancel.Visible = false; + buttonOk.Label = Gtk.Stock.Close; + buttonOk.UseStock = true; + + if (installMonitor.Success && installMonitor.Warnings.Count == 0) { + Respond (Gtk.ResponseType.Ok); + return; + } else if (installMonitor.Success) { + txt = "" + warnmessage + "\n\n"; + foreach (string s in installMonitor.Warnings) + txt += GLib.Markup.EscapeText (s) + "\n"; + response = Gtk.ResponseType.Ok; + buttonOk.Sensitive = true; + } else { + buttonCancel.Label = Gtk.Stock.Close; + buttonCancel.UseStock = true; + txt = "" + errmessage + "\n\n"; + foreach (string s in installMonitor.Errors) + txt += GLib.Markup.EscapeText (s) + "\n"; + response = Gtk.ResponseType.Cancel; + buttonOk.Sensitive = true; + } + + ShowMessage (txt); + } + + void RunInstall () + { + try { + if (filesToInstall != null) + service.Install (installMonitor, filesToInstall); + else + service.Install (installMonitor, packagesToInstall); + } catch (Exception ex) { + installMonitor.Errors.Add (ex.Message); + } finally { + installMonitor.Dispose (); + } + } + + void RunUninstall () + { + try { + service.Uninstall (installMonitor, uninstallIds); + } catch (Exception ex) { + installMonitor.Errors.Add (ex.Message); + } finally { + installMonitor.Dispose (); + } + } + } +} + diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallMonitor.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallMonitor.cs new file mode 100644 index 00000000..e5deaa7f --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/InstallMonitor.cs @@ -0,0 +1,133 @@ +// +// AddinInstallDialog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Text; +using System.Threading; +using System.Collections; +using System.Collections.Specialized; +using System.Diagnostics; +using Mono.Unix; +using Gtk; +using Mono.Addins.Setup; +using Mono.Addins.Description; +namespace Mono.Addins.GuiGtk3 +{ + class InstallMonitor: IProgressStatus, IDisposable + { + Label progressLabel; + ProgressBar progressBar; + StringCollection errors = new StringCollection (); + StringCollection warnings = new StringCollection (); + bool canceled; + bool done; + string mainOperation; + + public InstallMonitor (Label progressLabel, ProgressBar progressBar, string mainOperation) + { + this.progressLabel = progressLabel; + this.progressBar = progressBar; + this.mainOperation = mainOperation; + } + + public InstallMonitor () + { + } + + public void SetMessage (string msg) + { + if (progressLabel != null) + progressLabel.Markup = "" + GLib.Markup.EscapeText (mainOperation) + "\n" + GLib.Markup.EscapeText (msg); + RunPendingEvents (); + } + + public void SetProgress (double progress) + { + if (progressBar != null) + progressBar.Fraction = progress; + RunPendingEvents (); + } + + public void Log (string msg) + { + Console.WriteLine (msg); + } + + public void ReportWarning (string message) + { + warnings.Add (message); + } + + public void ReportError (string message, Exception exception) + { + errors.Add (message); + } + + public bool IsCanceled { + get { return canceled; } + } + + public StringCollection Errors { + get { return errors; } + } + + public StringCollection Warnings { + get { return warnings; } + } + + public void Cancel () + { + canceled = true; + } + + public int LogLevel { + get { return 1; } + } + + public void Dispose () + { + done = true; + } + + public void WaitForCompleted () + { + while (!done) { + RunPendingEvents (); + Thread.Sleep (50); + } + } + + public bool Success { + get { return errors.Count == 0; } + } + + void RunPendingEvents () + { + while (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ManageSitesDialog.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ManageSitesDialog.cs new file mode 100644 index 00000000..62d4c75c --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ManageSitesDialog.cs @@ -0,0 +1,190 @@ +// +// ManageSitesDialog.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; +using Mono.Unix; +using System.Threading; + +using Mono.Addins.Setup; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + + +namespace Mono.Addins.GuiGtk3 +{ + class ManageSitesDialog : Dialog + { + //From UI File + [UI] TreeView repoTree; + [UI] Button btnRemove; + [UI] Button btnAdd; + + ListStore treeStore; + SetupService service; + + public ManageSitesDialog (SetupService service, Builder builder, IntPtr handle): base (handle) + { + builder.Autoconnect (this); +// TransientFor = parent; +// Services.PlaceDialog (this, parent); + this.service = service; + treeStore = new Gtk.ListStore (typeof (string), typeof (string), typeof(bool)); + repoTree.Model = treeStore; + repoTree.HeadersVisible = false; + var crt = new Gtk.CellRendererToggle (); + crt.Toggled += HandleRepoToggled; + repoTree.AppendColumn ("", crt, "active", 2); + repoTree.AppendColumn ("", new Gtk.CellRendererText (), "markup", 1); + repoTree.Selection.Changed += new EventHandler(OnSelect); + + AddinRepository[] reps = service.Repositories.GetRepositories (); + foreach (AddinRepository rep in reps) + AppendRepository (rep); + + btnRemove.Sensitive = false; + + //Wire buttons + btnRemove.Clicked += OnRemove; + btnAdd.Clicked += OnAdd; + + ShowAll (); + } + +// public override void Dispose () +// { +// base.Dispose (); +// Destroy (); +// } + + void AppendRepository (AddinRepository rep) + { + string txt = GLib.Markup.EscapeText (rep.Title) + "\n" + GLib.Markup.EscapeText (rep.Url) + ""; + treeStore.AppendValues (rep.Url, txt, rep.Enabled); + } + + protected void OnAdd (object sender, EventArgs e) + { + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.NewSiteDialog.ui", null); + NewSiteDialog dlg = new NewSiteDialog (builder, builder.GetObject ("NewSiteDialog").Handle); + try { + if (dlg.Run ()) { + string url = dlg.Url; + if (!url.StartsWith ("http://") && !url.StartsWith ("https://") && !url.StartsWith ("file://")) { + url = "http://" + url; + } + + try { + new Uri (url); + } catch { + Services.ShowError (null, "Invalid url: " + url, null, true); + } + + if (!service.Repositories.ContainsRepository (url)) { + builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.ProgressDialog.ui", null); + ProgressDialog pdlg = new ProgressDialog (builder, builder.GetObject ("ProgressDialog").Handle); + pdlg.Show (); + pdlg.SetMessage (AddinManager.CurrentLocalizer.GetString ("Registering repository")); + + bool done = false; + AddinRepository rr = null; + Exception error = null; + + ThreadPool.QueueUserWorkItem (delegate { + try { + rr = service.Repositories.RegisterRepository (pdlg, url, true); + } catch (System.Exception ex) { + error = ex; + } finally { + done = true; + } + }); + + while (!done) { + if (Gtk.Application.EventsPending ()) + Gtk.Application.RunIteration (); + else + Thread.Sleep (100); + } + + pdlg.Destroy (); + + if (pdlg.HadError) { + if (rr != null) + service.Repositories.RemoveRepository (rr.Url); + return; + } + + if (error != null) { + Services.ShowError (error, "The repository could not be registered", null, true); + return; + } + + AppendRepository (rr); + } + } + } finally { + dlg.Destroy (); + } + } + + protected void OnRemove (object sender, EventArgs e) + { + Gtk.ITreeModel foo; + Gtk.TreeIter iter; + if (!repoTree.Selection.GetSelected (out foo, out iter)) + return; + + string rep = (string) treeStore.GetValue (iter, 0); + service.Repositories.RemoveRepository (rep); + + treeStore.Remove (ref iter); + } + + void HandleRepoToggled (object o, ToggledArgs args) + { + Gtk.TreeIter iter; + if (!treeStore.GetIterFromString (out iter, args.Path)) + return; + + bool newVal = !(bool) treeStore.GetValue (iter, 2); + string rep = (string) treeStore.GetValue (iter, 0); + service.Repositories.SetRepositoryEnabled (rep, newVal); + + treeStore.SetValue (iter, 2, newVal); + } + + protected void OnSelect(object sender, EventArgs e) + { + btnRemove.Sensitive = repoTree.Selection.CountSelectedRows() > 0; + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/NewSiteDialog.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/NewSiteDialog.cs new file mode 100644 index 00000000..db65ec89 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/NewSiteDialog.cs @@ -0,0 +1,135 @@ +// +// NewSiteDialog.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + class NewSiteDialog : Dialog + { + //From UI File + [UI] Entry pathEntry; + [UI] Entry urlText; + [UI] RadioButton btnOnlineRep; + [UI] RadioButton btnLocalRep; + [UI] Button buttonBrowse; + [UI] Button btnOk; + + public NewSiteDialog (Builder builder, IntPtr handle): base (handle) + { + builder.Autoconnect (this); +// TransientFor = parent; +// Services.PlaceDialog (this, parent); + pathEntry.Sensitive = false; + CheckValues (); + + //Wire buttons + buttonBrowse.Clicked += OnButtonBrowseClicked; + pathEntry.Changed += OnPathEntryChanged; + urlText.Changed += OnUrlTextChanged; + + ShowAll (); + } + +// public override void Dispose () +// { +// base.Dispose (); +// Destroy (); +// } + + public string Url { + get { + if (btnOnlineRep.Active) + return urlText.Text; + else if (pathEntry.Text.Length > 0) + return "file://" + pathEntry.Text; + else + return string.Empty; + } + } + + void CheckValues () + { + btnOk.Sensitive = (Url != ""); + } + + public new bool Run () + { + ShowAll (); + return ((ResponseType) base.Run ()) == ResponseType.Ok; + } + + protected void OnClose (object sender, EventArgs args) + { + Destroy (); + } + + protected void OnOptionClicked (object sender, EventArgs e) + { + if (btnOnlineRep.Active) { + urlText.Sensitive = true; + pathEntry.Sensitive = false; + } else { + urlText.Sensitive = false; + pathEntry.Sensitive = true; + } + CheckValues (); + } + + protected virtual void OnButtonBrowseClicked(object sender, System.EventArgs e) + { + FileChooserDialog dlg = new FileChooserDialog ("Select Folder", this, FileChooserAction.SelectFolder); + try { + dlg.AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel); + dlg.AddButton (Gtk.Stock.Open, Gtk.ResponseType.Ok); + + dlg.SetFilename (Environment.GetFolderPath (Environment.SpecialFolder.Personal)); + if (dlg.Run () == (int) ResponseType.Ok) { + pathEntry.Text = dlg.Filename; + } + } finally { + dlg.Destroy (); + } + } + + protected virtual void OnPathEntryChanged(object sender, System.EventArgs e) + { + CheckValues (); + } + + protected virtual void OnUrlTextChanged (object sender, System.EventArgs e) + { + CheckValues (); + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ProgressDialog.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ProgressDialog.cs new file mode 100644 index 00000000..295d3f10 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/ProgressDialog.cs @@ -0,0 +1,123 @@ +// ProgressDialog.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using Gtk; +using UI = Gtk.Builder.ObjectAttribute; + +namespace Mono.Addins.GuiGtk3 +{ + internal class ProgressDialog : Gtk.Dialog, IProgressStatus + { + //From UI file + [UI] Label labelMessage; + [UI] ProgressBar progressbar; + [UI] TextView textview; + [UI] Button buttonCancel; + + bool cancelled; + bool hadError; + + public ProgressDialog (Builder builder, IntPtr handle): base (handle) + { + builder.Autoconnect (this); +// Services.PlaceDialog (this, parent); + ShowAll (); + } + + public bool IsCanceled { + get { + return cancelled; + } + } + + public int LogLevel { + get { + return 1; + } + } + + public bool HadError { + get { + return hadError; + } + } + + public void SetMessage (string msg) + { + Gtk.Application.Invoke (delegate { + labelMessage.Text = msg; + }); + } + + public void SetProgress (double progress) + { + Gtk.Application.Invoke (delegate { + progressbar.Fraction = progress; + }); + } + + public void Log (string msg) + { + Gtk.Application.Invoke (delegate { + Gtk.TextIter it = textview.Buffer.EndIter; + textview.Buffer.Insert (ref it, msg + "\n"); + }); + } + + public void ReportWarning (string message) + { + Log ("WARNING: " + message); + } + + public void ReportError (string message, Exception exception) + { + Log ("Error: " + message); + if (exception != null) + Log (exception.ToString ()); + Gtk.Application.Invoke (delegate { + Services.ShowError (exception, message, null, true); + }); + hadError = true; + } + + public void Cancel () + { + Gtk.Application.Invoke (delegate { + cancelled = true; + buttonCancel.Sensitive = false; + }); + } + + protected virtual void OnButtonCancelClicked (object sender, System.EventArgs e) + { + Cancel (); + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/SearchEntry.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/SearchEntry.cs new file mode 100644 index 00000000..f3a667d0 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/SearchEntry.cs @@ -0,0 +1,130 @@ +// +// SearchEntry.cs +// +// Author: +// Aaron Bockover +// Gabriel Burt +// +// Copyright 2007-2010 Novell, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Gtk; + +namespace Mono.Addins.GuiGtk3 +{ + [System.ComponentModel.ToolboxItem(true)] + class SearchEntry : EventBox + { + HBox box = new HBox (); + Gtk.Entry entry = new Gtk.Entry (); + HoverImageButton iconFind; + HoverImageButton iconClean; + const int notifyDelay = 50; + bool notifying; + + public SearchEntry () + { + entry.HasFrame = false; + box.PackStart (entry, true, true, 0); + iconFind = new HoverImageButton (IconSize.Menu, Gtk.Stock.Find); + box.PackStart (iconFind, false, false, 0); + iconClean = new HoverImageButton (IconSize.Menu, Gtk.Stock.Clear); + box.PackStart (iconClean, false, false, 0); + box.BorderWidth = 1; + + HeaderBox hbox = new HeaderBox (1,1,1,1); + hbox.Show (); + hbox.Add (box); + Add (hbox); + + ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + iconClean.ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + iconFind.ModifyBg (StateType.Normal, entry.Style.Base (StateType.Normal)); + + iconClean.BorderWidth = 1; + iconClean.CanFocus = false; + iconFind.BorderWidth = 1; + iconFind.CanFocus = false; + + iconClean.Clicked += delegate { + entry.Text = string.Empty; + }; + + iconFind.Clicked += delegate { + FireSearch (); + }; + + entry.Activated += delegate { + FireSearch (); + }; + + ShowAll (); + UpdateIcon (); + + entry.Changed += delegate { + UpdateIcon (); + FireSearch (); + }; + } + + public event EventHandler TextChanged; + + public Gtk.Entry Entry { + get { + return this.entry; + } + set { + entry = value; + } + } + + public string Text { + get { return entry.Text; } + } + + void UpdateIcon () + { + if (entry.Text.Length > 0) { + iconFind.Hide (); + iconClean.Show (); + } + else { + iconFind.Show (); + iconClean.Hide (); + } + } + + void FireSearch () + { + if (!notifying) { + notifying = true; + GLib.Timeout.Add (notifyDelay, delegate { + notifying = false; + if (TextChanged != null) + TextChanged (this, EventArgs.Empty); + return false; + }); + } + } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/Services.cs b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/Services.cs new file mode 100644 index 00000000..74c17db7 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.Gui/Services.cs @@ -0,0 +1,155 @@ +// +// Services.cs +// +// Author: +// Lluis Sanchez Gual +// Robert Nordan (Ported to GTK#3) +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// Copyright (c) 2013 Robert Nordan +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Gtk; +using Mono.Unix; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using System.Linq; +using System.Collections.Generic; + +namespace Mono.Addins.GuiGtk3 +{ + internal class Services + { + public static bool InApplicationNamespace (SetupService service, string id) + { + return service.ApplicationNamespace == null || id.StartsWith (service.ApplicationNamespace + "."); + } + + public static bool AskQuestion (string question) + { + MessageDialog md = new MessageDialog (null, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.YesNo, question); + try { + int response = md.Run (); + return ((ResponseType) response == ResponseType.Yes); + } finally { + md.Destroy (); + } + } + + public static void ShowError (Exception ex, string message, Window parent, bool modal) + { + Gtk.Builder builder = new Gtk.Builder (null, "Mono.Addins.GuiGtk3.interfaces.ErrorDialog.ui", null); + ErrorDialog dlg = new ErrorDialog (builder, builder.GetObject ("ErrorDialog").Handle); + + if (message == null) { + if (ex != null) + dlg.Message = string.Format (Catalog.GetString ("Exception occurred: {0}"), ex.Message); + else { + dlg.Message = "An unknown error occurred"; + dlg.AddDetails (Environment.StackTrace, false); + } + } else + dlg.Message = message; + + if (ex != null) { + dlg.AddDetails (string.Format (Catalog.GetString ("Exception occurred: {0}"), ex.Message) + "\n\n", true); + dlg.AddDetails (ex.ToString (), false); + } + + if (modal) { + dlg.Run (); + dlg.Destroy (); + } else + dlg.Show (); + } + + public struct MissingDepInfo + { + public string Addin; + public string Required; + public string Found; + } + + public static IEnumerable GetMissingDependencies (Addin addin) + { + IEnumerable allAddins = AddinManager.Registry.GetAddins ().Union (AddinManager.Registry.GetAddinRoots ()); + foreach (var dep in addin.Description.MainModule.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep != null) { + if (!allAddins.Any (a => Addin.GetIdName (a.Id) == Addin.GetIdName (adep.FullAddinId) && a.SupportsVersion (adep.Version))) { + Addin found = allAddins.FirstOrDefault (a => Addin.GetIdName (a.Id) == Addin.GetIdName (adep.FullAddinId)); + yield return new MissingDepInfo () { Addin = Addin.GetIdName (adep.FullAddinId), Required = adep.Version, Found = found != null ? found.Version : null }; + } + } + } + } + + public static Gdk.Pixbuf AddIconOverlay (Gdk.Pixbuf target, Gdk.Pixbuf overlay) + { + Gdk.Pixbuf res = new Gdk.Pixbuf (target.Colorspace, target.HasAlpha, target.BitsPerSample, target.Width, target.Height); + res.Fill (0); + target.CopyArea (0, 0, target.Width, target.Height, res, 0, 0); + overlay.Composite (res, 0, 0, overlay.Width, overlay.Height, 0, 0, 1, 1, Gdk.InterpType.Bilinear, 255); + return res; + } + + public static Gdk.Pixbuf DesaturateIcon (Gdk.Pixbuf source) + { + Gdk.Pixbuf dest = new Gdk.Pixbuf (source.Colorspace, source.HasAlpha, source.BitsPerSample, source.Width, source.Height); + dest.Fill (0); + source.SaturateAndPixelate (dest, 0, false); + return dest; + } + + public static Gdk.Pixbuf FadeIcon (Gdk.Pixbuf source) + { + Gdk.Pixbuf result = source.Copy (); + result.Fill (0); + result = result.AddAlpha (true, 0, 0, 0); + source.Composite (result, 0, 0, source.Width, source.Height, 0, 0, 1, 1, Gdk.InterpType.Bilinear, 128); + return result; + } + +// /// +// /// Positions a dialog relative to its parent on platforms where default placement is known to be poor. +// /// +// public static void PlaceDialog (Window child, Window parent) +// { +// CenterWindow (child, parent); +// } +// +// /// Centers a window relative to its parent. +// static void CenterWindow (Window child, Window parent) +// { +// child.Child.Show (); +// int w, h, winw, winh, x, y, winx, winy; +// child.GetSize (out w, out h); +// parent.GetSize (out winw, out winh); +// parent.GetPosition (out winx, out winy); +// x = System.Math.Max (0, (winw - w) /2) + winx; +// y = System.Math.Max (0, (winh - h) /2) + winy; +// child.Move (x, y); +// } + } +} diff --git a/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.GuiGtk3.csproj b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.GuiGtk3.csproj new file mode 100644 index 00000000..8b3041d9 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/Mono.Addins.GuiGtk3.csproj @@ -0,0 +1,398 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {410A7DC9-E7DA-43E6-B592-93E2A344B660} + Library + Mono.Addins.GuiGtk3 + Mono.Addins.GuiGtk3 + True + ..\mono-addins.snk + v4.6 + + + + True + full + false + ..\bin + prompt + 4 + True + False + True + false + + + pdbonly + True + ..\bin + prompt + 4 + True + False + True + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + download-16.png + + + download-16@2x.png + + + download-16~dark.png + + + download-16~dark@2x.png + + + installed-overlay-16.png + + + installed-overlay-16@2x.png + + + installed-overlay-16~dark.png + + + installed-overlay-16~dark@2x.png + + + installed-overlay-16~sel.png + + + installed-overlay-16~sel@2x.png + + + installed-overlay-16~dark~sel.png + + + installed-overlay-16~dark~sel@2x.png + + + package-x-generic.png + + + package-x-generic_16.png + + + package-x-generic_22.png + + + plugin-16.png + + + plugin-16@2x.png + + + plugin-16~dark.png + + + plugin-16~dark@2x.png + + + plugin-16~sel.png + + + plugin-16~sel@2x.png + + + plugin-16~dark~sel.png + + + plugin-16~dark~sel@2x.png + + + plugin-22.png + + + plugin-22@2x.png + + + plugin-22~dark.png + + + plugin-22~dark@2x.png + + + plugin-22~sel.png + + + plugin-22~sel@2x.png + + + plugin-22~dark~sel.png + + + plugin-22~dark~sel@2x.png + + + plugin-32.png + + + plugin-32@2x.png + + + plugin-32~dark.png + + + plugin-32~dark@2x.png + + + plugin-32~sel.png + + + plugin-32~sel@2x.png + + + plugin-32~dark~sel.png + + + plugin-32~dark~sel@2x.png + + + plugin-avail-16.png + + + plugin-avail-16@2x.png + + + plugin-avail-16~dark.png + + + plugin-avail-16~dark@2x.png + + + plugin-avail-16~sel.png + + + plugin-avail-16~sel@2x.png + + + plugin-avail-16~dark~sel.png + + + plugin-avail-16~dark~sel@2x.png + + + plugin-avail-32.png + + + plugin-disabled-32.png + + + plugin-disabled-32@2x.png + + + plugin-disabled-32~dark.png + + + plugin-disabled-32~dark@2x.png + + + plugin-disabled-32~sel@2x.png + + + plugin-disabled-32~sel.png + + + plugin-disabled-32~dark~sel@2x.png + + + plugin-disabled-32~dark~sel.png + + + plugin-update-16.png + + + plugin-update-16@2x.png + + + plugin-update-16~dark.png + + + plugin-update-16~dark@2x.png + + + plugin-update-16~sel.png + + + plugin-update-16~sel@2x.png + + + plugin-update-16~dark~sel.png + + + plugin-update-16~dark~sel@2x.png + + + plugin-update-22.png + + + plugin-update-22@2x.png + + + plugin-update-22~dark.png + + + plugin-update-22~dark@2x.png + + + plugin-update-22~sel.png + + + plugin-update-22~sel@2x.png + + + plugin-update-22~dark~sel.png + + + plugin-update-22~dark~sel@2x.png + + + plugin-update-32.png + + + plugin-update-32@2x.png + + + plugin-update-32~dark.png + + + plugin-update-32~dark@2x.png + + + plugin-update-32~sel.png + + + plugin-update-32~sel@2x.png + + + plugin-update-32~dark~sel.png + + + plugin-update-32~dark~sel@2x.png + + + update-16.png + + + update-16@2x.png + + + update-16~dark.png + + + update-16~dark@2x.png + + + update-available-overlay-16.png + + + update-available-overlay-16@2x.png + + + update-available-overlay-16~dark.png + + + update-available-overlay-16~dark@2x.png + + + update-available-overlay-16~sel.png + + + update-available-overlay-16~sel@2x.png + + + update-available-overlay-16~dark~sel.png + + + update-available-overlay-16~dark~sel@2x.png + + + icons.web-search-16.png + + + icons.web-search-16@2x.png + + + icons.web-search-16~dark.png + + + icons.web-search-16~dark@2x.png + + + Mono.Addins.GuiGtk3.interfaces.AddinManagerDialog.ui + + + Mono.Addins.GuiGtk3.interfaces.AddinInfoView.ui + + + Mono.Addins.GuiGtk3.interfaces.AddinInstallerDialog.ui + + + Mono.Addins.GuiGtk3.interfaces.ManageSitesDialog.ui + + + Mono.Addins.GuiGtk3.interfaces.InstallDialog.ui + + + Mono.Addins.GuiGtk3.interfaces.ProgressDialog.ui + + + Mono.Addins.GuiGtk3.interfaces.NewSiteDialog.ui + + + Mono.Addins.GuiGtk3.interfaces.ErrorDialog.ui + + + + + + {A85C9721-C054-4BD8-A1F3-0227615F0A36} + Mono.Addins.Setup + + + {91DD5A2D-9FE3-4C3C-9253-876141874DAD} + Mono.Addins + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/download-16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/download-16.png new file mode 100644 index 00000000..0fcdd62c Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/download-16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/download-16@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/download-16@2x.png new file mode 100644 index 00000000..7cb21ad7 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/download-16@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/download-16~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/download-16~dark.png new file mode 100644 index 00000000..4547fc97 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/download-16~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/download-16~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/download-16~dark@2x.png new file mode 100644 index 00000000..e34e55ed Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/download-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16.png new file mode 100644 index 00000000..99516ff6 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16@2x.png new file mode 100644 index 00000000..06dec679 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark.png new file mode 100644 index 00000000..25ee0dc1 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark@2x.png new file mode 100644 index 00000000..2d6ff200 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark~sel.png new file mode 100644 index 00000000..c565a5bd Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark~sel@2x.png new file mode 100644 index 00000000..d959c8c4 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~sel.png new file mode 100644 index 00000000..c565a5bd Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~sel@2x.png new file mode 100644 index 00000000..d959c8c4 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/installed-overlay-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic.png b/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic.png new file mode 100644 index 00000000..9ea804ac Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic_16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic_16.png new file mode 100644 index 00000000..62383b25 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic_16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic_22.png b/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic_22.png new file mode 100644 index 00000000..fa1711f1 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/package-x-generic_22.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16.png new file mode 100644 index 00000000..b91883c6 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16@2x.png new file mode 100644 index 00000000..9f9c5280 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark.png new file mode 100644 index 00000000..ceff41f2 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark@2x.png new file mode 100644 index 00000000..a4c6e707 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark~sel.png new file mode 100644 index 00000000..d449be9d Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark~sel@2x.png new file mode 100644 index 00000000..7bdeee00 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~sel.png new file mode 100644 index 00000000..d449be9d Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~sel@2x.png new file mode 100644 index 00000000..7bdeee00 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22.png new file mode 100644 index 00000000..98a37fdf Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22@2x.png new file mode 100644 index 00000000..ccc24f77 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark.png new file mode 100644 index 00000000..21024fd6 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark@2x.png new file mode 100644 index 00000000..46a351e2 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark~sel.png new file mode 100644 index 00000000..0ac0e47b Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark~sel@2x.png new file mode 100644 index 00000000..45e5da32 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~sel.png new file mode 100644 index 00000000..0ac0e47b Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~sel@2x.png new file mode 100644 index 00000000..45e5da32 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-22~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32.png new file mode 100644 index 00000000..9f9c5280 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32@2x.png new file mode 100644 index 00000000..9db0330b Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark.png new file mode 100644 index 00000000..a4c6e707 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark@2x.png new file mode 100644 index 00000000..29934c06 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark~sel.png new file mode 100644 index 00000000..7bdeee00 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark~sel@2x.png new file mode 100644 index 00000000..cd52532a Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~sel.png new file mode 100644 index 00000000..7bdeee00 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~sel@2x.png new file mode 100644 index 00000000..cd52532a Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-32~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16.png new file mode 100644 index 00000000..a77427ff Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16@2x.png new file mode 100644 index 00000000..48c1eda1 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark.png new file mode 100644 index 00000000..8b143c52 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark@2x.png new file mode 100644 index 00000000..5e3d3371 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark~sel.png new file mode 100644 index 00000000..224066c4 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark~sel@2x.png new file mode 100644 index 00000000..f2f7f404 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~sel.png new file mode 100644 index 00000000..224066c4 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~sel@2x.png new file mode 100644 index 00000000..f2f7f404 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32.png new file mode 100644 index 00000000..48c1eda1 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32@2x.png new file mode 100644 index 00000000..dd962b8d Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark.png new file mode 100644 index 00000000..5e3d3371 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark@2x.png new file mode 100644 index 00000000..63e9ac76 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark~sel.png new file mode 100644 index 00000000..f2f7f404 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark~sel@2x.png new file mode 100644 index 00000000..34aaed63 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~sel.png new file mode 100644 index 00000000..f2f7f404 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~sel@2x.png new file mode 100644 index 00000000..34aaed63 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-avail-32~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32.png new file mode 100644 index 00000000..19e5bc49 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32@2x.png new file mode 100644 index 00000000..b2ec2a77 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark.png new file mode 100644 index 00000000..90b7212b Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark@2x.png new file mode 100644 index 00000000..a817cac9 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark~sel.png new file mode 100644 index 00000000..b5cc5ef6 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark~sel@2x.png new file mode 100644 index 00000000..603b09b3 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~sel.png new file mode 100644 index 00000000..b5cc5ef6 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~sel@2x.png new file mode 100644 index 00000000..603b09b3 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-disabled-32~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16.png new file mode 100644 index 00000000..19457650 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16@2x.png new file mode 100644 index 00000000..e9def363 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark.png new file mode 100644 index 00000000..e1b17e75 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark@2x.png new file mode 100644 index 00000000..fa4b0449 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark~sel.png new file mode 100644 index 00000000..5895d078 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark~sel@2x.png new file mode 100644 index 00000000..bbb9003f Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~sel.png new file mode 100644 index 00000000..5895d078 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~sel@2x.png new file mode 100644 index 00000000..bbb9003f Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22.png new file mode 100644 index 00000000..dfa61242 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22@2x.png new file mode 100644 index 00000000..be0273c1 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark.png new file mode 100644 index 00000000..7eaf157f Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark@2x.png new file mode 100644 index 00000000..2a5a7c1a Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark~sel.png new file mode 100644 index 00000000..e9cc9b30 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark~sel@2x.png new file mode 100644 index 00000000..ff60343e Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~sel.png new file mode 100644 index 00000000..e9cc9b30 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~sel@2x.png new file mode 100644 index 00000000..ff60343e Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-22~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32.png new file mode 100644 index 00000000..e9def363 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32@2x.png new file mode 100644 index 00000000..875d10c3 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark.png new file mode 100644 index 00000000..fa4b0449 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark@2x.png new file mode 100644 index 00000000..1a51e15e Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark~sel.png new file mode 100644 index 00000000..bbb9003f Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark~sel@2x.png new file mode 100644 index 00000000..14f79ecf Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~sel.png new file mode 100644 index 00000000..bbb9003f Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~sel@2x.png new file mode 100644 index 00000000..14f79ecf Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/plugin-update-32~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-16.png new file mode 100644 index 00000000..97df672b Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-16@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-16@2x.png new file mode 100644 index 00000000..c7cad401 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-16@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-16~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-16~dark.png new file mode 100644 index 00000000..efce32be Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-16~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-16~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-16~dark@2x.png new file mode 100644 index 00000000..90636c76 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16.png new file mode 100644 index 00000000..fe4899da Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16@2x.png new file mode 100644 index 00000000..97c0737f Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark.png new file mode 100644 index 00000000..e5080864 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark@2x.png new file mode 100644 index 00000000..c43d5443 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark~sel.png new file mode 100644 index 00000000..e01f5f33 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark~sel@2x.png new file mode 100644 index 00000000..de378c1e Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~dark~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~sel.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~sel.png new file mode 100644 index 00000000..e01f5f33 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~sel.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~sel@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~sel@2x.png new file mode 100644 index 00000000..de378c1e Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/update-available-overlay-16~sel@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16.png b/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16.png new file mode 100644 index 00000000..7ff800d4 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16@2x.png new file mode 100644 index 00000000..6fd81ad6 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16~dark.png b/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16~dark.png new file mode 100644 index 00000000..2ac4184d Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16~dark.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16~dark@2x.png b/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16~dark@2x.png new file mode 100644 index 00000000..7a0d0711 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/icons/web-search-16~dark@2x.png differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinInfoView.ui.xml b/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinInfoView.ui.xml new file mode 100644 index 00000000..d4c3d1fa --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinInfoView.ui.xml @@ -0,0 +1,294 @@ + + + + + + False + False + True + + + False + + + False + vertical + 320 + + + False + + + False + 6 + + + False + stock:gtk-dialog-warning Menu + 32 + + + False + False + 0 + + + + + 250 + False + 0 + label1 + True + 32 + + + False + False + 1 + + + + + + + False + False + 0 + + + + + False + 12 + vertical + 6 + + + False + 6 + + + False + 6 + + + False + vertical + 3 + + + 280 + False + 0 + <b><big>Some Addin</big></b> + True + True + + + False + False + 0 + + + + + 280 + False + 0 + Version 2.6 + True + + + False + False + 1 + + + + + False + False + 0 + + + + + + + + True + True + 0 + + + + + False + False + 0 + + + + + True + always + 250 + + + False + none + + + False + + + False + vertical + 6 + + + 250 + False + 0 + Long description of the add-in. Long description of the add-in. Long description of the add-in. Long description of the add-in. Long description of the add-in. Long description of the add-in. + True + + + False + False + 0 + + + + + + + + False + 6 + + + More information + True + False + True + + + False + False + 0 + + + + + + + + + + + False + False + 2 + + + + + + + + + + + False + True + 1 + + + + + False + True + 1 + + + + + False + + + False + 6 + + + Install... + True + False + True + + + True + False + 0 + + + + + Update + True + False + True + + + True + False + 1 + + + + + Disable + True + False + True + + + True + False + 2 + + + + + _Uninstall... + True + False + True + + + True + False + 3 + + + + + + + False + False + 2 + + + + + + + True + True + 0 + + + + diff --git a/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinInstallerDialog.ui.xml b/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinInstallerDialog.ui.xml new file mode 100644 index 00000000..4239fcb1 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinInstallerDialog.ui.xml @@ -0,0 +1,135 @@ + + + + + + False + 6 + Add-in Manager + normal + + + False + vertical + 6 + + + False + 6 + vertical + 6 + + + False + 0 + Additional extensions are required to perform this operation. + + + False + False + 0 + + + + + False + 0 + The following add-ins will be installed: + + + False + False + 1 + + + + + True + + + False + none + + + False + 0 + 0 + 6 + 6 + label3 + + + + + + + False + True + 2 + + + + + False + + + False + False + 3 + + + + + False + True + 0 + + + + + False + + + + gtk-cancel + True + True + False + True + + + False + False + 0 + + + + + gtk-ok + True + True + False + True + + + False + False + 1 + + + + + False + True + end + 0 + + + + + + buttonCancel + buttonOk + + + diff --git a/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinManagerDialog.ui.xml b/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinManagerDialog.ui.xml new file mode 100644 index 00000000..45aff3f2 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/interfaces/AddinManagerDialog.ui.xml @@ -0,0 +1,400 @@ + + + + + + False + 6 + Add-in Manager + 740 + 550 + normal + + + False + vertical + 3 + True + True + + + False + 6 + vertical + 6 + True + True + + + False + 12 + + + False + 12 + + + True + False + + + False + 9 + 9 + + + False + vertical + 6 + + + True + + + True + + + + + + + + True + True + 0 + + + + + True + True + 0 + + + + + False + + + True + True + + + False + False + 1 + + + + + + + + False + 9 + 9 + + + False + vertical + + + False + + + False + 6 + + + False + No updates found + + + False + False + 0 + + + + + Refresh + True + False + True + + + False + False + 1 + + + + + Update All + True + False + True + + + False + False + 2 + + + + + + + False + False + 0 + + + + + True + + + True + + + + + + + + True + True + 1 + + + + + True + True + 0 + + + + + False + + + + + + False + False + 1 + + + + + 1 + + + + + + False + 9 + 9 + + + False + vertical + 540 + + + False + + + False + 6 + + + False + Repository: + + + False + False + 0 + + + + + False + 48 + + + False + False + 1 + + + + + True + False + True + 32 + + + False + False + 2 + + + + + + + False + False + 0 + + + + + True + + + True + + + + + + + + True + True + 1 + + + + + True + True + 0 + + + + + False + + + + + + True + True + 1 + + + + + 2 + + + + + + True + True + 0 + + + + + True + True + 0 + + + + + True + True + 0 + + + + + True + True + 0 + + + + + False + + + + Install from file... + True + False + True + + + False + False + 0 + + + + + gtk-close + True + True + False + True + + + False + False + 1 + + + + + False + True + end + 0 + + + + + + buttonInstallFromFile + btnClose + + + diff --git a/mono-addins/Mono.Addins.GuiGtk3/interfaces/ErrorDialog.ui.xml b/mono-addins/Mono.Addins.GuiGtk3/interfaces/ErrorDialog.ui.xml new file mode 100644 index 00000000..b9f6f37d --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/interfaces/ErrorDialog.ui.xml @@ -0,0 +1,146 @@ + + + + + + False + Error + Dialog + 6 + 1 + False + + + vertical + + 6 + + + + 6 + 6 + + + vertical + + + + + stock:gtk-dialog-error Dialog + + + 0 + False + False + False + + + + + 0 + False + False + False + + + + + vertical + + 12 + + + + 540 + True + 0 + An exception has been thrown 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + True + True + + + 0 + False + False + False + + + + + + True + + + + True + In + + + + 250 + True + + 2 + 2 + 6 + 6 + + + + + + + + Details + + + label_item + + + + + 1 + False + + + + + 1 + False + + + + + 0 + False + + + + + + + + 10 + 5 + 1 + End + + + + True + True + True + StockItem + gtk-ok + -5 + gtk-ok + + + False + False + + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.GuiGtk3/interfaces/InstallDialog.ui.xml b/mono-addins/Mono.Addins.GuiGtk3/interfaces/InstallDialog.ui.xml new file mode 100644 index 00000000..2d59220d --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/interfaces/InstallDialog.ui.xml @@ -0,0 +1,158 @@ + + + + + + False + normal + 500 + 200 + + + False + vertical + + + False + 9 + vertical + 9 + 500 + + + True + always + always + + + False + none + + + False + vertical + 6 + + + 400 + False + 0 + 0 + label3 + True + + + False + False + 0 + + + + + + + + + False + True + 0 + + + + + False + + + False + False + 1 + + + + + False + vertical + 6 + + + False + 0 + + + False + False + 0 + + + + + False + + + False + False + 1 + + + + + False + False + 2 + + + + + True + True + 0 + + + + + False + + + + gtk-cancel + True + True + False + True + + + False + False + 0 + + + + + Install + True + True + False + True + + + False + False + 1 + + + + + False + True + end + 0 + + + + + + buttonCancel + buttonOk + + + diff --git a/mono-addins/Mono.Addins.GuiGtk3/interfaces/ManageSitesDialog.ui.xml b/mono-addins/Mono.Addins.GuiGtk3/interfaces/ManageSitesDialog.ui.xml new file mode 100644 index 00000000..161bf197 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/interfaces/ManageSitesDialog.ui.xml @@ -0,0 +1,123 @@ + + + + + + False + 6 + Add-in Repository Management + 600 + 300 + normal + + + False + vertical + 6 + + + False + 6 + 12 + + + True + + + True + False + + + + + + True + True + 0 + + + + + True + True + 0 + + + + + False + vertical + 6 + + + gtk-add + True + False + True + + + False + False + 0 + + + + + gtk-delete + True + False + True + + + False + False + 1 + + + + + False + False + 1 + + + + + True + True + 0 + + + + + False + + + + gtk-close + True + True + False + True + + + False + False + 0 + + + + + False + True + end + 0 + + + + + + closebutton2 + + + diff --git a/mono-addins/Mono.Addins.GuiGtk3/interfaces/NewSiteDialog.ui.xml b/mono-addins/Mono.Addins.GuiGtk3/interfaces/NewSiteDialog.ui.xml new file mode 100644 index 00000000..13fd280a --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/interfaces/NewSiteDialog.ui.xml @@ -0,0 +1,233 @@ + + + + + + False + 6 + Add New Repository + 550 + normal + + + False + vertical + 6 + + + False + 6 + vertical + 6 + + + False + 0 + Select the location of the repository you want to register: + + + False + False + 0 + + + + + Register an on-line repository + True + False + True + 0.5 + True + True + btnOnlineRep + + + False + False + 1 + + + + + False + 6 + + + 32 + False + + + False + False + 0 + + + + + False + Url: + + + False + False + 1 + + + + + True + + + + True + True + 2 + + + + + False + False + 2 + + + + + Register a local repository + True + False + True + 0.5 + True + btnOnlineRep + + + False + False + 3 + + + + + False + 6 + + + 32 + False + + + False + False + 0 + + + + + False + Path: + + + False + False + 1 + + + + + False + 6 + + + True + + + + True + True + 0 + + + + + Browse... + True + False + True + + + False + False + 1 + + + + + True + True + 2 + + + + + False + False + 4 + + + + + False + True + 0 + + + + + False + + + + gtk-cancel + True + True + False + True + + + False + False + 0 + + + + + gtk-ok + True + True + False + True + + + False + False + 1 + + + + + False + True + end + 0 + + + + + + cancelbutton1 + btnOk + + + diff --git a/mono-addins/Mono.Addins.GuiGtk3/interfaces/ProgressDialog.ui.xml b/mono-addins/Mono.Addins.GuiGtk3/interfaces/ProgressDialog.ui.xml new file mode 100644 index 00000000..217de511 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/interfaces/ProgressDialog.ui.xml @@ -0,0 +1,103 @@ + + + + + + False + Progress + True + normal + 300 + 200 + + + False + vertical + + + False + 9 + vertical + 6 + 300 + + + False + 0 + + + False + False + 0 + + + + + False + + + False + False + 1 + + + + + True + + + False + + + True + + + + + + + False + True + 2 + + + + + True + True + 0 + + + + + False + + + + gtk-cancel + True + True + False + True + + + False + False + 0 + + + + + False + True + end + 0 + + + + + + buttonCancel + + + diff --git a/mono-addins/Mono.Addins.GuiGtk3/obj/Debug/Mono.Addins.GuiGtk3.csproj.CoreCompileInputs.cache b/mono-addins/Mono.Addins.GuiGtk3/obj/Debug/Mono.Addins.GuiGtk3.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..01159fc5 --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/obj/Debug/Mono.Addins.GuiGtk3.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +53c6ffb4020d8e5c22ba18349cc5e3f9c7ee0e2e diff --git a/mono-addins/Mono.Addins.GuiGtk3/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/mono-addins/Mono.Addins.GuiGtk3/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 00000000..8f0eeba5 Binary files /dev/null and b/mono-addins/Mono.Addins.GuiGtk3/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/mono-addins/Mono.Addins.GuiGtk3/obj/Release/Mono.Addins.GuiGtk3.csproj.CoreCompileInputs.cache b/mono-addins/Mono.Addins.GuiGtk3/obj/Release/Mono.Addins.GuiGtk3.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..4074f8aa --- /dev/null +++ b/mono-addins/Mono.Addins.GuiGtk3/obj/Release/Mono.Addins.GuiGtk3.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +bd6c976c573a7281167a3c700d8dd40f9e8a47a6 diff --git a/mono-addins/Mono.Addins.GuiGtk3/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.GuiGtk3/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.GuiGtk3/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.GuiGtk3/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.GuiGtk3/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.GuiGtk3/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.MSBuild/AssemblyInfo.cs b/mono-addins/Mono.Addins.MSBuild/AssemblyInfo.cs new file mode 100644 index 00000000..0072da85 --- /dev/null +++ b/mono-addins/Mono.Addins.MSBuild/AssemblyInfo.cs @@ -0,0 +1,51 @@ +// +// AssemblyInfo.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("Mono.Addins")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.3.7.0")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/mono-addins/Mono.Addins.MSBuild/Mono.Addins.MSBuild.csproj b/mono-addins/Mono.Addins.MSBuild/Mono.Addins.MSBuild.csproj new file mode 100644 index 00000000..d6bee1b2 --- /dev/null +++ b/mono-addins/Mono.Addins.MSBuild/Mono.Addins.MSBuild.csproj @@ -0,0 +1,62 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3} + Library + Mono.Addins.MSBuild + Mono.Addins.MSBuild + True + ..\mono-addins.snk + v4.6 + + + + True + full + False + ..\bin + DEBUG + prompt + 4 + False + + + pdbonly + true + ..\bin + prompt + 4 + False + true + + + + + + + + + + + + + + + {A85C9721-C054-4BD8-A1F3-0227615F0A36} + Mono.Addins.Setup + False + + + {91DD5A2D-9FE3-4C3C-9253-876141874DAD} + Mono.Addins + False + + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.MSBuild/Mono.Addins.targets b/mono-addins/Mono.Addins.MSBuild/Mono.Addins.targets new file mode 100644 index 00000000..688d2c9f --- /dev/null +++ b/mono-addins/Mono.Addins.MSBuild/Mono.Addins.targets @@ -0,0 +1,17 @@ + + + + + + ResolveAddinReferences; + $(ResolveReferencesDependsOn) + + + + + + + + + + diff --git a/mono-addins/Mono.Addins.MSBuild/ResolveAddinReferences.cs b/mono-addins/Mono.Addins.MSBuild/ResolveAddinReferences.cs new file mode 100644 index 00000000..1a1658e4 --- /dev/null +++ b/mono-addins/Mono.Addins.MSBuild/ResolveAddinReferences.cs @@ -0,0 +1,94 @@ +// +// ResolveAddinReferences.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; +using Mono.Addins; +using Mono.Addins.Setup; +using System.IO; + +namespace Mono.Addins.MSBuild +{ + public class ResolveAddinReferences: Task + { + List references = new List (); + ITaskItem[] addinReferences; + string extensionDomain; + + public override bool Execute () + { + if (string.IsNullOrEmpty (extensionDomain)) { + Log.LogError ("ExtensionDomain item not found"); + return false; + } + if (addinReferences == null) { + return true; + } + + Application app = SetupService.GetExtensibleApplication (extensionDomain); + if (app == null) { + Log.LogError ("Extension domain '{0}' not found", extensionDomain); + return false; + } + + foreach (ITaskItem item in addinReferences) { + string addinId = item.ItemSpec.Replace (':',','); + Addin addin = app.Registry.GetAddin (addinId); + if (addin == null) { + Log.LogError ("Add-in '{0}' not found", addinId); + return false; + } + if (addin.Description == null) { + Log.LogError ("Add-in '{0}' could not be loaded", addinId); + return false; + } + foreach (string asm in addin.Description.MainModule.Assemblies) { + string file = Path.Combine (addin.Description.BasePath, Util.NormalizePath (asm)); + TaskItem ti = new TaskItem (file); + references.Add (ti); + } + } + return true; + } + + public ITaskItem[] AddinReferences { + get { return addinReferences; } + set { addinReferences = value; } + } + + public string ExtensionDomain { + get { return extensionDomain; } + set { extensionDomain = value; } + } + + [Output] + public ITaskItem[] References { + get { return references.ToArray (); } + } + } +} diff --git a/mono-addins/Mono.Addins.MSBuild/Util.cs b/mono-addins/Mono.Addins.MSBuild/Util.cs new file mode 100644 index 00000000..98ed52f1 --- /dev/null +++ b/mono-addins/Mono.Addins.MSBuild/Util.cs @@ -0,0 +1,63 @@ +// +// Util.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.IO; + +namespace Mono.Addins.MSBuild +{ + internal class Util + { + public static bool IsWindows { + get { return Path.DirectorySeparatorChar == '\\'; } + } + + public static string NormalizePath (string path) + { + if (path == null) + return null; + if (path.Length > 2 && path [0] == '[') { + int i = path.IndexOf (']', 1); + if (i != -1) { + try { + string fname = path.Substring (1, i - 1); + Environment.SpecialFolder sf = (Environment.SpecialFolder)Enum.Parse (typeof (Environment.SpecialFolder), fname, true); + path = Environment.GetFolderPath (sf) + path.Substring (i + 1); + } catch { + // Ignore + } + } + } + if (IsWindows) + return path.Replace ('/', '\\'); + else + return path.Replace ('\\', '/'); + } + } +} diff --git a/mono-addins/Mono.Addins.MSBuild/obj/Debug/Mono.Addins.MSBuild.csproj.CoreCompileInputs.cache b/mono-addins/Mono.Addins.MSBuild/obj/Debug/Mono.Addins.MSBuild.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..c32d837f --- /dev/null +++ b/mono-addins/Mono.Addins.MSBuild/obj/Debug/Mono.Addins.MSBuild.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +2991929b3d5264e3983a661eff682db76dfe0c0d diff --git a/mono-addins/Mono.Addins.MSBuild/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.MSBuild/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.MSBuild/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.MSBuild/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.MSBuild/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.MSBuild/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.MSBuild/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/mono-addins/Mono.Addins.MSBuild/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 00000000..ddff31c4 Binary files /dev/null and b/mono-addins/Mono.Addins.MSBuild/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/mono-addins/Mono.Addins.MSBuild/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.MSBuild/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.MSBuild/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.MSBuild/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.MSBuild/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.MSBuild/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Setup/AssemblyInfo.cs b/mono-addins/Mono.Addins.Setup/AssemblyInfo.cs new file mode 100644 index 00000000..36142e11 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("Mono.Addins.Setup")] +[assembly: AssemblyCopyright("Copyright (C) 2007 Novell, Inc (http://www.novell.com)")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.3.7.0")] diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ConsoleProgressMonitor.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ConsoleProgressMonitor.cs new file mode 100644 index 00000000..cfd8fe5b --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ConsoleProgressMonitor.cs @@ -0,0 +1,208 @@ +// +// ConsoleProgressMonitor.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; + +namespace Mono.Addins.Setup.ProgressMonitoring +{ + internal class ConsoleProgressMonitor: NullProgressMonitor + { + int columns = 80; + bool indent = true; + bool wrap = true; + int ilevel = 0; + int isize = 3; + int col = -1; + int logLevel; + LogTextWriter logger; + + public ConsoleProgressMonitor (): this (1) + { + } + + public ConsoleProgressMonitor (int logLevel) + { + this.logLevel = logLevel; + logger = new LogTextWriter (); + logger.TextWritten += new LogTextEventHandler (WriteLog); + } + + public bool WrapText { + get { return wrap; } + set { wrap = value; } + } + + public int WrapColumns { + get { return columns; } + set { columns = value; } + } + + public bool IndentTasks { + get { return indent; } + set { indent = value; } + } + + public override int LogLevel { + get { return logLevel; } + } + + public override void BeginTask (string name, int totalWork) + { + WriteText (name); + Indent (); + } + + public override void BeginStepTask (string name, int totalWork, int stepSize) + { + BeginTask (name, totalWork); + } + + public override void EndTask () + { + Unindent (); + } + + void WriteLog (string text) + { + WriteText (text); + } + + public override TextWriter Log { + get { return logger; } + } + + public override void ReportSuccess (string message) + { + WriteText (message); + } + + public override void ReportWarning (string message) + { + if (logLevel != 0) + WriteText ("WARNING: " + message + "\n"); + } + + public override void ReportError (string message, Exception ex) + { + if (logLevel == 0) + return; + + if (message != null && ex != null) { + WriteText ("ERROR: " + message + "\n"); + if (logLevel > 1) + WriteText (ex + "\n"); + } + if (message != null) + WriteText ("ERROR: " + message + "\n"); + else if (ex != null) { + if (logLevel > 1) + WriteText ("ERROR: " + ex + "\n"); + else + WriteText ("ERROR: " + ex.Message + "\n"); + } + } + + void WriteText (string text) + { + if (indent) + WriteText (text, ilevel); + else + WriteText (text, 0); + } + + void WriteText (string text, int leftMargin) + { + if (text == null || text.Length == 0) + return; + + int n = 0; + int maxCols = wrap ? columns : int.MaxValue; + + while (n < text.Length) + { + if (col == -1) { + Console.Write (new String (' ', leftMargin)); + col = leftMargin; + } + + int lastWhite = -1; + int sn = n; + bool eol = false; + + while (col < maxCols && n < text.Length) { + char c = text [n]; + if (c == '\r') { + n++; + continue; + } + if (c == '\n') { + eol = true; + break; + } + if (char.IsWhiteSpace (c)) + lastWhite = n; + col++; + n++; + } + + if (lastWhite == -1 || col < maxCols) + lastWhite = n; + else if (col >= maxCols) + n = lastWhite + 1; + + Console.Write (text.Substring (sn, lastWhite - sn)); + + if (eol || col >= maxCols) { + col = -1; + Console.WriteLine (); + if (eol) n++; + } + } + } + + void Indent () + { + ilevel += isize; + if (col != -1) { + Console.WriteLine (); + col = -1; + } + } + + void Unindent () + { + ilevel -= isize; + if (ilevel < 0) ilevel = 0; + if (col != -1) { + Console.WriteLine (); + col = -1; + } + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/LogTextWriter.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/LogTextWriter.cs new file mode 100644 index 00000000..67a063fb --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/LogTextWriter.cs @@ -0,0 +1,89 @@ +// +// LogTextWriter.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.IO; +using System.Text; +using System.Collections; + +namespace Mono.Addins.Setup.ProgressMonitoring +{ + internal delegate void LogTextEventHandler (string writtenText); + + internal class LogTextWriter: TextWriter + { + ArrayList chainedWriters; + + public void ChainWriter (TextWriter writer) + { + if (chainedWriters == null) chainedWriters = new ArrayList (); + chainedWriters.Add (writer); + } + + public void UnchainWriter (TextWriter writer) + { + if (chainedWriters != null) { + chainedWriters.Remove (writer); + if (chainedWriters.Count == 0) + chainedWriters = null; + } + } + + public override Encoding Encoding { + get { return Encoding.Default; } + } + + public override void Close () + { + if (Closed != null) + Closed (this, null); + } + + public override void Write (char value) + { + if (TextWritten != null) + TextWritten (value.ToString ()); + if (chainedWriters != null) + foreach (TextWriter cw in chainedWriters) + cw.Write (value); + } + + public override void Write (string value) + { + if (TextWritten != null) + TextWritten (value); + if (chainedWriters != null) + foreach (TextWriter cw in chainedWriters) + cw.Write (value); + } + + public event LogTextEventHandler TextWritten; + public event EventHandler Closed; + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/NullProgressMonitor.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/NullProgressMonitor.cs new file mode 100644 index 00000000..d4d3c3b3 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/NullProgressMonitor.cs @@ -0,0 +1,165 @@ +// +// NullProgressMonitor.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Threading; +using System.IO; + +namespace Mono.Addins.Setup.ProgressMonitoring +{ + internal class NullProgressMonitor: MarshalByRefObject, IProgressMonitor + { + bool done, canceled; + ArrayList errors; + ArrayList warnings; + ArrayList messages; + + public string[] Messages { + get { + if (messages != null) + return (string[]) messages.ToArray (typeof(string)); + else + return new string [0]; + } + } + + public string[] Warnings { + get { + if (warnings != null) + return (string[]) warnings.ToArray (typeof(string)); + else + return new string [0]; + } + } + + public ProgressError[] Errors { + get { + if (errors != null) + return (ProgressError[]) errors.ToArray (typeof(ProgressError)); + else + return new ProgressError [0]; + } + } + + public virtual void BeginTask (string name, int totalWork) + { + } + + public virtual void EndTask () + { + } + + public virtual void BeginStepTask (string name, int totalWork, int stepSize) + { + } + + public virtual void Step (int work) + { + } + + public virtual TextWriter Log { + get { return TextWriter.Null; } + } + + public virtual void ReportSuccess (string message) + { + if (messages == null) + messages = new ArrayList (); + messages.Add (message); + } + + public virtual void ReportWarning (string message) + { + if (warnings == null) + warnings = new ArrayList (); + messages.Add (message); + } + + public virtual void ReportError (string message, Exception ex) + { + if (errors == null) + errors = new ArrayList (); + + if (message == null && ex != null) + message = ex.Message; + else if (message != null && ex != null) { + if (!message.EndsWith (".")) message += "."; + message += " " + ex.Message; + } + + errors.Add (new ProgressError (message, ex)); + } + + public bool IsCancelRequested { + get { return canceled; } + } + + public void Cancel () + { + canceled = true; + } + + public virtual int LogLevel { + get { return 1; } + } + + public virtual void Dispose () + { + lock (this) { + if (done) return; + done = true; + } + OnCompleted (); + } + + protected virtual void OnCompleted () + { + } + } + + internal class ProgressError + { + Exception ex; + string message; + + public ProgressError (string message, Exception ex) + { + this.ex = ex; + this.message = message; + } + + public string Message { + get { return message; } + } + + public Exception Exception { + get { return ex; } } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ProgressStatusMonitor.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ProgressStatusMonitor.cs new file mode 100644 index 00000000..952a80e1 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ProgressStatusMonitor.cs @@ -0,0 +1,151 @@ +// +// ProgressStatusMonitor.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.IO; +using System.Text; + +namespace Mono.Addins.Setup.ProgressMonitoring +{ + internal class ProgressStatusMonitor: MarshalByRefObject, IProgressMonitor + { + IProgressStatus status; + LogTextWriter logger; + ProgressTracker tracker = new ProgressTracker (); + StringBuilder logBuffer = new StringBuilder (); + + public ProgressStatusMonitor (IProgressStatus status) + { + this.status = status; + logger = new LogTextWriter (); + logger.TextWritten += new LogTextEventHandler (WriteLog); + } + + public static IProgressMonitor GetProgressMonitor (IProgressStatus status) + { + if (status == null) + return new NullProgressMonitor (); + else + return new ProgressStatusMonitor (status); + } + + public void BeginTask (string name, int totalWork) + { + FlushLog (); + tracker.BeginTask (name, totalWork); + status.SetMessage (tracker.CurrentTask); + status.SetProgress (tracker.GlobalWork); + } + + public void BeginStepTask (string name, int totalWork, int stepSize) + { + FlushLog (); + tracker.BeginStepTask (name, totalWork, stepSize); + status.SetMessage (tracker.CurrentTask); + status.SetProgress (tracker.GlobalWork); + } + + public void Step (int work) + { + FlushLog (); + tracker.Step (work); + status.SetProgress (tracker.GlobalWork); + } + + public void EndTask () + { + FlushLog (); + tracker.EndTask (); + status.SetMessage (tracker.CurrentTask); + status.SetProgress (tracker.GlobalWork); + } + + void WriteLog (string text) + { + int pi = 0; + int i = text.IndexOf ('\n'); + while (i != -1) { + string line = text.Substring (pi, i - pi); + if (logBuffer.Length > 0) { + logBuffer.Append (line); + status.Log (logBuffer.ToString ()); + logBuffer.Clear (); + } else { + status.Log (line); + } + pi = i + 1; + i = text.IndexOf ('\n', pi); + } + logBuffer.Append (text, pi, text.Length - pi); + } + + public TextWriter Log { + get { return logger; } + } + + public void ReportWarning (string message) + { + FlushLog (); + status.ReportWarning (message); + } + + public void ReportError (string message, Exception ex) + { + FlushLog (); + status.ReportError (message, ex); + } + + public bool IsCancelRequested { + get { return status.IsCanceled; } + } + + public void Cancel () + { + FlushLog (); + status.Cancel (); + } + + public int LogLevel { + get { return status.LogLevel; } + } + + void FlushLog () + { + if (logBuffer.Length > 0) { + status.Log (logBuffer.ToString ()); + logBuffer.Clear (); + } + } + + public void Dispose () + { + FlushLog (); + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ProgressTracker.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ProgressTracker.cs new file mode 100644 index 00000000..5698ea92 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.ProgressMonitoring/ProgressTracker.cs @@ -0,0 +1,147 @@ +// +// ProgressTracker.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; + +namespace Mono.Addins.Setup.ProgressMonitoring +{ + internal class ProgressTracker + { + bool done; + + ArrayList tasks = new ArrayList (); + class Task + { + public string Name; + public int TotalWork; + public int CurrentWork; + public int StepSize = 1; + public bool IsStep; + + public double GetWorkPercent (double part) + { + if (TotalWork <= 0) return 0; + if (CurrentWork >= TotalWork) return 1.0; + return ((double)CurrentWork + part) / (double)TotalWork; + } + } + + public void Reset () + { + done = false; + tasks.Clear (); + } + + public void BeginTask (string name, int totalWork) + { + Task t = new Task (); + t.Name = name; + t.TotalWork = totalWork; + tasks.Add (t); + } + + public void BeginStepTask (string name, int totalWork, int stepSize) + { + Task t = new Task (); + t.StepSize = stepSize; + t.IsStep = true; + t.Name = name; + t.TotalWork = totalWork; + tasks.Add (t); + } + + public void EndTask () + { + if (tasks.Count > 0) { + Task t = LastTask; + tasks.RemoveAt (tasks.Count - 1); + if (t.IsStep) + Step (t.StepSize); + } + } + + public void Step (int work) + { + if (tasks.Count == 0) return; + Task t = LastTask; + t.CurrentWork += work; + if (t.CurrentWork > t.TotalWork) + t.CurrentWork = t.TotalWork; + } + + Task LastTask { + get { return (Task)tasks [tasks.Count-1]; } + } + + public string CurrentTask { + get { + if (tasks.Count == 0) return null; + return LastTask.Name; + } + } + + public double CurrentTaskWork { + get { + if (tasks.Count == 0) return 0; + return LastTask.GetWorkPercent (0); + } + } + + public bool UnknownWork { + get { + if (tasks.Count == 0) return false; + return LastTask.TotalWork <= 1; + } + } + + public double GlobalWork { + get { + if (done) return 1.0; + + double work = 0; + for (int n = tasks.Count - 1; n >= 0; n--) { + Task t = (Task) tasks [n]; + work = t.GetWorkPercent (work) * (double)t.StepSize; + } + return work; + } + } + + public bool InProgress { + get { return !done; } + } + + public void Done () + { + done = true; + tasks.Clear (); + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.csproj b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.csproj new file mode 100644 index 00000000..2dc51ec0 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup.csproj @@ -0,0 +1,96 @@ + + + + + Debug + AnyCPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36} + Library + Mono.Addins.Setup + Mono.Addins.Setup + True + ..\mono-addins.snk + v4.6 + Mono.Addins.Setup + Lluis Sanchez + https://github.com/mono/mono-addins/blob/master/COPYING + https://github.com/mono/mono-addins + Mono.Addins is a framework for creating extensible applications, and for creating add-ins which extend applications. Mono.Addins.Setup provides an API for managing add-ins, creating add-in packages and publishing add-ins in on-line repositories. + 8.0.30703 + 2.0 + + + + True + full + false + ..\bin + prompt + 4 + True + False + 1574 + ..\bin\Mono.Addins.Setup.xml + + + pdbonly + True + ..\bin + prompt + 4 + True + False + true + 1574 + + + True + + + + + + + + {91DD5A2D-9FE3-4C3C-9253-876141874DAD} + Mono.Addins + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinInfo.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinInfo.cs new file mode 100644 index 00000000..1e119ab6 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinInfo.cs @@ -0,0 +1,394 @@ +// +// AddinInfo.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Collections; +using System.Xml; +using System.Xml.Serialization; +using Mono.Addins.Description; + +namespace Mono.Addins.Setup +{ + internal class AddinInfo: AddinHeader + { + string id = ""; + string namspace = ""; + string name = ""; + string version = ""; + string baseVersion = ""; + string author = ""; + string copyright = ""; + string url = ""; + string description = ""; + string category = ""; + DependencyCollection dependencies; + DependencyCollection optionalDependencies; + AddinPropertyCollectionImpl properties; + + public AddinInfo () + { + dependencies = new DependencyCollection (); + optionalDependencies = new DependencyCollection (); + properties = new AddinPropertyCollectionImpl (); + } + + public string Id { + get { return Addin.GetFullId (namspace, id, version); } + } + + [XmlElement ("Id")] + public string LocalId { + get { return id; } + set { id = value; } + } + + public string Namespace { + get { return namspace; } + set { namspace = value; } + } + + public string Name { + get { + string s = Properties.GetPropertyValue ("Name"); + if (s.Length > 0) + return s; + if (name != null && name.Length > 0) + return name; + string sid = id; + if (sid.StartsWith ("__")) + sid = sid.Substring (2); + return Addin.GetFullId (namspace, sid, null); + } + set { name = value; } + } + + public string Version { + get { return version; } + set { version = value; } + } + + public string BaseVersion { + get { return baseVersion; } + set { baseVersion = value; } + } + + public string Author { + get { + string s = Properties.GetPropertyValue ("Author"); + if (s.Length > 0) + return s; + return author; + } + set { author = value; } + } + + public string Copyright { + get { + string s = Properties.GetPropertyValue ("Copyright"); + if (s.Length > 0) + return s; + return copyright; + } + set { copyright = value; } + } + + public string Url { + get { + string s = Properties.GetPropertyValue ("Url"); + if (s.Length > 0) + return s; + return url; + } + set { url = value; } + } + + public string Description { + get { + string s = Properties.GetPropertyValue ("Description"); + if (s.Length > 0) + return s; + return description; + } + set { description = value; } + } + + public string Category { + get { + string s = Properties.GetPropertyValue ("Category"); + if (s.Length > 0) + return s; + return category; + } + set { category = value; } + } + + [XmlArrayItem ("AddinDependency", typeof(AddinDependency))] + [XmlArrayItem ("NativeDependency", typeof(NativeDependency))] + [XmlArrayItem ("AssemblyDependency", typeof(AssemblyDependency))] + public DependencyCollection Dependencies { + get { return dependencies; } + } + + [XmlArrayItem ("AddinDependency", typeof(AddinDependency))] + [XmlArrayItem ("NativeDependency", typeof(NativeDependency))] + [XmlArrayItem ("AssemblyDependency", typeof(AssemblyDependency))] + public DependencyCollection OptionalDependencies { + get { return optionalDependencies; } + } + + [XmlArrayItem ("Property", typeof(AddinProperty))] + public AddinPropertyCollectionImpl Properties { + get { return properties; } + } + + AddinPropertyCollection AddinHeader.Properties { + get { return properties; } + } + + public static AddinInfo ReadFromAddinFile (StreamReader r) + { + XmlDocument doc = new XmlDocument (); + doc.Load (r); + r.Close (); + + AddinInfo info = new AddinInfo (); + info.id = doc.DocumentElement.GetAttribute ("id"); + info.namspace = doc.DocumentElement.GetAttribute ("namespace"); + info.name = doc.DocumentElement.GetAttribute ("name"); + if (info.id == "") info.id = info.name; + info.version = doc.DocumentElement.GetAttribute ("version"); + info.author = doc.DocumentElement.GetAttribute ("author"); + info.copyright = doc.DocumentElement.GetAttribute ("copyright"); + info.url = doc.DocumentElement.GetAttribute ("url"); + info.description = doc.DocumentElement.GetAttribute ("description"); + info.category = doc.DocumentElement.GetAttribute ("category"); + info.baseVersion = doc.DocumentElement.GetAttribute ("compatVersion"); + AddinPropertyCollectionImpl props = new AddinPropertyCollectionImpl (); + info.properties = props; + ReadHeader (info, props, doc.DocumentElement); + ReadDependencies (info.Dependencies, info.OptionalDependencies, doc.DocumentElement); + return info; + } + + static void ReadDependencies (DependencyCollection deps, DependencyCollection opDeps, XmlElement elem) + { + foreach (XmlElement dep in elem.SelectNodes ("Dependencies/Addin")) { + AddinDependency adep = new AddinDependency (); + adep.AddinId = dep.GetAttribute ("id"); + string v = dep.GetAttribute ("version"); + if (v.Length != 0) + adep.Version = v; + deps.Add (adep); + } + + foreach (XmlElement dep in elem.SelectNodes ("Dependencies/Assembly")) { + AssemblyDependency adep = new AssemblyDependency (); + adep.FullName = dep.GetAttribute ("name"); + adep.Package = dep.GetAttribute ("package"); + deps.Add (adep); + } + + foreach (XmlElement mod in elem.SelectNodes ("Module")) + ReadDependencies (opDeps, opDeps, mod); + } + + static void ReadHeader (AddinInfo info, AddinPropertyCollectionImpl properties, XmlElement elem) + { + elem = elem.SelectSingleNode ("Header") as XmlElement; + if (elem == null) + return; + foreach (XmlNode xprop in elem.ChildNodes) { + XmlElement prop = xprop as XmlElement; + if (prop != null) { + switch (prop.LocalName) { + case "Id": info.id = prop.InnerText; break; + case "Namespace": info.namspace = prop.InnerText; break; + case "Version": info.version = prop.InnerText; break; + case "CompatVersion": info.baseVersion = prop.InnerText; break; + default: { + AddinProperty aprop = new AddinProperty (); + aprop.Name = prop.LocalName; + if (prop.HasAttribute ("locale")) + aprop.Locale = prop.GetAttribute ("locale"); + aprop.Value = prop.InnerText; + properties.Add (aprop); + break; + }} + } + } + } + + internal static AddinInfo ReadFromDescription (AddinDescription description) + { + AddinInfo info = new AddinInfo (); + info.id = description.LocalId; + info.namspace = description.Namespace; + info.name = description.Name; + info.version = description.Version; + info.author = description.Author; + info.copyright = description.Copyright; + info.url = description.Url; + info.description = description.Description; + info.category = description.Category; + info.baseVersion = description.CompatVersion; + info.properties = new AddinPropertyCollectionImpl (description.Properties); + + foreach (Dependency dep in description.MainModule.Dependencies) + info.Dependencies.Add (dep); + + foreach (ModuleDescription mod in description.OptionalModules) { + foreach (Dependency dep in mod.Dependencies) + info.OptionalDependencies.Add (dep); + } + return info; + } + + public bool SupportsVersion (string version) + { + if (Addin.CompareVersions (Version, version) > 0) + return false; + if (baseVersion == "") + return true; + return Addin.CompareVersions (BaseVersion, version) >= 0; + } + + public int CompareVersionTo (AddinHeader other) + { + return Addin.CompareVersions (this.version, other.Version); + } + } + + /// + /// Basic add-in information + /// + public interface AddinHeader + { + /// + /// Full identifier of the add-in + /// + string Id { + get; + } + + /// + /// Display name of the add-in + /// + string Name { + get; + } + + /// + /// Namespace of the add-in + /// + string Namespace { + get; + } + + /// + /// Version of the add-in + /// + string Version { + get; + } + + /// + /// Version with which this add-in is compatible + /// + string BaseVersion { + get; + } + + /// + /// Add-in author + /// + string Author { + get; + } + + /// + /// Add-in copyright + /// + string Copyright { + get; + } + + /// + /// Web page URL with more information about the add-in + /// + string Url { + get; + } + + /// + /// Description of the add-in + /// + string Description { + get; + } + + /// + /// Category of the add-in + /// + string Category { + get; + } + + /// + /// Dependencies of the add-in + /// + DependencyCollection Dependencies { + get; + } + + /// + /// Optional dependencies of the add-in + /// + DependencyCollection OptionalDependencies { + get; + } + + /// + /// Custom properties specified in the add-in header + /// + AddinPropertyCollection Properties { + get; + } + + /// + /// Compares the versions of two add-ins + /// + /// + /// Another add-in + /// + /// + /// Result of comparison + /// + int CompareVersionTo (AddinHeader other); + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinInfoCollection.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinInfoCollection.cs new file mode 100644 index 00000000..c7ef3e23 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinInfoCollection.cs @@ -0,0 +1,45 @@ +// +// AddinInfoCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; + +namespace Mono.Addins.Setup +{ + internal class AddinInfoCollection: CollectionBase + { + public AddinInfo this [int n] { + get { return (AddinInfo) List [n]; } + } + + public void Add (AddinInfo p) + { + List.Add (p); + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinPackage.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinPackage.cs new file mode 100644 index 00000000..6929928c --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinPackage.cs @@ -0,0 +1,409 @@ +// +// AddinPackage.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using System.Reflection; +using System.Diagnostics; +using System.Collections.Specialized; +using System.Net; + +using ICSharpCode.SharpZipLib.Zip; +using Mono.Addins; +using Mono.Addins.Description; +using System.Collections.Generic; +using System.Linq; +using Mono.Addins.Database; + +namespace Mono.Addins.Setup +{ + internal class AddinPackage: Package + { + AddinInfo info; + string packFile; + string url; + string tempFolder; + bool disablingOnUninstall; + bool uninstallingLoaded; + string configFile; + bool installed; + Addin iaddin; + + public AddinHeader Addin { + get { return info; } + } + + public override string Name { + get { return info.Name + " v" + info.Version; } + } + + public static AddinPackage PackageFromRepository (AddinRepositoryEntry repAddin) + { + AddinPackage pack = new AddinPackage (); + pack.info = (AddinInfo) repAddin.Addin; + pack.url = new Uri (new Uri (repAddin.RepositoryUrl), repAddin.Url).ToString (); + return pack; + } + + public static AddinPackage PackageFromFile (string file) + { + AddinPackage pack = new AddinPackage (); + pack.info = ReadAddinInfo (file); + pack.packFile = file; + return pack; + } + + public static AddinPackage FromInstalledAddin (Addin sinfo) + { + AddinPackage pack = new AddinPackage (); + pack.info = AddinInfo.ReadFromDescription (sinfo.Description); + return pack; + } + + static AddinInfo ReadAddinInfo (string file) + { + ZipFile zfile = new ZipFile (file); + try { + foreach (ZipEntry ze in zfile) { + if (ze.Name == "addin.info") { + using (Stream s = zfile.GetInputStream (ze)) { + return AddinInfo.ReadFromAddinFile (new StreamReader (s)); + } + } + } + } finally { + zfile.Close (); + } + throw new InstallException ("Addin configuration file not found in package."); + } + + internal override bool IsUpgradeOf (Package p) + { + AddinPackage ap = p as AddinPackage; + if (ap == null) return false; + return info.SupportsVersion (ap.info.Version); + } + + public override bool Equals (object ob) + { + AddinPackage ap = ob as AddinPackage; + if (ap == null) return false; + return ap.info.Id == info.Id && ap.info.Version == info.Version; + } + + public override int GetHashCode () + { + return (info.Id + info.Version).GetHashCode (); + } + + internal override void PrepareInstall (IProgressMonitor monitor, AddinStore service) + { + if (service.Registry.IsRegisteredForUninstall (info.Id)) + throw new InstallException ("The addin " + info.Name + " v" + info.Version + " is scheduled for uninstallation. Please restart the application before trying to install it again."); + if (service.Registry.GetAddin (Mono.Addins.Addin.GetFullId (info.Namespace, info.Id, info.Version), true) != null) + throw new InstallException ("The addin " + info.Name + " v" + info.Version + " is already installed."); + + if (url != null) + packFile = service.DownloadFile (monitor, url); + + tempFolder = CreateTempFolder (); + + // Extract the files + using (FileStream fs = new FileStream (packFile, FileMode.Open, FileAccess.Read)) { + ZipFile zip = new ZipFile (fs); + try { + foreach (ZipEntry entry in zip) { + string name; + if (Path.PathSeparator == '\\') + name = entry.Name.Replace ('/', '\\'); + else + name = entry.Name.Replace ('\\', '/'); + string path = Path.Combine (tempFolder, name); + string dir = Path.GetDirectoryName (path); + if (!Directory.Exists (dir)) + Directory.CreateDirectory (dir); + + byte [] buffer = new byte [8192]; + int n = 0; + Stream inStream = zip.GetInputStream (entry); + Stream outStream = null; + try { + outStream = File.Create (path); + while ((n = inStream.Read (buffer, 0, buffer.Length)) > 0) + outStream.Write (buffer, 0, n); + } finally { + inStream.Close (); + if (outStream != null) + outStream.Close (); + } + } + } finally { + zip.Close (); + } + } + + foreach (string s in Directory.GetFiles (tempFolder)) { + if (Path.GetFileName (s) == "addin.info") { + configFile = s; + break; + } + } + + if (configFile == null) + throw new InstallException ("Add-in information file not found in package."); + } + + internal override void CommitInstall (IProgressMonitor monitor, AddinStore service) + { + service.RegisterAddin (monitor, info, tempFolder); + installed = true; + } + + internal override void RollbackInstall (IProgressMonitor monitor, AddinStore service) + { + if (installed) { + iaddin = service.Registry.GetAddin (info.Id); + if (iaddin != null) + CommitUninstall (monitor, service); + } + } + + internal override void EndInstall (IProgressMonitor monitor, AddinStore service) + { + if (url != null && packFile != null) + File.Delete (packFile); + if (tempFolder != null) + Directory.Delete (tempFolder, true); + } + + internal override void Resolve (IProgressMonitor monitor, AddinStore service, PackageCollection toInstall, PackageCollection toUninstall, PackageCollection installedRequired, DependencyCollection unresolved) + { + Addin ia = service.Registry.GetAddin (Mono.Addins.Addin.GetIdName (info.Id)); + + if (ia != null) { + Package p = AddinPackage.FromInstalledAddin (ia); + if (!toUninstall.Contains (p)) + toUninstall.Add (p); + + if (!info.SupportsVersion (ia.Version)) { + + // This addin breaks the api of the currently installed one, + // it has to be removed, together with all dependencies + + Addin[] ainfos = service.GetDependentAddins (info.Id, true); + foreach (Addin ainfo in ainfos) { + p = AddinPackage.FromInstalledAddin (ainfo); + if (!toUninstall.Contains (p)) + toUninstall.Add (p); + } + } + } + + foreach (Dependency dep in info.Dependencies) { + service.ResolveDependency (monitor, dep, this, toInstall, toUninstall, installedRequired, unresolved); + } + } + + internal override void PrepareUninstall (IProgressMonitor monitor, AddinStore service) + { + iaddin = service.Registry.GetAddin (info.Id, true); + if (iaddin == null) + throw new InstallException (string.Format ("The add-in '{0}' is not installed.", info.Name)); + + AddinDescription conf = iaddin.Description; + + if (!File.Exists (iaddin.AddinFile)) { + monitor.ReportWarning (string.Format ("The add-in '{0}' is scheduled for uninstalling, but the add-in file could not be found.", info.Name)); + return; + } + + // The add-in is a core application add-in. It can't be uninstalled, so it will be disabled. + if (!service.IsUserAddin (iaddin.AddinFile)) { + disablingOnUninstall = true; + return; + } + + // If the add-in assemblies are loaded, or if there is any file with a write lock, delay the uninstallation + HashSet files = new HashSet (GetInstalledFiles (conf)); + if (AddinManager.CheckAssembliesLoaded (files) || files.Any (f => HasWriteLock (f))) { + uninstallingLoaded = true; + return; + } + + if (!service.HasWriteAccess (iaddin.AddinFile)) + throw new InstallException (AddinStore.GetUninstallErrorNoRoot (info)); + + foreach (string path in GetInstalledFiles (conf)) { + if (!service.HasWriteAccess (path)) + throw new InstallException (AddinStore.GetUninstallErrorNoRoot (info)); + } + + tempFolder = CreateTempFolder (); + CopyAddinFiles (monitor, conf, iaddin.AddinFile, tempFolder); + } + + bool HasWriteLock (string file) + { + if (!File.Exists (file)) + return false; + try { + File.OpenWrite (file).Close (); + return false; + } catch { + return true; + } + } + + IEnumerable GetInstalledFiles (AddinDescription conf) + { + string basePath = Path.GetDirectoryName (conf.AddinFile); + foreach (string relPath in conf.AllFiles) { + string afile = Path.Combine (basePath, Util.NormalizePath (relPath)); + if (File.Exists (afile)) + yield return afile; + } + foreach (var p in conf.Properties) { + string file; + try { + file = Path.Combine (basePath, p.Value); + if (!File.Exists (file)) + file = null; + } catch { + file = null; + } + if (file != null) + yield return file; + } + } + + internal override void CommitUninstall (IProgressMonitor monitor, AddinStore service) + { + if (disablingOnUninstall) { + disablingOnUninstall = false; + service.Registry.DisableAddin (info.Id, true); + return; + } + + AddinDescription conf = iaddin.Description; + + string basePath = Path.GetDirectoryName (conf.AddinFile); + + if (uninstallingLoaded) { + List files = new List (); + files.Add (iaddin.AddinFile); + foreach (string f in GetInstalledFiles (conf)) + files.Add (f); + service.Registry.RegisterForUninstall (info.Id, files); + return; + } + + if (tempFolder == null) + return; + + monitor.Log.WriteLine ("Uninstalling " + info.Name + " v" + info.Version); + + foreach (string path in GetInstalledFiles (conf)) + File.Delete (path); + + File.Delete (iaddin.AddinFile); + + RecDeleteDir (monitor, basePath); + + monitor.Log.WriteLine ("Done"); + } + + void RecDeleteDir (IProgressMonitor monitor, string path) + { + if (Directory.GetFiles (path).Length != 0) + return; + + foreach (string dir in Directory.GetDirectories (path)) + RecDeleteDir (monitor, dir); + + try { + Directory.Delete (path); + } catch { + monitor.ReportWarning ("Directory " + path + " could not be deleted."); + } + } + + internal override void RollbackUninstall (IProgressMonitor monitor, AddinStore service) + { + disablingOnUninstall = false; + if (tempFolder != null) { + AddinDescription conf = iaddin.Description; + string configFile = Path.Combine (tempFolder, Path.GetFileName (iaddin.AddinFile)); + + string addinDir = Path.GetDirectoryName (iaddin.AddinFile); + CopyAddinFiles (monitor, conf, configFile, addinDir); + } + } + + internal override void EndUninstall (IProgressMonitor monitor, AddinStore service) + { + if (tempFolder != null) + Directory.Delete (tempFolder, true); + tempFolder = null; + } + + void CopyAddinFiles (IProgressMonitor monitor, AddinDescription conf, string configFile, string destPath) + { + if (!Directory.Exists (destPath)) + Directory.CreateDirectory (destPath); + + string dfile = Path.Combine (destPath, Path.GetFileName (configFile)); + if (File.Exists (dfile)) + File.Delete (dfile); + + File.Copy (configFile, dfile); + + string basePath = Path.GetDirectoryName (configFile); + + foreach (string relPath in conf.AllFiles) { + string path = Path.Combine (basePath, Util.NormalizePath (relPath)); + if (!File.Exists (path)) + continue; + + string destf = Path.Combine (destPath, Path.GetDirectoryName (relPath)); + if (!Directory.Exists (destf)) + Directory.CreateDirectory (destf); + + dfile = Path.Combine (destPath, relPath); + if (File.Exists (dfile)) + File.Delete (dfile); + + File.Copy (path, dfile); + } + } + + + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinRepositoryEntry.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinRepositoryEntry.cs new file mode 100644 index 00000000..75a75297 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinRepositoryEntry.cs @@ -0,0 +1,146 @@ +// +// PackageRepositoryEntry.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Net; +using System.Collections.Generic; +using System.IO; +using System.Threading; + +namespace Mono.Addins.Setup +{ + internal class PackageRepositoryEntry: RepositoryEntry, AddinRepositoryEntry, IComparable + { + AddinInfo addin; + + public AddinInfo Addin { + get { return addin; } + set { addin = value; } + } + + AddinHeader AddinRepositoryEntry.Addin { + get { return addin; } + } + + public string RepositoryUrl { + get { return Repository.Url; } + } + + public string RepositoryName { + get { return Repository.Name; } + } + + public int CompareTo (object other) + { + PackageRepositoryEntry rep = (PackageRepositoryEntry) other; + string n1 = Mono.Addins.Addin.GetIdName (Addin.Id); + string n2 = Mono.Addins.Addin.GetIdName (rep.Addin.Id); + if (n1 != n2) + return n1.CompareTo (n2); + else + return Mono.Addins.Addin.CompareVersions (rep.Addin.Version, Addin.Version); + } + + public IAsyncResult BeginDownloadSupportFile (string name, AsyncCallback cb, object state) + { + return Repository.BeginDownloadSupportFile (name, cb, state); + } + + public Stream EndDownloadSupportFile (IAsyncResult ares) + { + return Repository.EndDownloadSupportFile (ares); + } + } + + /// + /// A reference to an add-in available in an on-line repository + /// + public interface AddinRepositoryEntry + { + /// + /// Add-in information + /// + AddinHeader Addin { + get; + } + + /// + /// Url to the add-in package + /// + string Url { + get; + } + + /// + /// The URL of the repository + /// + string RepositoryUrl { + get; + } + + /// + /// Name of the repository + /// + string RepositoryName { + get; + } + + /// + /// Begins downloading a support file + /// + /// + /// Result of the asynchronous operation, to be used when calling EndDownloadSupportFile to + /// get the download result. + /// + /// + /// Name of the file. + /// + /// + /// Callback to be called when the download operation ends. + /// + /// + /// Custom state object provided by the caller. + /// + /// + /// This method can be used to get the contents of a support file of an add-in. + /// A support file is a file referenced in the custom properties of an add-in. + /// + IAsyncResult BeginDownloadSupportFile (string name, AsyncCallback cb, object state); + + /// + /// Gets the result of the asynchronous download of a file + /// + /// + /// The downloaded file. + /// + /// + /// The async result object returned by BeginDownloadSupportFile. + /// + Stream EndDownloadSupportFile (IAsyncResult ares); + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinStore.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinStore.cs new file mode 100644 index 00000000..eef58f29 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinStore.cs @@ -0,0 +1,699 @@ +// +// AddinStore.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using System.Reflection; +using System.Diagnostics; +using System.Net; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; + +using ICSharpCode.SharpZipLib.Zip; +using Mono.Addins; +using Mono.Addins.Setup.ProgressMonitoring; +using Mono.Addins.Description; +using Mono.Addins.Serialization; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace Mono.Addins.Setup +{ + internal class AddinStore + { + SetupService service; + + public AddinStore (SetupService service) + { + this.service = service; + } + + internal void ResetCachedData () + { + } + + public AddinRegistry Registry { + get { return service.Registry; } + } + + public bool Install (IProgressStatus statusMonitor, params string[] files) + { + Package[] packages = new Package [files.Length]; + for (int n=0; n ids) + { + IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor); + monitor.BeginTask ("Uninstalling add-ins", ids.Count ()); + + foreach (string id in ids) { + bool rollback = false; + ArrayList toUninstall = new ArrayList (); + ArrayList uninstallPrepared = new ArrayList (); + + Addin ia = service.Registry.GetAddin (id); + if (ia == null) + throw new InstallException ("The add-in '" + id + "' is not installed."); + + toUninstall.Add (AddinPackage.FromInstalledAddin (ia)); + + Addin[] deps = GetDependentAddins (id, true); + foreach (Addin dep in deps) + toUninstall.Add (AddinPackage.FromInstalledAddin (dep)); + + monitor.BeginTask ("Deleting files", toUninstall.Count*2 + uninstallPrepared.Count + 1); + + // Prepare install + + foreach (Package mpack in toUninstall) { + try { + mpack.PrepareUninstall (monitor, this); + monitor.Step (1); + uninstallPrepared.Add (mpack); + } catch (Exception ex) { + ReportException (monitor, ex); + rollback = true; + break; + } + } + + // Commit install + + if (!rollback) { + foreach (Package mpack in toUninstall) { + try { + mpack.CommitUninstall (monitor, this); + monitor.Step (1); + } catch (Exception ex) { + ReportException (monitor, ex); + rollback = true; + break; + } + } + } + + // Rollback if failed + + if (rollback) { + monitor.BeginTask ("Rolling back uninstall", uninstallPrepared.Count); + foreach (Package mpack in uninstallPrepared) { + try { + mpack.RollbackUninstall (monitor, this); + } catch (Exception ex) { + ReportException (monitor, ex); + } + } + monitor.EndTask (); + } + monitor.Step (1); + + // Cleanup + + foreach (Package mpack in uninstallPrepared) { + try { + mpack.EndUninstall (monitor, this); + monitor.Step (1); + } catch (Exception ex) { + monitor.Log.WriteLine (ex); + } + } + + monitor.EndTask (); + monitor.Step (1); + } + + // Update the extension maps + service.Registry.Update (statusMonitor); + + monitor.EndTask (); + + service.SaveConfiguration (); + ResetCachedData (); + } + + public Addin[] GetDependentAddins (string id, bool recursive) + { + ArrayList list = new ArrayList (); + FindDependentAddins (list, id, recursive); + return (Addin[]) list.ToArray (typeof (Addin)); + } + + void FindDependentAddins (ArrayList list, string id, bool recursive) + { + foreach (Addin iaddin in service.Registry.GetAddins ()) { + if (list.Contains (iaddin)) + continue; + foreach (Dependency dep in iaddin.Description.MainModule.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep != null && adep.AddinId == id) { + list.Add (iaddin); + if (recursive) + FindDependentAddins (list, iaddin.Id, true); + } + } + } + } + + public bool ResolveDependencies (IProgressStatus statusMonitor, AddinRepositoryEntry[] addins, out PackageCollection resolved, out PackageCollection toUninstall, out DependencyCollection unresolved) + { + resolved = new PackageCollection (); + for (int n=0; n (HttpWebRequest)WebRequest.Create (url), + r => r.Headers ["Pragma"] = "no-cache" + ); + monitor.Step (1); + monitor.BeginTask ("Downloading " + url, (int) resp.ContentLength); + + file = Path.GetTempFileName (); + fs = new FileStream (file, FileMode.Create, FileAccess.Write); + s = resp.GetResponseStream (); + byte[] buffer = new byte [4096]; + + int n; + while ((n = s.Read (buffer, 0, buffer.Length)) != 0) { + monitor.Step (n); + fs.Write (buffer, 0, n); + if (monitor.IsCancelRequested) + throw new InstallException ("Installation cancelled."); + } + fs.Close (); + s.Close (); + return file; + } catch { + if (fs != null) + fs.Close (); + if (s != null) + s.Close (); + if (file != null) + File.Delete (file); + throw; + } finally { + monitor.EndTask (); + monitor.EndTask (); + } + } + + internal bool HasWriteAccess (string file) + { + FileInfo f = new FileInfo (file); + return !f.Exists || !f.IsReadOnly; + } + + internal bool IsUserAddin (string addinFile) + { + string installPath = service.InstallDirectory; + if (installPath [installPath.Length - 1] != Path.DirectorySeparatorChar) + installPath += Path.DirectorySeparatorChar; + return Path.GetFullPath (addinFile).StartsWith (installPath); + } + + internal static string GetUninstallErrorNoRoot (AddinHeader ainfo) + { + return string.Format ("The add-in '{0} v{1}' can't be uninstalled with the current user permissions.", ainfo.Name, ainfo.Version); + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfiguration.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfiguration.cs new file mode 100644 index 00000000..0a972aea --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfiguration.cs @@ -0,0 +1,64 @@ +// +// AddinSystemConfiguration.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Xml; +using System.Xml.Serialization; + +namespace Mono.Addins.Setup +{ + internal class AddinSystemConfiguration + { + ArrayList repositories = new ArrayList (); + int repositoryIdCount = 0; + StringCollection disabledAddins = new StringCollection (); + StringCollection addinPaths = new StringCollection (); + + [XmlArrayItem ("Repository", typeof(RepositoryRecord))] + public ArrayList Repositories { + get { return repositories; } + } + + public int RepositoryIdCount { + get { return repositoryIdCount; } + set { repositoryIdCount = value; } + } + + [XmlArrayItem ("Addin")] + public StringCollection DisabledAddins { + get { return disabledAddins; } + } + + [XmlArrayItem ("Addin")] + public StringCollection AddinPaths { + get { return addinPaths; } + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfigurationReaderWriter.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfigurationReaderWriter.cs new file mode 100644 index 00000000..370b3db9 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfigurationReaderWriter.cs @@ -0,0 +1,386 @@ +// It is automatically generated +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using System.Text; +using System.Collections; +using System.Globalization; + +namespace Mono.Addins.Setup +{ + internal class AddinSystemConfigurationReader : XmlSerializationReader + { + static readonly System.Reflection.MethodInfo fromBinHexStringMethod = typeof (XmlConvert).GetMethod ("FromBinHexString", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, null, new Type [] {typeof (string)}, null); + static byte [] FromBinHexString (string input) + { + return input == null ? null : (byte []) fromBinHexStringMethod.Invoke (null, new object [] {input}); + } + public object ReadRoot_AddinSystemConfiguration () + { + Reader.MoveToContent(); + if (Reader.LocalName != "AddinSystemConfiguration" || Reader.NamespaceURI != "") + throw CreateUnknownNodeException(); + return ReadObject_AddinSystemConfiguration (true, true); + } + + public Mono.Addins.Setup.AddinSystemConfiguration ReadObject_AddinSystemConfiguration (bool isNullable, bool checkType) + { + Mono.Addins.Setup.AddinSystemConfiguration ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "AddinSystemConfiguration" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Setup.AddinSystemConfiguration) Activator.CreateInstance(typeof(Mono.Addins.Setup.AddinSystemConfiguration), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + bool b0=false, b1=false, b2=false, b3=false; + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "AddinPaths" && Reader.NamespaceURI == "" && !b3) { + if (((object)ob.@AddinPaths) == null) + throw CreateReadOnlyCollectionException ("System.Collections.Specialized.StringCollection"); + if (Reader.IsEmptyElement) { + Reader.Skip(); + } else { + int n4 = 0; + Reader.ReadStartElement(); + Reader.MoveToContent(); + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Addin" && Reader.NamespaceURI == "") { + string s5 = Reader.ReadElementString (); + if (((object)ob.@AddinPaths) == null) + throw CreateReadOnlyCollectionException ("System.Collections.Specialized.StringCollection"); + ob.@AddinPaths.Add (s5); + n4++; + } + else UnknownNode (null); + } + else UnknownNode (null); + + Reader.MoveToContent(); + } + ReadEndElement(); + } + b3 = true; + } + else if (Reader.LocalName == "RepositoryIdCount" && Reader.NamespaceURI == "" && !b1) { + b1 = true; + string s6 = Reader.ReadElementString (); + ob.@RepositoryIdCount = Int32.Parse (s6, CultureInfo.InvariantCulture); + } + else if (Reader.LocalName == "DisabledAddins" && Reader.NamespaceURI == "" && !b2) { + if (((object)ob.@DisabledAddins) == null) + throw CreateReadOnlyCollectionException ("System.Collections.Specialized.StringCollection"); + if (Reader.IsEmptyElement) { + Reader.Skip(); + } else { + int n7 = 0; + Reader.ReadStartElement(); + Reader.MoveToContent(); + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Addin" && Reader.NamespaceURI == "") { + string s8 = Reader.ReadElementString (); + if (((object)ob.@DisabledAddins) == null) + throw CreateReadOnlyCollectionException ("System.Collections.Specialized.StringCollection"); + ob.@DisabledAddins.Add (s8); + n7++; + } + else UnknownNode (null); + } + else UnknownNode (null); + + Reader.MoveToContent(); + } + ReadEndElement(); + } + b2 = true; + } + else if (Reader.LocalName == "Repositories" && Reader.NamespaceURI == "" && !b0) { + if (((object)ob.@Repositories) == null) + throw CreateReadOnlyCollectionException ("System.Collections.ArrayList"); + if (Reader.IsEmptyElement) { + Reader.Skip(); + } else { + int n9 = 0; + Reader.ReadStartElement(); + Reader.MoveToContent(); + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Repository" && Reader.NamespaceURI == "") { + if (((object)ob.@Repositories) == null) + throw CreateReadOnlyCollectionException ("System.Collections.ArrayList"); + ob.@Repositories.Add (ReadObject_RepositoryRecord (false, true)); + n9++; + } + else UnknownNode (null); + } + else UnknownNode (null); + + Reader.MoveToContent(); + } + ReadEndElement(); + } + b0 = true; + } + else { + UnknownNode (ob); + } + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + public Mono.Addins.Setup.RepositoryRecord ReadObject_RepositoryRecord (bool isNullable, bool checkType) + { + Mono.Addins.Setup.RepositoryRecord ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "RepositoryRecord" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Setup.RepositoryRecord) Activator.CreateInstance(typeof(Mono.Addins.Setup.RepositoryRecord), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (Reader.LocalName == "id" && Reader.NamespaceURI == "") { + ob.@Id = Reader.Value; + } + else if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + bool b10=false, b11=false, b12=false, b13=false, b14=false, b15=false; + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "File" && Reader.NamespaceURI == "" && !b11) { + b11 = true; + string s16 = Reader.ReadElementString (); + ob.@File = s16; + } + else if (Reader.LocalName == "Enabled" && Reader.NamespaceURI == "" && !b15) { + b15 = true; + string s17 = Reader.ReadElementString (); + ob.@Enabled = XmlConvert.ToBoolean (s17); + } + else if (Reader.LocalName == "IsReference" && Reader.NamespaceURI == "" && !b10) { + b10 = true; + string s18 = Reader.ReadElementString (); + ob.@IsReference = XmlConvert.ToBoolean (s18); + } + else if (Reader.LocalName == "Name" && Reader.NamespaceURI == "" && !b13) { + b13 = true; + string s19 = Reader.ReadElementString (); + ob.@Name = s19; + } + else if (Reader.LocalName == "Url" && Reader.NamespaceURI == "" && !b12) { + b12 = true; + string s20 = Reader.ReadElementString (); + ob.@Url = s20; + } + else if (Reader.LocalName == "LastModified" && Reader.NamespaceURI == "" && !b14) { + b14 = true; + string s21 = Reader.ReadElementString (); + ob.@LastModified = XmlConvert.ToDateTime (s21, XmlDateTimeSerializationMode.RoundtripKind); + } + else { + UnknownNode (ob); + } + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + protected override void InitCallbacks () + { + } + + protected override void InitIDs () + { + } + + } + + internal class AddinSystemConfigurationWriter : XmlSerializationWriter + { + const string xmlNamespace = "http://www.w3.org/2000/xmlns/"; + static readonly System.Reflection.MethodInfo toBinHexStringMethod = typeof (XmlConvert).GetMethod ("ToBinHexString", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, null, new Type [] {typeof (byte [])}, null); + static string ToBinHexString (byte [] input) + { + return input == null ? null : (string) toBinHexStringMethod.Invoke (null, new object [] {input}); + } + public void WriteRoot_AddinSystemConfiguration (object o) + { + WriteStartDocument (); + Mono.Addins.Setup.AddinSystemConfiguration ob = (Mono.Addins.Setup.AddinSystemConfiguration) o; + TopLevelElement (); + WriteObject_AddinSystemConfiguration (ob, "AddinSystemConfiguration", "", true, false, true); + } + + void WriteObject_AddinSystemConfiguration (Mono.Addins.Setup.AddinSystemConfiguration ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Setup.AddinSystemConfiguration)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("AddinSystemConfiguration", ""); + + if (ob.@Repositories != null) { + WriteStartElement ("Repositories", "", ob.@Repositories); + for (int n22 = 0; n22 < ob.@Repositories.Count; n22++) { + WriteObject_RepositoryRecord (((Mono.Addins.Setup.RepositoryRecord) ob.@Repositories[n22]), "Repository", "", false, false, true); + } + WriteEndElement (ob.@Repositories); + } + WriteElementString ("RepositoryIdCount", "", ob.@RepositoryIdCount.ToString(CultureInfo.InvariantCulture)); + if (ob.@DisabledAddins != null) { + WriteStartElement ("DisabledAddins", "", ob.@DisabledAddins); + for (int n23 = 0; n23 < ob.@DisabledAddins.Count; n23++) { + WriteElementString ("Addin", "", ob.@DisabledAddins[n23]); + } + WriteEndElement (ob.@DisabledAddins); + } + if (ob.@AddinPaths != null) { + WriteStartElement ("AddinPaths", "", ob.@AddinPaths); + for (int n24 = 0; n24 < ob.@AddinPaths.Count; n24++) { + WriteElementString ("Addin", "", ob.@AddinPaths[n24]); + } + WriteEndElement (ob.@AddinPaths); + } + if (writeWrappingElem) WriteEndElement (ob); + } + + void WriteObject_RepositoryRecord (Mono.Addins.Setup.RepositoryRecord ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Setup.RepositoryRecord)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("RepositoryRecord", ""); + + WriteAttribute ("id", "", ob.@Id); + + WriteElementString ("IsReference", "", (ob.@IsReference?"true":"false")); + WriteElementString ("File", "", ob.@File); + WriteElementString ("Url", "", ob.@Url); + WriteElementString ("Name", "", ob.@Name); + WriteElementString ("LastModified", "", XmlConvert.ToString (ob.@LastModified, XmlDateTimeSerializationMode.RoundtripKind)); + if (ob.@Enabled != true) { + WriteElementString ("Enabled", "", (ob.@Enabled?"true":"false")); + } + if (writeWrappingElem) WriteEndElement (ob); + } + + protected override void InitCallbacks () + { + } + } +} + diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfigurationSerializer.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfigurationSerializer.cs new file mode 100644 index 00000000..7ff6ff16 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/AddinSystemConfigurationSerializer.cs @@ -0,0 +1,64 @@ +// +// AddinSystemConfigurationSerializer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using System.Text; +using System.Collections; +using System.Globalization; + +namespace Mono.Addins.Setup +{ + internal class AddinSystemConfigurationSerializer : XmlSerializer + { + protected override void Serialize (object o, XmlSerializationWriter writer) + { + AddinSystemConfigurationWriter xsWriter = writer as AddinSystemConfigurationWriter; + xsWriter.WriteRoot_AddinSystemConfiguration (o); + } + + protected override object Deserialize (XmlSerializationReader reader) + { + AddinSystemConfigurationReader xsReader = reader as AddinSystemConfigurationReader; + return xsReader.ReadRoot_AddinSystemConfiguration (); + } + + protected override XmlSerializationWriter CreateWriter () + { + return new AddinSystemConfigurationWriter (); + } + + protected override XmlSerializationReader CreateReader () + { + return new AddinSystemConfigurationReader (); + } + } +} + diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/ConsoleAddinInstaller.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/ConsoleAddinInstaller.cs new file mode 100644 index 00000000..cdad98e9 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/ConsoleAddinInstaller.cs @@ -0,0 +1,135 @@ +// +// ConsoleAddinInstaller.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; + +namespace Mono.Addins.Setup +{ + /// + /// An IAddinInstaller implementation which interacts with the user through the console + /// + public class ConsoleAddinInstaller: IAddinInstaller + { + bool prompt; + bool repoUpdated; + int logLevel = 1; + + /// + /// Initializes a new instance of the class. + /// + public ConsoleAddinInstaller () + { + } + + /// + /// Gets or sets whether the installer can ask questions to the user + /// + public bool UserPrompt { + get { return prompt; } + set { + prompt = value; + if (prompt && logLevel == 0) + logLevel = 1; + } + } + + /// + /// Log level (0:normal, 1+:verbose); + /// + public int LogLevel { + get { return logLevel; } + set { logLevel = value; } + } + + void IAddinInstaller.InstallAddins (AddinRegistry reg, string message, string[] addinIds) + { + if (logLevel > 0) { + if (message != null && message.Length > 0) { + Console.WriteLine (message); + } else { + Console.WriteLine ("Additional extensions are required to perform this operation."); + } + } + ArrayList entries = new ArrayList (); + SetupService setup = new SetupService (reg); + string idNotFound; + do { + idNotFound = null; + foreach (string id in addinIds) { + string name = Addin.GetIdName (id); + string version = Addin.GetIdVersion (id); + AddinRepositoryEntry[] ares = setup.Repositories.GetAvailableAddin (name, version); + if (ares.Length == 0) { + idNotFound = id; + entries.Clear (); + break; + } else + entries.Add (ares[0]); + } + if (idNotFound != null) { + if (repoUpdated) + throw new InstallException ("Add-in '" + idNotFound + "' not found in the registered add-in repositories"); + if (prompt) { + Console.WriteLine ("The add-in '" + idNotFound + "' could not be found in the registered repositories."); + Console.WriteLine ("The repository indices may be outdated."); + if (!Confirm ("Do you wan't to update them now?")) + throw new InstallException ("Add-in '" + idNotFound + "' not found in the registered add-in repositories"); + } + setup.Repositories.UpdateAllRepositories (new ConsoleProgressStatus (logLevel)); + repoUpdated = true; + } + } + while (idNotFound != null); + + if (logLevel > 0) { + Console.WriteLine ("The following add-ins will be installed:"); + foreach (AddinRepositoryEntry addin in entries) + Console.WriteLine (" - " + addin.Addin.Name + " v" + addin.Addin.Version); + + if (prompt) { + if (!Confirm ("Do you want to continue with the installation?")) + throw new InstallException ("Installation cancelled"); + } + } + setup.Install (new ConsoleProgressStatus (logLevel), (AddinRepositoryEntry[]) entries.ToArray (typeof(AddinRepositoryEntry))); + } + + bool Confirm (string msg) + { + string res; + do { + Console.Write (msg + " (Y/n): "); + res = Console.ReadLine (); + if (res.Length > 0 && res.ToLower()[0] == 'n') + return false; + } while (res.Length > 0 && res.ToLower()[0] != 'y'); + return true; + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/IProgressMonitor.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/IProgressMonitor.cs new file mode 100644 index 00000000..12a5818c --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/IProgressMonitor.cs @@ -0,0 +1,51 @@ +// +// IProgressMonitor.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.IO; + +namespace Mono.Addins +{ + internal interface IProgressMonitor: IDisposable + { + void BeginTask (string name, int totalWork); + void BeginStepTask (string name, int totalWork, int stepSize); + void EndTask (); + void Step (int work); + + TextWriter Log { get; } + int LogLevel { get; } + + void ReportWarning (string message); + void ReportError (string message, Exception exception); + + bool IsCancelRequested { get; } + void Cancel (); + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/InstallException.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/InstallException.cs new file mode 100644 index 00000000..9358a7f6 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/InstallException.cs @@ -0,0 +1,61 @@ +// +// InstallException.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Addins.Setup +{ + /// + /// An installation exception + /// + public class InstallException: Exception + { + /// + /// Initializes the exception + /// + /// + /// Error message + /// + public InstallException (string msg): base (msg) + { + } + + /// + /// Initializes the exception + /// + /// + /// Error message + /// + /// + /// Inner exception + /// + public InstallException (string msg, Exception ex): base (msg, ex) + { + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/NativePackage.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/NativePackage.cs new file mode 100644 index 00000000..0cdc22d4 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/NativePackage.cs @@ -0,0 +1,73 @@ +// +// NativePackage.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2005 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Mono.Addins.Description; + +namespace Mono.Addins.Setup +{ + internal class NativePackage: Package + { + public override string Name { + get { return "Native package"; } + } + + internal override void PrepareInstall (IProgressMonitor monitor, AddinStore service) + { + } + + internal override void CommitInstall (IProgressMonitor monitor, AddinStore service) + { + } + + internal override void RollbackInstall (IProgressMonitor monitor, AddinStore service) + { + } + + internal override void Resolve (IProgressMonitor monitor, AddinStore service, PackageCollection toInstall, PackageCollection toUninstall, PackageCollection required, DependencyCollection unresolved) + { + } + + internal override void PrepareUninstall (IProgressMonitor monitor, AddinStore service) + { + } + + internal override void CommitUninstall (IProgressMonitor monitor, AddinStore service) + { + } + + internal override void RollbackUninstall (IProgressMonitor monitor, AddinStore service) + { + } + + internal override bool IsUpgradeOf (Package p) + { + return false; + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/Package.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/Package.cs new file mode 100644 index 00000000..fa972e17 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/Package.cs @@ -0,0 +1,116 @@ +// +// Package.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using Mono.Addins.Description; + +namespace Mono.Addins.Setup +{ + /// + /// An add-in package + /// + public abstract class Package + { + internal Package () + { + } + + /// + /// Name of the package + /// + public abstract string Name { get; } + + /// + /// Returns true if the package will be installed in the shared directory, + /// false if it will be installed in the user directory. + /// + public virtual bool SharedInstall { + get { return false; } + } + + /// + /// Creates a package object for an add-in available in an on-line repository + /// + /// + /// An add-in reference + /// + /// + /// The package + /// + public static Package FromRepository (AddinRepositoryEntry repAddin) + { + return AddinPackage.PackageFromRepository (repAddin); + } + + /// + /// Creates a package object for a local package file + /// + /// + /// Package file path + /// + /// + /// The package + /// + public static Package FromFile (string file) + { + return AddinPackage.PackageFromFile (file); + } + + + internal abstract void Resolve (IProgressMonitor monitor, AddinStore service, PackageCollection toInstall, PackageCollection toUninstall, PackageCollection required, DependencyCollection unresolved); + + internal abstract void PrepareInstall (IProgressMonitor monitor, AddinStore service); + internal abstract void CommitInstall (IProgressMonitor monitor, AddinStore service); + internal abstract void RollbackInstall (IProgressMonitor monitor, AddinStore service); + + internal abstract void PrepareUninstall (IProgressMonitor monitor, AddinStore service); + internal abstract void CommitUninstall (IProgressMonitor monitor, AddinStore service); + internal abstract void RollbackUninstall (IProgressMonitor monitor, AddinStore service); + + internal abstract bool IsUpgradeOf (Package p); + + internal virtual void EndInstall (IProgressMonitor monitor, AddinStore service) + { + } + + internal virtual void EndUninstall (IProgressMonitor monitor, AddinStore service) + { + } + + internal string CreateTempFolder () + { + string bname = Path.Combine (Path.GetTempPath (), "mdtmp"); + string tempFolder = bname; + int n = 0; + while (Directory.Exists (tempFolder)) + tempFolder = bname + (++n); + return tempFolder; + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/PackageCollection.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/PackageCollection.cs new file mode 100644 index 00000000..9f570e22 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/PackageCollection.cs @@ -0,0 +1,107 @@ +// +// PackageCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Specialized; + +namespace Mono.Addins.Setup +{ + /// + /// A collection of packages + /// + public class PackageCollection: CollectionBase + { + /// + /// Initializes a new instance of the class. + /// + public PackageCollection () + { + } + + /// + /// Copy constructor + /// + /// + /// Collection where to copy from + /// + public PackageCollection (ICollection col) + { + AddRange (col); + } + + /// + /// Gets a package + /// + /// + /// Package index + /// + public Package this [int n] { + get { return (Package) List [n]; } + } + + /// + /// Adds a package + /// + /// + /// A package + /// + public void Add (Package p) + { + List.Add (p); + } + + /// + /// Checks if a package is present in the collection + /// + /// + /// The package + /// + /// + /// True if the package is preent + /// + public bool Contains (Package p) + { + return List.Contains (p); + } + + /// + /// Adds a list of packages to the collection + /// + /// + /// The list of packages to add + /// + public void AddRange (ICollection col) + { + foreach (Package p in col) + Add (p); + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/PcFileCache.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/PcFileCache.cs new file mode 100644 index 00000000..8c0d8979 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/PcFileCache.cs @@ -0,0 +1,642 @@ +// +// PcFileCache.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Text; +using System.Xml; +using System.IO; +using System.Collections.Generic; + +namespace Mono.PkgConfig +{ + internal interface IPcFileCacheContext where TP:PackageInfo, new() + { + // In the implementation of this method, the host application can extract + // information from the pc file and store it in the PackageInfo object + void StoreCustomData (PcFile pcfile, TP pkg); + + // Should return false if the provided package does not have required + // custom data + bool IsCustomDataComplete (string pcfile, TP pkg); + + // Called to report errors + void ReportError (string message, Exception ex); + } + + internal interface IPcFileCacheContext: IPcFileCacheContext + { + } + + internal abstract class PcFileCache: PcFileCache + { + public PcFileCache (IPcFileCacheContext ctx): base (ctx) + { + } + } + + internal abstract class PcFileCache where TP:PackageInfo, new() + { + const string CACHE_VERSION = "2"; + + Dictionary infos = new Dictionary (); + Dictionary> filesByFolder = new Dictionary> (); + + string cacheFile; + bool hasChanges; + IPcFileCacheContext ctx; + IEnumerable defaultPaths; + + public PcFileCache (IPcFileCacheContext ctx) + { + this.ctx = ctx; + try { + string path = CacheDirectory; + if (!Directory.Exists (path)) + Directory.CreateDirectory (path); + cacheFile = Path.Combine (path, "pkgconfig-cache-" + CACHE_VERSION + ".xml"); + + if (File.Exists (cacheFile)) + Load (); + + } catch (Exception ex) { + ctx.ReportError ("pc file cache could not be loaded.", ex); + } + } + + protected abstract string CacheDirectory { get; } + + // Updates the pkg-config index, using the default search directories + public void Update () + { + Update (GetDefaultPaths ()); + } + + // Updates the pkg-config index, looking for .pc files in the provided directories + public void Update (IEnumerable pkgConfigDirs) + { + foreach (string pcdir in pkgConfigDirs) { + foreach (string pcfile in Directory.GetFiles (pcdir, "*.pc")) + GetPackageInfo (pcfile); + } + Save (); + } + + public IEnumerable GetPackages () + { + return GetPackages (null); + } + + public IEnumerable GetPackages (IEnumerable pkgConfigDirs) + { + if (pkgConfigDirs == null) + pkgConfigDirs = GetDefaultPaths (); + + foreach (string sp in pkgConfigDirs) { + List list; + if (filesByFolder.TryGetValue (Path.GetFullPath (sp), out list)) { + foreach (TP p in list) + yield return p; + } + } + } + + public TP GetPackageInfoByName (string name) + { + return GetPackageInfoByName (name, null); + } + + public TP GetPackageInfoByName (string name, IEnumerable pkgConfigDirs) + { + foreach (TP p in GetPackages (pkgConfigDirs)) + if (p.Name == name) + return p; + return null; + } + + // Returns information about a .pc file + public TP GetPackageInfo (string file) + { + TP info, oldInfo = null; + file = Path.GetFullPath (file); + + DateTime wtime = File.GetLastWriteTime (file); + + lock (infos) { + if (infos.TryGetValue (file, out info)) { + if (info.LastWriteTime == wtime) + return info; + oldInfo = info; + } + } + + try { + info = ParsePackageInfo (file); + } catch (Exception ex) { + ctx.ReportError ("Error while parsing .pc file", ex); + info = new TP (); + } + + lock (infos) { + if (!info.IsValidPackage) + info = new TP (); // Create a default empty instance + info.LastWriteTime = wtime; + Add (file, info, oldInfo); + hasChanges = true; + } + + return info; + } + + void Add (string file, TP info, TP replacedInfo) + { + infos [file] = info; + string dir = Path.GetFullPath (Path.GetDirectoryName (file)); + List list; + if (!filesByFolder.TryGetValue (dir, out list)) { + list = new List (); + filesByFolder [dir] = list; + } + if (replacedInfo != null) { + int i = list.IndexOf (replacedInfo); + if (i != -1) { + list [i] = info; + return; + } + } + list.Add (info); + } + + FileStream OpenFile (FileAccess access) + { + int retries = 6; + FileMode mode = access == FileAccess.Read ? FileMode.Open : FileMode.Create; + Exception lastException = null; + + while (retries > 0) { + try { + return new FileStream (cacheFile, mode, access, FileShare.None); + } catch (Exception ex) { + // the file may be locked by another app. Wait a bit and try again + lastException = ex; + System.Threading.Thread.Sleep (200); + retries--; + } + } + ctx.ReportError ("File could not be opened: " + cacheFile, lastException); + return null; + } + + void Load () + { + // The serializer can't be used because this file is reused in xbuild + using (FileStream fs = OpenFile (FileAccess.Read)) { + if (fs == null) + return; + XmlTextReader xr = new XmlTextReader (fs); + xr.MoveToContent (); + xr.ReadStartElement (); + xr.MoveToContent (); + + while (xr.NodeType == XmlNodeType.Element) + ReadPackage (xr); + } + } + + public void Save () + { + // The serializer can't be used because this file is reused in xbuild + lock (infos) { + if (!hasChanges) + return; + + using (FileStream fs = OpenFile (FileAccess.Write)) { + if (fs == null) + return; + XmlTextWriter tw = new XmlTextWriter (new StreamWriter (fs)); + tw.Formatting = Formatting.Indented; + + tw.WriteStartElement ("PcFileCache"); + foreach (KeyValuePair file in infos) { + WritePackage (tw, file.Key, file.Value); + } + tw.WriteEndElement (); // PcFileCache + tw.Flush (); + + hasChanges = false; + } + } + } + + void WritePackage (XmlTextWriter tw, string file, TP pinfo) + { + tw.WriteStartElement ("File"); + tw.WriteAttributeString ("path", file); + tw.WriteAttributeString ("lastWriteTime", XmlConvert.ToString (pinfo.LastWriteTime, XmlDateTimeSerializationMode.Local)); + + if (pinfo.IsValidPackage) { + if (pinfo.Name != null) + tw.WriteAttributeString ("name", pinfo.Name); + if (pinfo.Version != null) + tw.WriteAttributeString ("version", pinfo.Version); + if (!string.IsNullOrEmpty (pinfo.Description)) + tw.WriteAttributeString ("description", pinfo.Description); + if (pinfo.CustomData != null) { + foreach (KeyValuePair cd in pinfo.CustomData) + tw.WriteAttributeString (cd.Key, cd.Value); + } + WritePackageContent (tw, file, pinfo); + } + tw.WriteEndElement (); // File + } + + protected virtual void WritePackageContent (XmlTextWriter tw, string file, TP pinfo) + { + } + + void ReadPackage (XmlReader tr) + { + TP pinfo = new TP (); + string file = null; + + tr.MoveToFirstAttribute (); + do { + switch (tr.LocalName) { + case "path": file = tr.Value; break; + case "lastWriteTime": pinfo.LastWriteTime = XmlConvert.ToDateTime (tr.Value, XmlDateTimeSerializationMode.Local); break; + case "name": pinfo.Name = tr.Value; break; + case "version": pinfo.Version = tr.Value; break; + case "description": pinfo.Description = tr.Value; break; + default: pinfo.SetData (tr.LocalName, tr.Value); break; + } + } while (tr.MoveToNextAttribute ()); + + tr.MoveToElement (); + + if (!tr.IsEmptyElement) { + tr.ReadStartElement (); + tr.MoveToContent (); + ReadPackageContent (tr, pinfo); + tr.MoveToContent (); + tr.ReadEndElement (); + } else + tr.Read (); + tr.MoveToContent (); + + if (!pinfo.IsValidPackage || ctx.IsCustomDataComplete (file, pinfo)) + Add (file, pinfo, null); + } + + protected virtual void ReadPackageContent (XmlReader tr, TP pinfo) + { + } + + public object SyncRoot { + get { return infos; } + } + + + TP ParsePackageInfo (string pcfile) + { + PcFile file = new PcFile (); + file.Load (pcfile); + + TP pinfo = new TP (); + pinfo.Name = Path.GetFileNameWithoutExtension (file.FilePath); + + if (!file.HasErrors) { + pinfo.Version = file.Version; + pinfo.Description = file.Description; + ParsePackageInfo (file, pinfo); + ctx.StoreCustomData (file, pinfo); + } + return pinfo; + } + + protected virtual void ParsePackageInfo (PcFile file, TP pinfo) + { + } + + IEnumerable GetDefaultPaths () + { + if (defaultPaths == null) { + string pkgConfigPath = Environment.GetEnvironmentVariable ("PKG_CONFIG_PATH"); + string pkgConfigDir = Environment.GetEnvironmentVariable ("PKG_CONFIG_LIBDIR"); + defaultPaths = GetPkgconfigPaths (null, pkgConfigPath, pkgConfigDir); + } + return defaultPaths; + } + + public IEnumerable GetPkgconfigPaths (string prefix, string pkgConfigPath, string pkgConfigLibdir) + { + char[] sep = new char[] { Path.PathSeparator }; + + string[] pkgConfigPaths = null; + if (!String.IsNullOrEmpty (pkgConfigPath)) { + pkgConfigPaths = pkgConfigPath.Split (sep, StringSplitOptions.RemoveEmptyEntries); + if (pkgConfigPaths.Length == 0) + pkgConfigPaths = null; + } + + string[] pkgConfigLibdirs = null; + if (!String.IsNullOrEmpty (pkgConfigLibdir)) { + pkgConfigLibdirs = pkgConfigLibdir.Split (sep, StringSplitOptions.RemoveEmptyEntries); + if (pkgConfigLibdirs.Length == 0) + pkgConfigLibdirs = null; + } + + if (prefix == null) + prefix = PathUp (typeof (int).Assembly.Location, 4); + + IEnumerable paths = GetUnfilteredPkgConfigDirs (pkgConfigPaths, pkgConfigLibdirs, new string [] { prefix }); + return NormaliseAndFilterPaths (paths, Environment.CurrentDirectory); + } + + IEnumerable GetUnfilteredPkgConfigDirs (IEnumerable pkgConfigPaths, IEnumerable pkgConfigLibdirs, IEnumerable systemPrefixes) + { + if (pkgConfigPaths != null) { + foreach (string dir in pkgConfigPaths) + yield return dir; + } + + if (pkgConfigLibdirs != null) { + foreach (string dir in pkgConfigLibdirs) + yield return dir; + } else if (systemPrefixes != null) { + string[] suffixes = new string [] { + Path.Combine ("lib", "pkgconfig"), + Path.Combine ("lib64", "pkgconfig"), + Path.Combine ("libdata", "pkgconfig"), + Path.Combine ("share", "pkgconfig"), + }; + foreach (string prefix in systemPrefixes) + foreach (string suffix in suffixes) + yield return Path.Combine (prefix, suffix); + } + } + + IEnumerable NormaliseAndFilterPaths (IEnumerable paths, string workingDirectory) + { + Dictionary filtered = new Dictionary (); + foreach (string p in paths) { + string path = p; + if (!Path.IsPathRooted (path)) + path = Path.Combine (workingDirectory, path); + path = Path.GetFullPath (path); + if (filtered.ContainsKey (path)) + continue; + filtered.Add (path,path); + try { + if (!Directory.Exists (path)) + continue; + } catch (IOException ex) { + ctx.ReportError ("Error checking for directory '" + path + "'.", ex); + } + yield return path; + } + } + + static string PathUp (string path, int up) + { + if (up == 0) + return path; + for (int i = path.Length -1; i >= 0; i--) { + if (path[i] == Path.DirectorySeparatorChar) { + up--; + if (up == 0) + return path.Substring (0, i); + } + } + return null; + } + } + + internal class PcFile + { + Dictionary variables = new Dictionary (); + + string filePath; + string name; + string description; + string version; + string libs; + bool hasErrors; + + public string Description { + get { + return description; + } + set { + description = value; + } + } + + public string FilePath { + get { + return filePath; + } + set { + filePath = value; + } + } + + public bool HasErrors { + get { + return hasErrors; + } + set { + hasErrors = value; + } + } + + public string Libs { + get { + return libs; + } + set { + libs = value; + } + } + + public string Name { + get { + return name; + } + set { + name = value; + } + } + + public string Version { + get { + return version; + } + set { + version = value; + } + } + + public string GetVariable (string varName) + { + string val; + variables.TryGetValue (varName, out val); + return val; + } + + public void Load (string pcfile) + { + FilePath = pcfile; + variables.Add ("pcfiledir", Path.GetDirectoryName (pcfile)); + using (StreamReader reader = new StreamReader (pcfile)) { + string line; + while ((line = reader.ReadLine ()) != null) { + int i = line.IndexOf (':'); + int j = line.IndexOf ('='); + int k = System.Math.Min (i != -1 ? i : int.MaxValue, j != -1 ? j : int.MaxValue); + if (k == int.MaxValue) + continue; + string var = line.Substring (0, k).Trim (); + string value = line.Substring (k + 1).Trim (); + value = Evaluate (value); + + if (k == j) { + // Is variable + variables [var] = value; + } + else { + switch (var) { + case "Name": Name = value; break; + case "Description": Description = value; break; + case "Version": Version = value; break; + case "Libs": Libs = value; break; + } + } + } + } + } + + string Evaluate (string value) + { + int i = value.IndexOf ("${"); + if (i == -1) + return value; + + StringBuilder sb = new StringBuilder (); + int last = 0; + while (i != -1 && i < value.Length) { + sb.Append (value, last, i - last); + if (i == 0 || value [i - 1] != '$') { + // Evaluate if var is not escaped + i += 2; + int n = value.IndexOf ('}', i); + if (n == -1 || n == i) { + // Closing bracket not found or empty name + HasErrors = true; + return value; + } + string rname = value.Substring (i, n - i); + string rval; + if (variables.TryGetValue (rname, out rval)) + sb.Append (rval); + else { + HasErrors = true; + return value; + } + i = n + 1; + last = i; + } else + last = i++; + + if (i < value.Length - 1) + i = value.IndexOf ("${", i); + } + sb.Append (value, last, value.Length - last); + return sb.ToString (); + } + } + + internal class PackageInfo + { + Dictionary customData; + string name; + string version; + string description; + DateTime lastWriteTime; + + public string Name { + get { return name; } + set { name = value; } + } + + public string Version { + get { return version; } + set { version = value; } + } + + public string Description { + get { return description; } + set { description = value; } + } + + public string GetData (string name) + { + if (customData == null) + return null; + string res; + customData.TryGetValue (name, out res); + return res; + } + + public void SetData (string name, string value) + { + if (customData == null) + customData = new Dictionary (); + customData [name] = value; + } + + public void RemoveData (string name) + { + if (customData != null) + customData.Remove (name); + } + + internal Dictionary CustomData { + get { return customData; } + } + + internal DateTime LastWriteTime { + get { return lastWriteTime; } + set { lastWriteTime = value; } + } + + internal bool HasCustomData { + get { return customData != null && customData.Count > 0; } + } + + internal protected virtual bool IsValidPackage { + get { return HasCustomData; } + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/ReferenceRepositoryEntry.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/ReferenceRepositoryEntry.cs new file mode 100644 index 00000000..af178398 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/ReferenceRepositoryEntry.cs @@ -0,0 +1,43 @@ +// +// ReferenceRepositoryEntry.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + + +namespace Mono.Addins.Setup +{ + internal class ReferenceRepositoryEntry: RepositoryEntry + { + DateTime lastModified; + + public DateTime LastModified { + get { return lastModified; } + set { lastModified = value; } + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/Repository.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/Repository.cs new file mode 100644 index 00000000..22ea391a --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/Repository.cs @@ -0,0 +1,201 @@ +// +// Repository.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Xml; +using System.Xml.Serialization; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Mono.Addins.Setup +{ + internal class Repository + { + RepositoryEntryCollection repositories; + RepositoryEntryCollection addins; + string name; + internal string url; + + public string Name { + get { return name; } + set { name = value; } + } + + public string Url { + get { return url; } + set { url = value; } + } + + internal string CachedFilesDir { get; set; } + + [XmlElement ("Repository", Type = typeof(ReferenceRepositoryEntry))] + public RepositoryEntryCollection Repositories { + get { + if (repositories == null) + repositories = new RepositoryEntryCollection (this); + return repositories; + } + } + + [XmlElement ("Addin", Type = typeof(PackageRepositoryEntry))] + public RepositoryEntryCollection Addins { + get { + if (addins == null) + addins = new RepositoryEntryCollection (this); + return addins; + } + } + + public RepositoryEntry FindEntry (string url) + { + if (Repositories != null) { + foreach (RepositoryEntry e in Repositories) + if (e.Url == url) return e; + } + if (Addins != null) { + foreach (RepositoryEntry e in Addins) + if (e.Url == url) return e; + } + return null; + } + + public void AddEntry (RepositoryEntry entry) + { + entry.owner = this; + if (entry is ReferenceRepositoryEntry) { + Repositories.Add (entry); + } else { + Addins.Add (entry); + } + } + + public void RemoveEntry (RepositoryEntry entry) + { + if (entry is PackageRepositoryEntry) + Addins.Remove (entry); + else + Repositories.Remove (entry); + } + + public IAsyncResult BeginDownloadSupportFile (string name, AsyncCallback cb, object state) + { + FileAsyncResult res = new FileAsyncResult (); + res.AsyncState = state; + res.Callback = cb; + + string cachedFile = Path.Combine (CachedFilesDir, Path.GetFileName (name)); + if (File.Exists (cachedFile)) { + res.FilePath = cachedFile; + res.CompletedSynchronously = true; + res.SetDone (); + return res; + } + + Uri u = new Uri (new Uri (Url), name); + if (u.Scheme == "file") { + res.FilePath = u.AbsolutePath; + res.CompletedSynchronously = true; + res.SetDone (); + return res; + } + + res.FilePath = cachedFile; + WebRequestHelper.GetResponseAsync (() => (HttpWebRequest)WebRequest.Create (u)).ContinueWith (t => { + try { + var resp = t.Result; + string dir = Path.GetDirectoryName (res.FilePath); + lock (this) { + if (!Directory.Exists (dir)) + Directory.CreateDirectory (dir); + } + byte[] buffer = new byte [8092]; + using (var s = resp.GetResponseStream ()) { + using (var f = File.OpenWrite (res.FilePath)) { + int nr = 0; + while ((nr = s.Read (buffer, 0, buffer.Length)) > 0) + f.Write (buffer, 0, nr); + } + } + } catch (Exception ex) { + res.Error = ex; + } + }); + return res; + } + + public Stream EndDownloadSupportFile (IAsyncResult ares) + { + FileAsyncResult res = ares as FileAsyncResult; + if (res == null) + throw new InvalidOperationException ("Invalid IAsyncResult instance"); + if (res.Error != null) + throw res.Error; + return File.OpenRead (res.FilePath); + } + } + + class FileAsyncResult: IAsyncResult + { + ManualResetEvent done; + + public string FilePath; + public AsyncCallback Callback; + public Exception Error; + + public void SetDone () + { + lock (this) { + IsCompleted = true; + if (done != null) + done.Set (); + } + if (Callback != null) + Callback (this); + } + + public object AsyncState { get; set; } + + public WaitHandle AsyncWaitHandle { + get { + lock (this) { + if (done == null) + done = new ManualResetEvent (IsCompleted); + } + return done; + } + } + + public bool CompletedSynchronously { get; set; } + + public bool IsCompleted { get; set; } + } + +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryEntry.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryEntry.cs new file mode 100644 index 00000000..6ce998a4 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryEntry.cs @@ -0,0 +1,48 @@ +// +// RepositoryEntry.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + + +namespace Mono.Addins.Setup +{ + internal class RepositoryEntry + { + string url; + internal Repository owner; + + public string Url { + get { return url; } + set { url = value; } + } + + internal Repository Repository { + get { return owner; } + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryEntryCollection.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryEntryCollection.cs new file mode 100644 index 00000000..eca64723 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryEntryCollection.cs @@ -0,0 +1,67 @@ +// +// RepositoryEntryCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; + +namespace Mono.Addins.Setup +{ + internal class RepositoryEntryCollection: CollectionBase + { + Repository owner; + + internal RepositoryEntryCollection (Repository owner) + { + this.owner = owner; + } + + public RepositoryEntry this [int n] { + get { return (RepositoryEntry) List [n]; } + } + + public void Add (RepositoryEntry entry) + { + List.Add (entry); + } + + public void Remove (RepositoryEntry entry) + { + List.Remove (entry); + } + + protected override void OnInsert(int index, object value) + { + ((RepositoryEntry)value).owner = owner; + } + + protected override void OnSet(int index, object oldValue, object newValue) + { + ((RepositoryEntry)newValue).owner = owner; + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryReaderWriter.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryReaderWriter.cs new file mode 100644 index 00000000..f5745616 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryReaderWriter.cs @@ -0,0 +1,1033 @@ +// It is automatically generated +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using System.Text; +using System.Collections; +using System.Globalization; + +namespace Mono.Addins.Setup +{ + internal class RepositoryReader : XmlSerializationReader + { + static readonly System.Reflection.MethodInfo fromBinHexStringMethod = typeof (XmlConvert).GetMethod ("FromBinHexString", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, null, new Type [] {typeof (string)}, null); + static byte [] FromBinHexString (string input) + { + return input == null ? null : (byte []) fromBinHexStringMethod.Invoke (null, new object [] {input}); + } + public object ReadRoot_Repository () + { + Reader.MoveToContent(); + if (Reader.LocalName != "Repository" || Reader.NamespaceURI != "") + throw CreateUnknownNodeException(); + return ReadObject_Repository (true, true); + } + + public Mono.Addins.Setup.Repository ReadObject_Repository (bool isNullable, bool checkType) + { + Mono.Addins.Setup.Repository ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "Repository" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Setup.Repository) Activator.CreateInstance(typeof(Mono.Addins.Setup.Repository), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + bool b0=false, b1=false, b2=false, b3=false; + + Mono.Addins.Setup.RepositoryEntryCollection o5; + o5 = ob.@Repositories; + Mono.Addins.Setup.RepositoryEntryCollection o7; + o7 = ob.@Addins; + int n4=0, n6=0; + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Addin" && Reader.NamespaceURI == "" && !b3) { + if (((object)o7) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Setup.RepositoryEntryCollection"); + o7.Add (ReadObject_PackageRepositoryEntry (false, true)); + n6++; + } + else if (Reader.LocalName == "Repository" && Reader.NamespaceURI == "" && !b2) { + if (((object)o5) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Setup.RepositoryEntryCollection"); + o5.Add (ReadObject_ReferenceRepositoryEntry (false, true)); + n4++; + } + else if (Reader.LocalName == "Name" && Reader.NamespaceURI == "" && !b0) { + b0 = true; + string s8 = Reader.ReadElementString (); + ob.@Name = s8; + } + else if (Reader.LocalName == "Url" && Reader.NamespaceURI == "" && !b1) { + b1 = true; + string s9 = Reader.ReadElementString (); + ob.@Url = s9; + } + else { + UnknownNode (ob); + } + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + + ReadEndElement(); + + return ob; + } + + public Mono.Addins.Setup.PackageRepositoryEntry ReadObject_PackageRepositoryEntry (bool isNullable, bool checkType) + { + Mono.Addins.Setup.PackageRepositoryEntry ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "PackageRepositoryEntry" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Setup.PackageRepositoryEntry) Activator.CreateInstance(typeof(Mono.Addins.Setup.PackageRepositoryEntry), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + bool b10=false, b11=false; + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Addin" && Reader.NamespaceURI == "" && !b11) { + b11 = true; + ob.@Addin = ReadObject_AddinInfo (false, true); + } + else if (Reader.LocalName == "Url" && Reader.NamespaceURI == "" && !b10) { + b10 = true; + string s12 = Reader.ReadElementString (); + ob.@Url = s12; + } + else { + UnknownNode (ob); + } + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + public Mono.Addins.Setup.ReferenceRepositoryEntry ReadObject_ReferenceRepositoryEntry (bool isNullable, bool checkType) + { + Mono.Addins.Setup.ReferenceRepositoryEntry ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "ReferenceRepositoryEntry" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Setup.ReferenceRepositoryEntry) Activator.CreateInstance(typeof(Mono.Addins.Setup.ReferenceRepositoryEntry), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + bool b13=false, b14=false; + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Url" && Reader.NamespaceURI == "" && !b13) { + b13 = true; + string s15 = Reader.ReadElementString (); + ob.@Url = s15; + } + else if (Reader.LocalName == "LastModified" && Reader.NamespaceURI == "" && !b14) { + b14 = true; + string s16 = Reader.ReadElementString (); + ob.@LastModified = XmlConvert.ToDateTime (s16, XmlDateTimeSerializationMode.RoundtripKind); + } + else { + UnknownNode (ob); + } + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + public Mono.Addins.Setup.AddinInfo ReadObject_AddinInfo (bool isNullable, bool checkType) + { + Mono.Addins.Setup.AddinInfo ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "AddinInfo" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Setup.AddinInfo) Activator.CreateInstance(typeof(Mono.Addins.Setup.AddinInfo), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + bool b17=false, b18=false, b19=false, b20=false, b21=false, b22=false, b23=false, b24=false, b25=false, b26=false, b27=false, b28=false, b29=false; + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Version" && Reader.NamespaceURI == "" && !b20) { + b20 = true; + string s30 = Reader.ReadElementString (); + ob.@Version = s30; + } + else if (Reader.LocalName == "Dependencies" && Reader.NamespaceURI == "" && !b27) { + if (((object)ob.@Dependencies) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Description.DependencyCollection"); + if (Reader.IsEmptyElement) { + Reader.Skip(); + } else { + int n31 = 0; + Reader.ReadStartElement(); + Reader.MoveToContent(); + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "AssemblyDependency" && Reader.NamespaceURI == "") { + if (((object)ob.@Dependencies) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Description.DependencyCollection"); + ob.@Dependencies.Add (ReadObject_AssemblyDependency (false, true)); + n31++; + } + else if (Reader.LocalName == "NativeDependency" && Reader.NamespaceURI == "") { + if (((object)ob.@Dependencies) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Description.DependencyCollection"); + ob.@Dependencies.Add (ReadObject_NativeReference (false, true)); + n31++; + } + else if (Reader.LocalName == "AddinDependency" && Reader.NamespaceURI == "") { + if (((object)ob.@Dependencies) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Description.DependencyCollection"); + ob.@Dependencies.Add (ReadObject_AddinReference (false, true)); + n31++; + } + else UnknownNode (null); + } + else UnknownNode (null); + + Reader.MoveToContent(); + } + ReadEndElement(); + } + b27 = true; + } + else if (Reader.LocalName == "Name" && Reader.NamespaceURI == "" && !b19) { + b19 = true; + string s32 = Reader.ReadElementString (); + ob.@Name = s32; + } + else if (Reader.LocalName == "BaseVersion" && Reader.NamespaceURI == "" && !b21) { + b21 = true; + string s33 = Reader.ReadElementString (); + ob.@BaseVersion = s33; + } + else if (Reader.LocalName == "Id" && Reader.NamespaceURI == "" && !b17) { + b17 = true; + string s34 = Reader.ReadElementString (); + ob.@LocalId = s34; + } + else if (Reader.LocalName == "Url" && Reader.NamespaceURI == "" && !b24) { + b24 = true; + string s35 = Reader.ReadElementString (); + ob.@Url = s35; + } + else if (Reader.LocalName == "Copyright" && Reader.NamespaceURI == "" && !b23) { + b23 = true; + string s36 = Reader.ReadElementString (); + ob.@Copyright = s36; + } + else if (Reader.LocalName == "Description" && Reader.NamespaceURI == "" && !b25) { + b25 = true; + string s37 = Reader.ReadElementString (); + ob.@Description = s37; + } + else if (Reader.LocalName == "Author" && Reader.NamespaceURI == "" && !b22) { + b22 = true; + string s38 = Reader.ReadElementString (); + ob.@Author = s38; + } + else if (Reader.LocalName == "OptionalDependencies" && Reader.NamespaceURI == "" && !b28) { + if (((object)ob.@OptionalDependencies) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Description.DependencyCollection"); + if (Reader.IsEmptyElement) { + Reader.Skip(); + } else { + int n39 = 0; + Reader.ReadStartElement(); + Reader.MoveToContent(); + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "AssemblyDependency" && Reader.NamespaceURI == "") { + if (((object)ob.@OptionalDependencies) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Description.DependencyCollection"); + ob.@OptionalDependencies.Add (ReadObject_AssemblyDependency (false, true)); + n39++; + } + else if (Reader.LocalName == "NativeDependency" && Reader.NamespaceURI == "") { + if (((object)ob.@OptionalDependencies) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Description.DependencyCollection"); + ob.@OptionalDependencies.Add (ReadObject_NativeReference (false, true)); + n39++; + } + else if (Reader.LocalName == "AddinDependency" && Reader.NamespaceURI == "") { + if (((object)ob.@OptionalDependencies) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Description.DependencyCollection"); + ob.@OptionalDependencies.Add (ReadObject_AddinReference (false, true)); + n39++; + } + else UnknownNode (null); + } + else UnknownNode (null); + + Reader.MoveToContent(); + } + ReadEndElement(); + } + b28 = true; + } + else if (Reader.LocalName == "Properties" && Reader.NamespaceURI == "" && !b29) { + if (((object)ob.@Properties) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Setup.AddinPropertyCollectionImpl"); + if (Reader.IsEmptyElement) { + Reader.Skip(); + } else { + int n40 = 0; + Reader.ReadStartElement(); + Reader.MoveToContent(); + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Property" && Reader.NamespaceURI == "") { + if (((object)ob.@Properties) == null) + throw CreateReadOnlyCollectionException ("Mono.Addins.Setup.AddinPropertyCollectionImpl"); + ob.@Properties.Add (ReadObject_AddinProperty (false, true)); + n40++; + } + else UnknownNode (null); + } + else UnknownNode (null); + + Reader.MoveToContent(); + } + ReadEndElement(); + } + b29 = true; + } + else if (Reader.LocalName == "Namespace" && Reader.NamespaceURI == "" && !b18) { + b18 = true; + string s41 = Reader.ReadElementString (); + ob.@Namespace = s41; + } + else if (Reader.LocalName == "Category" && Reader.NamespaceURI == "" && !b26) { + b26 = true; + string s42 = Reader.ReadElementString (); + ob.@Category = s42; + } + else { + UnknownNode (ob); + } + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + public Mono.Addins.Description.AssemblyDependency ReadObject_AssemblyDependency (bool isNullable, bool checkType) + { + Mono.Addins.Description.AssemblyDependency ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "AssemblyDependency" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Description.AssemblyDependency) Activator.CreateInstance(typeof(Mono.Addins.Description.AssemblyDependency), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + bool b43=false, b44=false; + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Package" && Reader.NamespaceURI == "" && !b44) { + b44 = true; + string s45 = Reader.ReadElementString (); + ob.@Package = s45; + } + else if (Reader.LocalName == "FullName" && Reader.NamespaceURI == "" && !b43) { + b43 = true; + string s46 = Reader.ReadElementString (); + ob.@FullName = s46; + } + else { + UnknownNode (ob); + } + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + public Mono.Addins.Description.NativeDependency ReadObject_NativeReference (bool isNullable, bool checkType) + { + Mono.Addins.Description.NativeDependency ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "NativeReference" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Description.NativeDependency) Activator.CreateInstance(typeof(Mono.Addins.Description.NativeDependency), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + UnknownNode (ob); + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + public Mono.Addins.Description.AddinDependency ReadObject_AddinReference (bool isNullable, bool checkType) + { + Mono.Addins.Description.AddinDependency ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "AddinReference" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Description.AddinDependency) Activator.CreateInstance(typeof(Mono.Addins.Description.AddinDependency), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + bool b47=false, b48=false; + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (Reader.LocalName == "Version" && Reader.NamespaceURI == "" && !b48) { + b48 = true; + string s49 = Reader.ReadElementString (); + ob.@Version = s49; + } + else if (Reader.LocalName == "AddinId" && Reader.NamespaceURI == "" && !b47) { + b47 = true; + string s50 = Reader.ReadElementString (); + ob.@AddinId = s50; + } + else { + UnknownNode (ob); + } + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + public Mono.Addins.Description.AddinProperty ReadObject_AddinProperty (bool isNullable, bool checkType) + { + Mono.Addins.Description.AddinProperty ob = null; + if (isNullable && ReadNull()) return null; + + if (checkType) + { + System.Xml.XmlQualifiedName t = GetXsiType(); + if (t == null) + { } + else if (t.Name != "AddinProperty" || t.Namespace != "") + throw CreateUnknownTypeException(t); + } + + ob = (Mono.Addins.Description.AddinProperty) Activator.CreateInstance(typeof(Mono.Addins.Description.AddinProperty), true); + + Reader.MoveToElement(); + + while (Reader.MoveToNextAttribute()) + { + if (Reader.LocalName == "name" && Reader.NamespaceURI == "") { + ob.@Name = Reader.Value; + } + else if (Reader.LocalName == "locale" && Reader.NamespaceURI == "") { + ob.@Locale = Reader.Value; + } + else if (IsXmlnsAttribute (Reader.Name)) { + } + else { + UnknownNode (ob); + } + } + + Reader.MoveToElement (); + Reader.MoveToElement(); + if (Reader.IsEmptyElement) { + Reader.Skip (); + return ob; + } + + Reader.ReadStartElement(); + Reader.MoveToContent(); + + + while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) + { + if (Reader.NodeType == System.Xml.XmlNodeType.Element) + { + UnknownNode (ob); + } + else if (Reader.NodeType == System.Xml.XmlNodeType.Text || Reader.NodeType == System.Xml.XmlNodeType.CDATA) + { + ob.@Value = ReadString (ob.@Value); + } + else + UnknownNode(ob); + + Reader.MoveToContent(); + } + + ReadEndElement(); + + return ob; + } + + protected override void InitCallbacks () + { + } + + protected override void InitIDs () + { + } + + } + + internal class RepositoryWriter : XmlSerializationWriter + { + const string xmlNamespace = "http://www.w3.org/2000/xmlns/"; + static readonly System.Reflection.MethodInfo toBinHexStringMethod = typeof (XmlConvert).GetMethod ("ToBinHexString", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, null, new Type [] {typeof (byte [])}, null); + static string ToBinHexString (byte [] input) + { + return input == null ? null : (string) toBinHexStringMethod.Invoke (null, new object [] {input}); + } + public void WriteRoot_Repository (object o) + { + WriteStartDocument (); + Mono.Addins.Setup.Repository ob = (Mono.Addins.Setup.Repository) o; + TopLevelElement (); + WriteObject_Repository (ob, "Repository", "", true, false, true); + } + + void WriteObject_Repository (Mono.Addins.Setup.Repository ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Setup.Repository)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("Repository", ""); + + WriteElementString ("Name", "", ob.@Name); + WriteElementString ("Url", "", ob.@Url); + if (ob.@Repositories != null) { + for (int n51 = 0; n51 < ob.@Repositories.Count; n51++) { + WriteObject_ReferenceRepositoryEntry (((Mono.Addins.Setup.ReferenceRepositoryEntry) ob.@Repositories[n51]), "Repository", "", false, false, true); + } + } + if (ob.@Addins != null) { + for (int n52 = 0; n52 < ob.@Addins.Count; n52++) { + WriteObject_PackageRepositoryEntry (((Mono.Addins.Setup.PackageRepositoryEntry) ob.@Addins[n52]), "Addin", "", false, false, true); + } + } + if (writeWrappingElem) WriteEndElement (ob); + } + + void WriteObject_ReferenceRepositoryEntry (Mono.Addins.Setup.ReferenceRepositoryEntry ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Setup.ReferenceRepositoryEntry)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("ReferenceRepositoryEntry", ""); + + WriteElementString ("Url", "", ob.@Url); + WriteElementString ("LastModified", "", XmlConvert.ToString (ob.@LastModified, XmlDateTimeSerializationMode.RoundtripKind)); + if (writeWrappingElem) WriteEndElement (ob); + } + + void WriteObject_PackageRepositoryEntry (Mono.Addins.Setup.PackageRepositoryEntry ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Setup.PackageRepositoryEntry)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("PackageRepositoryEntry", ""); + + WriteElementString ("Url", "", ob.@Url); + WriteObject_AddinInfo (ob.@Addin, "Addin", "", false, false, true); + if (writeWrappingElem) WriteEndElement (ob); + } + + void WriteObject_AddinInfo (Mono.Addins.Setup.AddinInfo ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Setup.AddinInfo)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("AddinInfo", ""); + + WriteElementString ("Id", "", ob.@LocalId); + WriteElementString ("Namespace", "", ob.@Namespace); + WriteElementString ("Name", "", ob.@Name); + WriteElementString ("Version", "", ob.@Version); + WriteElementString ("BaseVersion", "", ob.@BaseVersion); + WriteElementString ("Author", "", ob.@Author); + WriteElementString ("Copyright", "", ob.@Copyright); + WriteElementString ("Url", "", ob.@Url); + WriteElementString ("Description", "", ob.@Description); + WriteElementString ("Category", "", ob.@Category); + if (ob.@Dependencies != null) { + WriteStartElement ("Dependencies", "", ob.@Dependencies); + for (int n53 = 0; n53 < ob.@Dependencies.Count; n53++) { + if (((object)ob.@Dependencies[n53]) == null) { } + else if (ob.@Dependencies[n53].GetType() == typeof(Mono.Addins.Description.AssemblyDependency)) { + WriteObject_AssemblyDependency (((Mono.Addins.Description.AssemblyDependency) ob.@Dependencies[n53]), "AssemblyDependency", "", false, false, true); + } + else if (ob.@Dependencies[n53].GetType() == typeof(Mono.Addins.Description.NativeDependency)) { + WriteObject_NativeReference (((Mono.Addins.Description.NativeDependency) ob.@Dependencies[n53]), "NativeDependency", "", false, false, true); + } + else if (ob.@Dependencies[n53].GetType() == typeof(Mono.Addins.Description.AddinDependency)) { + WriteObject_AddinReference (((Mono.Addins.Description.AddinDependency) ob.@Dependencies[n53]), "AddinDependency", "", false, false, true); + } + else throw CreateUnknownTypeException (ob.@Dependencies[n53]); + } + WriteEndElement (ob.@Dependencies); + } + if (ob.@OptionalDependencies != null) { + WriteStartElement ("OptionalDependencies", "", ob.@OptionalDependencies); + for (int n54 = 0; n54 < ob.@OptionalDependencies.Count; n54++) { + if (((object)ob.@OptionalDependencies[n54]) == null) { } + else if (ob.@OptionalDependencies[n54].GetType() == typeof(Mono.Addins.Description.AssemblyDependency)) { + WriteObject_AssemblyDependency (((Mono.Addins.Description.AssemblyDependency) ob.@OptionalDependencies[n54]), "AssemblyDependency", "", false, false, true); + } + else if (ob.@OptionalDependencies[n54].GetType() == typeof(Mono.Addins.Description.NativeDependency)) { + WriteObject_NativeReference (((Mono.Addins.Description.NativeDependency) ob.@OptionalDependencies[n54]), "NativeDependency", "", false, false, true); + } + else if (ob.@OptionalDependencies[n54].GetType() == typeof(Mono.Addins.Description.AddinDependency)) { + WriteObject_AddinReference (((Mono.Addins.Description.AddinDependency) ob.@OptionalDependencies[n54]), "AddinDependency", "", false, false, true); + } + else throw CreateUnknownTypeException (ob.@OptionalDependencies[n54]); + } + WriteEndElement (ob.@OptionalDependencies); + } + if (ob.@Properties != null) { + WriteStartElement ("Properties", "", ob.@Properties); + for (int n55 = 0; n55 < ob.@Properties.Count; n55++) { + WriteObject_AddinProperty (ob.@Properties[n55], "Property", "", false, false, true); + } + WriteEndElement (ob.@Properties); + } + if (writeWrappingElem) WriteEndElement (ob); + } + + void WriteObject_AssemblyDependency (Mono.Addins.Description.AssemblyDependency ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Description.AssemblyDependency)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("AssemblyDependency", ""); + + WriteElementString ("FullName", "", ob.@FullName); + WriteElementString ("Package", "", ob.@Package); + if (writeWrappingElem) WriteEndElement (ob); + } + + void WriteObject_NativeReference (Mono.Addins.Description.NativeDependency ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Description.NativeDependency)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("NativeReference", ""); + + if (writeWrappingElem) WriteEndElement (ob); + } + + void WriteObject_AddinReference (Mono.Addins.Description.AddinDependency ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Description.AddinDependency)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("AddinReference", ""); + + WriteElementString ("AddinId", "", ob.@AddinId); + WriteElementString ("Version", "", ob.@Version); + if (writeWrappingElem) WriteEndElement (ob); + } + + void WriteObject_AddinProperty (Mono.Addins.Description.AddinProperty ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) + { + if (((object)ob) == null) + { + if (isNullable) + WriteNullTagLiteral(element, namesp); + return; + } + + System.Type type = ob.GetType (); + if (type == typeof(Mono.Addins.Description.AddinProperty)) + { } + else { + throw CreateUnknownTypeException (ob); + } + + if (writeWrappingElem) { + WriteStartElement (element, namesp, ob); + } + + if (needType) WriteXsiType("AddinProperty", ""); + + WriteAttribute ("name", "", ob.@Name); + WriteAttribute ("locale", "", ob.@Locale); + + WriteValue (ob.@Value); + if (writeWrappingElem) WriteEndElement (ob); + } + + protected override void InitCallbacks () + { + } + + } +} + diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryRecord.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryRecord.cs new file mode 100644 index 00000000..cb9920ab --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryRecord.cs @@ -0,0 +1,174 @@ +// +// RepositoryRecord.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.IO; +using System.Xml; +using System.Xml.Serialization; + + +namespace Mono.Addins.Setup +{ + internal class RepositoryRecord: AddinRepository + { + string id; + bool isReference; + string file; + string url; + string name; + bool enabled = true; + DateTime lastModified = new DateTime (1900,1,1); + + [XmlAttribute ("id")] + public string Id { + get { return id; } + set { id = value; } + } + + public bool IsReference { + get { return isReference; } + set { isReference = value; } + } + + public string File { + get { return file; } + set { file = value; } + } + + public string CachedFilesDir { + get { + return Path.Combine (Path.GetDirectoryName (File), Path.GetFileNameWithoutExtension (File) + "_files"); + } + } + + public string Url { + get { return url; } + set { url = value; } + } + + public string Name { + get { return name; } + set { name = value; } + } + + public string Title { + get { return Name != null && Name != "" ? Name : Url; } + } + + public DateTime LastModified { + get { return lastModified; } + set { lastModified = value; } + } + + [System.ComponentModel.DefaultValue (true)] + public bool Enabled { + get { return this.enabled; } + set { enabled = value; } + } + + public Repository GetCachedRepository () + { + Repository repo = (Repository) AddinStore.ReadObject (File, typeof(Repository)); + if (repo != null) + repo.CachedFilesDir = CachedFilesDir; + return repo; + } + + public void ClearCachedRepository () + { + if (System.IO.File.Exists (File)) + System.IO.File.Delete (File); + if (Directory.Exists (CachedFilesDir)) + Directory.Delete (CachedFilesDir, true); + } + + internal void UpdateCachedRepository (Repository newRep) + { + newRep.url = Url; + if (newRep.Name == null) + newRep.Name = new Uri (Url).Host; + AddinStore.WriteObject (File, newRep); + if (name == null) + name = newRep.Name; + newRep.CachedFilesDir = CachedFilesDir; + } + } + + /// + /// An on-line add-in repository + /// + public interface AddinRepository + { + /// + /// Path to the cached add-in repository file + /// + string File { + get; + } + + /// + /// Url of the repository + /// + string Url { + get; + } + + /// + /// Do not use. Use Title instead. + /// + string Name { + get; + set; + } + + /// + /// Title of the repository + /// + string Title { + get; + } + + /// + /// Last change timestamp + /// + DateTime LastModified { + get; + } + + /// + /// Gets a value indicating whether this is enabled. + /// + /// + /// true if enabled; otherwise, false. + /// + bool Enabled { + get; + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryRegistry.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryRegistry.cs new file mode 100644 index 00000000..acbb1bd6 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositoryRegistry.cs @@ -0,0 +1,741 @@ +// +// RepositoryRegistry.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Linq; +using System.IO; +using System.Collections; +using Mono.Addins.Setup.ProgressMonitoring; +using System.Collections.Generic; + +namespace Mono.Addins.Setup +{ + /// + /// A registry of on-line repositories + /// + /// + /// This class can be used to manage on-line repository subscriptions. + /// + public class RepositoryRegistry + { + ArrayList repoList; + SetupService service; + + internal RepositoryRegistry (SetupService service) + { + this.service = service; + } + + /// + /// Subscribes to an on-line repository + /// + /// + /// Progress monitor where to show progress status and log + /// + /// + /// URL of the repository + /// + /// + /// A repository reference + /// + /// + /// The repository index is not downloaded by default. It can be downloaded + /// by calling UpdateRepository. + /// + public AddinRepository RegisterRepository (IProgressStatus monitor, string url) + { + return RegisterRepository (monitor, url, false); + } + + /// + /// Subscribes to an on-line repository + /// + /// + /// Progress monitor where to show progress status and log + /// + /// + /// URL of the repository + /// + /// + /// When set to True, the repository index will be downloaded. + /// + /// + /// A repository reference + /// + public AddinRepository RegisterRepository (IProgressStatus monitor, string url, bool updateNow) + { + if (string.IsNullOrEmpty (url)) + throw new ArgumentException ("Emtpy url"); + + if (!url.EndsWith (".mrep")) { + if (url [url.Length - 1] != '/') + url += "/"; + url = url + "main.mrep"; + } + + RepositoryRecord rr = FindRepositoryRecord (url); + if (rr != null) + return rr; + + rr = RegisterRepository (url, false); + + try { + if (updateNow) { + UpdateRepository (monitor, url); + rr = FindRepositoryRecord (url); + Repository rep = rr.GetCachedRepository (); + if (rep != null) + rr.Name = rep.Name; + } + service.SaveConfiguration (); + return rr; + } catch (Exception ex) { + if (monitor != null) + monitor.ReportError ("The repository could not be registered", ex); + if (ContainsRepository (url)) + RemoveRepository (url); + return null; + } + } + + internal RepositoryRecord RegisterRepository (string url, bool isReference) + { + RepositoryRecord rr = FindRepositoryRecord (url); + if (rr != null) { + if (rr.IsReference && !isReference) { + rr.IsReference = false; + service.SaveConfiguration (); + } + return rr; + } + + rr = new RepositoryRecord (); + rr.Url = url; + rr.IsReference = isReference; + + string name = service.RepositoryCachePath; + if (!Directory.Exists (name)) + Directory.CreateDirectory (name); + string host = new Uri (url).Host; + if (host.Length == 0) + host = "repo"; + name = Path.Combine (name, host); + rr.File = name + "_" + service.Configuration.RepositoryIdCount + ".mrep"; + + rr.Id = "rep" + service.Configuration.RepositoryIdCount; + service.Configuration.Repositories.Add (rr); + service.Configuration.RepositoryIdCount++; + service.SaveConfiguration (); + repoList = null; + return rr; + } + + internal RepositoryRecord FindRepositoryRecord (string url) + { + foreach (RepositoryRecord rr in service.Configuration.Repositories) + if (rr.Url == url) return rr; + return null; + } + + /// + /// Removes an on-line repository subscription. + /// + /// + /// URL of the repository. + /// + public void RemoveRepository (string url) + { + RepositoryRecord rep = FindRepositoryRecord (url); + if (rep == null) + return; // Nothing to do + + rep.IsReference = true; + PurgeUnusedRepositories (); + service.SaveConfiguration (); + repoList = null; + } + + void PurgeUnusedRepositories () + { + bool changed; + + do { + changed = false; + + HashSet referencedRepos = new HashSet (); + + // Get all referenced repos + foreach (RepositoryRecord rr in service.Configuration.Repositories) { + Repository repInfo = rr.GetCachedRepository (); + if (repInfo == null) + continue; + + foreach (ReferenceRepositoryEntry re in repInfo.Repositories) + referencedRepos.Add (new Uri (new Uri (repInfo.Url), re.Url).ToString ()); + } + + foreach (RepositoryRecord rr in service.Configuration.Repositories.ToArray ()) { + if (rr.IsReference && !referencedRepos.Contains (rr.Url)) { + changed = true; + service.Configuration.Repositories.Remove (rr); + rr.ClearCachedRepository (); + } + } + } + while (changed); + } + + /// + /// Enables or disables a repository + /// + /// + /// URL of the repository + /// + /// + /// 'true' if the repository has to be enabled. + /// + /// + /// Disabled repositories are ignored when calling UpdateAllRepositories. + /// + public void SetRepositoryEnabled (string url, bool enabled) + { + RepositoryRecord rep = FindRepositoryRecord (url); + if (rep == null) + return; // Nothing to do + rep.Enabled = enabled; + Repository crep = rep.GetCachedRepository (); + if (crep != null) { + foreach (RepositoryEntry re in crep.Repositories) + SetRepositoryEnabled (new Uri (new Uri (url), re.Url).ToString (), enabled); + } + + service.SaveConfiguration (); + } + + /// + /// Checks if a repository is already subscribed. + /// + /// + /// URL of the repository + /// + /// + /// True if the repository is already subscribed. + /// + public bool ContainsRepository (string url) + { + return FindRepositoryRecord (url) != null; + } + + ArrayList RepositoryList { + get { + if (repoList == null) { + ArrayList list = new ArrayList (); + foreach (RepositoryRecord rep in service.Configuration.Repositories) { + if (!rep.IsReference) + list.Add (rep); + } + repoList = list; + } + return repoList; + } + } + + /// + /// Gets a list of subscribed repositories + /// + /// + /// A list of repositories. + /// + public AddinRepository[] GetRepositories () + { + return (AddinRepository[]) RepositoryList.ToArray (typeof(AddinRepository)); + } + + /// + /// Updates the add-in index of all subscribed repositories. + /// + /// + /// Progress monitor where to show progress status and log + /// + public void UpdateAllRepositories (IProgressStatus monitor) + { + UpdateRepository (monitor, (string)null); + } + + /// + /// Updates the add-in index of the provided repository + /// + /// + /// Progress monitor where to show progress status and log + /// + /// + /// URL of the repository + /// + public void UpdateRepository (IProgressStatus statusMonitor, string url) + { + repoList = null; + + IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor); + + monitor.BeginTask ("Updating repositories", service.Configuration.Repositories.Count); + try { + int num = service.Configuration.Repositories.Count; + for (int n=0; n + /// Gets a list of available add-in updates. + /// + /// + /// A list of add-in references. + /// + /// + /// The list is generated by looking at the add-ins currently installed and checking if there is any + /// add-in with a newer version number in any of the subscribed repositories. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableUpdates () + { + return GetAvailableAddin (null, null, null, true, RepositorySearchFlags.None); + } + + /// + /// Gets a list of available add-in updates. + /// + /// + /// Search flags + /// + /// + /// A list of add-in references. + /// + /// + /// The list is generated by looking at the add-ins currently installed and checking if there is any + /// add-in with a newer version number in any of the subscribed repositories. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableUpdates (RepositorySearchFlags flags) + { + return GetAvailableAddin (null, null, null, true, flags); + } + + /// + /// Gets a list of available add-in updates in a specific repository. + /// + /// + /// The repository URL + /// + /// + /// A list of add-in references. + /// + /// + /// The list is generated by looking at the add-ins currently installed and checking if there is any + /// add-in with a newer version number in the provided repository. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableUpdates (string repositoryUrl) + { + return GetAvailableAddin (repositoryUrl, null, null, true, RepositorySearchFlags.None); + } + +#pragma warning disable 1591 + [Obsolete ("Use GetAvailableAddinUpdates (id) instead")] + public AddinRepositoryEntry[] GetAvailableUpdates (string id, string version) + { + return GetAvailableAddin (null, id, version, true, RepositorySearchFlags.None); + } + + [Obsolete ("Use GetAvailableAddinUpdates (repositoryUrl, id) instead")] + public AddinRepositoryEntry[] GetAvailableUpdates (string repositoryUrl, string id, string version) + { + return GetAvailableAddin (repositoryUrl, id, version, true, RepositorySearchFlags.None); + } +#pragma warning restore 1591 + + /// + /// Gets a list of available updates for an add-in. + /// + /// + /// Identifier of the add-in. + /// + /// + /// List of updates for the specified add-in. + /// + /// + /// The list is generated by checking if there is any + /// add-in with a newer version number in any of the subscribed repositories. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddinUpdates (string id) + { + return GetAvailableAddin (null, id, null, true, RepositorySearchFlags.None); + } + + /// + /// Gets a list of available updates for an add-in. + /// + /// + /// Identifier of the add-in. + /// + /// + /// Search flags. + /// + /// + /// List of updates for the specified add-in. + /// + /// + /// The list is generated by checking if there is any + /// add-in with a newer version number in any of the subscribed repositories. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddinUpdates (string id, RepositorySearchFlags flags) + { + return GetAvailableAddin (null, id, null, true, flags); + } + + /// + /// Gets a list of available updates for an add-in in a specific repository + /// + /// + /// Identifier of the add-in. + /// + /// + /// Identifier of the add-in. + /// + /// + /// List of updates for the specified add-in. + /// + /// + /// The list is generated by checking if there is any + /// add-in with a newer version number in the provided repository. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddinUpdates (string repositoryUrl, string id) + { + return GetAvailableAddin (repositoryUrl, id, null, true, RepositorySearchFlags.None); + } + + /// + /// Gets a list of available updates for an add-in in a specific repository + /// + /// + /// Identifier of the add-in. + /// + /// + /// Identifier of the add-in. + /// + /// + /// Search flags. + /// + /// + /// List of updates for the specified add-in. + /// + /// + /// The list is generated by checking if there is any + /// add-in with a newer version number in the provided repository. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddinUpdates (string repositoryUrl, string id, RepositorySearchFlags flags) + { + return GetAvailableAddin (repositoryUrl, id, null, true, flags); + } + + /// + /// Gets a list of all available add-ins + /// + /// + /// A list of add-ins + /// + /// + /// This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddins () + { + return GetAvailableAddin (null, null, null, false, RepositorySearchFlags.None); + } + + /// + /// Gets a list of all available add-ins + /// + /// + /// The available addins. + /// + /// + /// Search flags. + /// + /// + /// This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddins (RepositorySearchFlags flags) + { + return GetAvailableAddin (null, null, null, false, flags); + } + + /// + /// Gets a list of all available add-ins in a repository + /// + /// + /// A repository URL + /// + /// + /// A list of add-ins + /// + /// + /// This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddins (string repositoryUrl) + { + return GetAvailableAddin (repositoryUrl, null, null); + } + + /// + /// Gets a list of all available add-ins in a repository + /// + /// + /// A repository URL + /// + /// + /// Search flags. + /// + /// + /// A list of add-ins + /// + /// + /// This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddins (string repositoryUrl, RepositorySearchFlags flags) + { + return GetAvailableAddin (repositoryUrl, null, null, false, flags); + } + + /// + /// Checks if an add-in is available to be installed + /// + /// + /// Identifier of the add-in + /// + /// + /// Version of the add-in (optional, it can be null) + /// + /// + /// A list of add-ins + /// + /// + /// List of references to add-ins available in on-line repositories. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddin (string id, string version) + { + return GetAvailableAddin (null, id, version); + } + + /// + /// Checks if an add-in is available to be installed from a repository + /// + /// + /// A repository URL + /// + /// + /// Identifier of the add-in + /// + /// + /// Version of the add-in (optional, it can be null) + /// + /// + /// A list of add-ins + /// + /// + /// List of references to add-ins available in the repository. This method uses cached + /// information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + /// before using this method to ensure that the latest information is available. + /// + public AddinRepositoryEntry[] GetAvailableAddin (string repositoryUrl, string id, string version) + { + return GetAvailableAddin (repositoryUrl, id, version, false, RepositorySearchFlags.None); + } + + PackageRepositoryEntry[] GetAvailableAddin (string repositoryUrl, string id, string version, bool updates, RepositorySearchFlags flags) + { + List list = new List (); + + IEnumerable ee; + if (repositoryUrl != null) { + ArrayList repos = new ArrayList (); + GetRepositoryTree (repositoryUrl, repos); + ee = repos; + } else + ee = service.Configuration.Repositories; + + foreach (RepositoryRecord rr in ee) { + if (!rr.Enabled) + continue; + Repository rep = rr.GetCachedRepository(); + if (rep == null) continue; + foreach (PackageRepositoryEntry addin in rep.Addins) { + if ((id == null || Addin.GetIdName (addin.Addin.Id) == id) && (version == null || addin.Addin.Version == version)) { + if (updates) { + Addin ainfo = service.Registry.GetAddin (Addin.GetIdName (addin.Addin.Id)); + if (ainfo == null || Addin.CompareVersions (ainfo.Version, addin.Addin.Version) <= 0) + continue; + } + list.Add (addin); + } + } + } + + if ((flags & RepositorySearchFlags.LatestVersionsOnly) != 0) + FilterOldVersions (list); + + // Old versions are returned first + list.Sort (); + return list.ToArray (); + } + + void FilterOldVersions (List addins) + { + Dictionary versions = new Dictionary (); + foreach (PackageRepositoryEntry a in addins) { + string last; + string id, version; + Addin.GetIdParts (a.Addin.Id, out id, out version); + if (!versions.TryGetValue (id, out last) || Addin.CompareVersions (last, version) > 0) + versions [id] = version; + } + for (int n=0; n + /// Repository search flags. + /// + public enum RepositorySearchFlags + { + /// + /// No special search options + /// + None, + + /// + /// Only the latest version of every add-in is included in the search + /// + LatestVersionsOnly = 1, + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositorySerializer.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositorySerializer.cs new file mode 100644 index 00000000..53119154 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/RepositorySerializer.cs @@ -0,0 +1,64 @@ +// +// RepositorySerializer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using System.Text; +using System.Collections; +using System.Globalization; + +namespace Mono.Addins.Setup +{ + internal class RepositorySerializer : XmlSerializer + { + protected override void Serialize (object o, XmlSerializationWriter writer) + { + RepositoryWriter xsWriter = writer as RepositoryWriter; + xsWriter.WriteRoot_Repository (o); + } + + protected override object Deserialize (XmlSerializationReader reader) + { + RepositoryReader xsReader = reader as RepositoryReader; + return xsReader.ReadRoot_Repository (); + } + + protected override XmlSerializationWriter CreateWriter () + { + return new RepositoryWriter (); + } + + protected override XmlSerializationReader CreateReader () + { + return new RepositoryReader (); + } + } +} + diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/SetupService.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/SetupService.cs new file mode 100644 index 00000000..f6adef91 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/SetupService.cs @@ -0,0 +1,983 @@ +// +// SetupService.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Xml; +using ICSharpCode.SharpZipLib.Zip; +using Mono.Addins.Database; +using Mono.Addins.Description; +using Mono.Addins.Setup.ProgressMonitoring; +using Mono.PkgConfig; + +namespace Mono.Addins.Setup +{ + /// + /// Provides tools for managing add-ins + /// + /// + /// This class can be used to manage the add-ins of an application. It allows installing and uninstalling + /// add-ins, taking into account add-in dependencies. It provides methods for installing add-ins from on-line + /// repositories and tools for generating those repositories. + /// + public class SetupService + { + RepositoryRegistry repositories; + string applicationNamespace; + string installDirectory; + AddinStore store; + AddinSystemConfiguration config; + const string addinFilesDir = "_addin_files"; + + AddinRegistry registry; + + /// + /// Initializes a new instance + /// + /// + /// If the add-in manager is initialized (AddinManager.Initialize has been called), then this instance + /// will manage the add-in registry of the initialized engine. + /// + public SetupService () + { + if (AddinManager.IsInitialized) + registry = AddinManager.Registry; + else + registry = AddinRegistry.GetGlobalRegistry (); + + repositories = new RepositoryRegistry (this); + store = new AddinStore (this); + } + + /// + /// Initializes a new instance + /// + /// + /// Add-in registry to manage + /// + public SetupService (AddinRegistry registry) + { + this.registry = registry; + repositories = new RepositoryRegistry (this); + store = new AddinStore (this); + } + + /// + /// The add-in registry being managed + /// + public AddinRegistry Registry { + get { return registry; } + } + + internal string RepositoryCachePath { + get { return Path.Combine (registry.RegistryPath, "repository-cache"); } + } + + string RootConfigFile { + get { return Path.Combine (registry.RegistryPath, "addins-setup.config"); } + } + + /// + /// Default add-in namespace of the application (optional). If set, only add-ins that belong to that namespace + /// will be shown in add-in lists. + /// + public string ApplicationNamespace { + get { return applicationNamespace; } + set { applicationNamespace = value; } + } + + /// + /// Directory where to install add-ins. If not specified, the 'addins' subdirectory of the + /// registry location is used. + /// + public string InstallDirectory { + get { + if (installDirectory != null && installDirectory.Length > 0) + return installDirectory; + else + return registry.DefaultAddinsFolder; + } + set { installDirectory = value; } + } + + /// + /// Returns a RepositoryRegistry which can be used to manage on-line repository references + /// + public RepositoryRegistry Repositories { + get { return repositories; } + } + + internal AddinStore Store { + get { return store; } + } + + /// + /// Resolves add-in dependencies. + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// List of add-ins to check + /// + /// + /// Packages that need to be installed. + /// + /// + /// Packages that need to be uninstalled. + /// + /// + /// Add-in dependencies that could not be resolved. + /// + /// + /// True if all dependencies could be resolved. + /// + /// + /// This method can be used to get a list of all packages that have to be installed in order to install + /// an add-in or set of add-ins. The list of packages to install will include the package that provides the + /// add-in, and all packages that provide the add-in dependencies. In some cases, packages may need to + /// be installed (for example, when an installed add-in needs to be upgraded). + /// + public bool ResolveDependencies (IProgressStatus statusMonitor, AddinRepositoryEntry[] addins, out PackageCollection resolved, out PackageCollection toUninstall, out DependencyCollection unresolved) + { + return store.ResolveDependencies (statusMonitor, addins, out resolved, out toUninstall, out unresolved); + } + + /// + /// Resolves add-in dependencies. + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// Packages that need to be installed. + /// + /// + /// Packages that need to be uninstalled. + /// + /// + /// Add-in dependencies that could not be resolved. + /// + /// + /// True if all dependencies could be resolved. + /// + /// + /// This method can be used to get a list of all packages that have to be installed in order to satisfy + /// the dependencies of a package or set of packages. The 'packages' argument must have the list of packages + /// to be resolved. When resolving dependencies, if there is any additional package that needs to be installed, + /// it will be added to the same 'packages' collection. In some cases, packages may need to + /// be installed (for example, when an installed add-in needs to be upgraded). Those packages will be added + /// to the 'toUninstall' collection. Packages that could not be resolved are added to the 'unresolved' + /// collection. + /// + public bool ResolveDependencies (IProgressStatus statusMonitor, PackageCollection packages, out PackageCollection toUninstall, out DependencyCollection unresolved) + { + return store.ResolveDependencies (statusMonitor, packages, out toUninstall, out unresolved); + } + + /// + /// Installs add-in packages + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// Paths to the packages to install + /// + /// + /// True if the installation succeeded + /// + public bool Install (IProgressStatus statusMonitor, params string[] files) + { + return store.Install (statusMonitor, files); + } + + /// + /// Installs add-in packages from on-line repositories + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// References to the add-ins to be installed + /// + /// + /// True if the installation succeeded + /// + public bool Install (IProgressStatus statusMonitor, params AddinRepositoryEntry[] addins) + { + return store.Install (statusMonitor, addins); + } + + /// + /// Installs add-in packages + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// Packages to install + /// + /// + /// True if the installation succeeded + /// + public bool Install (IProgressStatus statusMonitor, PackageCollection packages) + { + return store.Install (statusMonitor, packages); + } + + /// + /// Uninstalls an add-in. + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// Full identifier of the add-in to uninstall. + /// + public void Uninstall (IProgressStatus statusMonitor, string id) + { + store.Uninstall (statusMonitor, id); + } + + /// + /// Uninstalls a set of add-ins + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// Full identifiers of the add-ins to uninstall. + /// + public void Uninstall (IProgressStatus statusMonitor, IEnumerable ids) + { + store.Uninstall (statusMonitor, ids); + } + + /// + /// Gets information about an add-in + /// + /// + /// The add-in + /// + /// + /// Add-in header data + /// + public static AddinHeader GetAddinHeader (Addin addin) + { + return AddinInfo.ReadFromDescription (addin.Description); + } + + /// + /// Gets a list of add-ins which depend on an add-in + /// + /// + /// Full identifier of an add-in. + /// + /// + /// When set to True, dependencies will be gathered recursivelly + /// + /// + /// List of dependent add-ins. + /// + /// + /// This methods returns a list of add-ins which have the add-in identified by 'id' as a direct + /// (or indirect if recursive=True) dependency. + /// + public Addin[] GetDependentAddins (string id, bool recursive) + { + return store.GetDependentAddins (id, recursive); + } + + /// + /// Packages an add-in + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// Directory where to generate the package + /// + /// + /// Paths to the add-ins to be packaged. Paths can be either the main assembly of an add-in, or an add-in + /// manifest (.addin or .addin.xml). + /// + /// + /// This method can be used to create a package for an add-in, which can then be pushed to an on-line + /// repository. The package will include the main assembly or manifest of the add-in and any external + /// file declared in the add-in metadata. + /// + public string[] BuildPackage (IProgressStatus statusMonitor, string targetDirectory, params string[] filePaths) + { + return BuildPackage (statusMonitor, false, targetDirectory, filePaths); + } + + /// + /// Packages an add-in + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// True if debug symbols (.pdb or .mdb) should be included in the package, if they exist + /// + /// + /// Directory where to generate the package + /// + /// + /// Paths to the add-ins to be packaged. Paths can be either the main assembly of an add-in, or an add-in + /// manifest (.addin or .addin.xml). + /// + /// + /// This method can be used to create a package for an add-in, which can then be pushed to an on-line + /// repository. The package will include the main assembly or manifest of the add-in and any external + /// file declared in the add-in metadata. + /// + public string[] BuildPackage (IProgressStatus statusMonitor, bool debugSymbols, string targetDirectory, params string[] filePaths) + { + List outFiles = new List (); + foreach (string file in filePaths) { + string f = BuildPackageInternal (statusMonitor, debugSymbols, targetDirectory, file); + if (f != null) + outFiles.Add (f); + } + return outFiles.ToArray (); + } + + string BuildPackageInternal (IProgressStatus monitor, bool debugSymbols, string targetDirectory, string filePath) + { + AddinDescription conf = registry.GetAddinDescription (monitor, filePath); + if (conf == null) { + monitor.ReportError ("Could not read add-in file: " + filePath, null); + return null; + } + + string basePath = Path.GetDirectoryName (Path.GetFullPath (filePath)); + + if (targetDirectory == null) + targetDirectory = basePath; + + // Generate the file name + + string name; + if (conf.LocalId.Length == 0) + name = Path.GetFileNameWithoutExtension (filePath); + else + name = conf.LocalId; + name = Addin.GetFullId (conf.Namespace, name, conf.Version); + name = name.Replace (',','_').Replace (".__", "."); + + string outFilePath = Path.Combine (targetDirectory, name) + ".mpack"; + + ZipOutputStream s = new ZipOutputStream (File.Create (outFilePath)); + s.SetLevel(5); + + // Generate a stripped down description of the add-in in a file, since the complete + // description may be declared as assembly attributes + + XmlDocument doc = new XmlDocument (); + doc.PreserveWhitespace = false; + doc.LoadXml (conf.SaveToXml ().OuterXml); + CleanDescription (doc.DocumentElement); + MemoryStream ms = new MemoryStream (); + XmlTextWriter tw = new XmlTextWriter (ms, System.Text.Encoding.UTF8); + tw.Formatting = Formatting.Indented; + doc.WriteTo (tw); + tw.Flush (); + byte[] data = ms.ToArray (); + + var infoEntry = new ZipEntry ("addin.info") { Size = data.Length }; + s.PutNextEntry (infoEntry); + s.Write (data, 0, data.Length); + s.CloseEntry (); + + // Now add the add-in files + + var files = new HashSet (); + + files.Add (Path.GetFileName (Util.NormalizePath (filePath))); + + foreach (string f in conf.AllFiles) { + var file = Util.NormalizePath (f); + files.Add (file); + if (debugSymbols) { + if (File.Exists (Path.ChangeExtension (file, ".pdb"))) + files.Add (Path.ChangeExtension (file, ".pdb")); + else if (File.Exists (file + ".mdb")) + files.Add (file + ".mdb"); + } + } + + foreach (var prop in conf.Properties) { + try { + var file = Util.NormalizePath (prop.Value); + if (File.Exists (Path.Combine (basePath, file))) { + files.Add (file); + } + } catch { + // Ignore errors + } + } + + //add satellite assemblies for assemblies in the list + var satelliteFinder = new SatelliteAssemblyFinder (); + foreach (var f in files.ToList ()) { + foreach (var satellite in satelliteFinder.FindSatellites (Path.Combine (basePath, f))) { + var relativeSatellite = satellite.Substring (basePath.Length + 1); + files.Add (relativeSatellite); + } + } + + monitor.Log ("Creating package " + Path.GetFileName (outFilePath)); + + foreach (string file in files) { + string fp = Path.Combine (basePath, file); + using (FileStream fs = File.OpenRead (fp)) { + byte[] buffer = new byte [fs.Length]; + fs.Read (buffer, 0, buffer.Length); + + var fileName = Path.PathSeparator == '\\' ? file.Replace ('\\', '/') : file; + var entry = new ZipEntry (fileName) { Size = fs.Length }; + s.PutNextEntry (entry); + s.Write (buffer, 0, buffer.Length); + s.CloseEntry (); + } + } + + s.Finish(); + s.Close(); + return outFilePath; + } + + class SatelliteAssemblyFinder + { + Dictionary> cultureSubdirCache = new Dictionary> (); + HashSet cultureNames = new HashSet (StringComparer.OrdinalIgnoreCase); + + public SatelliteAssemblyFinder () + { + foreach (var cultureName in CultureInfo.GetCultures (CultureTypes.AllCultures)) { + cultureNames.Add (cultureName.Name); + } + } + + List GetCultureSubdirectories (string directory) + { + if (!cultureSubdirCache.TryGetValue (directory, out List cultureDirs)) { + cultureDirs = Directory.EnumerateDirectories (directory) + .Where (d => cultureNames.Contains (Path.GetFileName ((d)))) + .ToList (); + + cultureSubdirCache [directory] = cultureDirs; + } + return cultureDirs; + } + + public IEnumerable FindSatellites (string assemblyPath) + { + if (!assemblyPath.EndsWith (".dll", StringComparison.OrdinalIgnoreCase)) { + yield break; + } + + var satelliteName = Path.GetFileNameWithoutExtension (assemblyPath) + ".resources.dll"; + + foreach (var cultureDir in GetCultureSubdirectories (Path.GetDirectoryName (assemblyPath))) { + string cultureName = Path.GetFileName (cultureDir); + string satellitePath = Path.Combine (cultureDir, satelliteName); + if (File.Exists (satellitePath)) { + yield return satellitePath; + } + } + } + } + + void CleanDescription (XmlElement parent) + { + ArrayList todelete = new ArrayList (); + + foreach (XmlNode nod in parent.ChildNodes) { + XmlElement elem = nod as XmlElement; + if (elem == null) { + todelete.Add (nod); + continue; + } + if (elem.LocalName == "Module") + CleanDescription (elem); + else if (elem.LocalName != "Dependencies" && elem.LocalName != "Runtime" && elem.LocalName != "Header") + todelete.Add (elem); + } + foreach (XmlNode e in todelete) + parent.RemoveChild (e); + } + + /// + /// Generates an on-line repository + /// + /// + /// Progress monitor where to show progress status + /// + /// + /// Path to the directory that contains the add-ins and that is going to be published + /// + /// + /// This method generates the index files required to publish a directory as an online repository + /// of add-ins. + /// + public void BuildRepository (IProgressStatus statusMonitor, string path) + { + string mainPath = Path.Combine (path, "main.mrep"); + ArrayList allAddins = new ArrayList (); + + Repository rootrep = (Repository) AddinStore.ReadObject (mainPath, typeof(Repository)); + if (rootrep == null) + rootrep = new Repository (); + + IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor); + BuildRepository (monitor, rootrep, path, "root.mrep", allAddins); + AddinStore.WriteObject (mainPath, rootrep); + GenerateIndexPage (rootrep, allAddins, path); + monitor.Log.WriteLine ("Updated main.mrep"); + } + + void BuildRepository (IProgressMonitor monitor, Repository rootrep, string rootPath, string relFilePath, ArrayList allAddins) + { + DateTime lastModified = DateTime.MinValue; + + string mainFile = Path.Combine (rootPath, relFilePath); + string mainPath = Path.GetDirectoryName (mainFile); + string supportFileDir = Path.Combine (mainPath, addinFilesDir); + + if (File.Exists (mainFile)) + lastModified = File.GetLastWriteTime (mainFile); + + Repository mainrep = (Repository) AddinStore.ReadObject (mainFile, typeof(Repository)); + if (mainrep == null) { + mainrep = new Repository (); + } + + ReferenceRepositoryEntry repEntry = (ReferenceRepositoryEntry) rootrep.FindEntry (relFilePath); + DateTime rootLastModified = repEntry != null ? repEntry.LastModified : DateTime.MinValue; + + bool modified = false; + + monitor.Log.WriteLine ("Checking directory: " + mainPath); + foreach (string file in Directory.GetFiles (mainPath, "*.mpack")) { + + DateTime date = File.GetLastWriteTime (file); + string fname = Path.GetFileName (file); + PackageRepositoryEntry entry = (PackageRepositoryEntry) mainrep.FindEntry (fname); + + if (entry != null && date > rootLastModified) { + mainrep.RemoveEntry (entry); + DeleteSupportFiles (supportFileDir, entry.Addin); + entry = null; + } + + if (entry == null) { + entry = new PackageRepositoryEntry (); + AddinPackage p = (AddinPackage) Package.FromFile (file); + entry.Addin = (AddinInfo) p.Addin; + entry.Url = fname; + entry.Addin.Properties.SetPropertyValue ("DownloadSize", new FileInfo (file).Length.ToString ()); + ExtractSupportFiles (supportFileDir, file, entry.Addin); + mainrep.AddEntry (entry); + modified = true; + monitor.Log.WriteLine ("Added addin: " + fname); + } + allAddins.Add (entry); + } + + ArrayList toRemove = new ArrayList (); + foreach (PackageRepositoryEntry entry in mainrep.Addins) { + if (!File.Exists (Path.Combine (mainPath, entry.Url))) { + toRemove.Add (entry); + modified = true; + } + } + + foreach (PackageRepositoryEntry entry in toRemove) { + DeleteSupportFiles (supportFileDir, entry.Addin); + mainrep.RemoveEntry (entry); + } + + if (modified) { + AddinStore.WriteObject (mainFile, mainrep); + monitor.Log.WriteLine ("Updated " + relFilePath); + lastModified = File.GetLastWriteTime (mainFile); + } + + if (repEntry != null) { + if (repEntry.LastModified < lastModified) + repEntry.LastModified = lastModified; + } else if (modified) { + repEntry = new ReferenceRepositoryEntry (); + repEntry.LastModified = lastModified; + repEntry.Url = relFilePath; + rootrep.AddEntry (repEntry); + } + + foreach (string dir in Directory.GetDirectories (mainPath)) { + if (Path.GetFileName (dir) == addinFilesDir) + continue; + string based = dir.Substring (rootPath.Length + 1); + BuildRepository (monitor, rootrep, rootPath, Path.Combine (based, "main.mrep"), allAddins); + } + } + + void DeleteSupportFiles (string targetDir, AddinInfo ainfo) + { + foreach (var prop in ainfo.Properties) { + if (prop.Value.StartsWith (addinFilesDir + Path.DirectorySeparatorChar)) { + string file = Path.Combine (targetDir, Path.GetFileName (prop.Value)); + if (File.Exists (file)) + File.Delete (file); + } + } + if (Directory.Exists (targetDir) && Directory.GetFileSystemEntries (targetDir).Length == 0) + Directory.Delete (targetDir, true); + } + + void ExtractSupportFiles (string targetDir, string file, AddinInfo ainfo) + { + Random r = new Random (); + ZipFile zfile = new ZipFile (file); + try { + foreach (var prop in ainfo.Properties) { + ZipEntry ze = zfile.GetEntry (prop.Value); + if (ze != null) { + string fname; + do { + fname = Path.Combine (targetDir, r.Next ().ToString ("x") + Path.GetExtension (prop.Value)); + } while (File.Exists (fname)); + + if (!Directory.Exists (targetDir)) + Directory.CreateDirectory (targetDir); + + using (var f = File.OpenWrite (fname)) { + using (Stream s = zfile.GetInputStream (ze)) { + byte [] buffer = new byte [8092]; + int nr = 0; + while ((nr = s.Read (buffer, 0, buffer.Length)) > 0) + f.Write (buffer, 0, nr); + } + } + prop.Value = Path.Combine (addinFilesDir, Path.GetFileName (fname)); + } + } + } finally { + zfile.Close (); + } + } + + void GenerateIndexPage (Repository rep, ArrayList addins, string basePath) + { + StreamWriter sw = new StreamWriter (Path.Combine (basePath, "index.html")); + sw.WriteLine (""); + sw.WriteLine ("

Add-in Repository

"); + if (rep.Name != null && rep.Name != "") + sw.WriteLine ("

" + rep.Name + "

"); + sw.WriteLine ("

This is a list of add-ins available in this repository.

"); + sw.WriteLine (""); + + foreach (PackageRepositoryEntry entry in addins) { + sw.WriteLine (""); + } + + sw.WriteLine ("
Add-inVersionDescription
" + entry.Addin.Name + "" + entry.Addin.Version + "" + entry.Addin.Description + "
"); + sw.WriteLine (""); + sw.Close (); + } + + internal AddinSystemConfiguration Configuration { + get { + if (config == null) { + config = (AddinSystemConfiguration) AddinStore.ReadObject (RootConfigFile, typeof(AddinSystemConfiguration)); + if (config == null) + config = new AddinSystemConfiguration (); + } + return config; + } + } + + internal void SaveConfiguration () + { + if (config != null) { + AddinStore.WriteObject (RootConfigFile, config); + } + } + + internal void ResetConfiguration () + { + if (File.Exists (RootConfigFile)) + File.Delete (RootConfigFile); + ResetAddinInfo (); + } + + internal void ResetAddinInfo () + { + if (Directory.Exists (RepositoryCachePath)) + Directory.Delete (RepositoryCachePath, true); + } + + /// + /// Gets a reference to an extensible application + /// + /// + /// Name of the application + /// + /// + /// The Application object. Null if not found. + /// + public static Application GetExtensibleApplication (string name) + { + return GetExtensibleApplication (name, null); + } + + /// + /// Gets a reference to an extensible application + /// + /// + /// Name of the application + /// + /// + /// Custom paths where to look for the application. + /// + /// + /// The Application object. Null if not found. + /// + public static Application GetExtensibleApplication (string name, IEnumerable searchPaths) + { + AddinsPcFileCache pcc = GetAddinsPcFileCache (searchPaths); + PackageInfo pi = pcc.GetPackageInfoByName (name, searchPaths); + if (pi != null) + return new Application (pi); + else + return null; + } + + /// + /// Gets a lis of all known extensible applications + /// + /// + /// A list of applications. + /// + public static Application[] GetExtensibleApplications () + { + return GetExtensibleApplications (null); + } + + /// + /// Gets a lis of all known extensible applications + /// + /// + /// Custom paths where to look for applications. + /// + /// + /// A list of applications. + /// + public static Application[] GetExtensibleApplications (IEnumerable searchPaths) + { + List list = new List (); + + AddinsPcFileCache pcc = GetAddinsPcFileCache (searchPaths); + foreach (PackageInfo pinfo in pcc.GetPackages (searchPaths)) { + if (pinfo.IsValidPackage) + list.Add (new Application (pinfo)); + } + return list.ToArray (); + } + + static AddinsPcFileCache pcFileCache; + + static AddinsPcFileCache GetAddinsPcFileCache (IEnumerable searchPaths) + { + if (pcFileCache == null) { + pcFileCache = new AddinsPcFileCache (); + if (searchPaths != null) + pcFileCache.Update (searchPaths); + else + pcFileCache.Update (); + } + return pcFileCache; + } + } + + class AddinsPcFileCacheContext: IPcFileCacheContext + { + public bool IsCustomDataComplete (string pcfile, PackageInfo pkg) + { + return true; + } + + public void StoreCustomData (Mono.PkgConfig.PcFile pcfile, PackageInfo pkg) + { + } + + public void ReportError (string message, System.Exception ex) + { + Console.WriteLine (message); + Console.WriteLine (ex); + } + } + + class AddinsPcFileCache: PcFileCache + { + public AddinsPcFileCache (): base (new AddinsPcFileCacheContext ()) + { + } + + protected override string CacheDirectory { + get { + string path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); + path = Path.Combine (path, "mono.addins"); + return path; + } + } + + protected override void ParsePackageInfo (PcFile file, PackageInfo pinfo) + { + string rootPath = file.GetVariable ("MonoAddinsRoot"); + string regPath = file.GetVariable ("MonoAddinsRegistry"); + string addinsPath = file.GetVariable ("MonoAddinsInstallPath"); + string databasePath = file.GetVariable ("MonoAddinsCachePath"); + string testCmd = file.GetVariable ("MonoAddinsTestCommand"); + if (string.IsNullOrEmpty (rootPath) || string.IsNullOrEmpty (regPath)) + return; + pinfo.SetData ("MonoAddinsRoot", rootPath); + pinfo.SetData ("MonoAddinsRegistry", regPath); + pinfo.SetData ("MonoAddinsInstallPath", addinsPath); + pinfo.SetData ("MonoAddinsCachePath", databasePath); + pinfo.SetData ("MonoAddinsTestCommand", testCmd); + } + } + + /// + /// A registered extensible application + /// + public class Application + { + AddinRegistry registry; + string description; + string name; + string testCommand; + string startupPath; + string registryPath; + string addinsPath; + string databasePath; + + internal Application (PackageInfo pinfo) + { + name = pinfo.Name; + description = pinfo.Description; + startupPath = pinfo.GetData ("MonoAddinsRoot"); + registryPath = pinfo.GetData ("MonoAddinsRegistry"); + addinsPath = pinfo.GetData ("MonoAddinsInstallPath"); + databasePath = pinfo.GetData ("MonoAddinsCachePath"); + testCommand = pinfo.GetData ("MonoAddinsTestCommand"); + } + + /// + /// Add-in registry of the application + /// + public AddinRegistry Registry { + get { + if (registry == null) + registry = new AddinRegistry (RegistryPath, StartupPath, AddinsPath, AddinCachePath); + return registry; + } + } + + /// + /// Description of the application + /// + public string Description { + get { + return description; + } + } + + /// + /// Name of the application + /// + public string Name { + get { + return name; + } + } + + /// + /// Path to the add-in registry + /// + public string RegistryPath { + get { + return registryPath; + } + } + + /// + /// Path to the directory that contains the main executable assembly of the application + /// + public string StartupPath { + get { + return startupPath; + } + } + + /// + /// Command to be used to execute the application in add-in development mode. + /// + public string TestCommand { + get { + return testCommand; + } + } + + /// + /// Path to the default add-ins directory for the aplpication + /// + public string AddinsPath { + get { + return addinsPath; + } + } + + /// + /// Path to the add-in cache for the application + /// + public string AddinCachePath { + get { + return databasePath; + } + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/SetupTool.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/SetupTool.cs new file mode 100644 index 00000000..ab678a72 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/SetupTool.cs @@ -0,0 +1,1198 @@ +// +// mdsetup.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Xml; +using System.Collections; +using Mono.Addins; +using Mono.Addins.Setup.ProgressMonitoring; +using Mono.Addins.Setup; +using System.IO; +using Mono.Addins.Description; +using System.Linq; + +namespace Mono.Addins.Setup +{ + /// + /// A command line add-in manager. + /// + /// + /// This class can be used to provide an add-in management command line tool to applications. + /// + public class SetupTool + { + Hashtable options = new Hashtable (); + string[] arguments; + string applicationName = "Mono"; + SetupService service; + AddinRegistry registry; + ArrayList commands = new ArrayList (); + string setupAppName = ""; + int uniqueId = 0; + + int verbose = 1; + + /// + /// Creates a new instance + /// + /// + /// Add-in registry to manage. + /// + public SetupTool (AddinRegistry registry) + { + this.registry = registry; + service = new SetupService (registry); + CreateCommands (); + } + + /// + /// Display name of the host application + /// + public string ApplicationName { + get { return applicationName; } + set { applicationName = value; } + } + + /// + /// Default add-in namespace of the application (optional). If set, only add-ins that belong to that namespace + /// will be shown in add-in lists. + /// + public string ApplicationNamespace { + get { return service.ApplicationNamespace; } + set { service.ApplicationNamespace = value; } + } + + /// + /// Enables or disables verbose output + /// + public bool VerboseOutput { + get { return verbose > 1; } + set { verbose = value ? 2 : 1; } + } + + /// + /// Sets or gets the verbose output level (0: normal output, 1:verbose, 2+:extra verbose) + /// + public int VerboseOutputLevel { + get { return verbose; } + set { verbose = value; } + } + + /// + /// Runs the command line tool. + /// + /// + /// Array that contains the command line arguments + /// + /// + /// Index of the arguments array that has the first argument for the management tool + /// + /// + /// 0 if it succeeds. != 0 otherwise + /// + public int Run (string[] args, int firstArgumentIndex) + { + string[] aa = new string [args.Length - firstArgumentIndex]; + Array.Copy (args, firstArgumentIndex, aa, 0, aa.Length); + return Run (aa); + } + + /// + /// Runs the command line tool. + /// + /// + /// Command line arguments + /// + /// + /// 0 if it succeeds. != 0 otherwise + /// + public int Run (string[] args) + { + if (args.Length == 0) { + PrintHelp (); + return 0; + } + + string[] parms = new string [args.Length - 1]; + Array.Copy (args, 1, parms, 0, args.Length - 1); + + try { + ReadOptions (parms); + if (HasOption ("v")) + verbose++; + return RunCommand (args [0], parms); + } catch (InstallException ex) { + Console.WriteLine (ex.Message); + return -1; + } + } + + int RunCommand (string cmd, string[] parms) + { + SetupCommand cc = FindCommand (cmd); + if (cc != null) { + cc.Handler (parms); + return 0; + } + else { + Console.WriteLine ("Unknown command: " + cmd); + return 1; + } + } + + void Install (string[] args) + { + bool prompt = !args.Any (a => a == "-y"); + var addins = args.Where (a => a != "-y"); + + if (!addins.Any ()) { + PrintHelp ("install"); + return; + } + + PackageCollection packs = new PackageCollection (); + foreach (string arg in addins) { + if (File.Exists (arg)) { + packs.Add (AddinPackage.FromFile (arg)); + } else { + string aname = Addin.GetIdName (GetFullId (arg)); + string aversion = Addin.GetIdVersion (arg); + if (aversion.Length == 0) aversion = null; + + AddinRepositoryEntry[] ads = service.Repositories.GetAvailableAddin (aname, aversion); + if (ads.Length == 0) + throw new InstallException ("The addin '" + arg + "' is not available for install."); + packs.Add (AddinPackage.FromRepository (ads[ads.Length-1])); + } + } + Install (packs, prompt); + } + + void CheckInstall (string[] args) + { + if (args.Length < 1) { + PrintHelp ("check-install"); + return; + } + + PackageCollection packs = new PackageCollection (); + for (int n=0; n a == "-y"); + var addins = args.Where (a => a != "-y"); + + if (!addins.Any ()) + throw new InstallException ("The add-in id is required."); + if (addins.Count () > 1) + throw new InstallException ("Only one add-in id can be provided."); + + string id = addins.First (); + Addin ads = registry.GetAddin (GetFullId (id)); + if (ads == null) + throw new InstallException ("The add-in '" + id + "' is not installed."); + if (!ads.Description.CanUninstall) + throw new InstallException ("The add-in '" + id + "' is protected and can't be uninstalled."); + + if (prompt) { + Console.WriteLine ("The following add-ins will be uninstalled:"); + Console.WriteLine (" - " + ads.Description.Name); + foreach (Addin si in service.GetDependentAddins (id, true)) + Console.WriteLine (" - " + si.Description.Name); + + Console.WriteLine (); + Console.Write ("Are you sure you want to continue? (y/N): "); + string res = Console.ReadLine (); + if (res != "y" && res != "Y") + return; + } + service.Uninstall (new ConsoleProgressStatus (verbose), ads.Id); + } + + bool IsHidden (Addin ainfo) + { + return service.ApplicationNamespace != null && !(ainfo.Namespace + ".").StartsWith (service.ApplicationNamespace + ".") || ainfo.Description.IsHidden; + } + + bool IsHidden (AddinHeader ainfo) + { + return service.ApplicationNamespace != null && !(ainfo.Namespace + ".").StartsWith (service.ApplicationNamespace + "."); + } + + string GetId (AddinHeader ainfo) + { + if (service.ApplicationNamespace != null && (ainfo.Namespace + ".").StartsWith (service.ApplicationNamespace + ".")) + return ainfo.Id.Substring (service.ApplicationNamespace.Length + 1); + else + return ainfo.Id; + } + + string GetFullId (string id) + { + if (service.ApplicationNamespace != null) + return service.ApplicationNamespace + "." + id; + else + return id; + } + + void ListInstalled (string[] args) + { + IList alist = args; + bool showAll = alist.Contains ("-a"); + Console.WriteLine ("Installed add-ins:"); + ArrayList list = new ArrayList (); + list.AddRange (registry.GetAddins ()); + if (alist.Contains ("-r")) + list.AddRange (registry.GetAddinRoots ()); + foreach (Addin addin in list) { + if (!showAll && IsHidden (addin)) + continue; + Console.Write (" - " + addin.Name + " " + addin.Version); + if (showAll) + Console.Write (" (" + addin.AddinFile + ")"); + Console.WriteLine (); + } + } + + void ListAvailable (string[] args) + { + bool showAll = args.Length > 0 && args [0] == "-a"; + Console.WriteLine ("Available add-ins:"); + AddinRepositoryEntry[] addins = service.Repositories.GetAvailableAddins (); + foreach (PackageRepositoryEntry addin in addins) { + if (!showAll && IsHidden (addin.Addin)) + continue; + Console.WriteLine (" - " + GetId (addin.Addin) + " (" + addin.Repository.Name + ")"); + } + } + + void ListUpdates (string[] args) + { + bool showAll = args.Length > 0 && args [0] == "-a"; + + Console.WriteLine ("Looking for updates..."); + service.Repositories.UpdateAllRepositories (null); + Console.WriteLine ("Available add-in updates:"); + AddinRepositoryEntry[] addins = service.Repositories.GetAvailableAddins (); + bool found = false; + foreach (PackageRepositoryEntry addin in addins) { + Addin sinfo = registry.GetAddin (addin.Addin.Id); + if (!showAll && IsHidden (sinfo)) + continue; + if (sinfo != null && Addin.CompareVersions (sinfo.Version, addin.Addin.Version) == 1) { + Console.WriteLine (" - " + addin.Addin.Id + " " + addin.Addin.Version + " (" + addin.Repository.Name + ")"); + found = true; + } + } + if (!found) + Console.WriteLine ("No updates found."); + } + + void Update (string [] args) + { + bool showAll = args.Length > 0 && args [0] == "-a"; + + Console.WriteLine ("Looking for updates..."); + service.Repositories.UpdateAllRepositories (null); + + PackageCollection packs = new PackageCollection (); + AddinRepositoryEntry[] addins = service.Repositories.GetAvailableAddins (); + foreach (PackageRepositoryEntry addin in addins) { + Addin sinfo = registry.GetAddin (addin.Addin.Id); + if (!showAll && IsHidden (sinfo)) + continue; + if (sinfo != null && Addin.CompareVersions (sinfo.Version, addin.Addin.Version) == 1) + packs.Add (AddinPackage.FromRepository (addin)); + } + if (packs.Count > 0) + Install (packs, true); + else + Console.WriteLine ("No updates found."); + } + + void UpdateAvailableAddins (string[] args) + { + service.Repositories.UpdateAllRepositories (new ConsoleProgressStatus (verbose)); + } + + void AddRepository (string[] args) + { + foreach (string rep in args) + service.Repositories.RegisterRepository (new ConsoleProgressStatus (verbose), rep); + } + + string GetRepositoryUrl (string url) + { + AddinRepository[] reps = GetRepositoryList (); + int nr; + if (int.TryParse (url, out nr)) { + if (nr < 0 || nr >= reps.Length) + throw new InstallException ("Invalid repository number."); + return reps[nr].Url; + } else { + if (!service.Repositories.ContainsRepository (url)) + throw new InstallException ("Repository not registered."); + return url; + } + } + + void RemoveRepository (string[] args) + { + foreach (string rep in args) { + service.Repositories.RemoveRepository (GetRepositoryUrl (rep)); + } + } + + void EnableRepository (string[] args) + { + foreach (string rep in args) + service.Repositories.SetRepositoryEnabled (GetRepositoryUrl(rep), true); + } + + void DisableRepository (string[] args) + { + foreach (string rep in args) + service.Repositories.SetRepositoryEnabled (GetRepositoryUrl(rep), false); + } + + AddinRepository[] GetRepositoryList () + { + AddinRepository[] reps = service.Repositories.GetRepositories (); + Array.Sort (reps, (r1,r2) => r1.Title.CompareTo(r2.Title)); + return reps; + } + + void ListRepositories (string[] args) + { + AddinRepository[] reps = GetRepositoryList (); + if (reps.Length == 0) { + Console.WriteLine ("No repositories have been registered."); + return; + } + int n = 0; + Console.WriteLine ("Registered repositories:"); + foreach (RepositoryRecord rep in reps) { + string num = n.ToString (); + Console.Write (num + ") "); + if (!rep.Enabled) + Console.Write ("(Disabled) "); + Console.WriteLine (rep.Title); + if (rep.Title != rep.Url) + Console.WriteLine (new string (' ', num.Length + 2) + rep.Url); + n++; + } + } + + void BuildRepository (string[] args) + { + if (args.Length < 1) + throw new InstallException ("A directory name is required."); + service.BuildRepository (new ConsoleProgressStatus (verbose), args[0]); + } + + void BuildPackage (string[] args) + { + if (args.Length < 1) + throw new InstallException ("A file name is required."); + + service.BuildPackage (new ConsoleProgressStatus (verbose), bool.Parse (GetOption ("debugSymbols", "false")), GetOption ("d", "."), GetArguments ()); + } + + void PrintLibraries (string[] args) + { + if (GetArguments ().Length < 1) + throw new InstallException ("An add-in id is required."); + + bool refFormat = HasOption ("r"); + + System.Text.StringBuilder sb = new System.Text.StringBuilder (); + foreach (string id in GetArguments ()) { + Addin addin = service.Registry.GetAddin (id); + if (addin != null) { + foreach (string asm in addin.Description.MainModule.Assemblies) { + string file = Path.Combine (addin.Description.BasePath, asm); + if (sb.Length > 0) + sb.Append (' '); + if (refFormat) + sb.Append ("-r:"); + sb.Append (file); + } + } + } + Console.WriteLine (sb); + } + + void PrintApplications (string[] args) + { + foreach (Application app in SetupService.GetExtensibleApplications ()) { + string line = app.Name; + if (!string.IsNullOrEmpty (app.Description)) + line += " - " + app.Description; + Console.WriteLine (line); + } + } + + void UpdateRegistry (string[] args) + { + registry.Update (new ConsoleProgressStatus (verbose)); + } + + void RepairRegistry (string[] args) + { + registry.Rebuild (new ConsoleProgressStatus (verbose)); + } + + void DumpRegistryFile (string[] args) + { + if (args.Length < 1) + throw new InstallException ("A file name is required."); + registry.DumpFile (args[0]); + } + + void PrintAddinInfo (string[] args) + { + bool generateXml = false; + bool pickNamespace = false; + bool extensionModel = true; + + ArrayList addins = new ArrayList (); + ArrayList namespaces = new ArrayList (); + + bool generateAll = args [0] == "--all"; + if (!generateAll) { + AddinDescription desc = null; + if (File.Exists (args [0])) + desc = registry.GetAddinDescription (new ConsoleProgressStatus (verbose), args [0]); + else { + Addin addin = registry.GetAddin (args [0]); + if (addin != null) + desc = addin.Description; + } + if (desc == null) + throw new InstallException (string.Format ("Add-in '{0}' not found.", args [0])); + if (desc != null) + addins.Add (desc); + } + + for (int i = 1; i < args.Length; i++) { + string a = args [i]; + + if (a == "--all") + throw new InstallException (string.Format ("--all needs to be the first parameter")); + + if (pickNamespace) { + namespaces.Add (a); + pickNamespace = false; + continue; + } + if (a == "--xml") { + generateXml = true; + continue; + } + if (a == "--namespace" || a == "-n") { + pickNamespace = true; + continue; + } + if (a == "--full") { + extensionModel = false; + continue; + } + } + + if (generateAll) { + ArrayList list = new ArrayList (); + list.AddRange (registry.GetAddinRoots ()); + list.AddRange (registry.GetAddins ()); + foreach (Addin addin in list) { + if (namespaces.Count > 0) { + foreach (string ns in namespaces) { + if (addin.Id.StartsWith (ns + ".")) { + addins.Add (addin.Description); + break; + } + } + } else { + addins.Add (addin.Description); + } + } + } + + if (addins.Count == 0) + throw new InstallException ("A file name or add-in ID is required."); + + + if (generateXml) { + XmlTextWriter tw = new XmlTextWriter (Console.Out); + tw.Formatting = Formatting.Indented; + tw.WriteStartElement ("Addins"); + foreach (AddinDescription desc in addins) { + if (extensionModel && desc.ExtensionPoints.Count == 0) + continue; + PrintAddinXml (tw, desc); + } + tw.Close (); + } + else { + foreach (AddinDescription des in addins) + PrintAddin (des); + } + } + + void PrintAddinXml (XmlWriter tw, AddinDescription desc) + { + tw.WriteStartElement ("Addin"); + tw.WriteAttributeString ("name", desc.Name); + tw.WriteAttributeString ("addinId", desc.LocalId); + tw.WriteAttributeString ("fullId", desc.AddinId); + tw.WriteAttributeString ("id", "addin_" + uniqueId); + uniqueId++; + if (desc.Namespace.Length > 0) + tw.WriteAttributeString ("namespace", desc.Namespace); + tw.WriteAttributeString ("isroot", desc.IsRoot.ToString ()); + + tw.WriteAttributeString ("version", desc.Version); + if (desc.CompatVersion.Length > 0) + tw.WriteAttributeString ("compatVersion", desc.CompatVersion); + + if (desc.Author.Length > 0) + tw.WriteAttributeString ("author", desc.Author); + if (desc.Category.Length > 0) + tw.WriteAttributeString ("category", desc.Category); + if (desc.Copyright.Length > 0) + tw.WriteAttributeString ("copyright", desc.Copyright); + if (desc.Url.Length > 0) + tw.WriteAttributeString ("url", desc.Url); + + if (desc.Description.Length > 0) + tw.WriteElementString ("Description", desc.Description); + + if (desc.ExtensionPoints.Count > 0) { + ArrayList list = new ArrayList (); + Hashtable visited = new Hashtable (); + foreach (ExtensionPoint ep in desc.ExtensionPoints) { + tw.WriteStartElement ("ExtensionPoint"); + tw.WriteAttributeString ("path", ep.Path); + if (ep.Name.Length > 0) + tw.WriteAttributeString ("name", ep.Name); + else + tw.WriteAttributeString ("name", ep.Path); + if (ep.Description.Length > 0) + tw.WriteElementString ("Description", ep.Description); + PrintExtensionNodeSetXml (tw, desc, ep.NodeSet, list, visited); + tw.WriteEndElement (); + } + + for (int n=0; n 0) + tw.WriteElementString ("Description", nt.Description); + + if (nt.Attributes.Count > 0) { + tw.WriteStartElement ("Attributes"); + foreach (NodeTypeAttribute att in nt.Attributes) { + tw.WriteStartElement ("Attribute"); + tw.WriteAttributeString ("name", att.Name); + tw.WriteAttributeString ("type", att.Type); + tw.WriteAttributeString ("required", att.Required.ToString ()); + tw.WriteAttributeString ("localizable", att.Localizable.ToString ()); + if (att.Description.Length > 0) + tw.WriteElementString ("Description", att.Description); + tw.WriteEndElement (); + } + tw.WriteEndElement (); + } + + if (nt.NodeTypes.Count > 0 || nt.NodeSets.Count > 0) { + tw.WriteStartElement ("ChildNodes"); + PrintExtensionNodeSetXml (tw, desc, nt, list, visited); + tw.WriteEndElement (); + } + tw.WriteEndElement (); + } + } + tw.WriteEndElement (); + } + + void PrintExtensionNodeSetXml (XmlWriter tw, AddinDescription desc, ExtensionNodeSet nset, ArrayList list, Hashtable visited) + { + foreach (ExtensionNodeType nt in nset.GetAllowedNodeTypes ()) { + tw.WriteStartElement ("ExtensionNode"); + tw.WriteAttributeString ("name", nt.Id); + string id = RegisterNodeXml (nt, list, visited); + tw.WriteAttributeString ("id", id.ToString ()); + if (nt.Description.Length > 0) + tw.WriteElementString ("Description", nt.Description); + tw.WriteEndElement (); + } + } + + string RegisterNodeXml (ExtensionNodeType nt, ArrayList list, Hashtable visited) + { + string key = nt.Id + " " + nt.TypeName; + if (visited.Contains (key)) + return (string) visited [key]; + string k = "ntype_" + uniqueId; + uniqueId++; + visited [key] = k; + list.Add (nt); + return k; + } + + void PrintAddin (AddinDescription desc) + { + Console.WriteLine (); + Console.WriteLine ("Addin Header"); + Console.WriteLine ("------------"); + Console.WriteLine (); + Console.WriteLine ("Name: " + desc.Name); + Console.WriteLine ("Id: " + desc.LocalId); + if (desc.Namespace.Length > 0) + Console.WriteLine ("Namespace: " + desc.Namespace); + + Console.Write ("Version: " + desc.Version); + if (desc.CompatVersion.Length > 0) + Console.WriteLine (" (compatible with: " + desc.CompatVersion + ")"); + else + Console.WriteLine (); + + if (desc.AddinFile.Length > 0) + Console.WriteLine ("File: " + desc.AddinFile); + if (desc.Author.Length > 0) + Console.WriteLine ("Author: " + desc.Author); + if (desc.Category.Length > 0) + Console.WriteLine ("Category: " + desc.Category); + if (desc.Copyright.Length > 0) + Console.WriteLine ("Copyright: " + desc.Copyright); + if (desc.Url.Length > 0) + Console.WriteLine ("Url: " + desc.Url); + + if (desc.Description.Length > 0) { + Console.WriteLine (); + Console.WriteLine ("Description: \n" + desc.Description); + } + + if (desc.ExtensionPoints.Count > 0) { + Console.WriteLine (); + Console.WriteLine ("Extenstion Points"); + Console.WriteLine ("-----------------"); + foreach (ExtensionPoint ep in desc.ExtensionPoints) + PrintExtensionPoint (desc, ep); + } + } + + void PrintExtensionPoint (AddinDescription desc, ExtensionPoint ep) + { + Console.WriteLine (); + Console.WriteLine ("* Extension Point: " + ep.Path); + if (ep.Description.Length > 0) + Console.WriteLine (ep.Description); + + ArrayList list = new ArrayList (); + Hashtable visited = new Hashtable (); + + Console.WriteLine (); + Console.WriteLine (" Extension nodes:"); + GetNodes (desc, ep.NodeSet, list, new Hashtable ()); + + foreach (ExtensionNodeType nt in list) + Console.WriteLine (" - " + nt.Id + ": " + nt.Description); + + Console.WriteLine (); + Console.WriteLine (" Node description:"); + + string sind = " "; + + for (int n=0; n 0) { + Console.WriteLine (nsind + "Attributes:"); + foreach (NodeTypeAttribute att in nt.Attributes) { + string req = att.Required ? " (required)" : ""; + Console.WriteLine (nsind + " " + att.Name + " (" + att.Type + "): " + att.Description + req); + } + } + + if (nt.NodeTypes.Count > 0 || nt.NodeSets.Count > 0) { + Console.WriteLine (nsind + "Child nodes:"); + ArrayList newList = new ArrayList (); + GetNodes (desc, nt, newList, new Hashtable ()); + list.AddRange (newList); + foreach (ExtensionNodeType cnt in newList) + Console.WriteLine (" " + cnt.Id + ": " + cnt.Description); + } + } + Console.WriteLine (); + } + + void GetNodes (AddinDescription desc, ExtensionNodeSet nset, ArrayList list, Hashtable visited) + { + if (visited.Contains (nset)) + return; + visited.Add (nset, nset); + + foreach (ExtensionNodeType nt in nset.NodeTypes) { + if (!visited.Contains (nt.Id + " " + nt.TypeName)) { + list.Add (nt); + visited.Add (nt.Id + " " + nt.TypeName, nt); + } + } + + foreach (string nsid in nset.NodeSets) { + ExtensionNodeSet rset = desc.ExtensionNodeSets [nsid]; + if (rset != null) + GetNodes (desc, rset, list, visited); + } + } + + string[] GetArguments () + { + return arguments; + } + + bool HasOption (string key) + { + return options.Contains (key); + } + + string GetOption (string key, string defValue) + { + object val = options [key]; + if (val == null || val == (object) this) + return defValue; + else + return (string) val; + } + + void ReadOptions (string[] args) + { + options = new Hashtable (); + ArrayList list = new ArrayList (); + + foreach (string arg in args) { + if (arg.StartsWith ("-")) { + int i = arg.IndexOf (':'); + if (i == -1) + options [arg.Substring (1)] = this; + else + options [arg.Substring (1, i-1)] = arg.Substring (i+1); + } else + list.Add (arg); + } + + arguments = (string[]) list.ToArray (typeof(string)); + } + + /// + /// Adds a custom command to the add-in manager + /// + /// + /// Category under which the command has to be shown in the help text + /// + /// + /// Name of the command + /// + /// + /// Short name of the command (it's an alias of the normal name) + /// + /// + /// Formal description of the arguments that the command accepts. For example: "[addin-id|addin-file] [--xml] [--all] [--full] [--namespace <namespace>]" + /// + /// + /// Short description of the command + /// + /// + /// Long description of the command + /// + /// + /// Delegate to be invoked to run the command + /// + public void AddCommand (string category, string command, string shortName, string arguments, string description, string longDescription, SetupCommandHandler handler) + { + SetupCommand cmd = new SetupCommand (category, command, shortName, handler); + cmd.Usage = arguments; + cmd.Description = description; + cmd.LongDescription = longDescription; + + int lastCatPos = -1; + for (int n=0; n + /// Prints help about the add-in management tool, or about a specific command + /// + /// + /// Optional command name and arguments + /// + public void PrintHelp (params string[] parms) + { + if (parms.Length == 0) { + string lastCat = null; + foreach (SetupCommand cmd in commands) { + if (cmd.Command == "help") + continue; + if (lastCat != cmd.Category) { + Console.WriteLine (); + Console.WriteLine (cmd.Category + ":"); + lastCat = cmd.Category; + } + string cc = cmd.CommandDesc; + if (cc.Length < 16) + cc += new string (' ', 16 - cc.Length); + Console.WriteLine (" " + cc + " " + cmd.Description); + } + Console.WriteLine (); + Console.WriteLine ("Run '" + setupAppName + "help ' to get help about a specific command."); + Console.WriteLine (); + return; + } + else { + Console.WriteLine (); + SetupCommand cmd = FindCommand (parms [0]); + if (cmd != null) { + Console.WriteLine ("{0}: {1}", cmd.CommandDesc, cmd.Description); + Console.WriteLine (); + Console.WriteLine ("Usage: {0}{1}", setupAppName, cmd.Usage); + Console.WriteLine (); + + TextFormatter fm = new TextFormatter (); + fm.Wrap = WrappingType.Word; + fm.Append (cmd.LongDescription); + Console.WriteLine (fm.ToString ()); + } + else + Console.WriteLine ("Unknown command: " + parms [0]); + Console.WriteLine (); + } + } + + void CreateCommands () + { + SetupCommand cmd; + string cat = "Add-in commands"; + + cmd = new SetupCommand (cat, "install", "i", new SetupCommandHandler (Install)); + cmd.Description = "Installs add-ins."; + cmd.Usage = "[-y] [package-name|package-file] ..."; + cmd.AppendDesc ("Installs an add-in or set of addins. The command argument is a list"); + cmd.AppendDesc ("of files and/or package names. If a package name is provided"); + cmd.AppendDesc ("the package will be looked up in the registered repositories."); + cmd.AppendDesc ("A specific add-in version can be specified by appending it to."); + cmd.AppendDesc ("the package name using '/' as a separator, like in this example:"); + cmd.AppendDesc ("MonoDevelop.SourceEditor/0.9.1\n"); + cmd.AppendDesc ("-y: Don't ask for confirmation."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "uninstall", "u", new SetupCommandHandler (Uninstall)); + cmd.Description = "Uninstalls add-ins."; + cmd.Usage = "[-y] "; + cmd.AppendDesc ("Uninstalls an add-in. The command argument is the name"); + cmd.AppendDesc ("of the add-in to uninstall.\n"); + cmd.AppendDesc ("-y: Don't ask for confirmation."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "check-install", "ci", new SetupCommandHandler (CheckInstall)); + cmd.Description = "Checks installed add-ins."; + cmd.Usage = "[package-name|package-file] ..."; + cmd.AppendDesc ("Checks if a package is installed. If it is not, it looks for"); + cmd.AppendDesc ("the package in the registered repositories, and if found"); + cmd.AppendDesc ("the package is downloaded and installed, including all"); + cmd.AppendDesc ("needed dependencies."); + commands.Add (cmd); + + + cmd = new SetupCommand (cat, "update", "up", new SetupCommandHandler (Update)); + cmd.Description = "Updates installed add-ins."; + cmd.AppendDesc ("Downloads and installs available updates for installed add-ins."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "list", "l", new SetupCommandHandler (ListInstalled)); + cmd.Description = "Lists installed add-ins."; + cmd.AppendDesc ("Prints a list of all installed add-ins."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "list-av", "la", new SetupCommandHandler (ListAvailable)); + cmd.Description = "Lists add-ins available in registered repositories."; + cmd.AppendDesc ("Prints a list of add-ins available to install in the"); + cmd.AppendDesc ("registered repositories."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "list-update", "lu", new SetupCommandHandler (ListUpdates)); + cmd.Description = "Lists available add-in updates."; + cmd.AppendDesc ("Prints a list of available add-in updates in the registered repositories."); + commands.Add (cmd); + + cat = "Repository Commands"; + + cmd = new SetupCommand (cat, "rep-add", "ra", new SetupCommandHandler (AddRepository)); + cmd.Description = "Registers repositories."; + cmd.Usage = " ..."; + cmd.AppendDesc ("Registers an add-in repository. Several URLs can be provided."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "rep-remove", "rr", new SetupCommandHandler (RemoveRepository)); + cmd.Description = "Unregisters repositories."; + cmd.Usage = " ..."; + cmd.AppendDesc ("Unregisters an add-in repository. Several URLs can be provided."); + cmd.AppendDesc ("Instead of an url, a repository number can be used (repository numbers are"); + cmd.AppendDesc ("shown by the rep-list command."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "rep-enable", "re", new SetupCommandHandler (EnableRepository)); + cmd.Description = "Enables repositories."; + cmd.Usage = " ..."; + cmd.AppendDesc ("Enables an add-in repository which has been disabled. Several URLs can be"); + cmd.AppendDesc ("provided. Instead of an url, a repository number can be used (repository"); + cmd.AppendDesc ("numbers are shown by the rep-list command."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "rep-disable", "rd", new SetupCommandHandler (DisableRepository)); + cmd.Description = "Disables repositories."; + cmd.Usage = " ..."; + cmd.AppendDesc ("Disables an add-in repository. Several URLs can be provided"); + cmd.AppendDesc ("When a repository is disabled, it will be ignored when using the update and"); + cmd.AppendDesc ("install commands."); + cmd.AppendDesc ("Instead of an url, a repository number can be used (repository numbers are"); + cmd.AppendDesc ("shown by the rep-list command."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "rep-update", "ru", new SetupCommandHandler (UpdateAvailableAddins)); + cmd.Description = "Updates the lists of available addins."; + cmd.AppendDesc ("Updates the lists of addins available in all registered repositories."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "rep-list", "rl", new SetupCommandHandler (ListRepositories)); + cmd.Description = "Lists registered repositories."; + cmd.AppendDesc ("Shows a list of all registered repositories."); + commands.Add (cmd); + + cat = "Add-in Registry Commands"; + + cmd = new SetupCommand (cat, "reg-update", "rgu", new SetupCommandHandler (UpdateRegistry)); + cmd.Description = "Updates the add-in registry."; + cmd.AppendDesc ("Looks for changes in add-in directories and updates the registry."); + cmd.AppendDesc ("New add-ins will be added and deleted add-ins will be removed."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "reg-build", "rgb", new SetupCommandHandler (RepairRegistry)); + cmd.Description = "Rebuilds the add-in registry."; + cmd.AppendDesc ("Regenerates the add-in registry"); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "info", null, new SetupCommandHandler (PrintAddinInfo)); + cmd.Usage = "[addin-id|addin-file|--all] [--xml] [--full] [--namespace ]"; + cmd.Description = "Prints information about add-ins."; + cmd.AppendDesc ("Prints information about add-ins. Options:\n"); + cmd.AppendDesc (" --xml: Dump the information using an XML format.\n"); + cmd.AppendDesc (" --full: Include add-ins which don't define extension points.\n"); + cmd.AppendDesc (" --namespace ns: Include only add-ins from the specified 'ns' namespace."); + commands.Add (cmd); + + cat = "Packaging Commands"; + + cmd = new SetupCommand (cat, "rep-build", "rb", new SetupCommandHandler (BuildRepository)); + cmd.Description = "Creates a repository index file for a directory structure."; + cmd.Usage = ""; + cmd.AppendDesc ("Scans the provided directory and generates a set of index files with entries"); + cmd.AppendDesc ("for all add-in packages found in the directory tree. The resulting file"); + cmd.AppendDesc ("structure is an add-in repository that can be published in a web site or a"); + cmd.AppendDesc ("shared directory."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "pack", "p", new SetupCommandHandler (BuildPackage)); + cmd.Description = "Creates a package from an add-in configuration file."; + cmd.Usage = " [-d:output-directory] [-debugSymbols:(true|false)]"; + cmd.AppendDesc ("Creates an add-in package (.mpack file) which includes all files "); + cmd.AppendDesc ("needed to deploy an add-in. The command parameter is the path to"); + cmd.AppendDesc ("the add-in's configuration file. If 'debugSymbols' is set to true"); + cmd.AppendDesc ("then pdb or mdb debug symbols will automatically be included in the"); + cmd.AppendDesc ("final package."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "help", "h", new SetupCommandHandler (PrintHelp)); + cmd.Description = "Shows help about a command."; + cmd.Usage = ""; + commands.Add (cmd); + + cat = "Build Commands"; + + cmd = new SetupCommand (cat, "libraries", "libs", new SetupCommandHandler (PrintLibraries)); + cmd.Description = "Lists add-in assemblies."; + cmd.Usage = "[-r] ..."; + cmd.AppendDesc ("Prints a list of assemblies exported by the add-in or add-ins provided"); + cmd.AppendDesc ("as arguments. This list of assemblies can be used as references for"); + cmd.AppendDesc ("building add-ins that depend on them. If the -r option is specified,"); + cmd.AppendDesc ("each assembly is prefixed with '-r:'."); + commands.Add (cmd); + + cmd = new SetupCommand (cat, "applications", "apps", new SetupCommandHandler (PrintApplications)); + cmd.Description = "Lists extensible applications."; + cmd.AppendDesc ("Prints a list of registered extensible applications."); + commands.Add (cmd); + + cat = "Debug Commands"; + + cmd = new SetupCommand (cat, "dump-file", null, new SetupCommandHandler (DumpRegistryFile)); + cmd.Description = "Prints the contents of a registry file."; + cmd.Usage = ""; + cmd.AppendDesc ("Prints the contents of a registry file for debugging."); + commands.Add (cmd); + } + } + + class SetupCommand + { + string usage; + + public SetupCommand (string cat, string cmd, string shortCmd, SetupCommandHandler handler) + { + Category = cat; + Command = cmd; + ShortCommand = shortCmd; + Handler = handler; + } + + public void AppendDesc (string s) + { + LongDescription += s + " "; + } + + public string Category; + public string Command; + public string ShortCommand; + public SetupCommandHandler Handler; + + public string Usage { + get { return usage != null ? Command + " " + usage : Command; } + set { usage = value; } + } + + public string CommandDesc { + get { + if (ShortCommand != null && ShortCommand.Length > 0) + return Command + " (" + ShortCommand + ")"; + else + return Command; + } + } + + public string Description = ""; + public string LongDescription = ""; + } + + /// + /// A command handler + /// + public delegate void SetupCommandHandler (string[] args); +} + diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/TextFormatter.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/TextFormatter.cs new file mode 100644 index 00000000..c1f37a1f --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/TextFormatter.cs @@ -0,0 +1,378 @@ +// +// TextFormatter.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Text; + +namespace Mono.Addins.Setup +{ + enum WrappingType + { + None, + Char, + Word, + WordChar + } + + class TextFormatter + { + string indentString = ""; + string formattedIndentString; + int indentColumnWidth; + string paragFormattedIndentString; + int paragIndentColumnWidth; + int leftMargin; + int paragraphStartMargin; + WrappingType wrap; + int tabWidth; + bool tabsAsSpaces; + + public int MaxColumns { get; set; } + + StringBuilder builder = new StringBuilder (); + StringBuilder currentWord = new StringBuilder (); + int curCol; + bool lineStart = true; + bool lastWasSeparator; + bool paragraphStart = true; + int wordLevel; + + public TextFormatter () + { + MaxColumns = 80; + TabWidth = 4; + } + + public int TabWidth { + get { return tabWidth; } + set { + tabWidth = value; + formattedIndentString = null; + } + } + + public string IndentString { + get { return indentString; } + set { + if (value == null) + throw new ArgumentNullException ("value"); + indentString = value; + formattedIndentString = null; + } + } + + public int LeftMargin { + get { + return leftMargin; + } + set { + leftMargin = value; + formattedIndentString = null; + } + } + + public int ParagraphStartMargin { + get { return paragraphStartMargin; } + set { + paragraphStartMargin = value; + formattedIndentString = null; + } + } + + public WrappingType Wrap { + get { + return wrap; + } + set { + if (wrap != value) { + AppendCurrentWord ('x'); + wrap = value; + } + } + } + + public bool TabsAsSpaces { + get { return tabsAsSpaces; } + set { + tabsAsSpaces = value; + formattedIndentString = null; + } + } + + string FormattedIndentString { + get { + if (formattedIndentString == null) + CreateIndentString (); + return formattedIndentString; + } + } + + int IndentColumnWidth { + get { + if (formattedIndentString == null) + CreateIndentString (); + return indentColumnWidth; + } + } + + string ParagFormattedIndentString { + get { + if (formattedIndentString == null) + CreateIndentString (); + return paragFormattedIndentString; + } + } + + int ParagIndentColumnWidth { + get { + if (formattedIndentString == null) + CreateIndentString (); + return paragIndentColumnWidth; + } + } + + public void Clear () + { + builder = new StringBuilder (); + currentWord = new StringBuilder (); + curCol = 0; + lineStart = true; + paragraphStart = true; + lastWasSeparator = false; + } + + public void AppendWord (string text) + { + BeginWord (); + Append (text); + EndWord (); + } + + public void Append (string text) + { + if (string.IsNullOrEmpty (text)) + return; + + if (builder.Length == 0) { + curCol = IndentColumnWidth; + lineStart = true; + paragraphStart = true; + } + + if (Wrap == WrappingType.None || Wrap == WrappingType.Char) { + AppendChars (text, Wrap == WrappingType.Char); + return; + } + + int n = 0; + + while (n < text.Length) + { + int sn = n; + bool foundSpace = false; + while (n < text.Length && !foundSpace) { + if ((char.IsWhiteSpace (text [n]) && wordLevel == 0) || text [n] == '\n') + foundSpace = true; + else + n++; + } + + if (n != sn) + currentWord.Append (text, sn, n - sn); + if (foundSpace) { + AppendCurrentWord (text[n]); + n++; + } + } + } + + public void AppendLine () + { + AppendCurrentWord ('x'); + AppendChar ('\n', false); + } + + public void BeginWord () + { + wordLevel++; + } + + public void EndWord () + { + if (wordLevel == 0) + throw new InvalidOperationException ("Missing BeginWord call"); + wordLevel--; + + char lastChar = 'x'; + if (currentWord.Length > 0) { + lastChar = currentWord [currentWord.Length - 1]; + if (char.IsWhiteSpace (lastChar)) + currentWord.Remove (currentWord.Length - 1, 1); + } + AppendCurrentWord (lastChar); + } + + public void FlushWord () + { + AppendCurrentWord ('x'); + if (curCol > MaxColumns) + AppendSoftBreak (); + } + + public override string ToString () + { + if (currentWord.Length > 0) + AppendCurrentWord ('x'); + return builder.ToString (); + } + + void AppendChars (string s, bool wrapChars) + { + foreach (char c in s) + AppendChar (c, wrapChars); + } + + void AppendSoftBreak () + { + AppendChar ('\n', true); + paragraphStart = false; + curCol = IndentColumnWidth; + } + + void AppendChar (char c, bool wrapChars) + { + if (c == '\n') { + lineStart = true; + paragraphStart = true; + builder.Append (c); + curCol = ParagIndentColumnWidth; + lastWasSeparator = false; + return; + } + else if (lineStart) { + if (paragraphStart) + builder.Append (ParagFormattedIndentString); + else + builder.Append (FormattedIndentString); + lineStart = false; + paragraphStart = false; + lastWasSeparator = false; + } + if (wrapChars && curCol >= MaxColumns) { + AppendSoftBreak (); + if (!char.IsWhiteSpace (c)) + AppendChar (c, false); + return; + } + + if (c == '\t') { + int tw = GetTabWidth (curCol); + if (TabsAsSpaces) + builder.Append (' ', tw); + else + builder.Append (c); + curCol += tw; + } + else { + builder.Append (c); + curCol++; + } + } + + void AppendCurrentWord (char separatorChar) + { + if (currentWord.Length == 0) + return; + if (Wrap == WrappingType.Word || Wrap == WrappingType.WordChar) { + if (curCol + currentWord.Length > MaxColumns) { + // If the last char was a word separator, remove it + if (lastWasSeparator) + builder.Remove (builder.Length - 1, 1); + if (!lineStart) + AppendSoftBreak (); + } + } + AppendChars (currentWord.ToString (), Wrap == WrappingType.WordChar); + if (char.IsWhiteSpace (separatorChar) || (separatorChar == '\n' && !lineStart)) { + lastWasSeparator = true; + AppendChar (separatorChar, true); + } else + lastWasSeparator = false; + currentWord = new StringBuilder (); + } + + int GetTabWidth (int startCol) + { + int res = startCol % TabWidth; + if (res == 0) + return TabWidth; + else + return TabWidth - res; + } + + void CreateIndentString () + { + StringBuilder sb = new StringBuilder (); + indentColumnWidth = AddIndentString (sb, indentString); + + paragFormattedIndentString = sb.ToString () + new string (' ', paragraphStartMargin); + paragIndentColumnWidth = indentColumnWidth + paragraphStartMargin; + + if (LeftMargin > 0) { + sb.Append (' ', LeftMargin); + indentColumnWidth += LeftMargin; + } + formattedIndentString = sb.ToString (); + + if (paragraphStart) + curCol = paragIndentColumnWidth; + else if (lineStart) + curCol = indentColumnWidth; + } + + int AddIndentString (StringBuilder sb, string txt) + { + if (string.IsNullOrEmpty (txt)) + return 0; + int count = 0; + foreach (char c in txt) { + if (c == '\t') { + int tw = GetTabWidth (count); + count += tw; + if (TabsAsSpaces) + sb.Append (' ', tw); + else + sb.Append (c); + } + else { + sb.Append (c); + count++; + } + } + return count; + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/WebRequestHelper.cs b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/WebRequestHelper.cs new file mode 100644 index 00000000..5685b5ac --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/WebRequestHelper.cs @@ -0,0 +1,116 @@ +// +// WebRequestHelper.cs +// +// Author: +// Bojan Rajkovic +// Michael Hutchinson +// +// based on NuGet src/Core/Http +// +// Copyright (c) 2013-2014 Xamarin Inc. +// Copyright (c) 2010-2014 Outercurve Foundation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Mono.Addins.Setup +{ + /// + /// Helper for making web requests with support for authenticated proxies. + /// + public static class WebRequestHelper + { + static Func, Action,CancellationToken,HttpWebResponse> _handler; + + /// + /// Sets a custom request handler that can handle requests for authenticated proxy servers. + /// + /// The custom request handler. + public static void SetRequestHandler (Func, Action,CancellationToken,HttpWebResponse> handler) + { + _handler = handler; + } + + /// + /// Gets the web response, using the request handler to handle proxy authentication + /// if necessary. + /// + /// The response. + /// Callback for creating the request. + /// Callback for preparing the request, e.g. writing the request stream. + /// Cancellation token. + /// + /// Keeps sending requests until a response code that doesn't require authentication happens or if the request + /// requires authentication and the user has stopped trying to enter them (i.e. they hit cancel when they are prompted). + /// + public static Task GetResponseAsync ( + Func createRequest, + Action prepareRequest = null, + CancellationToken token = default(CancellationToken)) + { + return Task.Factory.StartNew (() => GetResponse (createRequest, prepareRequest, token), token); + } + + /// + /// Gets the web response, using the request handler to handle proxy authentication + /// if necessary. + /// + /// The response. + /// Callback for creating the request. + /// Callback for preparing the request, e.g. writing the request stream. + /// Cancellation token. + /// + /// Keeps sending requests until a response code that doesn't require authentication happens or if the request + /// requires authentication and the user has stopped trying to enter them (i.e. they hit cancel when they are prompted). + /// + public static HttpWebResponse GetResponse ( + Func createRequest, + Action prepareRequest = null, + CancellationToken token = default(CancellationToken)) + { + var handler = _handler; + if (handler != null) + return handler (createRequest, prepareRequest, token); + + var req = createRequest (); + if (token.CanBeCanceled) + token.Register (req.Abort); + if (prepareRequest != null) + prepareRequest (req); + return (HttpWebResponse) req.GetResponse (); + } + + /// + /// Determines whether an error code is likely to have been caused by internet reachability problems. + /// + public static bool IsCannotReachInternetError (this WebExceptionStatus status) + { + switch (status) { + case WebExceptionStatus.NameResolutionFailure: + case WebExceptionStatus.ConnectFailure: + case WebExceptionStatus.ConnectionClosed: + case WebExceptionStatus.ProxyNameResolutionFailure: + case WebExceptionStatus.SendFailure: + case WebExceptionStatus.Timeout: + return true; + default: + return false; + } + } + } +} diff --git a/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/serializers.xml b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/serializers.xml new file mode 100644 index 00000000..3d22e70e --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/Mono.Addins.Setup/serializers.xml @@ -0,0 +1,16 @@ + + + AddinSystemConfigurationReader + AddinSystemConfigurationWriter + Mono.Addins.Setup + AddinSystemConfigurationReaderWriter.cs + true + + + RepositoryReader + RepositoryWriter + Mono.Addins.Setup + RepositoryReaderWriter.cs + true + + diff --git a/mono-addins/Mono.Addins.Setup/obj/Debug/Mono.Addins.Setup.csproj.CoreCompileInputs.cache b/mono-addins/Mono.Addins.Setup/obj/Debug/Mono.Addins.Setup.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..3a74597c --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/obj/Debug/Mono.Addins.Setup.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +46d87cb08a7a3ece7edf43f06db27c1be2a0812b diff --git a/mono-addins/Mono.Addins.Setup/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.Setup/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Setup/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.Setup/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Setup/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.Setup/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Setup/obj/Mono.Addins.Setup.csproj.nuget.g.props b/mono-addins/Mono.Addins.Setup/obj/Mono.Addins.Setup.csproj.nuget.g.props new file mode 100644 index 00000000..70655941 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/obj/Mono.Addins.Setup.csproj.nuget.g.props @@ -0,0 +1,18 @@ + + + + True + NuGet + D:\opensim\MonoAddins\mono-addins\Mono.Addins.Setup\obj\project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\ld\.nuget\packages\ + PackageReference + 4.9.3 + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.Setup/obj/Mono.Addins.Setup.csproj.nuget.g.targets b/mono-addins/Mono.Addins.Setup/obj/Mono.Addins.Setup.csproj.nuget.g.targets new file mode 100644 index 00000000..f9da2a43 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/obj/Mono.Addins.Setup.csproj.nuget.g.targets @@ -0,0 +1,9 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins.Setup/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/mono-addins/Mono.Addins.Setup/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 00000000..8257741f Binary files /dev/null and b/mono-addins/Mono.Addins.Setup/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/mono-addins/Mono.Addins.Setup/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins.Setup/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Setup/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins.Setup/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Setup/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins.Setup/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins.Setup/obj/project.assets.json b/mono-addins/Mono.Addins.Setup/obj/project.assets.json new file mode 100644 index 00000000..f3e0b979 --- /dev/null +++ b/mono-addins/Mono.Addins.Setup/obj/project.assets.json @@ -0,0 +1,254 @@ +{ + "version": 3, + "targets": { + ".NETFramework,Version=v4.6": { + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + }, + "SharpZipLib/0.86.0": { + "type": "package", + "compile": { + "lib/20/ICSharpCode.SharpZipLib.dll": {} + }, + "runtime": { + "lib/20/ICSharpCode.SharpZipLib.dll": {} + } + }, + "Mono.Addins/1.3.7": { + "type": "project", + "framework": ".NETFramework,Version=v4.6", + "dependencies": { + "NuGet.Build.Packaging": "0.2.0" + }, + "compile": { + "bin/placeholder/Mono.Addins.dll": {} + }, + "runtime": { + "bin/placeholder/Mono.Addins.dll": {} + } + } + }, + ".NETFramework,Version=v4.6/win": { + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + }, + "SharpZipLib/0.86.0": { + "type": "package", + "compile": { + "lib/20/ICSharpCode.SharpZipLib.dll": {} + }, + "runtime": { + "lib/20/ICSharpCode.SharpZipLib.dll": {} + } + }, + "Mono.Addins/1.3.7": { + "type": "project", + "framework": ".NETFramework,Version=v4.6", + "dependencies": { + "NuGet.Build.Packaging": "0.2.0" + }, + "compile": { + "bin/placeholder/Mono.Addins.dll": {} + }, + "runtime": { + "bin/placeholder/Mono.Addins.dll": {} + } + } + }, + ".NETFramework,Version=v4.6/win-x64": { + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + }, + "SharpZipLib/0.86.0": { + "type": "package", + "compile": { + "lib/20/ICSharpCode.SharpZipLib.dll": {} + }, + "runtime": { + "lib/20/ICSharpCode.SharpZipLib.dll": {} + } + }, + "Mono.Addins/1.3.7": { + "type": "project", + "framework": ".NETFramework,Version=v4.6", + "dependencies": { + "NuGet.Build.Packaging": "0.2.0" + }, + "compile": { + "bin/placeholder/Mono.Addins.dll": {} + }, + "runtime": { + "bin/placeholder/Mono.Addins.dll": {} + } + } + }, + ".NETFramework,Version=v4.6/win-x86": { + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + }, + "SharpZipLib/0.86.0": { + "type": "package", + "compile": { + "lib/20/ICSharpCode.SharpZipLib.dll": {} + }, + "runtime": { + "lib/20/ICSharpCode.SharpZipLib.dll": {} + } + }, + "Mono.Addins/1.3.7": { + "type": "project", + "framework": ".NETFramework,Version=v4.6", + "dependencies": { + "NuGet.Build.Packaging": "0.2.0" + }, + "compile": { + "bin/placeholder/Mono.Addins.dll": {} + }, + "runtime": { + "bin/placeholder/Mono.Addins.dll": {} + } + } + } + }, + "libraries": { + "NuGet.Build.Packaging/0.2.0": { + "sha512": "iqo7f9c+oA12IcelLjD232BMxdGR2Dzrqk00C8w7NiB1sbXa8YHsZGCJkt6CEQKR5XYqZ/8f3z0kHTBJ0Gua3Q==", + "type": "package", + "path": "nuget.build.packaging/0.2.0", + "files": [ + "build/ApiIntersect.exe", + "build/ApiIntersect.exe.config", + "build/GenerateReferenceAssembly.csproj", + "build/ICSharpCode.Decompiler.dll", + "build/ICSharpCode.NRefactory.CSharp.dll", + "build/ICSharpCode.NRefactory.Cecil.dll", + "build/ICSharpCode.NRefactory.Xml.dll", + "build/ICSharpCode.NRefactory.dll", + "build/Mono.Cecil.Mdb.dll", + "build/Mono.Cecil.Pdb.dll", + "build/Mono.Cecil.Rocks.dll", + "build/Mono.Cecil.dll", + "build/Mono.Options.dll", + "build/NuGet.Build.Packaging.Authoring.props", + "build/NuGet.Build.Packaging.Authoring.targets", + "build/NuGet.Build.Packaging.Compatibility.props", + "build/NuGet.Build.Packaging.CrossTargeting.targets", + "build/NuGet.Build.Packaging.Inference.targets", + "build/NuGet.Build.Packaging.Legacy.props", + "build/NuGet.Build.Packaging.Legacy.targets", + "build/NuGet.Build.Packaging.ReferenceAssembly.targets", + "build/NuGet.Build.Packaging.Tasks.dll", + "build/NuGet.Build.Packaging.Tasks.pdb", + "build/NuGet.Build.Packaging.Version.props", + "build/NuGet.Build.Packaging.props", + "build/NuGet.Build.Packaging.targets", + "nuget.build.packaging.0.2.0.nupkg.sha512", + "nuget.build.packaging.nuspec" + ] + }, + "SharpZipLib/0.86.0": { + "sha512": "5DbS1SlKLMi+WG00Cm0ueYf2oyXJrowETQk8nx96rmdjTuHsA3laPykGV7Sxi0R5Xxfx0Kh/EfacAZ1f6M7y/g==", + "type": "package", + "path": "sharpziplib/0.86.0", + "files": [ + "lib/11/ICSharpCode.SharpZipLib.dll", + "lib/20/ICSharpCode.SharpZipLib.dll", + "lib/SL3/SharpZipLib.Silverlight3.dll", + "lib/SL4/SharpZipLib.Silverlight4.dll", + "sharpziplib.0.86.0.nupkg.sha512", + "sharpziplib.nuspec" + ] + }, + "Mono.Addins/1.3.7": { + "type": "project", + "path": "../Mono.Addins/Mono.Addins.csproj", + "msbuildProject": "../Mono.Addins/Mono.Addins.csproj" + } + }, + "projectFileDependencyGroups": { + ".NETFramework,Version=v4.6": [ + "Mono.Addins >= 1.3.7", + "NuGet.Build.Packaging >= 0.2.0", + "SharpZipLib >= 0.86.0" + ] + }, + "packageFolders": { + "C:\\Users\\ld\\.nuget\\packages\\": {} + }, + "project": { + "version": "1.3.7", + "restore": { + "projectUniqueName": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins.Setup\\Mono.Addins.Setup.csproj", + "projectName": "Mono.Addins.Setup", + "projectPath": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins.Setup\\Mono.Addins.Setup.csproj", + "packagesPath": "C:\\Users\\ld\\.nuget\\packages\\", + "outputPath": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins.Setup\\obj\\", + "projectStyle": "PackageReference", + "skipContentFileWrite": true, + "configFilePaths": [ + "C:\\Users\\ld\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net46" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Users\\ld\\AppData\\Local\\Xenko\\NugetDev": {}, + "D:\\xenko\\xenko\\bin\\packages": {}, + "https://api.nuget.org/v3/index.json": {}, + "https://packages.xenko.com/nuget": {} + }, + "frameworks": { + "net46": { + "projectReferences": { + "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins\\Mono.Addins.csproj": { + "projectPath": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins\\Mono.Addins.csproj" + } + } + } + } + }, + "frameworks": { + "net46": { + "dependencies": { + "NuGet.Build.Packaging": { + "target": "Package", + "version": "[0.2.0, )" + }, + "SharpZipLib": { + "target": "Package", + "version": "[0.86.0, )" + } + } + } + }, + "runtimes": { + "win": { + "#import": [] + }, + "win-x64": { + "#import": [] + }, + "win-x86": { + "#import": [] + } + } + } +} \ No newline at end of file diff --git a/mono-addins/Mono.Addins.sln b/mono-addins/Mono.Addins.sln new file mode 100644 index 00000000..4156afb4 --- /dev/null +++ b/mono-addins/Mono.Addins.sln @@ -0,0 +1,270 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2003 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Addins", "Mono.Addins\Mono.Addins.csproj", "{91DD5A2D-9FE3-4C3C-9253-876141874DAD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mautil", "mautil\mautil.csproj", "{EA2F08DC-8289-4A89-A405-1A70D8B4C569}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Addins.Gui", "Mono.Addins.Gui\Mono.Addins.Gui.csproj", "{FEC19BDA-4904-4005-8C09-68E82E8BEF6A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{7EFC0684-310E-417D-B8BD-5584C3F34BD5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommandExtension", "Test\CommandExtension\CommandExtension.csproj", "{F109148D-849E-4044-8700-5E8EA0AB2476}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileContentExtension", "Test\FileContentExtension\FileContentExtension.csproj", "{4F29F0C0-725A-4927-9931-AAB0A595F370}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorldExtension", "Test\HelloWorldExtension\HelloWorldExtension.csproj", "{04C62888-E58A-4C6E-8688-A4F6F5459E14}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemInfoExtension", "Test\SystemInfoExtension\SystemInfoExtension.csproj", "{2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "Test\UnitTests\UnitTests.csproj", "{1CD51E61-1985-4D22-9BFA-D14C8FC61B46}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileExtender", "Test\FileExtender\FileExtender.csproj", "{A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MultiAssemblyAddin", "MultiAssemblyAddin", "{0CE17344-E7E3-4620-B6D4-B04778EC5739}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultiAssemblyAddin", "Test\MultiAssemblyAddin\MultiAssemblyAddin.csproj", "{8C374D09-E916-4C6C-A01B-43A06A0D0499}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SecondAssembly", "Test\MultiAssemblyAddin\SecondAssembly\SecondAssembly.csproj", "{EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OptionalModule", "Test\MultiAssemblyAddin\OptionalModule\OptionalModule.csproj", "{B051C84E-48CC-448D-B00C-1525EB64E4BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GuiTester", "Test\GuiTester\GuiTester.csproj", "{87331208-C6EA-4F1E-99A6-595778EFA39E}" +EndProject +Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "docs", "docs\docs.mdproj", "{87EADEFB-B389-4479-9C36-CDAA07839983}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Addins.Setup", "Mono.Addins.Setup\Mono.Addins.Setup.csproj", "{A85C9721-C054-4BD8-A1F3-0227615F0A36}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Addins.CecilReflector", "Mono.Addins.CecilReflector\Mono.Addins.CecilReflector.csproj", "{42D1CE65-A14B-4218-B787-58AD7AA68513}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Addins.MSBuild", "Mono.Addins.MSBuild\Mono.Addins.MSBuild.csproj", "{B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Addins.GuiGtk3", "Mono.Addins.GuiGtk3\Mono.Addins.GuiGtk3.csproj", "{410A7DC9-E7DA-43E6-B592-93E2A344B660}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + DebugGtk3|Any CPU = DebugGtk3|Any CPU + DebugNoGui|Any CPU = DebugNoGui|Any CPU + DebugWin32|Any CPU = DebugWin32|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD}.Release|Any CPU.Build.0 = Release|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA2F08DC-8289-4A89-A405-1A70D8B4C569}.Release|Any CPU.Build.0 = Release|Any CPU + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU + {FEC19BDA-4904-4005-8C09-68E82E8BEF6A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {F109148D-849E-4044-8700-5E8EA0AB2476}.Release|Any CPU.Build.0 = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {4F29F0C0-725A-4927-9931-AAB0A595F370}.Release|Any CPU.Build.0 = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {04C62888-E58A-4C6E-8688-A4F6F5459E14}.Release|Any CPU.Build.0 = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D}.Release|Any CPU.Build.0 = Debug|Any CPU + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9}.Release|Any CPU.Build.0 = Release|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C374D09-E916-4C6C-A01B-43A06A0D0499}.Release|Any CPU.Build.0 = Release|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4}.Release|Any CPU.Build.0 = Release|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B051C84E-48CC-448D-B00C-1525EB64E4BE}.Release|Any CPU.Build.0 = Release|Any CPU + {87331208-C6EA-4F1E-99A6-595778EFA39E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87331208-C6EA-4F1E-99A6-595778EFA39E}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {87331208-C6EA-4F1E-99A6-595778EFA39E}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {87331208-C6EA-4F1E-99A6-595778EFA39E}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {87331208-C6EA-4F1E-99A6-595778EFA39E}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {87331208-C6EA-4F1E-99A6-595778EFA39E}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU + {87331208-C6EA-4F1E-99A6-595778EFA39E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87EADEFB-B389-4479-9C36-CDAA07839983}.Debug|Any CPU.ActiveCfg = Default|Any CPU + {87EADEFB-B389-4479-9C36-CDAA07839983}.DebugGtk3|Any CPU.ActiveCfg = Default|Any CPU + {87EADEFB-B389-4479-9C36-CDAA07839983}.DebugNoGui|Any CPU.ActiveCfg = Default|Any CPU + {87EADEFB-B389-4479-9C36-CDAA07839983}.DebugWin32|Any CPU.ActiveCfg = Default|Any CPU + {87EADEFB-B389-4479-9C36-CDAA07839983}.Release|Any CPU.ActiveCfg = Default|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A85C9721-C054-4BD8-A1F3-0227615F0A36}.Release|Any CPU.Build.0 = Release|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42D1CE65-A14B-4218-B787-58AD7AA68513}.Release|Any CPU.Build.0 = Release|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.DebugNoGui|Any CPU.Build.0 = Debug|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B4B44F14-32C3-4D50-8C6A-06AA30E56CA3}.Release|Any CPU.Build.0 = Release|Any CPU + {410A7DC9-E7DA-43E6-B592-93E2A344B660}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {410A7DC9-E7DA-43E6-B592-93E2A344B660}.DebugGtk3|Any CPU.ActiveCfg = Debug|Any CPU + {410A7DC9-E7DA-43E6-B592-93E2A344B660}.DebugGtk3|Any CPU.Build.0 = Debug|Any CPU + {410A7DC9-E7DA-43E6-B592-93E2A344B660}.DebugNoGui|Any CPU.ActiveCfg = Debug|Any CPU + {410A7DC9-E7DA-43E6-B592-93E2A344B660}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU + {410A7DC9-E7DA-43E6-B592-93E2A344B660}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU + {410A7DC9-E7DA-43E6-B592-93E2A344B660}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F109148D-849E-4044-8700-5E8EA0AB2476} = {7EFC0684-310E-417D-B8BD-5584C3F34BD5} + {4F29F0C0-725A-4927-9931-AAB0A595F370} = {7EFC0684-310E-417D-B8BD-5584C3F34BD5} + {04C62888-E58A-4C6E-8688-A4F6F5459E14} = {7EFC0684-310E-417D-B8BD-5584C3F34BD5} + {2FF5459A-495C-4FDF-81EA-D0A6C07E7C0D} = {7EFC0684-310E-417D-B8BD-5584C3F34BD5} + {1CD51E61-1985-4D22-9BFA-D14C8FC61B46} = {7EFC0684-310E-417D-B8BD-5584C3F34BD5} + {A32AFFBA-4B83-4D6E-8CB3-812908BA14A9} = {7EFC0684-310E-417D-B8BD-5584C3F34BD5} + {0CE17344-E7E3-4620-B6D4-B04778EC5739} = {7EFC0684-310E-417D-B8BD-5584C3F34BD5} + {8C374D09-E916-4C6C-A01B-43A06A0D0499} = {0CE17344-E7E3-4620-B6D4-B04778EC5739} + {EB38A832-1BA5-4073-910C-7ACC5F1D1AD4} = {0CE17344-E7E3-4620-B6D4-B04778EC5739} + {B051C84E-48CC-448D-B00C-1525EB64E4BE} = {0CE17344-E7E3-4620-B6D4-B04778EC5739} + {87331208-C6EA-4F1E-99A6-595778EFA39E} = {7EFC0684-310E-417D-B8BD-5584C3F34BD5} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6005C9BC-F615-42D9-8477-6DFEB505C5AD} + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + Policies = $0 + $0.DotNetNamingPolicy = $1 + $1.DirectoryNamespaceAssociation = Flat + $1.ResourceNamePolicy = FileName + $0.StandardHeader = $2 + $2.Text = @\n${FileName}\n \nAuthor:\n ${AuthorName} <${AuthorEmail}>\n\nCopyright (c) ${Year} ${CopyrightHolder}\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE. + $0.TextStylePolicy = $6 + $3.IndentWidth = 8 + $3.FileWidth = 80 + $3.TabWidth = 8 + $0.VersionControlPolicy = $4 + $0.ChangeLogPolicy = $5 + $5.UpdateMode = None + $5.inheritsSet = Mono + $6.scope = text/x-csharp + $0.CSharpFormattingPolicy = $7 + $7.IndentSwitchSection = False + $7.NewLinesForBracesInProperties = False + $7.NewLinesForBracesInAccessors = False + $7.NewLinesForBracesInAnonymousMethods = False + $7.NewLinesForBracesInControlBlocks = False + $7.NewLinesForBracesInAnonymousTypes = False + $7.NewLinesForBracesInObjectCollectionArrayInitializers = False + $7.NewLinesForBracesInLambdaExpressionBody = False + $7.NewLineForElse = False + $7.NewLineForCatch = False + $7.NewLineForFinally = False + $7.NewLineForMembersInObjectInit = False + $7.NewLineForMembersInAnonymousTypes = False + $7.NewLineForClausesInQuery = False + $7.SpacingAfterMethodDeclarationName = True + $7.SpaceAfterMethodCallName = True + $7.SpaceBeforeOpenSquareBracket = True + $7.scope = text/x-csharp + name = Mono.Addins + defaultDeployTarget = Directory + EndGlobalSection +EndGlobal diff --git a/mono-addins/Mono.Addins/AssemblyInfo.cs b/mono-addins/Mono.Addins/AssemblyInfo.cs new file mode 100644 index 00000000..12a57d31 --- /dev/null +++ b/mono-addins/Mono.Addins/AssemblyInfo.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("Mono.Addins")] +[assembly: AssemblyCopyright("Copyright (C) 2007 Novell, Inc (http://www.novell.com)")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.3.7.0")] + +[assembly: InternalsVisibleTo ("Mono.Addins.Setup, PublicKey=00240000048000009400000006020000002400005253413100" + + "0400000100010079159977d2d03a8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c" + + "3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fddafa277b22de47d6ffea449674a4" + + "f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef0065d016df")] diff --git a/mono-addins/Mono.Addins/CustomConditionAttribute.cs b/mono-addins/Mono.Addins/CustomConditionAttribute.cs new file mode 100644 index 00000000..a6a977cb --- /dev/null +++ b/mono-addins/Mono.Addins/CustomConditionAttribute.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mono.Addins +{ + /// + /// Base class for custom condition attributes. Will be treated as a condition of the same short name, but without the "ConditionAttribute" suffix. For example, Foo.NameConditionAttribute will map to a condition with the ID "Name". + /// + /// + /// Properties and constructor arguments must be tagged with NodeAttributes to indicate the condition attributes to which they should be mapped. + /// + public abstract class CustomConditionAttribute : Attribute + { + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs new file mode 100644 index 00000000..ebd15d18 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs @@ -0,0 +1,2011 @@ +// +// AddinDatabase.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Threading; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Xml; +using System.Reflection; +using Mono.Addins.Description; +using System.Collections.Generic; +using System.Linq; + +namespace Mono.Addins.Database +{ + class AddinDatabase + { + public const string GlobalDomain = "global"; + public const string UnknownDomain = "unknown"; + + public const string VersionTag = "002"; + + List allSetupInfos; + List addinSetupInfos; + List rootSetupInfos; + internal static bool RunningSetupProcess; + bool fatalDatabseError; + Hashtable cachedAddinSetupInfos = new Hashtable (); + AddinScanResult currentScanResult; + AddinHostIndex hostIndex; + FileDatabase fileDatabase; + string addinDbDir; + DatabaseConfiguration config = null; + AddinRegistry registry; + int lastDomainId; + AddinEngine addinEngine; + AddinFileSystemExtension fs = new AddinFileSystemExtension (); + List extensions = new List (); + + public AddinDatabase (AddinEngine addinEngine, AddinRegistry registry) + { + this.addinEngine = addinEngine; + this.registry = registry; + addinDbDir = Path.Combine (registry.AddinCachePath, "addin-db-" + VersionTag); + fileDatabase = new FileDatabase (AddinDbDir); + } + + string AddinDbDir { + get { return addinDbDir; } + } + + public AddinFileSystemExtension FileSystem { + get { return fs; } + } + + public string AddinCachePath { + get { return Path.Combine (AddinDbDir, "addin-data"); } + } + + public string AddinFolderCachePath { + get { return Path.Combine (AddinDbDir, "addin-dir-data"); } + } + + public string AddinPrivateDataPath { + get { return Path.Combine (AddinDbDir, "addin-priv-data"); } + } + + public string HostsPath { + get { return Path.Combine (AddinDbDir, "hosts"); } + } + + string HostIndexFile { + get { return Path.Combine (AddinDbDir, "host-index"); } + } + + string ConfigFile { + get { return Path.Combine (AddinDbDir, "config.xml"); } + } + + internal bool IsGlobalRegistry { + get { + return registry.RegistryPath == AddinRegistry.GlobalRegistryPath; + } + } + + public AddinRegistry Registry { + get { + return this.registry; + } + } + + public void Clear () + { + if (Directory.Exists (AddinCachePath)) + Directory.Delete (AddinCachePath, true); + if (Directory.Exists (AddinFolderCachePath)) + Directory.Delete (AddinFolderCachePath, true); + } + + public void CopyExtensions (AddinDatabase other) + { + foreach (object o in other.extensions) + RegisterExtension (o); + } + + public void RegisterExtension (object extension) + { + extensions.Add (extension); + if (extension is AddinFileSystemExtension) + fs = (AddinFileSystemExtension) extension; + else + throw new NotSupportedException (); + } + + public void UnregisterExtension (object extension) + { + extensions.Remove (extension); + if ((extension as AddinFileSystemExtension) == fs) + fs = new AddinFileSystemExtension (); + else + throw new InvalidOperationException (); + } + + public ExtensionNodeSet FindNodeSet (string domain, string addinId, string id) + { + return FindNodeSet (domain, addinId, id, new Hashtable ()); + } + + ExtensionNodeSet FindNodeSet (string domain, string addinId, string id, Hashtable visited) + { + if (visited.Contains (addinId)) + return null; + visited.Add (addinId, addinId); + Addin addin = GetInstalledAddin (domain, addinId, true, false); + if (addin == null) + return null; + AddinDescription desc = addin.Description; + if (desc == null) + return null; + foreach (ExtensionNodeSet nset in desc.ExtensionNodeSets) + if (nset.Id == id) + return nset; + + // Not found in the add-in. Look on add-ins on which it depends + + foreach (Dependency dep in desc.MainModule.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) continue; + + string aid = Addin.GetFullId (desc.Namespace, adep.AddinId, adep.Version); + ExtensionNodeSet nset = FindNodeSet (domain, aid, id, visited); + if (nset != null) + return nset; + } + return null; + } + + public IEnumerable GetInstalledAddins (string domain, AddinSearchFlagsInternal flags) + { + if (domain == null) + domain = registry.CurrentDomain; + + // Get the cached list if the add-in list has already been loaded. + // The domain doesn't have to be checked again, since it is always the same + + IEnumerable result = null; + + if ((flags & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll) { + if (allSetupInfos != null) + result = allSetupInfos; + } + else if ((flags & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) { + if (addinSetupInfos != null) + result = addinSetupInfos; + } + else { + if (rootSetupInfos != null) + result = rootSetupInfos; + } + + if (result == null) { + InternalCheck (domain); + using (fileDatabase.LockRead ()) { + result = InternalGetInstalledAddins (domain, null, flags & ~AddinSearchFlagsInternal.LatestVersionsOnly); + } + } + + if ((flags & AddinSearchFlagsInternal.LatestVersionsOnly) == AddinSearchFlagsInternal.LatestVersionsOnly) + result = result.Where (a => a.IsLatestVersion); + + if ((flags & AddinSearchFlagsInternal.ExcludePendingUninstall) == AddinSearchFlagsInternal.ExcludePendingUninstall) + result = result.Where (a => !IsRegisteredForUninstall (a.Description.Domain, a.Id)); + + return result; + } + + IEnumerable InternalGetInstalledAddins (string domain, AddinSearchFlagsInternal type) + { + return InternalGetInstalledAddins (domain, null, type); + } + + IEnumerable InternalGetInstalledAddins (string domain, string idFilter, AddinSearchFlagsInternal type) + { + if ((type & AddinSearchFlagsInternal.LatestVersionsOnly) != 0) + throw new InvalidOperationException ("LatestVersionsOnly flag not supported here"); + + if (allSetupInfos == null) { + Dictionary adict = new Dictionary (); + + // Global add-ins are valid for any private domain + if (domain != AddinDatabase.GlobalDomain) + FindInstalledAddins (adict, AddinDatabase.GlobalDomain, idFilter); + + FindInstalledAddins (adict, domain, idFilter); + List alist = new List (adict.Values); + UpdateLastVersionFlags (alist); + if (idFilter != null) + return alist; + allSetupInfos = alist; + } + if ((type & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll) + return FilterById (allSetupInfos, idFilter); + + if ((type & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) { + if (addinSetupInfos == null) { + addinSetupInfos = new List (); + foreach (Addin adn in allSetupInfos) + if (!adn.Description.IsRoot) + addinSetupInfos.Add (adn); + } + return FilterById (addinSetupInfos, idFilter); + } + else { + if (rootSetupInfos == null) { + rootSetupInfos = new List (); + foreach (Addin adn in allSetupInfos) + if (adn.Description.IsRoot) + rootSetupInfos.Add (adn); + } + return FilterById (rootSetupInfos, idFilter); + } + } + + IEnumerable FilterById (List addins, string id) + { + if (id == null) + return addins; + return addins.Where (a => Addin.GetIdName (a.Id) == id); + } + + void FindInstalledAddins (Dictionary result, string domain, string idFilter) + { + if (idFilter == null) + idFilter = "*"; + string dir = Path.Combine (AddinCachePath, domain); + if (Directory.Exists (dir)) { + foreach (string file in fileDatabase.GetDirectoryFiles (dir, idFilter + ",*.maddin")) { + string id = Path.GetFileNameWithoutExtension (file); + if (!result.ContainsKey (id)) { + var adesc = GetInstalledDomainAddin (domain, id, true, false, false); + if (adesc != null) + result.Add (id, adesc); + } + } + } + } + + void UpdateLastVersionFlags (List addins) + { + Dictionary versions = new Dictionary (); + foreach (Addin a in addins) { + string last; + string id, version; + Addin.GetIdParts (a.Id, out id, out version); + if (!versions.TryGetValue (id, out last) || Addin.CompareVersions (last, version) > 0) + versions [id] = version; + } + foreach (Addin a in addins) { + string id, version; + Addin.GetIdParts (a.Id, out id, out version); + a.IsLatestVersion = versions [id] == version; + } + } + + public Addin GetInstalledAddin (string domain, string id) + { + return GetInstalledAddin (domain, id, false, false); + } + + public Addin GetInstalledAddin (string domain, string id, bool exactVersionMatch) + { + return GetInstalledAddin (domain, id, exactVersionMatch, false); + } + + public Addin GetInstalledAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly) + { + // Try the given domain, and if not found, try the shared domain + Addin ad = GetInstalledDomainAddin (domain, id, exactVersionMatch, enabledOnly, true); + if (ad != null) + return ad; + if (domain != AddinDatabase.GlobalDomain) + return GetInstalledDomainAddin (AddinDatabase.GlobalDomain, id, exactVersionMatch, enabledOnly, true); + else + return null; + } + + Addin GetInstalledDomainAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly, bool dbLockCheck) + { + Addin sinfo = null; + string idd = id + " " + domain; + object ob = cachedAddinSetupInfos [idd]; + if (ob != null) { + sinfo = ob as Addin; + if (sinfo != null) { + if (!enabledOnly || sinfo.Enabled) + return sinfo; + if (exactVersionMatch) + return null; + } + else if (enabledOnly) + // Ignore the 'not installed' flag when disabled add-ins are allowed + return null; + } + + if (dbLockCheck) + InternalCheck (domain); + + using ((dbLockCheck ? fileDatabase.LockRead () : null)) + { + string path = GetDescriptionPath (domain, id); + if (sinfo == null && fileDatabase.Exists (path)) { + sinfo = new Addin (this, domain, id); + cachedAddinSetupInfos [idd] = sinfo; + if (!enabledOnly || sinfo.Enabled) + return sinfo; + if (exactVersionMatch) { + // Cache lookups with negative result + cachedAddinSetupInfos [idd] = this; + return null; + } + } + + // Exact version not found. Look for a compatible version + if (!exactVersionMatch) { + sinfo = null; + string version, name, bestVersion = null; + Addin.GetIdParts (id, out name, out version); + + foreach (Addin ia in InternalGetInstalledAddins (domain, name, AddinSearchFlagsInternal.IncludeAll)) + { + if ((!enabledOnly || ia.Enabled) && + (version.Length == 0 || ia.SupportsVersion (version)) && + (bestVersion == null || Addin.CompareVersions (bestVersion, ia.Version) > 0)) + { + bestVersion = ia.Version; + sinfo = ia; + } + } + if (sinfo != null) { + cachedAddinSetupInfos [idd] = sinfo; + return sinfo; + } + } + + // Cache lookups with negative result + // Ignore the 'not installed' flag when disabled add-ins are allowed + if (enabledOnly) + cachedAddinSetupInfos [idd] = this; + return null; + } + } + + public void Shutdown () + { + ResetCachedData (); + } + + public Addin GetAddinForHostAssembly (string domain, string assemblyLocation) + { + InternalCheck (domain); + Addin ainfo = null; + + object ob = cachedAddinSetupInfos [assemblyLocation]; + if (ob != null) + return ob as Addin; // Don't use a cast here is ob may not be an Addin. + + AddinHostIndex index = GetAddinHostIndex (); + string addin, addinFile, rdomain; + if (index.GetAddinForAssembly (assemblyLocation, out addin, out addinFile, out rdomain)) { + string sid = addin + " " + rdomain; + ainfo = cachedAddinSetupInfos [sid] as Addin; + if (ainfo == null) + ainfo = new Addin (this, rdomain, addin); + cachedAddinSetupInfos [assemblyLocation] = ainfo; + cachedAddinSetupInfos [addin + " " + rdomain] = ainfo; + } + + return ainfo; + } + + + public bool IsAddinEnabled (string domain, string id) + { + Addin ainfo = GetInstalledAddin (domain, id); + if (ainfo != null) + return ainfo.Enabled; + else + return false; + } + + internal bool IsAddinEnabled (string domain, string id, bool exactVersionMatch) + { + if (!exactVersionMatch) + return IsAddinEnabled (domain, id); + Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false); + if (ainfo == null) + return false; + return Configuration.IsEnabled (id, ainfo.AddinInfo.EnabledByDefault); + } + + public void EnableAddin (string domain, string id) + { + EnableAddin (domain, id, true); + } + + internal void EnableAddin (string domain, string id, bool exactVersionMatch) + { + Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false); + if (ainfo == null) + // It may be an add-in root + return; + + if (IsAddinEnabled (domain, id)) + return; + + // Enable required add-ins + + foreach (Dependency dep in ainfo.AddinInfo.Dependencies) { + if (dep is AddinDependency) { + AddinDependency adep = dep as AddinDependency; + string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version); + EnableAddin (domain, adepid, false); + } + } + + Configuration.SetEnabled (id, true, ainfo.AddinInfo.EnabledByDefault, true); + SaveConfiguration (); + + if (addinEngine != null && addinEngine.IsInitialized) + addinEngine.ActivateAddin (id); + } + + public void DisableAddin (string domain, string id, bool exactVersionMatch = false) + { + Addin ai = GetInstalledAddin (domain, id, true); + if (ai == null) + throw new InvalidOperationException ("Add-in '" + id + "' not installed."); + + if (!IsAddinEnabled (domain, id, exactVersionMatch)) + return; + + Configuration.SetEnabled (id, false, ai.AddinInfo.EnabledByDefault, exactVersionMatch); + SaveConfiguration (); + + // Disable all add-ins which depend on it + + try { + string idName = Addin.GetIdName (id); + + foreach (Addin ainfo in GetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) { + foreach (Dependency dep in ainfo.AddinInfo.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) + continue; + + string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, null); + if (adepid != idName) + continue; + + // The add-in that has been disabled, might be a requirement of this one, or maybe not + // if there is an older version available. Check it now. + + adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version); + Addin adepinfo = GetInstalledAddin (domain, adepid, false, true); + + if (adepinfo == null) { + DisableAddin (domain, ainfo.Id); + break; + } + } + } + } + catch { + // If something goes wrong, enable the add-in again + Configuration.SetEnabled (id, true, ai.AddinInfo.EnabledByDefault, false); + SaveConfiguration (); + throw; + } + + if (addinEngine != null && addinEngine.IsInitialized) + addinEngine.UnloadAddin (id); + } + + public void RegisterForUninstall (string domain, string id, IEnumerable files) + { + DisableAddin (domain, id, true); + Configuration.RegisterForUninstall (id, files); + SaveConfiguration (); + } + + public bool IsRegisteredForUninstall (string domain, string addinId) + { + return Configuration.IsRegisteredForUninstall (addinId); + } + + internal bool HasPendingUninstalls (string domain) + { + return Configuration.HasPendingUninstalls; + } + + internal string GetDescriptionPath (string domain, string id) + { + return Path.Combine (Path.Combine (AddinCachePath, domain), id + ".maddin"); + } + + void InternalCheck (string domain) + { + // If the database is broken, don't try to regenerate it at every check. + if (fatalDatabseError) + return; + + bool update = false; + using (fileDatabase.LockRead ()) { + if (!Directory.Exists (AddinCachePath)) { + update = true; + } + } + if (update) + Update (null, domain); + } + + void GenerateAddinExtensionMapsInternal (IProgressStatus monitor, string domain, List addinsToUpdate, List addinsToUpdateRelations, List removedAddins) + { + AddinUpdateData updateData = new AddinUpdateData (this, monitor); + + // Clear cached data + cachedAddinSetupInfos.Clear (); + + // Collect all information + + AddinIndex addinHash = new AddinIndex (); + + if (monitor.LogLevel > 1) + monitor.Log ("Generating add-in extension maps"); + + Hashtable changedAddins = null; + ArrayList descriptionsToSave = new ArrayList (); + ArrayList files = new ArrayList (); + + bool partialGeneration = addinsToUpdate != null; + string[] domains = GetDomains ().Where (d => d == domain || d == GlobalDomain).ToArray (); + + // Get the files to be updated + + if (partialGeneration) { + changedAddins = new Hashtable (); + + if (monitor.LogLevel > 2) + monitor.Log ("Doing a partial registry update.\nAdd-ins to be updated:"); + // Get the files and ids of all add-ins that have to be updated + // Include removed add-ins: if there are several instances of the same add-in, removing one of + // them will make other instances to show up. If there is a single instance, its files are + // already removed. + foreach (string sa in addinsToUpdate.Union (removedAddins)) { + changedAddins [sa] = sa; + if (monitor.LogLevel > 2) + monitor.Log (" - " + sa); + foreach (string file in GetAddinFiles (sa, domains)) { + if (!files.Contains (file)) { + files.Add (file); + string an = Path.GetFileNameWithoutExtension (file); + changedAddins [an] = an; + if (monitor.LogLevel > 2 && an != sa) + monitor.Log (" - " + an); + } + } + } + + if (monitor.LogLevel > 2) + monitor.Log ("Add-ins whose relations have to be updated:"); + + // Get the files and ids of all add-ins whose relations have to be updated + foreach (string sa in addinsToUpdateRelations) { + foreach (string file in GetAddinFiles (sa, domains)) { + if (!files.Contains (file)) { + if (monitor.LogLevel > 2) { + string an = Path.GetFileNameWithoutExtension (file); + monitor.Log (" - " + an); + } + files.Add (file); + } + } + } + } + else { + foreach (var dom in domains) + files.AddRange (fileDatabase.GetDirectoryFiles (Path.Combine (AddinCachePath, dom), "*.maddin")); + } + + // Load the descriptions. + foreach (string file in files) { + + AddinDescription conf; + if (!ReadAddinDescription (monitor, file, out conf)) { + SafeDelete (monitor, file); + continue; + } + + // If the original file does not exist, the description can be deleted + if (!fs.FileExists (conf.AddinFile)) { + SafeDelete (monitor, file); + continue; + } + + // Remove old data from the description. Remove the data of the add-ins that + // have changed. This data will be re-added later. + + conf.UnmergeExternalData (changedAddins); + descriptionsToSave.Add (conf); + + addinHash.Add (conf); + } + + // Sort the add-ins, to make sure add-ins are processed before + // all their dependencies + + var sorted = addinHash.GetSortedAddins (); + + // Register extension points and node sets + foreach (AddinDescription conf in sorted) + CollectExtensionPointData (conf, updateData); + + if (monitor.LogLevel > 2) + monitor.Log ("Registering new extensions:"); + + // Register extensions + foreach (AddinDescription conf in sorted) { + if (changedAddins == null || changedAddins.ContainsKey (conf.AddinId)) { + if (monitor.LogLevel > 2) + monitor.Log ("- " + conf.AddinId + " (" + conf.Domain + ")"); + CollectExtensionData (monitor, addinHash, conf, updateData); + } + } + + // Save the maps + foreach (AddinDescription conf in descriptionsToSave) { + ConsolidateExtensions (conf); + conf.SaveBinary (fileDatabase); + } + + if (monitor.LogLevel > 1) { + monitor.Log ("Addin relation map generated."); + monitor.Log (" Addins Updated: " + descriptionsToSave.Count); + monitor.Log (" Extension points: " + updateData.RelExtensionPoints); + monitor.Log (" Extensions: " + updateData.RelExtensions); + monitor.Log (" Extension nodes: " + updateData.RelExtensionNodes); + monitor.Log (" Node sets: " + updateData.RelNodeSetTypes); + } + } + + void ConsolidateExtensions (AddinDescription conf) + { + // Merges extensions with the same path + + foreach (ModuleDescription module in conf.AllModules) { + Dictionary extensions = new Dictionary (); + foreach (Extension ext in module.Extensions) { + Extension mainExt; + if (extensions.TryGetValue (ext.Path, out mainExt)) { + ArrayList list = new ArrayList (); + EnsureInsertionsSorted (ext.ExtensionNodes); + list.AddRange (ext.ExtensionNodes); + int pos = -1; + foreach (ExtensionNodeDescription node in list) { + ext.ExtensionNodes.Remove (node); + AddNodeSorted (mainExt.ExtensionNodes, node, ref pos); + } + } else { + extensions [ext.Path] = ext; + EnsureInsertionsSorted (ext.ExtensionNodes); + } + } + + // Sort the nodes + } + } + + void EnsureInsertionsSorted (ExtensionNodeDescriptionCollection list) + { + // Makes sure that the nodes in the collections are properly sorted wrt insertafter and insertbefore attributes + Dictionary added = new Dictionary (); + List halfSorted = new List (); + bool orderChanged = false; + + for (int n = list.Count - 1; n >= 0; n--) { + ExtensionNodeDescription node = list [n]; + if (node.Id.Length > 0) + added [node.Id] = node; + if (node.InsertAfter.Length > 0) { + ExtensionNodeDescription relNode; + if (added.TryGetValue (node.InsertAfter, out relNode)) { + // Out of order. Move it before the referenced node + int i = halfSorted.IndexOf (relNode); + halfSorted.Insert (i, node); + orderChanged = true; + } else { + halfSorted.Add (node); + } + } else + halfSorted.Add (node); + } + halfSorted.Reverse (); + List fullSorted = new List (); + added.Clear (); + + foreach (ExtensionNodeDescription node in halfSorted) { + if (node.Id.Length > 0) + added [node.Id] = node; + if (node.InsertBefore.Length > 0) { + ExtensionNodeDescription relNode; + if (added.TryGetValue (node.InsertBefore, out relNode)) { + // Out of order. Move it before the referenced node + int i = fullSorted.IndexOf (relNode); + fullSorted.Insert (i, node); + orderChanged = true; + } else { + fullSorted.Add (node); + } + } else + fullSorted.Add (node); + } + if (orderChanged) { + list.Clear (); + foreach (ExtensionNodeDescription node in fullSorted) + list.Add (node); + } + } + + void AddNodeSorted (ExtensionNodeDescriptionCollection list, ExtensionNodeDescription node, ref int curPos) + { + // Adds the node at the correct position, taking into account insertbefore and insertafter + + if (node.InsertAfter.Length > 0) { + string afterId = node.InsertAfter; + for (int n=0; n 0) { + string beforeId = node.InsertBefore; + for (int n=0; n missingDeps = addinHash.GetMissingDependencies (conf, conf.MainModule); + if (missingDeps.Any ()) { + string w = "The add-in '" + conf.AddinId + "' could not be updated because some of its dependencies are missing or not compatible:"; + w += BuildMissingAddinsList (addinHash, conf, missingDeps); + monitor.ReportWarning (w); + return; + } + + CollectModuleExtensionData (conf, conf.MainModule, updateData, addinHash); + + foreach (ModuleDescription module in conf.OptionalModules) { + missingDeps = addinHash.GetMissingDependencies (conf, module); + if (missingDeps.Any ()) { + if (monitor.LogLevel > 1) { + string w = "An optional module of the add-in '" + conf.AddinId + "' could not be updated because some of its dependencies are missing or not compatible:"; + w += BuildMissingAddinsList (addinHash, conf, missingDeps); + } + } + else + CollectModuleExtensionData (conf, module, updateData, addinHash); + } + } + + string BuildMissingAddinsList (AddinIndex addinHash, AddinDescription conf, IEnumerable missingDeps) + { + string w = ""; + foreach (string dep in missingDeps) { + var found = addinHash.GetSimilarExistingAddin (conf, dep); + if (found == null) + w += "\n missing: " + dep; + else + w += "\n required: " + dep + ", found: " + found.AddinId; + } + return w; + } + + void CollectModuleExtensionData (AddinDescription conf, ModuleDescription module, AddinUpdateData updateData, AddinIndex index) + { + foreach (Extension ext in module.Extensions) { + updateData.RelExtensions++; + updateData.RegisterExtension (conf, module, ext); + AddChildExtensions (conf, module, updateData, index, ext.Path, ext.ExtensionNodes, false); + } + } + + void AddChildExtensions (AddinDescription conf, ModuleDescription module, AddinUpdateData updateData, AddinIndex index, string path, ExtensionNodeDescriptionCollection nodes, bool conditionChildren) + { + // Don't register conditions as extension nodes. + if (!conditionChildren) + updateData.RegisterExtension (conf, module, path); + + foreach (ExtensionNodeDescription node in nodes) { + if (node.NodeName == "ComplexCondition") + continue; + updateData.RelExtensionNodes++; + string id = node.GetAttribute ("id"); + if (id.Length != 0) { + bool isCondition = node.NodeName == "Condition"; + if (isCondition) { + // Find the add-in that provides the implementation for this condition. + // Store that id in the condition. The add-in engine will ensure the add-in + // is loaded when it tries to evaluate this condition. + var condAsm = index.FindCondition (conf, module, id); + if (condAsm != null) + node.SetAttribute (Condition.SourceAddinAttribute, condAsm); + } + AddChildExtensions (conf, module, updateData, index, path + "/" + id, node.ChildNodes, isCondition); + } + } + } + + string[] GetDomains () + { + string[] dirs = fileDatabase.GetDirectories (AddinCachePath); + string[] ids = new string [dirs.Length]; + for (int n=0; n= lastDomainId) + lastDomainId = n + 1; + } catch { + } + } + return lastDomainId.ToString (); + } + + internal void ResetBasicCachedData () + { + allSetupInfos = null; + addinSetupInfos = null; + rootSetupInfos = null; + } + + internal void ResetCachedData () + { + ResetBasicCachedData (); + hostIndex = null; + cachedAddinSetupInfos.Clear (); + if (addinEngine != null) + addinEngine.ResetCachedData (); + } + + + public bool AddinDependsOn (string domain, string id1, string id2) + { + Hashtable visited = new Hashtable (); + return AddinDependsOn (visited, domain, id1, id2); + } + + bool AddinDependsOn (Hashtable visited, string domain, string id1, string id2) + { + if (visited.Contains (id1)) + return false; + + visited.Add (id1, id1); + + Addin addin1 = GetInstalledAddin (domain, id1, false); + + // We can assume that if the add-in is not returned here, it may be a root addin. + if (addin1 == null) + return false; + + id2 = Addin.GetIdName (id2); + foreach (Dependency dep in addin1.AddinInfo.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) + continue; + string depid = Addin.GetFullId (addin1.AddinInfo.Namespace, adep.AddinId, null); + if (depid == id2) + return true; + else if (AddinDependsOn (visited, domain, depid, id2)) + return true; + } + return false; + } + + public void Repair (IProgressStatus monitor, string domain) + { + using (fileDatabase.LockWrite ()) { + try { + if (Directory.Exists (AddinCachePath)) + Directory.Delete (AddinCachePath, true); + if (Directory.Exists (AddinFolderCachePath)) + Directory.Delete (AddinFolderCachePath, true); + if (File.Exists (HostIndexFile)) + File.Delete (HostIndexFile); + } + catch (Exception ex) { + monitor.ReportError ("The add-in registry could not be rebuilt. It may be due to lack of write permissions to the directory: " + AddinDbDir, ex); + } + } + ResetBasicCachedData (); + + Update (monitor, domain); + } + + public void Update (IProgressStatus monitor, string domain) + { + if (monitor == null) + monitor = new ConsoleProgressStatus (false); + + if (RunningSetupProcess) + return; + + fatalDatabseError = false; + + DateTime tim = DateTime.Now; + + RunPendingUninstalls (monitor); + + Hashtable installed = new Hashtable (); + bool changesFound = CheckFolders (monitor, domain); + + if (monitor.IsCanceled) + return; + + if (monitor.LogLevel > 1) + monitor.Log ("Folders checked (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)"); + + if (changesFound) { + // Something has changed, the add-ins need to be re-scanned, but it has + // to be done in an external process + + if (domain != null) { + using (fileDatabase.LockRead ()) { + foreach (Addin ainfo in InternalGetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) { + installed [ainfo.Id] = ainfo.Id; + } + } + } + + RunScannerProcess (monitor); + + ResetCachedData (); + + registry.NotifyDatabaseUpdated (); + } + + if (fatalDatabseError) + monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", null); + + // Update the currently loaded add-ins + if (changesFound && domain != null && addinEngine != null && addinEngine.IsInitialized) { + Hashtable newInstalled = new Hashtable (); + foreach (Addin ainfo in GetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) { + newInstalled [ainfo.Id] = ainfo.Id; + } + + foreach (string aid in installed.Keys) { + // Always try to unload, event if the add-in was not currently loaded. + // Required since the add-ins has to be marked as 'disabled', to avoid + // extensions from this add-in to be loaded + if (!newInstalled.Contains (aid)) + addinEngine.UnloadAddin (aid); + } + + foreach (string aid in newInstalled.Keys) { + if (!installed.Contains (aid)) { + Addin addin = addinEngine.Registry.GetAddin (aid); + if (addin != null) + addinEngine.ActivateAddin (aid); + } + } + } + } + + void RunPendingUninstalls (IProgressStatus monitor) + { + bool changesDone = false; + + foreach (var adn in Configuration.GetPendingUninstalls ()) { + HashSet files = new HashSet (adn.Files); + if (AddinManager.CheckAssembliesLoaded (files)) + continue; + + if (monitor.LogLevel > 1) + monitor.Log ("Uninstalling " + adn.AddinId); + + // Make sure all files can be deleted before doing so + bool canUninstall = true; + foreach (string f in adn.Files) { + if (!File.Exists (f)) + continue; + try { + File.OpenWrite (f).Close (); + } catch { + canUninstall = false; + break; + } + } + + if (!canUninstall) + continue; + + foreach (string f in adn.Files) { + try { + if (File.Exists (f)) + File.Delete (f); + } catch { + canUninstall = false; + } + } + + if (canUninstall) { + Configuration.UnregisterForUninstall (adn.AddinId); + changesDone = true; + } + } + if (changesDone) + SaveConfiguration (); + } + + void RunScannerProcess (IProgressStatus monitor) + { + ISetupHandler setup = GetSetupHandler (); + + IProgressStatus scanMonitor = monitor; + ArrayList pparams = new ArrayList (); + + bool retry = false; + do { + try { + if (monitor.LogLevel > 1) + monitor.Log ("Looking for addins"); + setup.Scan (scanMonitor, registry, null, (string[]) pparams.ToArray (typeof(string))); + retry = false; + } + catch (Exception ex) { + ProcessFailedException pex = ex as ProcessFailedException; + if (pex != null) { + // Get the last logged operation. + if (pex.LastLog.StartsWith ("scan:")) { + // It crashed while scanning a file. Add the file to the ignore list and try again. + string file = pex.LastLog.Substring (5); + pparams.Add (file); + monitor.ReportWarning ("Could not scan file: " + file); + retry = true; + continue; + } + } + fatalDatabseError = true; + // If the process has crashed, try to do a new scan, this time using verbose log, + // to give the user more information about the origin of the crash. + if (pex != null && !retry) { + monitor.ReportError ("Add-in scan operation failed. The runtime may have encountered an error while trying to load an assembly.", null); + if (monitor.LogLevel <= 1) { + // Re-scan again using verbose log, to make it easy to find the origin of the error. + retry = true; + scanMonitor = new ConsoleProgressStatus (true); + } + } else + retry = false; + + if (!retry) { + var pfex = ex as ProcessFailedException; + monitor.ReportError ("Add-in scan operation failed", pfex != null? pfex.InnerException : ex); + monitor.Cancel (); + return; + } + } + } + while (retry); + } + + bool DatabaseInfrastructureCheck (IProgressStatus monitor) + { + // Do some sanity check, to make sure the basic database infrastructure can be created + + bool hasChanges = false; + + try { + + if (!Directory.Exists (AddinCachePath)) { + Directory.CreateDirectory (AddinCachePath); + hasChanges = true; + } + + if (!Directory.Exists (AddinFolderCachePath)) { + Directory.CreateDirectory (AddinFolderCachePath); + hasChanges = true; + } + + // Make sure we can write in those folders + + Util.CheckWrittableFloder (AddinCachePath); + Util.CheckWrittableFloder (AddinFolderCachePath); + + fatalDatabseError = false; + } + catch (Exception ex) { + monitor.ReportError ("Add-in cache directory could not be created", ex); + fatalDatabseError = true; + monitor.Cancel (); + } + return hasChanges; + } + + + internal bool CheckFolders (IProgressStatus monitor, string domain) + { + using (fileDatabase.LockRead ()) { + AddinScanResult scanResult = new AddinScanResult (); + scanResult.CheckOnly = true; + scanResult.Domain = domain; + InternalScanFolders (monitor, scanResult); + return scanResult.ChangesFound; + } + } + + internal void ScanFolders (IProgressStatus monitor, string currentDomain, string folderToScan, StringCollection filesToIgnore) + { + AddinScanResult res = new AddinScanResult (); + res.Domain = currentDomain; + res.AddPathsToIgnore (filesToIgnore); + ScanFolders (monitor, res); + } + + void ScanFolders (IProgressStatus monitor, AddinScanResult scanResult) + { + IDisposable checkLock = null; + + if (scanResult.CheckOnly) + checkLock = fileDatabase.LockRead (); + else { + // All changes are done in a transaction, which won't be committed until + // all files have been updated. + + if (!fileDatabase.BeginTransaction ()) { + // The database is already being updated. Can't do anything for now. + return; + } + } + + EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve"); + ResolveEventHandler resolver = new ResolveEventHandler (OnResolveAddinAssembly); + + try + { + // Perform the add-in scan + + if (!scanResult.CheckOnly) { + AppDomain.CurrentDomain.AssemblyResolve += resolver; + if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver); + } + + InternalScanFolders (monitor, scanResult); + + if (!scanResult.CheckOnly) + fileDatabase.CommitTransaction (); + } + catch { + if (!scanResult.CheckOnly) + fileDatabase.RollbackTransaction (); + throw; + } + finally { + currentScanResult = null; + + if (scanResult.CheckOnly) + checkLock.Dispose (); + else { + AppDomain.CurrentDomain.AssemblyResolve -= resolver; + if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver); + } + } + } + + void InternalScanFolders (IProgressStatus monitor, AddinScanResult scanResult) + { + try { + fs.ScanStarted (); + InternalScanFolders2 (monitor, scanResult); + } finally { + fs.ScanFinished (); + } + } + + void InternalScanFolders2 (IProgressStatus monitor, AddinScanResult scanResult) + { + DateTime tim = DateTime.Now; + + DatabaseInfrastructureCheck (monitor); + if (monitor.IsCanceled) + return; + + try { + scanResult.HostIndex = GetAddinHostIndex (); + } + catch (Exception ex) { + if (scanResult.CheckOnly) { + scanResult.ChangesFound = true; + return; + } + monitor.ReportError ("Add-in root index is corrupt. The add-in database will be regenerated.", ex); + scanResult.RegenerateAllData = true; + } + + AddinScanner scanner = new AddinScanner (this, scanResult, monitor); + try { + + // Check if any of the previously scanned folders has been deleted + + foreach (string file in Directory.GetFiles (AddinFolderCachePath, "*.data")) { + AddinScanFolderInfo folderInfo; + bool res = ReadFolderInfo (monitor, file, out folderInfo); + bool validForDomain = scanResult.Domain == null || folderInfo.Domain == GlobalDomain || folderInfo.Domain == scanResult.Domain; + if (!res || (validForDomain && !fs.DirectoryExists (folderInfo.Folder))) { + if (res) { + // Folder has been deleted. Remove the add-ins it had. + scanner.UpdateDeletedAddins (monitor, folderInfo, scanResult); + } else { + // Folder info file corrupt. Regenerate all. + scanResult.ChangesFound = true; + scanResult.RegenerateRelationData = true; + } + + if (!scanResult.CheckOnly) + SafeDelete (monitor, file); + else if (scanResult.ChangesFound) + return; + } + } + + // Look for changes in the add-in folders + + if (registry.StartupDirectory != null) + scanner.ScanFolder (monitor, registry.StartupDirectory, null, scanResult); + + if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled)) + return; + + if (scanResult.Domain == null) + scanner.ScanFolder (monitor, HostsPath, GlobalDomain, scanResult); + + if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled)) + return; + + foreach (string dir in registry.GlobalAddinDirectories) { + if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled)) + return; + scanner.ScanFolderRec (monitor, dir, GlobalDomain, scanResult); + } + + if (scanResult.CheckOnly || !scanResult.ChangesFound) + return; + + // Scan the files which have been modified + + currentScanResult = scanResult; + + foreach (FileToScan file in scanResult.FilesToScan) + scanner.ScanFile (monitor, file.File, file.AddinScanFolderInfo, scanResult); + } finally { + scanner.CleanupReflector (); + } + + // Save folder info + + foreach (AddinScanFolderInfo finfo in scanResult.ModifiedFolderInfos) + SaveFolderInfo (monitor, finfo); + + if (monitor.LogLevel > 1) + monitor.Log ("Folders scan completed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)"); + + SaveAddinHostIndex (); + ResetCachedData (); + + if (!scanResult.ChangesFound) { + if (monitor.LogLevel > 1) + monitor.Log ("No changes found"); + return; + } + + tim = DateTime.Now; + try { + if (scanResult.RegenerateRelationData) { + if (monitor.LogLevel > 1) + monitor.Log ("Regenerating all add-in relations."); + scanResult.AddinsToUpdate = null; + scanResult.AddinsToUpdateRelations = null; + } + + GenerateAddinExtensionMapsInternal (monitor, scanResult.Domain, scanResult.AddinsToUpdate, scanResult.AddinsToUpdateRelations, scanResult.RemovedAddins); + } + catch (Exception ex) { + fatalDatabseError = true; + monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", ex); + } + + if (monitor.LogLevel > 1) + monitor.Log ("Add-in relations analyzed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)"); + + SaveAddinHostIndex (); + } + + public void ParseAddin (IProgressStatus progressStatus, string domain, string file, string outFile, bool inProcess) + { + if (!inProcess) { + ISetupHandler setup = GetSetupHandler (); + setup.GetAddinDescription (progressStatus, registry, Path.GetFullPath (file), outFile); + return; + } + + using (fileDatabase.LockRead ()) + { + // First of all, check if the file belongs to a registered add-in + AddinScanFolderInfo finfo; + if (GetFolderInfoForPath (progressStatus, Path.GetDirectoryName (file), out finfo) && finfo != null) { + AddinFileInfo afi = finfo.GetAddinFileInfo (file); + if (afi != null && afi.IsAddin) { + AddinDescription adesc; + GetAddinDescription (progressStatus, afi.Domain, afi.AddinId, file, out adesc); + if (adesc != null) + adesc.Save (outFile); + return; + } + } + + AddinScanResult sr = new AddinScanResult (); + sr.Domain = domain; + AddinScanner scanner = new AddinScanner (this, sr, progressStatus); + + SingleFileAssemblyResolver res = new SingleFileAssemblyResolver (progressStatus, registry, scanner); + ResolveEventHandler resolver = new ResolveEventHandler (res.Resolve); + + EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve"); + + try { + AppDomain.CurrentDomain.AssemblyResolve += resolver; + if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver); + + AddinDescription desc = scanner.ScanSingleFile (progressStatus, file, sr); + if (desc != null) { + // Reset the xml doc so that it is not reused when saving. We want a brand new document + desc.ResetXmlDoc (); + desc.Save (outFile); + } + } + finally { + scanner.CleanupReflector (); + AppDomain.CurrentDomain.AssemblyResolve -= resolver; + if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver); + } + } + } + + public string GetFolderDomain (IProgressStatus progressStatus, string path) + { + AddinScanFolderInfo folderInfo; + + if (GetFolderInfoForPath (progressStatus, path, out folderInfo) && folderInfo == null) { + if (path.Length > 0 && path [path.Length - 1] != Path.DirectorySeparatorChar) + // Try again by appending a directory separator at the end. Some directories are registered like this. + GetFolderInfoForPath (progressStatus, path + Path.DirectorySeparatorChar, out folderInfo); + else if (path.Length > 0 && path [path.Length - 1] == Path.DirectorySeparatorChar) + // Try again by removing the directory separator at the end. Some directories are registered like this. + GetFolderInfoForPath (progressStatus, path.TrimEnd (Path.DirectorySeparatorChar), out folderInfo); + } + if (folderInfo != null && !string.IsNullOrEmpty (folderInfo.Domain)) + return folderInfo.Domain; + else + return UnknownDomain; + } + + Assembly OnResolveAddinAssembly (object s, ResolveEventArgs args) + { + string file = currentScanResult != null ? currentScanResult.GetAssemblyLocation (args.Name) : null; + if (file != null) + return Util.LoadAssemblyForReflection (file); + else { + if (!args.Name.StartsWith ("Mono.Addins.CecilReflector")) + Console.WriteLine ("Assembly not found: " + args.Name); + return null; + } + } + + public string GetFolderConfigFile (string path) + { + path = Path.GetFullPath (path); + + string s = path.Replace ("_", "__"); + s = s.Replace (Path.DirectorySeparatorChar, '_'); + s = s.Replace (Path.AltDirectorySeparatorChar, '_'); + s = s.Replace (Path.VolumeSeparatorChar, '_'); + + return Path.Combine (AddinFolderCachePath, s + ".data"); + } + + internal void UninstallAddin (IProgressStatus monitor, string domain, string addinId, string addinFile, AddinScanResult scanResult) + { + AddinDescription desc; + + if (!GetAddinDescription (monitor, domain, addinId, addinFile, out desc)) { + // If we can't get information about the old assembly, just regenerate all relation data + scanResult.RegenerateRelationData = true; + return; + } + + scanResult.AddRemovedAddin (addinId); + + // If the add-in didn't exist, there is nothing left to do + + if (desc == null) + return; + + // If the add-in already existed, the dependencies of the old add-in need to be re-analyzed + + Util.AddDependencies (desc, scanResult); + if (desc.IsRoot) + scanResult.HostIndex.RemoveHostData (desc.AddinId, desc.AddinFile); + + RemoveAddinDescriptionFile (monitor, desc.FileName); + } + + public bool GetAddinDescription (IProgressStatus monitor, string domain, string addinId, string addinFile, out AddinDescription description) + { + // If the same add-in is installed in different folders (in the same domain) there will be several .maddin files for it, + // using the suffix "_X" where X is a number > 1 (for example: someAddin,1.0.maddin, someAddin,1.0.maddin_2, someAddin,1.0.maddin_3, ...) + // We need to return the .maddin whose AddinFile matches the one being requested + + addinFile = Path.GetFullPath (addinFile); + int altNum = 1; + string baseFile = GetDescriptionPath (domain, addinId); + string file = baseFile; + bool failed = false; + + do { + if (!ReadAddinDescription (monitor, file, out description)) { + // Remove the AddinDescription here since it is corrupted. + // Avoids creating alternate versions of corrupted files when later calling SaveDescription. + RemoveAddinDescriptionFile (monitor, file); + failed = true; + continue; + } + if (description == null) + break; + if (Path.GetFullPath (description.AddinFile) == addinFile) + return true; + file = baseFile + "_" + (++altNum); + } + while (fileDatabase.Exists (file)); + + // File not found. Return false only if there has been any read error. + description = null; + return failed; + } + + bool RemoveAddinDescriptionFile (IProgressStatus monitor, string file) + { + // Removes an add-in description and shifts up alternate instances of the description file + // (so xxx,1.0.maddin_2 will become xxx,1.0.maddin, xxx,1.0.maddin_3 -> xxx,1.0.maddin_2, etc) + + if (!SafeDelete (monitor, file)) + return false; + + int dversion; + if (file.EndsWith (".maddin")) + dversion = 2; + else { + int i = file.LastIndexOf ('_'); + dversion = 1 + int.Parse (file.Substring (i + 1)); + file = file.Substring (0, i); + } + + while (fileDatabase.Exists (file + "_" + dversion)) { + string newFile = dversion == 2 ? file : file + "_" + (dversion-1); + try { + fileDatabase.Rename (file + "_" + dversion, newFile); + } catch (Exception ex) { + if (monitor.LogLevel > 1) { + monitor.Log ("Could not rename file '" + file + "_" + dversion + "' to '" + newFile + "'"); + monitor.Log (ex.ToString ()); + } + } + dversion++; + } + string dir = Path.GetDirectoryName (file); + if (fileDatabase.DirectoryIsEmpty (dir)) + SafeDeleteDir (monitor, dir); + + if (dversion == 2) { + // All versions of the add-in removed. + SafeDeleteDir (monitor, Path.Combine (AddinPrivateDataPath, Path.GetFileNameWithoutExtension (file))); + } + + return true; + } + + public bool ReadAddinDescription (IProgressStatus monitor, string file, out AddinDescription description) + { + try { + description = AddinDescription.ReadBinary (fileDatabase, file); + if (description != null) + description.OwnerDatabase = this; + return true; + } + catch (Exception ex) { + if (monitor == null) + throw; + description = null; + monitor.ReportError ("Could not read folder info file", ex); + return false; + } + } + + public bool SaveDescription (IProgressStatus monitor, AddinDescription desc, string replaceFileName) + { + try { + if (replaceFileName != null) + desc.SaveBinary (fileDatabase, replaceFileName); + else { + string file = GetDescriptionPath (desc.Domain, desc.AddinId); + string dir = Path.GetDirectoryName (file); + if (!fileDatabase.DirExists (dir)) + fileDatabase.CreateDir (dir); + if (fileDatabase.Exists (file)) { + // Another AddinDescription already exists with the same name. + // Create an alternate AddinDescription file + int altNum = 2; + while (fileDatabase.Exists (file + "_" + altNum)) + altNum++; + file = file + "_" + altNum; + } + desc.SaveBinary (fileDatabase, file); + } + return true; + } + catch (Exception ex) { + monitor.ReportError ("Add-in info file could not be saved", ex); + return false; + } + } + + public bool AddinDescriptionExists (string domain, string addinId) + { + string file = GetDescriptionPath (domain, addinId); + return fileDatabase.Exists (file); + } + + public bool ReadFolderInfo (IProgressStatus monitor, string file, out AddinScanFolderInfo folderInfo) + { + try { + folderInfo = AddinScanFolderInfo.Read (fileDatabase, file); + return true; + } + catch (Exception ex) { + folderInfo = null; + monitor.ReportError ("Could not read folder info file", ex); + return false; + } + } + + public bool GetFolderInfoForPath (IProgressStatus monitor, string path, out AddinScanFolderInfo folderInfo) + { + try { + folderInfo = AddinScanFolderInfo.Read (fileDatabase, AddinFolderCachePath, path); + return true; + } + catch (Exception ex) { + folderInfo = null; + if (monitor != null) + monitor.ReportError ("Could not read folder info file", ex); + return false; + } + } + + public bool SaveFolderInfo (IProgressStatus monitor, AddinScanFolderInfo folderInfo) + { + try { + folderInfo.Write (fileDatabase, AddinFolderCachePath); + return true; + } + catch (Exception ex) { + monitor.ReportError ("Could not write folder info file", ex); + return false; + } + } + + public bool DeleteFolderInfo (IProgressStatus monitor, AddinScanFolderInfo folderInfo) + { + return SafeDelete (monitor, folderInfo.FileName); + } + + public bool SafeDelete (IProgressStatus monitor, string file) + { + try { + fileDatabase.Delete (file); + return true; + } + catch (Exception ex) { + if (monitor.LogLevel > 1) { + monitor.Log ("Could not delete file: " + file); + monitor.Log (ex.ToString ()); + } + return false; + } + } + + public bool SafeDeleteDir (IProgressStatus monitor, string dir) + { + try { + fileDatabase.DeleteDir (dir); + return true; + } + catch (Exception ex) { + if (monitor.LogLevel > 1) { + monitor.Log ("Could not delete directory: " + dir); + monitor.Log (ex.ToString ()); + } + return false; + } + } + + AddinHostIndex GetAddinHostIndex () + { + if (hostIndex != null) + return hostIndex; + + using (fileDatabase.LockRead ()) { + if (fileDatabase.Exists (HostIndexFile)) + hostIndex = AddinHostIndex.Read (fileDatabase, HostIndexFile); + else + hostIndex = new AddinHostIndex (); + } + return hostIndex; + } + + void SaveAddinHostIndex () + { + if (hostIndex != null) + hostIndex.Write (fileDatabase, HostIndexFile); + } + + internal string GetUniqueAddinId (string file, string oldId, string ns, string version) + { + string baseId = "__" + Path.GetFileNameWithoutExtension (file); + + if (Path.GetExtension (baseId) == ".addin") + baseId = Path.GetFileNameWithoutExtension (baseId); + + string name = baseId; + string id = Addin.GetFullId (ns, name, version); + + // If the old Id is already an automatically generated one, reuse it + if (oldId != null && oldId.StartsWith (id)) + return name; + + int n = 1; + while (AddinIdExists (id)) { + name = baseId + "_" + n; + id = Addin.GetFullId (ns, name, version); + n++; + } + return name; + } + + bool AddinIdExists (string id) + { + foreach (string d in fileDatabase.GetDirectories (AddinCachePath)) { + if (fileDatabase.Exists (Path.Combine (d, id + ".addin"))) + return true; + } + return false; + } + + ISetupHandler GetSetupHandler () + { +// if (Util.IsMono) +// return new SetupProcess (); +// else + if (fs.RequiresIsolation) + return new SetupDomain (); + else + return new SetupLocal (); + } + + public void ResetConfiguration () + { + if (File.Exists (ConfigFile)) + File.Delete (ConfigFile); + config = null; + ResetCachedData (); + } + + DatabaseConfiguration Configuration { + get { + if (config == null) { + using (fileDatabase.LockRead ()) { + if (fileDatabase.Exists (ConfigFile)) + config = DatabaseConfiguration.Read (ConfigFile); + else + config = DatabaseConfiguration.ReadAppConfig (); + } + } + return config; + } + } + + void SaveConfiguration () + { + if (config != null) { + using (fileDatabase.LockWrite ()) { + config.Write (ConfigFile); + } + } + } + } + + class SingleFileAssemblyResolver + { + AddinScanResult scanResult; + AddinScanner scanner; + AddinRegistry registry; + IProgressStatus progressStatus; + + public SingleFileAssemblyResolver (IProgressStatus progressStatus, AddinRegistry registry, AddinScanner scanner) + { + this.scanner = scanner; + this.registry = registry; + this.progressStatus = progressStatus; + } + + public Assembly Resolve (object s, ResolveEventArgs args) + { + if (scanResult == null) { + scanResult = new AddinScanResult (); + scanResult.LocateAssembliesOnly = true; + + if (registry.StartupDirectory != null) + scanner.ScanFolder (progressStatus, registry.StartupDirectory, null, scanResult); + foreach (string dir in registry.GlobalAddinDirectories) + scanner.ScanFolderRec (progressStatus, dir, AddinDatabase.GlobalDomain, scanResult); + } + + string afile = scanResult.GetAssemblyLocation (args.Name); + if (afile != null) + return Util.LoadAssemblyForReflection (afile); + else + return null; + } + } + + class AddinIndex + { + Dictionary> addins = new Dictionary> (); + + public void Add (AddinDescription desc) + { + string id = Addin.GetFullId (desc.Namespace, desc.LocalId, null); + List list; + if (!addins.TryGetValue (id, out list)) + addins [id] = list = new List (); + list.Add (desc); + } + + List FindDescriptions (string domain, string fullid) + { + // Returns all registered add-ins which are compatible with the provided + // fullid. Compatible means that the id is the same and the version is within + // the range of compatible versions of the add-in. + + var res = new List (); + string id = Addin.GetIdName (fullid); + List list; + if (!addins.TryGetValue (id, out list)) + return res; + string version = Addin.GetIdVersion (fullid); + foreach (AddinDescription desc in list) { + if ((desc.Domain == domain || domain == AddinDatabase.GlobalDomain) && desc.SupportsVersion (version)) + res.Add (desc); + } + return res; + } + + public IEnumerable GetMissingDependencies (AddinDescription desc, ModuleDescription mod) + { + foreach (Dependency dep in mod.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) + continue; + var descs = FindDescriptions (desc.Domain, adep.FullAddinId); + if (descs.Count == 0) + yield return adep.FullAddinId; + } + } + + public AddinDescription GetSimilarExistingAddin (AddinDescription conf, string addinId) + { + string domain = conf.Domain; + List list; + if (!addins.TryGetValue (Addin.GetIdName (addinId), out list)) + return null; + string version = Addin.GetIdVersion (addinId); + foreach (AddinDescription desc in list) { + if ((desc.Domain == domain || domain == AddinDatabase.GlobalDomain) && !desc.SupportsVersion (version)) + return desc; + } + return null; + } + + public string FindCondition (AddinDescription desc, ModuleDescription mod, string conditionId) + { + if (desc.ConditionTypes.Any (c => c.Id == conditionId)) + return desc.AddinId; + + foreach (Dependency dep in mod.Dependencies) { + AddinDependency adep = dep as AddinDependency; + + if (adep == null) + continue; + var descs = FindDescriptions (desc.Domain, adep.FullAddinId); + foreach (var d in descs) { + var c = FindCondition (d, d.MainModule, conditionId); + if (c != null) + return c; + } + } + return null; + } + + public List GetSortedAddins () + { + var inserted = new HashSet (); + var lists = new Dictionary> (); + + foreach (List dlist in addins.Values) { + foreach (AddinDescription desc in dlist) + InsertSortedAddin (inserted, lists, desc); + } + + // Merge all domain lists into a single list. + // Make sure the global domain is inserted the last + + List global; + lists.TryGetValue (AddinDatabase.GlobalDomain, out global); + lists.Remove (AddinDatabase.GlobalDomain); + + List sortedAddins = new List (); + foreach (var dl in lists.Values) { + sortedAddins.AddRange (dl); + } + if (global != null) + sortedAddins.AddRange (global); + return sortedAddins; + } + + void InsertSortedAddin (HashSet inserted, Dictionary> lists, AddinDescription desc) + { + string sid = desc.AddinId + " " + desc.Domain; + if (!inserted.Add (sid)) + return; + + foreach (ModuleDescription mod in desc.AllModules) { + foreach (Dependency dep in mod.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) + continue; + var descs = FindDescriptions (desc.Domain, adep.FullAddinId); + if (descs.Count > 0) { + foreach (AddinDescription sd in descs) + InsertSortedAddin (inserted, lists, sd); + } + } + } + List list; + if (!lists.TryGetValue (desc.Domain, out list)) + lists [desc.Domain] = list = new List (); + + list.Add (desc); + } + } + + // Keep in sync with AddinSearchFlags + enum AddinSearchFlagsInternal + { + IncludeAddins = 1, + IncludeRoots = 1 << 1, + IncludeAll = IncludeAddins | IncludeRoots, + LatestVersionsOnly = 1 << 3, + ExcludePendingUninstall = 1 << 4 + } +} + + diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/AddinFileSystemExtension.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinFileSystemExtension.cs new file mode 100644 index 00000000..4dd8d37b --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinFileSystemExtension.cs @@ -0,0 +1,212 @@ +// +// AddinFileSystemExtension.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.IO; +using System.Reflection; + +namespace Mono.Addins.Database +{ + /// + /// An add-in file system extension. + /// + /// + /// File system extensions can override the behavior of the add-in scanner and provide custom rules for + /// locating and scanning assemblies. + /// + public class AddinFileSystemExtension + { + IAssemblyReflector reflector; + + /// + /// Called when the add-in scan is about to start + /// + public virtual void ScanStarted () + { + } + + /// + /// Called when the add-in scan has finished + /// + public virtual void ScanFinished () + { + } + + /// + /// Checks if a directory exists + /// + /// + /// 'true' if the directory exists + /// + /// + /// Directory path + /// + public virtual bool DirectoryExists (string path) + { + return Directory.Exists (path); + } + + /// + /// Checks if a file exists + /// + /// + /// 'true' if the file exists + /// + /// + /// Path to the file + /// + public virtual bool FileExists (string path) + { + return File.Exists (path); + } + + /// + /// Gets the files in a directory + /// + /// + /// The full path of the files in the directory + /// + /// + /// Directory path + /// + public virtual System.Collections.Generic.IEnumerable GetFiles (string path) + { + return Directory.GetFiles (path); + } + + /// + /// Gets the subdirectories of a directory + /// + /// + /// The subdirectories. + /// + /// + /// The directory + /// + public virtual System.Collections.Generic.IEnumerable GetDirectories (string path) + { + return Directory.GetDirectories (path); + } + + /// + /// Gets the last write time of a file + /// + /// + /// The last write time. + /// + /// + /// File path. + /// + public virtual DateTime GetLastWriteTime (string filePath) + { + return File.GetLastWriteTime (filePath); + } + + /// + /// Opens a text file + /// + /// + /// The text file stream + /// + /// + /// File path. + /// + public virtual System.IO.StreamReader OpenTextFile (string path) + { + return new StreamReader (path); + } + + /// + /// Opens a file. + /// + /// + /// The file stream. + /// + /// + /// The file path. + /// + public virtual System.IO.Stream OpenFile (string path) + { + return File.OpenRead (path); + } + + /// + /// Gets an assembly reflector for a file. + /// + /// + /// The reflector for the file. + /// + /// + /// An assembly locator + /// + /// + /// A file path + /// + public virtual IAssemblyReflector GetReflectorForFile (IAssemblyLocator locator, string path) + { + if (reflector != null) + return reflector; + + // If there is a local copy of the cecil reflector, use it instead of the one in the gac + Type t; + string asmFile = Path.Combine (Path.GetDirectoryName (GetType().Assembly.Location), "Mono.Addins.CecilReflector.dll"); + if (File.Exists (asmFile)) { + Assembly asm = Assembly.LoadFrom (asmFile); + t = asm.GetType ("Mono.Addins.CecilReflector.Reflector"); + } + else { + string refName = GetType().Assembly.FullName; + int i = refName.IndexOf (','); + refName = "Mono.Addins.CecilReflector.Reflector, Mono.Addins.CecilReflector" + refName.Substring (i); + t = Type.GetType (refName, false); + } + if (t != null) + reflector = (IAssemblyReflector) Activator.CreateInstance (t); + else + reflector = new DefaultAssemblyReflector (); + + reflector.Initialize (locator); + return reflector; + } + + internal void CleanupReflector() + { + var disposable = reflector as IDisposable; + if (disposable != null) + disposable.Dispose (); + } + + /// + /// Gets a value indicating whether this needs to be isolated from the main execution process + /// + /// + /// true if requires isolation; otherwise, false. + /// + public virtual bool RequiresIsolation { + get { return true; } + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/AddinHostIndex.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinHostIndex.cs new file mode 100644 index 00000000..79194971 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinHostIndex.cs @@ -0,0 +1,109 @@ +// +// AddinHostIndex.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using Mono.Addins.Serialization; +using System.IO; + +namespace Mono.Addins.Database +{ + class AddinHostIndex: IBinaryXmlElement + { + static BinaryXmlTypeMap typeMap = new BinaryXmlTypeMap (typeof(AddinHostIndex)); + + Hashtable index = new Hashtable (); + + public void RegisterAssembly (string assemblyLocation, string addinId, string addinLocation, string domain) + { + assemblyLocation = NormalizeFileName (assemblyLocation); + index [Path.GetFullPath (assemblyLocation)] = addinId + " " + addinLocation + " " + domain; + } + + public bool GetAddinForAssembly (string assemblyLocation, out string addinId, out string addinLocation, out string domain) + { + assemblyLocation = NormalizeFileName (assemblyLocation); + string s = index [Path.GetFullPath (assemblyLocation)] as string; + if (s == null) { + addinId = null; + addinLocation = null; + domain = null; + return false; + } + else { + int i = s.IndexOf (' '); + int j = s.LastIndexOf (' '); + addinId = s.Substring (0, i); + addinLocation = s.Substring (i+1, j-i-1); + domain = s.Substring (j+1); + return true; + } + } + + public void RemoveHostData (string addinId, string addinLocation) + { + string loc = addinId + " " + Path.GetFullPath (addinLocation) + " "; + ArrayList todelete = new ArrayList (); + foreach (DictionaryEntry e in index) { + if (((string)e.Value).StartsWith (loc)) + todelete.Add (e.Key); + } + foreach (string s in todelete) + index.Remove (s); + } + + public static AddinHostIndex Read (FileDatabase fileDatabase, string file) + { + return (AddinHostIndex) fileDatabase.ReadObject (file, typeMap); + } + + public void Write (FileDatabase fileDatabase, string file) + { + fileDatabase.WriteObject (file, this, typeMap); + } + + void IBinaryXmlElement.Write (BinaryXmlWriter writer) + { + writer.WriteValue ("index", index); + } + + void IBinaryXmlElement.Read (BinaryXmlReader reader) + { + reader.ReadValue ("index", index); + } + + string NormalizeFileName (string name) + { + if (Util.IsWindows) + return name.ToLower (); + else + return name; + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanFolderInfo.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanFolderInfo.cs new file mode 100644 index 00000000..1641de98 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanFolderInfo.cs @@ -0,0 +1,277 @@ +// +// AddinScanFolderInfo.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.IO; +using System.Collections; +using System.Collections.Specialized; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Database +{ + class AddinScanFolderInfo: IBinaryXmlElement + { + Hashtable files = new Hashtable (); + string folder; + string fileName; + string domain; + bool sharedFolder = true; + + static BinaryXmlTypeMap typeMap = new BinaryXmlTypeMap ( + typeof(AddinScanFolderInfo), + typeof(AddinFileInfo) + ); + + internal AddinScanFolderInfo () + { + } + + public AddinScanFolderInfo (string folder) + { + this.folder = folder; + } + + public string FileName { + get { return fileName; } + } + + public static AddinScanFolderInfo Read (FileDatabase filedb, string file) + { + AddinScanFolderInfo finfo = (AddinScanFolderInfo) filedb.ReadSharedObject (file, typeMap); + if (finfo != null) + finfo.fileName = file; + return finfo; + } + + public static AddinScanFolderInfo Read (FileDatabase filedb, string basePath, string folderPath) + { + string fileName; + AddinScanFolderInfo finfo = (AddinScanFolderInfo) filedb.ReadSharedObject (basePath, GetDomain (folderPath), ".data", Path.GetFullPath (folderPath), typeMap, out fileName); + if (finfo != null) + finfo.fileName = fileName; + return finfo; + } + + static string GetDomain (string path) + { + path = Path.GetFullPath (path); + string s = path.Replace (Path.DirectorySeparatorChar, '_'); + s = s.Replace (Path.AltDirectorySeparatorChar, '_'); + s = s.Replace (Path.VolumeSeparatorChar, '_'); + s = s.Trim ('_'); + return s; + } + + public void Write (FileDatabase filedb, string basePath) + { + filedb.WriteSharedObject (basePath, GetDomain (folder), ".data", Path.GetFullPath (folder), fileName, typeMap, this); + } + + public string GetExistingLocalDomain () + { + foreach (AddinFileInfo info in files.Values) { + if (info.Domain != null && info.Domain != AddinDatabase.GlobalDomain) + return info.Domain; + } + return AddinDatabase.GlobalDomain; + } + + public string Folder { + get { return folder; } + } + + public string Domain { + get { + if (sharedFolder) + return AddinDatabase.GlobalDomain; + else + return domain; + } + set { + domain = value; + sharedFolder = true; + } + } + + public string RootsDomain { + get { + return domain; + } + set { + domain = value; + } + } + + public string GetDomain (bool isRoot) + { + if (isRoot) + return RootsDomain; + else + return Domain; + } + + public bool SharedFolder { + get { + return sharedFolder; + } + set { + sharedFolder = value; + } + } + + public DateTime GetLastScanTime (string file) + { + AddinFileInfo info = (AddinFileInfo) files [file]; + if (info == null) + return DateTime.MinValue; + else + return info.LastScan; + } + + public AddinFileInfo GetAddinFileInfo (string file) + { + return (AddinFileInfo) files [file]; + } + + public AddinFileInfo SetLastScanTime (string file, string addinId, bool isRoot, DateTime time, bool scanError) + { + AddinFileInfo info = (AddinFileInfo) files [file]; + if (info == null) { + info = new AddinFileInfo (); + info.File = file; + files [file] = info; + } + info.LastScan = time; + info.AddinId = addinId; + info.IsRoot = isRoot; + info.ScanError = scanError; + if (addinId != null) + info.Domain = GetDomain (isRoot); + else + info.Domain = null; + return info; + } + + public ArrayList GetMissingAddins (AddinFileSystemExtension fs) + { + ArrayList missing = new ArrayList (); + + if (!fs.DirectoryExists (folder)) { + // All deleted + foreach (AddinFileInfo info in files.Values) { + if (info.IsAddin) + missing.Add (info); + } + files.Clear (); + return missing; + } + ArrayList toDelete = new ArrayList (); + foreach (AddinFileInfo info in files.Values) { + if (!fs.FileExists (info.File)) { + if (info.IsAddin) + missing.Add (info); + toDelete.Add (info.File); + } + else if (info.IsAddin && info.Domain != GetDomain (info.IsRoot)) { + missing.Add (info); + } + } + foreach (string file in toDelete) + files.Remove (file); + + return missing; + } + + void IBinaryXmlElement.Write (BinaryXmlWriter writer) + { + if (files.Count == 0) { + domain = null; + sharedFolder = true; + } + writer.WriteValue ("folder", folder); + writer.WriteValue ("files", files); + writer.WriteValue ("domain", domain); + writer.WriteValue ("sharedFolder", sharedFolder); + } + + void IBinaryXmlElement.Read (BinaryXmlReader reader) + { + folder = reader.ReadStringValue ("folder"); + reader.ReadValue ("files", files); + domain = reader.ReadStringValue ("domain"); + sharedFolder = reader.ReadBooleanValue ("sharedFolder"); + } + } + + + class AddinFileInfo: IBinaryXmlElement + { + public string File; + public DateTime LastScan; + public string AddinId; + public bool IsRoot; + public bool ScanError; + public string Domain; + public StringCollection IgnorePaths; + + public bool IsAddin { + get { return AddinId != null && AddinId.Length != 0; } + } + + public void AddPathToIgnore (string path) + { + if (IgnorePaths == null) + IgnorePaths = new StringCollection (); + IgnorePaths.Add (path); + } + + void IBinaryXmlElement.Write (BinaryXmlWriter writer) + { + writer.WriteValue ("File", File); + writer.WriteValue ("LastScan", LastScan); + writer.WriteValue ("AddinId", AddinId); + writer.WriteValue ("IsRoot", IsRoot); + writer.WriteValue ("ScanError", ScanError); + writer.WriteValue ("Domain", Domain); + if (IgnorePaths != null && IgnorePaths.Count > 0) + writer.WriteValue ("IgnorePaths", IgnorePaths); + } + + void IBinaryXmlElement.Read (BinaryXmlReader reader) + { + File = reader.ReadStringValue ("File"); + LastScan = reader.ReadDateTimeValue ("LastScan"); + AddinId = reader.ReadStringValue ("AddinId"); + IsRoot = reader.ReadBooleanValue ("IsRoot"); + ScanError = reader.ReadBooleanValue ("ScanError"); + Domain = reader.ReadStringValue ("Domain"); + IgnorePaths = (StringCollection) reader.ReadValue ("IgnorePaths", new StringCollection ()); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanResult.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanResult.cs new file mode 100644 index 00000000..e33becc2 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanResult.cs @@ -0,0 +1,210 @@ +// +// AddinScanResult.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; + +namespace Mono.Addins.Database +{ + internal class AddinScanResult: MarshalByRefObject, IAssemblyLocator + { + internal ArrayList AddinsToScan = new ArrayList (); + internal List AddinsToUpdateRelations = new List (); + internal List AddinsToUpdate = new List (); + internal ArrayList FilesToScan = new ArrayList (); + internal ArrayList ModifiedFolderInfos = new ArrayList (); + internal ArrayList FilesWithScanFailure = new ArrayList (); + internal AddinHostIndex HostIndex; + internal List RemovedAddins = new List (); + Hashtable visitedFolders = new Hashtable (); + + Hashtable assemblyLocations = new Hashtable (); + Hashtable assemblyLocationsByFullName = new Hashtable (); + Hashtable filesToIgnore; + + bool regenerateRelationData; + bool changesFound; + + public bool RegenerateAllData; + public bool CheckOnly; + public bool LocateAssembliesOnly; + public string Domain; + + public bool ChangesFound { + get { return changesFound; } + set { changesFound = value; } + } + + public bool RegenerateRelationData { + get { return regenerateRelationData; } + set { + regenerateRelationData = value; + if (value) + ChangesFound = true; + } + } + + public bool VisitFolder (string folder) + { + if (visitedFolders.Contains (folder) || IgnorePath (folder)) + return false; + else { + visitedFolders.Add (folder, folder); + return true; + } + } + + public bool IgnorePath (string file) + { + if (filesToIgnore == null) + return false; + string root = Path.GetPathRoot (file); + while (root != file) { + if (filesToIgnore.Contains (file)) + return true; + file = Path.GetDirectoryName (file); + } + return false; + } + + public void AddPathToIgnore (string path) + { + if (filesToIgnore == null) + filesToIgnore = new Hashtable (); + filesToIgnore [path] = path; + } + + public void AddPathsToIgnore (IEnumerable paths) + { + foreach (string p in paths) + AddPathToIgnore (p); + } + + public void AddAddinToScan (string addinId) + { + if (!AddinsToScan.Contains (addinId)) + AddinsToScan.Add (addinId); + } + + public void AddRemovedAddin (string addinId) + { + if (!RemovedAddins.Contains (addinId)) + RemovedAddins.Add (addinId); + } + + public void AddFileToWithFailure (string file) + { + if (!FilesWithScanFailure.Contains (file)) + FilesWithScanFailure.Add (file); + } + + public void AddFileToScan (string file, AddinScanFolderInfo folderInfo) + { + FileToScan di = new FileToScan (); + di.File = file; + di.AddinScanFolderInfo = folderInfo; + FilesToScan.Add (di); + RegisterModifiedFolderInfo (folderInfo); + } + + public void RegisterModifiedFolderInfo (AddinScanFolderInfo folderInfo) + { + if (!ModifiedFolderInfos.Contains (folderInfo)) + ModifiedFolderInfos.Add (folderInfo); + } + + public void AddAddinToUpdateRelations (string addinId) + { + if (!AddinsToUpdateRelations.Contains (addinId)) + AddinsToUpdateRelations.Add (addinId); + } + + public void AddAddinToUpdate (string addinId) + { + if (!AddinsToUpdate.Contains (addinId)) + AddinsToUpdate.Add (addinId); + } + + public void AddAssemblyLocation (string file) + { + string name = Path.GetFileNameWithoutExtension (file); + ArrayList list = assemblyLocations [name] as ArrayList; + if (list == null) { + list = new ArrayList (); + assemblyLocations [name] = list; + } + list.Add (file); + } + + public string GetAssemblyLocation (string fullName) + { + string loc = assemblyLocationsByFullName [fullName] as String; + if (loc != null) + return loc; + + int i = fullName.IndexOf (','); + string name = fullName.Substring (0,i); + if (name == "Mono.Addins") + return GetType ().Assembly.Location; + ArrayList list = assemblyLocations [name] as ArrayList; + if (list == null) + return null; + + string lastAsm = null; + foreach (string file in list.ToArray ()) { + try { + list.Remove (file); + AssemblyName aname = AssemblyName.GetAssemblyName (file); + lastAsm = file; + assemblyLocationsByFullName [aname.FullName] = file; + if (aname.FullName == fullName) + return file; + } catch { + // Could not get the assembly name. The file either doesn't exist or it is not a valid assembly. + // In this case, just ignore it. + } + } + + if (lastAsm != null) { + // If an exact version is not found, just take any of them + return lastAsm; + } + return null; + } + } + + class FileToScan + { + public string File; + public AddinScanFolderInfo AddinScanFolderInfo; + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanner.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanner.cs new file mode 100644 index 00000000..80e0e7db --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinScanner.cs @@ -0,0 +1,1295 @@ +// +// AddinScanner.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Reflection; +using System.Collections.Specialized; +using System.Xml; +using System.ComponentModel; + +using Mono.Addins.Description; + +namespace Mono.Addins.Database +{ + class AddinScanner: MarshalByRefObject + { + AddinDatabase database; + AddinFileSystemExtension fs; + Dictionary coreAssemblies = new Dictionary (); + + public AddinScanner (AddinDatabase database, AddinScanResult scanResult, IProgressStatus monitor) + { + this.database = database; + fs = database.FileSystem; + } + + public void ScanFolder (IProgressStatus monitor, string path, string domain, AddinScanResult scanResult) + { + path = Path.GetFullPath (path); + + // Avoid folders including each other + if (!scanResult.VisitFolder (path)) + return; + + AddinScanFolderInfo folderInfo; + if (!database.GetFolderInfoForPath (monitor, path, out folderInfo)) { + // folderInfo file was corrupt. + // Just in case, we are going to regenerate all relation data. + if (!fs.DirectoryExists (path)) + scanResult.RegenerateRelationData = true; + } else { + // Directory is included but it doesn't exist. Ignore it. + if (folderInfo == null && !fs.DirectoryExists (path)) + return; + } + + // if domain is null it means that a new domain has to be created. + + bool sharedFolder = domain == AddinDatabase.GlobalDomain; + bool isNewFolder = folderInfo == null; + + if (isNewFolder) { + // No folder info. It is the first time this folder is scanned. + // There is no need to store this object if the folder does not + // contain add-ins. + folderInfo = new AddinScanFolderInfo (path); + } + + if (!sharedFolder && (folderInfo.SharedFolder || folderInfo.Domain != domain)) { + // If the folder already has a domain, reuse it + if (domain == null && folderInfo.RootsDomain != null && folderInfo.RootsDomain != AddinDatabase.GlobalDomain) + domain = folderInfo.RootsDomain; + else if (domain == null) { + folderInfo.Domain = domain = database.GetUniqueDomainId (); + scanResult.RegenerateRelationData = true; + } + else { + folderInfo.Domain = domain; + if (!isNewFolder) { + // Domain has changed. Update the folder info and regenerate everything. + scanResult.RegenerateRelationData = true; + scanResult.RegisterModifiedFolderInfo (folderInfo); + } + } + } + else if (!folderInfo.SharedFolder && sharedFolder) { + scanResult.RegenerateRelationData = true; + } + + folderInfo.SharedFolder = sharedFolder; + + // If there is no domain assigned to the host, get one now + if (scanResult.Domain == AddinDatabase.UnknownDomain) + scanResult.Domain = domain; + + // Discard folders not belonging to the required domain + if (scanResult.Domain != null && domain != scanResult.Domain && domain != AddinDatabase.GlobalDomain) { + return; + } + + if (monitor.LogLevel > 1 && !scanResult.LocateAssembliesOnly) + monitor.Log ("Checking: " + path); + + if (fs.DirectoryExists (path)) + { + IEnumerable files = fs.GetFiles (path); + + // First of all, look for .addin files. Addin files must be processed before + // assemblies, because they may add files to the ignore list (i.e., assemblies + // included in .addin files won't be scanned twice). + foreach (string file in files) { + if (file.EndsWith (".addin.xml") || file.EndsWith (".addin")) { + RegisterFileToScan (monitor, file, scanResult, folderInfo); + } + } + + // Now scan assemblies. They can also add files to the ignore list. + + foreach (string file in files) { + string ext = Path.GetExtension (file).ToLower (); + if (ext == ".dll" || ext == ".exe") { + RegisterFileToScan (monitor, file, scanResult, folderInfo); + scanResult.AddAssemblyLocation (file); + } + } + + // Finally scan .addins files + + foreach (string file in files) { + if (Path.GetExtension (file).EndsWith (".addins")) { + ScanAddinsFile (monitor, file, domain, scanResult); + } + } + } + else if (!scanResult.LocateAssembliesOnly) { + // The folder has been deleted. All add-ins defined in that folder should also be deleted. + scanResult.RegenerateRelationData = true; + scanResult.ChangesFound = true; + if (scanResult.CheckOnly) + return; + database.DeleteFolderInfo (monitor, folderInfo); + } + + if (scanResult.LocateAssembliesOnly) + return; + + // Look for deleted add-ins. + + UpdateDeletedAddins (monitor, folderInfo, scanResult); + } + + public void UpdateDeletedAddins (IProgressStatus monitor, AddinScanFolderInfo folderInfo, AddinScanResult scanResult) + { + ArrayList missing = folderInfo.GetMissingAddins (fs); + if (missing.Count > 0) { + if (fs.DirectoryExists (folderInfo.Folder)) + scanResult.RegisterModifiedFolderInfo (folderInfo); + scanResult.ChangesFound = true; + if (scanResult.CheckOnly) + return; + + foreach (AddinFileInfo info in missing) { + database.UninstallAddin (monitor, info.Domain, info.AddinId, info.File, scanResult); + } + } + } + + void RegisterFileToScan (IProgressStatus monitor, string file, AddinScanResult scanResult, AddinScanFolderInfo folderInfo) + { + if (scanResult.LocateAssembliesOnly) + return; + + AddinFileInfo finfo = folderInfo.GetAddinFileInfo (file); + bool added = false; + + if (finfo != null && (!finfo.IsAddin || finfo.Domain == folderInfo.GetDomain (finfo.IsRoot)) && fs.GetLastWriteTime (file) == finfo.LastScan && !scanResult.RegenerateAllData) { + if (finfo.ScanError) { + // Always schedule the file for scan if there was an error in a previous scan. + // However, don't set ChangesFound=true, in this way if there isn't any other + // change in the registry, the file won't be scanned again. + scanResult.AddFileToScan (file, folderInfo); + added = true; + } + + if (!finfo.IsAddin) + return; + + if (database.AddinDescriptionExists (finfo.Domain, finfo.AddinId)) { + // It is an add-in and it has not changed. Paths in the ignore list + // are still valid, so they can be used. + if (finfo.IgnorePaths != null) + scanResult.AddPathsToIgnore (finfo.IgnorePaths); + return; + } + } + + scanResult.ChangesFound = true; + + if (!scanResult.CheckOnly && !added) + scanResult.AddFileToScan (file, folderInfo); + } + + public void ScanFile (IProgressStatus monitor, string file, AddinScanFolderInfo folderInfo, AddinScanResult scanResult) + { + if (scanResult.IgnorePath (file)) { + // The file must be ignored. Maybe it caused a crash in a previous scan, or it + // might be included by a .addin file (in which case it will be scanned when processing + // the .addin file). + folderInfo.SetLastScanTime (file, null, false, fs.GetLastWriteTime (file), true); + return; + } + + string ext = Path.GetExtension (file).ToLower (); + if ((ext == ".dll" || ext == ".exe") && !Util.IsManagedAssembly (file)) { + // Ignore dlls and exes which are not managed assemblies + folderInfo.SetLastScanTime (file, null, false, fs.GetLastWriteTime (file), true); + return; + } + + if (monitor.LogLevel > 1) + monitor.Log ("Scanning file: " + file); + + // Log the file to be scanned, so in case of a process crash the main process + // will know what crashed + monitor.Log ("plog:scan:" + file); + + string scannedAddinId = null; + bool scannedIsRoot = false; + bool scanSuccessful = false; + AddinDescription config = null; + + try { + if (ext == ".dll" || ext == ".exe") + scanSuccessful = ScanAssembly (monitor, file, scanResult, out config); + else + scanSuccessful = ScanConfigAssemblies (monitor, file, scanResult, out config); + + if (config != null) { + + AddinFileInfo fi = folderInfo.GetAddinFileInfo (file); + + // If version is not specified, make up one + if (config.Version.Length == 0) { + config.Version = "0.0.0.0"; + } + + if (config.LocalId.Length == 0) { + // Generate an internal id for this add-in + config.LocalId = database.GetUniqueAddinId (file, (fi != null ? fi.AddinId : null), config.Namespace, config.Version); + config.HasUserId = false; + } + + // Check errors in the description + StringCollection errors = config.Verify (fs); + + if (database.IsGlobalRegistry && config.AddinId.IndexOf ('.') == -1) { + errors.Add ("Add-ins registered in the global registry must have a namespace."); + } + + if (errors.Count > 0) { + scanSuccessful = false; + foreach (string err in errors) + monitor.ReportError (string.Format ("{0}: {1}", file, err), null); + } + + // Make sure all extensions sets are initialized with the correct add-in id + + config.SetExtensionsAddinId (config.AddinId); + + scanResult.ChangesFound = true; + + // If the add-in already existed, try to reuse the relation data it had. + // Also, the dependencies of the old add-in need to be re-analyzed + + AddinDescription existingDescription = null; + bool res = database.GetAddinDescription (monitor, folderInfo.Domain, config.AddinId, config.AddinFile, out existingDescription); + + // If we can't get information about the old assembly, just regenerate all relation data + if (!res) + scanResult.RegenerateRelationData = true; + + string replaceFileName = null; + + if (existingDescription != null) { + // Reuse old relation data + config.MergeExternalData (existingDescription); + Util.AddDependencies (existingDescription, scanResult); + replaceFileName = existingDescription.FileName; + } + + // If the scanned file results in an add-in version different from the one obtained from + // previous scans, the old add-in needs to be uninstalled. + if (fi != null && fi.IsAddin && fi.AddinId != config.AddinId) { + database.UninstallAddin (monitor, folderInfo.Domain, fi.AddinId, fi.File, scanResult); + + // If the add-in version has changed, regenerate everything again since old data can't be reused + if (Addin.GetIdName (fi.AddinId) == Addin.GetIdName (config.AddinId)) + scanResult.RegenerateRelationData = true; + } + + // If a description could be generated, save it now (if the scan was successful) + if (scanSuccessful) { + + // Assign the domain + if (config.IsRoot) { + if (folderInfo.RootsDomain == null) { + if (scanResult.Domain != null && scanResult.Domain != AddinDatabase.UnknownDomain && scanResult.Domain != AddinDatabase.GlobalDomain) + folderInfo.RootsDomain = scanResult.Domain; + else + folderInfo.RootsDomain = database.GetUniqueDomainId (); + } + config.Domain = folderInfo.RootsDomain; + } else + config.Domain = folderInfo.Domain; + + if (config.IsRoot && scanResult.HostIndex != null) { + // If the add-in is a root, register its assemblies + foreach (string f in config.MainModule.Assemblies) { + string asmFile = Path.Combine (config.BasePath, Util.NormalizePath (f)); + scanResult.HostIndex.RegisterAssembly (asmFile, config.AddinId, config.AddinFile, config.Domain); + } + } + + // Finally save + + if (database.SaveDescription (monitor, config, replaceFileName)) { + // The new dependencies also have to be updated + Util.AddDependencies (config, scanResult); + scanResult.AddAddinToUpdate (config.AddinId); + scannedAddinId = config.AddinId; + scannedIsRoot = config.IsRoot; + return; + } + } + } + } + catch (Exception ex) { + monitor.ReportError ("Unexpected error while scanning file: " + file, ex); + } + finally { + AddinFileInfo ainfo = folderInfo.SetLastScanTime (file, scannedAddinId, scannedIsRoot, fs.GetLastWriteTime (file), !scanSuccessful); + + if (scanSuccessful && config != null) { + // Update the ignore list in the folder info object. To be used in the next scan + foreach (string df in config.AllIgnorePaths) { + string path = Path.Combine (config.BasePath, Util.NormalizePath (df)); + ainfo.AddPathToIgnore (Path.GetFullPath (path)); + } + } + + monitor.Log ("plog:endscan"); + } + } + + public AddinDescription ScanSingleFile (IProgressStatus monitor, string file, AddinScanResult scanResult) + { + AddinDescription config = null; + + if (monitor.LogLevel > 1) + monitor.Log ("Scanning file: " + file); + + monitor.Log ("plog:scan:" + file); + + try { + string ext = Path.GetExtension (file).ToLower (); + bool scanSuccessful; + + if (ext == ".dll" || ext == ".exe") + scanSuccessful = ScanAssembly (monitor, file, scanResult, out config); + else + scanSuccessful = ScanConfigAssemblies (monitor, file, scanResult, out config); + + if (scanSuccessful && config != null) { + + config.Domain = "global"; + if (config.Version.Length == 0) + config.Version = "0.0.0.0"; + + if (config.LocalId.Length == 0) { + // Generate an internal id for this add-in + config.LocalId = database.GetUniqueAddinId (file, "", config.Namespace, config.Version); + } + } + } + catch (Exception ex) { + monitor.ReportError ("Unexpected error while scanning file: " + file, ex); + } finally { + monitor.Log ("plog:endscan"); + } + return config; + } + + public void ScanAddinsFile (IProgressStatus monitor, string file, string domain, AddinScanResult scanResult) + { + XmlTextReader r = null; + ArrayList directories = new ArrayList (); + ArrayList directoriesWithSubdirs = new ArrayList (); + string basePath = Path.GetDirectoryName (file); + + try { + r = new XmlTextReader (fs.OpenTextFile (file)); + r.MoveToContent (); + if (r.IsEmptyElement) + return; + r.ReadStartElement (); + r.MoveToContent (); + while (r.NodeType != XmlNodeType.EndElement) { + if (r.NodeType == XmlNodeType.Element && r.LocalName == "Directory") { + string subs = r.GetAttribute ("include-subdirs"); + string sdom; + string share = r.GetAttribute ("shared"); + if (share == "true") + sdom = AddinDatabase.GlobalDomain; + else if (share == "false") + sdom = null; + else + sdom = domain; // Inherit the domain + + string path = r.ReadElementString ().Trim (); + if (path.Length > 0) { + path = Util.NormalizePath (path); + if (subs == "true") + directoriesWithSubdirs.Add (new string[] {path, sdom}); + else + directories.Add (new string[] {path, sdom}); + } + } + else if (r.NodeType == XmlNodeType.Element && r.LocalName == "GacAssembly") { + string aname = r.ReadElementString ().Trim (); + if (aname.Length > 0) { + aname = Util.NormalizePath (aname); + aname = Util.GetGacPath (aname); + if (aname != null) { + // Gac assemblies always use the global domain + directories.Add (new string[] {aname, AddinDatabase.GlobalDomain}); + } + } + } + else if (r.NodeType == XmlNodeType.Element && r.LocalName == "Exclude") { + string path = r.ReadElementString ().Trim (); + if (path.Length > 0) { + path = Util.NormalizePath (path); + if (!Path.IsPathRooted (path)) + path = Path.Combine (basePath, path); + scanResult.AddPathToIgnore (Path.GetFullPath (path)); + } + } + else + r.Skip (); + r.MoveToContent (); + } + } catch (Exception ex) { + monitor.ReportError ("Could not process addins file: " + file, ex); + return; + } finally { + if (r != null) + r.Close (); + } + + foreach (string[] d in directories) { + string dir = d[0]; + if (!Path.IsPathRooted (dir)) + dir = Path.Combine (basePath, dir); + ScanFolder (monitor, dir, d[1], scanResult); + } + foreach (string[] d in directoriesWithSubdirs) { + string dir = d[0]; + if (!Path.IsPathRooted (dir)) + dir = Path.Combine (basePath, dir); + ScanFolderRec (monitor, dir, d[1], scanResult); + } + } + + public void ScanFolderRec (IProgressStatus monitor, string dir, string domain, AddinScanResult scanResult) + { + ScanFolder (monitor, dir, domain, scanResult); + + if (!fs.DirectoryExists (dir)) + return; + + foreach (string sd in fs.GetDirectories (dir)) + ScanFolderRec (monitor, sd, domain, scanResult); + } + + bool ScanConfigAssemblies (IProgressStatus monitor, string filePath, AddinScanResult scanResult, out AddinDescription config) + { + config = null; + + IAssemblyReflector reflector = null; + try { + reflector = GetReflector (monitor, scanResult, filePath); + + string basePath = Path.GetDirectoryName (filePath); + + using (var s = fs.OpenFile (filePath)) { + config = AddinDescription.Read (s, basePath); + } + config.FileName = filePath; + config.SetBasePath (basePath); + config.AddinFile = filePath; + + return ScanDescription (monitor, reflector, config, null, scanResult); + } + catch (Exception ex) { + // Something went wrong while scanning the assembly. We'll ignore it for now. + monitor.ReportError ("There was an error while scanning add-in: " + filePath, ex); + return false; + } + } + + IAssemblyReflector GetReflector (IProgressStatus monitor, AddinScanResult scanResult, string filePath) + { + IAssemblyReflector reflector = fs.GetReflectorForFile (scanResult, filePath); + object coreAssembly; + if (!coreAssemblies.TryGetValue (reflector, out coreAssembly)) { + if (monitor.LogLevel > 1) + monitor.Log ("Using assembly reflector: " + reflector.GetType ()); + coreAssemblies [reflector] = coreAssembly = reflector.LoadAssembly (GetType().Assembly.Location); + } + return reflector; + } + + bool ScanAssembly (IProgressStatus monitor, string filePath, AddinScanResult scanResult, out AddinDescription config) + { + config = null; + + IAssemblyReflector reflector = null; + object asm = null; + try { + reflector = GetReflector (monitor, scanResult, filePath); + asm = reflector.LoadAssembly (filePath); + if (asm == null) + throw new Exception ("Could not load assembly: " + filePath); + + // Get the config file from the resources, if there is one + + if (!ScanEmbeddedDescription (monitor, filePath, reflector, asm, out config)) + return false; + + if (config == null || config.IsExtensionModel) { + // In this case, only scan the assembly if it has the Addin attribute. + AddinAttribute att = (AddinAttribute) reflector.GetCustomAttribute (asm, typeof(AddinAttribute), false); + if (att == null) { + config = null; + reflector.UnloadAssembly (asm); + return true; + } + + if (config == null) + config = new AddinDescription (); + } + + config.SetBasePath (Path.GetDirectoryName (filePath)); + config.AddinFile = filePath; + + string rasmFile = Path.GetFileName (filePath); + if (!config.MainModule.Assemblies.Contains (rasmFile)) + config.MainModule.Assemblies.Add (rasmFile); + + bool res = ScanDescription (monitor, reflector, config, asm, scanResult); + if (!res) + reflector.UnloadAssembly (asm); + return res; + } + catch (Exception ex) { + if (asm != null) + reflector.UnloadAssembly (asm); + // Something went wrong while scanning the assembly. We'll ignore it for now. + monitor.ReportError ("There was an error while scanning assembly: " + filePath, ex); + return false; + } + } + + static bool ScanEmbeddedDescription (IProgressStatus monitor, string filePath, IAssemblyReflector reflector, object asm, out AddinDescription config) + { + config = null; + foreach (string res in reflector.GetResourceNames (asm)) { + if (res.EndsWith (".addin") || res.EndsWith (".addin.xml")) { + using (Stream s = reflector.GetResourceStream (asm, res)) { + AddinDescription ad = AddinDescription.Read (s, Path.GetDirectoryName (filePath)); + if (config != null) { + if (!config.IsExtensionModel && !ad.IsExtensionModel) { + // There is more than one add-in definition + monitor.ReportError ("Duplicate add-in definition found in assembly: " + filePath, null); + return false; + } + config = AddinDescription.Merge (config, ad); + } + else + config = ad; + } + } + } + return true; + } + + bool ScanDescription (IProgressStatus monitor, IAssemblyReflector reflector, AddinDescription config, object rootAssembly, AddinScanResult scanResult) + { + // First of all scan the main module + + ArrayList assemblies = new ArrayList (); + + try { + string rootAsmFile = null; + + if (rootAssembly != null) { + ScanAssemblyAddinHeaders (reflector, config, rootAssembly, scanResult); + ScanAssemblyImports (reflector, config.MainModule, rootAssembly); + assemblies.Add (rootAssembly); + rootAsmFile = Path.GetFileName (config.AddinFile); + } + + // The assembly list may be modified while scanning the headers, so + // we use a for loop instead of a foreach + for (int n=0; n> (); + for (int n=0; n (asmFile,asm)); + scanResult.AddPathToIgnore (Path.GetFullPath (asmFile)); + ScanAssemblyImports (reflector, mod, asm); + } + // Add all data files to the ignore file list. It avoids scanning assemblies + // which are included as 'data' in an add-in. + foreach (string df in mod.DataFiles) { + string file = Path.Combine (config.BasePath, Util.NormalizePath (df)); + scanResult.AddPathToIgnore (Path.GetFullPath (file)); + } + foreach (string df in mod.IgnorePaths) { + string path = Path.Combine (config.BasePath, Util.NormalizePath (df)); + scanResult.AddPathToIgnore (Path.GetFullPath (path)); + } + + foreach (var asm in asmList) + ScanSubmodule (monitor, mod, reflector, config, scanResult, asm.Item1, asm.Item2); + + } catch (Exception ex) { + ReportReflectionException (monitor, ex, config, scanResult); + } + } + } + + config.StoreFileInfo (); + return true; + } + + bool ScanSubmodule (IProgressStatus monitor, ModuleDescription mod, IAssemblyReflector reflector, AddinDescription config, AddinScanResult scanResult, string assemblyName, object asm) + { + AddinDescription mconfig; + ScanEmbeddedDescription (monitor, assemblyName, reflector, asm, out mconfig); + if (mconfig != null) { + if (!mconfig.IsExtensionModel) { + monitor.ReportError ("Submodules can't define new add-ins: " + assemblyName, null); + return false; + } + if (mconfig.OptionalModules.Count != 0) { + monitor.ReportError ("Submodules can't define nested submodules: " + assemblyName, null); + return false; + } + if (mconfig.ConditionTypes.Count != 0) { + monitor.ReportError ("Submodules can't define condition types: " + assemblyName, null); + return false; + } + if (mconfig.ExtensionNodeSets.Count != 0) { + monitor.ReportError ("Submodules can't define extension node sets: " + assemblyName, null); + return false; + } + if (mconfig.ExtensionPoints.Count != 0) { + monitor.ReportError ("Submodules can't define extension points sets: " + assemblyName, null); + return false; + } + mod.MergeWith (mconfig.MainModule); + } + ScanAssemblyContents (reflector, config, mod, asm, scanResult); + return true; + } + + void ReportReflectionException (IProgressStatus monitor, Exception ex, AddinDescription config, AddinScanResult scanResult) + { + scanResult.AddFileToWithFailure (config.AddinFile); + monitor.ReportWarning ("[" + config.AddinId + "] Could not load some add-in assemblies: " + ex.Message); + if (monitor.LogLevel <= 1) + return; + + ReflectionTypeLoadException rex = ex as ReflectionTypeLoadException; + if (rex != null) { + foreach (Exception e in rex.LoaderExceptions) + monitor.Log ("Load exception: " + e); + } + } + + void ScanAssemblyAddinHeaders (IAssemblyReflector reflector, AddinDescription config, object asm, AddinScanResult scanResult) + { + // Get basic add-in information + AddinAttribute att = (AddinAttribute) reflector.GetCustomAttribute (asm, typeof(AddinAttribute), false); + if (att != null) { + if (att.Id.Length > 0) + config.LocalId = att.Id; + if (att.Version.Length > 0) + config.Version = att.Version; + if (att.Namespace.Length > 0) + config.Namespace = att.Namespace; + if (att.Category.Length > 0) + config.Category = att.Category; + if (att.CompatVersion.Length > 0) + config.CompatVersion = att.CompatVersion; + if (att.Url.Length > 0) + config.Url = att.Url; + config.IsRoot = att is AddinRootAttribute; + config.EnabledByDefault = att.EnabledByDefault; + config.Flags = att.Flags; + } + + // Author attributes + + object[] atts = reflector.GetCustomAttributes (asm, typeof(AddinAuthorAttribute), false); + foreach (AddinAuthorAttribute author in atts) { + if (config.Author.Length == 0) + config.Author = author.Name; + else + config.Author += ", " + author.Name; + } + + // Name + + atts = reflector.GetCustomAttributes (asm, typeof(AddinNameAttribute), false); + foreach (AddinNameAttribute at in atts) { + if (string.IsNullOrEmpty (at.Locale)) + config.Name = at.Name; + else + config.Properties.SetPropertyValue ("Name", at.Name, at.Locale); + } + + // Description + + object catt = reflector.GetCustomAttribute (asm, typeof(AssemblyDescriptionAttribute), false); + if (catt != null && config.Description.Length == 0) + config.Description = ((AssemblyDescriptionAttribute)catt).Description; + + atts = reflector.GetCustomAttributes (asm, typeof(AddinDescriptionAttribute), false); + foreach (AddinDescriptionAttribute at in atts) { + if (string.IsNullOrEmpty (at.Locale)) + config.Description = at.Description; + else + config.Properties.SetPropertyValue ("Description", at.Description, at.Locale); + } + + // Copyright + + catt = reflector.GetCustomAttribute (asm, typeof(AssemblyCopyrightAttribute), false); + if (catt != null && config.Copyright.Length == 0) + config.Copyright = ((AssemblyCopyrightAttribute)catt).Copyright; + + // Category + + catt = reflector.GetCustomAttribute (asm, typeof(AddinCategoryAttribute), false); + if (catt != null && config.Category.Length == 0) + config.Category = ((AddinCategoryAttribute)catt).Category; + + // Url + + catt = reflector.GetCustomAttribute (asm, typeof(AddinUrlAttribute), false); + if (catt != null && config.Url.Length == 0) + config.Url = ((AddinUrlAttribute)catt).Url; + + // Flags + + catt = reflector.GetCustomAttribute (asm, typeof(AddinFlagsAttribute), false); + if (catt != null) + config.Flags |= ((AddinFlagsAttribute)catt).Flags; + + // Localizer + + AddinLocalizerGettextAttribute locat = (AddinLocalizerGettextAttribute) reflector.GetCustomAttribute (asm, typeof(AddinLocalizerGettextAttribute), false); + if (locat != null) { + ExtensionNodeDescription node = new ExtensionNodeDescription (); + node.SetAttribute ("type", "Gettext"); + if (!string.IsNullOrEmpty (locat.Catalog)) + node.SetAttribute ("catalog", locat.Catalog); + if (!string.IsNullOrEmpty (locat.Location)) + node.SetAttribute ("location", locat.Location); + config.Localizer = node; + } + + var customLocat = (AddinLocalizerAttribute) reflector.GetCustomAttribute (asm, typeof(AddinLocalizerAttribute), false); + if (customLocat != null) { + var node = new ExtensionNodeDescription (); + node.SetAttribute ("type", customLocat.TypeName); + config.Localizer = node; + } + + // Optional modules + + atts = reflector.GetCustomAttributes (asm, typeof(AddinModuleAttribute), false); + foreach (AddinModuleAttribute mod in atts) { + if (mod.AssemblyFile.Length > 0) { + ModuleDescription module = new ModuleDescription (); + module.Assemblies.Add (mod.AssemblyFile); + config.OptionalModules.Add (module); + } + } + } + + void ScanAssemblyImports (IAssemblyReflector reflector, ModuleDescription module, object asm) + { + object[] atts = reflector.GetCustomAttributes (asm, typeof(ImportAddinAssemblyAttribute), false); + foreach (ImportAddinAssemblyAttribute import in atts) { + if (!string.IsNullOrEmpty (import.FilePath)) { + module.Assemblies.Add (import.FilePath); + if (!import.Scan) + module.IgnorePaths.Add (import.FilePath); + } + } + atts = reflector.GetCustomAttributes (asm, typeof(ImportAddinFileAttribute), false); + foreach (ImportAddinFileAttribute import in atts) { + if (!string.IsNullOrEmpty (import.FilePath)) + module.DataFiles.Add (import.FilePath); + } + } + + void ScanAssemblyContents (IAssemblyReflector reflector, AddinDescription config, ModuleDescription module, object asm, AddinScanResult scanResult) + { + bool isMainModule = module == config.MainModule; + + // Get dependencies + + object[] deps = reflector.GetCustomAttributes (asm, typeof(AddinDependencyAttribute), false); + foreach (AddinDependencyAttribute dep in deps) { + AddinDependency adep = new AddinDependency (); + adep.AddinId = dep.Id; + adep.Version = dep.Version; + module.Dependencies.Add (adep); + } + + if (isMainModule) { + + // Get properties + + object[] props = reflector.GetCustomAttributes (asm, typeof(AddinPropertyAttribute), false); + foreach (AddinPropertyAttribute prop in props) + config.Properties.SetPropertyValue (prop.Name, prop.Value, prop.Locale); + + // Get extension points + + object[] extPoints = reflector.GetCustomAttributes (asm, typeof(ExtensionPointAttribute), false); + foreach (ExtensionPointAttribute ext in extPoints) { + ExtensionPoint ep = config.AddExtensionPoint (ext.Path); + ep.Description = ext.Description; + ep.Name = ext.Name; + ep.DefaultInsertBefore = ext.DefaultInsertBefore; + ep.DefaultInsertAfter = ext.DefaultInsertAfter; + ExtensionNodeType nt = ep.AddExtensionNode (ext.NodeName, ext.NodeTypeName); + nt.ExtensionAttributeTypeName = ext.ExtensionAttributeTypeName; + } + } + + // Look for extension nodes declared using assembly attributes + + foreach (CustomAttribute att in reflector.GetRawCustomAttributes (asm, typeof(CustomExtensionAttribute), true)) + AddCustomAttributeExtension (module, att, "Type", null); + + // Get extensions or extension points applied to types + + foreach (object t in reflector.GetAssemblyTypes (asm)) { + + string typeFullName = reflector.GetTypeFullName (t); + + //condition attributes apply independently but identically to all extension attributes on this node + //depending on ordering is too messy due to inheritance etc + var conditionAtts = new Lazy> (() => reflector.GetRawCustomAttributes (t, typeof (CustomConditionAttribute), false)); + + // Look for extensions + + object[] extensionAtts = reflector.GetCustomAttributes (t, typeof(ExtensionAttribute), false); + if (extensionAtts.Length > 0) { + Dictionary nodes = new Dictionary (); + ExtensionNodeDescription uniqueNode = null; + foreach (ExtensionAttribute eatt in extensionAtts) { + string path; + string nodeName = eatt.NodeName; + + if (eatt.TypeName.Length > 0) { + path = "$" + eatt.TypeName; + } + else if (eatt.Path.Length == 0) { + path = GetBaseTypeNameList (reflector, t); + if (path == "$") { + // The type does not implement any interface and has no superclass. + // Will be reported later as an error. + path = "$" + typeFullName; + } + } else { + path = eatt.Path; + } + + ExtensionNodeDescription elem = AddConditionedExtensionNode (module, path, nodeName, conditionAtts.Value); + nodes [path] = elem; + uniqueNode = elem; + + if (eatt.Id.Length > 0) { + elem.SetAttribute ("id", eatt.Id); + elem.SetAttribute ("type", typeFullName); + } else { + elem.SetAttribute ("id", typeFullName); + } + if (eatt.InsertAfter.Length > 0) + elem.SetAttribute ("insertafter", eatt.InsertAfter); + if (eatt.InsertBefore.Length > 0) + elem.SetAttribute ("insertbefore", eatt.InsertBefore); + } + + // Get the node attributes + + foreach (ExtensionAttributeAttribute eat in reflector.GetCustomAttributes (t, typeof(ExtensionAttributeAttribute), false)) { + ExtensionNodeDescription node; + if (!string.IsNullOrEmpty (eat.Path)) + nodes.TryGetValue (eat.Path, out node); + else if (eat.TypeName.Length > 0) + nodes.TryGetValue ("$" + eat.TypeName, out node); + else { + if (nodes.Count > 1) + throw new Exception ("Missing type or extension path value in ExtensionAttribute for type '" + typeFullName + "'."); + node = uniqueNode; + } + if (node == null) + throw new Exception ("Invalid type or path value in ExtensionAttribute for type '" + typeFullName + "'."); + + node.SetAttribute (eat.Name ?? string.Empty, eat.Value ?? string.Empty); + } + } + else { + // Look for extension points + + extensionAtts = reflector.GetCustomAttributes (t, typeof(TypeExtensionPointAttribute), false); + if (extensionAtts.Length > 0 && isMainModule) { + foreach (TypeExtensionPointAttribute epa in extensionAtts) { + ExtensionPoint ep; + + ExtensionNodeType nt = new ExtensionNodeType (); + + if (epa.Path.Length > 0) { + ep = config.AddExtensionPoint (epa.Path); + } + else { + ep = config.AddExtensionPoint (GetDefaultTypeExtensionPath (config, typeFullName)); + nt.ObjectTypeName = typeFullName; + } + nt.Id = epa.NodeName; + nt.TypeName = epa.NodeTypeName; + nt.ExtensionAttributeTypeName = epa.ExtensionAttributeTypeName; + ep.NodeSet.NodeTypes.Add (nt); + ep.Description = epa.Description; + ep.Name = epa.Name; + ep.RootAddin = config.AddinId; + ep.SetExtensionsAddinId (config.AddinId); + } + } + else { + // Look for custom extension attribtues + foreach (CustomAttribute att in reflector.GetRawCustomAttributes (t, typeof(CustomExtensionAttribute), false)) { + ExtensionNodeDescription elem = AddCustomAttributeExtension (module, att, "Type", conditionAtts.Value); + elem.SetAttribute ("type", typeFullName); + if (string.IsNullOrEmpty (elem.GetAttribute ("id"))) + elem.SetAttribute ("id", typeFullName); + } + } + } + } + } + + static ExtensionNodeDescription AddConditionedExtensionNode (ModuleDescription module, string path, string nodeName, List conditionAtts) + { + if (conditionAtts == null || conditionAtts.Count == 0) { + return module.AddExtensionNode (path, nodeName); + } + + ExtensionNodeDescription conditionNode; + + if (conditionAtts.Count == 1) { + conditionNode = CreateConditionNode (conditionAtts[0]); + module.GetExtension (path).ExtensionNodes.Add (conditionNode); + } + else { + conditionNode = new ExtensionNodeDescription ("ComplexCondition"); + ExtensionNodeDescription andNode = new ExtensionNodeDescription ("And"); + conditionNode.ChildNodes.Add (andNode); + foreach (var catt in conditionAtts) { + var cnode = CreateConditionNode (catt); + andNode.ChildNodes.Add (cnode); + } + } + + var node = new ExtensionNodeDescription (nodeName); + conditionNode.ChildNodes.Add (node); + return node; + } + + static ExtensionNodeDescription CreateConditionNode (CustomAttribute conditionAtt) + { + var conditionNode = new ExtensionNodeDescription ("Condition"); + + var id = GetConditionId (conditionAtt); + conditionNode.SetAttribute ("id", id); + + foreach (KeyValuePair prop in conditionAtt) { + if (string.IsNullOrEmpty (prop.Key)) { + throw new Exception ("Empty key in attribute '" + conditionAtt.TypeName + "'."); + } + conditionNode.SetAttribute (prop.Key, prop.Value); + } + + return conditionNode; + } + + static string GetConditionId (CustomAttribute conditionAtt) + { + var id = conditionAtt.TypeName; + var start = id.LastIndexOf ('.') + 1; + int length = id.Length - start; + + if (id.EndsWith ("ConditionAttribute", StringComparison.Ordinal)) { + length -= "ConditionAttribute".Length; + } else if (id.EndsWith ("Attribute", StringComparison.Ordinal)) { + length -= "Attribute".Length; + } + + id = id.Substring (start, length); + return id; + } + + ExtensionNodeDescription AddCustomAttributeExtension (ModuleDescription module, CustomAttribute att, string nameName, List conditionAtts) + { + string path; + if (!att.TryGetValue (CustomExtensionAttribute.PathFieldKey, out path)) + path = "%" + att.TypeName; + ExtensionNodeDescription elem = AddConditionedExtensionNode (module, path, nameName, conditionAtts); + foreach (KeyValuePair prop in att) { + if (string.IsNullOrEmpty (prop.Key)) { + throw new Exception ("Empty key in attribute '" + att.TypeName + "'."); + } + if (prop.Key != CustomExtensionAttribute.PathFieldKey) + elem.SetAttribute (prop.Key, prop.Value); + } + return elem; + } + + void ScanNodeSet (IAssemblyReflector reflector, AddinDescription config, ExtensionNodeSet nset, ArrayList assemblies, Hashtable internalNodeSets) + { + foreach (ExtensionNodeType nt in nset.NodeTypes) + ScanNodeType (reflector, config, nt, assemblies, internalNodeSets); + } + + void ScanNodeType (IAssemblyReflector reflector, AddinDescription config, ExtensionNodeType nt, ArrayList assemblies, Hashtable internalNodeSets) + { + if (nt.TypeName.Length == 0) + nt.TypeName = "Mono.Addins.TypeExtensionNode"; + + object ntype = FindAddinType (reflector, nt.TypeName, assemblies); + if (ntype == null) + return; + + // Add type information declared with attributes in the code + ExtensionNodeAttribute nodeAtt = (ExtensionNodeAttribute) reflector.GetCustomAttribute (ntype, typeof(ExtensionNodeAttribute), true); + if (nodeAtt != null) { + if (nt.Id.Length == 0 && nodeAtt.NodeName.Length > 0) + nt.Id = nodeAtt.NodeName; + if (nt.Description.Length == 0 && nodeAtt.Description.Length > 0) + nt.Description = nodeAtt.Description; + if (nt.ExtensionAttributeTypeName.Length == 0 && nodeAtt.ExtensionAttributeTypeName.Length > 0) + nt.ExtensionAttributeTypeName = nodeAtt.ExtensionAttributeTypeName; + } else { + // Use the node type name as default name + if (nt.Id.Length == 0) + nt.Id = reflector.GetTypeName (ntype); + } + + // Add information about attributes + object[] fieldAtts = reflector.GetCustomAttributes (ntype, typeof(NodeAttributeAttribute), true); + foreach (NodeAttributeAttribute fatt in fieldAtts) { + NodeTypeAttribute natt = new NodeTypeAttribute (); + natt.Name = fatt.Name; + natt.Required = fatt.Required; + if (fatt.TypeName != null) + natt.Type = fatt.TypeName; + if (fatt.Description.Length > 0) + natt.Description = fatt.Description; + nt.Attributes.Add (natt); + } + + // Check if the type has NodeAttribute attributes applied to fields. + foreach (object field in reflector.GetFields (ntype)) { + NodeAttributeAttribute fatt = (NodeAttributeAttribute) reflector.GetCustomAttribute (field, typeof(NodeAttributeAttribute), false); + if (fatt != null) { + NodeTypeAttribute natt = new NodeTypeAttribute (); + if (fatt.Name.Length > 0) + natt.Name = fatt.Name; + else + natt.Name = reflector.GetFieldName (field); + if (fatt.Description.Length > 0) + natt.Description = fatt.Description; + natt.Type = reflector.GetFieldTypeFullName (field); + natt.Required = fatt.Required; + nt.Attributes.Add (natt); + } + } + + // Check if the extension type allows children by looking for [ExtensionNodeChild] attributes. + // First of all, look in the internalNodeSets hashtable, which is being used as cache + + string childSet = (string) internalNodeSets [nt.TypeName]; + + if (childSet == null) { + object[] ats = reflector.GetCustomAttributes (ntype, typeof(ExtensionNodeChildAttribute), true); + if (ats.Length > 0) { + // Create a new node set for this type. It is necessary to create a new node set + // instead of just adding child ExtensionNodeType objects to the this node type + // because child types references can be recursive. + ExtensionNodeSet internalSet = new ExtensionNodeSet (); + internalSet.Id = reflector.GetTypeName (ntype) + "_" + Guid.NewGuid().ToString (); + foreach (ExtensionNodeChildAttribute at in ats) { + ExtensionNodeType internalType = new ExtensionNodeType (); + internalType.Id = at.NodeName; + internalType.TypeName = at.ExtensionNodeTypeName; + internalSet.NodeTypes.Add (internalType); + } + config.ExtensionNodeSets.Add (internalSet); + nt.NodeSets.Add (internalSet.Id); + + // Register the new set in a hashtable, to allow recursive references to the + // same internal set. + internalNodeSets [nt.TypeName] = internalSet.Id; + internalNodeSets [reflector.GetTypeAssemblyQualifiedName (ntype)] = internalSet.Id; + ScanNodeSet (reflector, config, internalSet, assemblies, internalNodeSets); + } + } + else { + if (childSet.Length == 0) { + // The extension type does not declare children. + return; + } + // The extension type can have children. The allowed children are + // defined in this extension set. + nt.NodeSets.Add (childSet); + return; + } + + ScanNodeSet (reflector, config, nt, assemblies, internalNodeSets); + } + + string GetBaseTypeNameList (IAssemblyReflector reflector, object type) + { + StringBuilder sb = new StringBuilder ("$"); + foreach (string tn in reflector.GetBaseTypeFullNameList (type)) + sb.Append (tn).Append (','); + if (sb.Length > 0) + sb.Remove (sb.Length - 1, 1); + return sb.ToString (); + } + + object FindAddinType (IAssemblyReflector reflector, string typeName, ArrayList assemblies) + { + // Look in the current assembly + object etype = reflector.GetType (coreAssemblies [reflector], typeName); + if (etype != null) + return etype; + + // Look in referenced assemblies + foreach (object asm in assemblies) { + etype = reflector.GetType (asm, typeName); + if (etype != null) + return etype; + } + + Hashtable visited = new Hashtable (); + + // Look in indirectly referenced assemblies + foreach (object asm in assemblies) { + foreach (object aref in reflector.GetAssemblyReferences (asm)) { + if (visited.Contains (aref)) + continue; + visited.Add (aref, aref); + object rasm = reflector.LoadAssemblyFromReference (aref); + if (rasm != null) { + etype = reflector.GetType (rasm, typeName); + if (etype != null) + return etype; + } + } + } + return null; + } + + void RegisterTypeNode (AddinDescription config, ExtensionAttribute eatt, string path, string nodeName, string typeFullName) + { + ExtensionNodeDescription elem = config.MainModule.AddExtensionNode (path, nodeName); + if (eatt.Id.Length > 0) { + elem.SetAttribute ("id", eatt.Id); + elem.SetAttribute ("type", typeFullName); + } else { + elem.SetAttribute ("id", typeFullName); + } + if (eatt.InsertAfter.Length > 0) + elem.SetAttribute ("insertafter", eatt.InsertAfter); + if (eatt.InsertBefore.Length > 0) + elem.SetAttribute ("insertbefore", eatt.InsertBefore); + } + + internal string GetDefaultTypeExtensionPath (AddinDescription desc, string typeFullName) + { + return "/" + Addin.GetIdName (desc.AddinId) + "/TypeExtensions/" + typeFullName; + } + + internal void CleanupReflector() + { + fs.CleanupReflector (); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/AddinUpdateData.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinUpdateData.cs new file mode 100644 index 00000000..cbae4d47 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/AddinUpdateData.cs @@ -0,0 +1,304 @@ +// +// AddinUpdateData.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using Mono.Addins.Description; +using System.Collections.Generic; +using System.Linq; + +namespace Mono.Addins.Database +{ + class AddinUpdateData + { + // This table collects information about extensions. Each path (key) + // has a RootExtensionPoint object with information about the addin that + // defines the extension point and the addins which extend it + Dictionary> pathHash = new Dictionary> (); + + // Collects globally defined node sets. Key is node set name. Value is + // a RootExtensionPoint + Dictionary> nodeSetHash = new Dictionary> (); + + Dictionary> objectTypeExtensions = new Dictionary> (); + + Dictionary> customAttributeTypeExtensions = new Dictionary> (); + + internal int RelExtensionPoints; + internal int RelExtensions; + internal int RelNodeSetTypes; + internal int RelExtensionNodes; + + class RootExtensionPoint + { + public AddinDescription Description; + public ExtensionPoint ExtensionPoint; + } + + IProgressStatus monitor; + + public AddinUpdateData (AddinDatabase database, IProgressStatus monitor) + { + this.monitor = monitor; + } + + public void RegisterNodeSet (AddinDescription description, ExtensionNodeSet nset) + { + List extensions; + if (nodeSetHash.TryGetValue (nset.Id, out extensions)) { + // Extension point already registered + List compatExtensions = GetCompatibleExtensionPoints (nset.Id, description, description.MainModule, extensions); + if (compatExtensions.Count > 0) { + foreach (ExtensionPoint einfo in compatExtensions) + einfo.NodeSet.MergeWith (null, nset); + return; + } + } + // Create a new extension set + RootExtensionPoint rep = new RootExtensionPoint (); + rep.ExtensionPoint = new ExtensionPoint (); + rep.ExtensionPoint.SetNodeSet (nset); + rep.ExtensionPoint.RootAddin = description.AddinId; + rep.ExtensionPoint.Path = nset.Id; + rep.Description = description; + if (extensions == null) { + extensions = new List (); + nodeSetHash [nset.Id] = extensions; + } + extensions.Add (rep); + } + + public void RegisterExtensionPoint (AddinDescription description, ExtensionPoint ep) + { + List extensions; + if (pathHash.TryGetValue (ep.Path, out extensions)) { + // Extension point already registered + List compatExtensions = GetCompatibleExtensionPoints (ep.Path, description, description.MainModule, extensions); + if (compatExtensions.Count > 0) { + foreach (ExtensionPoint einfo in compatExtensions) + einfo.MergeWith (null, ep); + RegisterObjectTypes (ep); + return; + } + } + // Create a new extension + RootExtensionPoint rep = new RootExtensionPoint (); + rep.ExtensionPoint = ep; + rep.ExtensionPoint.RootAddin = description.AddinId; + rep.Description = description; + if (extensions == null) { + extensions = new List (); + pathHash [ep.Path] = extensions; + } + extensions.Add (rep); + RegisterObjectTypes (ep); + } + + void RegisterObjectTypes (ExtensionPoint ep) + { + // Register extension points bound to a node type + + foreach (ExtensionNodeType nt in ep.NodeSet.NodeTypes) { + if (nt.ObjectTypeName.Length > 0) { + List list; + if (!objectTypeExtensions.TryGetValue (nt.ObjectTypeName, out list)) { + list = new List (); + objectTypeExtensions [nt.ObjectTypeName] = list; + } + list.Add (ep); + } + if (nt.ExtensionAttributeTypeName.Length > 0) { + List list; + if (!customAttributeTypeExtensions.TryGetValue (nt.ExtensionAttributeTypeName, out list)) { + list = new List (); + customAttributeTypeExtensions [nt.ExtensionAttributeTypeName] = list; + } + list.Add (nt); + } + } + } + + public void RegisterExtension (AddinDescription description, ModuleDescription module, Extension extension) + { + if (extension.Path.StartsWith ("$")) { + string[] objectTypes = extension.Path.Substring (1).Split (','); + bool found = false; + foreach (string s in objectTypes) { + List list; + if (objectTypeExtensions.TryGetValue (s, out list)) { + found = true; + foreach (ExtensionPoint ep in list) { + if (IsAddinCompatible (ep.ParentAddinDescription, description, module)) { + extension.Path = ep.Path; + RegisterExtension (description, module, ep.Path); + } + } + } + } + if (!found) + monitor.ReportWarning ("The add-in '" + description.AddinId + "' is trying to register the class '" + extension.Path.Substring (1) + "', but there isn't any add-in defining a suitable extension point"); + } + else if (extension.Path.StartsWith ("%", StringComparison.Ordinal)) { + string[] objectTypes = extension.Path.Substring (1).Split (','); + bool found = false; + foreach (string s in objectTypes) { + List list; + if (customAttributeTypeExtensions.TryGetValue (s, out list)) { + found = true; + foreach (ExtensionNodeType nt in list) { + ExtensionPoint ep = (ExtensionPoint) ((ExtensionNodeSet)nt.Parent).Parent; + if (IsAddinCompatible (ep.ParentAddinDescription, description, module)) { + extension.Path = ep.Path; + foreach (ExtensionNodeDescription node in GetNodesIgnoringConditions (extension)) { + node.NodeName = nt.NodeName; + } + RegisterExtension (description, module, ep.Path); + } + } + } + } + if (!found) + monitor.ReportWarning ("The add-in '" + description.AddinId + "' is trying to register the class '" + extension.Path.Substring (1) + "', but there isn't any add-in defining a suitable extension point"); + } + } + + static IEnumerable GetNodesIgnoringConditions (Extension extension) + { + foreach (ExtensionNodeDescription node in extension.ExtensionNodes) { + if (node.IsCondition) { + //first node in a complex condition is the actual condition + bool skipFirst = node.NodeName == "ComplexCondition"; + foreach (ExtensionNodeDescription child in node.ChildNodes) { + if (skipFirst) { + skipFirst = false; + continue; + } + yield return child; + } + } else { + yield return node; + } + } + } + + public void RegisterExtension (AddinDescription description, ModuleDescription module, string path) + { + List extensions; + if (!pathHash.TryGetValue (path, out extensions)) { + // Root add-in extension points are registered before any other kind of extension, + // so we should find it now. + extensions = GetParentExtensionInfo (path); + } + if (extensions == null) { + monitor.ReportWarning ("The add-in '" + description.AddinId + "' is trying to extend '" + path + "', but there isn't any add-in defining this extension point"); + return; + } + + bool found = false; + foreach (RootExtensionPoint einfo in extensions) { + if (IsAddinCompatible (einfo.Description, description, module)) { + if (!einfo.ExtensionPoint.Addins.Contains (description.AddinId)) + einfo.ExtensionPoint.Addins.Add (description.AddinId); + found = true; + if (monitor.LogLevel > 2) { + monitor.Log (" * " + einfo.Description.AddinId + "(" + einfo.Description.Domain + ") <- " + path); + } + } + } + if (!found) + monitor.ReportWarning ("The add-in '" + description.AddinId + "' is trying to extend '" + path + "', but there isn't any compatible add-in defining this extension point"); + } + + List GetCompatibleExtensionPoints (string path, AddinDescription description, ModuleDescription module, List rootExtensionPoints) + { + List list = new List (); + foreach (RootExtensionPoint rep in rootExtensionPoints) { + + // Find an extension point defined in a root add-in which is compatible with the version of the extender dependency + if (IsAddinCompatible (rep.Description, description, module)) + list.Add (rep.ExtensionPoint); + } + return list; + } + + List GetParentExtensionInfo (string path) + { + int i = path.LastIndexOf ('/'); + if (i == -1) + return null; + string np = path.Substring (0, i); + List ep; + if (pathHash.TryGetValue (np, out ep)) + return ep; + else + return GetParentExtensionInfo (np); + } + + bool IsAddinCompatible (AddinDescription installedDescription, AddinDescription description, ModuleDescription module) + { + if (installedDescription == description) + return true; + if (installedDescription.Domain != AddinDatabase.GlobalDomain) { + if (description.Domain != AddinDatabase.GlobalDomain && description.Domain != installedDescription.Domain) + return false; + } else if (description.Domain != AddinDatabase.GlobalDomain) + return false; + + string addinId = Addin.GetFullId (installedDescription.Namespace, installedDescription.LocalId, null); + string requiredVersion = null; + + IEnumerable deps; + if (module == description.MainModule) + deps = module.Dependencies; + else { + ArrayList list = new ArrayList (); + list.AddRange (module.Dependencies); + list.AddRange (description.MainModule.Dependencies); + deps = list; + } + foreach (object dep in deps) { + AddinDependency adep = dep as AddinDependency; + if (adep != null && Addin.GetFullId (description.Namespace, adep.AddinId, null) == addinId) { + requiredVersion = adep.Version; + break; + } + } + if (requiredVersion == null) + return false; + + // Check if the required version is between rep.Description.CompatVersion and rep.Description.Version + if (Addin.CompareVersions (installedDescription.Version, requiredVersion) > 0) + return false; + if (installedDescription.CompatVersion.Length > 0 && Addin.CompareVersions (installedDescription.CompatVersion, requiredVersion) < 0) + return false; + + return true; + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/DatabaseConfiguration.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/DatabaseConfiguration.cs new file mode 100644 index 00000000..37779ecd --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/DatabaseConfiguration.cs @@ -0,0 +1,213 @@ +// +// DatabaseConfiguration.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Linq; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Xml; + +namespace Mono.Addins.Database +{ + internal class DatabaseConfiguration + { + Dictionary addinStatus = new Dictionary (); + + internal class AddinStatus + { + public AddinStatus (string addinId) + { + this.AddinId = addinId; + } + + public string AddinId; + public bool Enabled; + public bool Uninstalled; + public List Files; + } + + public bool IsEnabled (string addinId, bool defaultValue) + { + var addinName = Addin.GetIdName (addinId); + + AddinStatus s; + + // If the add-in is globaly disabled, it is disabled no matter what the version specific status is + if (addinStatus.TryGetValue (addinName, out s)) { + if (!s.Enabled) + return false; + } + + if (addinStatus.TryGetValue (addinId, out s)) + return s.Enabled && !IsRegisteredForUninstall (addinId); + else + return defaultValue; + } + + public void SetEnabled (string addinId, bool enabled, bool defaultValue, bool exactVersionMatch) + { + if (IsRegisteredForUninstall (addinId)) + return; + + var addinName = exactVersionMatch ? addinId : Addin.GetIdName (addinId); + + AddinStatus s; + addinStatus.TryGetValue (addinName, out s); + + if (s == null) + s = addinStatus [addinName] = new AddinStatus (addinName); + s.Enabled = enabled; + + // If enabling a specific version of an add-in, make sure the add-in is enabled as a whole + if (enabled && exactVersionMatch) + SetEnabled (addinId, true, defaultValue, false); + } + + public void RegisterForUninstall (string addinId, IEnumerable files) + { + AddinStatus s; + if (!addinStatus.TryGetValue (addinId, out s)) + s = addinStatus [addinId] = new AddinStatus (addinId); + + s.Enabled = false; + s.Uninstalled = true; + s.Files = new List (files); + } + + public void UnregisterForUninstall (string addinId) + { + addinStatus.Remove (addinId); + } + + public bool IsRegisteredForUninstall (string addinId) + { + AddinStatus s; + if (addinStatus.TryGetValue (addinId, out s)) + return s.Uninstalled; + else + return false; + } + + public bool HasPendingUninstalls { + get { return addinStatus.Values.Where (s => s.Uninstalled).Any (); } + } + + public AddinStatus[] GetPendingUninstalls () + { + return addinStatus.Values.Where (s => s.Uninstalled).ToArray (); + } + + public static DatabaseConfiguration Read (string file) + { + var config = ReadInternal (file); + // Try to read application level config to support disabling add-ins by default. + var appConfig = ReadAppConfig (); + + if (appConfig == null) + return config; + + // Overwrite app config values with user config values + foreach (var entry in config.addinStatus) + appConfig.addinStatus [entry.Key] = entry.Value; + + return appConfig; + } + + public static DatabaseConfiguration ReadAppConfig() + { + var assemblyPath = System.Reflection.Assembly.GetExecutingAssembly ().Location; + var assemblyDirectory = Path.GetDirectoryName (assemblyPath); + var appAddinsConfigFilePath = Path.Combine (assemblyDirectory, "addins-config.xml"); + + if (!File.Exists (appAddinsConfigFilePath)) + return new DatabaseConfiguration (); + + return ReadInternal (appAddinsConfigFilePath); + } + + static DatabaseConfiguration ReadInternal (string file) + { + DatabaseConfiguration config = new DatabaseConfiguration (); + XmlDocument doc = new XmlDocument (); + doc.Load (file); + + XmlElement disabledElem = (XmlElement) doc.DocumentElement.SelectSingleNode ("DisabledAddins"); + if (disabledElem != null) { + // For back compatibility + foreach (XmlElement elem in disabledElem.SelectNodes ("Addin")) + config.SetEnabled (elem.InnerText, false, true, false); + return config; + } + + XmlElement statusElem = (XmlElement) doc.DocumentElement.SelectSingleNode ("AddinStatus"); + if (statusElem != null) { + foreach (XmlElement elem in statusElem.SelectNodes ("Addin")) { + AddinStatus status = new AddinStatus (elem.GetAttribute ("id")); + string senabled = elem.GetAttribute ("enabled"); + status.Enabled = senabled.Length == 0 || senabled == "True"; + status.Uninstalled = elem.GetAttribute ("uninstalled") == "True"; + config.addinStatus [status.AddinId] = status; + foreach (XmlElement fileElem in elem.SelectNodes ("File")) { + if (status.Files == null) + status.Files = new List (); + status.Files.Add (fileElem.InnerText); + } + } + } + return config; + } + + public void Write (string file) + { + StreamWriter s = new StreamWriter (file); + using (s) { + XmlTextWriter tw = new XmlTextWriter (s); + tw.Formatting = Formatting.Indented; + tw.WriteStartElement ("Configuration"); + + tw.WriteStartElement ("AddinStatus"); + foreach (AddinStatus e in addinStatus.Values) { + tw.WriteStartElement ("Addin"); + tw.WriteAttributeString ("id", e.AddinId); + tw.WriteAttributeString ("enabled", e.Enabled.ToString ()); + if (e.Uninstalled) + tw.WriteAttributeString ("uninstalled", "True"); + if (e.Files != null && e.Files.Count > 0) { + foreach (var f in e.Files) + tw.WriteElementString ("File", f); + } + tw.WriteEndElement (); + } + tw.WriteEndElement (); // AddinStatus + tw.WriteEndElement (); // Configuration + } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/DefaultAssemblyReflector.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/DefaultAssemblyReflector.cs new file mode 100644 index 00000000..e668d2c7 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/DefaultAssemblyReflector.cs @@ -0,0 +1,188 @@ +// DefaultAssemblyReflector.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; + +namespace Mono.Addins.Database +{ + class DefaultAssemblyReflector: IAssemblyReflector + { + public void Initialize (IAssemblyLocator locator) + { + } + + public object LoadAssembly (string file) + { + return Util.LoadAssemblyForReflection (file); + } + + public void UnloadAssembly (object assembly) + { + } + + public string[] GetResourceNames (object asm) + { + return ((Assembly)asm).GetManifestResourceNames (); + } + + public System.IO.Stream GetResourceStream (object asm, string resourceName) + { + return ((Assembly)asm).GetManifestResourceStream (resourceName); + } + + public object[] GetCustomAttributes (object obj, Type type, bool inherit) + { + ICustomAttributeProvider aprov = obj as ICustomAttributeProvider; + if (aprov != null) + return aprov.GetCustomAttributes (type, inherit); + else + return new object [0]; + } + + public object GetCustomAttribute (object obj, Type type, bool inherit) + { + foreach (object att in GetCustomAttributes (obj, type, inherit)) + if (type.IsInstanceOfType (att)) + return att; + return null; + } + + public List GetRawCustomAttributes (object obj, Type type, bool inherit) + { + ICustomAttributeProvider aprov = obj as ICustomAttributeProvider; + List atts = new List (); + if (aprov == null) + return atts; + + foreach (object at in aprov.GetCustomAttributes (type, inherit)) + atts.Add (ConvertAttribute (at)); + + return atts; + } + + CustomAttribute ConvertAttribute (object ob) + { + CustomAttribute at = new CustomAttribute (); + Type type = ob.GetType (); + at.TypeName = type.FullName; + + foreach (PropertyInfo prop in type.GetProperties (BindingFlags.Public | BindingFlags.Instance)) { + object val = prop.GetValue (ob, null); + if (val != null) { + NodeAttributeAttribute bt = (NodeAttributeAttribute) Attribute.GetCustomAttribute (prop, typeof(NodeAttributeAttribute), true); + if (bt != null) { + string name = string.IsNullOrEmpty (bt.Name) ? prop.Name : bt.Name; + at [name] = Convert.ToString (val, System.Globalization.CultureInfo.InvariantCulture); + } + } + } + foreach (FieldInfo field in type.GetFields (BindingFlags.Public | BindingFlags.Instance)) { + object val = field.GetValue (ob); + if (val != null) { + NodeAttributeAttribute bt = (NodeAttributeAttribute) Attribute.GetCustomAttribute (field, typeof(NodeAttributeAttribute), true); + if (bt != null) { + string name = string.IsNullOrEmpty (bt.Name) ? field.Name : bt.Name; + at [name] = Convert.ToString (val, System.Globalization.CultureInfo.InvariantCulture); + } + } + } + return at; + } + + public string GetTypeName (object type) + { + return ((Type)type).Name; + } + + public IEnumerable GetFields (object type) + { + return ((Type)type).GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + } + + public string GetFieldName (object field) + { + return ((FieldInfo)field).Name; + } + + public string GetFieldTypeFullName (object field) + { + return ((FieldInfo)field).FieldType.FullName; + } + + public IEnumerable GetAssemblyTypes (object asm) + { + return ((Assembly)asm).GetTypes (); + } + + public IEnumerable GetBaseTypeFullNameList (object type) + { + ArrayList list = new ArrayList (); + Type btype = ((Type)type).BaseType; + while (btype != typeof(object)) { + list.Add (btype.FullName); + btype = btype.BaseType; + } + foreach (Type iterf in ((Type)type).GetInterfaces ()) { + list.Add (iterf.FullName); + } + return list; + } + + public object LoadAssemblyFromReference (object asmReference) + { + return Assembly.Load ((AssemblyName)asmReference); + } + + public IEnumerable GetAssemblyReferences (object asm) + { + return ((Assembly)asm).GetReferencedAssemblies (); + } + + public object GetType (object asm, string typeName) + { + return ((Assembly)asm).GetType (typeName); + } + + public string GetTypeFullName (object type) + { + return ((Type)type).FullName; + } + + public bool TypeIsAssignableFrom (object baseType, object type) + { + return ((Type)baseType).IsAssignableFrom ((Type)type); + } + + public string GetTypeAssemblyQualifiedName (object type) + { + return ((Type)type).AssemblyQualifiedName; + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/FileDatabase.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/FileDatabase.cs new file mode 100644 index 00000000..3fc88a62 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/FileDatabase.cs @@ -0,0 +1,468 @@ +// +// FileDatabase.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.IO; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Database +{ + internal class FileDatabase + { + Stream updatingLock; + + bool inTransaction; + string rootDirectory; + Hashtable foldersToUpdate; + Hashtable deletedFiles; + Hashtable deletedDirs; + IDisposable transactionLock; + bool ignoreDesc; + + public FileDatabase (string rootDirectory) + { + this.rootDirectory = rootDirectory; + } + + string DatabaseLockFile { + get { return Path.Combine (rootDirectory, "fdb-lock"); } + } + + string UpdateDatabaseLockFile { + get { return Path.Combine (rootDirectory, "fdb-update-lock"); } + } + + // Returns 'true' if description data must be ignored when reading the contents of a file + public bool IgnoreDescriptionData { + get { return ignoreDesc; } + set { ignoreDesc = value; } + } + + public bool BeginTransaction () + { + if (inTransaction) + throw new InvalidOperationException ("Already in a transaction"); + + transactionLock = LockWrite (); + try { + updatingLock = new FileStream (UpdateDatabaseLockFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); + } catch (IOException) { + // The database is already being updated. Can't do anything for now. + return false; + } finally { + transactionLock.Dispose (); + } + + // Delete .new files that could have been left by an aborted database update + + transactionLock = LockRead (); + CleanDirectory (rootDirectory); + + inTransaction = true; + foldersToUpdate = new Hashtable (); + deletedFiles = new Hashtable (); + deletedDirs = new Hashtable (); + return true; + } + + void CleanDirectory (string dir) + { + foreach (string file in Directory.GetFiles (dir, "*.new")) + File.Delete (file); + + foreach (string sdir in Directory.GetDirectories (dir)) + CleanDirectory (sdir); + } + + public IDisposable LockRead () + { + return FileLock (FileAccess.Read, -1); + } + + public IDisposable LockWrite () + { + return FileLock (FileAccess.Write, -1); + } + + IDisposable FileLock (FileAccess access, int timeout) + { + DateTime tim = DateTime.Now; + DateTime wt = tim; + + FileShare share = access == FileAccess.Read ? FileShare.Read : FileShare.None; + string path = Path.GetDirectoryName (DatabaseLockFile); + + if (!Directory.Exists (path)) + Directory.CreateDirectory (path); + + do { + try { + return new FileStream (DatabaseLockFile, FileMode.OpenOrCreate, access, share); + } + catch (IOException) { + // Wait and try again + if ((DateTime.Now - wt).TotalSeconds >= 4) { + Console.WriteLine ("Waiting for " + access + " add-in database lock"); + wt = DateTime.Now; + } + + } + System.Threading.Thread.Sleep (100); + } + while (timeout <= 0 || (DateTime.Now - tim).TotalMilliseconds < timeout); + + throw new Exception ("Lock timed out"); + } + + public Stream Create (string fileName) + { + if (inTransaction) { + deletedFiles.Remove (fileName); + deletedDirs.Remove (Path.GetDirectoryName (fileName)); + foldersToUpdate [Path.GetDirectoryName (fileName)] = null; + return File.Create (fileName + ".new"); + } + else + return File.Create (fileName); + } + + public void Rename (string fileName, string newName) + { + if (inTransaction) { + deletedFiles.Remove (newName); + deletedDirs.Remove (Path.GetDirectoryName (newName)); + foldersToUpdate [Path.GetDirectoryName (newName)] = null; + string s = File.Exists (fileName + ".new") ? fileName + ".new" : fileName; + File.Copy (s, newName + ".new"); + Delete (fileName); + } + else + File.Move (fileName, newName); + } + + public Stream OpenRead (string fileName) + { + if (inTransaction) { + if (deletedFiles.Contains (fileName)) + throw new FileNotFoundException (); + if (File.Exists (fileName + ".new")) + return File.OpenRead (fileName + ".new"); + } + return File.OpenRead (fileName); + } + + public void Delete (string fileName) + { + if (inTransaction) { + if (deletedFiles.Contains (fileName)) + return; + if (File.Exists (fileName + ".new")) + File.Delete (fileName + ".new"); + if (File.Exists (fileName)) + deletedFiles [fileName] = null; + } + else { + File.Delete (fileName); + } + } + + public void DeleteDir (string dirName) + { + if (inTransaction) { + if (deletedDirs.Contains (dirName)) + return; + if (Directory.Exists (dirName + ".new")) + Directory.Delete (dirName + ".new", true); + if (Directory.Exists (dirName)) + deletedDirs [dirName] = null; + } + else { + Directory.Delete (dirName, true); + } + } + + + public bool Exists (string fileName) + { + if (inTransaction) { + if (deletedFiles.Contains (fileName)) + return false; + if (File.Exists (fileName + ".new")) + return true; + } + return File.Exists (fileName); + } + + public bool DirExists (string dir) + { + return Directory.Exists (dir); + } + + public void CreateDir (string dir) + { + Directory.CreateDirectory (dir); + } + + public string[] GetDirectories (string dir) + { + return Directory.GetDirectories (dir); + } + + public bool DirectoryIsEmpty (string dir) + { + foreach (string f in Directory.GetFiles (dir)) { + if (!inTransaction || !deletedFiles.Contains (f)) + return false; + } + return true; + } + + public string[] GetDirectoryFiles (string dir, string pattern) + { + if (pattern == null || pattern.Length == 0 || pattern.EndsWith ("*")) + throw new NotSupportedException (); + + if (inTransaction) { + Hashtable files = new Hashtable (); + foreach (string f in Directory.GetFiles (dir, pattern)) { + if (!deletedFiles.Contains (f)) + files [f] = f; + } + foreach (string f in Directory.GetFiles (dir, pattern + ".new")) { + string ofile = f.Substring (0, f.Length - 4); + files [ofile] = ofile; + } + string[] res = new string [files.Count]; + int n = 0; + foreach (string s in files.Keys) + res [n++] = s; + return res; + } + else + return Directory.GetFiles (dir, pattern); + } + + public void CommitTransaction () + { + if (!inTransaction) + return; + + try { + transactionLock.Dispose (); + transactionLock = LockWrite (); + foreach (string dir in foldersToUpdate.Keys) { + foreach (string file in Directory.GetFiles (dir, "*.new")) { + string dst = file.Substring (0, file.Length - 4); + File.Delete (dst); + File.Move (file, dst); + } + } + foreach (string file in deletedFiles.Keys) + File.Delete (file); + foreach (string dir in deletedDirs.Keys) + Directory.Delete (dir, true); + } + finally { + transactionLock.Dispose (); + EndTransaction (); + } + } + + public void RollbackTransaction () + { + if (!inTransaction) + return; + + try { + // There is no need for write lock since existing files won't be updated. + + foreach (string dir in foldersToUpdate.Keys) { + foreach (string file in Directory.GetFiles (dir, "*.new")) + File.Delete (file); + } + } + finally { + transactionLock.Dispose (); + EndTransaction (); + } + } + + void EndTransaction () + { + inTransaction = false; + deletedFiles = null; + foldersToUpdate = null; + updatingLock.Close (); + updatingLock = null; + transactionLock = null; + } + + + + // The ReadSharedObject and WriteSharedObject methods can be used to read/write objects from/to files. + // What's special about those methods is that they handle file name collisions. + + public string[] GetObjectSharedFiles (string directory, string sharedFileName, string extension) + { + return GetDirectoryFiles (directory, sharedFileName + "*" + extension); + } + + public object ReadSharedObject (string fullFileName, BinaryXmlTypeMap typeMap) + { + object result; + OpenFileForPath (fullFileName, null, typeMap, false, out result); + return result; + } + + public bool SharedObjectExists (string directory, string sharedFileName, string extension, string objectId) + { + return null != GetSharedObjectFile (directory, sharedFileName, extension, objectId); + } + + public string GetSharedObjectFile (string directory, string sharedFileName, string extension, string objectId) + { + string fileName; + ReadSharedObject (directory, sharedFileName, extension, objectId, null, true, out fileName); + return fileName; + } + + public object ReadSharedObject (string directory, string sharedFileName, string extension, string objectId, BinaryXmlTypeMap typeMap, out string fileName) + { + return ReadSharedObject (directory, sharedFileName, extension, objectId, typeMap, false, out fileName); + } + + object ReadSharedObject (string directory, string sharedFileName, string extension, string objectId, BinaryXmlTypeMap typeMap, bool checkOnly, out string fileName) + { + string name = GetFileKey (directory, sharedFileName, objectId); + string file = Path.Combine (directory, name + extension); + + object result; + if (OpenFileForPath (file, objectId, typeMap, checkOnly, out result)) { + fileName = file; + return result; + } + + // The file is not the one we expected. There has been a name collision + + foreach (string f in GetDirectoryFiles (directory, name + "*" + extension)) { + if (f != file && OpenFileForPath (f, objectId, typeMap, checkOnly, out result)) { + fileName = f; + return result; + } + } + + // File not found + fileName = null; + return null; + } + + bool OpenFileForPath (string f, string objectId, BinaryXmlTypeMap typeMap, bool checkOnly, out object result) + { + result = null; + + if (!Exists (f)) { + return false; + } + using (Stream s = OpenRead (f)) { + BinaryXmlReader reader = new BinaryXmlReader (s, typeMap); + reader.ReadBeginElement (); + string id = reader.ReadStringValue ("id"); + if (objectId == null || objectId == id) { + if (!checkOnly) + result = reader.ReadValue ("data"); + return true; + } + } + return false; + } + + public void WriteSharedObject (string objectId, string targetFile, BinaryXmlTypeMap typeMap, IBinaryXmlElement obj) + { + WriteSharedObject (null, null, null, objectId, targetFile, typeMap, obj); + } + + public string WriteSharedObject (string directory, string sharedFileName, string extension, string objectId, string readFileName, BinaryXmlTypeMap typeMap, IBinaryXmlElement obj) + { + string file = readFileName; + + if (file == null) { + int count = 1; + string name = GetFileKey (directory, sharedFileName, objectId); + file = Path.Combine (directory, name + extension); + + while (Exists (file)) { + count++; + file = Path.Combine (directory, name + "_" + count + extension); + } + } + + using (Stream s = Create (file)) { + BinaryXmlWriter writer = new BinaryXmlWriter (s, typeMap); + writer.WriteBeginElement ("File"); + writer.WriteValue ("id", objectId); + writer.WriteValue ("data", obj); + writer.WriteEndElement (); + } + return file; + } + + public object ReadObject (string file, BinaryXmlTypeMap typeMap) + { + using (Stream s = OpenRead (file)) { + BinaryXmlReader reader = new BinaryXmlReader (s, typeMap); + return reader.ReadValue ("data"); + } + } + + public void WriteObject (string file, object obj, BinaryXmlTypeMap typeMap) + { + using (Stream s = Create (file)) { + BinaryXmlWriter writer = new BinaryXmlWriter (s, typeMap); + writer.WriteValue ("data", obj); + } + } + + string GetFileKey (string directory, string sharedFileName, string objectId) + { + // We have two magic numbers here. 240 is a "room to spare" number based on 255, + // the Windows MAX_PATH length for the full path of a file on disk. Then 130 is + // a "room to spare" number based on 143-"ish", the maximum filename length for + // files stored on eCryptFS on Linux. 240 relates to the complete path + // (including the directory structure), and 130 is just the filename, so we pick + // whichever is the smaller of those two numbers when truncating. + int avlen = System.Math.Min (System.Math.Max (240 - directory.Length, 10), 130); + string name = sharedFileName + "_" + Util.GetStringHashCode (objectId).ToString ("x"); + if (name.Length > avlen) + return name.Substring (name.Length - avlen); + else + return name; + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/IAssemblyReflector.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/IAssemblyReflector.cs new file mode 100644 index 00000000..e6d4dab0 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/IAssemblyReflector.cs @@ -0,0 +1,320 @@ +// IAssemblyReflector.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace Mono.Addins.Database +{ + /// + /// An assembly reflector + /// + /// + /// This interface can be implemented to provide a custom method for getting information about assemblies. + /// + public interface IAssemblyReflector + { + /// + /// Called to initialize the assembly reflector + /// + /// + /// IAssemblyLocator instance which can be used to locate referenced assemblies. + /// + void Initialize (IAssemblyLocator locator); + + /// + /// Gets a list of custom attributes + /// + /// + /// The custom attributes. + /// + /// + /// An assembly, class or class member + /// + /// + /// Type of the attribute to be returned. It will always be one of the attribute types + /// defined in Mono.Addins. + /// + /// + /// 'true' if inherited attributes must be returned + /// + object[] GetCustomAttributes (object obj, Type type, bool inherit); + + /// + /// Gets a list of custom attributes + /// + /// + /// The attributes. + /// + /// + /// An assembly, class or class member + /// + /// + /// Base type of the attribute to be returned + /// + /// + /// 'true' if inherited attributes must be returned + /// + List GetRawCustomAttributes (object obj, Type type, bool inherit); + + /// + /// Loads an assembly. + /// + /// + /// The loaded assembly + /// + /// + /// Path of the assembly. + /// + object LoadAssembly (string file); + + /// + /// Unloads an assembly. + /// + /// + /// Assembly to unload. + /// + void UnloadAssembly (object assembly); + + /// + /// Loads the assembly specified in an assembly reference + /// + /// + /// The assembly + /// + /// + /// An assembly reference + /// + object LoadAssemblyFromReference (object asmReference); + + /// + /// Gets the names of all resources embedded in an assembly + /// + /// + /// The names of the resources + /// + /// + /// An assembly + /// + string[] GetResourceNames (object asm); + + /// + /// Gets the data stream of a resource + /// + /// + /// The stream. + /// + /// + /// An assembly + /// + /// + /// The name of a resource + /// + Stream GetResourceStream (object asm, string resourceName); + + /// + /// Gets all types defined in an assembly + /// + /// + /// The types + /// + /// + /// An assembly + /// + IEnumerable GetAssemblyTypes (object asm); + + /// + /// Gets all assembly references of an assembly + /// + /// + /// A list of assembly references + /// + /// + /// An assembly + /// + IEnumerable GetAssemblyReferences (object asm); + + /// + /// Looks for a type in an assembly + /// + /// + /// The type. + /// + /// + /// An assembly + /// + /// + /// Name of the type + /// + object GetType (object asm, string typeName); + + + /// + /// Gets a custom attribute + /// + /// + /// The custom attribute. + /// + /// + /// An assembly, class or class member + /// + /// + /// Base type of the attribute to be returned. It will always be one of the attribute types + /// defined in Mono.Addins. + /// + /// + /// 'true' if inherited attributes must be returned + /// + object GetCustomAttribute (object obj, Type type, bool inherit); + + /// + /// Gets the name of a type (not including namespace) + /// + /// + /// The type name. + /// + /// + /// A type + /// + string GetTypeName (object type); + + /// + /// Gets the full name of a type (including namespace) + /// + /// + /// The full name of the type + /// + /// + /// A type + /// + string GetTypeFullName (object type); + + /// + /// Gets the assembly qualified name of a type + /// + /// + /// The assembly qualified type name + /// + /// + /// A type + /// + string GetTypeAssemblyQualifiedName (object type); + + /// + /// Gets a list of all base types (including interfaces) of a type + /// + /// + /// An enumeration of the full name of all base types of the type + /// + /// + /// A type + /// + IEnumerable GetBaseTypeFullNameList (object type); + + /// + /// Checks if a type is assignable to another type + /// + /// + /// 'true' if the type is assignable + /// + /// + /// Expected base type. + /// + /// + /// A type. + /// + bool TypeIsAssignableFrom (object baseType, object type); + + /// + /// Gets the fields of a type + /// + /// + /// The fields. + /// + /// + /// A type + /// + IEnumerable GetFields (object type); + + /// + /// Gets the name of a field. + /// + /// + /// The field name. + /// + /// + /// A field. + /// + string GetFieldName (object field); + + /// + /// Gets the full name of the type of a field + /// + /// + /// The full type name + /// + /// + /// A field. + /// + string GetFieldTypeFullName (object field); + } + + /// + /// Allows finding assemblies in the file system + /// + public interface IAssemblyLocator + { + /// + /// Locates an assembly + /// + /// + /// The full path to the assembly, or null if not found + /// + /// + /// Full name of the assembly + /// + string GetAssemblyLocation (string fullName); + } + + /// + /// A custom attribute + /// + public class CustomAttribute: Dictionary + { + string typeName; + + /// + /// Full name of the type of the custom attribute + /// + public string TypeName { + get { return typeName; } + set { typeName = value; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/ISetupHandler.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/ISetupHandler.cs new file mode 100644 index 00000000..7de46eae --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/ISetupHandler.cs @@ -0,0 +1,36 @@ +// +// ISetupHandler.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins.Database +{ + internal interface ISetupHandler + { + void Scan (IProgressStatus monitor, AddinRegistry registry, string scanFolder, string[] filesToIgnore); + void GetAddinDescription (IProgressStatus monitor, AddinRegistry registry, string file, string outFile); + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/ProcessProgressStatus.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/ProcessProgressStatus.cs new file mode 100644 index 00000000..3af29826 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/ProcessProgressStatus.cs @@ -0,0 +1,161 @@ +// +// ProcessProgressStatus.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections.Specialized; +using System.IO; + +namespace Mono.Addins.Database +{ + internal class ProcessProgressStatus: MarshalByRefObject, IProgressStatus + { + bool canceled; + int logLevel; + + public ProcessProgressStatus (int logLevel) + { + this.logLevel = logLevel; + } + + public void SetMessage (string msg) + { + Console.WriteLine ("process-ps-msg:" + Encode (msg)); + } + + public void SetProgress (double progress) + { + Console.WriteLine ("process-ps-progress:" + progress.ToString ()); + } + + public void Log (string msg) + { + if (msg.StartsWith ("plog:")) + // This is an special type of log that will be provided to the + // main process in case of a crash in the setup process + Console.WriteLine ("process-ps-plog:" + Encode (msg.Substring (5))); + else + Console.WriteLine ("process-ps-log:" + Encode (msg)); + } + + public void ReportWarning (string message) + { + Console.WriteLine ("process-ps-warning:" + Encode (message)); + } + + public void ReportError (string message, Exception exception) + { + if (message == null) message = string.Empty; + string et; + if (logLevel > 1) + et = exception != null ? exception.ToString () : string.Empty; + else + et = exception != null ? exception.Message : string.Empty; + + Console.WriteLine ("process-ps-exception:" + Encode (et)); + Console.WriteLine ("process-ps-error:" + Encode (message)); + } + + public bool IsCanceled { + get { return canceled; } + } + + public int LogLevel { + get { return logLevel; } + } + + public void Cancel () + { + canceled = true; + Console.WriteLine ("process-ps-cancel:"); + } + + static string Encode (string msg) + { + msg = msg.Replace ("&", "&a"); + return msg.Replace ("\n", "&n"); + } + + static string Decode (string msg) + { + msg = msg.Replace ("&n", "\n"); + return msg.Replace ("&a", "&"); + } + + public static void MonitorProcessStatus (IProgressStatus monitor, TextReader reader, StringCollection progessLog) + { + string line; + string exceptionText = null; + while ((line = reader.ReadLine ()) != null) { + int i = line.IndexOf (':'); + if (i != -1) { + string tag = line.Substring (0, i); + string txt = line.Substring (i+1); + bool wasTag = true; + + switch (tag) { + case "process-ps-msg": + monitor.SetMessage (Decode (txt)); + break; + case "process-ps-progress": + monitor.SetProgress (double.Parse (txt)); + break; + case "process-ps-log": + monitor.Log (Decode (txt)); + break; + case "process-ps-warning": + monitor.ReportWarning (Decode (txt)); + break; + case "process-ps-exception": + exceptionText = Decode (txt); + if (exceptionText == string.Empty) + exceptionText = null; + break; + case "process-ps-error": + string err = Decode (txt); + if (err == string.Empty) err = null; + monitor.ReportError (err, exceptionText != null ? new Exception (exceptionText) : null); + break; + case "process-ps-cancel": + monitor.Cancel (); + break; + case "process-ps-plog": + progessLog.Add (Decode (txt)); + break; + default: + wasTag = false; + break; + } + if (wasTag) + continue; + } + Console.WriteLine (line); + } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/SetupDomain.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/SetupDomain.cs new file mode 100644 index 00000000..8e3db85b --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/SetupDomain.cs @@ -0,0 +1,199 @@ +// +// SetupDomain.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Specialized; + +namespace Mono.Addins.Database +{ + class SetupDomain: ISetupHandler + { + AppDomain domain; + RemoteSetupDomain remoteSetupDomain; + int useCount; + + public void Scan (IProgressStatus monitor, AddinRegistry registry, string scanFolder, string[] filesToIgnore) + { + RemoteProgressStatus remMonitor = new RemoteProgressStatus (monitor); + try { + RemoteSetupDomain rsd = GetDomain (); + rsd.Scan (remMonitor, registry.RegistryPath, registry.StartupDirectory, registry.DefaultAddinsFolder, registry.AddinCachePath, scanFolder, filesToIgnore); + } catch (Exception ex) { + throw new ProcessFailedException (remMonitor.ProgessLog, ex); + } finally { + System.Runtime.Remoting.RemotingServices.Disconnect (remMonitor); + ReleaseDomain (); + } + } + + public void GetAddinDescription (IProgressStatus monitor, AddinRegistry registry, string file, string outFile) + { + RemoteProgressStatus remMonitor = new RemoteProgressStatus (monitor); + try { + RemoteSetupDomain rsd = GetDomain (); + rsd.GetAddinDescription (remMonitor, registry.RegistryPath, registry.StartupDirectory, registry.DefaultAddinsFolder, registry.AddinCachePath, file, outFile); + } catch (Exception ex) { + throw new ProcessFailedException (remMonitor.ProgessLog, ex); + } finally { + System.Runtime.Remoting.RemotingServices.Disconnect (remMonitor); + ReleaseDomain (); + } + } + + // ensure types from this assembly returned to this domain from the remote domain can + // be resolved even if we're in the LoadFrom context + static System.Reflection.Assembly MonoAddinsAssemblyResolve(object sender, ResolveEventArgs args) + { + var asm = typeof(SetupDomain).Assembly; + return args.Name == asm.FullName? asm : null; + } + + RemoteSetupDomain GetDomain () + { + lock (this) { + if (useCount++ == 0) { + AppDomain.CurrentDomain.AssemblyResolve += MonoAddinsAssemblyResolve; + domain = AppDomain.CreateDomain ("SetupDomain", null, AppDomain.CurrentDomain.SetupInformation); + var type = typeof(RemoteSetupDomain); + remoteSetupDomain = (RemoteSetupDomain) domain.CreateInstanceFromAndUnwrap (type.Assembly.Location, type.FullName); + } + return remoteSetupDomain; + } + } + + void ReleaseDomain () + { + lock (this) { + if (--useCount == 0) { + AppDomain.Unload (domain); + domain = null; + remoteSetupDomain = null; + AppDomain.CurrentDomain.AssemblyResolve -= MonoAddinsAssemblyResolve; + } + } + } + } + + class RemoteSetupDomain: MarshalByRefObject + { + public RemoteSetupDomain () + { + // ensure types from this assembly passed to this domain from the main domain + // can be resolved even though we're in the LoadFrom context + AppDomain.CurrentDomain.AssemblyResolve += (o, a) => { + var asm = typeof(RemoteSetupDomain).Assembly; + return a.Name == asm.FullName? asm : null; + }; + } + + public override object InitializeLifetimeService () + { + return null; + } + + public void Scan (IProgressStatus monitor, string registryPath, string startupDir, string addinsDir, string databaseDir, string scanFolder, string[] filesToIgnore) + { + AddinDatabase.RunningSetupProcess = true; + AddinRegistry reg = new AddinRegistry (registryPath, startupDir, addinsDir, databaseDir); + StringCollection files = new StringCollection (); + for (int n=0; n +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Specialized; + +namespace Mono.Addins.Database +{ + class SetupLocal: ISetupHandler + { + public void Scan (IProgressStatus monitor, AddinRegistry registry, string scanFolder, string[] filesToIgnore) + { + AddinRegistry reg = new AddinRegistry (registry.RegistryPath, registry.StartupDirectory, registry.DefaultAddinsFolder, registry.AddinCachePath); + reg.CopyExtensionsFrom (registry); + StringCollection files = new StringCollection (); + for (int n=0; n 2 ? args [2] : null; + if (folder.Length == 0) folder = null; + StringCollection filesToIgnore = new StringCollection (); + for (int n=3; n 0 ? progessLog [progessLog.Count - 1] : ""; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Database/Util.cs b/mono-addins/Mono.Addins/Mono.Addins.Database/Util.cs new file mode 100644 index 00000000..5a80a35b --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Database/Util.cs @@ -0,0 +1,322 @@ +// +// Util.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.IO; +using System.Reflection; +using Mono.Addins.Description; +using Mono.Addins.Serialization; +using System.Collections.Generic; + +namespace Mono.Addins.Database +{ + internal class Util + { + static int isMono; + static string monoVersion; + + public static bool IsWindows { + get { return Path.DirectorySeparatorChar == '\\'; } + } + + public static bool IsMono { + get { + if (isMono == 0) + isMono = Type.GetType ("Mono.Runtime") != null ? 1 : -1; + return isMono == 1; + } + } + + public static string MonoVersion { + get { + if (monoVersion == null) { + if (!IsMono) + throw new InvalidOperationException (); + MethodInfo mi = Type.GetType ("Mono.Runtime").GetMethod ("GetDisplayName", BindingFlags.NonPublic|BindingFlags.Static); + if (mi != null) + monoVersion = (string) mi.Invoke (null, null); + else + monoVersion = string.Empty; + } + return monoVersion; + } + } + + public static void CheckWrittableFloder (string path) + { + string testFile = null; + int n = 0; + var random = new Random (); + do { + testFile = Path.Combine (path, random.Next ().ToString ()); + n++; + } while (File.Exists (testFile) && n < 100); + if (n == 100) + throw new InvalidOperationException ("Could not create file in directory: " + path); + + StreamWriter w = new StreamWriter (testFile); + w.Close (); + File.Delete (testFile); + } + + public static void AddDependencies (AddinDescription desc, AddinScanResult scanResult) + { + // Not implemented in AddinScanResult to avoid making AddinDescription remotable + foreach (ModuleDescription mod in desc.AllModules) { + foreach (Dependency dep in mod.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) continue; + string depid = Addin.GetFullId (desc.Namespace, adep.AddinId, adep.Version); + scanResult.AddAddinToUpdateRelations (depid); + } + } + } + + public static Assembly LoadAssemblyForReflection (string fileName) + { +/* if (!gotLoadMethod) { + reflectionOnlyLoadFrom = typeof(Assembly).GetMethod ("ReflectionOnlyLoadFrom"); + gotLoadMethod = true; + LoadAssemblyForReflection (typeof(Util).Assembly.Location); + } + + if (reflectionOnlyLoadFrom != null) + return (Assembly) reflectionOnlyLoadFrom.Invoke (null, new string [] { fileName }); + else +*/ return Assembly.LoadFile (fileName); + } + + public static string NormalizePath (string path) + { + if (path == null) + return null; + if (path.Length > 2 && path [0] == '[') { + int i = path.IndexOf (']', 1); + if (i != -1) { + try { + string fname = path.Substring (1, i - 1); + Environment.SpecialFolder sf = (Environment.SpecialFolder) Enum.Parse (typeof(Environment.SpecialFolder), fname, true); + path = Environment.GetFolderPath (sf) + path.Substring (i + 1); + } catch { + // Ignore + } + } + } + if (IsWindows) + return path.Replace ('/','\\'); + else + return path.Replace ('\\','/'); + } + + // A private hash calculation method is used to be able to get consistent + // results across different .NET versions and implementations. + public static int GetStringHashCode (string s) + { + int h = 0; + int n = 0; + for (; n < s.Length - 1; n+=2) { + h = unchecked ((h << 5) - h + s[n]); + h = unchecked ((h << 5) - h + s[n+1]); + } + if (n < s.Length) + h = unchecked ((h << 5) - h + s[n]); + return h; + } + + public static string GetGacPath (string fullName) + { + string[] parts = fullName.Split (','); + if (parts.Length != 4) return null; + string name = parts[0].Trim (); + + int i = parts[1].IndexOf ('='); + string version = i != -1 ? parts[1].Substring (i+1).Trim () : parts[1].Trim (); + + i = parts[2].IndexOf ('='); + string culture = i != -1 ? parts[2].Substring (i+1).Trim () : parts[2].Trim (); + if (culture == "neutral") culture = ""; + + i = parts[3].IndexOf ('='); + string token = i != -1 ? parts[3].Substring (i+1).Trim () : parts[3].Trim (); + + string versionDirName = version + "_" + culture + "_" + token; + + if (Util.IsMono) { + string gacDir = typeof(Uri).Assembly.Location; + gacDir = Path.GetDirectoryName (gacDir); + gacDir = Path.GetDirectoryName (gacDir); + gacDir = Path.GetDirectoryName (gacDir); + string dir = Path.Combine (gacDir, name); + return Path.Combine (dir, versionDirName); + } else { + // .NET 4.0 introduces a new GAC directory structure and location. + // Assembly version directory names are now prefixed with the CLR version + // Since there can be different assembly versions for different target CLR runtimes, + // we now look for the best match, that is, the assembly with the higher CLR version + + var currentVersion = new Version (Environment.Version.Major, Environment.Version.Minor); + + foreach (var gacDir in GetDotNetGacDirectories ()) { + var asmDir = Path.Combine (gacDir, name); + if (!Directory.Exists (asmDir)) + continue; + Version bestVersion = new Version (0, 0); + string bestDir = null; + foreach (var dir in Directory.GetDirectories (asmDir, "v*_" + versionDirName)) { + var dirName = Path.GetFileName (dir); + i = dirName.IndexOf ('_'); + Version av; + if (Version.TryParse (dirName.Substring (1, i - 1), out av)) { + if (av == currentVersion) + return dir; + else if (av < currentVersion && av > bestVersion) { + bestDir = dir; + bestVersion = av; + } + } + } + if (bestDir != null) + return bestDir; + } + + // Look in the old GAC. There are no CLR prefixes here + + foreach (var gacDir in GetLegacyDotNetGacDirectories ()) { + var asmDir = Path.Combine (gacDir, name); + asmDir = Path.Combine (asmDir, versionDirName); + if (Directory.Exists (asmDir)) + return asmDir; + } + return null; + } + } + + static IEnumerable GetLegacyDotNetGacDirectories () + { + var winDir = Path.GetFullPath (Environment.SystemDirectory + "\\.."); + + string gacDir = winDir + "\\assembly\\GAC"; + if (Directory.Exists (gacDir)) + yield return gacDir; + if (Directory.Exists (gacDir + "_32")) + yield return gacDir + "_32"; + if (Directory.Exists (gacDir + "_64")) + yield return gacDir + "_64"; + if (Directory.Exists (gacDir + "_MSIL")) + yield return gacDir + "_MSIL"; + } + + static IEnumerable GetDotNetGacDirectories () + { + var winDir = Path.GetFullPath (Environment.SystemDirectory + "\\.."); + + string gacDir = winDir + "\\Microsoft.NET\\assembly\\GAC"; + if (Directory.Exists (gacDir)) + yield return gacDir; + if (Directory.Exists (gacDir + "_32")) + yield return gacDir + "_32"; + if (Directory.Exists (gacDir + "_64")) + yield return gacDir + "_64"; + if (Directory.Exists (gacDir + "_MSIL")) + yield return gacDir + "_MSIL"; + } + + internal static bool IsManagedAssembly (string filePath) + { + try + { + using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) + using (BinaryReader binaryReader = new BinaryReader(fileStream)) + { + if (fileStream.Length < 64) + { + return false; + } + + // PE Header starts @ 0x3C (60). Its a 4 byte header. + fileStream.Position = 0x3C; + uint peHeaderPointer = binaryReader.ReadUInt32(); + if (peHeaderPointer == 0) + { + peHeaderPointer = 0x80; + } + + // Ensure there is at least enough room for the following structures: + // 24 byte PE Signature & Header + // 28 byte Standard Fields (24 bytes for PE32+) + // 68 byte NT Fields (88 bytes for PE32+) + // >= 128 byte Data Dictionary Table + if (peHeaderPointer > fileStream.Length - 256) + { + return false; + } + + // Check the PE signature. Should equal 'PE\0\0'. + fileStream.Position = peHeaderPointer; + uint peHeaderSignature = binaryReader.ReadUInt32(); + if (peHeaderSignature != 0x00004550) + { + return false; + } + + // skip over the PEHeader fields + fileStream.Position += 20; + + const ushort PE32 = 0x10b; + const ushort PE32Plus = 0x20b; + + // Read PE magic number from Standard Fields to determine format. + var peFormat = binaryReader.ReadUInt16(); + if (peFormat != PE32 && peFormat != PE32Plus) + { + return false; + } + + // Read the 15th Data Dictionary RVA field which contains the CLI header RVA. + // When this is non-zero then the file contains CLI data otherwise not. + ushort dataDictionaryStart = (ushort)(peHeaderPointer + (peFormat == PE32 ? 232 : 248)); + fileStream.Position = dataDictionaryStart; + + uint cliHeaderRva = binaryReader.ReadUInt32(); + if (cliHeaderRva == 0) + { + return false; + } + + return true; + } + } + catch (Exception) + { + return false; + } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/AddinDependency.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinDependency.cs new file mode 100644 index 00000000..b0ff3645 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinDependency.cs @@ -0,0 +1,176 @@ +// +// AddinDependency.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Xml; +using System.Xml.Serialization; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Description +{ + /// + /// Definition of a dependency of an add-in on another add-in. + /// + [XmlType ("AddinReference")] + public class AddinDependency: Dependency + { + string id; + string version; + + /// + /// Initializes a new instance of the class. + /// + public AddinDependency () + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Full identifier of the add-in (includes version) + /// + public AddinDependency (string fullId) + { + Addin.GetIdParts (fullId, out id, out version); + id = "::" + id; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Identifier of the add-in. + /// + /// + /// Version of the add-in. + /// + public AddinDependency (string id, string version) + { + this.id = id; + this.version = version; + } + + internal AddinDependency (XmlElement elem): base (elem) + { + id = elem.GetAttribute ("id"); + version = elem.GetAttribute ("version"); + } + + internal override void Verify (string location, StringCollection errors) + { + VerifyNotEmpty (location + "Dependencies/Addin/", errors, "id", AddinId); + VerifyNotEmpty (location + "Dependencies/Addin/", errors, "version", Version); + } + + internal override void SaveXml (XmlElement parent) + { + CreateElement (parent, "Addin"); + Element.SetAttribute ("id", AddinId); + Element.SetAttribute ("version", Version); + } + + /// + /// Gets the full addin identifier. + /// + /// + /// The full addin identifier. + /// + /// + /// Includes namespace and version number. For example: MonoDevelop.TextEditor,1.0 + /// + public string FullAddinId { + get { + AddinDescription desc = ParentAddinDescription; + if (desc == null) + return Addin.GetFullId (null, AddinId, Version); + else + return Addin.GetFullId (desc.Namespace, AddinId, Version); + } + } + + /// + /// Gets or sets the addin identifier. + /// + /// + /// The addin identifier. + /// + public string AddinId { + get { return id != null ? ParseString (id) : string.Empty; } + set { id = value; } + } + + /// + /// Gets or sets the version. + /// + /// + /// The version. + /// + public string Version { + get { return version != null ? ParseString (version) : string.Empty; } + set { version = value; } + } + + /// + /// Display name of the dependency. + /// + /// + /// The name. + /// + public override string Name { + get { return AddinId + " v" + Version; } + } + + internal override bool CheckInstalled (AddinRegistry registry) + { + Addin[] addins = registry.GetAddins (); + foreach (Addin addin in addins) { + if (addin.Id == id && addin.SupportsVersion (version)) { + return true; + } + } + return false; + } + + internal override void Write (BinaryXmlWriter writer) + { + base.Write (writer); + writer.WriteValue ("id", ParseString (id)); + writer.WriteValue ("version", ParseString (version)); + } + + internal override void Read (BinaryXmlReader reader) + { + base.Read (reader); + id = reader.ReadStringValue ("id"); + version = reader.ReadStringValue ("version"); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/AddinDescription.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinDescription.cs new file mode 100644 index 00000000..158dbec5 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinDescription.cs @@ -0,0 +1,1326 @@ +// +// AddinDescription.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Specialized; +using Mono.Addins.Serialization; +using Mono.Addins.Database; +using System.Text; + +namespace Mono.Addins.Description +{ + /// + /// An add-in description + /// + /// + /// This class represent an add-in manifest. It has properties for getting + /// all information, and methods for loading and saving files. + /// + public class AddinDescription: IBinaryXmlElement + { + XmlDocument configDoc; + string configFile; + AddinDatabase ownerDatabase; + + string id; + string name; + string ns; + string version; + string compatVersion; + string author; + string url; + string copyright; + string description; + string category; + string basePath; + string sourceAddinFile; + bool isroot; + bool hasUserId; + bool canWrite = true; + bool defaultEnabled = true; + AddinFlags flags = AddinFlags.None; + string domain; + + ModuleDescription mainModule; + ModuleCollection optionalModules; + ExtensionNodeSetCollection nodeSets; + ConditionTypeDescriptionCollection conditionTypes; + ExtensionPointCollection extensionPoints; + ExtensionNodeDescription localizer; + object[] fileInfo; + + AddinPropertyCollectionImpl properties; + Dictionary variables; + + internal static BinaryXmlTypeMap typeMap; + + static AddinDescription () + { + typeMap = new BinaryXmlTypeMap (); + typeMap.RegisterType (typeof(AddinDescription), "AddinDescription"); + typeMap.RegisterType (typeof(Extension), "Extension"); + typeMap.RegisterType (typeof(ExtensionNodeDescription), "Node"); + typeMap.RegisterType (typeof(ExtensionNodeSet), "NodeSet"); + typeMap.RegisterType (typeof(ExtensionNodeType), "NodeType"); + typeMap.RegisterType (typeof(ExtensionPoint), "ExtensionPoint"); + typeMap.RegisterType (typeof(ModuleDescription), "ModuleDescription"); + typeMap.RegisterType (typeof(ConditionTypeDescription), "ConditionType"); + typeMap.RegisterType (typeof(Condition), "Condition"); + typeMap.RegisterType (typeof(AddinDependency), "AddinDependency"); + typeMap.RegisterType (typeof(AssemblyDependency), "AssemblyDependency"); + typeMap.RegisterType (typeof(NodeTypeAttribute), "NodeTypeAttribute"); + typeMap.RegisterType (typeof(AddinFileInfo), "FileInfo"); + typeMap.RegisterType (typeof(AddinProperty), "Property"); + } + + internal AddinDatabase OwnerDatabase { + get { return ownerDatabase; } + set { ownerDatabase = value; } + } + + /// + /// Gets or sets the path to the main addin file. + /// + /// + /// The addin file. + /// + /// + /// The add-in file can be either the main assembly of an add-in or an xml manifest. + /// + public string AddinFile { + get { return sourceAddinFile; } + set { sourceAddinFile = value; } + } + + /// + /// Gets the addin identifier. + /// + /// + /// The addin identifier. + /// + public string AddinId { + get { return Addin.GetFullId (Namespace, LocalId, Version); } + } + + /// + /// Gets or sets the local identifier. + /// + /// + /// The local identifier. + /// + public string LocalId { + get { return id != null ? ParseString (id) : string.Empty; } + set { id = value; hasUserId = true; } + } + + /// + /// Gets or sets the namespace. + /// + /// + /// The namespace. + /// + public string Namespace { + get { return ns != null ? ParseString (ns) : string.Empty; } + set { ns = value; } + } + + /// + /// Gets or sets the display name of the add-in. + /// + /// + /// The name. + /// + public string Name { + get { + string val = Properties.GetPropertyValue ("Name"); + if (val.Length > 0) + return val; + if (name != null && name.Length > 0) + return ParseString (name); + if (HasUserId) + return AddinId; + else if (sourceAddinFile != null) + return Path.GetFileNameWithoutExtension (sourceAddinFile); + else + return string.Empty; + } + set { name = value; } + } + + /// + /// Gets or sets the version. + /// + /// + /// The version. + /// + public string Version { + get { return version != null ? ParseString (version) : string.Empty; } + set { version = value; } + } + + /// + /// Gets or sets the version of the add-in with which this add-in is backwards compatible. + /// + /// + /// The compat version. + /// + public string CompatVersion { + get { return compatVersion != null ? ParseString (compatVersion) : string.Empty; } + set { compatVersion = value; } + } + + /// + /// Gets or sets the author. + /// + /// + /// The author. + /// + public string Author { + get { + string val = Properties.GetPropertyValue ("Author"); + if (val.Length > 0) + return val; + return ParseString (author) ?? string.Empty; + } + set { author = value; } + } + + /// + /// Gets or sets the Url where more information about the add-in can be found. + /// + /// + /// The URL. + /// + public string Url { + get { + string val = Properties.GetPropertyValue ("Url"); + if (val.Length > 0) + return val; + return ParseString (url) ?? string.Empty; + } + set { url = value; } + } + + /// + /// Gets or sets the copyright. + /// + /// + /// The copyright. + /// + public string Copyright { + get { + string val = Properties.GetPropertyValue ("Copyright"); + if (val.Length > 0) + return val; + return ParseString (copyright) ?? string.Empty; + } + set { copyright = value; } + } + + /// + /// Gets or sets the description of the add-in. + /// + /// + /// The description. + /// + public string Description { + get { + string val = Properties.GetPropertyValue ("Description"); + if (val.Length > 0) + return val; + return ParseString (description) ?? string.Empty; + } + set { description = value; } + } + + /// + /// Gets or sets the category of the add-in. + /// + /// + /// The category. + /// + public string Category { + get { + string val = Properties.GetPropertyValue ("Category"); + if (val.Length > 0) + return val; + return ParseString (category) ?? string.Empty; + } + set { category = value; } + } + + /// + /// Gets the base path for locating external files relative to the add-in. + /// + /// + /// The base path. + /// + public string BasePath { + get { return basePath != null ? basePath : string.Empty; } + } + + internal void SetBasePath (string path) + { + basePath = path; + } + + /// + /// Gets or sets a value indicating whether this instance is an add-in root. + /// + /// + /// true if this instance is an add-in root; otherwise, false. + /// + public bool IsRoot { + get { return isroot; } + set { isroot = value; } + } + + /// + /// Gets or sets a value indicating whether this add-in is enabled by default. + /// + /// + /// true if enabled by default; otherwise, false. + /// + public bool EnabledByDefault { + get { return defaultEnabled; } + set { defaultEnabled = value; } + } + + /// + /// Gets or sets the add-in flags. + /// + /// + /// The flags. + /// + public AddinFlags Flags { + get { return flags; } + set { flags = value; } + } + + internal bool HasUserId { + get { return hasUserId; } + set { hasUserId = value; } + } + + /// + /// Gets a value indicating whether this add-in can be disabled. + /// + /// + /// true if this add-in can be disabled; otherwise, false. + /// + public bool CanDisable { + get { return (flags & AddinFlags.CantDisable) == 0 && !IsHidden; } + } + + /// + /// Gets a value indicating whether this add-in can be uninstalled. + /// + /// + /// true if this instance can be uninstalled; otherwise, false. + /// + public bool CanUninstall { + get { return (flags & AddinFlags.CantUninstall) == 0 && !IsHidden; } + } + + /// + /// Gets a value indicating whether this add-in is hidden. + /// + /// + /// true if this add-in is hidden; otherwise, false. + /// + public bool IsHidden { + get { return (flags & AddinFlags.Hidden) != 0; } + } + + internal bool SupportsVersion (string ver) + { + return Addin.CompareVersions (ver, Version) >= 0 && + (CompatVersion.Length == 0 || Addin.CompareVersions (ver, CompatVersion) <= 0); + } + + /// + /// Gets all external files + /// + /// + /// All files. + /// + /// + /// External files are data files and assemblies explicitly referenced in the Runtime section of the add-in manifest. + /// + public StringCollection AllFiles { + get { + StringCollection col = new StringCollection (); + foreach (string s in MainModule.AllFiles) + col.Add (s); + + foreach (ModuleDescription mod in OptionalModules) { + foreach (string s in mod.AllFiles) + col.Add (s); + } + return col; + } + } + + /// + /// Gets all paths to be ignored by the add-in scanner. + /// + /// + /// All paths to be ignored. + /// + public StringCollection AllIgnorePaths { + get { + StringCollection col = new StringCollection (); + foreach (string s in MainModule.IgnorePaths) + col.Add (s); + + foreach (ModuleDescription mod in OptionalModules) { + foreach (string s in mod.IgnorePaths) + col.Add (s); + } + return col; + } + } + + /// + /// Gets the main module. + /// + /// + /// The main module. + /// + public ModuleDescription MainModule { + get { + if (mainModule == null) { + if (RootElement == null) + mainModule = new ModuleDescription (); + else + mainModule = new ModuleDescription (RootElement); + mainModule.SetParent (this); + } + return mainModule; + } + } + + /// + /// Gets the optional modules. + /// + /// + /// The optional modules. + /// + /// + /// Optional modules can be used to declare extensions which will be registered only if some specified + /// add-in dependencies can be satisfied. Dependencies specified in optional modules are 'soft dependencies', + /// which means that they don't need to be satisfied in order to load the add-in. + /// + public ModuleCollection OptionalModules { + get { + if (optionalModules == null) { + optionalModules = new ModuleCollection (this); + if (RootElement != null) { + foreach (XmlElement mod in RootElement.SelectNodes ("Module")) + optionalModules.Add (new ModuleDescription (mod)); + } + } + return optionalModules; + } + } + + /// + /// Gets all modules (including the main module and all optional modules) + /// + /// + /// All modules. + /// + public ModuleCollection AllModules { + get { + ModuleCollection col = new ModuleCollection (this); + col.Add (MainModule); + foreach (ModuleDescription mod in OptionalModules) + col.Add (mod); + return col; + } + } + + /// + /// Gets the extension node sets. + /// + /// + /// The extension node sets. + /// + public ExtensionNodeSetCollection ExtensionNodeSets { + get { + if (nodeSets == null) { + nodeSets = new ExtensionNodeSetCollection (this); + if (RootElement != null) { + foreach (XmlElement elem in RootElement.SelectNodes ("ExtensionNodeSet")) + nodeSets.Add (new ExtensionNodeSet (elem)); + } + } + return nodeSets; + } + } + + /// + /// Gets the extension points. + /// + /// + /// The extension points. + /// + public ExtensionPointCollection ExtensionPoints { + get { + if (extensionPoints == null) { + extensionPoints = new ExtensionPointCollection (this); + if (RootElement != null) { + foreach (XmlElement elem in RootElement.SelectNodes ("ExtensionPoint")) + extensionPoints.Add (new ExtensionPoint (elem)); + } + } + return extensionPoints; + } + } + + /// + /// Gets the condition types. + /// + /// + /// The condition types. + /// + public ConditionTypeDescriptionCollection ConditionTypes { + get { + if (conditionTypes == null) { + conditionTypes = new ConditionTypeDescriptionCollection (this); + if (RootElement != null) { + foreach (XmlElement elem in RootElement.SelectNodes ("ConditionType")) + conditionTypes.Add (new ConditionTypeDescription (elem)); + } + } + return conditionTypes; + } + } + + /// + /// Gets or sets the add-in localizer. + /// + /// + /// The description of the add-in localizer for this add-in. + /// + public ExtensionNodeDescription Localizer { + get { return localizer; } + set { localizer = value; } + } + + /// + /// Custom properties specified in the add-in header + /// + public AddinPropertyCollection Properties { + get { + if (properties == null) + properties = new AddinPropertyCollectionImpl (this); + return properties; + } + } + + /// + /// Adds an extension point. + /// + /// + /// The extension point. + /// + /// + /// Path that identifies the new extension point. + /// + public ExtensionPoint AddExtensionPoint (string path) + { + ExtensionPoint ep = new ExtensionPoint (); + ep.Path = path; + ExtensionPoints.Add (ep); + return ep; + } + + internal ExtensionNodeDescription FindExtensionNode (string path, bool lookInDeps) + { + // Look in the extensions of this add-in + + foreach (Extension ext in MainModule.Extensions) { + if (path.StartsWith (ext.Path + "/")) { + string subp = path.Substring (ext.Path.Length).Trim ('/'); + ExtensionNodeDescriptionCollection nodes = ext.ExtensionNodes; + ExtensionNodeDescription node = null; + foreach (string p in subp.Split ('/')) { + if (p.Length == 0) continue; + node = nodes [p]; + if (node == null) + break; + nodes = node.ChildNodes; + } + if (node != null) + return node; + } + } + + if (!lookInDeps || OwnerDatabase == null) + return null; + + // Look in dependencies + + foreach (Dependency dep in MainModule.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) continue; + Addin ad = OwnerDatabase.GetInstalledAddin (Domain, adep.FullAddinId); + if (ad != null && ad.Description != null) { + ExtensionNodeDescription node = ad.Description.FindExtensionNode (path, false); + if (node != null) + return node; + } + } + return null; + } + + XmlElement RootElement { + get { + if (configDoc != null) + return configDoc.DocumentElement; + else + return null; + } + } + + internal void ResetXmlDoc () + { + configDoc = null; + } + + /// + /// Gets or sets file where this description is stored + /// + /// + /// The file path. + /// + public string FileName { + get { return configFile; } + set { configFile = value; } + } + + internal string Domain { + get { return domain; } + set { domain = value; } + } + + internal void StoreFileInfo () + { + ArrayList list = new ArrayList (); + foreach (string f in AllFiles) { + string file = Path.Combine (this.BasePath, f); + AddinFileInfo fi = new AddinFileInfo (); + fi.FileName = f; + fi.Timestamp = File.GetLastWriteTime (file); + list.Add (fi); + } + fileInfo = list.ToArray (); + } + + internal bool FilesChanged () + { + // Checks if the files of the add-in have changed. + if (fileInfo == null) + return true; + + foreach (AddinFileInfo f in fileInfo) { + string file = Path.Combine (this.BasePath, f.FileName); + if (!File.Exists (file)) + return true; + if (f.Timestamp != File.GetLastWriteTime (file)) + return true; + } + + return false; + } + + void TransferCoreProperties (bool removeProperties) + { + if (properties == null) + return; + + string val = properties.ExtractCoreProperty ("Id", removeProperties); + if (val != null) + id = val; + + val = properties.ExtractCoreProperty ("Namespace", removeProperties); + if (val != null) + ns = val; + + val = properties.ExtractCoreProperty ("Version", removeProperties); + if (val != null) + version = val; + + val = properties.ExtractCoreProperty ("CompatVersion", removeProperties); + if (val != null) + compatVersion = val; + + val = properties.ExtractCoreProperty ("DefaultEnabled", removeProperties); + if (val != null) + defaultEnabled = GetBool (val, true); + + val = properties.ExtractCoreProperty ("IsRoot", removeProperties); + if (val != null) + isroot = GetBool (val, true); + + val = properties.ExtractCoreProperty ("Flags", removeProperties); + if (val != null) + flags = (AddinFlags) Enum.Parse (typeof(AddinFlags), val); + } + + bool TryGetVariableValue (string name, out string value) + { + if (variables != null && variables.TryGetValue (name, out value)) + return true; + + switch (name) { + case "Id": value = id; return true; + case "Namespace": value = ns; return true; + case "Version": value = version; return true; + case "CompatVersion": value = compatVersion; return true; + case "DefaultEnabled": value = defaultEnabled.ToString (); return true; + case "IsRoot": value = isroot.ToString (); return true; + case "Flags": value = flags.ToString (); return true; + } + if (properties != null && properties.HasProperty (name)) { + value = properties.GetPropertyValue (name); + return true; + } + value = null; + return false; + } + + /// + /// Saves the add-in description. + /// + /// + /// File name where to save this instance + /// + /// + /// Saves the add-in description to the specified file and sets the FileName property. + /// + public void Save (string fileName) + { + configFile = fileName; + Save (); + } + + /// + /// Saves the add-in description. + /// + /// + /// It is thrown if FileName is not set + /// + /// + /// The description is saved to the file specified in the FileName property. + /// + public void Save () + { + if (configFile == null) + throw new InvalidOperationException ("File name not specified."); + + SaveXml (); + + using (StreamWriter sw = new StreamWriter (configFile)) { + XmlTextWriter tw = new XmlTextWriter (sw); + tw.Formatting = Formatting.Indented; + configDoc.Save (tw); + } + } + + /// + /// Generates an XML representation of the add-in description + /// + /// + /// An XML manifest. + /// + public XmlDocument SaveToXml () + { + SaveXml (); + return configDoc; + } + + void SaveXml () + { + if (!canWrite) + throw new InvalidOperationException ("Can't write incomplete description."); + + XmlElement elem; + + if (configDoc == null) { + configDoc = new XmlDocument (); + configDoc.AppendChild (configDoc.CreateElement ("Addin")); + } + + elem = configDoc.DocumentElement; + + SaveCoreProperty (elem, HasUserId ? id : null, "id", "Id"); + SaveCoreProperty (elem, version, "version", "Version"); + SaveCoreProperty (elem, ns, "namespace", "Namespace"); + SaveCoreProperty (elem, isroot ? "true" : null, "isroot", "IsRoot"); + + // Name will return the file name when HasUserId=false + if (!string.IsNullOrEmpty (name)) + elem.SetAttribute ("name", name); + else + elem.RemoveAttribute ("name"); + + SaveCoreProperty (elem, compatVersion, "compatVersion", "CompatVersion"); + SaveCoreProperty (elem, defaultEnabled ? null : "false", "defaultEnabled", "DefaultEnabled"); + SaveCoreProperty (elem, flags != AddinFlags.None ? flags.ToString () : null, "flags", "Flags"); + + if (author != null && author.Length > 0) + elem.SetAttribute ("author", author); + else + elem.RemoveAttribute ("author"); + + if (url != null && url.Length > 0) + elem.SetAttribute ("url", url); + else + elem.RemoveAttribute ("url"); + + if (copyright != null && copyright.Length > 0) + elem.SetAttribute ("copyright", copyright); + else + elem.RemoveAttribute ("copyright"); + + if (description != null && description.Length > 0) + elem.SetAttribute ("description", description); + else + elem.RemoveAttribute ("description"); + + if (category != null && category.Length > 0) + elem.SetAttribute ("category", category); + else + elem.RemoveAttribute ("category"); + + if (localizer == null || localizer.Element == null) { + // Remove old element if it exists + XmlElement oldLoc = (XmlElement) elem.SelectSingleNode ("Localizer"); + if (oldLoc != null) + elem.RemoveChild (oldLoc); + } + if (localizer != null) + localizer.SaveXml (elem); + + if (mainModule != null) { + mainModule.Element = elem; + mainModule.SaveXml (elem); + } + + if (optionalModules != null) + optionalModules.SaveXml (elem); + + if (nodeSets != null) + nodeSets.SaveXml (elem); + + if (extensionPoints != null) + extensionPoints.SaveXml (elem); + + XmlElement oldHeader = (XmlElement) elem.SelectSingleNode ("Header"); + if (properties == null || properties.Count == 0) { + if (oldHeader != null) + elem.RemoveChild (oldHeader); + } else { + if (oldHeader == null) { + oldHeader = elem.OwnerDocument.CreateElement ("Header"); + if (elem.FirstChild != null) + elem.InsertBefore (oldHeader, elem.FirstChild); + else + elem.AppendChild (oldHeader); + } + else + oldHeader.RemoveAll (); + foreach (var prop in properties) { + XmlElement propElem = elem.OwnerDocument.CreateElement (prop.Name); + if (!string.IsNullOrEmpty (prop.Locale)) + propElem.SetAttribute ("locale", prop.Locale); + propElem.InnerText = prop.Value ?? string.Empty; + oldHeader.AppendChild (propElem); + } + } + + XmlElement oldVars = (XmlElement) elem.SelectSingleNode ("Variables"); + if (variables == null || variables.Count == 0) { + if (oldVars != null) + elem.RemoveChild (oldVars); + } else { + if (oldVars == null) { + oldVars = elem.OwnerDocument.CreateElement ("Variables"); + if (elem.FirstChild != null) + elem.InsertBefore (oldVars, elem.FirstChild); + else + elem.AppendChild (oldVars); + } + else + oldVars.RemoveAll (); + foreach (var prop in variables) { + XmlElement propElem = elem.OwnerDocument.CreateElement (prop.Key); + propElem.InnerText = prop.Value ?? string.Empty; + oldVars.AppendChild (propElem); + } + } + } + + void SaveCoreProperty (XmlElement elem, string val, string attr, string prop) + { + if (properties != null && properties.HasProperty (prop)) { + elem.RemoveAttribute (attr); + if (!string.IsNullOrEmpty (val)) + properties.SetPropertyValue (prop, val); + else + properties.RemoveProperty (prop); + } + else if (string.IsNullOrEmpty (val)) + elem.RemoveAttribute (attr); + else + elem.SetAttribute (attr, val); + } + + + /// + /// Load an add-in description from a file + /// + /// + /// The file. + /// + public static AddinDescription Read (string configFile) + { + AddinDescription config; + using (Stream s = File.OpenRead (configFile)) { + config = Read (s, Path.GetDirectoryName (configFile)); + } + config.configFile = configFile; + return config; + } + + /// + /// Load an add-in description from a stream + /// + /// + /// The stream + /// + /// + /// The path to be used to resolve relative file paths. + /// + public static AddinDescription Read (Stream stream, string basePath) + { + return Read (new StreamReader (stream), basePath); + } + + /// + /// Load an add-in description from a text reader + /// + /// + /// The text reader + /// + /// + /// The path to be used to resolve relative file paths. + /// + public static AddinDescription Read (TextReader reader, string basePath) + { + AddinDescription config = new AddinDescription (); + + try { + config.configDoc = new XmlDocument (); + config.configDoc.Load (reader); + } catch (Exception ex) { + throw new InvalidOperationException ("The add-in configuration file is invalid: " + ex.Message, ex); + } + + XmlElement elem = config.configDoc.DocumentElement; + if (elem.LocalName == "ExtensionModel") + return config; + + XmlElement varsElem = (XmlElement) elem.SelectSingleNode ("Variables"); + if (varsElem != null) { + foreach (XmlNode node in varsElem.ChildNodes) { + XmlElement prop = node as XmlElement; + if (prop == null) + continue; + if (config.variables == null) + config.variables = new Dictionary (); + config.variables [prop.LocalName] = prop.InnerText; + } + } + + config.id = elem.GetAttribute ("id"); + config.ns = elem.GetAttribute ("namespace"); + config.name = elem.GetAttribute ("name"); + config.version = elem.GetAttribute ("version"); + config.compatVersion = elem.GetAttribute ("compatVersion"); + config.author = elem.GetAttribute ("author"); + config.url = elem.GetAttribute ("url"); + config.copyright = elem.GetAttribute ("copyright"); + config.description = elem.GetAttribute ("description"); + config.category = elem.GetAttribute ("category"); + config.basePath = elem.GetAttribute ("basePath"); + config.domain = "global"; + + string s = elem.GetAttribute ("isRoot"); + if (s.Length == 0) s = elem.GetAttribute ("isroot"); + config.isroot = GetBool (s, false); + + config.defaultEnabled = GetBool (elem.GetAttribute ("defaultEnabled"), true); + + string prot = elem.GetAttribute ("flags"); + if (prot.Length == 0) + config.flags = AddinFlags.None; + else + config.flags = (AddinFlags) Enum.Parse (typeof(AddinFlags), prot); + + XmlElement localizerElem = (XmlElement) elem.SelectSingleNode ("Localizer"); + if (localizerElem != null) + config.localizer = new ExtensionNodeDescription (localizerElem); + + XmlElement headerElem = (XmlElement) elem.SelectSingleNode ("Header"); + if (headerElem != null) { + foreach (XmlNode node in headerElem.ChildNodes) { + XmlElement prop = node as XmlElement; + if (prop == null) + continue; + config.Properties.SetPropertyValue (prop.LocalName, prop.InnerText, prop.GetAttribute ("locale")); + } + } + + config.TransferCoreProperties (false); + + if (config.id.Length > 0) + config.hasUserId = true; + + return config; + } + + internal string ParseString (string input) + { + if (input == null || input.Length < 4) + return input; + + int i = input.IndexOf ("$("); + if (i == -1) + return input; + + StringBuilder result = new StringBuilder (input.Length); + result.Append (input, 0, i); + + while (i < input.Length) { + if (input [i] == '$') { + i++; + + if (i >= input.Length || input[i] != '(') { + result.Append ('$'); + continue; + } + + i++; + int start = i; + while (i < input.Length && input [i] != ')') + i++; + + string tag = input.Substring (start, i - start); + + string tagValue; + if (TryGetVariableValue (tag, out tagValue)) + result.Append (tagValue); + else { + result.Append ('$'); + i = start - 1; + } + } else { + result.Append (input [i]); + } + i++; + } + return result.ToString (); + } + + static bool GetBool (string s, bool defval) + { + if (s.Length == 0) + return defval; + else + return s == "true" || s == "yes"; + } + + internal static AddinDescription ReadBinary (FileDatabase fdb, string configFile) + { + AddinDescription description = (AddinDescription) fdb.ReadSharedObject (configFile, typeMap); + if (description != null) { + description.FileName = configFile; + description.canWrite = !fdb.IgnoreDescriptionData; + } + return description; + } + + internal void SaveBinary (FileDatabase fdb, string file) + { + configFile = file; + SaveBinary (fdb); + } + + internal void SaveBinary (FileDatabase fdb) + { + if (!canWrite) + throw new InvalidOperationException ("Can't write incomplete description."); + fdb.WriteSharedObject (AddinFile, FileName, typeMap, this); +// BinaryXmlReader.DumpFile (configFile); + } + + /// + /// Verify this instance. + /// + /// + /// This method checks all the definitions in the description and returns a list of errors. + /// If the returned list is empty, it means that the description is valid. + /// + public StringCollection Verify () + { + return Verify (new AddinFileSystemExtension ()); + } + + internal StringCollection Verify (AddinFileSystemExtension fs) + { + StringCollection errors = new StringCollection (); + + if (IsRoot) { + if (OptionalModules.Count > 0) + errors.Add ("Root add-in hosts can't have optional modules."); + } + + if (AddinId.Length == 0 || Version.Length == 0) { + if (ExtensionPoints.Count > 0) + errors.Add ("Add-ins which define new extension points must have an Id and Version."); + } + + MainModule.Verify ("", errors); + OptionalModules.Verify ("", errors); + ExtensionNodeSets.Verify ("", errors); + ExtensionPoints.Verify ("", errors); + ConditionTypes.Verify ("", errors); + + foreach (ExtensionNodeSet nset in ExtensionNodeSets) { + if (nset.Id.Length == 0) + errors.Add ("Attribute 'id' can't be empty for global node sets."); + } + + string bp = null; + if (BasePath.Length > 0) + bp = BasePath; + else if (sourceAddinFile != null && sourceAddinFile.Length > 0) + bp = Path.GetDirectoryName (AddinFile); + else if (configFile != null && configFile.Length > 0) + bp = Path.GetDirectoryName (configFile); + + if (bp != null) { + foreach (string file in AllFiles) { + string asmFile = Path.Combine (bp, Util.NormalizePath (file)); + if (!fs.FileExists (asmFile)) + errors.Add ("The file '" + asmFile + "' referenced in the manifest could not be found."); + } + } + + if (localizer != null && localizer.GetAttribute ("type").Length == 0) { + errors.Add ("The attribute 'type' in the Location element is required."); + } + + // Ensure that there are no duplicated properties + + if (properties != null) { + HashSet props = new HashSet (); + foreach (var prop in properties) { + if (!props.Add (prop.Name + " " + prop.Locale)) + errors.Add (string.Format ("Property {0} specified more than once", prop.Name + (prop.Locale != null ? " (" + prop.Locale + ")" : ""))); + } + } + + return errors; + } + + internal void SetExtensionsAddinId (string addinId) + { + foreach (ExtensionPoint ep in ExtensionPoints) + ep.SetExtensionsAddinId (addinId); + + foreach (ExtensionNodeSet ns in ExtensionNodeSets) + ns.SetExtensionsAddinId (addinId); + } + + internal void UnmergeExternalData (Hashtable addins) + { + // Removes extension types and extension sets coming from other add-ins. + foreach (ExtensionPoint ep in ExtensionPoints) + ep.UnmergeExternalData (AddinId, addins); + + foreach (ExtensionNodeSet ns in ExtensionNodeSets) + ns.UnmergeExternalData (AddinId, addins); + } + + internal void MergeExternalData (AddinDescription other) + { + // Removes extension types and extension sets coming from other add-ins. + foreach (ExtensionPoint ep in other.ExtensionPoints) { + ExtensionPoint tep = ExtensionPoints [ep.Path]; + if (tep != null) + tep.MergeWith (AddinId, ep); + } + + foreach (ExtensionNodeSet ns in other.ExtensionNodeSets) { + ExtensionNodeSet tns = ExtensionNodeSets [ns.Id]; + if (tns != null) + tns.MergeWith (AddinId, ns); + } + } + + internal bool IsExtensionModel { + get { return RootElement.LocalName == "ExtensionModel"; } + } + + internal static AddinDescription Merge (AddinDescription desc1, AddinDescription desc2) + { + if (!desc2.IsExtensionModel) { + AddinDescription tmp = desc1; + desc1 = desc2; desc2 = tmp; + } + ((AddinPropertyCollectionImpl)desc1.Properties).AddRange (desc2.Properties); + desc1.ExtensionPoints.AddRange (desc2.ExtensionPoints); + desc1.ExtensionNodeSets.AddRange (desc2.ExtensionNodeSets); + desc1.ConditionTypes.AddRange (desc2.ConditionTypes); + desc1.OptionalModules.AddRange (desc2.OptionalModules); + foreach (string s in desc2.MainModule.Assemblies) + desc1.MainModule.Assemblies.Add (s); + foreach (string s in desc2.MainModule.DataFiles) + desc1.MainModule.DataFiles.Add (s); + desc1.MainModule.MergeWith (desc2.MainModule); + return desc1; + } + + void IBinaryXmlElement.Write (BinaryXmlWriter writer) + { + TransferCoreProperties (true); + writer.WriteValue ("id", ParseString (id)); + writer.WriteValue ("ns", ParseString (ns)); + writer.WriteValue ("isroot", isroot); + writer.WriteValue ("name", ParseString (name)); + writer.WriteValue ("version", ParseString (version)); + writer.WriteValue ("compatVersion", ParseString (compatVersion)); + writer.WriteValue ("hasUserId", hasUserId); + writer.WriteValue ("author", ParseString (author)); + writer.WriteValue ("url", ParseString (url)); + writer.WriteValue ("copyright", ParseString (copyright)); + writer.WriteValue ("description", ParseString (description)); + writer.WriteValue ("category", ParseString (category)); + writer.WriteValue ("basePath", basePath); + writer.WriteValue ("sourceAddinFile", sourceAddinFile); + writer.WriteValue ("defaultEnabled", defaultEnabled); + writer.WriteValue ("domain", domain); + writer.WriteValue ("MainModule", MainModule); + writer.WriteValue ("OptionalModules", OptionalModules); + writer.WriteValue ("NodeSets", ExtensionNodeSets); + writer.WriteValue ("ExtensionPoints", ExtensionPoints); + writer.WriteValue ("ConditionTypes", ConditionTypes); + writer.WriteValue ("FilesInfo", fileInfo); + writer.WriteValue ("Localizer", localizer); + writer.WriteValue ("flags", (int)flags); + writer.WriteValue ("Properties", properties); + } + + void IBinaryXmlElement.Read (BinaryXmlReader reader) + { + id = reader.ReadStringValue ("id"); + ns = reader.ReadStringValue ("ns"); + isroot = reader.ReadBooleanValue ("isroot"); + name = reader.ReadStringValue ("name"); + version = reader.ReadStringValue ("version"); + compatVersion = reader.ReadStringValue ("compatVersion"); + hasUserId = reader.ReadBooleanValue ("hasUserId"); + author = reader.ReadStringValue ("author"); + url = reader.ReadStringValue ("url"); + copyright = reader.ReadStringValue ("copyright"); + description = reader.ReadStringValue ("description"); + category = reader.ReadStringValue ("category"); + basePath = reader.ReadStringValue ("basePath"); + sourceAddinFile = reader.ReadStringValue ("sourceAddinFile"); + defaultEnabled = reader.ReadBooleanValue ("defaultEnabled"); + domain = reader.ReadStringValue ("domain"); + mainModule = (ModuleDescription) reader.ReadValue ("MainModule"); + optionalModules = (ModuleCollection) reader.ReadValue ("OptionalModules", new ModuleCollection (this)); + nodeSets = (ExtensionNodeSetCollection) reader.ReadValue ("NodeSets", new ExtensionNodeSetCollection (this)); + extensionPoints = (ExtensionPointCollection) reader.ReadValue ("ExtensionPoints", new ExtensionPointCollection (this)); + conditionTypes = (ConditionTypeDescriptionCollection) reader.ReadValue ("ConditionTypes", new ConditionTypeDescriptionCollection (this)); + fileInfo = (object[]) reader.ReadValue ("FilesInfo", null); + localizer = (ExtensionNodeDescription) reader.ReadValue ("Localizer"); + flags = (AddinFlags) reader.ReadInt32Value ("flags"); + properties = (AddinPropertyCollectionImpl) reader.ReadValue ("Properties", new AddinPropertyCollectionImpl (this)); + + if (mainModule != null) + mainModule.SetParent (this); + } + } + + class AddinFileInfo: IBinaryXmlElement + { + string fileName; + DateTime timestamp; + + public string FileName { + get { + return fileName; + } + set { + fileName = value; + } + } + + public System.DateTime Timestamp { + get { + return timestamp; + } + set { + timestamp = value; + } + } + + public void Read (BinaryXmlReader reader) + { + fileName = reader.ReadStringValue ("fileName"); + timestamp = reader.ReadDateTimeValue ("timestamp"); + } + + public void Write (BinaryXmlWriter writer) + { + writer.WriteValue ("fileName", fileName); + writer.WriteValue ("timestamp", timestamp); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/AddinFlags.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinFlags.cs new file mode 100644 index 00000000..2aeab94d --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinFlags.cs @@ -0,0 +1,55 @@ +// AddinFlags.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2008 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; + +namespace Mono.Addins.Description +{ + /// + /// Add-in flags + /// + [Flags] + public enum AddinFlags + { + /// + /// No flags + /// + None = 0, + /// + /// The add-in can't be uninstalled + /// + CantUninstall = 1, + /// + /// The add-in can't be disabled + /// + CantDisable = 2, + /// + /// The add-in is not visible to end users + /// + Hidden = 4 + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/AddinProperty.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinProperty.cs new file mode 100644 index 00000000..aee8d682 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinProperty.cs @@ -0,0 +1,70 @@ +// +// AddinProperty.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Xml.Serialization; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Description +{ + /// + /// An add-in property. + /// + public class AddinProperty: IBinaryXmlElement + { + /// + /// Name of the property + /// + [XmlAttribute ("name")] + public string Name { get; set; } + + /// + /// Locale of the property. It is null if the property is not localized. + /// + [XmlAttribute ("locale")] + public string Locale { get; set; } + + /// + /// Value of the property. + /// + [XmlText] + public string Value { get; set; } + + void IBinaryXmlElement.Read (BinaryXmlReader reader) + { + Name = reader.ReadStringValue ("name"); + Locale = reader.ReadStringValue ("locale"); + Value = reader.ReadStringValue ("value"); + } + + void IBinaryXmlElement.Write (BinaryXmlWriter writer) + { + writer.WriteValue ("name", Name); + writer.WriteValue ("locale", Locale); + writer.WriteValue ("value", Value); + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/AddinPropertyCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinPropertyCollection.cs new file mode 100644 index 00000000..1b35be70 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/AddinPropertyCollection.cs @@ -0,0 +1,265 @@ +// +// AddinPropertyCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Linq; +using System.Collections.Generic; + +namespace Mono.Addins.Description +{ + /// + /// A collection of add-in properties + /// + public interface AddinPropertyCollection: IEnumerable + { + /// + /// Gets the value of a property + /// + /// + /// The property value. + /// + /// + /// Name of the property. + /// + /// + /// If the property is localized, it will return the value for the current language if exists, or the + /// default value if it doesn't. + /// + string GetPropertyValue (string name); + + /// + /// Gets the value of a property + /// + /// + /// The property value. + /// + /// + /// Name of the property. + /// + /// + /// Locale for which the value must be returned. + /// + string GetPropertyValue (string name, string locale); + + /// + /// Sets the value of a property + /// + /// + /// Name of the property + /// + /// + /// New value. + /// + void SetPropertyValue (string name, string value); + + /// + /// Sets the value of a property for a specific locale + /// + /// + /// Name of the property. + /// + /// + /// New value. + /// + /// + /// Locale of the property to be set. + /// + void SetPropertyValue (string name, string value, string locale); + + /// + /// Removes a property. + /// + /// + /// Name of the property. + /// + /// + /// This method only removes properties which have no locale set. + /// + void RemoveProperty (string name); + + /// + /// Removes a property with a specified locale + /// + /// + /// Name of the property + /// + /// + /// Locale of the property + /// + void RemoveProperty (string name, string locale); + + + /// + /// Checks whether this collection contains a property + /// + /// true, if the collection has the property, false otherwise. + /// Name of the property + bool HasProperty (string name); + } + + class AddinPropertyCollectionImpl: List, AddinPropertyCollection + { + AddinDescription desc; + + public AddinPropertyCollectionImpl () + { + } + + public AddinPropertyCollectionImpl (AddinDescription desc) + { + this.desc = desc; + } + + public AddinPropertyCollectionImpl (AddinPropertyCollection col) + { + AddRange (col); + } + + public string GetPropertyValue (string name) + { + return GetPropertyValue (name, System.Threading.Thread.CurrentThread.CurrentCulture.ToString ()); + } + + public string GetPropertyValue (string name, string locale) + { + locale = NormalizeLocale (locale); + string lang = GetLocaleLang (locale); + AddinProperty sameLangDifCountry = null; + AddinProperty sameLang = null; + AddinProperty defaultLoc = null; + + foreach (var p in this) { + if (p.Name == name) { + if (p.Locale == locale) + return ParseString (p.Value); + string plang = GetLocaleLang (p.Locale); + if (plang == p.Locale && plang == lang) // No country specified + sameLang = p; + else if (plang == lang) + sameLangDifCountry = p; + else if (p.Locale == null) + defaultLoc = p; + } + } + if (sameLang != null) + return ParseString (sameLang.Value); + else if (sameLangDifCountry != null) + return ParseString (sameLangDifCountry.Value); + else if (defaultLoc != null) + return ParseString (defaultLoc.Value); + else + return string.Empty; + } + + string ParseString (string s) + { + if (desc != null) + return desc.ParseString (s); + else + return s; + } + + string NormalizeLocale (string loc) + { + if (string.IsNullOrEmpty (loc)) + return null; + return loc.Replace ('_','-'); + } + + string GetLocaleLang (string loc) + { + if (loc == null) + return null; + int i = loc.IndexOf ('-'); + if (i != -1) + return loc.Substring (0, i); + else + return loc; + } + + public void SetPropertyValue (string name, string value) + { + SetPropertyValue (name, value, null); + } + + public void SetPropertyValue (string name, string value, string locale) + { + if (string.IsNullOrEmpty (name)) + throw new ArgumentException ("name can't be null or empty"); + + if (value == null) + throw new ArgumentNullException ("value"); + + locale = NormalizeLocale (locale); + + foreach (var p in this) { + if (p.Name == name && p.Locale == locale) { + p.Value = value; + return; + } + } + AddinProperty prop = new AddinProperty (); + prop.Name = name; + prop.Value = value; + prop.Locale = locale; + Add (prop); + } + + public void RemoveProperty (string name) + { + RemoveProperty (name, null); + } + + public void RemoveProperty (string name, string locale) + { + locale = NormalizeLocale (locale); + + foreach (var p in this) { + if (p.Name == name && p.Locale == locale) { + Remove (p); + return; + } + } + } + + public bool HasProperty (string name) + { + return this.Any (p => p.Name == name); + } + + internal string ExtractCoreProperty (string name, bool removeProperty) + { + foreach (var p in this) { + if (p.Name == name && p.Locale == null) { + if (removeProperty) + Remove (p); + return p.Value; + } + } + return null; + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/AssemblyDependency.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/AssemblyDependency.cs new file mode 100644 index 00000000..79d92b45 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/AssemblyDependency.cs @@ -0,0 +1,129 @@ +// +// AssemblyDependency.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Xml; +using System.Xml.Serialization; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Description +{ + /// + /// Definition of a dependency of an add-in on an assembly. + /// + [XmlType ("AssemblyDependency")] + public class AssemblyDependency: Dependency + { + string fullName; + string package; + + /// + /// Initializes a new instance of the class. + /// + public AssemblyDependency () + { + } + + internal AssemblyDependency (XmlElement elem): base (elem) + { + fullName = elem.GetAttribute ("name"); + package = elem.GetAttribute ("package"); + } + + internal override void Verify (string location, StringCollection errors) + { + VerifyNotEmpty (location + "Dependencies/Assembly/", errors, "name", FullName); + } + + internal override void SaveXml (XmlElement parent) + { + CreateElement (parent, "Assembly"); + Element.SetAttribute ("name", FullName); + Element.SetAttribute ("package", Package); + } + + /// + /// Gets or sets the full name of the assembly + /// + /// + /// The full name of the assembly. + /// + public string FullName { + get { return fullName != null ? fullName : string.Empty; } + set { fullName = value; } + } + + /// + /// Gets or sets the name of the package that provides the assembly. + /// + /// + /// The name of the package that provides the assembly. + /// + public string Package { + get { return package != null ? package : string.Empty; } + set { package = value; } + } + + /// + /// Display name of the dependency + /// + /// + /// The name. + /// + public override string Name { + get { + if (Package.Length > 0) + return FullName + " " + GettextCatalog.GetString ("(provided by {0})", Package); + else + return FullName; + } + } + + internal override bool CheckInstalled (AddinRegistry registry) + { + // TODO + return true; + } + + internal override void Write (BinaryXmlWriter writer) + { + base.Write (writer); + writer.WriteValue ("fullName", fullName); + writer.WriteValue ("package", package); + } + + internal override void Read (BinaryXmlReader reader) + { + base.Read (reader); + fullName = reader.ReadStringValue ("fullName"); + package = reader.ReadStringValue ("package"); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ConditionTypeDescription.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ConditionTypeDescription.cs new file mode 100644 index 00000000..a156f95d --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ConditionTypeDescription.cs @@ -0,0 +1,144 @@ +// +// ConditionTypeDescription.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Xml; +using System.Collections.Specialized; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Description +{ + /// + /// A condition type definition. + /// + public sealed class ConditionTypeDescription: ObjectDescription + { + string id; + string typeName; + string addinId; + string description; + + /// + /// Initializes a new instance of the class. + /// + public ConditionTypeDescription () + { + } + + internal ConditionTypeDescription (XmlElement elem): base (elem) + { + id = elem.GetAttribute ("id"); + typeName = elem.GetAttribute ("type"); + description = ReadXmlDescription (); + } + + /// + /// Copies data from another condition type definition + /// + /// + /// Condition from which to copy + /// + public void CopyFrom (ConditionTypeDescription cond) + { + id = cond.id; + typeName = cond.typeName; + addinId = cond.AddinId; + description = cond.description; + } + + internal override void Verify (string location, StringCollection errors) + { + VerifyNotEmpty (location + "ConditionType", errors, Id, "id"); + VerifyNotEmpty (location + "ConditionType (" + Id + ")", errors, TypeName, "type"); + } + + /// + /// Gets or sets the identifier of the condition type + /// + /// + /// The identifier. + /// + public string Id { + get { return id != null ? id : string.Empty; } + set { id = value; } + } + + /// + /// Gets or sets the name of the type that implements the condition + /// + /// + /// The name of the type. + /// + public string TypeName { + get { return typeName != null ? typeName : string.Empty; } + set { typeName = value; } + } + + /// + /// Gets or sets the description of the condition. + /// + /// + /// The description. + /// + public string Description { + get { return description != null ? description : string.Empty; } + set { description = value; } + } + + internal string AddinId { + get { return addinId; } + set { addinId = value; } + } + + internal override void SaveXml (XmlElement parent) + { + CreateElement (parent, "ConditionType"); + Element.SetAttribute ("id", id); + Element.SetAttribute ("type", typeName); + SaveXmlDescription (description); + } + + internal override void Write (BinaryXmlWriter writer) + { + writer.WriteValue ("Id", Id); + writer.WriteValue ("TypeName", TypeName); + writer.WriteValue ("Description", Description); + writer.WriteValue ("AddinId", AddinId); + } + + internal override void Read (BinaryXmlReader reader) + { + Id = reader.ReadStringValue ("Id"); + TypeName = reader.ReadStringValue ("TypeName"); + if (!reader.IgnoreDescriptionData) + Description = reader.ReadStringValue ("Description"); + AddinId = reader.ReadStringValue ("AddinId"); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ConditionTypeDescriptionCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ConditionTypeDescriptionCollection.cs new file mode 100644 index 00000000..9f5d9139 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ConditionTypeDescriptionCollection.cs @@ -0,0 +1,63 @@ +// +// ConditionTypeDescriptionCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins.Description +{ + /// + /// A collection of condition types + /// + public class ConditionTypeDescriptionCollection: ObjectDescriptionCollection + { + /// + /// Initializes a new instance of the class. + /// + public ConditionTypeDescriptionCollection () + { + } + + internal ConditionTypeDescriptionCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// Index. + /// + /// + /// The condition. + /// + public ConditionTypeDescription this [int n] { + get { return (ConditionTypeDescription) List [n]; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/Dependency.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/Dependency.cs new file mode 100644 index 00000000..159fd528 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/Dependency.cs @@ -0,0 +1,59 @@ +// +// Dependency.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Xml.Serialization; +using Mono.Addins.Serialization; +using System.Xml; + +namespace Mono.Addins.Description +{ + /// + /// Definition of an add-in dependency. + /// + [XmlInclude (typeof(AddinDependency))] + public abstract class Dependency: ObjectDescription + { + internal Dependency (XmlElement elem): base (elem) + { + } + + internal Dependency () + { + } + + /// + /// Gets the display name of the dependency. + /// + /// + /// The name. + /// + public abstract string Name { get; } + internal abstract bool CheckInstalled (AddinRegistry registry); + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/DependencyCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/DependencyCollection.cs new file mode 100644 index 00000000..0d13bfbc --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/DependencyCollection.cs @@ -0,0 +1,82 @@ +// +// DependencyCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; + +namespace Mono.Addins.Description +{ + /// + /// A collection of dependency definitions. + /// + public class DependencyCollection: ObjectDescriptionCollection + { + /// + /// Initializes a new instance of the class. + /// + public DependencyCollection () + { + } + + internal DependencyCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// The idnex. + /// + public Dependency this [int n] { + get { return (Dependency) List [n]; } + } + + /// + /// Adds a dependency to the collection + /// + /// + /// The dependency to add. + /// + public void Add (Dependency dep) + { + List.Add (dep); + } + + /// + /// Remove the specified dependency. + /// + /// + /// Dependency to remove. + /// + public void Remove (Dependency dep) + { + List.Remove (dep); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/Extension.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/Extension.cs new file mode 100644 index 00000000..2dd88236 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/Extension.cs @@ -0,0 +1,258 @@ +// +// Extension.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Xml; +using System.Collections.Specialized; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Description +{ + /// + /// An extension definition. + /// + /// + /// An Extension is a collection of nodes which have to be registered in an extension point. + /// The target extension point is specified in the .Path property. + /// + public class Extension: ObjectDescription, IComparable + { + string path; + ExtensionNodeDescriptionCollection nodes; + + /// + /// Initializes a new instance of the class. + /// + public Extension () + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Path that identifies the extension point being extended + /// + public Extension (string path) + { + this.path = path; + } + + /// + /// Gets the object extended by this extension + /// + /// + /// The extended object can be an or + /// an . + /// + /// + /// This method only works when the add-in description to which the extension belongs has been + /// loaded from an add-in registry. + /// + public ObjectDescription GetExtendedObject () + { + AddinDescription desc = ParentAddinDescription; + if (desc == null) + return null; + ExtensionPoint ep = FindExtensionPoint (desc, path); + if (ep == null && desc.OwnerDatabase != null) { + foreach (Dependency dep in desc.MainModule.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) continue; + Addin ad = desc.OwnerDatabase.GetInstalledAddin (ParentAddinDescription.Domain, adep.FullAddinId); + if (ad != null && ad.Description != null) { + ep = FindExtensionPoint (ad.Description, path); + if (ep != null) + break; + } + } + } + if (ep != null) { + string subp = path.Substring (ep.Path.Length).Trim ('/'); + if (subp.Length == 0) + return ep; // The extension is directly extending the extension point + + // The extension is extending a node of the extension point + + return desc.FindExtensionNode (path, true); + } + return null; + } + + /// + /// Gets the node types allowed in this extension. + /// + /// + /// The allowed node types. + /// + /// + /// This method only works when the add-in description to which the extension belongs has been + /// loaded from an add-in registry. + /// + public ExtensionNodeTypeCollection GetAllowedNodeTypes () + { + ObjectDescription ob = GetExtendedObject (); + ExtensionPoint ep = ob as ExtensionPoint; + if (ep != null) + return ep.NodeSet.GetAllowedNodeTypes (); + + ExtensionNodeDescription node = ob as ExtensionNodeDescription; + if (node != null) { + ExtensionNodeType nt = node.GetNodeType (); + if (nt != null) + return nt.GetAllowedNodeTypes (); + } + return new ExtensionNodeTypeCollection (); + } + + ExtensionPoint FindExtensionPoint (AddinDescription desc, string path) + { + foreach (ExtensionPoint ep in desc.ExtensionPoints) { + if (ep.Path == path || path.StartsWith (ep.Path + "/")) + return ep; + } + return null; + } + + internal override void Verify (string location, StringCollection errors) + { + VerifyNotEmpty (location + "Extension", errors, path, "path"); + ExtensionNodes.Verify (location + "Extension (" + path + ")/", errors); + + foreach (ExtensionNodeDescription cnode in ExtensionNodes) + VerifyNode (location, cnode, errors); + } + + void VerifyNode (string location, ExtensionNodeDescription node, StringCollection errors) + { + string id = node.GetAttribute ("id"); + if (id.Length > 0) + id = "(" + id + ")"; + if (node.NodeName == "Condition" && node.GetAttribute ("id").Length == 0) { + errors.Add (location + node.NodeName + id + ": Missing 'id' attribute in Condition element."); + } + if (node.NodeName == "ComplexCondition") { + if (node.ChildNodes.Count > 0) { + VerifyConditionNode (location, node.ChildNodes[0], errors); + for (int n=1; n + /// Initializes a new instance of the class. + /// + /// + /// XML that describes the extension. + /// + public Extension (XmlElement element) + { + Element = element; + path = element.GetAttribute ("path"); + } + + /// + /// Gets or sets the path that identifies the extension point being extended. + /// + /// + /// The path. + /// + public string Path { + get { return path; } + set { path = value; } + } + + internal override void SaveXml (XmlElement parent) + { + if (Element == null) { + Element = parent.OwnerDocument.CreateElement ("Extension"); + parent.AppendChild (Element); + } + Element.SetAttribute ("path", path); + if (nodes != null) + nodes.SaveXml (Element); + } + + /// + /// Gets the extension nodes. + /// + /// + /// The extension nodes. + /// + public ExtensionNodeDescriptionCollection ExtensionNodes { + get { + if (nodes == null) { + nodes = new ExtensionNodeDescriptionCollection (this); + if (Element != null) { + foreach (XmlNode node in Element.ChildNodes) { + XmlElement e = node as XmlElement; + if (e != null) + nodes.Add (new ExtensionNodeDescription (e)); + } + } + } + return nodes; + } + } + + int IComparable.CompareTo (object obj) + { + Extension other = (Extension) obj; + return Path.CompareTo (other.Path); + } + + internal override void Write (BinaryXmlWriter writer) + { + writer.WriteValue ("path", path); + writer.WriteValue ("Nodes", ExtensionNodes); + } + + internal override void Read (BinaryXmlReader reader) + { + path = reader.ReadStringValue ("path"); + nodes = (ExtensionNodeDescriptionCollection) reader.ReadValue ("Nodes", new ExtensionNodeDescriptionCollection (this)); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionCollection.cs new file mode 100644 index 00000000..3587ff3e --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionCollection.cs @@ -0,0 +1,61 @@ +// +// ExtensionCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; + +namespace Mono.Addins.Description +{ + /// + /// A collection of extensions + /// + public class ExtensionCollection: ObjectDescriptionCollection + { + /// + /// Initializes a new instance of the class. + /// + public ExtensionCollection () + { + } + + internal ExtensionCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// The index. + /// + public Extension this [int n] { + get { return (Extension) List [n]; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeDescription.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeDescription.cs new file mode 100644 index 00000000..7274c58c --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeDescription.cs @@ -0,0 +1,374 @@ +// +// ExtensionNodeDescription.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Xml; +using System.Collections.Specialized; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Description +{ + /// + /// An extension node definition. + /// + public class ExtensionNodeDescription: ObjectDescription, NodeElement + { + ExtensionNodeDescriptionCollection childNodes; + string[] attributes; + string nodeName; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Node name. + /// + public ExtensionNodeDescription (string nodeName) + { + this.nodeName = nodeName; + } + + internal ExtensionNodeDescription (XmlElement elem) + { + Element = elem; + nodeName = elem.LocalName; + } + + internal ExtensionNodeDescription () + { + } + + /// + /// Gets the type of the node. + /// + /// + /// The node type. + /// + /// + /// This method only works when the add-in description to which the node belongs has been + /// loaded from an add-in registry. + /// + public ExtensionNodeType GetNodeType () + { + if (Parent is Extension) { + Extension ext = (Extension) Parent; + object ob = ext.GetExtendedObject (); + if (ob is ExtensionPoint) { + ExtensionPoint ep = (ExtensionPoint) ob; + return ep.NodeSet.GetAllowedNodeTypes () [NodeName]; + } else if (ob is ExtensionNodeDescription) { + ExtensionNodeDescription pn = (ExtensionNodeDescription) ob; + ExtensionNodeType pt = ((ExtensionNodeDescription) pn).GetNodeType (); + if (pt != null) + return pt.GetAllowedNodeTypes () [NodeName]; + } + } + else if (Parent is ExtensionNodeDescription) { + ExtensionNodeType pt = ((ExtensionNodeDescription) Parent).GetNodeType (); + if (pt != null) + return pt.GetAllowedNodeTypes () [NodeName]; + } + return null; + } + + /// + /// Gets the extension path under which this node is registered + /// + /// + /// The parent path. + /// + /// + /// For example, if the id of the node is 'ThisNode', and the node is a child of another node with id 'ParentNode', and + /// that parent node is defined in an extension with the path '/Core/MainExtension', then the parent path is 'Core/MainExtension/ParentNode'. + /// + public string GetParentPath () + { + if (Parent is Extension) + return ((Extension)Parent).Path; + else if (Parent is ExtensionNodeDescription) { + ExtensionNodeDescription pn = (ExtensionNodeDescription) Parent; + return pn.GetParentPath () + "/" + pn.Id; + } + else + return string.Empty; + } + + internal override void Verify (string location, StringCollection errors) + { + if (nodeName == null || nodeName.Length == 0) + errors.Add (location + "Node: NodeName can't be empty."); + ChildNodes.Verify (location + NodeName + "/", errors); + } + + /// + /// Gets or sets the name of the node. + /// + /// + /// The name of the node. + /// + public string NodeName { + get { return nodeName; } + internal set { + if (Element != null) + throw new InvalidOperationException ("Can't change node name of xml element"); + nodeName = value; + } + } + + /// + /// Gets or sets the identifier of the node. + /// + /// + /// The identifier. + /// + public string Id { + get { return GetAttribute ("id"); } + set { SetAttribute ("id", value); } + } + + /// + /// Gets or sets the identifier of the node after which this node has to be inserted + /// + /// + /// The identifier of the reference node + /// + public string InsertAfter { + get { return GetAttribute ("insertafter"); } + set { + if (value == null || value.Length == 0) + RemoveAttribute ("insertafter"); + else + SetAttribute ("insertafter", value); + } + } + + /// + /// Gets or sets the identifier of the node before which this node has to be inserted + /// + /// + /// The identifier of the reference node + /// + public string InsertBefore { + get { return GetAttribute ("insertbefore"); } + set { + if (value == null || value.Length == 0) + RemoveAttribute ("insertbefore"); + else + SetAttribute ("insertbefore", value); + } + } + + /// + /// Gets a value indicating whether this node is a condition. + /// + /// + /// true if this node is a condition; otherwise, false. + /// + public bool IsCondition { + get { return nodeName == "Condition" || nodeName == "ComplexCondition"; } + } + + internal override void SaveXml (XmlElement parent) + { + if (Element == null) { + Element = parent.OwnerDocument.CreateElement (nodeName); + parent.AppendChild (Element); + if (attributes != null) { + for (int n=0; n + /// Gets the value of an attribute. + /// + /// + /// The value of the attribute, or an empty string if the attribute is not defined. + /// + /// + /// Name of the attribute. + /// + public string GetAttribute (string key) + { + if (Element != null) + return Element.GetAttribute (key); + + if (attributes == null) + return string.Empty; + for (int n=0; n + /// Sets the value of an attribute. + /// + /// + /// Name of the attribute + /// + /// + /// The value. + /// + public void SetAttribute (string key, string value) + { + if (Element != null) { + Element.SetAttribute (key, value); + return; + } + + if (value == null) + value = string.Empty; + + if (attributes == null) { + attributes = new string [2]; + attributes [0] = key; + attributes [1] = value; + return; + } + + for (int n=0; n + /// Removes an attribute. + /// + /// + /// Name of the attribute to remove. + /// + public void RemoveAttribute (string name) + { + if (Element != null) { + Element.RemoveAttribute (name); + return; + } + + if (attributes == null) + return; + + for (int n=0; n + /// Gets the attributes of the node. + /// + /// + /// The attributes. + /// + public NodeAttribute[] Attributes { + get { + if (Element != null) + SaveXmlAttributes (); + if (attributes == null) + return new NodeAttribute [0]; + NodeAttribute[] ats = new NodeAttribute [attributes.Length / 2]; + for (int n=0; n + /// Gets the child nodes. + /// + /// + /// The child nodes. + /// + public ExtensionNodeDescriptionCollection ChildNodes { + get { + if (childNodes == null) { + childNodes = new ExtensionNodeDescriptionCollection (this); + if (Element != null) { + foreach (XmlNode nod in Element.ChildNodes) { + if (nod is XmlElement) + childNodes.Add (new ExtensionNodeDescription ((XmlElement)nod)); + } + } + } + return childNodes; + } + } + + NodeElementCollection NodeElement.ChildNodes { + get { return ChildNodes; } + } + + void SaveXmlAttributes () + { + attributes = new string [Element.Attributes.Count * 2]; + for (int n=0; n + /// A collection of extension nodes + /// + public class ExtensionNodeDescriptionCollection: ObjectDescriptionCollection, NodeElementCollection + { + /// + /// Initializes a new instance of the class. + /// + public ExtensionNodeDescriptionCollection () + { + } + + internal ExtensionNodeDescriptionCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// The index. + /// + public ExtensionNodeDescription this [int n] { + get { return (ExtensionNodeDescription) List [n]; } + } + + /// + /// Gets the with the specified identifier. + /// + /// + /// Identifier. + /// + public ExtensionNodeDescription this [string id] { + get { + foreach (ExtensionNodeDescription node in List) + if (node.Id == id) + return node; + return null; + } + } + + NodeElement NodeElementCollection.this [int n] { + get { return (NodeElement) List [n]; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeSet.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeSet.cs new file mode 100644 index 00000000..70d6cb0a --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeSet.cs @@ -0,0 +1,441 @@ +// +// ExtensionNodeSet.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Xml; +using Mono.Addins.Serialization; +using System.Collections.Specialized; + +namespace Mono.Addins.Description +{ + /// + /// An extension node set definition. + /// + /// + /// Node sets allow grouping a set of extension node declarations and give an identifier to that group + /// (the node set). Once a node set is declared, it can be referenced from several extension points + /// which use the same extension node structure. Extension node sets also allow declaring recursive + /// extension nodes, that is, extension nodes with a tree structure. + /// + public class ExtensionNodeSet: ObjectDescription + { + string id; + ExtensionNodeTypeCollection nodeTypes; + NodeSetIdCollection nodeSets; + bool missingNodeSetId; + ExtensionNodeTypeCollection cachedAllowedTypes; + + internal string SourceAddinId { get; set; } + + internal ExtensionNodeSet (XmlElement element) + { + Element = element; + id = element.GetAttribute (IdAttribute); + } + + /// + /// Copies data from another node set + /// + /// + /// Node set from which to copy + /// + public void CopyFrom (ExtensionNodeSet nset) + { + id = nset.id; + NodeTypes.Clear (); + foreach (ExtensionNodeType nt in nset.NodeTypes) { + ExtensionNodeType cnt = new ExtensionNodeType (); + cnt.CopyFrom (nt); + NodeTypes.Add (cnt); + } + NodeSets.Clear (); + foreach (string ns in nset.NodeSets) + NodeSets.Add (ns); + missingNodeSetId = nset.missingNodeSetId; + } + + internal override void Verify (string location, StringCollection errors) + { + if (missingNodeSetId) + errors.Add (location + "Missing id attribute in extension set reference"); + + NodeTypes.Verify (location + "ExtensionNodeSet (" + Id + ")/", errors); + } + + internal override void SaveXml (XmlElement parent) + { + SaveXml (parent, "ExtensionNodeSet"); + } + + internal virtual void SaveXml (XmlElement parent, string nodeName) + { + if (Element == null) { + Element = parent.OwnerDocument.CreateElement (nodeName); + parent.AppendChild (Element); + } + if (Id.Length > 0) + Element.SetAttribute (IdAttribute, Id); + if (nodeTypes != null) + nodeTypes.SaveXml (Element); + if (nodeSets != null) { + foreach (string s in nodeSets) { + if (Element.SelectSingleNode ("ExtensionNodeSet[@id='" + s + "']") == null) { + XmlElement e = Element.OwnerDocument.CreateElement ("ExtensionNodeSet"); + e.SetAttribute ("id", s); + Element.AppendChild (e); + } + } + ArrayList list = new ArrayList (); + foreach (XmlElement e in Element.SelectNodes ("ExtensionNodeSet")) { + if (!nodeSets.Contains (e.GetAttribute ("id"))) + list.Add (e); + } + foreach (XmlElement e in list) + Element.RemoveChild (e); + } + } + + /// + /// Initializes a new instance of the class. + /// + public ExtensionNodeSet () + { + } + + /// + /// Gets or sets the identifier of the node set. + /// + /// + /// The identifier. + /// + public string Id { + get { return id != null ? id : string.Empty; } + set { id = value; } + } + + internal virtual string IdAttribute { + get { return "id"; } + } + + /// + /// Gets the node types allowed in this node set. + /// + /// + /// The node types. + /// + public ExtensionNodeTypeCollection NodeTypes { + get { + if (nodeTypes == null) { + if (Element != null) + InitCollections (); + else + nodeTypes = new ExtensionNodeTypeCollection (this); + } + return nodeTypes; + } + } + + /// + /// Gets a list of other node sets included in this node set. + /// + /// + /// The node sets. + /// + public NodeSetIdCollection NodeSets { + get { + if (nodeSets == null) { + if (Element != null) + InitCollections (); + else + nodeSets = new NodeSetIdCollection (); + } + return nodeSets; + } + } + + /// + /// Gets all the allowed node types. + /// + /// + /// The allowed node types. + /// + /// + /// Gets all allowed node types, including those defined in included node sets. + /// This method only works for descriptions loaded from a registry. + /// + public ExtensionNodeTypeCollection GetAllowedNodeTypes () + { + if (cachedAllowedTypes == null) { + cachedAllowedTypes = new ExtensionNodeTypeCollection (); + GetAllowedNodeTypes (new Hashtable (), cachedAllowedTypes); + } + return cachedAllowedTypes; + } + + void GetAllowedNodeTypes (Hashtable visitedSets, ExtensionNodeTypeCollection col) + { + if (Id.Length > 0) { + if (visitedSets.Contains (Id)) + return; + visitedSets [Id] = Id; + } + + // Gets all allowed node types, including those defined in node sets + // It only works for descriptions generated from a registry + + foreach (ExtensionNodeType nt in NodeTypes) + col.Add (nt); + + AddinDescription desc = ParentAddinDescription; + if (desc == null || desc.OwnerDatabase == null) + return; + + foreach (string[] ns in NodeSets.InternalList) { + string startAddin = ns [1]; + if (startAddin == null || startAddin.Length == 0) + startAddin = desc.AddinId; + ExtensionNodeSet nset = desc.OwnerDatabase.FindNodeSet (ParentAddinDescription.Domain, startAddin, ns[0]); + if (nset != null) + nset.GetAllowedNodeTypes (visitedSets, col); + } + } + + internal void Clear () + { + Element = null; + nodeSets = null; + nodeTypes = null; + } + + internal void SetExtensionsAddinId (string addinId) + { + foreach (ExtensionNodeType nt in NodeTypes) { + nt.AddinId = addinId; + nt.SetExtensionsAddinId (addinId); + } + NodeSets.SetExtensionsAddinId (addinId); + } + + internal void MergeWith (string thisAddinId, ExtensionNodeSet other) + { + foreach (ExtensionNodeType nt in other.NodeTypes) { + if (nt.AddinId != thisAddinId && !NodeTypes.Contains (nt)) + NodeTypes.Add (nt); + } + NodeSets.MergeWith (thisAddinId, other.NodeSets); + } + + internal void UnmergeExternalData (string thisAddinId, Hashtable addinsToUnmerge) + { + // Removes extension types and extension sets coming from other add-ins. + + ArrayList todelete = new ArrayList (); + foreach (ExtensionNodeType nt in NodeTypes) { + if (nt.AddinId != thisAddinId && (addinsToUnmerge == null || addinsToUnmerge.Contains (nt.AddinId))) + todelete.Add (nt); + } + foreach (ExtensionNodeType nt in todelete) + NodeTypes.Remove (nt); + + NodeSets.UnmergeExternalData (thisAddinId, addinsToUnmerge); + } + + void InitCollections () + { + nodeTypes = new ExtensionNodeTypeCollection (this); + nodeSets = new NodeSetIdCollection (); + + foreach (XmlNode n in Element.ChildNodes) { + XmlElement nt = n as XmlElement; + if (nt == null) + continue; + if (nt.LocalName == "ExtensionNode") { + ExtensionNodeType etype = new ExtensionNodeType (nt); + nodeTypes.Add (etype); + } + else if (nt.LocalName == "ExtensionNodeSet") { + string id = nt.GetAttribute ("id"); + if (id.Length > 0) + nodeSets.Add (id); + else + missingNodeSetId = true; + } + } + } + + internal override void Write (BinaryXmlWriter writer) + { + writer.WriteValue ("Id", id); + writer.WriteValue ("NodeTypes", NodeTypes); + writer.WriteValue ("NodeSets", NodeSets.InternalList); + } + + internal override void Read (BinaryXmlReader reader) + { + id = reader.ReadStringValue ("Id"); + nodeTypes = (ExtensionNodeTypeCollection) reader.ReadValue ("NodeTypes", new ExtensionNodeTypeCollection (this)); + reader.ReadValue ("NodeSets", NodeSets.InternalList); + } + } + + /// + /// A collection of node set identifiers + /// + public class NodeSetIdCollection: IEnumerable + { + // A list of string[2]. Item 0 is the node set id, item 1 is the addin that defines it. + + ArrayList list = new ArrayList (); + + /// + /// Gets the node set identifier at the specified index. + /// + /// + /// An index. + /// + public string this [int n] { + get { return ((string[])list [n])[0]; } + } + + /// + /// Gets the item count. + /// + /// + /// The count. + /// + public int Count { + get { return list.Count; } + } + + /// + /// Gets the collection enumerator. + /// + /// + /// The enumerator. + /// + public IEnumerator GetEnumerator () + { + ArrayList ll = new ArrayList (list.Count); + foreach (string[] ns in list) + ll.Add (ns [0]); + return ll.GetEnumerator (); + } + + /// + /// Add the specified node set identifier. + /// + /// + /// Node set identifier. + /// + public void Add (string nodeSetId) + { + if (!Contains (nodeSetId)) + list.Add (new string [] { nodeSetId, null }); + } + + /// + /// Remove a node set identifier + /// + /// + /// Node set identifier. + /// + public void Remove (string nodeSetId) + { + int i = IndexOf (nodeSetId); + if (i != -1) + list.RemoveAt (i); + } + + /// + /// Clears the collection + /// + public void Clear () + { + list.Clear (); + } + + /// + /// Checks if the specified identifier is present in the collection + /// + /// + /// true if the node set identifier is present. + /// + public bool Contains (string nodeSetId) + { + return IndexOf (nodeSetId) != -1; + } + + /// + /// Returns the index of the specified node set identifier + /// + /// + /// The index. + /// + /// + /// A node set identifier. + /// + public int IndexOf (string nodeSetId) + { + for (int n=0; n + /// A collection of node sets. + /// + public class ExtensionNodeSetCollection: ObjectDescriptionCollection + { + /// + /// Initializes a new instance of the class. + /// + public ExtensionNodeSetCollection () + { + } + + internal ExtensionNodeSetCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// The index. + /// + public ExtensionNodeSet this [int n] { + get { return (ExtensionNodeSet) List [n]; } + } + + /// + /// Gets the with the specified id. + /// + /// + /// Identifier. + /// + public ExtensionNodeSet this [string id] { + get { + for (int n=0; n + /// An extension node type definition. + /// + public sealed class ExtensionNodeType: ExtensionNodeSet + { + string typeName; + string objectTypeName; + string description; + string addinId; + NodeTypeAttributeCollection attributes; + string customAttributeTypeName; + + // Cached clr type + [NonSerialized] + internal Type Type; + + // Cached serializable fields + [NonSerialized] + internal Dictionary Fields; + + // Cached serializable fields for the custom attribute + [NonSerialized] + internal Dictionary CustomAttributeFields; + + [NonSerialized] + internal FieldData CustomAttributeMember; + + internal class FieldData { + public MemberInfo Member; + public bool Required; + public bool Localizable; + + public void SetValue (object target, object val) + { + if (Member is FieldInfo) + ((FieldInfo)Member).SetValue (target, val); + else + ((PropertyInfo)Member).SetValue (target, val, null); + } + + public Type MemberType { + get { + return (Member is FieldInfo) ? ((FieldInfo)Member).FieldType : ((PropertyInfo)Member).PropertyType; + } + } + } + + // Addin where this extension type is implemented + internal string AddinId { + get { return addinId; } + set { addinId = value; } + } + + /// + /// Type that implements the extension node. + /// + /// + /// The full name of the type. + /// + public string TypeName { + get { return typeName != null ? typeName : string.Empty; } + set { typeName = value; } + } + + /// + /// Element name to be used when defining an extension in an XML manifest. The default name is "Type". + /// + /// + /// The name of the node. + /// + public string NodeName { + get { return Id; } + set { Id = value; } + } + + /// + /// Type of the object that the extension creates (only valid for TypeNodeExtension). + /// + public string ObjectTypeName { + get { return objectTypeName != null ? objectTypeName : string.Empty; } + set { objectTypeName = value; } + } + + /// + /// Name of the custom attribute that can be used to declare nodes of this type + /// + public string ExtensionAttributeTypeName { + get { return customAttributeTypeName ?? string.Empty; } + set { customAttributeTypeName = value; } + } + + /// + /// Long description of the node type + /// + public string Description { + get { return description != null ? description : string.Empty; } + set { description = value; } + } + + /// + /// Attributes supported by the extension node type. + /// + public NodeTypeAttributeCollection Attributes { + get { + if (attributes == null) { + attributes = new NodeTypeAttributeCollection (this); + if (Element != null) { + XmlElement atts = Element ["Attributes"]; + if (atts != null) { + foreach (XmlNode node in atts.ChildNodes) { + XmlElement e = node as XmlElement; + if (e != null) + attributes.Add (new NodeTypeAttribute (e)); + } + } + } + } + return attributes; + } + } + + internal ExtensionNodeType (XmlElement element): base (element) + { + XmlAttribute at = element.Attributes ["type"]; + if (at != null) + typeName = at.Value; + at = element.Attributes ["objectType"]; + if (at != null) + objectTypeName = at.Value; + at = element.Attributes ["customAttributeType"]; + if (at != null) + customAttributeTypeName = at.Value; + XmlElement de = element ["Description"]; + if (de != null) + description = de.InnerText; + } + + /// + /// Initializes a new instance of the class. + /// + public ExtensionNodeType () + { + } + + /// + /// Copies data from another node set + /// + public void CopyFrom (ExtensionNodeType ntype) + { + base.CopyFrom (ntype); + this.typeName = ntype.TypeName; + this.objectTypeName = ntype.ObjectTypeName; + this.description = ntype.Description; + this.addinId = ntype.AddinId; + Attributes.Clear (); + foreach (NodeTypeAttribute att in ntype.Attributes) { + NodeTypeAttribute catt = new NodeTypeAttribute (); + catt.CopyFrom (att); + Attributes.Add (catt); + } + } + + internal override string IdAttribute { + get { return "name"; } + } + + internal override void Verify (string location, StringCollection errors) + { + base.Verify (location, errors); + } + + internal override void SaveXml (XmlElement parent, string nodeName) + { + base.SaveXml (parent, "ExtensionNode"); + + XmlElement atts = Element ["Attributes"]; + if (Attributes.Count > 0) { + if (atts == null) { + atts = parent.OwnerDocument.CreateElement ("Attributes"); + Element.AppendChild (atts); + } + Attributes.SaveXml (atts); + } else { + if (atts != null) + Element.RemoveChild (atts); + } + + if (TypeName.Length > 0) + Element.SetAttribute ("type", TypeName); + else + Element.RemoveAttribute ("type"); + + if (ObjectTypeName.Length > 0) + Element.SetAttribute ("objectType", ObjectTypeName); + else + Element.RemoveAttribute ("objectType"); + + if (ExtensionAttributeTypeName.Length > 0) + Element.SetAttribute ("customAttributeType", ExtensionAttributeTypeName); + else + Element.RemoveAttribute ("customAttributeType"); + + SaveXmlDescription (Description); + } + + internal override void Write (BinaryXmlWriter writer) + { + base.Write (writer); + if (Id.Length == 0) + Id = "Type"; + if (TypeName.Length == 0) + typeName = "Mono.Addins.TypeExtensionNode"; + writer.WriteValue ("typeName", typeName); + writer.WriteValue ("objectTypeName", objectTypeName); + writer.WriteValue ("description", description); + writer.WriteValue ("addinId", addinId); + writer.WriteValue ("Attributes", attributes); + writer.WriteValue ("customAttributeType", customAttributeTypeName); + } + + internal override void Read (BinaryXmlReader reader) + { + base.Read (reader); + typeName = reader.ReadStringValue ("typeName"); + objectTypeName = reader.ReadStringValue ("objectTypeName"); + if (!reader.IgnoreDescriptionData) + description = reader.ReadStringValue ("description"); + addinId = reader.ReadStringValue ("addinId"); + if (!reader.IgnoreDescriptionData) + attributes = (NodeTypeAttributeCollection) reader.ReadValue ("Attributes", new NodeTypeAttributeCollection (this)); + customAttributeTypeName = reader.ReadStringValue ("customAttributeType"); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeTypeCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeTypeCollection.cs new file mode 100644 index 00000000..977ddae0 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionNodeTypeCollection.cs @@ -0,0 +1,76 @@ +// +// ExtensionNodeTypeCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; + +namespace Mono.Addins.Description +{ + /// + /// A collection of node types. + /// + public class ExtensionNodeTypeCollection: ObjectDescriptionCollection + { + /// + /// Initializes a new instance of the class. + /// + public ExtensionNodeTypeCollection () + { + } + + internal ExtensionNodeTypeCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// The index. + /// + public ExtensionNodeType this [int n] { + get { return (ExtensionNodeType) List [n]; } + } + + /// + /// Gets the with the specified id. + /// + /// + /// Identifier. + /// + public ExtensionNodeType this [string id] { + get { + for (int n=0; n + /// An extension point definition. + /// + public sealed class ExtensionPoint: ObjectDescription + { + string path; + string name; + string description; + ExtensionNodeSet nodeSet; + ConditionTypeDescriptionCollection conditions; + string defaultInsertBefore; + string defaultInsertAfter; + + // Information gathered from others addins: + + List addins; // Add-ins which extend this extension point + string rootAddin; // Add-in which defines this extension point + + internal ExtensionPoint (XmlElement elem): base (elem) + { + path = elem.GetAttribute ("path"); + name = elem.GetAttribute ("name"); + defaultInsertBefore = elem.GetAttribute ("defaultInsertBefore"); + defaultInsertAfter = elem.GetAttribute ("defaultInsertAfter"); + description = ReadXmlDescription (); + } + + /// + /// Initializes a new instance of the class. + /// + public ExtensionPoint () + { + } + + /// + /// Copies another extension point. + /// + /// + /// Extension point from which to copy. + /// + public void CopyFrom (ExtensionPoint ep) + { + path = ep.path; + name = ep.name; + defaultInsertBefore = ep.defaultInsertBefore; + defaultInsertAfter = ep.defaultInsertAfter; + description = ep.description; + NodeSet.CopyFrom (ep.NodeSet); + Conditions.Clear (); + foreach (ConditionTypeDescription cond in ep.Conditions) { + ConditionTypeDescription cc = new ConditionTypeDescription (); + cc.CopyFrom (cond); + Conditions.Add (cc); + } + Addins.Clear (); + foreach (string s in ep.Addins) + Addins.Add (s); + rootAddin = ep.rootAddin; + } + + internal override void Verify (string location, StringCollection errors) + { + VerifyNotEmpty (location + "ExtensionPoint", errors, Path, "path"); + NodeSet.Verify (location + "ExtensionPoint (" + Path + ")/", errors); + Conditions.Verify (location + "ExtensionPoint (" + Path + ")/", errors); + } + + internal void SetExtensionsAddinId (string addinId) + { + NodeSet.SetExtensionsAddinId (addinId); + foreach (ConditionTypeDescription cond in Conditions) + cond.AddinId = addinId; + Addins.Add (addinId); + } + + internal void MergeWith (string thisAddinId, ExtensionPoint ep) + { + NodeSet.MergeWith (thisAddinId, ep.NodeSet); + + foreach (ConditionTypeDescription cond in ep.Conditions) { + if (cond.AddinId != thisAddinId && !Conditions.Contains (cond)) + Conditions.Add (cond); + } + foreach (string s in ep.Addins) { + if (!Addins.Contains (s)) + Addins.Add (s); + } + } + + internal void UnmergeExternalData (string thisAddinId, Hashtable addinsToUnmerge) + { + NodeSet.UnmergeExternalData (thisAddinId, addinsToUnmerge); + + ArrayList todel = new ArrayList (); + foreach (ConditionTypeDescription cond in Conditions) { + if (cond.AddinId != thisAddinId && (addinsToUnmerge == null || addinsToUnmerge.Contains (cond.AddinId))) + todel.Add (cond); + } + foreach (ConditionTypeDescription cond in todel) + Conditions.Remove (cond); + + if (addinsToUnmerge == null) + Addins.Clear (); + else { + foreach (string s in addinsToUnmerge.Keys) + Addins.Remove (s); + } + if (thisAddinId != null && !Addins.Contains (thisAddinId)) + Addins.Add (thisAddinId); + } + + internal void Clear () + { + NodeSet.Clear (); + Conditions.Clear (); + Addins.Clear (); + } + + internal override void SaveXml (XmlElement parent) + { + CreateElement (parent, "ExtensionPoint"); + + Element.SetAttribute ("path", Path); + + if (Name.Length > 0) + Element.SetAttribute ("name", Name); + else + Element.RemoveAttribute ("name"); + + if (DefaultInsertBefore.Length > 0) + Element.SetAttribute ("defaultInsertBefore", DefaultInsertBefore); + else + Element.RemoveAttribute ("defaultInsertBefore"); + + if (DefaultInsertAfter.Length > 0) + Element.SetAttribute ("defaultInsertAfter", DefaultInsertAfter); + else + Element.RemoveAttribute ("defaultInsertAfter"); + + SaveXmlDescription (Description); + + if (nodeSet != null) { + nodeSet.Element = Element; + nodeSet.SaveXml (parent); + } + } + + /// + /// Gets or sets the path that identifies the extension point. + /// + /// + /// The path. + /// + public string Path { + get { return path != null ? path : string.Empty; } + set { path = value; } + } + + /// + /// Gets or sets the display name of the extension point. + /// + /// + /// The name. + /// + public string Name { + get { return name != null ? name : string.Empty; } + set { name = value; } + } + + /// + /// Gets or sets the description of the extension point. + /// + /// + /// The description. + /// + public string Description { + get { return description != null ? description : string.Empty; } + set { description = value; } + } + + /// + /// Gets a list of add-ins that extend this extension point. + /// + /// + /// This value is only available when the add-in description is loaded from an add-in registry. + /// + public string[] ExtenderAddins { + get { + return Addins.ToArray (); + } + } + + internal List Addins { + get { + if (addins == null) + addins = new List (); + return addins; + } + } + + internal string RootAddin { + get { return rootAddin; } + set { rootAddin = value; } + } + + /// + /// A node set which specifies the node types allowed in this extension point. + /// + /// + /// The node set. + /// + public ExtensionNodeSet NodeSet { + get { + if (nodeSet == null) { + if (Element != null) + nodeSet = new ExtensionNodeSet (Element); + else + nodeSet = new ExtensionNodeSet (); + nodeSet.SetParent (this); + } + return nodeSet; + } + } + + internal void SetNodeSet (ExtensionNodeSet nset) + { + // Used only by the addin updater + nodeSet = nset; + nodeSet.SetParent (this); + } + + /// + /// Gets the conditions available in this node set. + /// + /// + /// The conditions. + /// + public ConditionTypeDescriptionCollection Conditions { + get { + if (conditions == null) { + conditions = new ConditionTypeDescriptionCollection (this); + if (Element != null) { + foreach (XmlElement elem in Element.SelectNodes ("ConditionType")) + conditions.Add (new ConditionTypeDescription (elem)); + } + } + return conditions; + } + } + + /// + /// Adds an extension node type. + /// + /// + /// The extension node type. + /// + /// + /// Name of the node + /// + /// + /// Name of the type that implements the extension node. + /// + /// + /// This method can be used to register a new allowed node type for the extension point. + /// + public ExtensionNodeType AddExtensionNode (string name, string typeName) + { + ExtensionNodeType ntype = new ExtensionNodeType (); + ntype.Id = name; + ntype.TypeName = typeName; + NodeSet.NodeTypes.Add (ntype); + return ntype; + } + + /// + /// The id of the extension before which new extensions will be added, unless the extension defines its own InsertBefore value + /// + /// The default insert before. + public string DefaultInsertBefore { + get { return defaultInsertBefore ?? ""; } + set { defaultInsertBefore = value; } + } + + /// + /// The id of the extension after which new extensions will be added, unless the extension defines its own InsertAfter value + /// + /// The default insert before. + public string DefaultInsertAfter { + get { return defaultInsertAfter ?? ""; } + set { defaultInsertAfter = value; } + } + + internal override void Write (BinaryXmlWriter writer) + { + writer.WriteValue ("path", path); + writer.WriteValue ("name", name); + writer.WriteValue ("description", Description); + writer.WriteValue ("rootAddin", rootAddin); + writer.WriteValue ("addins", Addins); + writer.WriteValue ("NodeSet", NodeSet); + writer.WriteValue ("Conditions", Conditions); + writer.WriteValue ("defaultInsertBefore", defaultInsertBefore); + writer.WriteValue ("defaultInsertAfter", defaultInsertAfter); + } + + internal override void Read (BinaryXmlReader reader) + { + path = reader.ReadStringValue ("path"); + name = reader.ReadStringValue ("name"); + if (!reader.IgnoreDescriptionData) + description = reader.ReadStringValue ("description"); + rootAddin = reader.ReadStringValue ("rootAddin"); + addins = (List) reader.ReadValue ("addins", new List ()); + nodeSet = (ExtensionNodeSet) reader.ReadValue ("NodeSet"); + conditions = (ConditionTypeDescriptionCollection) reader.ReadValue ("Conditions", new ConditionTypeDescriptionCollection (this)); + defaultInsertBefore = reader.ReadStringValue ("defaultInsertBefore"); + defaultInsertAfter = reader.ReadStringValue ("defaultInsertAfter"); + if (nodeSet != null) + nodeSet.SetParent (this); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionPointCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionPointCollection.cs new file mode 100644 index 00000000..bbadb2b2 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ExtensionPointCollection.cs @@ -0,0 +1,76 @@ +// +// ExtensionPointCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; + +namespace Mono.Addins.Description +{ + /// + /// A collection of extension point definitions. + /// + public class ExtensionPointCollection: ObjectDescriptionCollection + { + /// + /// Initializes a new instance of the class. + /// + public ExtensionPointCollection () + { + } + + internal ExtensionPointCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// The index. + /// + public ExtensionPoint this [int n] { + get { return (ExtensionPoint) List [n]; } + } + + /// + /// Gets the with the specified path. + /// + /// + /// Path. + /// + public ExtensionPoint this [string path] { + get { + for (int n=0; n + /// A module definition. + /// + /// + /// Optional modules can be used to declare extensions which will be registered only if some + /// specified add-in dependencies can be satisfied. + /// + public class ModuleDescription: ObjectDescription + { + StringCollection assemblies; + StringCollection dataFiles; + StringCollection ignorePaths; + DependencyCollection dependencies; + ExtensionCollection extensions; + + // Used only at run time + internal RuntimeAddin RuntimeAddin; + + internal ModuleDescription (XmlElement element) + { + Element = element; + } + + /// + /// Initializes a new instance of the class. + /// + public ModuleDescription () + { + } + + internal void MergeWith (ModuleDescription module) + { + Dependencies.AddRange (module.Dependencies); + Extensions.AddRange (module.Extensions); + } + + /// + /// Checks if this module depends on the specified add-in. + /// + /// + /// true if there is a dependency. + /// + /// + /// Identifier of the add-in + /// + public bool DependsOnAddin (string addinId) + { + AddinDescription desc = Parent as AddinDescription; + if (desc == null) + throw new InvalidOperationException (); + + foreach (Dependency dep in Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep == null) continue; + if (Addin.GetFullId (desc.Namespace, adep.AddinId, adep.Version) == addinId) + return true; + } + return false; + } + + /// + /// Gets the list of paths to be ignored by the add-in scanner. + /// + public StringCollection IgnorePaths { + get { + if (ignorePaths == null) + ignorePaths = new StringCollection (); + return ignorePaths; + } + } + + /// + /// Gets all external files + /// + /// + /// All files. + /// + /// + /// External files are data files and assemblies explicitly referenced in the Runtime section of the add-in manifest. + /// + public StringCollection AllFiles { + get { + StringCollection col = new StringCollection (); + foreach (string s in Assemblies) + col.Add (s); + + foreach (string d in DataFiles) + col.Add (d); + + return col; + } + } + + /// + /// Gets the list of external assemblies used by this module. + /// + public StringCollection Assemblies { + get { + if (assemblies == null) { + if (Element != null) + InitCollections (); + else + assemblies = new StringCollection (); + } + return assemblies; + } + } + + /// + /// Gets the list of external data files used by this module + /// + public StringCollection DataFiles { + get { + if (dataFiles == null) { + if (Element != null) + InitCollections (); + else + dataFiles = new StringCollection (); + } + return dataFiles; + } + } + + /// + /// Gets the dependencies of this module + /// + public DependencyCollection Dependencies { + get { + if (dependencies == null) { + dependencies = new DependencyCollection (this); + if (Element != null) { + XmlNodeList elems = Element.SelectNodes ("Dependencies/*"); + + foreach (XmlNode node in elems) { + XmlElement elem = node as XmlElement; + if (elem == null) continue; + + if (elem.Name == "Addin") { + AddinDependency dep = new AddinDependency (elem); + dependencies.Add (dep); + } else if (elem.Name == "Assembly") { + AssemblyDependency dep = new AssemblyDependency (elem); + dependencies.Add (dep); + } + } + } + } + return dependencies; + } + } + + /// + /// Gets the extensions of this module + /// + public ExtensionCollection Extensions { + get { + if (extensions == null) { + extensions = new ExtensionCollection (this); + if (Element != null) { + foreach (XmlElement elem in Element.SelectNodes ("Extension")) + extensions.Add (new Extension (elem)); + } + } + return extensions; + } + } + + /// + /// Adds an extension node to the module. + /// + /// + /// The extension node. + /// + /// + /// Path that identifies the extension point. + /// + /// + /// Node name. + /// + /// + /// This method creates a new Extension object for the provided path if none exist. + /// + public ExtensionNodeDescription AddExtensionNode (string path, string nodeName) + { + ExtensionNodeDescription node = new ExtensionNodeDescription (nodeName); + GetExtension (path).ExtensionNodes.Add (node); + return node; + } + + /// + /// Gets an extension instance. + /// + /// + /// The extension instance. + /// + /// + /// Path that identifies the extension point that the extension extends. + /// + /// + /// This method creates a new Extension object for the provided path if none exist. + /// + public Extension GetExtension (string path) + { + foreach (Extension e in Extensions) { + if (e.Path == path) + return e; + } + Extension ex = new Extension (path); + Extensions.Add (ex); + return ex; + } + + internal override void SaveXml (XmlElement parent) + { + CreateElement (parent, "Module"); + + if (assemblies != null || dataFiles != null || ignorePaths != null) { + XmlElement runtime = GetRuntimeElement (); + + while (runtime.FirstChild != null) + runtime.RemoveChild (runtime.FirstChild); + + if (assemblies != null) { + foreach (string s in assemblies) { + XmlElement asm = Element.OwnerDocument.CreateElement ("Import"); + asm.SetAttribute ("assembly", s); + runtime.AppendChild (asm); + } + } + if (dataFiles != null) { + foreach (string s in dataFiles) { + XmlElement asm = Element.OwnerDocument.CreateElement ("Import"); + asm.SetAttribute ("file", s); + runtime.AppendChild (asm); + } + } + if (ignorePaths != null) { + foreach (string s in ignorePaths) { + XmlElement asm = Element.OwnerDocument.CreateElement ("ScanExclude"); + asm.SetAttribute ("path", s); + runtime.AppendChild (asm); + } + } + runtime.AppendChild (Element.OwnerDocument.CreateTextNode ("\n")); + } + + // Save dependency information + + if (dependencies != null) { + XmlElement deps = GetDependenciesElement (); + dependencies.SaveXml (deps); + deps.AppendChild (Element.OwnerDocument.CreateTextNode ("\n")); + + if (extensions != null) + extensions.SaveXml (Element); + } + } + + /// + /// Adds an add-in reference (there is a typo in the method name) + /// + /// + /// Identifier of the add-in. + /// + /// + /// Version of the add-in. + /// + public void AddAssemblyReference (string id, string version) + { + XmlElement deps = GetDependenciesElement (); + if (deps.SelectSingleNode ("Addin[@id='" + id + "']") != null) + return; + + XmlElement dep = Element.OwnerDocument.CreateElement ("Addin"); + dep.SetAttribute ("id", id); + dep.SetAttribute ("version", version); + deps.AppendChild (dep); + } + + XmlElement GetDependenciesElement () + { + XmlElement de = Element ["Dependencies"]; + if (de != null) + return de; + + de = Element.OwnerDocument.CreateElement ("Dependencies"); + Element.AppendChild (de); + return de; + } + + XmlElement GetRuntimeElement () + { + XmlElement de = Element ["Runtime"]; + if (de != null) + return de; + + de = Element.OwnerDocument.CreateElement ("Runtime"); + Element.AppendChild (de); + return de; + } + + void InitCollections () + { + dataFiles = new StringCollection (); + assemblies = new StringCollection (); + + XmlNodeList elems = Element.SelectNodes ("Runtime/*"); + foreach (XmlElement elem in elems) { + if (elem.LocalName == "Import") { + string asm = elem.GetAttribute ("assembly"); + if (asm.Length > 0) { + assemblies.Add (asm); + } else { + string file = elem.GetAttribute ("file"); + if (file.Length > 0) + dataFiles.Add (file); + } + } else if (elem.LocalName == "ScanExclude") { + string path = elem.GetAttribute ("path"); + if (path.Length > 0) + IgnorePaths.Add (path); + } + } + } + + internal override void Verify (string location, StringCollection errors) + { + Dependencies.Verify (location + "Module/", errors); + Extensions.Verify (location + "Module/", errors); + } + + internal override void Write (BinaryXmlWriter writer) + { + // Normalize assembly and data file paths when saving as binary. Binary files are not supposed to be portable, + // so it is safe to store platform-specific path separators. + + writer.WriteValue ("Assemblies", NormalizePaths (Assemblies)); + writer.WriteValue ("DataFiles", NormalizePaths (DataFiles)); + writer.WriteValue ("Dependencies", Dependencies); + writer.WriteValue ("Extensions", Extensions); + writer.WriteValue ("IgnorePaths", NormalizePaths (ignorePaths)); + } + + internal override void Read (BinaryXmlReader reader) + { + // We can assume that paths read from a binary files are always normalized + + assemblies = (StringCollection) reader.ReadValue ("Assemblies", new StringCollection ()); + dataFiles = (StringCollection) reader.ReadValue ("DataFiles", new StringCollection ()); + dependencies = (DependencyCollection) reader.ReadValue ("Dependencies", new DependencyCollection (this)); + extensions = (ExtensionCollection) reader.ReadValue ("Extensions", new ExtensionCollection (this)); + ignorePaths = (StringCollection) reader.ReadValue ("IgnorePaths", new StringCollection ()); + } + + StringCollection NormalizePaths (StringCollection collection) + { + var list = new StringCollection (); + foreach (var path in collection) + list.Add (Util.NormalizePath (path)); + return list; + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ModuleDescriptionCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ModuleDescriptionCollection.cs new file mode 100644 index 00000000..87d0c890 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ModuleDescriptionCollection.cs @@ -0,0 +1,60 @@ +// +// ModuleCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; + +namespace Mono.Addins.Description +{ + /// + /// A collection of module descriptions + /// + public class ModuleCollection: ObjectDescriptionCollection + { + /// + /// Initializes a new instance of the class. + /// + public ModuleCollection () + { + } + + internal ModuleCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// The index. + /// + public ModuleDescription this [int n] { + get { return (ModuleDescription) List [n]; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/NativeDependency.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/NativeDependency.cs new file mode 100644 index 00000000..d8c61795 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/NativeDependency.cs @@ -0,0 +1,52 @@ +// +// NativeDependency.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Xml; +using System.Xml.Serialization; +using Mono.Addins.Description; + +namespace Mono.Addins.Description +{ +#pragma warning disable 1591 + [Obsolete] + [XmlType ("NativeReference")] + public class NativeDependency: Dependency + { + public override string Name { + get { return "Native dependency"; } + } + + internal override bool CheckInstalled (AddinRegistry registry) + { + return false; + } + } +#pragma warning restore 1591 +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/NodeTypeAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/NodeTypeAttribute.cs new file mode 100644 index 00000000..149a401b --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/NodeTypeAttribute.cs @@ -0,0 +1,206 @@ +// +// NodeTypeAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Xml; +using System.Collections.Specialized; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Description +{ + /// + /// Description of the attribute of a node type. + /// + public sealed class NodeTypeAttribute: ObjectDescription + { + string name; + string type; + bool required; + bool localizable; + string description; + + /// + /// Initializes a new instance of the class. + /// + public NodeTypeAttribute() + { + } + + /// + /// Copies data from another node attribute. + /// + /// + /// The attribute from which to copy. + /// + public void CopyFrom (NodeTypeAttribute att) + { + name = att.name; + type = att.type; + required = att.required; + localizable = att.localizable; + description = att.description; + } + + /// + /// Gets or sets the name of the attribute. + /// + /// + /// The name. + /// + public string Name { + get { return name != null ? name : string.Empty; } + set { name = value; } + } + + /// + /// Gets or sets a value indicating whether this is required. + /// + /// + /// true if required; otherwise, false. + /// + public bool Required { + get { return required; } + set { required = value; } + } + + /// + /// Gets or sets a value indicating whether this is localizable. + /// + /// + /// true if localizable; otherwise, false. + /// + public bool Localizable { + get { return localizable; } + set { localizable = value; } + } + + /// + /// Gets or sets the type of the attribute. + /// + /// + /// The type. + /// + public string Type { + get { return type != null ? type : string.Empty; } + set { type = value; } + } + + /// + /// Gets or sets the description of the attribute. + /// + /// + /// The description. + /// + public string Description { + get { return description != null ? description : string.Empty; } + set { description = value; } + } + + /// + /// Gets or sets the type of the content. + /// + /// + /// Allows specifying the type of the content of a string attribute. + /// The value of this property is only informative, and it doesn't + /// have any effect on how add-ins are packaged or loaded. + /// + public ContentType ContentType { get; set; } + + internal override void Verify (string location, StringCollection errors) + { + VerifyNotEmpty (location + "Attribute", errors, Name, "name"); + } + + internal NodeTypeAttribute (XmlElement elem): base (elem) + { + name = elem.GetAttribute ("name"); + type = elem.GetAttribute ("type"); + required = elem.GetAttribute ("required").ToLower () == "true"; + localizable = elem.GetAttribute ("localizable").ToLower () == "true"; + string ct = elem.GetAttribute ("contentType"); + if (!string.IsNullOrEmpty (ct)) + ContentType = (ContentType) Enum.Parse (typeof(ContentType), ct); + description = ReadXmlDescription (); + } + + internal override void SaveXml (XmlElement parent) + { + CreateElement (parent, "Attribute"); + Element.SetAttribute ("name", name); + + if (Type.Length > 0) + Element.SetAttribute ("type", Type); + else + Element.RemoveAttribute ("type"); + + if (required) + Element.SetAttribute ("required", "True"); + else + Element.RemoveAttribute ("required"); + + if (localizable) + Element.SetAttribute ("localizable", "True"); + else + Element.RemoveAttribute ("localizable"); + + if (ContentType != ContentType.Text) + Element.SetAttribute ("contentType", ContentType.ToString ()); + else + Element.RemoveAttribute ("contentType"); + + SaveXmlDescription (description); + } + + internal override void Write (BinaryXmlWriter writer) + { + writer.WriteValue ("name", name); + writer.WriteValue ("type", type); + writer.WriteValue ("required", required); + writer.WriteValue ("description", description); + writer.WriteValue ("localizable", localizable); + writer.WriteValue ("contentType", ContentType.ToString ()); + } + + internal override void Read (BinaryXmlReader reader) + { + name = reader.ReadStringValue ("name"); + type = reader.ReadStringValue ("type"); + required = reader.ReadBooleanValue ("required"); + if (!reader.IgnoreDescriptionData) + description = reader.ReadStringValue ("description"); + localizable = reader.ReadBooleanValue ("localizable"); + string ct = reader.ReadStringValue ("contentType"); + try { + ContentType = (ContentType) Enum.Parse (typeof(ContentType), ct); + } catch { + ContentType = ContentType.Text; + } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/NodeTypeAttributeCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/NodeTypeAttributeCollection.cs new file mode 100644 index 00000000..d0ea4f66 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/NodeTypeAttributeCollection.cs @@ -0,0 +1,60 @@ +// +// NodeTypeAttributeCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins.Description +{ + /// + /// A collection of node attributes + /// + public class NodeTypeAttributeCollection: ObjectDescriptionCollection + { + /// + /// Initializes a new instance of the class. + /// + public NodeTypeAttributeCollection () + { + } + + internal NodeTypeAttributeCollection (object owner): base (owner) + { + } + + /// + /// Gets the at the specified index. + /// + /// + /// The index. + /// + public NodeTypeAttribute this [int n] { + get { return (NodeTypeAttribute) List [n]; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ObjectDescription.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ObjectDescription.cs new file mode 100644 index 00000000..4556c312 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ObjectDescription.cs @@ -0,0 +1,159 @@ +// +// ObjectDescription.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections.Specialized; +using System.Xml; +using Mono.Addins.Serialization; + +namespace Mono.Addins.Description +{ + /// + /// Base class for add-in description definitions. + /// + public class ObjectDescription: IBinaryXmlElement + { + internal XmlElement Element; + object parent; + + internal ObjectDescription (XmlElement elem) + { + Element = elem; + } + + internal ObjectDescription () + { + } + + /// + /// Gets the parent object. + /// + /// + /// The parent object. + /// + public object Parent { + get { return parent; } + } + + /// + /// Gets the parent add-in description. + /// + /// + /// The parent add-in description. + /// + public AddinDescription ParentAddinDescription { + get { + if (parent is AddinDescription) + return (AddinDescription) parent; + else if (parent is ObjectDescription) + return ((ObjectDescription)parent).ParentAddinDescription; + else + return null; + } + } + + internal string ParseString (string s) + { + var desc = ParentAddinDescription; + if (desc != null) + return desc.ParseString (s); + else + return s; + } + + internal void SetParent (object ob) + { + parent = ob; + } + + void IBinaryXmlElement.Write (BinaryXmlWriter writer) + { + Write (writer); + } + + void IBinaryXmlElement.Read (BinaryXmlReader reader) + { + Read (reader); + } + + internal virtual void Write (BinaryXmlWriter writer) + { + } + + internal virtual void Read (BinaryXmlReader reader) + { + } + + internal virtual void SaveXml (XmlElement parent) + { + } + + internal void CreateElement (XmlElement parent, string nodeName) + { + if (Element == null) { + Element = parent.OwnerDocument.CreateElement (nodeName); + parent.AppendChild (Element); + } + } + + internal string ReadXmlDescription () + { + XmlElement de = Element ["Description"]; + if (de != null) + return de.InnerText; + else + return null; + } + + internal void SaveXmlDescription (string desc) + { + XmlElement de = Element ["Description"]; + if (desc != null && desc.Length > 0) { + if (de == null) { + de = Element.OwnerDocument.CreateElement ("Description"); + Element.AppendChild (de); + } + de.InnerText = desc; + } else { + if (de != null) + Element.RemoveChild (de); + } + } + + internal virtual void Verify (string location, StringCollection errors) + { + } + + internal void VerifyNotEmpty (string location, StringCollection errors, string attr, string val) + { + if (val == null || val.Length == 0) + errors.Add (location + ": attribute '" + attr + "' can't be empty."); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Description/ObjectDescriptionCollection.cs b/mono-addins/Mono.Addins/Mono.Addins.Description/ObjectDescriptionCollection.cs new file mode 100644 index 00000000..0c92b650 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Description/ObjectDescriptionCollection.cs @@ -0,0 +1,187 @@ +// +// ObjectDescriptionCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Xml; +using System.Linq; +using System.Collections; +using System.Collections.Specialized; +using System.Collections.Generic; + +namespace Mono.Addins.Description +{ + /// + /// Base class for add-in description collections. + /// + public class ObjectDescriptionCollection: CollectionBase + { + object owner; + + internal ObjectDescriptionCollection (object owner) + { + this.owner = owner; + } + + /// + /// Initializes a new instance of the class. + /// + public ObjectDescriptionCollection () + { + } + + /// + /// Add an object. + /// + /// + /// The object. + /// + public void Add (ObjectDescription ep) + { + List.Add (ep); + } + + /// + /// Adds a collection of objects. + /// + /// + /// The objects to add. + /// + public void AddRange (ObjectDescriptionCollection collection) + { + foreach (ObjectDescription ob in collection) + Add (ob); + } + + /// + /// Insert an object. + /// + /// + /// Insertion index. + /// + /// + /// The object. + /// + public void Insert (int index, ObjectDescription ep) + { + List.Insert (index, ep); + } + + /// + /// Removes an object. + /// + /// + /// Object to remove. + /// + public void Remove (ObjectDescription ep) + { + List.Remove (ep); + } + + /// + /// Checks if an object is present in the collection. + /// + /// + /// Object to check. + /// + public bool Contains (ObjectDescription ob) + { + return List.Contains (ob); + } + +#pragma warning disable 1591 + protected override void OnRemove (int index, object value) + { + ObjectDescription ep = (ObjectDescription) value; + if (ep.Element != null) { + ep.Element.ParentNode.RemoveChild (ep.Element); + ep.Element = null; + } + if (owner != null) + ep.SetParent (null); + } + + protected override void OnInsertComplete (int index, object value) + { + if (owner != null) + ((ObjectDescription)value).SetParent (owner); + } + + protected override void OnSetComplete (int index, object oldValue, object newValue) + { + if (owner != null) { + ((ObjectDescription)newValue).SetParent (owner); + ((ObjectDescription)oldValue).SetParent (null); + } + } + + protected override void OnClear () + { + if (owner != null) { + foreach (ObjectDescription ob in List) + ob.SetParent (null); + } + } +#pragma warning restore 1591 + + + internal void SaveXml (XmlElement parent) + { + foreach (ObjectDescription ob in this) + ob.SaveXml (parent); + } + + internal void Verify (string location, StringCollection errors) + { + int n=0; + foreach (ObjectDescription ob in this) { + ob.Verify (location + "[" + n + "]/", errors); + n++; + } + } + } + + /// + /// Base class for add-in description collections. + /// + public class ObjectDescriptionCollection: ObjectDescriptionCollection, IEnumerable where T:ObjectDescription + { + internal ObjectDescriptionCollection () + { + } + + internal ObjectDescriptionCollection (object owner): base (owner) + { + } + + IEnumerator IEnumerable.GetEnumerator () + { + return Enumerable.Cast (InnerList).GetEnumerator (); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Localization/GettextDomain.cs b/mono-addins/Mono.Addins/Mono.Addins.Localization/GettextDomain.cs new file mode 100644 index 00000000..e31b16ea --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Localization/GettextDomain.cs @@ -0,0 +1,140 @@ +// +// GettextDomain.cs: Wrappers for the libintl library. +// +// Authors: +// Edd Dumbill (edd@usefulinc.com) +// Jonathan Pryor (jonpryor@vt.edu) +// Lluis Sanchez Gual (lluis@novell.com) +// +// (C) 2004 Edd Dumbill +// (C) 2005-2006 Jonathan Pryor +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Runtime.InteropServices; +using System.IO; +using System.Text; + +namespace Mono.Addins.Localization +{ + class GettextDomain + { + [DllImport("intl", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr bindtextdomain (IntPtr domainname, IntPtr dirname); + [DllImport("intl", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr bind_textdomain_codeset (IntPtr domainname, IntPtr codeset); + [DllImport("intl", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr dgettext (IntPtr domainname, IntPtr instring); + [DllImport("intl", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr dngettext (IntPtr domainname, IntPtr instring, IntPtr plural, int n); + + IntPtr ipackage; + + public void Init (String package, string localedir) + { + if (localedir == null) { + localedir = System.Reflection.Assembly.GetEntryAssembly ().CodeBase; + FileInfo f = new FileInfo (localedir); + string prefix = f.Directory.Parent.Parent.Parent.ToString (); + prefix = Path.Combine (Path.Combine (prefix, "share"), "locale"); + } + + ipackage = StringToPtr (package); + IntPtr ilocaledir = StringToPtr (localedir); + IntPtr iutf8 = StringToPtr ("UTF-8"); + + try { + if (bindtextdomain (ipackage, ilocaledir) == IntPtr.Zero) + throw new InvalidOperationException ("Gettext localizer: bindtextdomain failed"); + if (bind_textdomain_codeset (ipackage, iutf8) == IntPtr.Zero) + throw new InvalidOperationException ("Gettext localizer: bind_textdomain_codeset failed"); + } + finally { + Marshal.FreeHGlobal (ilocaledir); + Marshal.FreeHGlobal (iutf8); + } + } + + ~GettextDomain () + { + Marshal.FreeHGlobal (ipackage); + } + + public String GetString (String s) + { + IntPtr ints = StringToPtr (s); + try { + // gettext(3) returns the input pointer if no translation is found + IntPtr r = dgettext (ipackage, ints); + if (r != ints) + return PtrToString (r); + return s; + } + finally { + Marshal.FreeHGlobal (ints); + } + } + + public String GetPluralString (String singular, String defaultPlural, int n) + { + IntPtr ints = StringToPtr (singular); + IntPtr intp = StringToPtr (defaultPlural); + try { + // gettext(3) returns the input pointer if no translation is found + IntPtr r = dngettext (ipackage, ints, intp, n); + if (r == ints) + return singular; + if (r == intp) + return defaultPlural; + return PtrToString (r); + } + finally { + Marshal.FreeHGlobal (ints); + Marshal.FreeHGlobal (intp); + } + } + + static IntPtr StringToPtr (string s) + { + if (s == null) + return IntPtr.Zero; + byte[] marshal = Encoding.UTF8.GetBytes (s); + IntPtr mem = Marshal.AllocHGlobal (marshal.Length + 1); + Marshal.Copy (marshal, 0, mem, marshal.Length); + Marshal.WriteByte (mem, marshal.Length, 0); + return mem; + } + + static string PtrToString (IntPtr ptr) + { + if (ptr == IntPtr.Zero) + return null; + int sz = 0; + while (Marshal.ReadByte (ptr, sz) != 0) + sz++; + byte[] bytes = new byte [sz]; + Marshal.Copy (ptr, bytes, 0, sz); + return Encoding.UTF8.GetString (bytes); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Localization/GettextLocalizer.cs b/mono-addins/Mono.Addins/Mono.Addins.Localization/GettextLocalizer.cs new file mode 100644 index 00000000..56a27749 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Localization/GettextLocalizer.cs @@ -0,0 +1,62 @@ +// GettextLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using Mono.Addins; + +namespace Mono.Addins.Localization +{ + class GettextLocalizer: IAddinLocalizerFactory, IAddinLocalizer, IPluralAddinLocalizer + { + GettextDomain domain; + + public IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element) + { + string pkg = element.GetAttribute ("catalog"); + if (pkg.Length == 0) + pkg = addin.Id; + string dir = element.GetAttribute ("location"); + if (dir.Length == 0) + dir = "locale"; + dir = addin.GetFilePath (dir); + domain = new GettextDomain (); + domain.Init (pkg, dir); + return this; + } + + public string GetString (string msgid) + { + return domain.GetString (msgid); + } + + public string GetPluralString (string singular, string defaultPlural, int n) + { + return domain.GetPluralString (singular, defaultPlural, n); + } + + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Localization/IAddinLocalizer.cs b/mono-addins/Mono.Addins/Mono.Addins.Localization/IAddinLocalizer.cs new file mode 100644 index 00000000..09a39c25 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Localization/IAddinLocalizer.cs @@ -0,0 +1,53 @@ +// +// IAddinLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Addins.Localization +{ + /// + /// An add-in localizer. + /// + /// + /// Add-in localizers which want to provide support for localization of plural forms + /// can additionally implement . + /// + public interface IAddinLocalizer + { + /// + /// Gets a localized message. + /// + /// + /// The localized message. + /// + /// + /// The message identifier. + /// + string GetString (string msgid); + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Localization/IAddinLocalizerFactory.cs b/mono-addins/Mono.Addins/Mono.Addins.Localization/IAddinLocalizerFactory.cs new file mode 100644 index 00000000..b5c54c58 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Localization/IAddinLocalizerFactory.cs @@ -0,0 +1,52 @@ +// +// IAddinLocalizerFactory.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Addins.Localization +{ + /// + /// A localizer factory. + /// + public interface IAddinLocalizerFactory + { + /// + /// Creates a localizer for an add-in. + /// + /// + /// The localizer. + /// + /// + /// The add-in for which to create the localizer. + /// + /// + /// Localizer parameters. + /// + IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element); + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Localization/IPluralAddinLocalizer.cs b/mono-addins/Mono.Addins/Mono.Addins.Localization/IPluralAddinLocalizer.cs new file mode 100644 index 00000000..828b135a --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Localization/IPluralAddinLocalizer.cs @@ -0,0 +1,58 @@ +// IPluralAddinLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; + +namespace Mono.Addins.Localization +{ + /// + /// A localizer that supports localization of plural forms. + /// + /// + /// This interface can be implemented by add-in localizers which want to provide + /// support plural forms. + /// + public interface IPluralAddinLocalizer + { + /// + /// Gets a localized message which may contain plural forms. + /// + /// + /// The localized message. + /// + /// + /// Message identifier to use when the specified count is 1. + /// + /// + /// Default message identifier to use when the specified count is not 1. + /// + /// + /// The count that determines which plural form to use. + /// + string GetPluralString (string singular, String defaultPlural, int n); + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Localization/NullLocalizer.cs b/mono-addins/Mono.Addins/Mono.Addins.Localization/NullLocalizer.cs new file mode 100644 index 00000000..d412c8e7 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Localization/NullLocalizer.cs @@ -0,0 +1,41 @@ +// NullLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; + +namespace Mono.Addins.Localization +{ + class NullLocalizer: IAddinLocalizer + { + public static AddinLocalizer Instance = new AddinLocalizer (new NullLocalizer ()); + + public string GetString (string msgid) + { + return msgid; + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Localization/StringResourceLocalizer.cs b/mono-addins/Mono.Addins/Mono.Addins.Localization/StringResourceLocalizer.cs new file mode 100644 index 00000000..9fe05448 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Localization/StringResourceLocalizer.cs @@ -0,0 +1,52 @@ +// StringResourceLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using Mono.Addins; + +namespace Mono.Addins.Localization +{ + class StringResourceLocalizer: IAddinLocalizerFactory, IAddinLocalizer + { + RuntimeAddin addin; + + public IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element) + { + this.addin = addin; + return this; + } + + public string GetString (string msgid) + { + string s = addin.GetResourceString (msgid, false, System.Threading.Thread.CurrentThread.CurrentCulture); + if (s == null) + return msgid; + else + return s; + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins.Localization/StringTableLocalizer.cs b/mono-addins/Mono.Addins/Mono.Addins.Localization/StringTableLocalizer.cs new file mode 100644 index 00000000..613ae515 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Localization/StringTableLocalizer.cs @@ -0,0 +1,95 @@ +// StringTableLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using System.Collections; +using Mono.Addins; + +namespace Mono.Addins.Localization +{ + class StringTableLocalizer: IAddinLocalizerFactory, IAddinLocalizer + { + Hashtable locales = new Hashtable (); + static Hashtable nullLocale = new Hashtable (); + + public IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element) + { + foreach (NodeElement nloc in element.ChildNodes) { + if (nloc.NodeName != "Locale") + throw new InvalidOperationException ("Invalid element found: '" + nloc.NodeName + "'. Expected: 'Locale'"); + string ln = nloc.GetAttribute ("id"); + if (ln.Length == 0) + throw new InvalidOperationException ("Locale id not specified"); + ln = ln.Replace ('_','-'); + Hashtable messages = new Hashtable (); + foreach (NodeElement nmsg in nloc.ChildNodes) { + if (nmsg.NodeName != "Msg") + throw new InvalidOperationException ("Locale '" + ln + "': Invalid element found: '" + nmsg.NodeName + "'. Expected: 'Msg'"); + string id = nmsg.GetAttribute ("id"); + if (id.Length == 0) + throw new InvalidOperationException ("Locale '" + ln + "': Message id not specified"); + messages [id] = nmsg.GetAttribute ("str"); + } + locales [ln] = messages; + } + return this; + } + + public string GetString (string id) + { + string cname = System.Threading.Thread.CurrentThread.CurrentCulture.Name; + Hashtable loc = (Hashtable) locales [cname]; + if (loc == null) { + string sn = cname.Substring (0, 2); + loc = (Hashtable) locales [sn]; + if (loc != null) + locales [cname] = loc; + else { + locales [cname] = nullLocale; + return id; + } + } + string msg = (string) loc [id]; + if (msg == null) { + if (cname.Length > 2) { + // Try again without the country + cname = cname.Substring (0, 2); + loc = (Hashtable) locales [cname]; + if (loc != null) { + msg = (string) loc [id]; + if (msg != null) + return msg; + } + } + return id; + } + else + return msg; + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins.Serialization/BinaryXmlReader.cs b/mono-addins/Mono.Addins/Mono.Addins.Serialization/BinaryXmlReader.cs new file mode 100644 index 00000000..7170c997 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins.Serialization/BinaryXmlReader.cs @@ -0,0 +1,553 @@ +// +// BinaryXmlReader.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Text; +using System.IO; + +namespace Mono.Addins.Serialization +{ + internal class BinaryXmlReader + { + BinaryReader reader; + + internal const byte TagEndOfFile = 0; + internal const byte TagBeginElement = 1; + internal const byte TagEndElement = 2; + internal const byte TagValue = 4; + + internal const byte TagObject = 5; + internal const byte TagObjectArray = 6; + internal const byte TagObjectDictionary = 7; + internal const byte TagObjectNull = 8; + + byte currentType; + string currentName; + ArrayList stringTable = new ArrayList (); + BinaryXmlTypeMap typeMap; + object contextData; + bool ignoreDesc; + + public BinaryXmlReader (Stream stream, BinaryXmlTypeMap typeMap) + { + reader = new BinaryReader (stream); + this.typeMap = typeMap; + ReadNext (); + } + + public BinaryXmlTypeMap TypeMap { + get { return typeMap; } + set { typeMap = value; } + } + + public object ContextData { + get { return contextData; } + set { contextData = value; } + } + + // Returns 'true' if description data must be ignored when reading the contents of a file + public bool IgnoreDescriptionData { + get { return ignoreDesc; } + set { ignoreDesc = value; } + } + + void ReadNext () + { + int b = reader.BaseStream.ReadByte (); + if (b == -1) { + currentType = TagEndOfFile; + return; + } + currentType = (byte) b; + if (currentType == TagBeginElement || currentType == TagValue) + currentName = ReadString (); + } + + string ReadString () + { + // The first integer means: + // >=0: string of the specified length + // -1: null string + // <-1: a string from the string table + + int len = reader.ReadInt32 (); + if (len == -1) + return null; + if (len < -1) + return (string) stringTable [-(len + 2)]; + + byte[] bytes = new byte [len]; + int n = 0; + while (n < len) { + int read = reader.Read (bytes, n, len - n); + if (read == 0) + throw new InvalidOperationException ("Length too high for string: " + len); + n += read; + } + string s = Encoding.UTF8.GetString (bytes); + stringTable.Add (s); + return s; + } + + public string LocalName { + get { return currentName; } + } + + public bool IsElement { + get { return currentType == TagBeginElement; } + } + + public bool IsValue { + get { return currentType == TagValue; } + } + + TypeCode ReadValueType (TypeCode type) + { + if (currentType != TagValue) + throw new InvalidOperationException ("Reader not positioned on a value."); + TypeCode t = (TypeCode) reader.ReadByte (); + if (t != type && type != TypeCode.Empty) + throw new InvalidOperationException ("Invalid value type. Expected " + type + ", found " + t); + return t; + } + + public string ReadStringValue (string name) + { + if (!SkipToValue (name)) + return null; + return ReadStringValue (); + } + + public string ReadStringValue () + { + if (currentType != TagValue) + throw new InvalidOperationException ("Reader not positioned on a value."); + + TypeCode t = (TypeCode) reader.ReadByte (); + if (t == TypeCode.Empty) { + ReadNext (); + return null; + } + if (t != TypeCode.String) + throw new InvalidOperationException ("Invalid value type. Expected String, found " + t); + + string s = ReadString (); + ReadNext (); + return s; + } + + public bool ReadBooleanValue (string name) + { + if (!SkipToValue (name)) + return false; + return ReadBooleanValue (); + } + + public bool ReadBooleanValue () + { + ReadValueType (TypeCode.Boolean); + bool value = reader.ReadBoolean (); + ReadNext (); + return value; + } + + public char ReadCharValue (string name) + { + if (!SkipToValue (name)) + return (char)0; + return ReadCharValue (); + } + + public char ReadCharValue () + { + ReadValueType (TypeCode.Char); + char value = reader.ReadChar (); + ReadNext (); + return value; + } + + public byte ReadByteValue (string name) + { + if (!SkipToValue (name)) + return (byte)0; + return ReadByteValue (); + } + + public byte ReadByteValue () + { + ReadValueType (TypeCode.Byte); + byte value = reader.ReadByte (); + ReadNext (); + return value; + } + + public short ReadInt16Value (string name) + { + if (!SkipToValue (name)) + return (short)0; + return ReadInt16Value (); + } + + public short ReadInt16Value () + { + ReadValueType (TypeCode.Int16); + short value = reader.ReadInt16 (); + ReadNext (); + return value; + } + + public int ReadInt32Value (string name) + { + if (!SkipToValue (name)) + return 0; + return ReadInt32Value (); + } + + public int ReadInt32Value () + { + ReadValueType (TypeCode.Int32); + int value = reader.ReadInt32 (); + ReadNext (); + return value; + } + + public long ReadInt64Value (string name) + { + if (!SkipToValue (name)) + return (long)0; + return ReadInt64Value (); + } + + public long ReadInt64Value () + { + ReadValueType (TypeCode.Int64); + long value = reader.ReadInt64 (); + ReadNext (); + return value; + } + + public DateTime ReadDateTimeValue (string name) + { + if (!SkipToValue (name)) + return DateTime.MinValue; + return ReadDateTimeValue (); + } + + public DateTime ReadDateTimeValue () + { + ReadValueType (TypeCode.DateTime); + DateTime value = new DateTime (reader.ReadInt64 ()); + ReadNext (); + return value; + } + + public object ReadValue (string name) + { + if (!SkipToValue (name)) + return null; + return ReadValue (); + } + + public object ReadValue () + { + object res = ReadValueInternal (); + ReadNext (); + return res; + } + + public object ReadValue (string name, object targetInstance) + { + if (!SkipToValue (name)) + return null; + return ReadValue (targetInstance); + } + + public object ReadValue (object targetInstance) + { + TypeCode t = (TypeCode) reader.ReadByte (); + if (t == TypeCode.Empty) { + ReadNext (); + return null; + } + if (t != TypeCode.Object) + throw new InvalidOperationException ("Invalid value type. Expected Object, found " + t); + + object res = ReadObject (targetInstance); + ReadNext (); + return res; + } + + object ReadValueInternal () + { + TypeCode t = (TypeCode) reader.ReadByte (); + if (t == TypeCode.Empty) + return null; + return ReadValueInternal (t); + } + + object ReadValueInternal (TypeCode t) + { + object res; + switch (t) { + case TypeCode.Boolean: res = reader.ReadBoolean (); break; + case TypeCode.Char: res = reader.ReadChar (); break; + case TypeCode.SByte: res = reader.ReadSByte (); break; + case TypeCode.Byte: res = reader.ReadByte (); break; + case TypeCode.Int16: res = reader.ReadInt16 (); break; + case TypeCode.UInt16: res = reader.ReadUInt16 (); break; + case TypeCode.Int32: res = reader.ReadInt32 (); break; + case TypeCode.UInt32: res = reader.ReadUInt32 (); break; + case TypeCode.Int64: res = reader.ReadInt64 (); break; + case TypeCode.UInt64: res = reader.ReadUInt64 (); break; + case TypeCode.Single: res = reader.ReadSingle (); break; + case TypeCode.Double: res = reader.ReadDouble (); break; + case TypeCode.DateTime: res = new DateTime (reader.ReadInt64 ()); break; + case TypeCode.String: res = ReadString (); break; + case TypeCode.Object: res = ReadObject (null); break; + case TypeCode.Empty: res = null; break; + default: + throw new InvalidOperationException ("Unexpected value type: " + t); + } + return res; + } + + bool SkipToValue (string name) + { + do { + if ((currentType == TagBeginElement || currentType == TagValue) && currentName == name) + return true; + if (EndOfElement) + return false; + Skip (); + } while (true); + } + + public void ReadBeginElement () + { + if (currentType != TagBeginElement) + throw new InvalidOperationException ("Reader not positioned on an element."); + ReadNext (); + } + + public void ReadEndElement () + { + if (currentType != TagEndElement) + throw new InvalidOperationException ("Reader not positioned on an element."); + ReadNext (); + } + + public bool EndOfElement { + get { return currentType == TagEndElement; } + } + + public void Skip () + { + if (currentType == TagValue) + ReadValue (); + else if (currentType == TagEndElement) + ReadNext (); + else if (currentType == TagBeginElement) { + ReadNext (); + while (!EndOfElement) + Skip (); + ReadNext (); + } + } + + object ReadObject (object targetInstance) + { + byte ot = reader.ReadByte (); + if (ot == TagObjectNull) { + return null; + } + else if (ot == TagObject) { + string tname = ReadString (); + IBinaryXmlElement ob; + if (targetInstance != null) { + ob = targetInstance as IBinaryXmlElement; + if (ob == null) + throw new InvalidOperationException ("Target instance has an invalid type. Expected an IBinaryXmlElement implementation."); + } else { + ob = typeMap.CreateObject (tname); + } + ReadNext (); + ob.Read (this); + while (currentType != TagEndElement) + Skip (); + return ob; + } + else if (ot == TagObjectArray) { + TypeCode tc = (TypeCode) reader.ReadByte (); + int len = reader.ReadInt32 (); + if (targetInstance != null) { + IList list = targetInstance as IList; + if (list == null) + throw new InvalidOperationException ("Target instance has an invalid type. Expected an IList implementation."); + for (int n=0; n"); + DumpElement (ind + IndSize); + Console.WriteLine (new string (' ', ind) + ""); + } + } + + public void DumpElement (int ind) + { + ReadNext (); + while (currentType != TagEndElement) { + Dump (ind + IndSize); + ReadNext (); + } + } + + void DumpValue (int ind) + { + TypeCode t = (TypeCode) reader.ReadByte (); + if (t != TypeCode.Object) { + object ob = ReadValueInternal (t); + if (ob == null) ob = "(null)"; + Console.Write (ob); + } else { + byte ot = reader.ReadByte (); + switch (ot) { + case TagObjectNull: { + Console.Write ("(null)"); + break; + } + case TagObject: { + string tname = ReadString (); + Console.WriteLine ("(" + tname + ")"); + DumpElement (ind + IndSize); + break; + } + case TagObjectArray: { + TypeCode tc = (TypeCode) reader.ReadByte (); + int len = reader.ReadInt32 (); + Console.WriteLine ("(" + tc + "[" + len + "])"); + for (int n=0; n + + + + Debug + AnyCPU + {91DD5A2D-9FE3-4C3C-9253-876141874DAD} + Library + Mono.Addins + Mono.Addins + True + ..\mono-addins.snk + v4.6 + Mono.Addins + Lluis Sanchez + https://github.com/mono/mono-addins/blob/master/COPYING + https://github.com/mono/mono-addins + Mono.Addins is a framework for creating extensible applications, and for creating add-ins which extend applications. + 8.0.30703 + 2.0 + + + + True + full + false + ..\bin + prompt + 4 + True + False + 1574 + ..\bin\Mono.Addins.xml + + + pdbonly + True + ..\bin + prompt + 4 + True + False + true + 1574 + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins/Mono.Addins/Addin.cs b/mono-addins/Mono.Addins/Mono.Addins/Addin.cs new file mode 100644 index 00000000..297a8908 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/Addin.cs @@ -0,0 +1,406 @@ +// +// Addin.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2005 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Specialized; +using Mono.Addins.Description; +using Mono.Addins.Database; +using System.Linq; + +namespace Mono.Addins +{ + /// + /// An add-in. + /// + public class Addin + { + AddinInfo addin; + string sourceFile; + WeakReference desc; + AddinDatabase database; + bool? isLatestVersion; + bool? isUserAddin; + string id; + string domain; + + internal Addin (AddinDatabase database, string domain, string id) + { + this.database = database; + this.id = id; + this.domain = domain; + LoadAddinInfo (); + } + + /// + /// Full identifier of the add-in, including namespace and version. + /// + public string Id { + get { return id; } + } + + /// + /// Namespace of the add-in. + /// + public string Namespace { + get { return this.AddinInfo.Namespace; } + } + + /// + /// Identifier of the add-in (without namespace) + /// + public string LocalId { + get { return this.AddinInfo.LocalId; } + } + + /// + /// Version of the add-in + /// + public string Version { + get { return this.AddinInfo.Version; } + } + + /// + /// Display name of the add-in + /// + public string Name { + get { return this.AddinInfo.Name; } + } + + /// + /// Custom properties specified in the add-in header + /// + public AddinPropertyCollection Properties { + get { return this.AddinInfo.Properties; } + } + + internal string PrivateDataPath { + get { return Path.Combine (database.AddinPrivateDataPath, Path.GetFileNameWithoutExtension (Description.FileName)); } + } + + /// + /// Checks version compatibility. + /// + /// + /// An add-in version. + /// + /// + /// True if the provided version is compatible with this add-in. + /// + /// + /// This method checks the CompatVersion property to know if the provided version is compatible with the version of this add-in. + /// + public bool SupportsVersion (string version) + { + return AddinInfo.SupportsVersion (version); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString () + { + return Id; + } + + internal AddinInfo AddinInfo { + get { + if (addin == null) { + try { + addin = AddinInfo.ReadFromDescription (Description); + } catch (Exception ex) { + throw new InvalidOperationException ("Could not read add-in file: " + database.GetDescriptionPath (domain, id), ex); + } + } + return addin; + } + } + + /// + /// Gets or sets the enabled status of the add-in. + /// + /// + /// This property can be used to enable or disable an add-in. + /// The enabled status of an add-in is stored in the add-in registry, + /// so when an add-in is disabled, it will be disabled for all applications + /// sharing the same registry. + /// When an add-in is enabled or disabled, the extension points currently loaded + /// in memory will be properly updated to include or exclude extensions from the add-in. + /// + public bool Enabled { + get { + if (!IsLatestVersion) + return false; + return AddinInfo.IsRoot ? true : database.IsAddinEnabled (Description.Domain, AddinInfo.Id, true); + } + set { + if (value) + database.EnableAddin (Description.Domain, AddinInfo.Id, true); + else + database.DisableAddin (Description.Domain, AddinInfo.Id); + } + } + + internal bool IsLatestVersion { + get { + if (isLatestVersion == null) { + string id, version; + Addin.GetIdParts (AddinInfo.Id, out id, out version); + var addins = database.GetInstalledAddins (null, AddinSearchFlagsInternal.IncludeAll | AddinSearchFlagsInternal.LatestVersionsOnly); + isLatestVersion = addins.Any (a => Addin.GetIdName (a.Id) == id && a.Version == version); + } + return isLatestVersion.Value; + } + set { + isLatestVersion = value; + } + } + + /// + /// Returns 'true' if the add-in is installed in the user's personal folder + /// + public bool IsUserAddin { + get { + if (isUserAddin == null) + SetIsUserAddin (Description); + return isUserAddin.Value; + } + } + + void SetIsUserAddin (AddinDescription adesc) + { + string installPath = database.Registry.DefaultAddinsFolder; + if (installPath [installPath.Length - 1] != Path.DirectorySeparatorChar) + installPath += Path.DirectorySeparatorChar; + isUserAddin = adesc != null && Path.GetFullPath (adesc.AddinFile).StartsWith (installPath); + } + + /// + /// Path to the add-in file (it can be an assembly or a standalone XML manifest) + /// + public string AddinFile { + get { + if (sourceFile == null && addin == null) + LoadAddinInfo (); + return sourceFile; + } + } + + void LoadAddinInfo () + { + if (addin == null) { + try { + AddinDescription m = Description; + sourceFile = m.AddinFile; + addin = AddinInfo.ReadFromDescription (m); + } catch (Exception ex) { + throw new InvalidOperationException ("Could not read add-in file: " + database.GetDescriptionPath (domain, id), ex); + } + } + } + + /// + /// Description of the add-in + /// + public AddinDescription Description { + get { + if (desc != null) { + AddinDescription d = desc.Target as AddinDescription; + if (d != null) + return d; + } + + var configFile = database.GetDescriptionPath (domain, id); + AddinDescription m; + database.ReadAddinDescription (new ConsoleProgressStatus (true), configFile, out m); + + if (m == null) { + try { + if (File.Exists (configFile)) { + // The file is corrupted. Remove it. + File.Delete (configFile); + } + } catch { + // Ignore + } + throw new InvalidOperationException ("Could not read add-in description"); + } + if (addin == null) { + addin = AddinInfo.ReadFromDescription (m); + sourceFile = m.AddinFile; + } + SetIsUserAddin (m); + if (!isUserAddin.Value) + m.Flags |= AddinFlags.CantUninstall; + desc = new WeakReference (m); + return m; + } + } + + internal void ResetCachedData () + { + // The domain may have changed + if (sourceFile != null) + domain = database.GetFolderDomain (null, Path.GetDirectoryName (sourceFile)); + desc = null; + addin = null; + } + + /// + /// Compares two add-in versions + /// + /// + /// -1 if v1 is greater than v2, 0 if v1 == v2, 1 if v1 less than v2 + /// + /// + /// A version + /// + /// + /// A version + /// + public static int CompareVersions (string v1, string v2) + { + string[] a1 = v1.Split ('.'); + string[] a2 = v2.Split ('.'); + + for (int n=0; n= a2.Length) + return -1; + if (a1[n].Length == 0) { + if (a2[n].Length != 0) + return 1; + continue; + } + try { + int n1 = int.Parse (a1[n]); + int n2 = int.Parse (a2[n]); + if (n1 < n2) + return 1; + else if (n1 > n2) + return -1; + } catch { + return 1; + } + } + if (a2.Length > a1.Length) + return 1; + return 0; + } + + /// + /// Returns the identifier of an add-in + /// + /// + /// The full identifier. + /// + /// + /// Namespace of the add-in + /// + /// + /// Name of the add-in + /// + /// + /// Version of the add-in + /// + public static string GetFullId (string ns, string id, string version) + { + string res; + if (id.StartsWith ("::")) + res = id.Substring (2); + else if (ns != null && ns.Length > 0) + res = ns + "." + id; + else + res = id; + + if (version != null && version.Length > 0) + return res + "," + version; + else + return res; + } + + /// + /// Given a full add-in identifier, returns the namespace and name of the add-in (it removes the version number) + /// + /// + /// Add-in identifier. + /// + public static string GetIdName (string addinId) + { + int i = addinId.IndexOf (','); + if (i != -1) + return addinId.Substring (0, i); + else + return addinId; + } + + /// + /// Given a full add-in identifier, returns the version the add-in + /// + /// + /// The version. + /// + public static string GetIdVersion (string addinId) + { + int i = addinId.IndexOf (','); + if (i != -1) + return addinId.Substring (i + 1).Trim (); + else + return string.Empty; + } + + /// + /// Splits a full add-in identifier in name and version + /// + /// + /// Add-in identifier. + /// + /// + /// The resulting name + /// + /// + /// The resulting version + /// + public static void GetIdParts (string addinId, out string name, out string version) + { + int i = addinId.IndexOf (','); + if (i != -1) { + name = addinId.Substring (0, i); + version = addinId.Substring (i+1).Trim (); + } else { + name = addinId; + version = string.Empty; + } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinAttribute.cs new file mode 100644 index 00000000..6f9f7386 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinAttribute.cs @@ -0,0 +1,148 @@ +// +// AddinAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using Mono.Addins.Description; + +namespace Mono.Addins +{ + /// + /// Marks an assembly as being an add-in. + /// + [AttributeUsage (AttributeTargets.Assembly)] + public class AddinAttribute: Attribute + { + string id; + string version; + string ns; + string category; + bool enabledByDefault = true; + AddinFlags flags; + string compatVersion; + string url; + + /// + /// Initializes an add-in marker attribute + /// + public AddinAttribute () + { + } + + /// + /// Initializes an add-in marker attribute + /// + /// + /// Identifier of the add-in + /// + public AddinAttribute (string id) + { + this.id = id; + } + + /// + /// Initializes an add-in marker attribute + /// + /// + /// Identifier of the add-in + /// + /// + /// Version of the add-in + /// + public AddinAttribute (string id, string version) + { + this.id = id; + this.version = version; + } + + /// + /// Identifier of the add-in. + /// + public string Id { + get { return id != null ? id : string.Empty; } + set { id = value; } + } + + /// + /// Version of the add-in. + /// + public string Version { + get { return version != null ? version : string.Empty; } + set { version = value; } + } + + /// + /// Version of the add-in with which this add-in is backwards compatible. + /// + public string CompatVersion { + get { return compatVersion != null ? compatVersion : string.Empty; } + set { compatVersion = value; } + } + + /// + /// Namespace of the add-in + /// + public string Namespace { + get { return ns != null ? ns : string.Empty; } + set { ns = value; } + } + + /// + /// Category of the add-in + /// + public string Category { + get { return category != null ? category : string.Empty; } + set { category = value; } + } + + /// + /// Url to a web page with more information about the add-in + /// + public string Url { + get { return url != null ? url : string.Empty; } + set { url = value; } + } + + /// + /// When set to True, the add-in will be automatically enabled after installing. + /// It's True by default. + /// + public bool EnabledByDefault { + get { return this.enabledByDefault; } + set { this.enabledByDefault = value; } + } + + /// + /// Add-in flags + /// + public AddinFlags Flags { + get { return this.flags; } + set { this.flags = value; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinAuthorAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinAuthorAttribute.cs new file mode 100644 index 00000000..1ef03217 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinAuthorAttribute.cs @@ -0,0 +1,59 @@ +// +// AddinAuthorAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Declares an author of the add-in + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=true)] + public class AddinAuthorAttribute: Attribute + { + string name; + + /// + /// Initializes the attribute + /// + /// + /// Name of the author + /// + public AddinAuthorAttribute (string name) + { + this.name = name; + } + + /// + /// Author name + /// + public string Name { + get { return this.name; } + set { this.name = value; } + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinCategoryAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinCategoryAttribute.cs new file mode 100644 index 00000000..aeeaab3f --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinCategoryAttribute.cs @@ -0,0 +1,53 @@ +// +// AddinCategoryAttribute.cs +// +// Author: +// Lluis Sanchez +// +// Copyright (c) 2013 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace Mono.Addins +{ + /// + /// Addin category attribute. + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=false)] + public class AddinCategoryAttribute: Attribute + { + /// + /// Initializes the attribute + /// + /// + /// The category to which the add-in belongs + /// + public AddinCategoryAttribute (string category) + { + this.Category = category; + } + + /// + /// The category to which the add-in belongs + /// + public string Category { get; set; } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinDependencyAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinDependencyAttribute.cs new file mode 100644 index 00000000..3009a7d9 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinDependencyAttribute.cs @@ -0,0 +1,73 @@ +// +// AddinDependencyAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Declares a dependency on an add-in or add-in host + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=true)] + public class AddinDependencyAttribute: Attribute + { + string id; + string version; + + /// + /// Initializes the attribute + /// + /// + /// Identifier of the add-in + /// + /// + /// Version of the add-in + /// + public AddinDependencyAttribute (string id, string version) + { + this.id = id; + this.version = version; + } + + /// + /// Identifier of the add-in + /// + public string Id { + get { return id; } + } + + /// + /// Version of the add-in + /// + public string Version { + get { return version; } + } + + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinDescriptionAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinDescriptionAttribute.cs new file mode 100644 index 00000000..d79c4d81 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinDescriptionAttribute.cs @@ -0,0 +1,73 @@ +// +// AddinDescriptionAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace Mono.Addins +{ + /// + /// Describes the purpose of an add-in or add-in root + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public class AddinDescriptionAttribute: Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Description of the add-in + /// + public AddinDescriptionAttribute (string description) + { + Description = description; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Description of the add-in + /// + /// + /// Locale of the description (for example, 'en-US', or 'en') + /// + public AddinDescriptionAttribute (string description, string locale) + { + Description = description; + Locale = locale; + } + + /// + /// Description of the add-in + /// + public string Description { get; set; } + + /// + /// Locale of the description (for example, 'en-US', or 'en') + /// + public string Locale { get; set; } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinEngine.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinEngine.cs new file mode 100644 index 00000000..d027aec0 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinEngine.cs @@ -0,0 +1,847 @@ +// +// AddinService.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Linq; +using System.Xml; +using System.Collections; +using System.Reflection; + +using Mono.Addins.Description; +using Mono.Addins.Database; +using Mono.Addins.Localization; +using System.Collections.Generic; + +namespace Mono.Addins +{ + /// + /// An add-in engine. + /// + /// + /// This class allows hosting several independent add-in engines in a single application domain. + /// In general, applications use the AddinManager class to query and manage extensions. Most of the API is + /// static, so easily accessible. However, some kind applications may need to use several isolated + /// add-in engines, and in this case the AddinManager class can't be used, because it is bound to a single + /// add-in engine. Those applications can instead create several instances of the AddinEngine class. Each + /// add-in engine can be independently initialized with different add-in registries and extension models. + /// + public class AddinEngine: ExtensionContext + { + bool initialized; + string startupDirectory; + AddinRegistry registry; + IAddinInstaller installer; + + bool checkAssemblyLoadConflicts; + Dictionary loadedAddins = new Dictionary (); + Dictionary nodeSets = new Dictionary (); + Hashtable autoExtensionTypes = new Hashtable (); + Dictionary loadedAssemblies = new Dictionary (); + AddinLocalizer defaultLocalizer; + IProgressStatus defaultProgressStatus = new ConsoleProgressStatus (false); + + /// + /// Raised when there is an error while loading an add-in + /// + public static event AddinErrorEventHandler AddinLoadError; + + /// + /// Raised when an add-in is loaded + /// + public static event AddinEventHandler AddinLoaded; + + /// + /// Raised when an add-in is unloaded + /// + public static event AddinEventHandler AddinUnloaded; + + /// + /// Initializes a new instance of the class. + /// + public AddinEngine () + { + } + + /// + /// Initializes the add-in engine + /// + /// + /// Location of the add-in registry. + /// + /// The add-in engine needs to be initialized before doing any add-in operation. + /// When initialized with this method, it will look for add-in in the add-in registry + /// located in the specified path. + /// + public void Initialize (string configDir) + { + if (initialized) + return; + + Assembly asm = Assembly.GetEntryAssembly (); + if (asm == null) asm = Assembly.GetCallingAssembly (); + Initialize (asm, configDir, null, null); + } + + /// + /// Initializes the add-in engine. + /// + /// + /// Location of the add-in registry. + /// + /// + /// Add-ins directory. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// The add-in engine needs to be initialized before doing any add-in operation. + /// Configuration information about the add-in registry will be stored in the + /// provided location. The add-in engine will look for add-ins in the provided + /// 'addinsDir' directory. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public void Initialize (string configDir, string addinsDir) + { + if (initialized) + return; + + Assembly asm = Assembly.GetEntryAssembly (); + if (asm == null) asm = Assembly.GetCallingAssembly (); + Initialize (asm, configDir, addinsDir, null); + } + + /// + /// Initializes the add-in engine. + /// + /// + /// Location of the add-in registry. + /// + /// + /// Add-ins directory. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// Location of the add-in database. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// The add-in engine needs to be initialized before doing any add-in operation. + /// Configuration information about the add-in registry will be stored in the + /// provided location. The add-in engine will look for add-ins in the provided + /// 'addinsDir' directory. Cached information about add-ins will be stored in + /// the 'databaseDir' directory. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public void Initialize (string configDir, string addinsDir, string databaseDir) + { + if (initialized) + return; + + Assembly asm = Assembly.GetEntryAssembly (); + if (asm == null) asm = Assembly.GetCallingAssembly (); + Initialize (asm, configDir, addinsDir, databaseDir); + } + + internal void Initialize (Assembly startupAsm, string configDir, string addinsDir, string databaseDir) + { + lock (LocalLock) { + if (initialized) + return; + + Initialize (this); + + string asmFile = new Uri (startupAsm.CodeBase).LocalPath; + startupDirectory = System.IO.Path.GetDirectoryName (asmFile); + + string customDir = Environment.GetEnvironmentVariable ("MONO_ADDINS_REGISTRY"); + if (customDir != null && customDir.Length > 0) + configDir = customDir; + + if (string.IsNullOrEmpty (configDir)) + registry = AddinRegistry.GetGlobalRegistry (this, startupDirectory); + else + registry = new AddinRegistry (this, configDir, startupDirectory, addinsDir, databaseDir); + + if (registry.CreateHostAddinsFile (asmFile) || registry.UnknownDomain) + registry.Update (new ConsoleProgressStatus (false)); + + initialized = true; + + ActivateRoots (); + OnAssemblyLoaded (null, null); + AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler (OnAssemblyLoaded); + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve; + } + } + + Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args) + { + lock (LocalLock) { + // MS.NET is more strict than Mono when loading assemblies. Assemblies loaded in the "Load" context can't see assemblies loaded + // in the "LoadFrom" context, unless assemblies are explicitly resolved in the AssemblyResolve event. + return loadedAddins.Values.Where(a => a.AssembliesLoaded).SelectMany(a => a.Assemblies).FirstOrDefault(a => a.FullName.ToString () == args.Name); + } + } + + /// + /// Finalizes the add-in engine. + /// + public void Shutdown () + { + lock (LocalLock) { + initialized = false; + AppDomain.CurrentDomain.AssemblyLoad -= new AssemblyLoadEventHandler (OnAssemblyLoaded); + AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainAssemblyResolve; + loadedAddins = new Dictionary(); + loadedAssemblies = new Dictionary (); + registry.Dispose (); + registry = null; + startupDirectory = null; + ClearContext (); + } + } + + /// + /// Sets the default localizer to be used for this add-in engine + /// + /// + /// The add-in localizer + /// + public void InitializeDefaultLocalizer (IAddinLocalizer localizer) + { + CheckInitialized (); + lock (LocalLock) { + if (localizer != null) + defaultLocalizer = new AddinLocalizer (localizer); + else + defaultLocalizer = null; + } + } + + internal string StartupDirectory { + get { return startupDirectory; } + } + + /// + /// Gets whether the add-in engine has been initialized. + /// + public bool IsInitialized { + get { return initialized; } + } + + /// + /// Gets the default add-in installer + /// + /// + /// The default installer is used by the CheckInstalled method to request + /// the installation of missing add-ins. + /// + public IAddinInstaller DefaultInstaller { + get { return installer; } + set { installer = value; } + } + + /// + /// Gets the default localizer for this add-in engine + /// + public AddinLocalizer DefaultLocalizer { + get { + CheckInitialized (); + var loc = defaultLocalizer; + return loc ?? NullLocalizer.Instance; + } + } + + internal ExtensionContext DefaultContext { + get { return this; } + } + + /// + /// Gets the localizer for the add-in that is invoking this property + /// + public AddinLocalizer CurrentLocalizer { + get { + CheckInitialized (); + Assembly asm = Assembly.GetCallingAssembly (); + RuntimeAddin addin = GetAddinForAssembly (asm); + if (addin != null) + return addin.Localizer; + else + return DefaultLocalizer; + } + } + + /// + /// Gets a reference to the RuntimeAddin object for the add-in that is invoking this property + /// + public RuntimeAddin CurrentAddin { + get { + CheckInitialized (); + Assembly asm = Assembly.GetCallingAssembly (); + return GetAddinForAssembly (asm); + } + } + + /// + /// Gets the add-in registry bound to this add-in engine + /// + public AddinRegistry Registry { + get { + CheckInitialized (); + return registry; + } + } + + internal RuntimeAddin GetAddinForAssembly (Assembly asm) + { + ValidateAddinRoots (); + RuntimeAddin ad; + loadedAssemblies.TryGetValue (asm, out ad); + return ad; + } + + /// + /// Checks if the provided add-ins are installed, and requests the installation of those + /// which aren't. + /// + /// + /// Message to show to the user when new add-ins have to be installed. + /// + /// + /// List of IDs of the add-ins to be checked. + /// + /// + /// This method checks if the specified add-ins are installed. + /// If some of the add-ins are not installed, it will use + /// the installer assigned to the DefaultAddinInstaller property + /// to install them. If the installation fails, or if DefaultAddinInstaller + /// is not set, an exception will be thrown. + /// + public void CheckInstalled (string message, params string[] addinIds) + { + ArrayList notInstalled = new ArrayList (); + foreach (string id in addinIds) { + Addin addin = Registry.GetAddin (id, false); + if (addin != null) { + // The add-in is already installed + // If the add-in is disabled, enable it now + if (!addin.Enabled) + addin.Enabled = true; + } else { + notInstalled.Add (id); + } + } + if (notInstalled.Count == 0) + return; + + var ins = installer; + + if (ins == null) + throw new InvalidOperationException ("Add-in installer not set"); + + // Install the add-ins + ins.InstallAddins (Registry, message, (string[]) notInstalled.ToArray (typeof(string))); + } + + // Enables or disables conflict checking while loading assemblies. + // Disabling makes loading faster, but less safe. + internal bool CheckAssemblyLoadConflicts { + get { return checkAssemblyLoadConflicts; } + set { checkAssemblyLoadConflicts = value; } + } + + /// + /// Checks if an add-in has been loaded. + /// + /// + /// Full identifier of the add-in. + /// + /// + /// True if the add-in is loaded. + /// + public bool IsAddinLoaded (string id) + { + CheckInitialized (); + ValidateAddinRoots (); + return loadedAddins.ContainsKey (Addin.GetIdName (id)); + } + + internal RuntimeAddin GetAddin (string id) + { + ValidateAddinRoots (); + RuntimeAddin a; + loadedAddins.TryGetValue (Addin.GetIdName (id), out a); + return a; + } + + internal void ActivateAddin (string id) + { + ActivateAddinExtensions (id); + } + + internal void UnloadAddin (string id) + { + RemoveAddinExtensions (id); + + RuntimeAddin addin = GetAddin (id); + if (addin != null) { + addin.UnloadExtensions (); + lock (LocalLock) { + var loadedAddinsCopy = new Dictionary (loadedAddins); + loadedAddinsCopy.Remove (Addin.GetIdName (id)); + loadedAddins = loadedAddinsCopy; + if (addin.AssembliesLoaded) { + var loadedAssembliesCopy = new Dictionary (loadedAssemblies); + foreach (Assembly asm in addin.Assemblies) + loadedAssembliesCopy.Remove (asm); + loadedAssemblies = loadedAssembliesCopy; + } + } + ReportAddinUnload (id); + } + } + + /// + /// Forces the loading of an add-in. + /// + /// + /// Status monitor to keep track of the loading process. + /// + /// + /// Full identifier of the add-in to load. + /// + /// + /// This method loads all assemblies that belong to an add-in in memory. + /// All add-ins on which the specified add-in depends will also be loaded. + /// Notice that in general add-ins don't need to be explicitly loaded using + /// this method, since the add-in engine will load them on demand. + /// + public void LoadAddin (IProgressStatus statusMonitor, string id) + { + CheckInitialized (); + if (LoadAddin (statusMonitor, id, true)) { + var adn = GetAddin (id); + adn.EnsureAssembliesLoaded (); + } + } + + internal bool LoadAddin (IProgressStatus statusMonitor, string id, bool throwExceptions) + { + try { + lock (LocalLock) { + if (IsAddinLoaded (id)) + return true; + + if (!Registry.IsAddinEnabled (id)) { + string msg = GettextCatalog.GetString ("Disabled add-ins can't be loaded."); + ReportError (msg, id, null, false); + if (throwExceptions) + throw new InvalidOperationException (msg); + return false; + } + + ArrayList addins = new ArrayList (); + Stack depCheck = new Stack (); + ResolveLoadDependencies (addins, depCheck, id, false); + addins.Reverse (); + + if (statusMonitor != null) + statusMonitor.SetMessage ("Loading Addins"); + + for (int n=0; n (loadedAddins); + loadedAddinsCopy [Addin.GetIdName (p.Id)] = p; + loadedAddins = loadedAddinsCopy; + + if (!AddinDatabase.RunningSetupProcess) { + // Load the extension points and other addin data + + RegisterNodeSets (iad.Id, description.ExtensionNodeSets); + + foreach (ConditionTypeDescription cond in description.ConditionTypes) { + Type ctype = p.GetType (cond.TypeName, true); + RegisterCondition (cond.Id, ctype); + } + } + + foreach (ExtensionPoint ep in description.ExtensionPoints) + InsertExtensionPoint (p, ep); + + // Fire loaded event + NotifyAddinLoaded (p); + ReportAddinLoad (p.Id); + return true; + } + catch (Exception ex) { + ReportError ("Add-in could not be loaded", iad.Id, ex, false); + if (statusMonitor != null) + statusMonitor.ReportError ("Add-in '" + iad.Id + "' could not be loaded.", ex); + return false; + } + } + + internal void RegisterAssemblies (RuntimeAddin addin) + { + lock (LocalLock) { + var loadedAssembliesCopy = new Dictionary (loadedAssemblies); + foreach (Assembly asm in addin.Assemblies) + loadedAssembliesCopy [asm] = addin; + loadedAssemblies = loadedAssembliesCopy; + } + } + + internal void InsertExtensionPoint (RuntimeAddin addin, ExtensionPoint ep) + { + CreateExtensionPoint (ep); + foreach (ExtensionNodeType nt in ep.NodeSet.NodeTypes) { + if (nt.ObjectTypeName.Length > 0) { + Type ntype = addin.GetType (nt.ObjectTypeName, true); + RegisterAutoTypeExtensionPoint (ntype, ep.Path); + } + } + } + + bool ResolveLoadDependencies (ArrayList addins, Stack depCheck, string id, bool optional) + { + if (IsAddinLoaded (id)) + return true; + + if (depCheck.Contains (id)) + throw new InvalidOperationException ("A cyclic addin dependency has been detected."); + + Addin iad = Registry.GetAddin (id); + if (iad == null || !iad.Enabled) { + if (optional) + return false; + else if (iad != null && !iad.Enabled) + throw new MissingDependencyException (GettextCatalog.GetString ("The required addin '{0}' is disabled.", id)); + else + throw new MissingDependencyException (GettextCatalog.GetString ("The required addin '{0}' is not installed.", id)); + } + + // If this addin has already been requested, bring it to the head + // of the list, so it is loaded earlier than before. + addins.Remove (iad); + addins.Add (iad); + + depCheck.Push (id); + + try { + foreach (Dependency dep in iad.AddinInfo.Dependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep != null) { + try { + string adepid = Addin.GetFullId (iad.AddinInfo.Namespace, adep.AddinId, adep.Version); + ResolveLoadDependencies (addins, depCheck, adepid, false); + } catch (MissingDependencyException) { + if (optional) + return false; + else + throw; + } + } + } + + if (iad.AddinInfo.OptionalDependencies != null) { + foreach (Dependency dep in iad.AddinInfo.OptionalDependencies) { + AddinDependency adep = dep as AddinDependency; + if (adep != null) { + string adepid = Addin.GetFullId (iad.Namespace, adep.AddinId, adep.Version); + if (!ResolveLoadDependencies (addins, depCheck, adepid, true)) + return false; + } + } + } + } finally { + depCheck.Pop (); + } + return true; + } + + void RegisterNodeSets (string addinId, ExtensionNodeSetCollection nsets) + { + lock (LocalLock) { + var nodeSetsCopy = new Dictionary (nodeSets); + foreach (ExtensionNodeSet nset in nsets) { + nset.SourceAddinId = addinId; + nodeSetsCopy [nset.Id] = nset; + } + nodeSets = nodeSetsCopy; + } + } + + internal void UnregisterAddinNodeSets (string addinId) + { + lock (LocalLock) { + var nodeSetsCopy = new Dictionary (nodeSets); + foreach (var nset in nodeSetsCopy.Values.Where (n => n.SourceAddinId == addinId).ToArray ()) + nodeSetsCopy.Remove (nset.Id); + nodeSets = nodeSetsCopy; + } + } + + internal string GetNodeTypeAddin (ExtensionNodeSet nset, string type, string callingAddinId) + { + ExtensionNodeType nt = FindType (nset, type, callingAddinId); + if (nt != null) + return nt.AddinId; + else + return null; + } + + internal ExtensionNodeType FindType (ExtensionNodeSet nset, string name, string callingAddinId) + { + if (nset == null) + return null; + + foreach (ExtensionNodeType nt in nset.NodeTypes) { + if (nt.Id == name) + return nt; + } + + foreach (string ns in nset.NodeSets) { + ExtensionNodeSet regSet; + if (!nodeSets.TryGetValue (ns, out regSet)) { + ReportError ("Unknown node set: " + ns, callingAddinId, null, false); + return null; + } + ExtensionNodeType nt = FindType (regSet, name, callingAddinId); + if (nt != null) + return nt; + } + return null; + } + + internal void RegisterAutoTypeExtensionPoint (Type type, string path) + { + autoExtensionTypes [type] = path; + } + + internal void UnregisterAutoTypeExtensionPoint (Type type, string path) + { + autoExtensionTypes.Remove (type); + } + + internal string GetAutoTypeExtensionPoint (Type type) + { + return autoExtensionTypes [type] as string; + } + + void OnAssemblyLoaded (object s, AssemblyLoadEventArgs a) + { + if (a != null) { + lock (pendingRootChecks) { + pendingRootChecks.Add (a.LoadedAssembly); + } + } + } + + List pendingRootChecks = new List (); + + internal void ValidateAddinRoots () + { + List copy = null; + lock (pendingRootChecks) { + if (pendingRootChecks.Count > 0) { + copy = new List (pendingRootChecks); + pendingRootChecks.Clear (); + } + } + if (copy != null) { + foreach (Assembly asm in copy) + CheckHostAssembly (asm); + } + } + + internal void ActivateRoots () + { + lock (pendingRootChecks) + pendingRootChecks.Clear (); + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies ()) + CheckHostAssembly (asm); + } + + void CheckHostAssembly (Assembly asm) + { + if (AddinDatabase.RunningSetupProcess || asm is System.Reflection.Emit.AssemblyBuilder || asm.IsDynamic) + return; + string codeBase; + try { + codeBase = asm.CodeBase; + } catch { + return; + } + + Uri u; + if (!Uri.TryCreate (codeBase, UriKind.Absolute, out u)) + return; + + string asmFile = u.LocalPath; + Addin ainfo; + try { + ainfo = Registry.GetAddinForHostAssembly (asmFile); + } catch (Exception ex) { + // GetAddinForHostAssembly may crash if the add-in db has been corrupted. In this case, update the db + // and try getting the add-in info again. If it crashes again, then this is a bug. + defaultProgressStatus.ReportError ("Add-in description could not be loaded.", ex); + Registry.Update (null); + ainfo = Registry.GetAddinForHostAssembly (asmFile); + } + + if (ainfo != null && !IsAddinLoaded (ainfo.Id)) { + AddinDescription adesc = null; + try { + adesc = ainfo.Description; + } catch (Exception ex) { + defaultProgressStatus.ReportError ("Add-in description could not be loaded.", ex); + } + if (adesc == null || adesc.FilesChanged ()) { + // If the add-in has changed, update the add-in database. + // We do it here because once loaded, add-in roots can't be + // reloaded like regular add-ins. + Registry.Update (null); + ainfo = Registry.GetAddinForHostAssembly (asmFile); + if (ainfo == null) + return; + } + LoadAddin (null, ainfo.Id, false); + } + } + + /// + /// Creates a new extension context. + /// + /// + /// The new extension context. + /// + /// + /// Extension contexts can be used to query the extension model using particular condition values. + /// + public ExtensionContext CreateExtensionContext () + { + CheckInitialized (); + return CreateChildContext (); + } + + internal void CheckInitialized () + { + if (!initialized) + throw new InvalidOperationException ("Add-in engine not initialized."); + } + + internal void ReportError (string message, string addinId, Exception exception, bool fatal) + { + var handler = AddinLoadError; + if (handler != null) + handler (null, new AddinErrorEventArgs (message, addinId, exception)); + else { + Console.WriteLine (message); + if (exception != null) + Console.WriteLine (exception); + } + } + + internal void ReportAddinLoad (string id) + { + var handler = AddinLoaded; + if (handler != null) { + try { + handler (null, new AddinEventArgs (id)); + } catch { + // Ignore subscriber exceptions + } + } + } + + internal void ReportAddinUnload (string id) + { + var handler = AddinUnloaded; + if (handler != null) { + try { + handler (null, new AddinEventArgs (id)); + } catch { + // Ignore subscriber exceptions + } + } + } + } + +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinErrorEventArgs.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinErrorEventArgs.cs new file mode 100644 index 00000000..1e895251 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinErrorEventArgs.cs @@ -0,0 +1,79 @@ +// +// AddinErrorEventArgs.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Delegate to be used in add-in error subscriptions + /// + public delegate void AddinErrorEventHandler (object sender, AddinErrorEventArgs args); + + /// + /// Provides information about an add-in loading error. + /// + public class AddinErrorEventArgs: AddinEventArgs + { + Exception exception; + string message; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Error message + /// + /// + /// Add-in identifier. + /// + /// + /// Exception that caused the error. + /// + public AddinErrorEventArgs (string message, string addinId, Exception exception): base (addinId) + { + this.message = message; + this.exception = exception; + } + + /// + /// Exception that caused the error. + /// + public Exception Exception { + get { return exception; } + } + + /// + /// Error message + /// + public string Message { + get { return message; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinEventArgs.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinEventArgs.cs new file mode 100644 index 00000000..bea04863 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinEventArgs.cs @@ -0,0 +1,64 @@ +// +// AddinEventArgs.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Delegate to be used in add-in engine events + /// + public delegate void AddinEventHandler (object sender, AddinEventArgs args); + + /// + /// Provides information about an add-in engine event. + /// + public class AddinEventArgs: EventArgs + { + string addinId; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Add-in identifier. + /// + public AddinEventArgs (string addinId) + { + this.addinId = addinId; + } + + /// + /// Identifier of the add-in that generated the event. + /// + public string AddinId { + get { return addinId; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinFlagsAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinFlagsAttribute.cs new file mode 100644 index 00000000..eca03c66 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinFlagsAttribute.cs @@ -0,0 +1,54 @@ +// +// AddinFlagsAttribute.cs +// +// Author: +// Lluis Sanchez +// +// Copyright (c) 2013 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Mono.Addins.Description; + +namespace Mono.Addins +{ + /// + /// Addin flags attribute. + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=false)] + public class AddinFlagsAttribute: Attribute + { + /// + /// Initializes the attribute + /// + /// + /// Add-in flags + /// + public AddinFlagsAttribute (AddinFlags flags) + { + this.Flags = flags; + } + + /// + /// Add-in flags + /// + public AddinFlags Flags { get; set; } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinInfo.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinInfo.cs new file mode 100644 index 00000000..b1125f77 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinInfo.cs @@ -0,0 +1,215 @@ +// +// AddinInfo.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Collections; +using System.Xml; +using System.Xml.Serialization; +using Mono.Addins.Description; + +namespace Mono.Addins +{ + internal class AddinInfo + { + string id = ""; + string namspace = ""; + string name = ""; + string version = ""; + string baseVersion = ""; + string author = ""; + string copyright = ""; + string url = ""; + string description = ""; + string category = ""; + bool defaultEnabled = true; + bool isroot; + DependencyCollection dependencies; + DependencyCollection optionalDependencies; + AddinPropertyCollection properties; + + private AddinInfo () + { + dependencies = new DependencyCollection (); + optionalDependencies = new DependencyCollection (); + } + + public string Id { + get { return Addin.GetFullId (namspace, id, version); } + } + + public string LocalId { + get { return id; } + set { id = value; } + } + + public string Namespace { + get { return namspace; } + set { namspace = value; } + } + + public bool IsRoot { + get { return isroot; } + set { isroot = value; } + } + + public string Name { + get { + string s = Properties.GetPropertyValue ("Name"); + if (s.Length > 0) + return s; + if (name != null && name.Length > 0) + return name; + string sid = id; + if (sid.StartsWith ("__")) + sid = sid.Substring (2); + return Addin.GetFullId (namspace, sid, null); + } + set { name = value; } + } + + public string Version { + get { return version; } + set { version = value; } + } + + public string BaseVersion { + get { return baseVersion; } + set { baseVersion = value; } + } + + public string Author { + get { + string s = Properties.GetPropertyValue ("Author"); + if (s.Length > 0) + return s; + return author; + } + set { author = value; } + } + + public string Copyright { + get { + string s = Properties.GetPropertyValue ("Copyright"); + if (s.Length > 0) + return s; + return copyright; + } + set { copyright = value; } + } + + public string Url { + get { + string s = Properties.GetPropertyValue ("Url"); + if (s.Length > 0) + return s; + return url; + } + set { url = value; } + } + + public string Description { + get { + string s = Properties.GetPropertyValue ("Description"); + if (s.Length > 0) + return s; + return description; + } + set { description = value; } + } + + public string Category { + get { + string s = Properties.GetPropertyValue ("Category"); + if (s.Length > 0) + return s; + return category; + } + set { category = value; } + } + + public bool EnabledByDefault { + get { return defaultEnabled; } + set { defaultEnabled = value; } + } + + public DependencyCollection Dependencies { + get { return dependencies; } + } + + public DependencyCollection OptionalDependencies { + get { return optionalDependencies; } + } + + public AddinPropertyCollection Properties { + get { return properties; } + } + + internal static AddinInfo ReadFromDescription (AddinDescription description) + { + AddinInfo info = new AddinInfo (); + info.id = description.LocalId; + info.namspace = description.Namespace; + info.name = description.Name; + info.version = description.Version; + info.author = description.Author; + info.copyright = description.Copyright; + info.url = description.Url; + info.description = description.Description; + info.category = description.Category; + info.baseVersion = description.CompatVersion; + info.isroot = description.IsRoot; + info.defaultEnabled = description.EnabledByDefault; + + foreach (Dependency dep in description.MainModule.Dependencies) + info.Dependencies.Add (dep); + + foreach (ModuleDescription mod in description.OptionalModules) { + foreach (Dependency dep in mod.Dependencies) + info.OptionalDependencies.Add (dep); + } + info.properties = description.Properties; + + return info; + } + + public bool SupportsVersion (string version) + { + if (Addin.CompareVersions (Version, version) > 0) + return false; + if (baseVersion == "") + return true; + return Addin.CompareVersions (BaseVersion, version) >= 0; + } + + public int CompareVersionTo (AddinInfo other) + { + return Addin.CompareVersions (this.version, other.Version); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizer.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizer.cs new file mode 100644 index 00000000..d1f11eef --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizer.cs @@ -0,0 +1,169 @@ +// +// AddinLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Mono.Addins.Localization; + +namespace Mono.Addins +{ + /// + /// Converts message identifiers to localized messages. + /// + public class AddinLocalizer + { + IAddinLocalizer localizer; + IPluralAddinLocalizer pluralLocalizer; + + internal AddinLocalizer (IAddinLocalizer localizer) + { + this.localizer = localizer; + pluralLocalizer = localizer as IPluralAddinLocalizer; + } + + /// + /// Gets a localized message + /// + /// + /// Message identifier + /// + /// + /// The localized message + /// + public string GetString (string msgid) + { + return localizer.GetString (msgid); + } + + /// + /// Gets a formatted and localized message + /// + /// + /// Message identifier (can contain string format placeholders) + /// + /// + /// Arguments for the string format operation + /// + /// + /// The formatted and localized string + /// + public string GetString (string msgid, params string[] args) + { + return string.Format (localizer.GetString (msgid), args); + } + + /// + /// Gets a formatted and localized message + /// + /// + /// Message identifier (can contain string format placeholders) + /// + /// + /// Arguments for the string format operation + /// + /// + /// The formatted and localized string + /// + public string GetString (string msgid, params object[] args) + { + return string.Format (localizer.GetString (msgid), args); + } + + /// + /// Gets a localized plural form for a message identifier + /// + /// + /// Message identifier for the singular form + /// + /// + /// Default result message for the plural form + /// + /// + /// Value count. Determines whether to use singular or plural form. + /// + /// + /// The localized message + /// + public string GetPluralString (string msgid, string defaultPlural, int n) + { + // If the localizer does not support plural forms, just use GetString to + // get a translation. It is not correct to check 'n' in this case because + // there is no guarantee that 'defaultPlural' will be translated. + + if (pluralLocalizer != null) + return pluralLocalizer.GetPluralString (msgid, defaultPlural, n); + else + return GetString (msgid); + } + + /// + /// Gets a localized and formatted plural form for a message identifier + /// + /// + /// Message identifier for the singular form (can contain string format placeholders) + /// + /// + /// Default result message for the plural form (can contain string format placeholders) + /// + /// + /// Value count. Determines whether to use singular or plural form. + /// + /// + /// Arguments for the string format operation + /// + /// + /// The localized message + /// + public string GetPluralString (string singular, string defaultPlural, int n, params string[] args) + { + return string.Format (GetPluralString (singular, defaultPlural, n), args); + } + + /// + /// Gets a localized and formatted plural form for a message identifier + /// + /// + /// Message identifier for the singular form (can contain string format placeholders) + /// + /// + /// Default result message for the plural form (can contain string format placeholders) + /// + /// + /// Value count. Determines whether to use singular or plural form. + /// + /// + /// Arguments for the string format operation + /// + /// + /// The localized message + /// + public string GetPluralString (string singular, string defaultPlural, int n, params object[] args) + { + return string.Format (GetPluralString (singular, defaultPlural, n), args); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizerAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizerAttribute.cs new file mode 100644 index 00000000..f2706d44 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizerAttribute.cs @@ -0,0 +1,72 @@ +// +// AddinLocalizerAttribute.cs +// +// Author: +// Matt Ward +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Declares a custom localizer for an add-in. + /// + [AttributeUsage (AttributeTargets.Assembly)] + public class AddinLocalizerAttribute: Attribute + { + Type type; + string typeName; + + /// + /// Initializes a new instance of the class. + /// + public AddinLocalizerAttribute () + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The type of the localizer. This type must implement the + /// interface. + /// + public AddinLocalizerAttribute (Type type) + { + Type = type; + } + + /// + /// Type of the localizer. + /// + public Type Type { + get { return type; } + set { type = value; typeName = type.FullName; } + } + + internal string TypeName { + get { return typeName; } + set { typeName = value; type = null; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizerGettextAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizerGettextAttribute.cs new file mode 100644 index 00000000..c4a36bf2 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinLocalizerGettextAttribute.cs @@ -0,0 +1,109 @@ +// +// AddinLocalizerAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Declares a Gettext-based localizer for an add-in + /// + [AttributeUsage (AttributeTargets.Assembly)] + public class AddinLocalizerGettextAttribute: Attribute + { + string catalog; + string location; + + /// + /// Initializes a new instance of the class. + /// + public AddinLocalizerGettextAttribute () + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Name of the catalog which contains the strings. + /// + public AddinLocalizerGettextAttribute (string catalog) + { + this.catalog = catalog; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Name of the catalog which contains the strings. + /// + /// + /// Relative path to the location of the catalog. This path must be relative to the add-in location. + /// + /// + /// The location path must contain a directory structure like this: + /// + /// {language-id}/LC_MESSAGES/{Catalog}.mo + /// + /// For example, the catalog for spanish strings would be located at: + /// + /// locale/es/LC_MESSAGES/some-addin.mo + /// + public AddinLocalizerGettextAttribute (string catalog, string location) + { + this.catalog = catalog; + this.location = location; + } + + /// + /// Name of the catalog which contains the strings. + /// + public string Catalog { + get { return this.catalog; } + set { this.catalog = value; } + } + + /// + /// Relative path to the location of the catalog. This path must be relative to the add-in location. + /// + /// + /// When not specified, the default value of this property is 'locale'. + /// The location path must contain a directory structure like this: + /// + /// {language-id}/LC_MESSAGES/{Catalog}.mo + /// + /// For example, the catalog for spanish strings would be located at: + /// + /// locale/es/LC_MESSAGES/some-addin.mo + /// + public string Location { + get { return this.location; } + set { this.location = value; } + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinManager.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinManager.cs new file mode 100644 index 00000000..11f3f81b --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinManager.cs @@ -0,0 +1,840 @@ +// +// AddinManager.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; + +using Mono.Addins.Localization; + +namespace Mono.Addins +{ + /// + /// Provides access to add-in and extension model management operations. + /// + public class AddinManager + { + static AddinEngine sessionService; + + private AddinManager () + { + } + + /// + /// Initializes the add-in engine. + /// + /// + /// The add-in engine needs to be initialized before doing any add-in operation. + /// When initialized with this method, it will look for add-ins in the global add-in registry. + /// + public static void Initialize () + { + // Code not shared with the other Initialize since I need to get the calling assembly + Assembly asm = Assembly.GetEntryAssembly (); + if (asm == null) asm = Assembly.GetCallingAssembly (); + AddinEngine.Initialize (asm, null, null, null); + } + + /// + /// Initializes the add-in engine. + /// + /// + /// Location of the add-in registry. + /// + /// + /// The add-in engine needs to be initialized before doing any add-in operation. + /// Configuration information about the add-in registry will be stored in the + /// provided location. The add-in engine will look for add-ins in an 'addins' + /// subdirectory of the provided directory. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public static void Initialize (string configDir) + { + Assembly asm = Assembly.GetEntryAssembly (); + if (asm == null) asm = Assembly.GetCallingAssembly (); + AddinEngine.Initialize (asm, configDir, null, null); + } + + /// + /// Initializes the add-in engine. + /// + /// + /// Location of the add-in registry. + /// + /// + /// Add-ins directory. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// The add-in engine needs to be initialized before doing any add-in operation. + /// Configuration information about the add-in registry will be stored in the + /// provided location. The add-in engine will look for add-ins in the provided + /// 'addinsDir' directory. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public static void Initialize (string configDir, string addinsDir) + { + Assembly asm = Assembly.GetEntryAssembly (); + if (asm == null) asm = Assembly.GetCallingAssembly (); + AddinEngine.Initialize (asm, configDir, addinsDir, null); + } + + /// + /// Initializes the add-in engine. + /// + /// + /// Location of the add-in registry. + /// + /// + /// Add-ins directory. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// Location of the add-in database. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// The add-in engine needs to be initialized before doing any add-in operation. + /// Configuration information about the add-in registry will be stored in the + /// provided location. The add-in engine will look for add-ins in the provided + /// 'addinsDir' directory. Cached information about add-ins will be stored in + /// the 'databaseDir' directory. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public static void Initialize (string configDir, string addinsDir, string databaseDir) + { + Assembly asm = Assembly.GetEntryAssembly (); + if (asm == null) asm = Assembly.GetCallingAssembly (); + AddinEngine.Initialize (asm, configDir, addinsDir, databaseDir); + } + + /// + /// Finalizes an add-in engine. + /// + public static void Shutdown () + { + AddinEngine.Shutdown (); + } + + /// + /// Sets the default localizer to be used for this add-in engine + /// + /// + /// The add-in localizer + /// + public static void InitializeDefaultLocalizer (IAddinLocalizer localizer) + { + AddinEngine.InitializeDefaultLocalizer (localizer); + } + + internal static string StartupDirectory { + get { return AddinEngine.StartupDirectory; } + } + + /// + /// Gets whether the add-in engine has been initialized. + /// + public static bool IsInitialized { + get { return AddinEngine.IsInitialized; } + } + + /// + /// Gets the default add-in installer + /// + /// + /// The default installer is used by the CheckInstalled method to request + /// the installation of missing add-ins. + /// + public static IAddinInstaller DefaultInstaller { + get { return AddinEngine.DefaultInstaller; } + set { AddinEngine.DefaultInstaller = value; } + } + + /// + /// Gets the default localizer for this add-in engine + /// + public static AddinLocalizer DefaultLocalizer { + get { + return AddinEngine.DefaultLocalizer; + } + } + + /// + /// Gets the localizer for the add-in that is invoking this property + /// + public static AddinLocalizer CurrentLocalizer { + get { + AddinEngine.CheckInitialized (); + RuntimeAddin addin = AddinEngine.GetAddinForAssembly (Assembly.GetCallingAssembly ()); + if (addin != null) + return addin.Localizer; + else + return AddinEngine.DefaultLocalizer; + } + } + + /// + /// Gets a reference to the RuntimeAddin object for the add-in that is invoking this property + /// + public static RuntimeAddin CurrentAddin { + get { + AddinEngine.CheckInitialized (); + return AddinEngine.GetAddinForAssembly (Assembly.GetCallingAssembly ()); + } + } + + /// + /// Gets the default add-in engine + /// + public static AddinEngine AddinEngine { + get { + if (sessionService == null) + sessionService = new AddinEngine(); + + return sessionService; + } + } + + /// + /// Gets the add-in registry bound to the default add-in engine + /// + public static AddinRegistry Registry { + get { + return AddinEngine.Registry; + } + } + + /// + /// Checks if the provided add-ins are installed, and requests the installation of those + /// which aren't. + /// + /// + /// Message to show to the user when new add-ins have to be installed. + /// + /// + /// List of IDs of the add-ins to be checked. + /// + /// + /// This method checks if the specified add-ins are installed. + /// If some of the add-ins are not installed, it will use + /// the installer assigned to the DefaultAddinInstaller property + /// to install them. If the installation fails, or if DefaultAddinInstaller + /// is not set, an exception will be thrown. + /// + public static void CheckInstalled (string message, params string[] addinIds) + { + AddinEngine.CheckInstalled (message, addinIds); + } + + /// + /// Checks if an add-in has been loaded. + /// + /// + /// Full identifier of the add-in. + /// + /// + /// True if the add-in is loaded. + /// + public static bool IsAddinLoaded (string id) + { + return AddinEngine.IsAddinLoaded (id); + } + + /// + /// Forces the loading of an add-in. + /// + /// + /// Status monitor to keep track of the loading process. + /// + /// + /// Full identifier of the add-in to load. + /// + /// + /// This method loads all assemblies that belong to an add-in in memory. + /// All add-ins on which the specified add-in depends will also be loaded. + /// Notice that in general add-ins don't need to be explicitly loaded using + /// this method, since the add-in engine will load them on demand. + /// + public static void LoadAddin (IProgressStatus statusMonitor, string id) + { + AddinEngine.LoadAddin (statusMonitor, id); + } + + /// + /// Creates a new extension context. + /// + /// + /// The new extension context. + /// + /// + /// Extension contexts can be used to query the extension model using particular condition values. + /// + public static ExtensionContext CreateExtensionContext () + { + return AddinEngine.CreateExtensionContext (); + } + + /// + /// Returns the extension node in a path + /// + /// + /// Location of the node. + /// + /// + /// The node, or null if not found. + /// + public static ExtensionNode GetExtensionNode (string path) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionNode (path); + } + + /// + /// Returns the extension node in a path + /// + /// + /// Location of the node. + /// + /// + /// The node, or null if not found. + /// + public static T GetExtensionNode (string path) where T:ExtensionNode + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionNode (path); + } + + /// + /// Gets extension nodes registered in a path. + /// + /// + /// An extension path.> + /// + /// + /// All nodes registered in the provided path. + /// + public static ExtensionNodeList GetExtensionNodes (string path) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionNodes (path); + } + + /// + /// Gets extension nodes registered in a path. + /// + /// + /// An extension path. + /// + /// + /// Expected node type. + /// + /// + /// A list of nodes + /// + /// + /// This method returns all nodes registered under the provided path. + /// It will throw a InvalidOperationException if the type of one of + /// the registered nodes is not assignable to the provided type. + /// + public static ExtensionNodeList GetExtensionNodes (string path, Type expectedNodeType) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionNodes (path, expectedNodeType); + } + + /// + /// Gets extension nodes registered in a path. + /// + /// + /// An extension path. + /// + /// + /// A list of nodes + /// + /// + /// This method returns all nodes registered under the provided path. + /// It will throw a InvalidOperationException if the type of one of + /// the registered nodes is not assignable to the provided type. + /// + public static ExtensionNodeList GetExtensionNodes (string path) where T:ExtensionNode + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionNodes (path); + } + + /// + /// Gets extension nodes for a type extension point + /// + /// + /// Type defining the extension point + /// + /// + /// A list of nodes + /// + /// + /// This method returns all extension nodes bound to the provided type. + /// + public static ExtensionNodeList GetExtensionNodes (Type instanceType) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionNodes (instanceType); + } + + /// + /// Gets extension nodes for a type extension point + /// + /// + /// Type defining the extension point + /// + /// + /// Expected extension node type + /// + /// + /// A list of nodes + /// + /// + /// This method returns all nodes registered for the provided type. + /// It will throw a InvalidOperationException if the type of one of + /// the registered nodes is not assignable to the provided node type. + /// + public static ExtensionNodeList GetExtensionNodes (Type instanceType, Type expectedNodeType) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionNodes (instanceType, expectedNodeType); + } + + /// + /// Gets extension nodes for a type extension point + /// + /// + /// Type defining the extension point + /// + /// + /// A list of nodes + /// + /// + /// This method returns all nodes registered for the provided type. + /// It will throw a InvalidOperationException if the type of one of + /// the registered nodes is not assignable to the specified node type argument. + /// + public static ExtensionNodeList GetExtensionNodes (Type instanceType) where T: ExtensionNode + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionNodes (instanceType); + } + + /// + /// Gets extension objects registered for a type extension point. + /// + /// + /// Type defining the extension point + /// + /// + /// A list of objects + /// + public static object[] GetExtensionObjects (Type instanceType) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (instanceType); + } + + /// + /// Gets extension objects registered for a type extension point. + /// + /// + /// A list of objects + /// + /// + /// The type argument of this generic method is the type that defines + /// the extension point. + /// + public static T[] GetExtensionObjects () + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (); + } + + /// + /// Gets extension objects registered for a type extension point. + /// + /// + /// Type defining the extension point + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// A list of extension objects. + /// + public static object[] GetExtensionObjects (Type instanceType, bool reuseCachedInstance) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (instanceType, reuseCachedInstance); + } + + /// + /// Gets extension objects registered for a type extension point. + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// A list of extension objects. + /// + /// + /// The type argument of this generic method is the type that defines + /// the extension point. + /// + public static T[] GetExtensionObjects (bool reuseCachedInstance) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (reuseCachedInstance); + } + + /// + /// Gets extension objects registered in a path + /// + /// + /// An extension path. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node. + /// + public static object[] GetExtensionObjects (string path) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (path); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node (or TypeExtensionNode.GetInstance() if + /// reuseCachedInstance is set to true) + /// + public static object[] GetExtensionObjects (string path, bool reuseCachedInstance) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (path, reuseCachedInstance); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// Type of the return array elements. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node. + /// + /// An InvalidOperationException exception is thrown if one of the found + /// objects is not a subclass of the provided type. + /// + public static object[] GetExtensionObjects (string path, Type arrayElementType) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (path, arrayElementType); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node. + /// + /// An InvalidOperationException exception is thrown if one of the found + /// objects is not a subclass of the provided type. + /// + public static T[] GetExtensionObjects (string path) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (path); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// Type of the return array elements. + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node (or TypeExtensionNode.GetInstance() if + /// reuseCachedInstance is set to true). + /// + /// An InvalidOperationException exception is thrown if one of the found + /// objects is not a subclass of the provided type. + /// + public static object[] GetExtensionObjects (string path, Type arrayElementType, bool reuseCachedInstance) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (path, arrayElementType, reuseCachedInstance); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node (or TypeExtensionNode.GetInstance() if + /// reuseCachedInstance is set to true). + /// + /// An InvalidOperationException exception is thrown if one of the found + /// objects is not a subclass of the provided type. + /// + public static T[] GetExtensionObjects (string path, bool reuseCachedInstance) + { + AddinEngine.CheckInitialized (); + return AddinEngine.GetExtensionObjects (path, reuseCachedInstance); + } + + /// + /// Extension change event. + /// + /// + /// This event is fired when any extension point in the add-in system changes. + /// The event args object provides the path of the changed extension, although + /// it does not provide information about what changed. Hosts subscribing to + /// this event should get the new list of nodes using a query method such as + /// AddinManager.GetExtensionNodes() and then update whatever needs to be updated. + /// + public static event ExtensionEventHandler ExtensionChanged { + add { AddinEngine.CheckInitialized(); AddinEngine.ExtensionChanged += value; } + remove { AddinEngine.CheckInitialized(); AddinEngine.ExtensionChanged -= value; } + } + + /// + /// Register a listener of extension node changes. + /// + /// + /// Path of the node. + /// + /// + /// A handler method. + /// + /// + /// Hosts can call this method to be subscribed to an extension change + /// event for a specific path. The event will be fired once for every + /// individual node change. The event arguments include the change type + /// (Add or Remove) and the extension node added or removed. + /// + /// NOTE: The handler will be called for all nodes existing in the path at the moment of registration. + /// + public static void AddExtensionNodeHandler (string path, ExtensionNodeEventHandler handler) + { + AddinEngine.CheckInitialized (); + AddinEngine.AddExtensionNodeHandler (path, handler); + } + + /// + /// Unregister a listener of extension node changes. + /// + /// + /// Path of the node. + /// + /// + /// A handler method. + /// + /// + /// This method unregisters a delegate from the node change event of a path. + /// + public static void RemoveExtensionNodeHandler (string path, ExtensionNodeEventHandler handler) + { + AddinEngine.CheckInitialized (); + AddinEngine.RemoveExtensionNodeHandler (path, handler); + } + + /// + /// Register a listener of extension node changes. + /// + /// + /// Type defining the extension point + /// + /// + /// A handler method. + /// + /// + /// Hosts can call this method to be subscribed to an extension change + /// event for a specific type extension point. The event will be fired once for every + /// individual node change. The event arguments include the change type + /// (Add or Remove) and the extension node added or removed. + /// + /// NOTE: The handler will be called for all nodes existing in the path at the moment of registration. + /// + public static void AddExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler) + { + AddinEngine.CheckInitialized (); + AddinEngine.AddExtensionNodeHandler (instanceType, handler); + } + + /// + /// Unregister a listener of extension node changes. + /// + /// + /// Type defining the extension point + /// + /// + /// A handler method. + /// + public static void RemoveExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler) + { + AddinEngine.CheckInitialized (); + AddinEngine.RemoveExtensionNodeHandler (instanceType, handler); + } + + /// + /// Add-in loading error event. + /// + /// + /// This event is fired when there is an error when loading the extension + /// of an add-in, or any other kind of error that may happen when querying extension points. + /// + public static event AddinErrorEventHandler AddinLoadError { + add { AddinEngine.AddinLoadError += value; } + remove { AddinEngine.AddinLoadError -= value; } + } + + /// + /// Add-in loaded event. + /// + /// + /// Fired after loading an add-in in memory. + /// + public static event AddinEventHandler AddinLoaded { + add { AddinEngine.AddinLoaded += value; } + remove { AddinEngine.AddinLoaded -= value; } + } + + /// + /// Add-in unload event. + /// + /// + /// Fired when an add-in is unloaded from memory. It may happen an add-in is disabled or uninstalled. + /// + public static event AddinEventHandler AddinUnloaded { + add { AddinEngine.AddinUnloaded += value; } + remove { AddinEngine.AddinUnloaded -= value; } + } + + internal static bool CheckAssembliesLoaded (HashSet files) + { + foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies ()) { + if (asm is System.Reflection.Emit.AssemblyBuilder) + continue; + try { + Uri u; + if (!Uri.TryCreate (asm.CodeBase, UriKind.Absolute, out u)) + continue; + string asmFile = u.LocalPath; + if (files.Contains (Path.GetFullPath (asmFile))) + return true; + } catch { + // Ignore + } + } + return false; + } + } + +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinModuleAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinModuleAttribute.cs new file mode 100644 index 00000000..a1f0174f --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinModuleAttribute.cs @@ -0,0 +1,59 @@ +// +// AddinModuleAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Declares an optional add-in module + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=true)] + public class AddinModuleAttribute: Attribute + { + string assemblyFile; + + /// + /// Initializes the instance. + /// + /// + /// Relative path to the assembly that implements the optional module + /// + public AddinModuleAttribute (string assemblyFile) + { + this.assemblyFile = assemblyFile; + } + + /// + /// Relative path to the assembly that implements the optional module + /// + public string AssemblyFile { + get { return this.assemblyFile ?? string.Empty; } + set { this.assemblyFile = value; } + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinNameAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinNameAttribute.cs new file mode 100644 index 00000000..32f66c41 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinNameAttribute.cs @@ -0,0 +1,73 @@ +// +// AddinNameAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace Mono.Addins +{ + /// + /// Sets the display name of an add-in + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public class AddinNameAttribute: Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Name of the add-in + /// + public AddinNameAttribute (string name) + { + Name = name; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Name of the add-in + /// + /// + /// Locale of the name (for example, 'en-US', or 'en') + /// + public AddinNameAttribute (string name, string locale) + { + Name = name; + Locale = locale; + } + + /// + /// Name of the add-in + /// + public string Name { get; set; } + + /// + /// Locale of the name (for example, 'en-US', or 'en') + /// + public string Locale { get; set; } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinPropertyAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinPropertyAttribute.cs new file mode 100644 index 00000000..31a64e40 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinPropertyAttribute.cs @@ -0,0 +1,84 @@ +// +// AddinPropertyAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace Mono.Addins +{ + /// + /// Defines an add-in property + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=true)] + public class AddinPropertyAttribute: Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// + /// Name of the property + /// + /// + /// Value of the property + /// + public AddinPropertyAttribute (string name, string value): this (name, null, value) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Name of the property + /// + /// + /// Locale of the property. It can be null if the property is not bound to a locale. + /// + /// + /// Value of the property + /// + public AddinPropertyAttribute (string name, string locale, string value) + { + Name = name; + Locale = locale; + Value = value; + } + + /// + /// Name of the property + /// + public string Name { get; set; } + + /// + /// Locale of the property. It can be null if the property is not bound to a locale. + /// + public string Locale { get; set; } + + /// + /// Value of the property + /// + public string Value { get; set; } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinRegistry.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinRegistry.cs new file mode 100644 index 00000000..f59b2b5f --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinRegistry.cs @@ -0,0 +1,828 @@ +// +// AddinRegistry.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.IO; +using System.Xml; +using System.Collections; +using System.Collections.Specialized; +using Mono.Addins.Database; +using Mono.Addins.Description; +using System.Collections.Generic; +using System.Linq; + +namespace Mono.Addins +{ + /// + /// An add-in registry. + /// + /// + /// An add-in registry is a data structure used by the add-in engine to locate add-ins to load. + /// + /// A registry can be configured to look for add-ins in several directories. However, add-ins + /// copied to those directories won't be detected until an explicit add-in scan is requested. + /// The registry can be updated by an application by calling Registry.Update(), or by a user by + /// running the 'mautil' add-in setup tool. + /// + /// The registry has information about the location of every add-in and a timestamp of the last + /// check, so the Update method will only scan new or modified add-ins. An application can + /// add a call to Registry.Update() in the Main method to detect all new add-ins every time the + /// app is started. + /// + /// Every add-in added to the registry is parsed and validated, and if there is any error it + /// will be rejected. The registry is also in charge of scanning the add-in assemblies and look + /// for extensions and other information declared using custom attributes. That information is + /// merged with the manifest information (if there is one) to create a complete add-in + /// description ready to be used at run-time. + /// + /// Mono.Addins allows sharing an add-in registry among several applications. In this context, + /// all applications sharing the registry share the same extension point model, and it is + /// possible to implement add-ins which extend several hosts. + /// + public class AddinRegistry: IDisposable + { + AddinDatabase database; + StringCollection addinDirs; + string basePath; + string currentDomain; + string startupDirectory; + string addinsDir; + string databaseDir; + + /// + /// Initializes a new instance. + /// + /// + /// Location of the add-in registry. + /// + /// + /// Creates a new add-in registry located in the provided path. + /// The add-in registry will look for add-ins in an 'addins' + /// subdirectory of the provided registryPath. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public AddinRegistry (string registryPath): this (null, registryPath, null, null, null) + { + } + + /// + /// Initializes a new instance. + /// + /// + /// Location of the add-in registry. + /// + /// + /// Location of the application. + /// + /// + /// Creates a new add-in registry located in the provided path. + /// The add-in registry will look for add-ins in an 'addins' + /// subdirectory of the provided registryPath. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public AddinRegistry (string registryPath, string startupDirectory): this (null, registryPath, startupDirectory, null, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Location of the add-in registry. + /// + /// + /// Location of the application. + /// + /// + /// Add-ins directory. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// Creates a new add-in registry located in the provided path. + /// Configuration information about the add-in registry will be stored in + /// 'registryPath'. The add-in registry will look for add-ins in the provided + /// 'addinsDir' directory. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public AddinRegistry (string registryPath, string startupDirectory, string addinsDir): this (null, registryPath, startupDirectory, addinsDir, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Location of the add-in registry. + /// + /// + /// Location of the application. + /// + /// + /// Add-ins directory. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// Location of the add-in database. If the path is relative, it is considered to be relative + /// to the configDir directory. + /// + /// + /// Creates a new add-in registry located in the provided path. + /// Configuration information about the add-in registry will be stored in + /// 'registryPath'. The add-in registry will look for add-ins in the provided + /// 'addinsDir' directory. Cached information about add-ins will be stored in + /// the 'databaseDir' directory. + /// + /// When specifying a path, it is possible to use a special folder name as root. + /// For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + /// by the location of the Environment.SpecialFolder.Personal folder. Any value + /// of the Environment.SpecialFolder enumeration can be used (always between square + /// brackets) + /// + public AddinRegistry (string registryPath, string startupDirectory, string addinsDir, string databaseDir): this (null, registryPath, startupDirectory, addinsDir, databaseDir) + { + } + + internal AddinRegistry (AddinEngine engine, string registryPath, string startupDirectory, string addinsDir, string databaseDir) + { + basePath = Path.GetFullPath (Util.NormalizePath (registryPath)); + + if (addinsDir != null) { + addinsDir = Util.NormalizePath (addinsDir); + if (Path.IsPathRooted (addinsDir)) + this.addinsDir = Path.GetFullPath (addinsDir); + else + this.addinsDir = Path.GetFullPath (Path.Combine (basePath, addinsDir)); + } else + this.addinsDir = Path.Combine (basePath, "addins"); + + if (databaseDir != null) { + databaseDir = Util.NormalizePath (databaseDir); + if (Path.IsPathRooted (databaseDir)) + this.databaseDir = Path.GetFullPath (databaseDir); + else + this.databaseDir = Path.GetFullPath (Path.Combine (basePath, databaseDir)); + } + else + this.databaseDir = Path.GetFullPath (basePath); + + // Look for add-ins in the hosts directory and in the default + // addins directory + addinDirs = new StringCollection (); + addinDirs.Add (DefaultAddinsFolder); + + // Initialize the database after all paths have been set + database = new AddinDatabase (engine, this); + + // Get the domain corresponding to the startup folder + if (startupDirectory != null && startupDirectory.Length > 0) { + this.startupDirectory = Util.NormalizePath (startupDirectory); + currentDomain = database.GetFolderDomain (null, this.startupDirectory); + } else + currentDomain = AddinDatabase.GlobalDomain; + } + + /// + /// Gets the global registry. + /// + /// + /// The global registry + /// + /// + /// The global add-in registry is created in "~/.config/mono.addins", + /// and it is the default registry used when none is specified. + /// + public static AddinRegistry GetGlobalRegistry () + { + return GetGlobalRegistry (null, null); + } + + internal static AddinRegistry GetGlobalRegistry (AddinEngine engine, string startupDirectory) + { + AddinRegistry reg = new AddinRegistry (engine, GlobalRegistryPath, startupDirectory, null, null); + string baseDir; + if (Util.IsWindows) + baseDir = Environment.GetFolderPath (Environment.SpecialFolder.CommonProgramFiles); + else + baseDir = "/etc"; + + reg.GlobalAddinDirectories.Add (Path.Combine (baseDir, "mono.addins")); + return reg; + } + + internal bool UnknownDomain { + get { return currentDomain == AddinDatabase.UnknownDomain; } + } + + internal static string GlobalRegistryPath { + get { + string customDir = Environment.GetEnvironmentVariable ("MONO_ADDINS_GLOBAL_REGISTRY"); + if (customDir != null && customDir.Length > 0) + return Path.GetFullPath (Util.NormalizePath (customDir)); + + string path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); + path = Path.Combine (path, "mono.addins"); + return Path.GetFullPath (path); + } + } + + internal string CurrentDomain { + get { return currentDomain; } + } + + /// + /// Location of the add-in registry. + /// + public string RegistryPath { + get { return basePath; } + } + + /// + /// Disposes the add-in engine. + /// + public void Dispose () + { + database.Shutdown (); + } + + /// + /// Returns an add-in from the registry. + /// + /// + /// Identifier of the add-in. + /// + /// + /// The add-in, or 'null' if not found. + /// + /// + /// The add-in identifier may optionally include a version number, for example: "TextEditor.Xml,1.2" + /// + public Addin GetAddin (string id) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return null; + Addin ad = database.GetInstalledAddin (currentDomain, id); + if (ad != null && IsRegisteredForUninstall (ad.Id)) + return null; + return ad; + } + + /// + /// Returns an add-in from the registry. + /// + /// + /// Identifier of the add-in. + /// + /// + /// 'true' if the exact add-in version must be found. + /// + /// + /// The add-in, or 'null' if not found. + /// + /// + /// The add-in identifier may optionally include a version number, for example: "TextEditor.Xml,1.2". + /// In this case, if the exact version is not found and exactVersionMatch is 'false', it will + /// return one than is compatible with the required version. + /// + public Addin GetAddin (string id, bool exactVersionMatch) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return null; + Addin ad = database.GetInstalledAddin (currentDomain, id, exactVersionMatch); + if (ad != null && IsRegisteredForUninstall (ad.Id)) + return null; + return ad; + } + + /// + /// Gets all add-ins or add-in roots registered in the registry. + /// + /// + /// The addins. + /// + /// + /// Flags. + /// + public Addin[] GetModules (AddinSearchFlags flags) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return new Addin [0]; + AddinSearchFlagsInternal f = (AddinSearchFlagsInternal)(int)flags; + return database.GetInstalledAddins (currentDomain, f | AddinSearchFlagsInternal.ExcludePendingUninstall).ToArray (); + } + + /// + /// Gets all add-ins registered in the registry. + /// + /// + /// Add-ins registered in the registry. + /// + public Addin[] GetAddins () + { + return GetModules (AddinSearchFlags.IncludeAddins); + } + + /// + /// Gets all add-in roots registered in the registry. + /// + /// + /// Descriptions of all add-in roots. + /// + public Addin[] GetAddinRoots () + { + return GetModules (AddinSearchFlags.IncludeRoots); + } + + /// + /// Loads an add-in description + /// + /// + /// Progress tracker. + /// + /// + /// Name of the file to load + /// + /// + /// An add-in description + /// + /// + /// This method loads an add-in description from a file. The file can be an XML manifest or an + /// assembly that implements an add-in. + /// + public AddinDescription GetAddinDescription (IProgressStatus progressStatus, string file) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return null; + string outFile = Path.GetTempFileName (); + try { + database.ParseAddin (progressStatus, currentDomain, file, outFile, false); + } + catch { + File.Delete (outFile); + throw; + } + + try { + AddinDescription desc = AddinDescription.Read (outFile); + if (desc != null) { + desc.AddinFile = file; + desc.OwnerDatabase = database; + } + return desc; + } + catch { + // Errors are already reported using the progress status object + return null; + } + finally { + File.Delete (outFile); + } + } + + /// + /// Reads an XML add-in manifest + /// + /// + /// Path to the XML file + /// + /// + /// An add-in description + /// + public AddinDescription ReadAddinManifestFile (string file) + { + AddinDescription desc = AddinDescription.Read (file); + if (currentDomain != AddinDatabase.UnknownDomain) { + desc.OwnerDatabase = database; + desc.Domain = currentDomain; + } + return desc; + } + + /// + /// Reads an XML add-in manifest + /// + /// + /// Reader that contains the XML + /// + /// + /// Base path to use to discover add-in files + /// + /// + /// An add-in description + /// + public AddinDescription ReadAddinManifestFile (TextReader reader, string baseFile) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return null; + AddinDescription desc = AddinDescription.Read (reader, baseFile); + desc.OwnerDatabase = database; + desc.Domain = currentDomain; + return desc; + } + + /// + /// Checks whether an add-in is enabled. + /// + /// + /// Identifier of the add-in. + /// + /// + /// 'true' if the add-in is enabled. + /// + public bool IsAddinEnabled (string id) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return false; + return database.IsAddinEnabled (currentDomain, id); + } + + /// + /// Enables an add-in. + /// + /// + /// Identifier of the add-in + /// + /// + /// If the enabled add-in depends on other add-ins which are disabled, + /// those will automatically be enabled too. + /// + public void EnableAddin (string id) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return; + database.EnableAddin (currentDomain, id, true); + } + + /// + /// Disables an add-in. + /// + /// + /// Identifier of the add-in. + /// + /// + /// When an add-in is disabled, all extension points it defines will be ignored + /// by the add-in engine. Other add-ins which depend on the disabled add-in will + /// also automatically be disabled. + /// + public void DisableAddin (string id) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return; + database.DisableAddin (currentDomain, id); + } + + /// + /// Disables an add-in. + /// + /// + /// Identifier of the add-in. + /// + /// + /// If true, it disables the add-in that exactly matches the provided version. If false, it disables + /// all versions of add-ins with the same Id + /// + /// + /// When an add-in is disabled, all extension points it defines will be ignored + /// by the add-in engine. Other add-ins which depend on the disabled add-in will + /// also automatically be disabled. + /// + public void DisableAddin (string id, bool exactVersionMatch) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return; + database.DisableAddin (currentDomain, id, exactVersionMatch); + } + + /// + /// Registers a set of add-ins for uninstallation. + /// + /// + /// Identifier of the add-in + /// + /// + /// Files to be uninstalled + /// + /// + /// This method can be used to instruct the add-in manager to uninstall + /// an add-in the next time the registry is updated. This is useful + /// when an add-in manager can't delete an add-in because if it is + /// loaded. + /// + public void RegisterForUninstall (string id, IEnumerable files) + { + database.RegisterForUninstall (currentDomain, id, files); + } + + /// + /// Determines whether an add-in is registered for uninstallation + /// + /// + /// true if the add-in is registered for uninstallation + /// + /// + /// Identifier of the add-in + /// + public bool IsRegisteredForUninstall (string addinId) + { + return database.IsRegisteredForUninstall (currentDomain, addinId); + } + + /// + /// Gets a value indicating whether there are pending add-ins to be uninstalled installed + /// + public bool HasPendingUninstalls { + get { return database.HasPendingUninstalls (currentDomain); } + } + + /// + /// Internal use only + /// + public void DumpFile (string file) + { + Mono.Addins.Serialization.BinaryXmlReader.DumpFile (file); + } + + /// + /// Resets the configuration files of the registry + /// + public void ResetConfiguration () + { + database.ResetConfiguration (); + } + + internal void NotifyDatabaseUpdated () + { + if (startupDirectory != null) + currentDomain = database.GetFolderDomain (null, startupDirectory); + } + + /// + /// Updates the add-in registry. + /// + /// + /// This method must be called after modifying, installing or uninstalling add-ins. + /// + /// When calling Update, every add-in added to the registry is parsed and validated, + /// and if there is any error it will be rejected. It will also cache add-in information + /// needed at run-time. + /// + /// If during the update operation the registry finds new add-ins or detects that some + /// add-ins have been deleted, the loaded extension points will be updated to include + /// or exclude extension nodes from those add-ins. + /// + public void Update () + { + Update (new ConsoleProgressStatus (false)); + } + + /// + /// Updates the add-in registry. + /// + /// + /// Progress monitor to keep track of the update operation. + /// + /// + /// This method must be called after modifying, installing or uninstalling add-ins. + /// + /// When calling Update, every add-in added to the registry is parsed and validated, + /// and if there is any error it will be rejected. It will also cache add-in information + /// needed at run-time. + /// + /// If during the update operation the registry finds new add-ins or detects that some + /// add-ins have been deleted, the loaded extension points will be updated to include + /// or exclude extension nodes from those add-ins. + /// + public void Update (IProgressStatus monitor) + { + database.Update (monitor, currentDomain); + } + + /// + /// Regenerates the cached data of the add-in registry. + /// + /// + /// Progress monitor to keep track of the rebuild operation. + /// + public void Rebuild (IProgressStatus monitor) + { + database.Repair (monitor, currentDomain); + + // A full rebuild may cause the domain to change + if (!string.IsNullOrEmpty (startupDirectory)) + currentDomain = database.GetFolderDomain (null, startupDirectory); + } + + /// + /// Registers an extension. Only AddinFileSystemExtension extensions are supported right now. + /// + /// + /// The extension to register + /// + public void RegisterExtension (object extension) + { + database.RegisterExtension (extension); + } + + /// + /// Unregisters an extension. + /// + /// + /// The extension to unregister + /// + public void UnregisterExtension (object extension) + { + database.UnregisterExtension (extension); + } + + internal void CopyExtensionsFrom (AddinRegistry other) + { + database.CopyExtensions (other.database); + } + + internal Addin GetAddinForHostAssembly (string filePath) + { + if (currentDomain == AddinDatabase.UnknownDomain) + return null; + return database.GetAddinForHostAssembly (currentDomain, filePath); + } + + internal bool AddinDependsOn (string id1, string id2) + { + return database.AddinDependsOn (currentDomain, id1, id2); + } + + internal void ScanFolders (IProgressStatus monitor, string folderToScan, StringCollection filesToIgnore) + { + database.ScanFolders (monitor, currentDomain, folderToScan, filesToIgnore); + } + + internal void ParseAddin (IProgressStatus progressStatus, string file, string outFile) + { + database.ParseAddin (progressStatus, currentDomain, file, outFile, true); + } + + /// + /// Gets the default add-ins folder of the registry. + /// + /// + /// For every add-in registry there is an add-in folder where the registry will look for add-ins by default. + /// This folder is an "addins" subdirectory of the directory where the repository is located. In most cases, + /// this folder will only contain .addins files referencing other more convenient locations for add-ins. + /// + public string DefaultAddinsFolder { + get { return addinsDir; } + } + + internal string AddinCachePath { + get { return databaseDir; } + } + + internal StringCollection GlobalAddinDirectories { + get { return addinDirs; } + } + + internal string StartupDirectory { + get { + return startupDirectory; + } + } + + internal bool CreateHostAddinsFile (string hostFile) + { + hostFile = Path.GetFullPath (hostFile); + string baseName = Path.GetFileNameWithoutExtension (hostFile); + if (!Directory.Exists (database.HostsPath)) + Directory.CreateDirectory (database.HostsPath); + + foreach (string s in Directory.GetFiles (database.HostsPath, baseName + "*.addins")) { + try { + using (StreamReader sr = new StreamReader (s)) { + XmlTextReader tr = new XmlTextReader (sr); + tr.MoveToContent (); + string host = tr.GetAttribute ("host-reference"); + if (host == hostFile) + return false; + } + } + catch { + // Ignore this file + } + } + + string file = Path.Combine (database.HostsPath, baseName) + ".addins"; + int n=1; + while (File.Exists (file)) { + file = Path.Combine (database.HostsPath, baseName) + "_" + n + ".addins"; + n++; + } + + using (StreamWriter sw = new StreamWriter (file)) { + XmlTextWriter tw = new XmlTextWriter (sw); + tw.Formatting = Formatting.Indented; + tw.WriteStartElement ("Addins"); + tw.WriteAttributeString ("host-reference", hostFile); + tw.WriteStartElement ("Directory"); + tw.WriteAttributeString ("shared", "false"); + tw.WriteString (Path.GetDirectoryName (hostFile)); + tw.WriteEndElement (); + tw.Close (); + } + return true; + } + +#pragma warning disable 1591 + [Obsolete] + public static string[] GetRegisteredStartupFolders (string registryPath) + { + string dbDir = Path.Combine (registryPath, "addin-db-" + AddinDatabase.VersionTag); + dbDir = Path.Combine (dbDir, "hosts"); + + if (!Directory.Exists (dbDir)) + return new string [0]; + + ArrayList dirs = new ArrayList (); + + foreach (string s in Directory.GetFiles (dbDir, "*.addins")) { + try { + using (StreamReader sr = new StreamReader (s)) { + XmlTextReader tr = new XmlTextReader (sr); + tr.MoveToContent (); + string host = tr.GetAttribute ("host-reference"); + host = Path.GetDirectoryName (host); + if (!dirs.Contains (host)) + dirs.Add (host); + } + } + catch { + // Ignore this file + } + } + return (string[]) dirs.ToArray (typeof(string)); + } +#pragma warning restore 1591 + } + + /// + /// Addin search flags. + /// + [Flags] + public enum AddinSearchFlags + { + /// + /// Add-ins are included in the search + /// + IncludeAddins = 1, + /// + /// Add-in roots are included in the search + /// + IncludeRoots = 1 << 1, + /// + /// Both add-in and add-in roots are included in the search + /// + IncludeAll = IncludeAddins | IncludeRoots, + /// + /// Only the latest version of every add-in or add-in root is included in the search + /// + LatestVersionsOnly = 1 << 3 + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinRootAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinRootAttribute.cs new file mode 100644 index 00000000..2732f209 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinRootAttribute.cs @@ -0,0 +1,74 @@ +// +// AddinRootAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Marks an assembly as being an add-in root. + /// + /// + /// An add-in root is an assembly which can be extended by add-ins. + /// + [AttributeUsage (AttributeTargets.Assembly)] + public class AddinRootAttribute: AddinAttribute + { + /// + /// Initializes a new instance + /// + public AddinRootAttribute () + { + } + + /// + /// Initializes a new instance + /// + /// + /// Identifier of the add-in root + /// + public AddinRootAttribute (string id): base (id) + { + } + + /// + /// Initializes a new instance + /// + /// + /// Identifier of the add-in root + /// + /// + /// Version of the add-in root + /// + public AddinRootAttribute (string id, string version): base (id, version) + { + } + + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/AddinUrlAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/AddinUrlAttribute.cs new file mode 100644 index 00000000..6a36aea9 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/AddinUrlAttribute.cs @@ -0,0 +1,53 @@ +// +// AddinUrlAttribute.cs +// +// Author: +// Lluis Sanchez +// +// Copyright (c) 2013 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace Mono.Addins +{ + /// + /// Addin URL attribute. + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=false)] + public class AddinUrlAttribute: Attribute + { + /// + /// Initializes the attribute + /// + /// + /// Url of the add-in + /// + public AddinUrlAttribute (string url) + { + this.Url = url; + } + + /// + /// Url of the add-in + /// + public string Url { get; set; } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/ConditionType.cs b/mono-addins/Mono.Addins/Mono.Addins/ConditionType.cs new file mode 100644 index 00000000..3c674715 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ConditionType.cs @@ -0,0 +1,236 @@ +// +// ConditionType.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Xml; +using Mono.Addins.Description; +using System.Collections; + +namespace Mono.Addins +{ + /// + /// A condition evaluator. + /// + /// + /// Add-ins may use conditions to register nodes in an extension point which + /// are only visible under some contexts. For example, an add-in registering + /// a custom menu option to the main menu of a sample text editor might want + /// to make that option visible only for some kind of files. To allow add-ins + /// to do this kind of check, the host application needs to define a new condition. + /// + public abstract class ConditionType + { + internal event EventHandler Changed; + string id; + + /// + /// Evaluates the condition. + /// + /// + /// Condition node information. + /// + /// + /// 'true' if the condition is satisfied. + /// + public abstract bool Evaluate (NodeElement conditionNode); + + /// + /// Notifies that the condition has changed, and that it has to be re-evaluated. + /// + /// This method must be called when there is a change in the state that determines + /// the result of the evaluation. When this method is called, all node conditions + /// depending on it are reevaluated and the corresponding events for adding or + /// removing extension nodes are fired. + /// + /// + public void NotifyChanged () + { + if (Changed != null) + Changed (this, EventArgs.Empty); + } + + internal string Id { + get { return id; } + set { id = value; } + } + } + + internal class BaseCondition + { + BaseCondition parent; + + internal BaseCondition (BaseCondition parent) + { + this.parent = parent; + } + + public virtual bool Evaluate (ExtensionContext ctx) + { + return parent == null || parent.Evaluate (ctx); + } + + internal virtual void GetConditionTypes (ArrayList listToFill) + { + } + } + + internal class NullCondition: BaseCondition + { + public NullCondition (): base (null) + { + } + + public override bool Evaluate (ExtensionContext ctx) + { + return false; + } + } + + class OrCondition: BaseCondition + { + BaseCondition[] conditions; + + public OrCondition (BaseCondition[] conditions, BaseCondition parent): base (parent) + { + this.conditions = conditions; + } + + public override bool Evaluate (ExtensionContext ctx) + { + if (!base.Evaluate (ctx)) + return false; + foreach (BaseCondition cond in conditions) + if (cond.Evaluate (ctx)) + return true; + return false; + } + + internal override void GetConditionTypes (ArrayList listToFill) + { + foreach (BaseCondition cond in conditions) + cond.GetConditionTypes (listToFill); + } + } + + class AndCondition: BaseCondition + { + BaseCondition[] conditions; + + public AndCondition (BaseCondition[] conditions, BaseCondition parent): base (parent) + { + this.conditions = conditions; + } + + public override bool Evaluate (ExtensionContext ctx) + { + if (!base.Evaluate (ctx)) + return false; + foreach (BaseCondition cond in conditions) + if (!cond.Evaluate (ctx)) + return false; + return true; + } + + internal override void GetConditionTypes (ArrayList listToFill) + { + foreach (BaseCondition cond in conditions) + cond.GetConditionTypes (listToFill); + } + } + + class NotCondition: BaseCondition + { + BaseCondition baseCond; + + public NotCondition (BaseCondition baseCond, BaseCondition parent): base (parent) + { + this.baseCond = baseCond; + } + + public override bool Evaluate (ExtensionContext ctx) + { + return !baseCond.Evaluate (ctx); + } + + internal override void GetConditionTypes (System.Collections.ArrayList listToFill) + { + baseCond.GetConditionTypes (listToFill); + } + } + + + internal sealed class Condition: BaseCondition + { + ExtensionNodeDescription node; + string typeId; + AddinEngine addinEngine; + string addin; + + internal const string SourceAddinAttribute = "__sourceAddin"; + + internal Condition (AddinEngine addinEngine, ExtensionNodeDescription element, BaseCondition parent): base (parent) + { + this.addinEngine = addinEngine; + typeId = element.GetAttribute ("id"); + addin = element.GetAttribute (SourceAddinAttribute); + node = element; + } + + public override bool Evaluate (ExtensionContext ctx) + { + if (!base.Evaluate (ctx)) + return false; + + if (!string.IsNullOrEmpty (addin)) { + // Make sure the add-in that implements the condition is loaded + addinEngine.LoadAddin (null, addin, true); + addin = null; // Don't try again + } + + ConditionType type = ctx.GetCondition (typeId); + if (type == null) { + addinEngine.ReportError ("Condition '" + typeId + "' not found in current extension context.", null, null, false); + return false; + } + + try { + return type.Evaluate (node); + } + catch (Exception ex) { + addinEngine.ReportError ("Error while evaluating condition '" + typeId + "'", null, ex, false); + return false; + } + } + + internal override void GetConditionTypes (ArrayList listToFill) + { + listToFill.Add (typeId); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ConsoleProgressStatus.cs b/mono-addins/Mono.Addins/Mono.Addins/ConsoleProgressStatus.cs new file mode 100644 index 00000000..092028e6 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ConsoleProgressStatus.cs @@ -0,0 +1,176 @@ +// +// ConsoleProgressStatus.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// An IProgressStatus class which writes output to the console. + /// + public class ConsoleProgressStatus: MarshalByRefObject, IProgressStatus + { + bool canceled; + int logLevel; + + /// + /// Initializes a new instance + /// + /// + /// Set to true to enabled verbose log + /// + public ConsoleProgressStatus (bool verboseLog) + { + if (verboseLog) + logLevel = 2; + else + logLevel = 1; + } + + /// + /// Initializes a new instance + /// + /// + /// Verbosity level. 0: not verbose, 1: normal, >1 extra verbose + /// + public ConsoleProgressStatus (int logLevel) + { + this.logLevel = logLevel; + } + + /// + /// Sets the description of the current operation. + /// + /// + /// A message + /// + /// + /// This method is called by the add-in engine to show a description of the operation being monitorized. + /// + public void SetMessage (string msg) + { + } + + /// + /// Sets the progress of the operation. + /// + /// + /// A number between 0 and 1. 0 means no progress, 1 means operation completed. + /// + /// + /// This method is called by the add-in engine to show the progress of the operation being monitorized. + /// + public void SetProgress (double progress) + { + } + + /// + /// Writes text to the log. + /// + /// + /// Message to write + /// + public void Log (string msg) + { + Console.WriteLine (msg); + } + + /// + /// Reports a warning. + /// + /// + /// Warning message + /// + /// + /// This method is called by the add-in engine to report a warning in the operation being monitorized. + /// + public void ReportWarning (string message) + { + if (logLevel > 0) + Console.WriteLine ("WARNING: " + message); + } + + /// + /// Reports an error. + /// + /// + /// Error message + /// + /// + /// Exception that caused the error. It can be null. + /// + /// + /// This method is called by the add-in engine to report an error occurred while executing the operation being monitorized. + /// + public void ReportError (string message, Exception exception) + { + if (logLevel == 0) + return; + Console.Write ("ERROR: "); + if (logLevel > 1) { + if (message != null) + Console.WriteLine (message); + if (exception != null) + Console.WriteLine (exception); + } else { + if (message != null && exception != null) + Console.WriteLine (message + " (" + exception.Message + ")"); + else { + if (message != null) + Console.WriteLine (message); + if (exception != null) + Console.WriteLine (exception.Message); + } + } + } + + /// + /// Returns True when the user requested to cancel this operation + /// + public bool IsCanceled { + get { return canceled; } + } + + /// + /// Log level requested by the user: 0: no log, 1: normal log, >1 verbose log + /// + public int LogLevel { + get { return logLevel; } + } + + /// + /// Cancels the operation being montorized. + /// + public void Cancel () + { + canceled = true; + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/ContentType.cs b/mono-addins/Mono.Addins/Mono.Addins/ContentType.cs new file mode 100644 index 00000000..909e77c4 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ContentType.cs @@ -0,0 +1,53 @@ +// +// ContentType.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2011 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; + +namespace Mono.Addins +{ + /// + /// Type of the content of a string extension node attribute + /// + public enum ContentType + { + /// + /// Plain text + /// + Text, + /// + /// A class name + /// + Class, + /// + /// A resource name + /// + Resource, + /// + /// A file name + /// + File + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/CustomConditionAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/CustomConditionAttribute.cs new file mode 100644 index 00000000..f2b0b8e6 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/CustomConditionAttribute.cs @@ -0,0 +1,43 @@ +// +// CustomConditionAttribute.cs +// +// Copyright (c) Microsoft Corp. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Base class for custom condition attributes. + /// + /// + /// Custom condition attributes can be used to apply conditions to extensions. + /// All custom condition attributes must subclass CustomConditionAttribute. + /// All arguments and properties must be tagged with NodeAttribute. + /// The ID of the condition is the simple name of this class without the "Attribute" + /// or "ConditionAttribute" suffix. For example "FooConditionAttribute" maps to the + /// condition ID "Foo" and "BarAttribute" maps to "Bar". + /// + [AttributeUsage (AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public abstract class CustomConditionAttribute : Attribute + { + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/CustomExtensionAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/CustomExtensionAttribute.cs new file mode 100644 index 00000000..d500190a --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/CustomExtensionAttribute.cs @@ -0,0 +1,104 @@ +// +// CustomExtensionAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Base class for custom extension attributes. + /// + /// + /// Custom extension attributes can be used to declare extensions with custom metadata. + /// All custom extension attributes must subclass CustomExtensionAttribute. + /// + public class CustomExtensionAttribute: Attribute + { + string id; + string insertBefore; + string insertAfter; + string path; + + internal const string PathFieldKey = "__path"; + + /// + /// Identifier of the node + /// + [NodeAttributeAttribute ("id")] + public string Id { + get { return id; } + set { id = value; } + } + + /// + /// Identifier of the node before which this node has to be placed + /// + [NodeAttributeAttribute ("insertbefore")] + public string InsertBefore { + get { return insertBefore; } + set { insertBefore = value; } + } + + /// + /// Identifier of the node after which this node has to be placed + /// + [NodeAttributeAttribute ("insertafter")] + public string InsertAfter { + get { return insertAfter; } + set { insertAfter = value; } + } + + /// + /// Path of the extension point being extended. + /// + /// + /// This property is optional and useful only when there are several extension points which allow + /// using this custom attribute to define extensions. + /// + [NodeAttributeAttribute ("__path")] + public string Path { + get { return path; } + set { path = value; } + } + + /// + /// The extension node bound to this attribute + /// + public ExtensionNode ExtensionNode { get; internal set; } + + + /// + /// The add-in that registered this extension node. + /// + /// + /// This property provides access to the resources and types of the add-in that created this extension node. + /// + public RuntimeAddin Addin { + get { return ExtensionNode?.Addin; } + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionAttribute.cs new file mode 100644 index 00000000..e70f6bf8 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionAttribute.cs @@ -0,0 +1,166 @@ +// +// ExtensionAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Declares a type extension. + /// + /// + /// When applied to a class, specifies that the class is an extension + /// class to be registered in a matching extension point. + /// + [AttributeUsage (AttributeTargets.Class, AllowMultiple=true)] + public class ExtensionAttribute: Attribute + { + string path; + string nodeName; + string id; + string insertBefore; + string insertAfter; + string typeName; + Type type; + + /// + /// Initializes a new instance of the ExtensionAttribute class. + /// + public ExtensionAttribute () + { + } + + /// + /// Initializes a new instance + /// + /// + /// Path of the extension point. + /// + /// The path is only required if there are several extension points defined for the same type. + public ExtensionAttribute (string path) + { + this.path = path; + } + + /// + /// Initializes a new instance + /// + /// + /// Type defining the extension point being extended + /// + /// + /// This constructor can be used to explicitly specify the type that defines the extension point + /// to be extended. By default, Mono.Addins will try to find any extension point defined in any + /// of the base classes or interfaces. The type parameter can be used when there is more than one + /// base type providing an extension point. + /// + public ExtensionAttribute (Type type) + { + Type = type; + } + + /// + /// Path of the extension point being extended + /// + /// + /// The path is only required if there are several extension points defined for the same type. + /// + public string Path { + get { return path ?? string.Empty; } + set { path = value; } + } + + /// + /// Name of the extension node + /// + /// + /// Extension points may require extensions to use a specific node name. + /// This is needed when an extension point may contain several different types of nodes. + /// + public string NodeName { + get { return !string.IsNullOrEmpty (nodeName) ? nodeName : "Type"; } + set { nodeName = value; } + } + + /// + /// Identifier of the extension node. + /// + /// + /// The ExtensionAttribute.InsertAfter and ExtensionAttribute.InsertBefore + /// properties can be used to specify the relative location of a node. The nodes + /// referenced in those properties must be defined either in the add-in host + /// being extended, or in any add-in on which this add-in depends. + /// + public string Id { + get { return id ?? string.Empty; } + set { id = value; } + } + + /// + /// Identifier of the extension node before which this node has to be added in the extension point. + /// + /// + /// The ExtensionAttribute.InsertAfter and ExtensionAttribute.InsertBefore + /// properties can be used to specify the relative location of a node. The nodes + /// referenced in those properties must be defined either in the add-in host + /// being extended, or in any add-in on which this add-in depends. + /// + public string InsertBefore { + get { return insertBefore ?? string.Empty; } + set { insertBefore = value; } + } + + /// + /// Identifier of the extension node after which this node has to be added in the extension point. + /// + public string InsertAfter { + get { return insertAfter ?? string.Empty; } + set { insertAfter = value; } + } + + /// + /// Type defining the extension point being extended + /// + /// + /// This property can be used to explicitly specify the type that defines the extension point + /// to be extended. By default, Mono.Addins will try to find any extension point defined in any + /// of the base classes or interfaces. This property can be used when there is more than one + /// base type providing an extension point. + /// + public Type Type { + get { return type; } + set { type = value; typeName = type.FullName; } + } + + internal string TypeName { + get { return typeName ?? string.Empty; } + set { typeName = value; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionAttributeAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionAttributeAttribute.cs new file mode 100644 index 00000000..4c77851c --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionAttributeAttribute.cs @@ -0,0 +1,137 @@ +// +// ExtensionAttributeAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Assigns an attribute value to an extension + /// + /// + /// This attribute can be used together with the [Extension] attribute to specify + /// a value for an attribute of the extension. + /// + public class ExtensionAttributeAttribute: Attribute + { + Type targetType; + string targetTypeName; + string name; + string val; + string path; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Name of the attribute + /// + /// + /// Value of the attribute + /// + public ExtensionAttributeAttribute (string name, string value) + { + Name = name; + Value = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Type of the extension for which the attribute value is being set + /// + /// + /// Name of the attribute + /// + /// + /// Value of the attribute + /// + public ExtensionAttributeAttribute (Type type, string name, string value) + { + Name = name; + Value = value; + Type = type; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Path of the extension for which the attribute value is being set + /// + /// + /// Name of the attribute + /// + /// + /// Value of the attribute + /// + public ExtensionAttributeAttribute (string path, string name, string value) + { + Name = name; + Value = value; + Path = path; + } + + /// + /// Name of the attribute + /// + public string Name { + get { return this.name; } + set { this.name = value; } + } + + /// + /// Value of the attribute + /// + public string Value { + get { return this.val; } + set { this.val = value; } + } + + /// + /// Path of the extension for which the attribute value is being set + /// + public string Path { + get { return this.path; } + set { this.path = value; } + } + + /// + /// Type of the extension for which the attribute value is being set + /// + public Type Type { + get { return targetType; } + set { targetType = value; targetTypeName = targetType.FullName; } + } + + internal string TypeName { + get { return targetTypeName ?? string.Empty; } + set { targetTypeName = value; } + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionContext.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionContext.cs new file mode 100644 index 00000000..ddb30564 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionContext.cs @@ -0,0 +1,1357 @@ +// +// ExtensionContext.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using Mono.Addins.Description; + +namespace Mono.Addins +{ + /// + /// An extension context. + /// + /// + /// Extension contexts can be used to query the extension tree + /// using particular condition values. Extension points which + /// declare the availability of a condition type can only be + /// queryed using an extension context which provides an + /// evaluator for that condition. + /// + public class ExtensionContext + { + internal object LocalLock = new object (); + + Hashtable conditionTypes = new Hashtable (); + Hashtable conditionsToNodes = new Hashtable (); + List childContexts; + ExtensionContext parentContext; + ExtensionTree tree; + bool fireEvents = false; + + ArrayList runTimeEnabledAddins; + ArrayList runTimeDisabledAddins; + + /// + /// Extension change event. + /// + /// + /// This event is fired when any extension point in the add-in system changes. + /// The event args object provides the path of the changed extension, although + /// it does not provide information about what changed. Hosts subscribing to + /// this event should get the new list of nodes using a query method such as + /// AddinManager.GetExtensionNodes() and then update whatever needs to be updated. + /// + public event ExtensionEventHandler ExtensionChanged; + + internal void Initialize (AddinEngine addinEngine) + { + fireEvents = false; + tree = new ExtensionTree (addinEngine, this); + } + +#pragma warning disable 1591 + [ObsoleteAttribute] + protected void Clear () + { + } +#pragma warning restore 1591 + + + internal void ClearContext () + { + conditionTypes.Clear (); + conditionsToNodes.Clear (); + childContexts = null; + parentContext = null; + tree = null; + runTimeEnabledAddins = null; + runTimeDisabledAddins = null; + } + + internal AddinEngine AddinEngine { + get { return tree.AddinEngine; } + } + + void CleanDisposedChildContexts () + { + if (childContexts != null) + childContexts.RemoveAll (w => w.Target == null); + } + + internal virtual void ResetCachedData () + { + tree.ResetCachedData (); + if (childContexts != null) { + foreach (WeakReference wref in childContexts) { + ExtensionContext ctx = wref.Target as ExtensionContext; + if (ctx != null) + ctx.ResetCachedData (); + } + } + } + + internal ExtensionContext CreateChildContext () + { + lock (conditionTypes) { + if (childContexts == null) + childContexts = new List (); + else + CleanDisposedChildContexts (); + ExtensionContext ctx = new ExtensionContext (); + ctx.Initialize (AddinEngine); + ctx.parentContext = this; + WeakReference wref = new WeakReference (ctx); + childContexts.Add (wref); + return ctx; + } + } + + /// + /// Registers a new condition in the extension context. + /// + /// + /// Identifier of the condition. + /// + /// + /// Condition evaluator. + /// + /// + /// The registered condition will be particular to this extension context. + /// Any event that might be fired as a result of changes in the condition will + /// only be fired in this context. + /// + public void RegisterCondition (string id, ConditionType type) + { + type.Id = id; + ConditionInfo info = CreateConditionInfo (id); + ConditionType ot = info.CondType as ConditionType; + if (ot != null) + ot.Changed -= new EventHandler (OnConditionChanged); + info.CondType = type; + type.Changed += new EventHandler (OnConditionChanged); + } + + /// + /// Registers a new condition in the extension context. + /// + /// + /// Identifier of the condition. + /// + /// + /// Type of the condition evaluator. Must be a subclass of Mono.Addins.ConditionType. + /// + /// + /// The registered condition will be particular to this extension context. Any event + /// that might be fired as a result of changes in the condition will only be fired in this context. + /// + public void RegisterCondition (string id, Type type) + { + // Allows delayed creation of condition types + ConditionInfo info = CreateConditionInfo (id); + ConditionType ot = info.CondType as ConditionType; + if (ot != null) + ot.Changed -= new EventHandler (OnConditionChanged); + info.CondType = type; + } + + ConditionInfo CreateConditionInfo (string id) + { + ConditionInfo info = conditionTypes [id] as ConditionInfo; + if (info == null) { + info = new ConditionInfo (); + conditionTypes [id] = info; + } + return info; + } + + internal bool FireEvents { + get { return fireEvents; } + } + + internal ConditionType GetCondition (string id) + { + ConditionType ct; + ConditionInfo info = (ConditionInfo) conditionTypes [id]; + + if (info != null) { + if (info.CondType is Type) { + // The condition was registered as a type, create an instance now + ct = (ConditionType) Activator.CreateInstance ((Type)info.CondType); + ct.Id = id; + ct.Changed += new EventHandler (OnConditionChanged); + info.CondType = ct; + } + else + ct = info.CondType as ConditionType; + + if (ct != null) + return ct; + } + + if (parentContext != null) + return parentContext.GetCondition (id); + else + return null; + } + + internal void RegisterNodeCondition (TreeNode node, BaseCondition cond) + { + ArrayList list = (ArrayList) conditionsToNodes [cond]; + if (list == null) { + list = new ArrayList (); + conditionsToNodes [cond] = list; + ArrayList conditionTypeIds = new ArrayList (); + cond.GetConditionTypes (conditionTypeIds); + + foreach (string cid in conditionTypeIds) { + + // Make sure the condition is properly created + GetCondition (cid); + + ConditionInfo info = CreateConditionInfo (cid); + if (info.BoundConditions == null) + info.BoundConditions = new ArrayList (); + + info.BoundConditions.Add (cond); + } + } + list.Add (node); + } + + internal void UnregisterNodeCondition (TreeNode node, BaseCondition cond) + { + ArrayList list = (ArrayList) conditionsToNodes [cond]; + if (list == null) + return; + + list.Remove (node); + if (list.Count == 0) { + conditionsToNodes.Remove (cond); + ArrayList conditionTypeIds = new ArrayList (); + cond.GetConditionTypes (conditionTypeIds); + foreach (string cid in conditionTypes.Keys) { + ConditionInfo info = conditionTypes [cid] as ConditionInfo; + if (info != null && info.BoundConditions != null) + info.BoundConditions.Remove (cond); + } + } + } + + /// + /// Returns the extension node in a path + /// + /// + /// Location of the node. + /// + /// + /// The node, or null if not found. + /// + public ExtensionNode GetExtensionNode (string path) + { + TreeNode node = GetNode (path); + if (node == null) + return null; + + if (node.Condition == null || node.Condition.Evaluate (this)) + return node.ExtensionNode; + else + return null; + } + + /// + /// Returns the extension node in a path + /// + /// + /// Location of the node. + /// + /// + /// The node, or null if not found. + /// + public T GetExtensionNode (string path) where T: ExtensionNode + { + return (T) GetExtensionNode (path); + } + + /// + /// Gets extension nodes registered in a path. + /// + /// + /// An extension path.> + /// + /// + /// All nodes registered in the provided path. + /// + public ExtensionNodeList GetExtensionNodes (string path) + { + return GetExtensionNodes (path, null); + } + + /// + /// Gets extension nodes registered in a path. + /// + /// + /// An extension path. + /// + /// + /// A list of nodes + /// + /// + /// This method returns all nodes registered under the provided path. + /// It will throw a InvalidOperationException if the type of one of + /// the registered nodes is not assignable to the provided type. + /// + public ExtensionNodeList GetExtensionNodes (string path) where T: ExtensionNode + { + ExtensionNodeList nodes = GetExtensionNodes (path, typeof(T)); + return new ExtensionNodeList (nodes.list); + } + + /// + /// Gets extension nodes for a type extension point + /// + /// + /// Type defining the extension point + /// + /// + /// A list of nodes + /// + /// + /// This method returns all extension nodes bound to the provided type. + /// + public ExtensionNodeList GetExtensionNodes (Type instanceType) + { + return GetExtensionNodes (instanceType, typeof(ExtensionNode)); + } + + /// + /// Gets extension nodes for a type extension point + /// + /// + /// Type defining the extension point + /// + /// + /// Expected extension node type + /// + /// + /// A list of nodes + /// + /// + /// This method returns all nodes registered for the provided type. + /// It will throw a InvalidOperationException if the type of one of + /// the registered nodes is not assignable to the provided node type. + /// + public ExtensionNodeList GetExtensionNodes (Type instanceType, Type expectedNodeType) + { + string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType); + if (path == null) + return new ExtensionNodeList (null); + return GetExtensionNodes (path, expectedNodeType); + } + + /// + /// Gets extension nodes for a type extension point + /// + /// + /// Type defining the extension point + /// + /// + /// A list of nodes + /// + /// + /// This method returns all nodes registered for the provided type. + /// It will throw a InvalidOperationException if the type of one of + /// the registered nodes is not assignable to the specified node type argument. + /// + public ExtensionNodeList GetExtensionNodes (Type instanceType) where T: ExtensionNode + { + string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType); + if (path == null) + return new ExtensionNodeList (null); + return new ExtensionNodeList (GetExtensionNodes (path, typeof (T)).list); + } + + /// + /// Gets extension nodes registered in a path. + /// + /// + /// An extension path. + /// + /// + /// Expected node type. + /// + /// + /// A list of nodes + /// + /// + /// This method returns all nodes registered under the provided path. + /// It will throw a InvalidOperationException if the type of one of + /// the registered nodes is not assignable to the provided type. + /// + public ExtensionNodeList GetExtensionNodes (string path, Type expectedNodeType) + { + TreeNode node = GetNode (path); + if (node == null || node.ExtensionNode == null) + return ExtensionNodeList.Empty; + + ExtensionNodeList list = node.ExtensionNode.ChildNodes; + + if (expectedNodeType != null) { + bool foundError = false; + foreach (ExtensionNode cnode in list) { + if (!expectedNodeType.IsInstanceOfType (cnode)) { + foundError = true; + AddinEngine.ReportError ("Error while getting nodes for path '" + path + "'. Expected subclass of node type '" + expectedNodeType + "'. Found '" + cnode.GetType (), null, null, false); + } + } + if (foundError) { + // Create a new list excluding the elements that failed the test + List newList = new List (); + foreach (ExtensionNode cnode in list) { + if (expectedNodeType.IsInstanceOfType (cnode)) + newList.Add (cnode); + } + return new ExtensionNodeList (newList); + } + } + return list; + } + + /// + /// Gets extension objects registered for a type extension point. + /// + /// + /// Type defining the extension point + /// + /// + /// A list of objects + /// + public object[] GetExtensionObjects (Type instanceType) + { + return GetExtensionObjects (instanceType, true); + } + + /// + /// Gets extension objects registered for a type extension point. + /// + /// + /// A list of objects + /// + /// + /// The type argument of this generic method is the type that defines + /// the extension point. + /// + public T[] GetExtensionObjects () + { + return GetExtensionObjects (true); + } + + /// + /// Gets extension objects registered for a type extension point. + /// + /// + /// Type defining the extension point + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// A list of extension objects. + /// + public object[] GetExtensionObjects (Type instanceType, bool reuseCachedInstance) + { + string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType); + if (path == null) + return (object[]) Array.CreateInstance (instanceType, 0); + return GetExtensionObjects (path, instanceType, reuseCachedInstance); + } + + /// + /// Gets extension objects registered for a type extension point. + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// A list of extension objects. + /// + /// + /// The type argument of this generic method is the type that defines + /// the extension point. + /// + public T[] GetExtensionObjects (bool reuseCachedInstance) + { + string path = AddinEngine.GetAutoTypeExtensionPoint (typeof(T)); + if (path == null) + return new T[0]; + return GetExtensionObjects (path, reuseCachedInstance); + } + + /// + /// Gets extension objects registered in a path + /// + /// + /// An extension path. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node. + /// + public object[] GetExtensionObjects (string path) + { + return GetExtensionObjects (path, typeof(object), true); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node (or TypeExtensionNode.GetInstance() if + /// reuseCachedInstance is set to true) + /// + public object[] GetExtensionObjects (string path, bool reuseCachedInstance) + { + return GetExtensionObjects (path, typeof(object), reuseCachedInstance); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// Type of the return array elements. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node. + /// + /// An InvalidOperationException exception is thrown if one of the found + /// objects is not a subclass of the provided type. + /// + public object[] GetExtensionObjects (string path, Type arrayElementType) + { + return GetExtensionObjects (path, arrayElementType, true); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node. + /// + /// An InvalidOperationException exception is thrown if one of the found + /// objects is not a subclass of the provided type. + /// + public T[] GetExtensionObjects (string path) + { + return GetExtensionObjects (path, true); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node (or TypeExtensionNode.GetInstance() if + /// reuseCachedInstance is set to true). + /// + /// An InvalidOperationException exception is thrown if one of the found + /// objects is not a subclass of the provided type. + /// + public T[] GetExtensionObjects (string path, bool reuseCachedInstance) + { + ExtensionNode node = GetExtensionNode (path); + if (node == null) + throw new InvalidOperationException ("Extension node not found in path: " + path); + return node.GetChildObjects (reuseCachedInstance); + } + + /// + /// Gets extension objects registered in a path. + /// + /// + /// An extension path. + /// + /// + /// Type of the return array elements. + /// + /// + /// When set to True, it will return instances created in previous calls. + /// + /// + /// An array of objects registered in the path. + /// + /// + /// This method can only be used if all nodes in the provided extension path + /// are of type Mono.Addins.TypeExtensionNode. The returned array is composed + /// by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node (or TypeExtensionNode.GetInstance() if + /// reuseCachedInstance is set to true). + /// + /// An InvalidOperationException exception is thrown if one of the found + /// objects is not a subclass of the provided type. + /// + public object[] GetExtensionObjects (string path, Type arrayElementType, bool reuseCachedInstance) + { + ExtensionNode node = GetExtensionNode (path); + if (node == null) + throw new InvalidOperationException ("Extension node not found in path: " + path); + return node.GetChildObjects (arrayElementType, reuseCachedInstance); + } + + /// + /// Register a listener of extension node changes. + /// + /// + /// Path of the node. + /// + /// + /// A handler method. + /// + /// + /// Hosts can call this method to be subscribed to an extension change + /// event for a specific path. The event will be fired once for every + /// individual node change. The event arguments include the change type + /// (Add or Remove) and the extension node added or removed. + /// + /// NOTE: The handler will be called for all nodes existing in the path at the moment of registration. + /// + public void AddExtensionNodeHandler (string path, ExtensionNodeEventHandler handler) + { + ExtensionNode node = GetExtensionNode (path); + if (node == null) + throw new InvalidOperationException ("Extension node not found in path: " + path); + node.ExtensionNodeChanged += handler; + } + + /// + /// Unregister a listener of extension node changes. + /// + /// + /// Path of the node. + /// + /// + /// A handler method. + /// + /// + /// This method unregisters a delegate from the node change event of a path. + /// + public void RemoveExtensionNodeHandler (string path, ExtensionNodeEventHandler handler) + { + ExtensionNode node = GetExtensionNode (path); + if (node == null) + throw new InvalidOperationException ("Extension node not found in path: " + path); + node.ExtensionNodeChanged -= handler; + } + + /// + /// Register a listener of extension node changes. + /// + /// + /// Type defining the extension point + /// + /// + /// A handler method. + /// + /// + /// Hosts can call this method to be subscribed to an extension change + /// event for a specific type extension point. The event will be fired once for every + /// individual node change. The event arguments include the change type + /// (Add or Remove) and the extension node added or removed. + /// + /// NOTE: The handler will be called for all nodes existing in the path at the moment of registration. + /// + public void AddExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler) + { + string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType); + if (path == null) + throw new InvalidOperationException ("Type '" + instanceType + "' not bound to an extension point."); + AddExtensionNodeHandler (path, handler); + } + + /// + /// Unregister a listener of extension node changes. + /// + /// + /// Type defining the extension point + /// + /// + /// A handler method. + /// + public void RemoveExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler) + { + string path = AddinEngine.GetAutoTypeExtensionPoint (instanceType); + if (path == null) + throw new InvalidOperationException ("Type '" + instanceType + "' not bound to an extension point."); + RemoveExtensionNodeHandler (path, handler); + } + + void OnConditionChanged (object s, EventArgs a) + { + ConditionType cond = (ConditionType) s; + NotifyConditionChanged (cond); + } + + internal void NotifyConditionChanged (ConditionType cond) + { + try { + fireEvents = true; + + ConditionInfo info = (ConditionInfo) conditionTypes [cond.Id]; + if (info != null && info.BoundConditions != null) { + Hashtable parentsToNotify = new Hashtable (); + foreach (BaseCondition c in info.BoundConditions) { + ArrayList nodeList = (ArrayList) conditionsToNodes [c]; + if (nodeList != null) { + foreach (TreeNode node in nodeList) + parentsToNotify [node.Parent] = null; + } + } + foreach (TreeNode node in parentsToNotify.Keys) { + if (node.NotifyChildrenChanged ()) + NotifyExtensionsChanged (new ExtensionEventArgs (node.GetPath ())); + } + } + } + finally { + fireEvents = false; + } + + // Notify child contexts + lock (conditionTypes) { + if (childContexts != null) { + CleanDisposedChildContexts (); + foreach (WeakReference wref in childContexts) { + ExtensionContext ctx = wref.Target as ExtensionContext; + if (ctx != null) + ctx.NotifyConditionChanged (cond); + } + } + } + } + + + internal void NotifyExtensionsChanged (ExtensionEventArgs args) + { + if (!fireEvents) + return; + + if (ExtensionChanged != null) + ExtensionChanged (this, args); + } + + internal void NotifyAddinLoaded (RuntimeAddin ad) + { + tree.NotifyAddinLoaded (ad, true); + + lock (conditionTypes) { + if (childContexts != null) { + CleanDisposedChildContexts (); + foreach (WeakReference wref in childContexts) { + ExtensionContext ctx = wref.Target as ExtensionContext; + if (ctx != null) + ctx.NotifyAddinLoaded (ad); + } + } + } + } + + internal void CreateExtensionPoint (ExtensionPoint ep) + { + TreeNode node = tree.GetNode (ep.Path, true); + if (node.ExtensionPoint == null) { + node.ExtensionPoint = ep; + node.ExtensionNodeSet = ep.NodeSet; + } + } + + internal void ActivateAddinExtensions (string id) + { + // Looks for loaded extension points which are extended by the provided + // add-in, and adds the new nodes + + try { + fireEvents = true; + + Addin addin = AddinEngine.Registry.GetAddin (id); + if (addin == null) { + AddinEngine.ReportError ("Required add-in not found", id, null, false); + return; + } + // Take note that this add-in has been enabled at run-time + // Needed because loaded add-in descriptions may not include this add-in. + RegisterRuntimeEnabledAddin (id); + + // Look for loaded extension points + Hashtable eps = new Hashtable (); + ArrayList newExtensions = new ArrayList (); + foreach (ModuleDescription mod in addin.Description.AllModules) { + foreach (Extension ext in mod.Extensions) { + if (!newExtensions.Contains (ext.Path)) + newExtensions.Add (ext.Path); + ExtensionPoint ep = tree.FindLoadedExtensionPoint (ext.Path); + if (ep != null && !eps.Contains (ep)) + eps.Add (ep, ep); + } + } + + // Add the new nodes + ArrayList loadedNodes = new ArrayList (); + foreach (ExtensionPoint ep in eps.Keys) { + ExtensionLoadData data = GetAddinExtensions (id, ep); + if (data != null) { + foreach (Extension ext in data.Extensions) { + TreeNode node = GetNode (ext.Path); + if (node != null && node.ExtensionNodeSet != null) { + if (node.ChildrenLoaded) + LoadModuleExtensionNodes (ext, data.AddinId, node.ExtensionNodeSet, loadedNodes); + } + else + AddinEngine.ReportError ("Extension node not found or not extensible: " + ext.Path, id, null, false); + } + } + } + + // Call the OnAddinLoaded method on nodes, if the add-in is already loaded + foreach (TreeNode nod in loadedNodes) + nod.ExtensionNode.OnAddinLoaded (); + + // Global extension change event. Other events are fired by LoadModuleExtensionNodes. + // The event is called for all extensions, even for those not loaded. This is for coherence, + // although that something that it doesn't make much sense to do (subscribing the ExtensionChanged + // event without first getting the list of nodes that may change). + foreach (string newExt in newExtensions) + NotifyExtensionsChanged (new ExtensionEventArgs (newExt)); + } + finally { + fireEvents = false; + } + // Do the same in child contexts + + lock (conditionTypes) { + if (childContexts != null) { + CleanDisposedChildContexts (); + foreach (WeakReference wref in childContexts) { + ExtensionContext ctx = wref.Target as ExtensionContext; + if (ctx != null) + ctx.ActivateAddinExtensions (id); + } + } + } + } + + internal void RemoveAddinExtensions (string id) + { + try { + // Registers this add-in as disabled, so from now on extension from this + // add-in will be ignored + RegisterRuntimeDisabledAddin (id); + + fireEvents = true; + + // This method removes all extension nodes added by the add-in + // Get all nodes created by the addin + ArrayList list = new ArrayList (); + tree.FindAddinNodes (id, list); + + // Remove each node and notify the change + foreach (TreeNode node in list) { + if (node.ExtensionNode == null) { + // It's an extension point. Just remove it, no notifications are needed + node.Remove (); + } + else { + node.ExtensionNode.OnAddinUnloaded (); + node.Remove (); + } + } + + // Notify global extension point changes. + // The event is called for all extensions, even for those not loaded. This is for coherence, + // although that something that it doesn't make much sense to do (subscribing the ExtensionChanged + // event without first getting the list of nodes that may change). + + // We get the runtime add-in because the add-in may already have been deleted from the registry + RuntimeAddin addin = AddinEngine.GetAddin (id); + if (addin != null) { + ArrayList paths = new ArrayList (); + // Using addin.Module.ParentAddinDescription here because addin.Addin.Description may not + // have a valid reference (the description is lazy loaded and may already have been removed from the registry) + foreach (ModuleDescription mod in addin.Module.ParentAddinDescription.AllModules) { + foreach (Extension ext in mod.Extensions) { + if (!paths.Contains (ext.Path)) + paths.Add (ext.Path); + } + } + foreach (string path in paths) + NotifyExtensionsChanged (new ExtensionEventArgs (path)); + } + } finally { + fireEvents = false; + } + } + + void RegisterRuntimeDisabledAddin (string addinId) + { + if (runTimeDisabledAddins == null) + runTimeDisabledAddins = new ArrayList (); + if (!runTimeDisabledAddins.Contains (addinId)) + runTimeDisabledAddins.Add (addinId); + + if (runTimeEnabledAddins != null) + runTimeEnabledAddins.Remove (addinId); + } + + void RegisterRuntimeEnabledAddin (string addinId) + { + if (runTimeEnabledAddins == null) + runTimeEnabledAddins = new ArrayList (); + if (!runTimeEnabledAddins.Contains (addinId)) + runTimeEnabledAddins.Add (addinId); + + if (runTimeDisabledAddins != null) + runTimeDisabledAddins.Remove (addinId); + } + + internal ICollection GetAddinsForPath (string path, List col) + { + ArrayList newlist = null; + + // Always consider add-ins which have been enabled at runtime since + // they may contain extension for this path. + // Ignore addins disabled at run-time. + + if (runTimeEnabledAddins != null && runTimeEnabledAddins.Count > 0) { + newlist = new ArrayList (); + newlist.AddRange (col); + foreach (string s in runTimeEnabledAddins) + if (!newlist.Contains (s)) + newlist.Add (s); + } + + if (runTimeDisabledAddins != null && runTimeDisabledAddins.Count > 0) { + if (newlist == null) { + newlist = new ArrayList (); + newlist.AddRange (col); + } + foreach (string s in runTimeDisabledAddins) + newlist.Remove (s); + } + + return newlist != null ? (ICollection)newlist : (ICollection)col; + } + + // Load the extension nodes at the specified path. If the path + // contains extension nodes implemented in an add-in which is + // not loaded, the add-in will be automatically loaded + + internal void LoadExtensions (string requestedExtensionPath) + { + TreeNode node = GetNode (requestedExtensionPath); + if (node == null) + throw new InvalidOperationException ("Extension point not defined: " + requestedExtensionPath); + + ExtensionPoint ep = node.ExtensionPoint; + + if (ep != null) { + + // Collect extensions to be loaded from add-ins. Before loading the extensions, + // they must be sorted, that's why loading is split in two steps (collecting + loading). + + ArrayList loadData = new ArrayList (); + + foreach (string addin in GetAddinsForPath (ep.Path, ep.Addins)) { + ExtensionLoadData ed = GetAddinExtensions (addin, ep); + if (ed != null) { + // Insert the addin data taking into account dependencies. + // An add-in must be processed after all its dependencies. + bool added = false; + for (int n=0; n + /// Delegate to be used in extension point subscriptions + /// + public delegate void ExtensionEventHandler (object sender, ExtensionEventArgs args); + + /// + /// Delegate to be used in extension point subscriptions + /// + public delegate void ExtensionNodeEventHandler (object sender, ExtensionNodeEventArgs args); + + /// + /// Arguments for extension events. + /// + public class ExtensionEventArgs: EventArgs + { + string path; + + internal ExtensionEventArgs () + { + } + + /// + /// Creates a new instance. + /// + /// + /// Path of the extension node that has changed. + /// + public ExtensionEventArgs (string path) + { + this.path = path; + } + + /// + /// Path of the extension node that has changed. + /// + public virtual string Path { + get { return path; } + } + + /// + /// Checks if a path has changed. + /// + /// + /// An extension path. + /// + /// + /// 'true' if the path is affected by the extension change event. + /// + /// + /// Checks if the specified path or any of its children paths is affected by the extension change event. + /// + public bool PathChanged (string pathToCheck) + { + if (pathToCheck.EndsWith ("/")) + return path.StartsWith (pathToCheck); + else + return path.StartsWith (pathToCheck) && (pathToCheck.Length == path.Length || path [pathToCheck.Length] == '/'); + } + } + + /// + /// Arguments for extension node events. + /// + public class ExtensionNodeEventArgs: ExtensionEventArgs + { + ExtensionNode node; + ExtensionChange change; + + /// + /// Creates a new instance + /// + /// + /// Type of change. + /// + /// + /// Node that has been added or removed. + /// + public ExtensionNodeEventArgs (ExtensionChange change, ExtensionNode node) + { + this.node = node; + this.change = change; + } + + /// + /// Path of the extension that changed. + /// + public override string Path { + get { return node.Path; } + } + + /// + /// Type of change. + /// + public ExtensionChange Change { + get { return change; } + } + + /// + /// Node that has been added or removed. + /// + public ExtensionNode ExtensionNode { + get { return node; } + } + + /// + /// Extension object that has been added or removed. + /// + public object ExtensionObject { + get { + InstanceExtensionNode tnode = node as InstanceExtensionNode; + if (tnode == null) + throw new InvalidOperationException ("Node is not an InstanceExtensionNode"); + return tnode.GetInstance (); + } + } + } + + /// + /// Type of change in an extension change event. + /// + public enum ExtensionChange + { + /// + /// An extension node has been added. + /// + Add, + + /// + /// An extension node has been removed. + /// + Remove + } + + + internal class ExtensionLoadData + { + public string AddinId; + public ArrayList Extensions; + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionNode.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionNode.cs new file mode 100644 index 00000000..2dc71265 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionNode.cs @@ -0,0 +1,565 @@ +// +// ExtensionNode.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Xml; +using System.Reflection; +using Mono.Addins.Description; + +namespace Mono.Addins +{ + /// + /// A node of the extension model. + /// + /// + /// An extension node is an element registered by an add-in in an extension point. + /// A host can get nodes registered in an extension point using methods such as + /// AddinManager.GetExtensionNodes(string), which returns a collection of ExtensionNode objects. + /// + /// ExtensionNode will normally be used as a base class of more complex extension point types. + /// The most common subclass is Mono.Addins.TypeExtensionNode, which allows registering a class + /// implemented in an add-in. + /// + public class ExtensionNode + { + bool childrenLoaded; + TreeNode treeNode; + ExtensionNodeList childNodes; + RuntimeAddin addin; + string addinId; + ExtensionNodeType nodeType; + ModuleDescription module; + AddinEngine addinEngine; + event ExtensionNodeEventHandler extensionNodeChanged; + + /// + /// Identifier of the node. + /// + /// + /// It is not mandatory to specify an 'id' for a node. When none is provided, + /// the add-in manager will automatically generate an unique id for the node. + /// The ExtensionNode.HasId property can be used to know if the 'id' has been + /// specified by the developer or not. + /// + public string Id { + get { return treeNode != null ? treeNode.Id : string.Empty; } + } + + /// + /// Location of this node in the extension tree. + /// + /// + /// The node path is composed by the path of the extension point where it is defined, + /// the identifiers of its parent nodes, and its own identifier. + /// + public string Path { + get { return treeNode != null ? treeNode.GetPath () : string.Empty; } + } + + /// + /// Parent node of this node. + /// + public ExtensionNode Parent { + get { + if (treeNode != null && treeNode.Parent != null) + return treeNode.Parent.ExtensionNode; + else + return null; + } + } + + /// + /// Extension context to which this node belongs + /// + public ExtensionContext ExtensionContext { + get { return treeNode.Context; } + } + + /// + /// Specifies whether the extension node has as an Id or not. + /// + /// + /// It is not mandatory to specify an 'id' for a node. When none is provided, + /// the add-in manager will automatically generate an unique id for the node. + /// This property will return true if an 'id' was provided for the node, and + /// false if the id was assigned by the add-in manager. + /// + public bool HasId { + get { return !Id.StartsWith (ExtensionTree.AutoIdPrefix); } + } + + internal void SetTreeNode (TreeNode node) + { + treeNode = node; + } + + internal void SetData (AddinEngine addinEngine, string plugid, ExtensionNodeType nodeType, ModuleDescription module) + { + this.addinEngine = addinEngine; + this.addinId = plugid; + this.nodeType = nodeType; + this.module = module; + } + + internal string AddinId { + get { return addinId; } + } + + internal TreeNode TreeNode { + get { return treeNode; } + } + + /// + /// The add-in that registered this extension node. + /// + /// + /// This property provides access to the resources and types of the add-in that created this extension node. + /// + public RuntimeAddin Addin { + get { + if (addin == null && addinId != null) { + if (!addinEngine.IsAddinLoaded (addinId)) + addinEngine.LoadAddin (null, addinId, true); + addin = addinEngine.GetAddin (addinId); + if (addin != null) + addin = addin.GetModule (module); + } + if (addin == null) + throw new InvalidOperationException ("Add-in '" + addinId + "' could not be loaded."); + return addin; + } + } + + /// + /// Notifies that a child node of this node has been added or removed. + /// + /// + /// The first time the event is subscribed, the handler will be called for each existing node. + /// + public event ExtensionNodeEventHandler ExtensionNodeChanged { + add { + extensionNodeChanged += value; + foreach (ExtensionNode node in ChildNodes) { + try { + value (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node)); + } catch (Exception ex) { + addinEngine.ReportError (null, node.Addin != null ? node.Addin.Id : null, ex, false); + } + } + } + remove { + extensionNodeChanged -= value; + } + } + + /// + /// Child nodes of this extension node. + /// + public ExtensionNodeList ChildNodes { + get { + if (childrenLoaded) + return childNodes; + + try { + if (treeNode.Children.Count == 0) { + childNodes = ExtensionNodeList.Empty; + return childNodes; + } + } + catch (Exception ex) { + addinEngine.ReportError (null, null, ex, false); + childNodes = ExtensionNodeList.Empty; + return childNodes; + } finally { + childrenLoaded = true; + } + + List list = new List (); + foreach (TreeNode cn in treeNode.Children) { + + // For each node check if it is visible for the current context. + // If something fails while evaluating the condition, just ignore the node. + + try { + if (cn.ExtensionNode != null && cn.IsEnabled) + list.Add (cn.ExtensionNode); + } catch (Exception ex) { + addinEngine.ReportError (null, null, ex, false); + } + } + if (list.Count > 0) + childNodes = new ExtensionNodeList (list); + else + childNodes = ExtensionNodeList.Empty; + + return childNodes; + } + } + + /// + /// Returns the child objects of a node. + /// + /// + /// An array of child objects. + /// + /// + /// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + /// The returned array is composed by all objects created by calling the + /// TypeExtensionNode.GetInstance() method for each node. + /// + public object[] GetChildObjects () + { + return GetChildObjects (typeof(object), true); + } + + /// + /// Returns the child objects of a node. + /// + /// + /// True if the method can reuse instances created in previous calls. + /// + /// + /// An array of child objects. + /// + /// + /// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + /// The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node (or TypeExtensionNode.GetInstance() if reuseCachedInstance is set to true). + /// + public object[] GetChildObjects (bool reuseCachedInstance) + { + return GetChildObjects (typeof(object), reuseCachedInstance); + } + + /// + /// Returns the child objects of a node (with type check). + /// + /// + /// Type of the return array elements. + /// + /// + /// An array of child objects. + /// + /// + /// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + /// The returned array is composed by all objects created by calling the + /// TypeExtensionNode.GetInstance(Type) method for each node. + /// + /// An InvalidOperationException exception is thrown if one of the found child objects is not a + /// subclass of the provided type. + /// + public object[] GetChildObjects (Type arrayElementType) + { + return GetChildObjects (arrayElementType, true); + } + + /// + /// Returns the child objects of a node (casting to the specified type) + /// + /// + /// An array of child objects. + /// + /// + /// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + /// The returned array is composed by all objects created by calling the + /// TypeExtensionNode.GetInstance() method for each node. + /// + public T[] GetChildObjects () + { + return (T[]) GetChildObjectsInternal (typeof(T), true); + } + /// + /// Returns the child objects of a node (with type check). + /// + /// + /// Type of the return array elements. + /// + /// + /// True if the method can reuse instances created in previous calls. + /// + /// + /// An array of child objects. + /// + /// + /// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + /// The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance(Type) + /// method for each node (or TypeExtensionNode.GetInstance(Type) if reuseCachedInstance is set to true). + /// + /// An InvalidOperationException exception will be thrown if one of the found child objects is not a subclass + /// of the provided type. + /// + public object[] GetChildObjects (Type arrayElementType, bool reuseCachedInstance) + { + return (object[]) GetChildObjectsInternal (arrayElementType, reuseCachedInstance); + } + + /// + /// Returns the child objects of a node (casting to the specified type). + /// + /// + /// True if the method can reuse instances created in previous calls. + /// + /// + /// An array of child objects. + /// + /// + /// This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + /// The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance() + /// method for each node (or TypeExtensionNode.GetInstance() if reuseCachedInstance is set to true). + /// + public T[] GetChildObjects (bool reuseCachedInstance) + { + return (T[]) GetChildObjectsInternal (typeof(T), reuseCachedInstance); + } + + Array GetChildObjectsInternal (Type arrayElementType, bool reuseCachedInstance) + { + ArrayList list = new ArrayList (ChildNodes.Count); + + for (int n=0; n + /// Reads the extension node data + /// + /// + /// The element containing the extension data + /// + /// + /// This method can be overridden to provide a custom method for reading extension node data from an element. + /// The default implementation reads the attributes if the element and assigns the values to the fields + /// and properties of the extension node that have the corresponding [NodeAttribute] decoration. + /// + internal protected virtual void Read (NodeElement elem) + { + if (nodeType == null) + return; + + NodeAttribute[] attributes = elem.Attributes; + ReadObject (this, attributes, nodeType.Fields); + + if (nodeType.CustomAttributeMember != null) { + var att = (CustomExtensionAttribute) Activator.CreateInstance (nodeType.CustomAttributeMember.MemberType, true); + att.ExtensionNode = this; + ReadObject (att, attributes, nodeType.CustomAttributeFields); + nodeType.CustomAttributeMember.SetValue (this, att); + } + } + + void ReadObject (object ob, NodeAttribute[] attributes, Dictionary fields) + { + if (fields == null) + return; + + // Make a copy because we are going to remove fields that have been used + fields = new Dictionary (fields); + + foreach (NodeAttribute at in attributes) { + + ExtensionNodeType.FieldData f; + if (!fields.TryGetValue (at.name, out f)) + continue; + + fields.Remove (at.name); + + object val; + Type memberType = f.MemberType; + + if (memberType == typeof(string)) { + if (f.Localizable) + val = Addin.Localizer.GetString (at.value); + else + val = at.value; + } + else if (memberType == typeof(string[])) { + string[] ss = at.value.Split (','); + if (ss.Length == 0 && ss[0].Length == 0) + val = new string [0]; + else { + for (int n=0; n 0) { + // Check if one of the remaining fields is mandatory + foreach (KeyValuePair e in fields) { + ExtensionNodeType.FieldData f = e.Value; + if (f.Required) + throw new InvalidOperationException ("Required attribute '" + e.Key + "' not found."); + } + } + } + + internal bool NotifyChildChanged () + { + if (!childrenLoaded) + return false; + + ExtensionNodeList oldList = childNodes; + childrenLoaded = false; + + bool changed = false; + + foreach (ExtensionNode nod in oldList) { + if (ChildNodes [nod.Id] == null) { + changed = true; + OnChildNodeRemoved (nod); + } + } + foreach (ExtensionNode nod in ChildNodes) { + if (oldList [nod.Id] == null) { + changed = true; + OnChildNodeAdded (nod); + } + } + if (changed) + OnChildrenChanged (); + return changed; + } + + /// + /// Called when the add-in that defined this extension node is actually loaded in memory. + /// + internal protected virtual void OnAddinLoaded () + { + } + + /// + /// Called when the add-in that defined this extension node is being + /// unloaded from memory. + /// + internal protected virtual void OnAddinUnloaded () + { + } + + /// + /// Called when the children list of this node has changed. It may be due to add-ins + /// being loaded/unloaded, or to conditions being changed. + /// + protected virtual void OnChildrenChanged () + { + } + + /// + /// Called when a child node is added + /// + /// + /// Added node. + /// + protected virtual void OnChildNodeAdded (ExtensionNode node) + { + if (extensionNodeChanged != null) + extensionNodeChanged (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node)); + } + + /// + /// Called when a child node is removed + /// + /// + /// Removed node. + /// + protected virtual void OnChildNodeRemoved (ExtensionNode node) + { + if (extensionNodeChanged != null) + extensionNodeChanged (this, new ExtensionNodeEventArgs (ExtensionChange.Remove, node)); + } + } + + /// + /// An extension node with custom metadata + /// + /// + /// This is the default type for extension nodes bound to a custom extension attribute. + /// + public class ExtensionNode: ExtensionNode, IAttributedExtensionNode where T:CustomExtensionAttribute + { + T data; + + /// + /// The custom attribute containing the extension metadata + /// + [NodeAttribute] + public T Data { + get { return data; } + internal set { data = value; } + } + + CustomExtensionAttribute IAttributedExtensionNode.Attribute { + get { return data; } + } + } + + /// + /// An extension node with custom metadata provided by an attribute + /// + /// + /// This interface is implemented by ExtensionNode<T> to provide non-generic access to the attribute instance. + /// + public interface IAttributedExtensionNode + { + /// + /// The custom attribute containing the extension metadata + /// + CustomExtensionAttribute Attribute { get; } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeAttribute.cs new file mode 100644 index 00000000..b53c99f6 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeAttribute.cs @@ -0,0 +1,110 @@ +// +// ExtensionNodeAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// This attribute can be applied to an ExtensionNode subclass to specify the default name and description. + /// + /// + /// This information will be used when an extension point does not define a name or description for a node type. + /// + [AttributeUsage (AttributeTargets.Class)] + public class ExtensionNodeAttribute: Attribute + { + string nodeName; + string description; + string customAttributeTypeName; + Type customAttributeType; + + /// + /// Initializes the attribute + /// + public ExtensionNodeAttribute () + { + } + + /// + /// Initializes the attribute + /// + /// + /// Name of the node + /// + public ExtensionNodeAttribute (string nodeName) + { + this.nodeName = nodeName; + } + + /// + /// Initializes the attribute + /// + /// + /// Name of the node + /// + /// + /// Description of the node + /// + public ExtensionNodeAttribute (string nodeName, string description) + { + this.nodeName = nodeName; + this.description = description; + } + + /// + /// Default name of the extension node + /// + public string NodeName { + get { return nodeName != null ? nodeName : string.Empty; } + set { nodeName = value; } + } + + /// + /// Default description of the extension node type + /// + public string Description { + get { return description != null ? description : string.Empty; } + set { description = value; } + } + + /// + /// Type of a custom attribute which can be used to specify metadata for this extension node type + /// + public Type ExtensionAttributeType { + get { return customAttributeType; } + set { customAttributeType = value; customAttributeTypeName = value.FullName; } + } + + internal string ExtensionAttributeTypeName { + get { return customAttributeTypeName ?? string.Empty; } + set { customAttributeTypeName = value; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeChildAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeChildAttribute.cs new file mode 100644 index 00000000..d5839e8c --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeChildAttribute.cs @@ -0,0 +1,105 @@ +// +// ExtensionNodeChildAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Declares allowed children of an extension node type. + /// + /// + /// This attribute allows declaring the type of children that an extension node can have. + /// + [AttributeUsage (AttributeTargets.Class, AllowMultiple=true)] + public class ExtensionNodeChildAttribute: Attribute + { + string nodeName; + Type extensionNodeType; + string extensionNodeTypeName; + + /// + /// Initializes a new instance + /// + /// + /// Name of the allowed child extension node. + /// + public ExtensionNodeChildAttribute (string nodeName) + : this (typeof(TypeExtensionNode), nodeName) + { + } + + /// + /// Initializes a new instance + /// + /// + /// Type of the allowed child extension node. + /// + public ExtensionNodeChildAttribute (Type extensionNodeType) + : this (extensionNodeType, null) + { + } + + /// + /// Initializes a new instance + /// + /// + /// Type of the allowed child extension node. + /// + /// + /// Name of the allowed child extension node. + /// + public ExtensionNodeChildAttribute (Type extensionNodeType, string nodeName) + { + ExtensionNodeType = extensionNodeType; + this.nodeName = nodeName; + } + + /// + /// Name of the allowed child extension node. + /// + public string NodeName { + get { return nodeName != null ? nodeName : string.Empty; } + set { nodeName = value; } + } + + /// + /// Type of the allowed child extension node. + /// + public Type ExtensionNodeType { + get { return extensionNodeType; } + set { extensionNodeType = value; extensionNodeTypeName = value.FullName; } + } + + internal string ExtensionNodeTypeName { + get { return extensionNodeTypeName; } + set { extensionNodeTypeName = value; extensionNodeType = null; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeList.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeList.cs new file mode 100644 index 00000000..729b5ae5 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionNodeList.cs @@ -0,0 +1,205 @@ +// +// ExtensionNodeList.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Mono.Addins +{ + /// + /// A list of extension nodes. + /// + public class ExtensionNodeList: IEnumerable + { + internal List list; + + internal static ExtensionNodeList Empty = new ExtensionNodeList (new List ()); + + internal ExtensionNodeList (List list) + { + this.list = list; + } + + /// + /// Returns the node in the specified index. + /// + /// + /// The index. + /// + public ExtensionNode this [int n] { + get { + if (list == null) + throw new System.IndexOutOfRangeException (); + else + return (ExtensionNode) list [n]; + } + } + + /// + /// Returns the node with the specified ID. + /// + /// + /// An id. + /// + public ExtensionNode this [string id] { + get { + if (list == null) + return null; + else { + for (int n = list.Count - 1; n >= 0; n--) + if (((ExtensionNode) list [n]).Id == id) + return (ExtensionNode) list [n]; + return null; + } + } + } + + /// + /// Gets an enumerator which enumerates all nodes in the list + /// + public IEnumerator GetEnumerator () + { + if (list == null) + return ((IList)Type.EmptyTypes).GetEnumerator (); + return list.GetEnumerator (); + } + + /// + /// Number of nodes of the collection. + /// + public int Count { + get { return list == null ? 0 : list.Count; } + } + + /// + /// Copies all nodes to an array + /// + /// + /// The target array + /// + /// + /// Initial index where to copy to + /// + public void CopyTo (ExtensionNode[] array, int index) + { + if (list != null) + list.CopyTo (array, index); + } + } + + /// + /// A list of extension nodes. + /// + public class ExtensionNodeList: IEnumerable, IEnumerable where T: ExtensionNode + { + List list; + + internal static ExtensionNodeList Empty = new ExtensionNodeList (new List ()); + + internal ExtensionNodeList (List list) + { + this.list = list; + } + + /// + /// Returns the node in the specified index. + /// + /// + /// The index. + /// + public T this [int n] { + get { + if (list == null) + throw new System.IndexOutOfRangeException (); + else + return (T) list [n]; + } + } + + /// + /// Returns the node with the specified ID. + /// + /// + /// An id. + /// + public T this [string id] { + get { + if (list == null) + return null; + else { + for (int n = list.Count - 1; n >= 0; n--) + if (list [n].Id == id) + return (T) list [n]; + return null; + } + } + } + + /// + /// Gets an enumerator which enumerates all nodes in the list + /// + public IEnumerator GetEnumerator () + { + if (list == null) + yield break; + foreach (ExtensionNode n in list) + yield return (T) n; + } + + IEnumerator IEnumerable.GetEnumerator () + { + if (list == null) + return ((IList)Type.EmptyTypes).GetEnumerator (); + return list.GetEnumerator (); + } + + /// + /// Number of nodes of the collection. + /// + public int Count { + get { return list == null ? 0 : list.Count; } + } + + /// + /// Copies all nodes to an array + /// + /// + /// The target array + /// + /// + /// Initial index where to copy to + /// + public void CopyTo (T[] array, int index) + { + if (list != null) + list.CopyTo (array, index); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionPointAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionPointAttribute.cs new file mode 100644 index 00000000..f7a97c37 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionPointAttribute.cs @@ -0,0 +1,194 @@ +// +// ExtensionPointAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Declares an extension point. + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=true)] + public class ExtensionPointAttribute: Attribute + { + string path; + Type nodeType; + string nodeName; + string desc; + string name; + Type objectType; + string nodeTypeName; + string objectTypeName; + Type customAttributeType; + string customAttributeTypeName; + string defaultInsertBefore; + string defaultInsertAfter; + + /// + /// Initializes a new instance + /// + public ExtensionPointAttribute () + { + } + + /// + /// Initializes a new instance + /// + /// + /// Extension path that identifies the extension point + /// + public ExtensionPointAttribute (string path) + { + this.path = path; + } + + /// + /// Initializes a new instance + /// + /// + /// Extension path that identifies the extension point + /// + /// + /// Type of the extension node to be created for extensions + /// + public ExtensionPointAttribute (string path, Type nodeType) + { + this.path = path; + this.nodeType = nodeType; + } + + /// + /// Initializes a new instance + /// + /// + /// Extension path that identifies the extension point + /// + /// + /// Element name to be used when defining an extension in an XML manifest. + /// + /// + /// Type of the extension node to be created for extensions + /// + public ExtensionPointAttribute (string path, string nodeName, Type nodeType) + { + this.path = path; + this.nodeType = nodeType; + this.nodeName = nodeName; + } + + /// + /// Extension path that identifies the extension point + /// + public string Path { + get { return path != null ? path : string.Empty; } + set { path = value; } + } + + /// + /// Long description of the extension point. + /// + public string Description { + get { return desc != null ? desc : string.Empty; } + set { desc = value; } + } + + /// + /// Type of the extension node to be created for extensions + /// + public Type NodeType { + get { return nodeType != null ? nodeType : typeof(TypeExtensionNode); } + set { nodeType = value; nodeTypeName = value.FullName; } + } + + /// + /// Expected extension object type (when nodes are of type TypeExtensionNode) + /// + public Type ObjectType { + get { return objectType; } + set { objectType = value; objectTypeName = value.FullName; } + } + + internal string NodeTypeName { + get { return nodeTypeName != null ? nodeTypeName : typeof(TypeExtensionNode).FullName; } + set { nodeTypeName = value; } + } + + internal string ObjectTypeName { + get { return objectTypeName; } + set { objectTypeName = value; } + } + + /// + /// Element name to be used when defining an extension in an XML manifest. The default name is "Type". + /// + public string NodeName { + get { return nodeName != null && nodeName.Length > 0 ? nodeName : string.Empty; } + set { nodeName = value; } + } + + /// + /// Display name of the extension point. + /// + public string Name { + get { return name != null ? name : string.Empty; } + set { name = value; } + } + + /// + /// Type of the custom attribute to be used to specify metadata for the extension point + /// + public Type ExtensionAttributeType { + get { return this.customAttributeType; } + set { this.customAttributeType = value; customAttributeTypeName = value.FullName; } + } + + internal string ExtensionAttributeTypeName { + get { return this.customAttributeTypeName; } + set { this.customAttributeTypeName = value; } + } + + /// + /// The id of the extension before which new extensions will be added, unless the extension defines its own InsertBefore value + /// + /// The default insert before. + public string DefaultInsertBefore { + get { return defaultInsertBefore ?? ""; } + set { defaultInsertBefore = value; } + } + + /// + /// The id of the extension after which new extensions will be added, unless the extension defines its own InsertAfter value + /// + /// The default insert before. + public string DefaultInsertAfter { + get { return defaultInsertAfter ?? ""; } + set { defaultInsertAfter = value; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ExtensionTree.cs b/mono-addins/Mono.Addins/Mono.Addins/ExtensionTree.cs new file mode 100644 index 00000000..49ee2b46 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ExtensionTree.cs @@ -0,0 +1,319 @@ +// +// ExtensionTree.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Reflection; +using System.Xml; +using Mono.Addins.Description; +using System.Collections.Generic; + +namespace Mono.Addins +{ + internal class ExtensionTree: TreeNode + { + int internalId; + internal const string AutoIdPrefix = "__nid_"; + ExtensionContext context; + + public ExtensionTree (AddinEngine addinEngine, ExtensionContext context): base (addinEngine, "") + { + this.context = context; + } + + public override ExtensionContext Context { + get { return context; } + } + + + public void LoadExtension (string addin, Extension extension, ArrayList addedNodes) + { + TreeNode tnode = GetNode (extension.Path); + if (tnode == null) { + addinEngine.ReportError ("Can't load extensions for path '" + extension.Path + "'. Extension point not defined.", addin, null, false); + return; + } + + int curPos = -1; + LoadExtensionElement (tnode, addin, extension.ExtensionNodes, (ModuleDescription) extension.Parent, ref curPos, tnode.Condition, false, addedNodes); + } + + void LoadExtensionElement (TreeNode tnode, string addin, ExtensionNodeDescriptionCollection extension, ModuleDescription module, ref int curPos, BaseCondition parentCondition, bool inComplextCondition, ArrayList addedNodes) + { + foreach (ExtensionNodeDescription elem in extension) { + + if (inComplextCondition) { + parentCondition = ReadComplexCondition (elem, parentCondition); + inComplextCondition = false; + continue; + } + + if (elem.NodeName == "ComplexCondition") { + LoadExtensionElement (tnode, addin, elem.ChildNodes, module, ref curPos, parentCondition, true, addedNodes); + continue; + } + + if (elem.NodeName == "Condition") { + Condition cond = new Condition (AddinEngine, elem, parentCondition); + LoadExtensionElement (tnode, addin, elem.ChildNodes, module, ref curPos, cond, false, addedNodes); + continue; + } + + var pnode = tnode; + ExtensionPoint extensionPoint = null; + while (pnode != null && (extensionPoint = pnode.ExtensionPoint) == null) + pnode = pnode.Parent; + + string after = elem.GetAttribute ("insertafter"); + if (after.Length == 0 && extensionPoint != null && curPos == -1) + after = extensionPoint.DefaultInsertAfter; + if (after.Length > 0) { + int i = tnode.Children.IndexOfNode (after); + if (i != -1) + curPos = i+1; + } + string before = elem.GetAttribute ("insertbefore"); + if (before.Length == 0 && extensionPoint != null && curPos == -1) + before = extensionPoint.DefaultInsertBefore; + if (before.Length > 0) { + int i = tnode.Children.IndexOfNode (before); + if (i != -1) + curPos = i; + } + + // If node position is not explicitly set, add the node at the end + if (curPos == -1) + curPos = tnode.Children.Count; + + // Find the type of the node in this extension + ExtensionNodeType ntype = addinEngine.FindType (tnode.ExtensionNodeSet, elem.NodeName, addin); + + if (ntype == null) { + addinEngine.ReportError ("Node '" + elem.NodeName + "' not allowed in extension: " + tnode.GetPath (), addin, null, false); + continue; + } + + string id = elem.GetAttribute ("id"); + if (id.Length == 0) + id = AutoIdPrefix + (++internalId); + + TreeNode cnode = new TreeNode (addinEngine, id); + + ExtensionNode enode = ReadNode (cnode, addin, ntype, elem, module); + if (enode == null) + continue; + + cnode.Condition = parentCondition; + cnode.ExtensionNodeSet = ntype; + tnode.InsertChildNode (curPos, cnode); + addedNodes.Add (cnode); + + if (cnode.Condition != null) + Context.RegisterNodeCondition (cnode, cnode.Condition); + + // Load children + if (elem.ChildNodes.Count > 0) { + int cp = 0; + LoadExtensionElement (cnode, addin, elem.ChildNodes, module, ref cp, parentCondition, false, addedNodes); + } + + curPos++; + } + if (Context.FireEvents) + tnode.NotifyChildrenChanged (); + } + + BaseCondition ReadComplexCondition (ExtensionNodeDescription elem, BaseCondition parentCondition) + { + if (elem.NodeName == "Or" || elem.NodeName == "And" || elem.NodeName == "Not") { + ArrayList conds = new ArrayList (); + foreach (ExtensionNodeDescription celem in elem.ChildNodes) { + conds.Add (ReadComplexCondition (celem, null)); + } + if (elem.NodeName == "Or") + return new OrCondition ((BaseCondition[]) conds.ToArray (typeof(BaseCondition)), parentCondition); + else if (elem.NodeName == "And") + return new AndCondition ((BaseCondition[]) conds.ToArray (typeof(BaseCondition)), parentCondition); + else { + if (conds.Count != 1) { + addinEngine.ReportError ("Invalid complex condition element '" + elem.NodeName + "'. 'Not' condition can only have one parameter.", null, null, false); + return new NullCondition (); + } + return new NotCondition ((BaseCondition) conds [0], parentCondition); + } + } + if (elem.NodeName == "Condition") { + return new Condition (AddinEngine, elem, parentCondition); + } + addinEngine.ReportError ("Invalid complex condition element '" + elem.NodeName + "'.", null, null, false); + return new NullCondition (); + } + + public ExtensionNode ReadNode (TreeNode tnode, string addin, ExtensionNodeType ntype, ExtensionNodeDescription elem, ModuleDescription module) + { + try { + if (ntype.Type == null) { + if (!InitializeNodeType (ntype)) + return null; + } + + ExtensionNode node; + node = Activator.CreateInstance (ntype.Type) as ExtensionNode; + if (node == null) { + addinEngine.ReportError ("Extension node type '" + ntype.Type + "' must be a subclass of ExtensionNode", addin, null, false); + return null; + } + + tnode.AttachExtensionNode (node); + node.SetData (addinEngine, addin, ntype, module); + node.Read (elem); + return node; + } + catch (Exception ex) { + addinEngine.ReportError ("Could not read extension node of type '" + ntype.Type + "' from extension path '" + tnode.GetPath() + "'", addin, ex, false); + return null; + } + } + + bool InitializeNodeType (ExtensionNodeType ntype) + { + RuntimeAddin p = addinEngine.GetAddin (ntype.AddinId); + if (p == null) { + if (!addinEngine.IsAddinLoaded (ntype.AddinId)) { + if (!addinEngine.LoadAddin (null, ntype.AddinId, false)) + return false; + p = addinEngine.GetAddin (ntype.AddinId); + if (p == null) { + addinEngine.ReportError ("Add-in not found", ntype.AddinId, null, false); + return false; + } + } + } + + // If no type name is provided, use TypeExtensionNode by default + if (ntype.TypeName == null || ntype.TypeName.Length == 0 || ntype.TypeName == typeof(TypeExtensionNode).FullName) { + // If it has a custom attribute, use the generic version of TypeExtensionNode + if (ntype.ExtensionAttributeTypeName.Length > 0) { + Type attType = p.GetType (ntype.ExtensionAttributeTypeName, false); + if (attType == null) { + addinEngine.ReportError ("Custom attribute type '" + ntype.ExtensionAttributeTypeName + "' not found.", ntype.AddinId, null, false); + return false; + } + if (ntype.ObjectTypeName.Length > 0 || ntype.TypeName == typeof(TypeExtensionNode).FullName) + ntype.Type = typeof(TypeExtensionNode<>).MakeGenericType (attType); + else + ntype.Type = typeof(ExtensionNode<>).MakeGenericType (attType); + } else { + ntype.Type = typeof(TypeExtensionNode); + return true; + } + } + else { + ntype.Type = p.GetType (ntype.TypeName, false); + if (ntype.Type == null) { + addinEngine.ReportError ("Extension node type '" + ntype.TypeName + "' not found.", ntype.AddinId, null, false); + return false; + } + } + + // Check if the type has NodeAttribute attributes applied to fields. + ExtensionNodeType.FieldData boundAttributeType = null; + Dictionary fields = GetMembersMap (ntype.Type, out boundAttributeType); + ntype.CustomAttributeMember = boundAttributeType; + if (fields.Count > 0) + ntype.Fields = fields; + + // If the node type is bound to a custom attribute and there is a member bound to that attribute, + // get the member map for the attribute. + + if (boundAttributeType != null) { + if (ntype.ExtensionAttributeTypeName.Length == 0) + throw new InvalidOperationException ("Extension node not bound to a custom attribute."); + if (ntype.ExtensionAttributeTypeName != boundAttributeType.MemberType.FullName) + throw new InvalidOperationException ("Incorrect custom attribute type declaration in " + ntype.Type + ". Expected '" + ntype.ExtensionAttributeTypeName + "' found '" + boundAttributeType.MemberType.FullName + "'"); + + fields = GetMembersMap (boundAttributeType.MemberType, out boundAttributeType); + if (fields.Count > 0) + ntype.CustomAttributeFields = fields; + } + + return true; + } + + Dictionary GetMembersMap (Type type, out ExtensionNodeType.FieldData boundAttributeType) + { + string fname; + Dictionary fields = new Dictionary (); + boundAttributeType = null; + + while (type != typeof(object) && type != null) { + foreach (FieldInfo field in type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { + NodeAttributeAttribute at = (NodeAttributeAttribute) Attribute.GetCustomAttribute (field, typeof(NodeAttributeAttribute), true); + if (at != null) { + ExtensionNodeType.FieldData fd = CreateFieldData (field, at, out fname, ref boundAttributeType); + if (fd != null) + fields [fname] = fd; + } + } + foreach (PropertyInfo prop in type.GetProperties (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) { + NodeAttributeAttribute at = (NodeAttributeAttribute) Attribute.GetCustomAttribute (prop, typeof(NodeAttributeAttribute), true); + if (at != null) { + ExtensionNodeType.FieldData fd = CreateFieldData (prop, at, out fname, ref boundAttributeType); + if (fd != null) + fields [fname] = fd; + } + } + type = type.BaseType; + } + return fields; + } + + ExtensionNodeType.FieldData CreateFieldData (MemberInfo member, NodeAttributeAttribute at, out string name, ref ExtensionNodeType.FieldData boundAttributeType) + { + ExtensionNodeType.FieldData fdata = new ExtensionNodeType.FieldData (); + fdata.Member = member; + fdata.Required = at.Required; + fdata.Localizable = at.Localizable; + + if (at.Name != null && at.Name.Length > 0) + name = at.Name; + else + name = member.Name; + + if (typeof(CustomExtensionAttribute).IsAssignableFrom (fdata.MemberType)) { + if (boundAttributeType != null) + throw new InvalidOperationException ("Type '" + member.DeclaringType + "' has two members bound to a custom attribute. There can be only one."); + boundAttributeType = fdata; + return null; + } + + return fdata; + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/GettextCatalog.cs b/mono-addins/Mono.Addins/Mono.Addins/GettextCatalog.cs new file mode 100644 index 00000000..959d166a --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/GettextCatalog.cs @@ -0,0 +1,47 @@ +// +// GettextCatalog.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Addins +{ + // TODO: Add real translation support, which can work in Windows + + internal class GettextCatalog + { + public static string GetString (string str) + { + return str; + } + + public static string GetString (string str, params object[] arguments) + { + return string.Format (GetString (str), arguments); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/IAddinInstaller.cs b/mono-addins/Mono.Addins/Mono.Addins/IAddinInstaller.cs new file mode 100644 index 00000000..3b9b7d54 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/IAddinInstaller.cs @@ -0,0 +1,26 @@ + + +using System; + +namespace Mono.Addins +{ + /// + /// An add-in installation handler + /// + public interface IAddinInstaller + { + /// + /// Installs a set of add-ins + /// + /// + /// Registry where to install + /// + /// + /// Message to show to the user when new add-ins have to be installed. + /// + /// + /// List of IDs of the add-ins to be installed. + /// + void InstallAddins (AddinRegistry reg, string message, string[] addinIds); + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/IProgressStatus.cs b/mono-addins/Mono.Addins/Mono.Addins/IProgressStatus.cs new file mode 100644 index 00000000..48100aa1 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/IProgressStatus.cs @@ -0,0 +1,109 @@ +// +// IProgressStatus.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Progress status listener. + /// + public interface IProgressStatus + { + /// + /// Sets the description of the current operation. + /// + /// + /// A message + /// + /// + /// This method is called by the add-in engine to show a description of the operation being monitorized. + /// + void SetMessage (string msg); + + /// + /// Sets the progress of the operation. + /// + /// + /// A number between 0 and 1. 0 means no progress, 1 means operation completed. + /// + /// + /// This method is called by the add-in engine to show the progress of the operation being monitorized. + /// + void SetProgress (double progress); + + /// + /// Writes text to the log. + /// + /// + /// Message to write + /// + void Log (string msg); + + /// + /// Log level requested by the user: 0: no log, 1: normal log, >1 verbose log + /// + int LogLevel { get; } + + /// + /// Reports a warning. + /// + /// + /// Warning message + /// + /// + /// This method is called by the add-in engine to report a warning in the operation being monitorized. + /// + void ReportWarning (string message); + + /// + /// Reports an error. + /// + /// + /// Error message + /// + /// + /// Exception that caused the error. It can be null. + /// + /// + /// This method is called by the add-in engine to report an error occurred while executing the operation being monitorized. + /// + void ReportError (string message, Exception exception); + + /// + /// Returns True when the user requested to cancel this operation + /// + bool IsCanceled { get; } + + /// + /// Cancels the operation being montorized. + /// + void Cancel (); + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/ImportAddinAssemblyAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/ImportAddinAssemblyAttribute.cs new file mode 100644 index 00000000..2ef0f1b7 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ImportAddinAssemblyAttribute.cs @@ -0,0 +1,79 @@ +// +// ImportAddinAssemblyAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Declares an add-in assembly import + /// + /// + /// An add-in may be composed by several assemblies and data files. + /// Assemblies must be declared in the main assembly using this attribute, or in the XML manifest. + /// + /// It is important to properly declare all files used by an add-in. + /// For example, when a type from the add-in is required (e.g. an ICommand implementation), + /// only properly declared assemblies will be checked. + /// This information is also used by setup tools to know exactly what needs to be packaged when creating + /// an add-in package, or to know what needs to be deleted when removing an add-in. + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public class ImportAddinAssemblyAttribute: Attribute + { + string filePath; + bool scan = true; + + /// + /// Initializes a new instance + /// + /// + /// Path to the assembly. Must be relative to the assembly declaring this attribute. + /// + public ImportAddinAssemblyAttribute (string filePath) + { + this.filePath = filePath; + } + + /// + /// Path to the assembly. Must be relative to the assembly declaring this attribute. + /// + public string FilePath { + get { return filePath; } + set { filePath = value; } + } + + /// + /// When set to true (the default), the included assembly will be scanned + /// looking for extension point declarations. + /// + public bool Scan { + get { return this.scan; } + set { this.scan = value; } + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/ImportAddinFileAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/ImportAddinFileAttribute.cs new file mode 100644 index 00000000..3db6f8fe --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/ImportAddinFileAttribute.cs @@ -0,0 +1,67 @@ +// +// ImportAddinFileAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; + +namespace Mono.Addins +{ + /// + /// Declares an add-in file import + /// + /// + /// An add-in may be composed by several assemblies and data files. + /// Data files must be declared in the main assembly using this attribute, or in the XML manifest. + /// + /// It is important to properly declare all files used by an add-in. + /// This information is used by setup tools to know exactly what needs to be packaged when creating + /// an add-in package, or to know what needs to be deleted when removing an add-in. + /// + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple = true)] + public class ImportAddinFileAttribute: Attribute + { + string filePath; + + /// + /// Initializes a new instance + /// + /// + /// Path to the file. Must be relative to the assembly declaring this attribute. + /// + public ImportAddinFileAttribute (string filePath) + { + this.filePath = filePath; + } + + /// + /// Path to the file. Must be relative to the assembly declaring this attribute. + /// + public string FilePath { + get { return filePath; } + set { filePath = value; } + } + } +} + diff --git a/mono-addins/Mono.Addins/Mono.Addins/InstanceExtensionNode.cs b/mono-addins/Mono.Addins/Mono.Addins/InstanceExtensionNode.cs new file mode 100644 index 00000000..3d6640da --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/InstanceExtensionNode.cs @@ -0,0 +1,102 @@ +// +// InstanceExtensionNode.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Base class for extension nodes which create extension objects + /// + public abstract class InstanceExtensionNode: ExtensionNode + { + object cachedInstance; + + /// + /// Gets the extension object declared by this node + /// + /// + /// Expected object type. An exception will be thrown if the object is not an instance of the specified type. + /// + /// + /// The extension object + /// + /// + /// The extension object is cached and the same instance will be returned at every call. + /// + public object GetInstance (Type expectedType) + { + object ob = GetInstance (); + if (!expectedType.IsInstanceOfType (ob)) + throw new InvalidOperationException (string.Format ("Expected subclass of type '{0}'. Found '{1}'.", expectedType, ob.GetType ())); + return ob; + } + + /// + /// Gets the extension object declared by this node + /// + /// + /// The extension object + /// + /// + /// The extension object is cached and the same instance will be returned at every call. + /// + public object GetInstance () + { + if (cachedInstance == null) + cachedInstance = CreateInstance (); + return cachedInstance; + } + + /// + /// Creates a new extension object + /// + /// + /// Expected object type. An exception will be thrown if the object is not an instance of the specified type. + /// + /// + /// The extension object + /// + public object CreateInstance (Type expectedType) + { + object ob = CreateInstance (); + if (!expectedType.IsInstanceOfType (ob)) + throw new InvalidOperationException (string.Format ("Expected subclass of type '{0}'. Found '{1}'.", expectedType, ob.GetType ())); + return ob; + } + + /// + /// Creates a new extension object + /// + /// + /// The extension object + /// + public abstract object CreateInstance (); + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/MissingDependencyException.cs b/mono-addins/Mono.Addins/Mono.Addins/MissingDependencyException.cs new file mode 100644 index 00000000..912aad22 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/MissingDependencyException.cs @@ -0,0 +1,49 @@ +// +// MissingDependencyException.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Runtime.Serialization; + +namespace Mono.Addins +{ + /// + /// Exception thrown when the add-in engine can't find a required add-in dependency + /// + [Serializable] + internal class MissingDependencyException: Exception + { + public MissingDependencyException (SerializationInfo inf, StreamingContext ctx) : base (inf, ctx) + { + } + + public MissingDependencyException (string message): base (message) + { + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/NodeAttributeAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/NodeAttributeAttribute.cs new file mode 100644 index 00000000..e6fe775f --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/NodeAttributeAttribute.cs @@ -0,0 +1,266 @@ +// +// NodeAttributeAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Indicates that a field or property is bound to a node attribute + /// + [AttributeUsage (AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple=true)] + public class NodeAttributeAttribute: Attribute + { + string name; + bool required; + bool localizable; + Type type; + string typeName; + string description; + + /// + /// Initializes a new instance + /// + public NodeAttributeAttribute () + { + } + + /// + /// Initializes a new instance + /// + /// + /// XML name of the attribute. + /// + public NodeAttributeAttribute (string name) + :this (name, false, null) + { + } + + /// + /// Initializes a new instance + /// + /// + /// XML name of the attribute. + /// + /// + /// Description of the attribute. + /// + public NodeAttributeAttribute (string name, string description) + :this (name, false, description) + { + } + + /// + /// Initializes a new instance + /// + /// + /// XML name of the attribute. + /// + /// + /// Indicates whether the attribute is required or not. + /// + public NodeAttributeAttribute (string name, bool required) + : this (name, required, null) + { + } + + /// + /// Initializes a new instance + /// + /// + /// XML name of the attribute. + /// + /// + /// Indicates whether the attribute is required or not. + /// + /// + /// Description of the attribute. + /// + public NodeAttributeAttribute (string name, bool required, string description) + { + this.name = name; + this.required = required; + this.description = description; + } + + /// + /// Initializes a new instance + /// + /// + /// XML name of the attribute. + /// + /// + /// Type of the extension node attribute. + /// + /// + /// The type of the attribute is only required when applying this attribute at class level. + /// It is not required when it is applied to a field, since the attribute type will be the type of the field. + /// + public NodeAttributeAttribute (string name, Type type) + : this (name, type, false, null) + { + } + + /// + /// Initializes a new instance + /// + /// + /// XML name of the attribute. + /// + /// + /// Type of the extension node attribute. + /// + /// + /// Description of the attribute. + /// + /// + /// The type of the attribute is only required when applying this attribute at class level. + /// It is not required when it is applied to a field, since the attribute type will be the type of the field. + /// + public NodeAttributeAttribute (string name, Type type, string description) + : this (name, type, false, description) + { + } + + /// + /// Initializes a new instance + /// + /// + /// XML name of the attribute. + /// + /// + /// Type of the extension node attribute. + /// + /// + /// Indicates whether the attribute is required or not. + /// + /// + /// The type of the attribute is only required when applying this attribute at class level. + /// It is not required when it is applied to a field, since the attribute type will be the type of the field. + /// + public NodeAttributeAttribute (string name, Type type, bool required) + : this (name, type, false, null) + { + } + + /// + /// Initializes a new instance + /// + /// + /// XML name of the attribute. + /// + /// + /// Type of the extension node attribute. + /// + /// + /// Indicates whether the attribute is required or not. + /// + /// + /// Description of the attribute. + /// + /// + /// The type of the attribute is only required when applying this attribute at class level. + /// It is not required when it is applied to a field, since the attribute type will be the type of the field. + /// + public NodeAttributeAttribute (string name, Type type, bool required, string description) + { + this.name = name; + this.type = type; + this.required = required; + this.description = description; + } + + /// + /// XML name of the attribute. + /// + /// + /// If the name is not specified, the field name to which the [NodeAttribute] + /// is applied will be used as name. Providing a name is mandatory when applying + /// [NodeAttribute] at class level. + /// + public string Name { + get { return name != null ? name : string.Empty; } + set { name = value; } + } + + /// + /// Indicates whether the attribute is required or not. + /// + public bool Required { + get { return required; } + set { required = value; } + } + + /// + /// Type of the extension node attribute. + /// + /// + /// To be used only when applying [NodeAttribute] at class level. It is not required when it + /// is applied to a field, since the attribute type will be the type of the field. + /// + public Type Type { + get { return type; } + set { type = value; typeName = type.FullName; } + } + + internal string TypeName { + get { return typeName; } + set { typeName = value; type = null; } + } + + /// + /// Description of the attribute. + /// + /// + /// To be used in the extension point documentation. + /// + public string Description { + get { return description != null ? description : string.Empty; } + set { description = value; } + } + + /// + /// When set to True, the value of the field or property is expected to be a string id which + /// will be localized by the add-in engine + /// + public bool Localizable { + get { return localizable; } + set { localizable = value; } + } + + /// + /// Gets or sets the type of the content. + /// + /// + /// Allows specifying the type of the content of a string attribute. + /// This value is for documentation purposes only. + /// + public ContentType ContentType { get; set; } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/NodeElement.cs b/mono-addins/Mono.Addins/Mono.Addins/NodeElement.cs new file mode 100644 index 00000000..3812d54f --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/NodeElement.cs @@ -0,0 +1,111 @@ +// +// NodeElement.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; + +namespace Mono.Addins +{ + /// + /// An extension node element. + /// + /// + /// A raw representation of an extension node. Contains the basic information + /// needed to create ExtensionNode instances. + /// + public interface NodeElement + { + /// + /// Name of the node element. + /// + string NodeName { get; } + + /// + /// Gets element attributes. + /// + /// + /// Name of the attribute + /// + /// + /// The value of the attribute + /// + string GetAttribute (string key); + + /// + /// Gets all attributes defined in the element. + /// + NodeAttribute[] Attributes { get; } + + /// + /// Gets child nodes of this node + /// + NodeElementCollection ChildNodes { get; } + } + + /// + /// Attribute of a NodeElement. + /// + public class NodeAttribute + { + internal string name; + internal string value; + + internal NodeAttribute () + { + } + + /// + /// Name of the attribute. + /// + public string Name { + get { return name; } + } + + /// + /// Value of the attribute. + /// + public string Value { + get { return value; } + } + } + + /// + /// A collection of NodeElement objects + /// + public interface NodeElementCollection: IList, ICollection, IEnumerable + { + /// + /// Gets the at the specified index + /// + /// + /// Index + /// + new NodeElement this [int n] { get; } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/RuntimeAddin.cs b/mono-addins/Mono.Addins/Mono.Addins/RuntimeAddin.cs new file mode 100644 index 00000000..d2e995db --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/RuntimeAddin.cs @@ -0,0 +1,724 @@ +// +// RuntimeAddin.cs +// +// Author: +// Lluis Sanchez Gual, +// Georg Wächter +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Xml; +using System.Resources; +using System.Globalization; + +using Mono.Addins.Description; +using Mono.Addins.Localization; + +namespace Mono.Addins +{ + /// + /// Run-time representation of an add-in. + /// + public class RuntimeAddin + { + string id; + string baseDirectory; + string privatePath; + Addin ainfo; + RuntimeAddin parentAddin; + + Assembly[] assemblies; + RuntimeAddin[] depAddins; + ResourceManager[] resourceManagers; + AddinLocalizer localizer; + ModuleDescription module; + AddinEngine addinEngine; + + internal RuntimeAddin (AddinEngine addinEngine) + { + this.addinEngine = addinEngine; + } + + internal RuntimeAddin (AddinEngine addinEngine, RuntimeAddin parentAddin, ModuleDescription module) + { + this.addinEngine = addinEngine; + this.parentAddin = parentAddin; + this.module = module; + id = parentAddin.id; + baseDirectory = parentAddin.baseDirectory; + privatePath = parentAddin.privatePath; + ainfo = parentAddin.ainfo; + localizer = parentAddin.localizer; + module.RuntimeAddin = this; + } + + internal ModuleDescription Module { + get { return module; } + } + + internal Assembly[] Assemblies { + get { + EnsureAssembliesLoaded (); + return assemblies; + } + } + + /// + /// Identifier of the add-in. + /// + public string Id { + get { return Addin.GetIdName (id); } + } + + /// + /// Version of the add-in. + /// + public string Version { + get { return Addin.GetIdVersion (id); } + } + + internal Addin Addin { + get { return ainfo; } + } + + /// + /// Returns a string that represents the current RuntimeAddin. + /// + /// + /// A string that represents the current RuntimeAddin. + /// + public override string ToString () + { + return ainfo.ToString (); + } + + ResourceManager[] GetResourceManagers () + { + if (resourceManagers != null) + return resourceManagers; + + EnsureAssembliesLoaded (); + ArrayList managersList = new ArrayList (); + + // Search for embedded resource files + foreach (Assembly asm in assemblies) + { + foreach (string res in asm.GetManifestResourceNames ()) { + if (res.EndsWith (".resources")) + managersList.Add (new ResourceManager (res.Substring (0, res.Length - ".resources".Length), asm)); + } + } + + return resourceManagers = (ResourceManager[]) managersList.ToArray (typeof(ResourceManager)); + } + + /// + /// Gets a resource string + /// + /// + /// Name of the resource + /// + /// + /// The value of the resource string, or null if the resource can't be found. + /// + /// + /// The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + /// + public string GetResourceString (string name) + { + return (string) GetResourceObject (name, true, null); + } + + /// + /// Gets a resource string + /// + /// + /// Name of the resource + /// + /// + /// When set to true, an exception will be thrown if the resource is not found. + /// + /// + /// The value of the resource string + /// + /// + /// The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + /// + public string GetResourceString (string name, bool throwIfNotFound) + { + return (string) GetResourceObject (name, throwIfNotFound, null); + } + + /// + /// Gets a resource string + /// + /// + /// Name of the resource + /// + /// + /// When set to true, an exception will be thrown if the resource is not found. + /// + /// + /// Culture of the resource + /// + /// + /// The value of the resource string + /// + /// + /// The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + /// + public string GetResourceString (string name, bool throwIfNotFound, CultureInfo culture) + { + return (string) GetResourceObject (name, throwIfNotFound, culture); + } + + /// + /// Gets a resource object + /// + /// + /// Name of the resource + /// + /// + /// Value of the resource + /// + /// + /// The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + /// + public object GetResourceObject (string name) + { + return GetResourceObject (name, true, null); + } + + /// + /// Gets a resource object + /// + /// + /// Name of the resource + /// + /// + /// When set to true, an exception will be thrown if the resource is not found. + /// + /// + /// Value of the resource + /// + /// + /// The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + /// + public object GetResourceObject (string name, bool throwIfNotFound) + { + return GetResourceObject (name, throwIfNotFound, null); + } + + /// + /// Gets a resource object + /// + /// + /// Name of the resource + /// + /// + /// When set to true, an exception will be thrown if the resource is not found. + /// + /// + /// Culture of the resource + /// + /// + /// Value of the resource + /// + /// + /// The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + /// + public object GetResourceObject (string name, bool throwIfNotFound, CultureInfo culture) + { + // Look in resources of this add-in + foreach (ResourceManager manager in GetAllResourceManagers ()) { + object t = manager.GetObject (name, culture); + if (t != null) + return t; + } + + // Look in resources of dependent add-ins + foreach (RuntimeAddin addin in GetAllDependencies ()) { + object t = addin.GetResourceObject (name, false, culture); + if (t != null) + return t; + } + + if (throwIfNotFound) + throw new InvalidOperationException ("Resource object '" + name + "' not found in add-in '" + id + "'"); + + return null; + } + + /// + /// Gets a type defined in the add-in + /// + /// + /// Full name of the type + /// + /// + /// A type. + /// + /// + /// The type will be looked up in the assemblies that implement the add-in, + /// and recursively in all add-ins on which it depends. + /// + /// This method throws an InvalidOperationException if the type can't be found. + /// + public Type GetType (string typeName) + { + return GetType (typeName, true); + } + + /// + /// Gets a type defined in the add-in + /// + /// + /// Full name of the type + /// + /// + /// Indicates whether the method should throw an exception if the type can't be found. + /// + /// + /// A + /// + /// + /// The type will be looked up in the assemblies that implement the add-in, + /// and recursively in all add-ins on which it depends. + /// + /// If the type can't be found, this method throw a InvalidOperationException if + /// 'throwIfNotFound' is 'true', or 'null' otherwise. + /// + public Type GetType (string typeName, bool throwIfNotFound) + { + EnsureAssembliesLoaded (); + + // Look in the addin assemblies + + Type at = Type.GetType (typeName, false); + if (at != null) + return at; + + foreach (Assembly asm in GetAllAssemblies ()) { + Type t = asm.GetType (typeName, false); + if (t != null) + return t; + } + + // Look in the dependent add-ins + foreach (RuntimeAddin addin in GetAllDependencies ()) { + Type t = addin.GetType (typeName, false); + if (t != null) + return t; + } + + if (throwIfNotFound) + throw new InvalidOperationException ("Type '" + typeName + "' not found in add-in '" + id + "'"); + return null; + } + + IEnumerable GetAllResourceManagers () + { + foreach (ResourceManager rm in GetResourceManagers ()) + yield return rm; + + if (parentAddin != null) { + foreach (ResourceManager rm in parentAddin.GetResourceManagers ()) + yield return rm; + } + } + + IEnumerable GetAllAssemblies () + { + foreach (Assembly asm in Assemblies) + yield return asm; + + // Look in the parent addin assemblies + + if (parentAddin != null) { + foreach (Assembly asm in parentAddin.Assemblies) + yield return asm; + } + } + + IEnumerable GetAllDependencies () + { + // Look in the dependent add-ins + foreach (RuntimeAddin addin in GetDepAddins ()) + yield return addin; + + if (parentAddin != null) { + // Look in the parent dependent add-ins + foreach (RuntimeAddin addin in parentAddin.GetDepAddins ()) + yield return addin; + } + } + + /// + /// Creates an instance of a type defined in the add-in + /// + /// + /// Name of the type. + /// + /// + /// A new instance of the type + /// + /// + /// The type will be looked up in the assemblies that implement the add-in, + /// and recursively in all add-ins on which it depends. + /// + /// This method throws an InvalidOperationException if the type can't be found. + /// + /// The specified type must have a default constructor. + /// + public object CreateInstance (string typeName) + { + return CreateInstance (typeName, true); + } + + /// + /// Creates an instance of a type defined in the add-in + /// + /// + /// Name of the type. + /// + /// + /// Indicates whether the method should throw an exception if the type can't be found. + /// + /// + /// A new instance of the type + /// + /// + /// The type will be looked up in the assemblies that implement the add-in, + /// and recursively in all add-ins on which it depends. + /// + /// If the type can't be found, this method throw a InvalidOperationException if + /// 'throwIfNotFound' is 'true', or 'null' otherwise. + /// + /// The specified type must have a default constructor. + /// + public object CreateInstance (string typeName, bool throwIfNotFound) + { + Type type = GetType (typeName, throwIfNotFound); + if (type == null) + return null; + else + return Activator.CreateInstance (type, true); + } + + /// + /// Gets the path of an add-in file + /// + /// + /// Relative path of the file + /// + /// + /// Full path of the file + /// + /// + /// This method can be used to get the full path of a data file deployed together with the add-in. + /// + public string GetFilePath (string fileName) + { + return Path.Combine (baseDirectory, fileName); + } + + /// + /// Gets the path of an add-in file + /// + /// + /// Components of the file path + /// + /// + /// Full path of the file + /// + /// + /// This method can be used to get the full path of a data file deployed together with the add-in. + /// + public string GetFilePath (params string[] filePath) + { + return Path.Combine (baseDirectory, string.Join ("" + Path.DirectorySeparatorChar, filePath)); + } + + /// + /// Path to a directory where add-ins can store private configuration or status data + /// + public string PrivateDataPath { + get { + if (privatePath == null) { + privatePath = ainfo.PrivateDataPath; + if (!Directory.Exists (privatePath)) + Directory.CreateDirectory (privatePath); + } + return privatePath; + } + } + + /// + /// Gets the content of a resource + /// + /// + /// Name of the resource + /// + /// + /// Content of the resource, or null if not found + /// + /// + /// The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + /// + public Stream GetResource (string resourceName) + { + return GetResource (resourceName, false); + } + + /// + /// Gets the content of a resource + /// + /// + /// Name of the resource + /// + /// + /// When set to true, an exception will be thrown if the resource is not found. + /// + /// + /// Content of the resource. + /// + /// + /// The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + /// + public Stream GetResource (string resourceName, bool throwIfNotFound) + { + EnsureAssembliesLoaded (); + + // Look in the addin assemblies + + foreach (Assembly asm in GetAllAssemblies ()) { + Stream res = asm.GetManifestResourceStream (resourceName); + if (res != null) + return res; + } + + // Look in the dependent add-ins + foreach (RuntimeAddin addin in GetAllDependencies ()) { + Stream res = addin.GetResource (resourceName); + if (res != null) + return res; + } + + if (throwIfNotFound) + throw new InvalidOperationException ("Resource '" + resourceName + "' not found in add-in '" + id + "'"); + + return null; + } + + /// + /// Returns information about how the given resource has been persisted + /// + /// + /// Name of the resource + /// + /// + /// Resource information, or null if the resource doesn't exist + /// + public ManifestResourceInfo GetResourceInfo (string resourceName) + { + EnsureAssembliesLoaded (); + + // Look in the addin assemblies + + foreach (Assembly asm in GetAllAssemblies ()) { + var res = asm.GetManifestResourceInfo (resourceName); + if (res != null) { + // Mono doesn't set the referenced assembly + if (res.ReferencedAssembly == null) + return new ManifestResourceInfo (asm, res.FileName, res.ResourceLocation); + return res; + } + } + + // Look in the dependent add-ins + foreach (RuntimeAddin addin in GetAllDependencies ()) { + var res = addin.GetResourceInfo (resourceName); + if (res != null) + return res; + } + + return null; + } + + /// + /// Localizer which can be used to localize strings defined in this add-in + /// + public AddinLocalizer Localizer { + get { + if (localizer != null) + return localizer; + else + return addinEngine.DefaultLocalizer; + } + } + + internal RuntimeAddin GetModule (ModuleDescription module) + { + // If requesting the root module, return this + if (module == module.ParentAddinDescription.MainModule) + return this; + + if (module.RuntimeAddin != null) + return module.RuntimeAddin; + + RuntimeAddin addin = new RuntimeAddin (addinEngine, this, module); + return addin; + } + + internal AddinDescription Load (Addin iad) + { + ainfo = iad; + + AddinDescription description = iad.Description; + id = description.AddinId; + baseDirectory = description.BasePath; + module = description.MainModule; + module.RuntimeAddin = this; + + if (description.Localizer != null) { + string cls = description.Localizer.GetAttribute ("type"); + + // First try getting one of the stock localizers. If none of found try getting the type. + object fob = null; + Type t = Type.GetType ("Mono.Addins.Localization." + cls + "Localizer, " + GetType().Assembly.FullName, false); + if (t != null) + fob = Activator.CreateInstance (t); + + if (fob == null) + fob = CreateInstance (cls, true); + + IAddinLocalizerFactory factory = fob as IAddinLocalizerFactory; + if (factory == null) + throw new InvalidOperationException ("Localizer factory type '" + cls + "' must implement IAddinLocalizerFactory"); + localizer = new AddinLocalizer (factory.CreateLocalizer (this, description.Localizer)); + } + + return description; + } + + RuntimeAddin[] GetDepAddins () + { + if (depAddins != null) + return depAddins; + + ArrayList plugList = new ArrayList (); + string ns = ainfo.Description.Namespace; + + // Collect dependent ids + foreach (Dependency dep in module.Dependencies) { + AddinDependency pdep = dep as AddinDependency; + if (pdep != null) { + RuntimeAddin adn = addinEngine.GetAddin (Addin.GetFullId (ns, pdep.AddinId, pdep.Version)); + if (adn != null) + plugList.Add (adn); + else + addinEngine.ReportError ("Add-in dependency not loaded: " + pdep.FullAddinId, module.ParentAddinDescription.AddinId, null, false); + } + } + return depAddins = (RuntimeAddin[]) plugList.ToArray (typeof(RuntimeAddin)); + } + + void LoadModule (ModuleDescription module, ArrayList asmList) + { + // Load the assemblies + foreach (string s in module.Assemblies) { + Assembly asm = null; + + // don't load the assembly if it's already loaded + string asmPath = Path.Combine (baseDirectory, s); + foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies ()) { + // Sorry, you can't load addins from + // dynamic assemblies as get_Location + // throws a NotSupportedException + if (a is System.Reflection.Emit.AssemblyBuilder || a.IsDynamic) { + continue; + } + + try { + if (a.Location == asmPath) { + asm = a; + break; + } + } catch (NotSupportedException) { + // Some assemblies don't have a location + } + } + + if (asm == null) { + asm = Assembly.LoadFrom (asmPath); + } + + asmList.Add (asm); + } + } + + internal void UnloadExtensions () + { + addinEngine.UnregisterAddinNodeSets (id); + } + + bool CheckAddinDependencies (ModuleDescription module, bool forceLoadAssemblies) + { + foreach (Dependency dep in module.Dependencies) { + AddinDependency pdep = dep as AddinDependency; + if (pdep == null) + continue; + if (!addinEngine.IsAddinLoaded (pdep.FullAddinId)) + return false; + if (forceLoadAssemblies) + addinEngine.GetAddin (pdep.FullAddinId).EnsureAssembliesLoaded (); + } + return true; + } + + internal bool AssembliesLoaded { + get { return assemblies != null; } + } + + internal void EnsureAssembliesLoaded () + { + if (assemblies != null) + return; + + ArrayList asmList = new ArrayList (); + + // Load the assemblies of the module + CheckAddinDependencies (module, true); + LoadModule (module, asmList); + + assemblies = (Assembly[]) asmList.ToArray (typeof(Assembly)); + addinEngine.RegisterAssemblies (this); + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/TreeNode.cs b/mono-addins/Mono.Addins/Mono.Addins/TreeNode.cs new file mode 100644 index 00000000..969ce004 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/TreeNode.cs @@ -0,0 +1,353 @@ +// +// TreeNode.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Text; +using System.Collections; +using Mono.Addins.Description; + +namespace Mono.Addins +{ + class TreeNode + { + ArrayList childrenList; + TreeNodeCollection children; + ExtensionNode extensionNode; + bool childrenLoaded; + string id; + TreeNode parent; + ExtensionNodeSet nodeTypes; + ExtensionPoint extensionPoint; + BaseCondition condition; + protected AddinEngine addinEngine; + + public TreeNode (AddinEngine addinEngine, string id) + { + this.id = id; + this.addinEngine = addinEngine; + + // Root node + if (id.Length == 0) + childrenLoaded = true; + } + + public AddinEngine AddinEngine { + get { return addinEngine; } + } + + internal void AttachExtensionNode (ExtensionNode enode) + { + this.extensionNode = enode; + if (extensionNode != null) + extensionNode.SetTreeNode (this); + } + + public string Id { + get { return id; } + } + + public ExtensionNode ExtensionNode { + get { + if (extensionNode == null && extensionPoint != null) { + extensionNode = new ExtensionNode (); + extensionNode.SetData (addinEngine, extensionPoint.RootAddin, null, null); + AttachExtensionNode (extensionNode); + } + return extensionNode; + } + } + + public ExtensionPoint ExtensionPoint { + get { return extensionPoint; } + set { extensionPoint = value; } + } + + public ExtensionNodeSet ExtensionNodeSet { + get { return nodeTypes; } + set { nodeTypes = value; } + } + + public TreeNode Parent { + get { return parent; } + } + + public BaseCondition Condition { + get { return condition; } + set { + condition = value; + } + } + + public virtual ExtensionContext Context { + get { + if (parent != null) + return parent.Context; + else + return null; + } + } + + public bool IsEnabled { + get { + if (condition == null) + return true; + ExtensionContext ctx = Context; + if (ctx == null) + return true; + else + return condition.Evaluate (ctx); + } + } + + public bool ChildrenLoaded { + get { return childrenLoaded; } + } + + public void AddChildNode (TreeNode node) + { + node.parent = this; + if (childrenList == null) + childrenList = new ArrayList (); + childrenList.Add (node); + } + + public void InsertChildNode (int n, TreeNode node) + { + node.parent = this; + if (childrenList == null) + childrenList = new ArrayList (); + childrenList.Insert (n, node); + + // Dont call NotifyChildrenChanged here. It is called by ExtensionTree, + // after inserting all children of the node. + } + + internal int ChildCount { + get { return childrenList == null ? 0 : childrenList.Count; } + } + + public ExtensionNode GetExtensionNode (string path, string childId) + { + TreeNode node = GetNode (path, childId); + return node != null ? node.ExtensionNode : null; + } + + public ExtensionNode GetExtensionNode (string path) + { + TreeNode node = GetNode (path); + return node != null ? node.ExtensionNode : null; + } + + public TreeNode GetNode (string path, string childId) + { + if (childId == null || childId.Length == 0) + return GetNode (path); + else + return GetNode (path + "/" + childId); + } + + public TreeNode GetNode (string path) + { + return GetNode (path, false); + } + + public TreeNode GetNode (string path, bool buildPath) + { + if (path.StartsWith ("/")) + path = path.Substring (1); + + string[] parts = path.Split ('/'); + TreeNode curNode = this; + + foreach (string part in parts) { + int i = curNode.Children.IndexOfNode (part); + if (i != -1) { + curNode = curNode.Children [i]; + continue; + } + + if (buildPath) { + TreeNode newNode = new TreeNode (addinEngine, part); + curNode.AddChildNode (newNode); + curNode = newNode; + } else + return null; + } + return curNode; + } + + public TreeNodeCollection Children { + get { + if (!childrenLoaded) { + childrenLoaded = true; + if (extensionPoint != null) + Context.LoadExtensions (GetPath ()); + // We have to keep the relation info, since add-ins may be loaded/unloaded + } + if (childrenList == null) + return TreeNodeCollection.Empty; + if (children == null) + children = new TreeNodeCollection (childrenList); + return children; + } + } + + public string GetPath () + { + int num=0; + TreeNode node = this; + while (node != null) { + num++; + node = node.parent; + } + + string[] ids = new string [num]; + + node = this; + while (node != null) { + ids [--num] = node.id; + node = node.parent; + } + return string.Join ("/", ids); + } + + public void NotifyAddinLoaded (RuntimeAddin ad, bool recursive) + { + if (extensionNode != null && extensionNode.AddinId == ad.Addin.Id) + extensionNode.OnAddinLoaded (); + if (recursive && childrenLoaded) { + foreach (TreeNode node in Children.Clone ()) + node.NotifyAddinLoaded (ad, true); + } + } + + public ExtensionPoint FindLoadedExtensionPoint (string path) + { + if (path.StartsWith ("/")) + path = path.Substring (1); + + string[] parts = path.Split ('/'); + TreeNode curNode = this; + + foreach (string part in parts) { + int i = curNode.Children.IndexOfNode (part); + if (i != -1) { + curNode = curNode.Children [i]; + if (!curNode.ChildrenLoaded) + return null; + if (curNode.ExtensionPoint != null) + return curNode.ExtensionPoint; + continue; + } + return null; + } + return null; + } + + public void FindAddinNodes (string id, ArrayList nodes) + { + if (id != null && extensionPoint != null && extensionPoint.RootAddin == id) { + // It is an extension point created by the add-in. All nodes below this + // extension point will be added to the list, even if they come from other add-ins. + id = null; + } + + if (childrenLoaded) { + // Deep-first search, to make sure children are removed before the parent. + foreach (TreeNode node in Children) + node.FindAddinNodes (id, nodes); + } + + if (id == null || (ExtensionNode != null && ExtensionNode.AddinId == id)) + nodes.Add (this); + } + + public bool FindExtensionPathByType (IProgressStatus monitor, Type type, string nodeName, out string path, out string pathNodeName) + { + if (extensionPoint != null) { + foreach (ExtensionNodeType nt in extensionPoint.NodeSet.NodeTypes) { + if (nt.ObjectTypeName.Length > 0 && (nodeName.Length == 0 || nodeName == nt.Id)) { + RuntimeAddin addin = addinEngine.GetAddin (extensionPoint.RootAddin); + Type ot = addin.GetType (nt.ObjectTypeName); + if (ot != null) { + if (ot.IsAssignableFrom (type)) { + path = extensionPoint.Path; + pathNodeName = nt.Id; + return true; + } + } + else + monitor.ReportError ("Type '" + nt.ObjectTypeName + "' not found in add-in '" + Id + "'", null); + } + } + } + else { + foreach (TreeNode node in Children) { + if (node.FindExtensionPathByType (monitor, type, nodeName, out path, out pathNodeName)) + return true; + } + } + path = null; + pathNodeName = null; + return false; + } + + public void Remove () + { + if (parent != null) { + if (Condition != null) + Context.UnregisterNodeCondition (this, Condition); + parent.childrenList.Remove (this); + parent.NotifyChildrenChanged (); + } + } + + public bool NotifyChildrenChanged () + { + if (extensionNode != null) + return extensionNode.NotifyChildChanged (); + else + return false; + } + + public void ResetCachedData () + { + if (extensionPoint != null) { + string aid = Addin.GetIdName (extensionPoint.ParentAddinDescription.AddinId); + RuntimeAddin ad = addinEngine.GetAddin (aid); + if (ad != null) + extensionPoint = ad.Addin.Description.ExtensionPoints [GetPath ()]; + } + if (childrenList != null) { + foreach (TreeNode cn in childrenList) + cn.ResetCachedData (); + } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/TreeNodeCollection.cs b/mono-addins/Mono.Addins/Mono.Addins/TreeNodeCollection.cs new file mode 100644 index 00000000..28d499d4 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/TreeNodeCollection.cs @@ -0,0 +1,84 @@ +// +// TreeNodeCollection.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections; + +namespace Mono.Addins +{ + class TreeNodeCollection: IEnumerable + { + ArrayList list; + + internal static TreeNodeCollection Empty = new TreeNodeCollection (null); + + public TreeNodeCollection (ArrayList list) + { + this.list = list; + } + + public IEnumerator GetEnumerator () + { + if (list != null) + return list.GetEnumerator (); + else + return Type.EmptyTypes.GetEnumerator (); + } + + public TreeNode this [int n] { + get { + if (list != null) + return (TreeNode) list [n]; + else + throw new System.IndexOutOfRangeException (); + } + } + + public int IndexOfNode (string id) + { + for (int n=0; n + /// An extension node which specifies a type. + /// + /// + /// This class is a kind of Mono.Addins.ExtensionNode which can be used to register + /// types in an extension point. This is a very common case: a host application + /// defines an interface, and add-ins create classes that implement that interface. + /// The host will define an extension point which will use TypeExtensionNode as nodetext + /// type. Add-ins will register the classes they implement in that extension point. + /// + /// When the nodes of an extension point are of type TypeExtensionNode it is then + /// possible to use query methods such as AddinManager.GetExtensionObjects(string), + /// which will get all nodes in the provided extension path and will create an object + /// for each node. + /// + /// When declaring extension nodes in an add-in manifest, the class names can be + /// specified using the 'class' or 'type' attribute. If none of those attributes is + /// provided, the class name will be taken from the 'id' attribute. + /// + /// TypeExtensionNode is the default extension type used when no type is provided + /// in the definition of an extension point. + /// + [ExtensionNode ("Type", Description="Specifies a class that will be used to create an extension object.")] + [NodeAttribute ("class", typeof(Type), false, ContentType = ContentType.Class, Description="Name of the class. If a value is not provided, the class name will be taken from the 'id' attribute")] + public class TypeExtensionNode: InstanceExtensionNode + { + string typeName; + Type type; + + /// + /// Reads the extension node data + /// + /// + /// The element containing the extension data + /// + /// + /// This method can be overridden to provide a custom method for reading extension node data from an element. + /// The default implementation reads the attributes if the element and assigns the values to the fields + /// and properties of the extension node that have the corresponding [NodeAttribute] decoration. + /// + internal protected override void Read (NodeElement elem) + { + base.Read (elem); + typeName = elem.GetAttribute ("type"); + if (typeName.Length == 0) + typeName = elem.GetAttribute ("class"); + if (typeName.Length == 0) + typeName = elem.GetAttribute ("id"); + } + + /// + /// Creates a new extension object + /// + /// + /// The extension object + /// + public override object CreateInstance () + { + return Activator.CreateInstance (Type); + } + + /// + /// Type of the object that this node creates + /// + public Type Type { + get { + if (type == null) { + if (typeName.Length == 0) + throw new InvalidOperationException ("Type name not specified."); + type = Addin.GetType (typeName, true); + } + return type; + } + } + + /// + /// Name of the type of the object that this node creates + /// + /// The name of the type. + public string TypeName { + get { + return typeName; + } + } + } + + /// + /// An extension node which specifies a type with custom extension metadata + /// + /// + /// This is the default type for type extension nodes bound to a custom extension attribute. + /// + public class TypeExtensionNode: TypeExtensionNode, IAttributedExtensionNode where T:CustomExtensionAttribute + { + T data; + + /// + /// The custom attribute containing the extension metadata + /// + [NodeAttribute] + public T Data { + get { return data; } + internal set { data = value; } + } + + CustomExtensionAttribute IAttributedExtensionNode.Attribute { + get { return data; } + } + } +} diff --git a/mono-addins/Mono.Addins/Mono.Addins/TypeExtensionPointAttribute.cs b/mono-addins/Mono.Addins/Mono.Addins/TypeExtensionPointAttribute.cs new file mode 100644 index 00000000..3bb89f38 --- /dev/null +++ b/mono-addins/Mono.Addins/Mono.Addins/TypeExtensionPointAttribute.cs @@ -0,0 +1,125 @@ +// +// TypeExtensionPointAttribute.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; + +namespace Mono.Addins +{ + /// + /// Declares an extension point bound to a type + /// + [AttributeUsage (AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple=true)] + public class TypeExtensionPointAttribute: Attribute + { + string path; + string nodeName; + Type nodeType; + string nodeTypeName; + string desc; + string name; + Type customAttributeType; + string customAttributeTypeName; + + /// + /// Initializes a new instance + /// + public TypeExtensionPointAttribute () + { + } + + /// + /// Initializes a new instance + /// + /// + /// Path that identifies the extension point + /// + public TypeExtensionPointAttribute (string path) + { + this.path = path; + } + + /// + /// Path that identifies the extension point + /// + public string Path { + get { return path != null ? path : string.Empty; } + set { path = value; } + } + + /// + /// Description of the extension point. + /// + public string Description { + get { return desc != null ? desc : string.Empty; } + set { desc = value; } + } + + /// + /// Element name to be used when defining an extension in an XML manifest. The default name is "Type". + /// + public string NodeName { + get { return nodeName != null && nodeName.Length > 0 ? nodeName : "Type"; } + set { nodeName = value; } + } + + /// + /// Display name of the extension point. + /// + public string Name { + get { return name != null ? name : string.Empty; } + set { name = value; } + } + + /// + /// Type of the extension node to be created for extensions + /// + public Type NodeType { + get { return nodeType != null ? nodeType : typeof(TypeExtensionNode); } + set { nodeType = value; nodeTypeName = value.FullName; } + } + + internal string NodeTypeName { + get { return nodeTypeName != null ? nodeTypeName : typeof(TypeExtensionNode).FullName; } + set { nodeTypeName = value; nodeType = null; } + } + + /// + /// Type of the custom attribute to be used to specify metadata for the extension point + /// + public Type ExtensionAttributeType { + get { return this.customAttributeType; } + set { this.customAttributeType = value; customAttributeTypeName = value.FullName; } + } + + internal string ExtensionAttributeTypeName { + get { return this.customAttributeTypeName; } + set { this.customAttributeTypeName = value; } + } + } +} diff --git a/mono-addins/Mono.Addins/obj/Debug/Mono.Addins.csproj.CoreCompileInputs.cache b/mono-addins/Mono.Addins/obj/Debug/Mono.Addins.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..95b82866 --- /dev/null +++ b/mono-addins/Mono.Addins/obj/Debug/Mono.Addins.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +5d2e67e1510c4a71b7cb3a00e61531d94fcb226e diff --git a/mono-addins/Mono.Addins/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins/obj/Debug/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins/obj/Debug/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins/obj/Debug/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins/obj/Mono.Addins.csproj.nuget.g.props b/mono-addins/Mono.Addins/obj/Mono.Addins.csproj.nuget.g.props new file mode 100644 index 00000000..73a5b5e6 --- /dev/null +++ b/mono-addins/Mono.Addins/obj/Mono.Addins.csproj.nuget.g.props @@ -0,0 +1,18 @@ + + + + True + NuGet + D:\opensim\MonoAddins\mono-addins\Mono.Addins\obj\project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\ld\.nuget\packages\ + PackageReference + 4.9.3 + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins/obj/Mono.Addins.csproj.nuget.g.targets b/mono-addins/Mono.Addins/obj/Mono.Addins.csproj.nuget.g.targets new file mode 100644 index 00000000..f9da2a43 --- /dev/null +++ b/mono-addins/Mono.Addins/obj/Mono.Addins.csproj.nuget.g.targets @@ -0,0 +1,9 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + \ No newline at end of file diff --git a/mono-addins/Mono.Addins/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/mono-addins/Mono.Addins/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 00000000..02bec2d3 Binary files /dev/null and b/mono-addins/Mono.Addins/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/mono-addins/Mono.Addins/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/mono-addins/Mono.Addins/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs b/mono-addins/Mono.Addins/obj/Release/TemporaryGeneratedFile_5937a670-0e60-4077-877b-f7221da3dda1.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs b/mono-addins/Mono.Addins/obj/Release/TemporaryGeneratedFile_E7A71F73-0F8D-4B9B-B56E-8E70B10BC5D3.cs new file mode 100644 index 00000000..e69de29b diff --git a/mono-addins/Mono.Addins/obj/project.assets.json b/mono-addins/Mono.Addins/obj/project.assets.json new file mode 100644 index 00000000..74a97e73 --- /dev/null +++ b/mono-addins/Mono.Addins/obj/project.assets.json @@ -0,0 +1,138 @@ +{ + "version": 3, + "targets": { + ".NETFramework,Version=v4.6": { + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + } + }, + ".NETFramework,Version=v4.6/win": { + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + } + }, + ".NETFramework,Version=v4.6/win-x64": { + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + } + }, + ".NETFramework,Version=v4.6/win-x86": { + "NuGet.Build.Packaging/0.2.0": { + "type": "package", + "build": { + "build/NuGet.Build.Packaging.props": {}, + "build/NuGet.Build.Packaging.targets": {} + } + } + } + }, + "libraries": { + "NuGet.Build.Packaging/0.2.0": { + "sha512": "iqo7f9c+oA12IcelLjD232BMxdGR2Dzrqk00C8w7NiB1sbXa8YHsZGCJkt6CEQKR5XYqZ/8f3z0kHTBJ0Gua3Q==", + "type": "package", + "path": "nuget.build.packaging/0.2.0", + "files": [ + "build/ApiIntersect.exe", + "build/ApiIntersect.exe.config", + "build/GenerateReferenceAssembly.csproj", + "build/ICSharpCode.Decompiler.dll", + "build/ICSharpCode.NRefactory.CSharp.dll", + "build/ICSharpCode.NRefactory.Cecil.dll", + "build/ICSharpCode.NRefactory.Xml.dll", + "build/ICSharpCode.NRefactory.dll", + "build/Mono.Cecil.Mdb.dll", + "build/Mono.Cecil.Pdb.dll", + "build/Mono.Cecil.Rocks.dll", + "build/Mono.Cecil.dll", + "build/Mono.Options.dll", + "build/NuGet.Build.Packaging.Authoring.props", + "build/NuGet.Build.Packaging.Authoring.targets", + "build/NuGet.Build.Packaging.Compatibility.props", + "build/NuGet.Build.Packaging.CrossTargeting.targets", + "build/NuGet.Build.Packaging.Inference.targets", + "build/NuGet.Build.Packaging.Legacy.props", + "build/NuGet.Build.Packaging.Legacy.targets", + "build/NuGet.Build.Packaging.ReferenceAssembly.targets", + "build/NuGet.Build.Packaging.Tasks.dll", + "build/NuGet.Build.Packaging.Tasks.pdb", + "build/NuGet.Build.Packaging.Version.props", + "build/NuGet.Build.Packaging.props", + "build/NuGet.Build.Packaging.targets", + "nuget.build.packaging.0.2.0.nupkg.sha512", + "nuget.build.packaging.nuspec" + ] + } + }, + "projectFileDependencyGroups": { + ".NETFramework,Version=v4.6": [ + "NuGet.Build.Packaging >= 0.2.0" + ] + }, + "packageFolders": { + "C:\\Users\\ld\\.nuget\\packages\\": {} + }, + "project": { + "version": "1.3.7", + "restore": { + "projectUniqueName": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins\\Mono.Addins.csproj", + "projectName": "Mono.Addins", + "projectPath": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins\\Mono.Addins.csproj", + "packagesPath": "C:\\Users\\ld\\.nuget\\packages\\", + "outputPath": "D:\\opensim\\MonoAddins\\mono-addins\\Mono.Addins\\obj\\", + "projectStyle": "PackageReference", + "skipContentFileWrite": true, + "configFilePaths": [ + "C:\\Users\\ld\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net46" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Users\\ld\\AppData\\Local\\Xenko\\NugetDev": {}, + "D:\\xenko\\xenko\\bin\\packages": {}, + "https://api.nuget.org/v3/index.json": {}, + "https://packages.xenko.com/nuget": {} + }, + "frameworks": { + "net46": { + "projectReferences": {} + } + } + }, + "frameworks": { + "net46": { + "dependencies": { + "NuGet.Build.Packaging": { + "target": "Package", + "version": "[0.2.0, )" + } + } + } + }, + "runtimes": { + "win": { + "#import": [] + }, + "win-x64": { + "#import": [] + }, + "win-x86": { + "#import": [] + } + } + } +} \ No newline at end of file diff --git a/mono-addins/Mono.Addins/packages.config b/mono-addins/Mono.Addins/packages.config new file mode 100644 index 00000000..6704d3e4 --- /dev/null +++ b/mono-addins/Mono.Addins/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/mono-addins/Mono.Addins/serializers.config b/mono-addins/Mono.Addins/serializers.config new file mode 100644 index 00000000..c49ea28f --- /dev/null +++ b/mono-addins/Mono.Addins/serializers.config @@ -0,0 +1,16 @@ + + + AddinSystemConfigurationReader + AddinSystemConfigurationWriter + true + Origin.Core.Addins.Setup + AddinSystemConfigurationReaderWriter.cs + + + RepositoryReader + RepositoryWriter + true + Origin.Core.Addins.Setup + RepositoryReaderWriter.cs + + diff --git a/mono-addins/README b/mono-addins/README new file mode 100644 index 00000000..bbfdeedb --- /dev/null +++ b/mono-addins/README @@ -0,0 +1,45 @@ + +Mono.Addins is a generic framework for creating extensible applications, +and for creating libraries which extend those applications. + +For for information about the library, see: +http://www.mono-project.com/Mono.Addins + +Building +-------- + +To build the library execute: + + ./configure + make + +Options: + +--prefix=/path/to/prefix + + Install to the specified prefix + +--enable-gui + + Include GUI support for GTK2 (requires gtk#2) + +--enable-gui-gtk3 + + Include GUI support for GTK3 (requires gtk#3) + +--enable-tests + + Include NUnit tests (requires nunit) + + +Building the samples +-------------------- + +cd to the Samples directory and run make. + +Running the NUnit tests +----------------------- + +To run the NUnit tests, you need to configure the build with --enable-tests, +cd to Tests, and run 'make test'. + diff --git a/mono-addins/Samples/HelloWorld/HelloWorld.sln b/mono-addins/Samples/HelloWorld/HelloWorld.sln new file mode 100644 index 00000000..6520c53a --- /dev/null +++ b/mono-addins/Samples/HelloWorld/HelloWorld.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorld", "HelloWorld\HelloWorld.csproj", "{409DA1E7-DC6D-4B00-858F-0ABF1593AE3C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorldAddin", "HelloWorldAddin\HelloWorldAddin.csproj", "{64A8A62E-133F-4CDB-B174-4B36E0827099}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {409DA1E7-DC6D-4B00-858F-0ABF1593AE3C}.Debug|x86.ActiveCfg = Debug|x86 + {409DA1E7-DC6D-4B00-858F-0ABF1593AE3C}.Debug|x86.Build.0 = Debug|x86 + {409DA1E7-DC6D-4B00-858F-0ABF1593AE3C}.Release|x86.ActiveCfg = Release|x86 + {409DA1E7-DC6D-4B00-858F-0ABF1593AE3C}.Release|x86.Build.0 = Release|x86 + {64A8A62E-133F-4CDB-B174-4B36E0827099}.Debug|x86.ActiveCfg = Debug|Any CPU + {64A8A62E-133F-4CDB-B174-4B36E0827099}.Debug|x86.Build.0 = Debug|Any CPU + {64A8A62E-133F-4CDB-B174-4B36E0827099}.Release|x86.ActiveCfg = Release|Any CPU + {64A8A62E-133F-4CDB-B174-4B36E0827099}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = HelloWorld\HelloWorld.csproj + Policies = $0 + $0.ChangeLogPolicy = $1 + $1.UpdateMode = None + $1.inheritsSet = Mono + $0.VersionControlPolicy = $2 + $2.inheritsSet = Mono + EndGlobalSection +EndGlobal diff --git a/mono-addins/Samples/HelloWorld/HelloWorld/HelloWorld.csproj b/mono-addins/Samples/HelloWorld/HelloWorld/HelloWorld.csproj new file mode 100644 index 00000000..0573afac --- /dev/null +++ b/mono-addins/Samples/HelloWorld/HelloWorld/HelloWorld.csproj @@ -0,0 +1,46 @@ + + + + Debug + x86 + 9.0.21022 + 2.0 + {409DA1E7-DC6D-4B00-858F-0ABF1593AE3C} + Exe + HelloWorld + HelloWorld + v3.5 + + + True + full + False + ..\bin + DEBUG + prompt + 4 + x86 + + + none + False + ..\bin + prompt + 4 + x86 + + + + + ..\..\..\bin\Mono.Addins.dll + + + ..\..\..\bin\Mono.Addins.CecilReflector.dll + + + + + + + + \ No newline at end of file diff --git a/mono-addins/Samples/HelloWorld/HelloWorld/ICommand.cs b/mono-addins/Samples/HelloWorld/HelloWorld/ICommand.cs new file mode 100644 index 00000000..6c4a5d68 --- /dev/null +++ b/mono-addins/Samples/HelloWorld/HelloWorld/ICommand.cs @@ -0,0 +1,41 @@ +// +// ICommand.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Mono.Addins; + +namespace HelloWorld +{ + // This is an interface which can be implemented by add-ins. By applying the TypeExtensionPoint we are + // creating a new extension point. + + [TypeExtensionPoint] + public interface ICommand + { + void Run (); + } +} + diff --git a/mono-addins/Samples/HelloWorld/HelloWorld/Main.cs b/mono-addins/Samples/HelloWorld/HelloWorld/Main.cs new file mode 100644 index 00000000..c71c8265 --- /dev/null +++ b/mono-addins/Samples/HelloWorld/HelloWorld/Main.cs @@ -0,0 +1,51 @@ +// +// Main.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Mono.Addins; + +// Specifies that this assembly is an add-in root, +// which means that it can be extended by add-ins. +[assembly:AddinRoot ("HelloWorld", "1.0")] + +namespace HelloWorld +{ + class MainClass + { + public static void Main (string[] args) + { + // Initializes the add-in engine + AddinManager.Initialize (); + + // Looks for new add-ins and updates the add-in registry. + AddinManager.Registry.Update (null); + + // Gets all commands implemented in add-ins. + foreach (ICommand cmd in AddinManager.GetExtensionObjects (typeof(ICommand))) + cmd.Run (); + } + } +} + diff --git a/mono-addins/Samples/HelloWorld/HelloWorldAddin/HelloCommand.cs b/mono-addins/Samples/HelloWorld/HelloWorldAddin/HelloCommand.cs new file mode 100644 index 00000000..ecd5f82a --- /dev/null +++ b/mono-addins/Samples/HelloWorld/HelloWorldAddin/HelloCommand.cs @@ -0,0 +1,52 @@ +// +// HelloCommand.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using HelloWorld; +using Mono.Addins; + +// Declares that this assembly is an add-in +[assembly:Addin] + +// Declares that this add-in depends on the HelloWorld v1.0 add-in root +[assembly:AddinDependency ("HelloWorld", "1.0")] + +namespace HelloWorldAddin +{ + // The Extension attribute must be applied to declare that this class is extending + // and extension point of the host + + [Extension] + public class HelloCommand: ICommand + { + public void Run () + { + Console.WriteLine ("Hello World!"); + } + + } +} + diff --git a/mono-addins/Samples/HelloWorld/HelloWorldAddin/HelloWorldAddin.csproj b/mono-addins/Samples/HelloWorld/HelloWorldAddin/HelloWorldAddin.csproj new file mode 100644 index 00000000..a01c7357 --- /dev/null +++ b/mono-addins/Samples/HelloWorld/HelloWorldAddin/HelloWorldAddin.csproj @@ -0,0 +1,48 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {64A8A62E-133F-4CDB-B174-4B36E0827099} + Library + HelloWorldAddin + HelloWorldAddin + v3.5 + + + True + full + False + ..\bin + DEBUG + prompt + 4 + False + + + none + False + ..\bin + prompt + 4 + False + + + + + ..\..\..\bin\Mono.Addins.dll + + + + + + + + {409DA1E7-DC6D-4B00-858F-0ABF1593AE3C} + HelloWorld + + + + \ No newline at end of file diff --git a/mono-addins/Samples/HelloWorld/Makefile b/mono-addins/Samples/HelloWorld/Makefile new file mode 100644 index 00000000..26efdd8c --- /dev/null +++ b/mono-addins/Samples/HelloWorld/Makefile @@ -0,0 +1,5 @@ +all: + xbuild + +clean: + xbuild /t:Clean \ No newline at end of file diff --git a/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/HelloWorld.addin.xml b/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/HelloWorld.addin.xml new file mode 100644 index 00000000..5fe9ca18 --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/HelloWorld.addin.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/HelloWorld.csproj b/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/HelloWorld.csproj new file mode 100644 index 00000000..1bf3a91b --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/HelloWorld.csproj @@ -0,0 +1,48 @@ + + + + Debug + x86 + 9.0.21022 + 2.0 + {5F813963-DE40-433A-ABE3-71EACDF19412} + Exe + HelloWorld + HelloWorld + v3.5 + + + True + full + False + ..\bin + DEBUG + prompt + 4 + x86 + + + none + False + ..\bin + prompt + 4 + x86 + + + + + ..\..\..\bin\Mono.Addins.dll + + + + + + + + + HelloWorld.addin.xml + + + + \ No newline at end of file diff --git a/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/ICommand.cs b/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/ICommand.cs new file mode 100644 index 00000000..6048538a --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/ICommand.cs @@ -0,0 +1,39 @@ +// +// ICommand.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Mono.Addins; + +namespace HelloWorld +{ + // This is an interface which can be implemented by add-ins. + + public interface ICommand + { + void Run (); + } +} + diff --git a/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/Main.cs b/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/Main.cs new file mode 100644 index 00000000..7c9f2192 --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/HelloWorld/Main.cs @@ -0,0 +1,48 @@ +// +// Main.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Mono.Addins; + +namespace HelloWorld +{ + class MainClass + { + public static void Main (string[] args) + { + // Initializes the add-in engine + AddinManager.Initialize (); + + // Looks for new add-ins and updates the add-in registry. + AddinManager.Registry.Update (null); + + // Gets all commands implemented in add-ins. + foreach (ICommand cmd in AddinManager.GetExtensionObjects ("/Commands")) + cmd.Run (); + } + } +} + diff --git a/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloCommand.cs b/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloCommand.cs new file mode 100644 index 00000000..ad97a6e2 --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloCommand.cs @@ -0,0 +1,40 @@ +// +// HelloCommand.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (c) 2010 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using HelloWorld; + +namespace HelloWorldAddin +{ + public class HelloCommand: ICommand + { + public void Run () + { + Console.WriteLine ("Hello World!"); + } + } +} + diff --git a/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloWorldAddin.addin.xml b/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloWorldAddin.addin.xml new file mode 100644 index 00000000..865ae4a7 --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloWorldAddin.addin.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloWorldAddin.csproj b/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloWorldAddin.csproj new file mode 100644 index 00000000..9517d629 --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/HelloWorldAddin/HelloWorldAddin.csproj @@ -0,0 +1,50 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {26C85FBC-0A97-4650-807F-E889C6D1F32F} + Library + HelloWorldAddin + HelloWorldAddin + v3.5 + + + True + full + False + ..\bin + DEBUG + prompt + 4 + False + + + none + False + ..\bin + prompt + 4 + False + + + + + + + HelloWorldAddin.addin.xml + + + + + + + + {5F813963-DE40-433A-ABE3-71EACDF19412} + HelloWorld + + + + \ No newline at end of file diff --git a/mono-addins/Samples/HelloWorldWithManifest/HelloWorldWithManifest.sln b/mono-addins/Samples/HelloWorldWithManifest/HelloWorldWithManifest.sln new file mode 100644 index 00000000..126e5131 --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/HelloWorldWithManifest.sln @@ -0,0 +1,30 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorld", "HelloWorld\HelloWorld.csproj", "{5F813963-DE40-433A-ABE3-71EACDF19412}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorldAddin", "HelloWorldAddin\HelloWorldAddin.csproj", "{26C85FBC-0A97-4650-807F-E889C6D1F32F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {26C85FBC-0A97-4650-807F-E889C6D1F32F}.Debug|x86.ActiveCfg = Debug|Any CPU + {26C85FBC-0A97-4650-807F-E889C6D1F32F}.Debug|x86.Build.0 = Debug|Any CPU + {26C85FBC-0A97-4650-807F-E889C6D1F32F}.Release|x86.ActiveCfg = Release|Any CPU + {26C85FBC-0A97-4650-807F-E889C6D1F32F}.Release|x86.Build.0 = Release|Any CPU + {5F813963-DE40-433A-ABE3-71EACDF19412}.Debug|x86.ActiveCfg = Debug|x86 + {5F813963-DE40-433A-ABE3-71EACDF19412}.Debug|x86.Build.0 = Debug|x86 + {5F813963-DE40-433A-ABE3-71EACDF19412}.Release|x86.ActiveCfg = Release|x86 + {5F813963-DE40-433A-ABE3-71EACDF19412}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = HelloWorld\HelloWorld.csproj + Policies = $0 + $0.ChangeLogPolicy = $1 + $1.UpdateMode = None + $1.inheritsSet = Mono + EndGlobalSection +EndGlobal diff --git a/mono-addins/Samples/HelloWorldWithManifest/Makefile b/mono-addins/Samples/HelloWorldWithManifest/Makefile new file mode 100644 index 00000000..26efdd8c --- /dev/null +++ b/mono-addins/Samples/HelloWorldWithManifest/Makefile @@ -0,0 +1,5 @@ +all: + xbuild + +clean: + xbuild /t:Clean \ No newline at end of file diff --git a/mono-addins/Samples/Makefile b/mono-addins/Samples/Makefile new file mode 100644 index 00000000..48da1191 --- /dev/null +++ b/mono-addins/Samples/Makefile @@ -0,0 +1,16 @@ +SUBDIRS = \ + HelloWorld \ + HelloWorldWithManifest \ + WriterService \ + TextEditor + +all: + for subdir in $(SUBDIRS); do \ + cd $$subdir && make && cd ..; \ + done + +clean: + for subdir in $(SUBDIRS); do \ + cd $$subdir && make clean && cd ..; \ + done + diff --git a/mono-addins/Samples/Samples.mdw b/mono-addins/Samples/Samples.mdw new file mode 100644 index 00000000..2f451b13 --- /dev/null +++ b/mono-addins/Samples/Samples.mdw @@ -0,0 +1,9 @@ + + + HelloWorld/HelloWorld.sln + TextEditor/TextEditor.sln + WriterService/WriterService.sln + HelloWorldWithManifest/HelloWorldWithManifest.sln + TextEditorSWF/TextEditorSWF.sln + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/Makefile b/mono-addins/Samples/TextEditor/Makefile new file mode 100644 index 00000000..26efdd8c --- /dev/null +++ b/mono-addins/Samples/TextEditor/Makefile @@ -0,0 +1,5 @@ +all: + xbuild + +clean: + xbuild /t:Clean \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/AssemblyInfo.cs b/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/AssemblyInfo.cs new file mode 100644 index 00000000..af4e2756 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes specify the key for the sign of your assembly. See the +// .NET Framework documentation for more information about signing. +// This is not required, if you don't want signing let these attributes like they're. +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/CSharpCompiler.cs b/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/CSharpCompiler.cs new file mode 100644 index 00000000..7845725a --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/CSharpCompiler.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using System.Diagnostics; +using Mono.Addins; +using TextEditor.CompilerService; + +[assembly:Addin] +[assembly:AddinDependency ("TextEditor.CompilerService", "1.0")] + +namespace TextEditor.CompilerService.CSharp +{ + [Extension] + public class CSharpCompiler: ICompiler + { + public bool CanCompile (string file) + { + return Path.GetExtension (file) == ".cs"; + } + + public string Compile (string file, string outFile) + { + string messages = ""; + + ProcessStartInfo ps = new ProcessStartInfo (); + ps.FileName = "mcs"; + ps.Arguments = "file"; + ps.UseShellExecute = false; + ps.RedirectStandardOutput = true; + Process p = Process.Start (ps); + + string line = null; + while ((line = p.StandardOutput.ReadLine ()) != null) { + messages += line + "\n"; + } + return messages; + } + } +} \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/TextEditor.CompilerService.CSharp.csproj b/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/TextEditor.CompilerService.CSharp.csproj new file mode 100644 index 00000000..a2bf2240 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService.CSharp/TextEditor.CompilerService.CSharp.csproj @@ -0,0 +1,49 @@ + + + + Debug + AnyCPU + 8.0.50727 + {B50C6B48-CA51-4538-B686-40B6F77F8623} + Library + TextEditor.CompilerService.CSharp + 2.0 + TextEditor.CompilerService.CSharp + + + True + full + True + ..\bin + prompt + 4 + True + False + + + none + True + ..\bin + prompt + 4 + True + False + + + + + ..\..\..\bin\Mono.Addins.dll + + + + + {613DC3EA-8A4E-4CE1-8836-DEC8ABC684A8} + TextEditor.CompilerService + + + + + + + + diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService/AssemblyInfo.cs b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/AssemblyInfo.cs new file mode 100644 index 00000000..af4e2756 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes specify the key for the sign of your assembly. See the +// .NET Framework documentation for more information about signing. +// This is not required, if you don't want signing let these attributes like they're. +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService/CompilerManager.cs b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/CompilerManager.cs new file mode 100644 index 00000000..e65ab65c --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/CompilerManager.cs @@ -0,0 +1,34 @@ + +using System; +using Mono.Addins; + +namespace TextEditor.CompilerService +{ + public class CompilerManager + { + public static void Run (string file) + { + ICompiler[] compilers = (ICompiler[]) AddinManager.GetExtensionObjects (typeof(ICompiler)); + + ICompiler compiler = null; + foreach (ICompiler comp in compilers) { + if (comp.CanCompile (file)) { + compiler = comp; + break; + } + } + if (compiler == null) { + string msg = "No compiler available for this kind of file."; + Gtk.MessageDialog dlg = new Gtk.MessageDialog (TextEditorApp.MainWindow, Gtk.DialogFlags.Modal, Gtk.MessageType.Error, Gtk.ButtonsType.Close, msg); + dlg.Run (); + dlg.Destroy (); + return; + } + + string messages = compiler.Compile (file, file + ".exe"); + + TextEditorApp.MainWindow.ConsoleWrite ("Compilation finished.\n"); + TextEditorApp.MainWindow.ConsoleWrite (messages); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService/ICompiler.cs b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/ICompiler.cs new file mode 100644 index 00000000..de63e699 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/ICompiler.cs @@ -0,0 +1,13 @@ + +using System; +using Mono.Addins; + +namespace TextEditor.CompilerService +{ + [TypeExtensionPoint] + public interface ICompiler + { + bool CanCompile (string file); + string Compile (string file, string outFile); + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService/RunCommand.cs b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/RunCommand.cs new file mode 100644 index 00000000..7480bc39 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/RunCommand.cs @@ -0,0 +1,15 @@ + +using System; +using TextEditor; +using Mono.Addins; + +namespace TextEditor.CompilerService +{ + public class RunCommand: ICommand + { + public void Run () + { + CompilerManager.Run (TextEditorApp.OpenFileName); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService/TextEditor.CompilerService.addin.xml b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/TextEditor.CompilerService.addin.xml new file mode 100644 index 00000000..eacf5a4a --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/TextEditor.CompilerService.addin.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mono-addins/Samples/TextEditor/TextEditor.CompilerService/TextEditor.CompilerService.csproj b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/TextEditor.CompilerService.csproj new file mode 100644 index 00000000..97966892 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.CompilerService/TextEditor.CompilerService.csproj @@ -0,0 +1,69 @@ + + + + Debug + AnyCPU + 8.0.50727 + {613DC3EA-8A4E-4CE1-8836-DEC8ABC684A8} + Library + TextEditor.CompilerService + 2.0 + TextEditor.CompilerService + + + True + full + True + ..\bin + prompt + 4 + True + False + + + none + True + ..\bin + prompt + 4 + True + False + + + + False + + + False + + + False + + + False + + + + + ..\..\..\bin\Mono.Addins.dll + + + + + {ED5EC705-1905-4FB6-821B-9464D60727EF} + TextEditorLib + + + + + + + + + + + TextEditor.CompilerService.addin.xml + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditor.Xml/AssemblyInfo.cs b/mono-addins/Samples/TextEditor/TextEditor.Xml/AssemblyInfo.cs new file mode 100644 index 00000000..198a48c0 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.Xml/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.0.0.0")] + +// The following attributes specify the key for the sign of your assembly. See the +// .NET Framework documentation for more information about signing. +// This is not required, if you don't want signing let these attributes like they're. +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] diff --git a/mono-addins/Samples/TextEditor/TextEditor.Xml/EmptyFile.xml b/mono-addins/Samples/TextEditor/TextEditor.Xml/EmptyFile.xml new file mode 100644 index 00000000..fcfff849 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.Xml/EmptyFile.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditor.Xml/FormatXmlCommand.cs b/mono-addins/Samples/TextEditor/TextEditor.Xml/FormatXmlCommand.cs new file mode 100644 index 00000000..ec63370b --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.Xml/FormatXmlCommand.cs @@ -0,0 +1,39 @@ + +using System; +using System.IO; +using System.Xml; +using TextEditor; +using Mono.Addins; + +[assembly: Addin (Namespace="TextEditor")] +[assembly: AddinDependency ("Core", "1.0")] + + +namespace TextEditor.Xml +{ + public class FormatXmlCommand: ICommand + { + public void Run () + { + string text = TextEditorApp.MainWindow.View.Buffer.Text; + XmlDocument doc = new XmlDocument (); + try { + doc.LoadXml (text); + StringWriter sw = new StringWriter (); + XmlTextWriter tw = new XmlTextWriter (sw); + tw.Formatting = Formatting.Indented; + doc.Save (tw); + TextEditorApp.MainWindow.View.Buffer.Text = sw.ToString (); + } + catch { + Gtk.MessageDialog dlg = new Gtk.MessageDialog (TextEditorApp.MainWindow, Gtk.DialogFlags.Modal, Gtk.MessageType.Error, Gtk.ButtonsType.Close, "Error parsing XML."); + dlg.Run (); + dlg.Destroy (); + } + } + } + + class Subno: TextEditor.CopyCommand + { + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditor.Xml/TextEditor.Xml.addin.xml b/mono-addins/Samples/TextEditor/TextEditor.Xml/TextEditor.Xml.addin.xml new file mode 100644 index 00000000..0605d035 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.Xml/TextEditor.Xml.addin.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/mono-addins/Samples/TextEditor/TextEditor.Xml/TextEditor.Xml.csproj b/mono-addins/Samples/TextEditor/TextEditor.Xml/TextEditor.Xml.csproj new file mode 100644 index 00000000..ebb67a84 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.Xml/TextEditor.Xml.csproj @@ -0,0 +1,71 @@ + + + + Debug + AnyCPU + 8.0.50727 + {4EFD5979-4000-4A32-A687-33D140081F1C} + Library + XmlAddin + 2.0 + TextEditor.Xml + + + True + full + True + ..\bin + prompt + 4 + True + False + + + none + True + ..\bin + prompt + 4 + True + False + + + + False + + + False + + + False + + + False + + + + + + ..\..\..\bin\Mono.Addins.dll + + + + + {ED5EC705-1905-4FB6-821B-9464D60727EF} + TextEditorLib + + + + + + + + + TextEditor.Xml.addin.xml + + + EmptyFile.xml + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditor.sln b/mono-addins/Samples/TextEditor/TextEditor.sln new file mode 100644 index 00000000..05f11171 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextEditor", "TextEditor\TextEditor.csproj", "{3592CAFF-C74F-4036-AC22-F0D9DA31CC09}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextEditorLib", "TextEditorLib\TextEditorLib.csproj", "{ED5EC705-1905-4FB6-821B-9464D60727EF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextEditor.CompilerService", "TextEditor.CompilerService\TextEditor.CompilerService.csproj", "{613DC3EA-8A4E-4CE1-8836-DEC8ABC684A8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextEditor.CompilerService.CSharp", "TextEditor.CompilerService.CSharp\TextEditor.CompilerService.CSharp.csproj", "{B50C6B48-CA51-4538-B686-40B6F77F8623}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextEditor.Xml", "TextEditor.Xml\TextEditor.Xml.csproj", "{4EFD5979-4000-4A32-A687-33D140081F1C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3592CAFF-C74F-4036-AC22-F0D9DA31CC09}.Debug|x86.ActiveCfg = Debug|x86 + {3592CAFF-C74F-4036-AC22-F0D9DA31CC09}.Debug|x86.Build.0 = Debug|x86 + {3592CAFF-C74F-4036-AC22-F0D9DA31CC09}.Release|x86.ActiveCfg = Release|x86 + {3592CAFF-C74F-4036-AC22-F0D9DA31CC09}.Release|x86.Build.0 = Release|x86 + {4EFD5979-4000-4A32-A687-33D140081F1C}.Debug|x86.ActiveCfg = Debug|Any CPU + {4EFD5979-4000-4A32-A687-33D140081F1C}.Debug|x86.Build.0 = Debug|Any CPU + {4EFD5979-4000-4A32-A687-33D140081F1C}.Release|x86.ActiveCfg = Release|Any CPU + {4EFD5979-4000-4A32-A687-33D140081F1C}.Release|x86.Build.0 = Release|Any CPU + {613DC3EA-8A4E-4CE1-8836-DEC8ABC684A8}.Debug|x86.ActiveCfg = Debug|Any CPU + {613DC3EA-8A4E-4CE1-8836-DEC8ABC684A8}.Debug|x86.Build.0 = Debug|Any CPU + {613DC3EA-8A4E-4CE1-8836-DEC8ABC684A8}.Release|x86.ActiveCfg = Release|Any CPU + {613DC3EA-8A4E-4CE1-8836-DEC8ABC684A8}.Release|x86.Build.0 = Release|Any CPU + {B50C6B48-CA51-4538-B686-40B6F77F8623}.Debug|x86.ActiveCfg = Debug|Any CPU + {B50C6B48-CA51-4538-B686-40B6F77F8623}.Debug|x86.Build.0 = Debug|Any CPU + {B50C6B48-CA51-4538-B686-40B6F77F8623}.Release|x86.ActiveCfg = Release|Any CPU + {B50C6B48-CA51-4538-B686-40B6F77F8623}.Release|x86.Build.0 = Release|Any CPU + {ED5EC705-1905-4FB6-821B-9464D60727EF}.Debug|x86.ActiveCfg = Debug|Any CPU + {ED5EC705-1905-4FB6-821B-9464D60727EF}.Debug|x86.Build.0 = Debug|Any CPU + {ED5EC705-1905-4FB6-821B-9464D60727EF}.Release|x86.ActiveCfg = Release|Any CPU + {ED5EC705-1905-4FB6-821B-9464D60727EF}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = TextEditor\TextEditor.csproj + Policies = $0 + $0.ChangeLogPolicy = $1 + $1.UpdateMode = None + $1.inheritsSet = Mono + EndGlobalSection +EndGlobal diff --git a/mono-addins/Samples/TextEditor/TextEditor/Main.cs b/mono-addins/Samples/TextEditor/TextEditor/Main.cs new file mode 100644 index 00000000..bcfadd8e --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor/Main.cs @@ -0,0 +1,53 @@ +using System; +using Gtk; +using Mono.Addins; + +namespace TextEditor +{ + class MainClass + { + public static void Main (string[] args) + { + Application.Init (); + + AddinManager.AddinLoadError += OnLoadError; + AddinManager.AddinLoaded += OnLoad; + AddinManager.AddinUnloaded += OnUnload; + + AddinManager.Initialize (); + AddinManager.Registry.Update (null); + AddinManager.ExtensionChanged += OnExtensionChange; + + + MainWindow win = new MainWindow (); + + foreach (ICommand cmd in AddinManager.GetExtensionObjects ("/TextEditor/StartupCommands")) + cmd.Run (); + + win.Show (); + Application.Run (); + } + + static void OnLoadError (object s, AddinErrorEventArgs args) + { + Console.WriteLine ("Add-in error: " + args.Message); + Console.WriteLine (args.AddinId); + Console.WriteLine (args.Exception); + } + + static void OnLoad (object s, AddinEventArgs args) + { + Console.WriteLine ("Add-in loaded: " + args.AddinId); + } + + static void OnUnload (object s, AddinEventArgs args) + { + Console.WriteLine ("Add-in unloaded: " + args.AddinId); + } + + static void OnExtensionChange (object s, ExtensionEventArgs args) + { + Console.WriteLine ("Extension changed: " + args.Path); + } + } +} \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditor/TextEditor.csproj b/mono-addins/Samples/TextEditor/TextEditor/TextEditor.csproj new file mode 100644 index 00000000..47e17bf6 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor/TextEditor.csproj @@ -0,0 +1,57 @@ + + + + Debug + x86 + 9.0.21022 + 2.0 + {3592CAFF-C74F-4036-AC22-F0D9DA31CC09} + Exe + TextEditor + TextEditor + v3.5 + + + True + full + False + ..\bin + DEBUG + prompt + 4 + x86 + + + none + False + ..\bin + prompt + 4 + x86 + + + + + + + + ..\..\..\bin\Mono.Addins.dll + + + + + + + + + {ED5EC705-1905-4FB6-821B-9464D60727EF} + TextEditorLib + + + + + gui.stetic + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditor/gtk-gui/generated.cs b/mono-addins/Samples/TextEditor/TextEditor/gtk-gui/generated.cs new file mode 100644 index 00000000..9ef33639 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor/gtk-gui/generated.cs @@ -0,0 +1,29 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Stetic +{ + internal class Gui + { + private static bool initialized; + + internal static void Initialize (Gtk.Widget iconRenderer) + { + if ((Stetic.Gui.initialized == false)) { + Stetic.Gui.initialized = true; + } + } + } + + internal class ActionGroups + { + public static Gtk.ActionGroup GetActionGroup (System.Type type) + { + return Stetic.ActionGroups.GetActionGroup (type.FullName); + } + + public static Gtk.ActionGroup GetActionGroup (string name) + { + return null; + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditor/gtk-gui/gui.stetic b/mono-addins/Samples/TextEditor/TextEditor/gtk-gui/gui.stetic new file mode 100644 index 00000000..e1c3e4f7 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditor/gtk-gui/gui.stetic @@ -0,0 +1,6 @@ + + + + 2.10.3 + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/AssemblyInfo.cs b/mono-addins/Samples/TextEditor/TextEditorLib/AssemblyInfo.cs new file mode 100644 index 00000000..e7981d7e --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using Mono.Addins; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly: AssemblyVersion("1.0.0.0")] + +// The following attributes specify the key for the sign of your assembly. See the +// .NET Framework documentation for more information about signing. +// This is not required, if you don't want signing let these attributes like they're. +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] + + + + diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Commands/CopyCommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/CopyCommand.cs new file mode 100644 index 00000000..c8dae47b --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/CopyCommand.cs @@ -0,0 +1,14 @@ + +using System; + +namespace TextEditor +{ + public class CopyCommand: ICommand + { + public void Run () + { + Gtk.Clipboard clipboard = Gtk.Clipboard.Get (Gdk.Atom.Intern ("CLIPBOARD", false)); + TextEditorApp.MainWindow.View.Buffer.CopyClipboard (clipboard); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Commands/CutCommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/CutCommand.cs new file mode 100644 index 00000000..751cac7c --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/CutCommand.cs @@ -0,0 +1,16 @@ + +using System; + +namespace TextEditor +{ + + + public class CutCommand: ICommand + { + public void Run () + { + Gtk.Clipboard clipboard = Gtk.Clipboard.Get (Gdk.Atom.Intern ("CLIPBOARD", false)); + TextEditorApp.MainWindow.View.Buffer.CutClipboard (clipboard, true); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Commands/ExitCommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/ExitCommand.cs new file mode 100644 index 00000000..314ebdfe --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/ExitCommand.cs @@ -0,0 +1,13 @@ + +using System; + +namespace TextEditor +{ + public class ExitCommand: ICommand + { + public void Run () + { + Gtk.Application.Quit (); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Commands/NewCommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/NewCommand.cs new file mode 100644 index 00000000..c3776181 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/NewCommand.cs @@ -0,0 +1,13 @@ + +using System; + +namespace TextEditor +{ + public class NewCommand: ICommand + { + public void Run () + { + TextEditorApp.NewFile (""); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Commands/OpenCommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/OpenCommand.cs new file mode 100644 index 00000000..f3701635 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/OpenCommand.cs @@ -0,0 +1,23 @@ + +using System; +using System.IO; + +namespace TextEditor +{ + public class OpenCommand: ICommand + { + public void Run () + { + Gtk.FileChooserDialog fcd = new Gtk.FileChooserDialog ("Open File", null, Gtk.FileChooserAction.Open); + fcd.AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel); + fcd.AddButton (Gtk.Stock.Open, Gtk.ResponseType.Ok); + fcd.DefaultResponse = Gtk.ResponseType.Ok; + fcd.SelectMultiple = false; + + Gtk.ResponseType response = (Gtk.ResponseType) fcd.Run (); + if (response == Gtk.ResponseType.Ok) + TextEditorApp.OpenFile (fcd.Filename); + fcd.Destroy (); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Commands/PasteCommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/PasteCommand.cs new file mode 100644 index 00000000..6a09738d --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/PasteCommand.cs @@ -0,0 +1,14 @@ + +using System; + +namespace TextEditor +{ + public class PasteCommand: ICommand + { + public void Run () + { + Gtk.Clipboard clipboard = Gtk.Clipboard.Get (Gdk.Atom.Intern ("CLIPBOARD", false)); + TextEditorApp.MainWindow.View.Buffer.PasteClipboard (clipboard); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Commands/SaveCommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/SaveCommand.cs new file mode 100644 index 00000000..5ab9987c --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/SaveCommand.cs @@ -0,0 +1,13 @@ + +using System; + +namespace TextEditor +{ + public class SaveCommand: ICommand + { + public void Run () + { + TextEditorApp.SaveFile (); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Commands/SetupCommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/SetupCommand.cs new file mode 100644 index 00000000..24624508 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Commands/SetupCommand.cs @@ -0,0 +1,14 @@ + +using System; +using Mono.Addins.Gui; + +namespace TextEditor +{ + public class SetupCommand: ICommand + { + public void Run () + { + AddinManagerWindow.Run (TextEditorApp.MainWindow); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/FileTemplateNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/FileTemplateNode.cs new file mode 100644 index 00000000..5fe93fff --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/FileTemplateNode.cs @@ -0,0 +1,27 @@ + +using System; +using System.IO; +using Mono.Addins; + +namespace TextEditor +{ + public class FileTemplateNode: ExtensionNode + { + [NodeAttribute] + string resource; + + [NodeAttribute] + string name; + + public string Name { + get { return name != null ? name : Id; } + } + + public virtual string GetContent () + { + using (StreamReader sr = new StreamReader(Addin.GetResource (resource))) { + return sr.ReadToEnd (); + } + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuItemNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuItemNode.cs new file mode 100644 index 00000000..e6e0fdf6 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuItemNode.cs @@ -0,0 +1,38 @@ + +using System; +using Mono.Addins; + +namespace TextEditor +{ + [ExtensionNode ("MenuItem")] + public class MenuItemNode: MenuNode + { + [NodeAttribute] + string label; + + [NodeAttribute] + string icon; + + [NodeAttribute] + string commandType; + + static Gtk.AccelGroup accelGroup = new Gtk.AccelGroup (); + + public override Gtk.MenuItem GetMenuItem () + { + Gtk.MenuItem item; + if (icon != null) + item = new Gtk.ImageMenuItem (icon, accelGroup); + else + item = new Gtk.MenuItem (label); + item.Activated += OnClicked; + return item; + } + + void OnClicked (object s, EventArgs a) + { + ICommand command = (ICommand) Addin.CreateInstance (commandType); + command.Run (); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuNode.cs new file mode 100644 index 00000000..bcde02d0 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuNode.cs @@ -0,0 +1,13 @@ + +using System; +using Mono.Addins; + +namespace TextEditor +{ + public abstract class MenuNode: ExtensionNode + { + // Abstract method to be implemented by subclasses, and which + // should return a menu item. + public abstract Gtk.MenuItem GetMenuItem (); + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuSeparatorNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuSeparatorNode.cs new file mode 100644 index 00000000..bfd6ce6b --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/MenuSeparatorNode.cs @@ -0,0 +1,15 @@ + +using System; +using Mono.Addins; + +namespace TextEditor +{ + [ExtensionNode ("MenuSeparator")] + public class MenuSeparatorNode: MenuNode + { + public override Gtk.MenuItem GetMenuItem () + { + return new Gtk.SeparatorMenuItem (); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/SubmenuNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/SubmenuNode.cs new file mode 100644 index 00000000..d2114456 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/SubmenuNode.cs @@ -0,0 +1,26 @@ + +using System; +using Mono.Addins; + +namespace TextEditor +{ + [ExtensionNode ("Menu")] + [ExtensionNodeChild (typeof(MenuItemNode))] + [ExtensionNodeChild (typeof(MenuSeparatorNode))] + [ExtensionNodeChild (typeof(SubmenuNode))] + public class SubmenuNode: MenuNode + { + [NodeAttribute] + string label; + + public override Gtk.MenuItem GetMenuItem () + { + Gtk.MenuItem it = new Gtk.MenuItem (label); + Gtk.Menu submenu = new Gtk.Menu (); + foreach (MenuNode node in ChildNodes) + submenu.Insert (node.GetMenuItem (), -1); + it.Submenu = submenu; + return it; + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/TemplateCategoryNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/TemplateCategoryNode.cs new file mode 100644 index 00000000..3161eff0 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/TemplateCategoryNode.cs @@ -0,0 +1,21 @@ + +using System; +using Mono.Addins; + +namespace TextEditor +{ + public class TemplateCategoryNode: ExtensionNode + { + [NodeAttribute] + string name; + + public string Name { + get { + if (name != null && name.Length > 0) + return name; + else + return Id; + } + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolButtonNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolButtonNode.cs new file mode 100644 index 00000000..4c3c7ec4 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolButtonNode.cs @@ -0,0 +1,28 @@ + +using System; +using Mono.Addins; + +namespace TextEditor +{ + public class ToolButtonNode: ToolbarNode + { + [NodeAttribute] + string icon; + + [NodeAttribute] + string commandType; + + public override Gtk.ToolItem GetToolItem () + { + Gtk.ToolButton but = new Gtk.ToolButton (icon); + but.Clicked += OnClicked; + return but; + } + + void OnClicked (object s, EventArgs a) + { + ICommand command = (ICommand) Addin.CreateInstance (commandType); + command.Run (); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolSeparatorNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolSeparatorNode.cs new file mode 100644 index 00000000..b46106b9 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolSeparatorNode.cs @@ -0,0 +1,13 @@ + +using System; + +namespace TextEditor +{ + public class ToolSeparatorNode: ToolbarNode + { + public override Gtk.ToolItem GetToolItem () + { + return new Gtk.SeparatorToolItem (); + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolbarNode.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolbarNode.cs new file mode 100644 index 00000000..57b647ad --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ExtensionNodes/ToolbarNode.cs @@ -0,0 +1,11 @@ + +using System; +using Mono.Addins; + +namespace TextEditor +{ + public abstract class ToolbarNode: ExtensionNode + { + public abstract Gtk.ToolItem GetToolItem (); + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/ICommand.cs b/mono-addins/Samples/TextEditor/TextEditorLib/ICommand.cs new file mode 100644 index 00000000..0ca66bba --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/ICommand.cs @@ -0,0 +1,12 @@ + +using System; +using Mono.Addins; + +namespace TextEditor +{ + [TypeExtensionPoint ("/TextEditor/StartupCommands")] + public interface ICommand + { + void Run (); + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/MainWindow.cs b/mono-addins/Samples/TextEditor/TextEditorLib/MainWindow.cs new file mode 100644 index 00000000..22d09aa9 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/MainWindow.cs @@ -0,0 +1,107 @@ +using System; +using Gtk; +using Mono.Addins; +using TextEditor; + +public partial class MainWindow: Gtk.Window +{ + internal static MainWindow Instance; + + public MainWindow (): base (Gtk.WindowType.Toplevel) + { + Instance = this; + Build (); + + AddinManager.ExtensionChanged += OnExtensionChanged; + BuildToolbar (); + BuildMenu (); + } + + public void ConsoleWrite (string txt) + { + console.Show (); + consoleView.Buffer.Text += txt; + consoleView.ScrollToMark (consoleView.Buffer.InsertMark, 0d, false, 0d, 0d); + } + + void BuildToolbar () + { + // Clean the toolbar + foreach (Gtk.Widget w in toolbar.Children) + toolbar.Remove (w); + + // Add the new buttons + foreach (ToolbarNode node in AddinManager.GetExtensionNodes ("/TextEditor/ToolbarButtons")) + toolbar.Insert (node.GetToolItem (), -1); + + toolbar.ShowAll (); + } + + void BuildMenu () + { + // Clean the toolbar + foreach (Gtk.Widget w in menubar.Children) + menubar.Remove (w); + + // Add the new buttons + foreach (MenuNode node in AddinManager.GetExtensionNodes ("/TextEditor/MainMenu")) + menubar.Insert (node.GetMenuItem (), -1); + + // Create the menu for creating documents from templates + + Gtk.Menu menu = BuildTemplateItems (AddinManager.GetExtensionNodes ("/TextEditor/Templates")); + Gtk.MenuItem it = new MenuItem ("New From Template"); + it.Submenu = menu; + + Gtk.MenuItem men = (Gtk.MenuItem) menubar.Children [0]; + ((Gtk.Menu)men.Submenu).Insert (it, 1); + + menubar.ShowAll (); + } + + Gtk.Menu BuildTemplateItems (ExtensionNodeList nodes) + { + Gtk.Menu menu = new Gtk.Menu (); + foreach (ExtensionNode tn in nodes) { + Gtk.MenuItem item; + if (tn is TemplateCategoryNode) { + TemplateCategoryNode cat = (TemplateCategoryNode) tn; + item = new Gtk.MenuItem (cat.Name); + item.Submenu = BuildTemplateItems (cat.ChildNodes); + } + else { + FileTemplateNode t = (FileTemplateNode) tn; + item = new Gtk.MenuItem (t.Name); + item.Activated += delegate { + TextEditor.TextEditorApp.NewFile (t.GetContent ()); + }; + } + menu.Insert (item, -1); + } + return menu; + } + + + void OnExtensionChanged (object o, ExtensionEventArgs args) + { + if (args.PathChanged ("/TextEditor/ToolbarButtons")) + BuildToolbar (); + else if (args.PathChanged ("/TextEditor/MainMenu") || args.PathChanged ("/TextEditor/Templates")) + BuildMenu (); + } + + protected void OnDeleteEvent (object sender, DeleteEventArgs a) + { + Application.Quit (); + a.RetVal = true; + } + + protected virtual void OnButton1Clicked(object sender, System.EventArgs e) + { + console.Hide (); + } + + public Gtk.TextView View { + get { return textview; } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/OpenFileCondition.cs b/mono-addins/Samples/TextEditor/TextEditorLib/OpenFileCondition.cs new file mode 100644 index 00000000..f3356b45 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/OpenFileCondition.cs @@ -0,0 +1,33 @@ + +using System; +using System.IO; +using Mono.Addins; + +namespace TextEditor +{ + public class OpenFileCondition: ConditionType + { + public OpenFileCondition () + { + // It's important to notify changes in the status of a condition, + // to make sure the extension points are properly updated. + TextEditorApp.OpenFileChanged += delegate { + NotifyChanged (); + }; + } + + public override bool Evaluate (NodeElement conditionNode) + { + // Get the required extension value from an attribute, + // and check againts the extension of the currently open document + string val = conditionNode.GetAttribute ("extension"); + if (val.Length > 0) { + string ext = Path.GetExtension (TextEditorApp.OpenFileName); + foreach (string requiredExtension in val.Split (',')) + if (ext == "." + requiredExtension) + return true; + } + return false; + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Templates/ChangeLogTemplate.txt b/mono-addins/Samples/TextEditor/TextEditorLib/Templates/ChangeLogTemplate.txt new file mode 100644 index 00000000..589bf180 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Templates/ChangeLogTemplate.txt @@ -0,0 +1,4 @@ +yyyy-mm-dd Developer name + + * File name: change done + diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Templates/DotConfigTemplate.txt b/mono-addins/Samples/TextEditor/TextEditorLib/Templates/DotConfigTemplate.txt new file mode 100644 index 00000000..d2437a12 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Templates/DotConfigTemplate.txt @@ -0,0 +1,4 @@ + + + + diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Templates/TextEditor.addin.xml b/mono-addins/Samples/TextEditor/TextEditorLib/Templates/TextEditor.addin.xml new file mode 100644 index 00000000..ad75f083 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Templates/TextEditor.addin.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/Templates/WorkReport.txt b/mono-addins/Samples/TextEditor/TextEditorLib/Templates/WorkReport.txt new file mode 100644 index 00000000..b3b53a0e --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/Templates/WorkReport.txt @@ -0,0 +1,8 @@ + +Work done last week: +* ... +* ... + +Work to do next week: +* ... +* ... \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/TextEditorApp.cs b/mono-addins/Samples/TextEditor/TextEditorLib/TextEditorApp.cs new file mode 100644 index 00000000..81a31e9d --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/TextEditorApp.cs @@ -0,0 +1,74 @@ + +using System; +using System.IO; + +namespace TextEditor +{ + public class TextEditorApp + { + static string openFile = ""; + + private TextEditorApp() + { + } + + public static string OpenFileName { + get { return openFile; } + } + + public static MainWindow MainWindow { + get { return MainWindow.Instance; } + } + + public static void OpenFile (string file) + { + using (StreamReader sr = new StreamReader (file)) { + MainWindow.View.Buffer.Text = sr.ReadToEnd (); + } + SetOpenFile (file); + } + + public static void SaveFile () + { + if (openFile == "") { + Gtk.FileChooserDialog fcd = new Gtk.FileChooserDialog ("Save File", null, Gtk.FileChooserAction.Save); + fcd.AddButton (Gtk.Stock.Cancel, Gtk.ResponseType.Cancel); + fcd.AddButton (Gtk.Stock.Open, Gtk.ResponseType.Ok); + fcd.DefaultResponse = Gtk.ResponseType.Ok; + fcd.SelectMultiple = false; + + Gtk.ResponseType response = (Gtk.ResponseType) fcd.Run (); + if (response != Gtk.ResponseType.Ok) { + fcd.Destroy (); + return; + } + + SetOpenFile (fcd.Filename); + fcd.Destroy (); + } + using (StreamWriter sr = new StreamWriter (openFile)) { + sr.Write (TextEditorApp.MainWindow.View.Buffer.Text); + } + } + + public static void NewFile (string content) + { + SetOpenFile (""); + MainWindow.View.Buffer.Text = content; + } + + static void SetOpenFile (string file) + { + openFile = file; + if (file.Length > 0) + MainWindow.Title = Path.GetFileName (file); + else + MainWindow.Title = "New File"; + + if (OpenFileChanged != null) + OpenFileChanged (null, EventArgs.Empty); + } + + public static event EventHandler OpenFileChanged; + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/TextEditorLib.csproj b/mono-addins/Samples/TextEditor/TextEditorLib/TextEditorLib.csproj new file mode 100644 index 00000000..477bbb5c --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/TextEditorLib.csproj @@ -0,0 +1,98 @@ + + + + Debug + AnyCPU + 8.0.50727 + {ED5EC705-1905-4FB6-821B-9464D60727EF} + Library + TextEditorLib + 2.0 + TextEditorLib + + + True + full + True + ..\bin + prompt + 4 + True + False + + + none + True + ..\bin + prompt + 4 + True + False + + + + False + + + False + + + False + + + False + + + + + ..\..\..\bin\Mono.Addins.dll + + + ..\..\..\bin\Mono.Addins.Gui.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gui.stetic + + + ChangeLogTemplate.txt + + + DotConfigTemplate.txt + + + TextEditor.addin.xml + + + WorkReport.txt + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/MainWindow.cs b/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/MainWindow.cs new file mode 100644 index 00000000..089d0f08 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/MainWindow.cs @@ -0,0 +1,154 @@ + +// This file has been generated by the GUI designer. Do not modify. + +public partial class MainWindow +{ + private global::Gtk.UIManager UIManager; + private global::Gtk.VBox vbox2; + private global::Gtk.MenuBar menubar; + private global::Gtk.Toolbar toolbar; + private global::Gtk.ScrolledWindow scrolledwindow1; + private global::Gtk.TextView textview; + private global::Gtk.VBox console; + private global::Gtk.HBox hbox1; + private global::Gtk.Label label1; + private global::Gtk.Button button1; + private global::Gtk.ScrolledWindow scrolledwindow2; + private global::Gtk.TextView consoleView; + private global::Gtk.Statusbar statusbar1; + + protected virtual void Build () + { + global::Stetic.Gui.Initialize (this); + // Widget MainWindow + this.UIManager = new global::Gtk.UIManager (); + global::Gtk.ActionGroup w1 = new global::Gtk.ActionGroup ("Default"); + this.UIManager.InsertActionGroup (w1, 0); + this.AddAccelGroup (this.UIManager.AccelGroup); + this.Name = "MainWindow"; + this.Title = global::Mono.Unix.Catalog.GetString ("Text Editor"); + // Container child MainWindow.Gtk.Container+ContainerChild + this.vbox2 = new global::Gtk.VBox (); + this.vbox2.Name = "vbox2"; + // Container child vbox2.Gtk.Box+BoxChild + this.UIManager.AddUiFromString (""); + this.menubar = ((global::Gtk.MenuBar)(this.UIManager.GetWidget ("/menubar"))); + this.menubar.Name = "menubar"; + this.vbox2.Add (this.menubar); + global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.menubar])); + w2.Position = 0; + w2.Expand = false; + w2.Fill = false; + // Container child vbox2.Gtk.Box+BoxChild + this.UIManager.AddUiFromString (""); + this.toolbar = ((global::Gtk.Toolbar)(this.UIManager.GetWidget ("/toolbar"))); + this.toolbar.Name = "toolbar"; + this.toolbar.ShowArrow = false; + this.toolbar.ToolbarStyle = ((global::Gtk.ToolbarStyle)(0)); + this.toolbar.IconSize = ((global::Gtk.IconSize)(3)); + this.vbox2.Add (this.toolbar); + global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.toolbar])); + w3.Position = 1; + w3.Expand = false; + w3.Fill = false; + // Container child vbox2.Gtk.Box+BoxChild + this.scrolledwindow1 = new global::Gtk.ScrolledWindow (); + this.scrolledwindow1.CanFocus = true; + this.scrolledwindow1.Name = "scrolledwindow1"; + this.scrolledwindow1.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledwindow1.Gtk.Container+ContainerChild + this.textview = new global::Gtk.TextView (); + this.textview.CanFocus = true; + this.textview.Name = "textview"; + this.scrolledwindow1.Add (this.textview); + this.vbox2.Add (this.scrolledwindow1); + global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.scrolledwindow1])); + w5.Position = 2; + // Container child vbox2.Gtk.Box+BoxChild + this.console = new global::Gtk.VBox (); + this.console.Name = "console"; + this.console.Spacing = 6; + this.console.BorderWidth = ((uint)(6)); + // Container child console.Gtk.Box+BoxChild + this.hbox1 = new global::Gtk.HBox (); + this.hbox1.Name = "hbox1"; + // Container child hbox1.Gtk.Box+BoxChild + this.label1 = new global::Gtk.Label (); + this.label1.Name = "label1"; + this.label1.Xalign = 0F; + this.label1.LabelProp = global::Mono.Unix.Catalog.GetString ("Console"); + this.hbox1.Add (this.label1); + global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.label1])); + w6.Position = 0; + w6.Expand = false; + w6.Fill = false; + // Container child hbox1.Gtk.Box+BoxChild + this.button1 = new global::Gtk.Button (); + this.button1.WidthRequest = 27; + this.button1.HeightRequest = 20; + this.button1.Name = "button1"; + this.button1.UseUnderline = true; + this.button1.Relief = ((global::Gtk.ReliefStyle)(2)); + // Container child button1.Gtk.Container+ContainerChild + global::Gtk.Alignment w7 = new global::Gtk.Alignment (0.5F, 0.5F, 0F, 0F); + // Container child GtkAlignment.Gtk.Container+ContainerChild + global::Gtk.HBox w8 = new global::Gtk.HBox (); + w8.Spacing = 2; + // Container child GtkHBox.Gtk.Container+ContainerChild + global::Gtk.Image w9 = new global::Gtk.Image (); + w9.Pixbuf = global::Stetic.IconLoader.LoadIcon (this, "gtk-close", global::Gtk.IconSize.Menu); + w8.Add (w9); + // Container child GtkHBox.Gtk.Container+ContainerChild + global::Gtk.Label w11 = new global::Gtk.Label (); + w8.Add (w11); + w7.Add (w8); + this.button1.Add (w7); + this.hbox1.Add (this.button1); + global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.button1])); + w15.PackType = ((global::Gtk.PackType)(1)); + w15.Position = 1; + w15.Expand = false; + w15.Fill = false; + this.console.Add (this.hbox1); + global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.console [this.hbox1])); + w16.Position = 0; + w16.Expand = false; + w16.Fill = false; + // Container child console.Gtk.Box+BoxChild + this.scrolledwindow2 = new global::Gtk.ScrolledWindow (); + this.scrolledwindow2.CanFocus = true; + this.scrolledwindow2.Name = "scrolledwindow2"; + this.scrolledwindow2.ShadowType = ((global::Gtk.ShadowType)(1)); + // Container child scrolledwindow2.Gtk.Container+ContainerChild + this.consoleView = new global::Gtk.TextView (); + this.consoleView.CanFocus = true; + this.consoleView.Name = "consoleView"; + this.scrolledwindow2.Add (this.consoleView); + this.console.Add (this.scrolledwindow2); + global::Gtk.Box.BoxChild w18 = ((global::Gtk.Box.BoxChild)(this.console [this.scrolledwindow2])); + w18.Position = 1; + this.vbox2.Add (this.console); + global::Gtk.Box.BoxChild w19 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.console])); + w19.Position = 3; + w19.Expand = false; + w19.Fill = false; + // Container child vbox2.Gtk.Box+BoxChild + this.statusbar1 = new global::Gtk.Statusbar (); + this.statusbar1.Name = "statusbar1"; + this.statusbar1.Spacing = 2; + this.vbox2.Add (this.statusbar1); + global::Gtk.Box.BoxChild w20 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.statusbar1])); + w20.Position = 4; + w20.Expand = false; + w20.Fill = false; + this.Add (this.vbox2); + if ((this.Child != null)) { + this.Child.ShowAll (); + } + this.DefaultWidth = 586; + this.DefaultHeight = 356; + this.Show (); + this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent); + this.button1.Clicked += new global::System.EventHandler (this.OnButton1Clicked); + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/generated.cs b/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/generated.cs new file mode 100644 index 00000000..d6085481 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/generated.cs @@ -0,0 +1,63 @@ + +// This file has been generated by the GUI designer. Do not modify. +namespace Stetic +{ + internal class Gui + { + private static bool initialized; + + internal static void Initialize (Gtk.Widget iconRenderer) + { + if ((Stetic.Gui.initialized == false)) { + Stetic.Gui.initialized = true; + } + } + } + + internal class IconLoader + { + public static Gdk.Pixbuf LoadIcon (Gtk.Widget widget, string name, Gtk.IconSize size) + { + Gdk.Pixbuf res = widget.RenderIcon (name, size, null); + if ((res != null)) { + return res; + } else { + int sz; + int sy; + global::Gtk.Icon.SizeLookup (size, out sz, out sy); + try { + return Gtk.IconTheme.Default.LoadIcon (name, sz, 0); + } catch (System.Exception) { + if ((name != "gtk-missing-image")) { + return Stetic.IconLoader.LoadIcon (widget, "gtk-missing-image", size); + } else { + Gdk.Pixmap pmap = new Gdk.Pixmap (Gdk.Screen.Default.RootWindow, sz, sz); + Gdk.GC gc = new Gdk.GC (pmap); + gc.RgbFgColor = new Gdk.Color (255, 255, 255); + pmap.DrawRectangle (gc, true, 0, 0, sz, sz); + gc.RgbFgColor = new Gdk.Color (0, 0, 0); + pmap.DrawRectangle (gc, false, 0, 0, (sz - 1), (sz - 1)); + gc.SetLineAttributes (3, Gdk.LineStyle.Solid, Gdk.CapStyle.Round, Gdk.JoinStyle.Round); + gc.RgbFgColor = new Gdk.Color (255, 0, 0); + pmap.DrawLine (gc, (sz / 4), (sz / 4), ((sz - 1) - (sz / 4)), ((sz - 1) - (sz / 4))); + pmap.DrawLine (gc, ((sz - 1) - (sz / 4)), (sz / 4), (sz / 4), ((sz - 1) - (sz / 4))); + return Gdk.Pixbuf.FromDrawable (pmap, pmap.Colormap, 0, 0, 0, 0, sz, sz); + } + } + } + } + } + + internal class ActionGroups + { + public static Gtk.ActionGroup GetActionGroup (System.Type type) + { + return Stetic.ActionGroups.GetActionGroup (type.FullName); + } + + public static Gtk.ActionGroup GetActionGroup (string name) + { + return null; + } + } +} diff --git a/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/gui.stetic b/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/gui.stetic new file mode 100644 index 00000000..3863d638 --- /dev/null +++ b/mono-addins/Samples/TextEditor/TextEditorLib/gtk-gui/gui.stetic @@ -0,0 +1,160 @@ + + + + .. + 2.8 + + + + + + + + + Text Editor + + + + + + + + + + + 0 + True + False + False + + + + + + False + Icons + LargeToolbar + + + + 1 + True + False + False + + + + + + True + In + + + + True + + + + + + 2 + True + + + + + + 6 + 6 + + + + + + + 0 + Console + + + 0 + True + False + False + + + + + + 27 + 20 + TextAndIcon + stock:gtk-close Menu + + True + None + + + + End + 1 + True + False + False + + + + + 0 + True + False + False + + + + + + True + In + + + + True + + + + + + 1 + True + + + + + 3 + False + False + False + + + + + + 2 + + + + + + + + + 4 + True + False + False + + + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditorSWF/DateAddin/DateAddin.csproj b/mono-addins/Samples/TextEditorSWF/DateAddin/DateAddin.csproj new file mode 100644 index 00000000..e1d7565d --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/DateAddin/DateAddin.csproj @@ -0,0 +1,78 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {D54B7805-BC96-4861-8352-DA403F430CD7} + Library + Properties + DateAddin + DateAddin + v3.5 + 512 + + + True + full + False + ..\TextEditorSWF\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + True + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + ..\..\..\bin\Mono.Addins.dll + + + + + + + + + + {54542FD2-7B2E-4CEB-874C-BB50CF4812FE} + SnippetsAddin + + + {85480AD8-781F-43FC-A48F-91962401DB95} + TextEditorSWF + + + + + + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditorSWF/DateAddin/DateSnippet.cs b/mono-addins/Samples/TextEditorSWF/DateAddin/DateSnippet.cs new file mode 100644 index 00000000..8cd29cf8 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/DateAddin/DateSnippet.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SnippetsAddin; +using Mono.Addins; + +[assembly: Addin] +[assembly: AddinDependency ("TextEditor.Core", "1.0")] +[assembly: AddinDependency ("TextEditor.SnippetsAddin", "1.0")] + +namespace DateAddin +{ + [Extension] + public class DateSnippet: ISnippetProvider + { + public string GetText (string shortcut) + { + if (shortcut == "date") + return DateTime.Now.ToShortDateString (); + else + return null; + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/DateAddin/InsertDateCommand.cs b/mono-addins/Samples/TextEditorSWF/DateAddin/InsertDateCommand.cs new file mode 100644 index 00000000..d8bdd6b5 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/DateAddin/InsertDateCommand.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TextEditorSWF.ExtensionModel; +using TextEditorSWF; +using System.Windows.Forms; + +namespace DateAddin +{ + [Command ("Insert Date")] + class InsertDateCommand: ICommand + { + public void Run () + { + Program.MainWindow.Editor.SelectedText = DateTime.Now.ToShortDateString (); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/DateAddin/MainMenu.addin b/mono-addins/Samples/TextEditorSWF/DateAddin/MainMenu.addin new file mode 100644 index 00000000..abff4a51 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/DateAddin/MainMenu.addin @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditorSWF/DateAddin/Properties/AssemblyInfo.cs b/mono-addins/Samples/TextEditorSWF/DateAddin/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..9f282d19 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/DateAddin/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle ("DateAddin")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("DateAddin")] +[assembly: AssemblyCopyright ("Copyright © 2010")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible (false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid ("1ef9e97b-cb49-4c26-8eb0-734c3ecb4eb2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion ("1.0.0.0")] +[assembly: AssemblyFileVersion ("1.0.0.0")] diff --git a/mono-addins/Samples/TextEditorSWF/SnippetsAddin/ISnippetProvider.cs b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/ISnippetProvider.cs new file mode 100644 index 00000000..0a7d37c3 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/ISnippetProvider.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Mono.Addins; + +namespace SnippetsAddin +{ + /// + /// Extension point for snippet providers. + /// + [TypeExtensionPoint] + public interface ISnippetProvider + { + string GetText (string shortcut); + } +} diff --git a/mono-addins/Samples/TextEditorSWF/SnippetsAddin/Properties/AssemblyInfo.cs b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..29563d9a --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle ("SnippetsAddin")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("SnippetsAddin")] +[assembly: AssemblyCopyright ("Copyright © 2010")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible (false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid ("5c440446-9242-4848-af10-5faf2cbad647")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion ("1.0.0.0")] +[assembly: AssemblyFileVersion ("1.0.0.0")] diff --git a/mono-addins/Samples/TextEditorSWF/SnippetsAddin/SnippetsAddin.cs b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/SnippetsAddin.cs new file mode 100644 index 00000000..441dd916 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/SnippetsAddin.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using Mono.Addins; +using TextEditorSWF.ExtensionModel; +using TextEditorSWF; + +[assembly: Addin ("SnippetsAddin","1.0", Namespace="TextEditor")] +[assembly: AddinDependency ("Core", "1.0")] + +namespace SnippetsAddin +{ + [Extension] + public class SnippetsExtension: EditorExtension + { + public override void Initialize () + { + Program.MainWindow.Editor.KeyPress += new KeyPressEventHandler (EditorKeyPress); + } + + void EditorKeyPress (object sender, KeyPressEventArgs e) + { + if (e.KeyChar != '\t') + return; + RichTextBox editor = Program.MainWindow.Editor; + int p = editor.SelectionStart - 1; + string txt = editor.Text; + while (p >= 0 && char.IsLetterOrDigit (txt[p])) + p--; + p++; + string word = txt.Substring (p, editor.SelectionStart - p); + + foreach (ISnippetProvider provider in AddinManager.GetExtensionObjects ()) { + string fullText = provider.GetText (word); + if (fullText != null) { + int nextp; + int cursorPos = fullText.IndexOf ("<|>"); + if (cursorPos != -1) { + fullText = fullText.Remove (cursorPos, 3); + nextp = p + cursorPos; + } + else + nextp = p + fullText.Length; + + editor.Text = txt.Substring (0, p) + fullText + txt.Substring (editor.SelectionStart); + editor.SelectionStart = nextp; + e.Handled = true; + return; + } + } + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/SnippetsAddin/SnippetsAddin.csproj b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/SnippetsAddin.csproj new file mode 100644 index 00000000..da8fa029 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/SnippetsAddin.csproj @@ -0,0 +1,74 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {54542FD2-7B2E-4CEB-874C-BB50CF4812FE} + Library + Properties + SnippetsAddin + SnippetsAddin + v3.5 + 512 + + + True + full + False + ..\TextEditorSWF\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + True + ..\TextEditorSWF\bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + ..\..\..\bin\Mono.Addins.dll + + + + + + + + + + + + {85480AD8-781F-43FC-A48F-91962401DB95} + TextEditorSWF + False + + + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditorSWF/SnippetsAddin/StockSnippetProvider.cs b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/StockSnippetProvider.cs new file mode 100644 index 00000000..e6b12d73 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/StockSnippetProvider.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Mono.Addins; +using SnippetsAddin; + +[assembly: ExtensionPoint ("/TextEditor/StockSnippets", ExtensionAttributeType = typeof (SnippetsAddin.SnippetAttribute))] + +namespace SnippetsAddin +{ + [Extension] + class StockSnippetProvider: ISnippetProvider + { + public string GetText (string shortcut) + { + foreach (ExtensionNode node in AddinManager.GetExtensionNodes ("/TextEditor/StockSnippets")) { + if (node.Data.Shortcut == shortcut) + return node.Data.Text; + } + return null; + } + } + + [AttributeUsage (AttributeTargets.Assembly, AllowMultiple=true)] + public class SnippetAttribute : CustomExtensionAttribute + { + public SnippetAttribute () + { + } + + public SnippetAttribute ([NodeAttribute ("Shortcut")] string shortcut, [NodeAttribute ("Text")] string text) + { + Shortcut = shortcut; + Text = Text; + } + + [NodeAttribute] + public string Shortcut { get; set; } + + [NodeAttribute] + public string Text { get; set; } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/SnippetsAddin/StockSnippets.cs b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/StockSnippets.cs new file mode 100644 index 00000000..daa5cd48 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/SnippetsAddin/StockSnippets.cs @@ -0,0 +1,5 @@ +using System; +using SnippetsAddin; + +[assembly: Snippet ("for", "for (int n=0; n\n}")] +[assembly: Snippet ("foreach", "foreach (var item in col)\n{\n\t<|>\n}")] diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF.sln b/mono-addins/Samples/TextEditorSWF/TextEditorSWF.sln new file mode 100644 index 00000000..571646b4 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextEditorSWF", "TextEditorSWF\TextEditorSWF.csproj", "{85480AD8-781F-43FC-A48F-91962401DB95}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SnippetsAddin", "SnippetsAddin\SnippetsAddin.csproj", "{54542FD2-7B2E-4CEB-874C-BB50CF4812FE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DateAddin", "DateAddin\DateAddin.csproj", "{D54B7805-BC96-4861-8352-DA403F430CD7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {54542FD2-7B2E-4CEB-874C-BB50CF4812FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54542FD2-7B2E-4CEB-874C-BB50CF4812FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54542FD2-7B2E-4CEB-874C-BB50CF4812FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54542FD2-7B2E-4CEB-874C-BB50CF4812FE}.Release|Any CPU.Build.0 = Release|Any CPU + {85480AD8-781F-43FC-A48F-91962401DB95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85480AD8-781F-43FC-A48F-91962401DB95}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85480AD8-781F-43FC-A48F-91962401DB95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85480AD8-781F-43FC-A48F-91962401DB95}.Release|Any CPU.Build.0 = Release|Any CPU + {D54B7805-BC96-4861-8352-DA403F430CD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D54B7805-BC96-4861-8352-DA403F430CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D54B7805-BC96-4861-8352-DA403F430CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D54B7805-BC96-4861-8352-DA403F430CD7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = TextEditorSWF\TextEditorSWF.csproj + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/CommandManager.cs b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/CommandManager.cs new file mode 100644 index 00000000..a8cef46f --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/CommandManager.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using TextEditorSWF.ExtensionModel; +using Mono.Addins; + +namespace TextEditorSWF +{ + /// + /// Manages commands, menus and toolbars + /// + static class CommandManager + { + /// + /// Returns the list of items for the main menu + /// + public static IEnumerable GetMainMenuItems () + { + foreach (IUserInterfaceItem item in AddinManager.GetExtensionNodes ("/TextEditor/MainMenu")) + yield return item.CreateMenuItem (); + } + + /// + /// Returns the list of items for the main toolbar + /// + public static IEnumerable GetToolbarItems () + { + foreach (IUserInterfaceItem item in AddinManager.GetExtensionNodes ("/TextEditor/Toolbar")) + yield return item.CreateButton (); + } + + /// + /// Returns the extension node for the provided command identifier. + /// + internal static CommandExtensionNode GetCommand (string id) + { + foreach (CommandExtensionNode cmd in AddinManager.GetExtensionNodes (typeof (ICommand))) { + if (cmd.Id == id) + return cmd; + } + throw new InvalidOperationException ("Unknown command: " + id); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/CopyCommand.cs b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/CopyCommand.cs new file mode 100644 index 00000000..17e2e0f1 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/CopyCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TextEditorSWF.ExtensionModel; + +namespace TextEditorSWF.Commands +{ + /// + /// The Copy command + /// + [Command ("Copy", IconResource = "TextEditorSWF.Icons.copy.png", Id="Copy")] + class CopyCommand : ICommand + { + public void Run () + { + Program.MainWindow.Editor.Copy (); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/CutCommand.cs b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/CutCommand.cs new file mode 100644 index 00000000..bb2b9fc7 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/CutCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TextEditorSWF.ExtensionModel; + +namespace TextEditorSWF.Commands +{ + /// + /// The cut command. + /// + [Command ("Cut", IconResource = "TextEditorSWF.Icons.cut.png", Id = "Cut")] + class CutCommand : ICommand + { + public void Run () + { + Program.MainWindow.Editor.Cut (); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/ExitCommand.cs b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/ExitCommand.cs new file mode 100644 index 00000000..58bba65e --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/ExitCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TextEditorSWF.ExtensionModel; + +namespace TextEditorSWF.Commands +{ + /// + /// The exit command. + /// + [Command ("Exit", Id = "Exit")] + class ExitCommand : ICommand + { + public void Run () + { + Environment.Exit (0); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/MainMenu.addin b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/MainMenu.addin new file mode 100644 index 00000000..b8bfa9e6 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/MainMenu.addin @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/NewCommand.cs b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/NewCommand.cs new file mode 100644 index 00000000..dd852c03 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/NewCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TextEditorSWF.ExtensionModel; + +namespace TextEditorSWF.Commands +{ + /// + /// The New command. + /// + [Command ("New", IconResource = "TextEditorSWF.Icons.new.png", Id = "New")] + class NewCommand : ICommand + { + public void Run () + { + Program.MainWindow.NewFile (); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/OpenCommand.cs b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/OpenCommand.cs new file mode 100644 index 00000000..1691dbee --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/OpenCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TextEditorSWF.ExtensionModel; + +namespace TextEditorSWF.Commands +{ + /// + /// The Open command. + /// + [Command ("Open", IconResource = "TextEditorSWF.Icons.open.png", Id = "Open")] + class OpenCommand : ICommand + { + public void Run () + { + Program.MainWindow.OpenFile (); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/PasteCommand.cs b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/PasteCommand.cs new file mode 100644 index 00000000..1b92ce9a --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/PasteCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TextEditorSWF.ExtensionModel; + +namespace TextEditorSWF.Commands +{ + /// + /// The Paste command. + /// + [Command ("Paste", IconResource = "TextEditorSWF.Icons.paste.png", Id = "Paste")] + class PasteCommand : ICommand + { + public void Run () + { + Program.MainWindow.Editor.Paste (); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/SaveCommand.cs b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/SaveCommand.cs new file mode 100644 index 00000000..b5d31c04 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/SaveCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TextEditorSWF.ExtensionModel; + +namespace TextEditorSWF.Commands +{ + /// + /// The Save command. + /// + [Command ("Save", IconResource = "TextEditorSWF.Icons.save.png", Id = "Save")] + class SaveCommand : ICommand + { + public void Run () + { + Program.MainWindow.SaveFile (); + } + } +} diff --git a/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/Toolbar.addin b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/Toolbar.addin new file mode 100644 index 00000000..705c0cb3 --- /dev/null +++ b/mono-addins/Samples/TextEditorSWF/TextEditorSWF/Commands/Toolbar.addin @@ -0,0 +1,12 @@ + + + +