diff --git a/.gitignore b/.gitignore index 920c8d61..fca70329 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,187 @@ -/TestBench -/ipch -bin -obj x32* x64* Certificate.bat + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Roslyn cache directories +*.ide/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* +/[Tt]est[Bb]ench + +#NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ *.aps -*.docstates -*.exe +*.ncb *.opensdf *.sdf -*.suo -*.txt -*.user +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# If using the old MSBuild-Integrated Package Restore, uncomment this: +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ \ No newline at end of file diff --git a/RainmeterEditor/Business/ProjectManager.cs b/RainmeterEditor/Business/ProjectManager.cs new file mode 100644 index 00000000..7781a19d --- /dev/null +++ b/RainmeterEditor/Business/ProjectManager.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using RainmeterEditor.Model; + +namespace RainmeterEditor.Business +{ + public class ProjectManager + { + public Project ActiveProject { get; protected set; } + + public void Open() { } + public void Close() { } + } +} diff --git a/RainmeterEditor/Model/Project.cs b/RainmeterEditor/Model/Project.cs new file mode 100644 index 00000000..d971beae --- /dev/null +++ b/RainmeterEditor/Model/Project.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace RainmeterEditor.Model +{ + public class Project + { + [XmlElement("name")] + public string Name { get; set; } + + [XmlElement("author")] + public string Author { get; set; } + + [XmlIgnore] + public Version Version { get; set; } + + [XmlElement("version")] + public string VersionString + { + get + { + return Version.ToString(); + } + set + { + Version = new Version(value); + } + } + + [XmlElement("autoLoadFile")] + public Reference AutoLoadFile { get; set; } + + [XmlArray("variableFiles")] + public List VariableFiles { get; set; } + + [XmlIgnore] + public Version MinimumRainmeter { get; set; } + + [XmlElement("minimumRainmeter")] + public string MinimumRainmeterString + { + get + { + return MinimumRainmeter.ToString(); + } + set + { + MinimumRainmeter = new Version(value); + } + } + + [XmlIgnore] + public Version MinimumWindows { get; set; } + + [XmlElement("minimumWindows")] + public string MinimumWindowsString + { + get + { + return MinimumWindows.ToString(); + } + set + { + MinimumWindows = new Version(value); + } + } + + [XmlElement("root")] + public Tree Root { get; set; } + + public Project() + { + Root = new Tree(); + VariableFiles = new List(); + Version = new Version(); + MinimumRainmeter = new Version("3.1"); + MinimumWindows = new Version("5.1"); + } + + public override bool Equals(object obj) + { + Project other = obj as Project; + + if (other == null) + return false; + + bool res = String.Equals(Author, other.Author); + res &= Reference.Equals(AutoLoadFile, other.AutoLoadFile); + res &= Version.Equals(MinimumRainmeter, other.MinimumRainmeter); + res &= Version.Equals(MinimumWindows, other.MinimumWindows); + res &= String.Equals(Name, other.Name); + res &= Tree.Equals(Root, other.Root); + res &= Version.Equals(Version, other.Version); + + return res; + } + + public override int GetHashCode() + { + int hash = (Author == null) ? 0 : Author.GetHashCode(); + hash = hash * 7 + ((AutoLoadFile == null) ? 0 : AutoLoadFile.GetHashCode()); + hash = hash * 7 + ((MinimumRainmeter == null) ? 0 : MinimumRainmeter.GetHashCode()); + hash = hash * 7 + ((MinimumWindows == null) ? 0 : MinimumWindows.GetHashCode()); + hash = hash * 7 + ((Name == null) ? 0 : Name.GetHashCode()); + hash = hash * 7 + ((Root == null) ? 0 : Root.GetHashCode()); + hash = hash * 7 + ((Version == null) ? 0 : Version.GetHashCode()); + + return hash; + } + } +} diff --git a/RainmeterEditor/Model/Reference.cs b/RainmeterEditor/Model/Reference.cs new file mode 100644 index 00000000..da7ade23 --- /dev/null +++ b/RainmeterEditor/Model/Reference.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace RainmeterEditor.Model +{ + /// + /// Reference to a file or folder + /// + public class Reference + { + [XmlAttribute("name")] + public string Name { get; set; } + + [XmlAttribute("path")] + public string Path { get; set; } + + public Reference() + { + } + + public Reference(string name, string path = null) + { + Name = name; + Path = path; + } + + public override bool Equals(object obj) + { + var other = obj as Reference; + + // Types are different, so not equal + if (other == null) + return false; + + // Compare using string equals + return String.Equals(Name, other.Name) && String.Equals(Path, other.Path); + } + + public override int GetHashCode() + { + int hash = (Name == null) ? 0 : Name.GetHashCode(); + hash = hash * 7 + ((Path == null) ? 0 : Path.GetHashCode()); + + return hash; + } + } +} diff --git a/RainmeterEditor/Model/Tree.cs b/RainmeterEditor/Model/Tree.cs new file mode 100644 index 00000000..984c6cdc --- /dev/null +++ b/RainmeterEditor/Model/Tree.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace RainmeterEditor.Model +{ + public class Tree + { + [XmlElement("data")] + public T Data { get; set; } + + [XmlArray("children"), XmlArrayItem("child")] + public List> Children { get; set; } + + public Tree() + { + Children = new List>(); + Data = default(T); + } + + public Tree(T data) + { + Children = new List>(); + Data = data; + } + + public int IndexOf(Tree item) + { + return Children.IndexOf(item); + } + + public void Insert(int index, Tree item) + { + Children.Insert(index, item); + } + + public void RemoveAt(int index) + { + Children.RemoveAt(index); + } + + public Tree this[int index] + { + get + { + return Children[index]; + } + set + { + Children[index] = value; + } + } + + public void Add(Tree item) + { + Children.Add(item); + } + + public void Clear() + { + Children.Clear(); + } + + public bool Contains(Tree item) + { + return Children.Contains(item); + } + + public void CopyTo(Tree[] array, int arrayIndex) + { + Children.CopyTo(array, arrayIndex); + } + + public int Count + { + get { return Children.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + + public bool Remove(Tree item) + { + return Children.Remove(item); + } + + public IEnumerator> GetEnumerator() + { + return Children.GetEnumerator(); + } + + public int IndexOf(T item) + { + return Children.IndexOf(new Tree(item)); + } + + public void Insert(int index, T item) + { + Children.Insert(index, new Tree(item)); + } + + public void Add(T item) + { + Children.Add(new Tree(item)); + } + + public bool Contains(T item) + { + return Children.Contains(new Tree(item)); + } + + public void CopyTo(T[] array, int arrayIndex) + { + foreach (var node in Children) + array[arrayIndex++] = node.Data; + } + + public bool Remove(T item) + { + return Children.Remove(new Tree(item)); + } + + public override bool Equals(object obj) + { + Tree other = obj as Tree; + + // Types are different, so not equal + if (other == null) + return false; + + // Compare data + if (!object.Equals(Data, other.Data)) + return false; + + // Compare children array + return Children.SequenceEqual(other.Children); + } + + public override int GetHashCode() + { + int hash = ((Data == null) ? 0 : Data.GetHashCode()); + + foreach (var c in Children) + hash = hash * 7 + c.GetHashCode(); + + return hash; + } + } +} diff --git a/RainmeterEditor/RainmeterStudio.csproj b/RainmeterEditor/RainmeterStudio.csproj index 87368a4d..eb22ed21 100644 --- a/RainmeterEditor/RainmeterStudio.csproj +++ b/RainmeterEditor/RainmeterStudio.csproj @@ -70,6 +70,7 @@ Designer + TextEditorControl.xaml @@ -88,8 +89,11 @@ + + + True @@ -97,6 +101,7 @@ Strings.resx + diff --git a/RainmeterEditor/Storage/ProjectStorage.cs b/RainmeterEditor/Storage/ProjectStorage.cs new file mode 100644 index 00000000..25f61d5d --- /dev/null +++ b/RainmeterEditor/Storage/ProjectStorage.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using RainmeterEditor.Model; + +namespace RainmeterEditor.Storage +{ + public class ProjectStorage + { + public Project Load(string path) + { + // Open file + var file = File.OpenText(path); + + // Deserialize file + var serializer = new XmlSerializer(typeof(Project), new XmlRootAttribute("project")); + Project project = serializer.Deserialize(file) as Project; + + // Clean up + file.Close(); + return project; + } + + public void Save(string path, Project project) + { + // Open file + var file = File.OpenWrite(path); + + // Deserialize file + var serializer = new XmlSerializer(typeof(Project), new XmlRootAttribute("project")); + serializer.Serialize(file, project); + + // Clean up + file.Close(); + } + } +} diff --git a/RainmeterStudio.Tests/Properties/AssemblyInfo.cs b/RainmeterStudio.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..11e77916 --- /dev/null +++ b/RainmeterStudio.Tests/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("RainmeterStudio.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RainmeterStudio.Tests")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[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("e4fee45f-07d7-4ce8-96f7-b8dc29e62150")] + +// 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/RainmeterStudio.Tests/RainmeterStudio.Tests.csproj b/RainmeterStudio.Tests/RainmeterStudio.Tests.csproj new file mode 100644 index 00000000..8aafd4ca --- /dev/null +++ b/RainmeterStudio.Tests/RainmeterStudio.Tests.csproj @@ -0,0 +1,96 @@ + + + + Debug + AnyCPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A} + Library + Properties + RainmeterStudio.Tests + RainmeterStudio.Tests + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x86 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + {438d0136-4a27-4e4d-a617-fface4554236} + RainmeterStudio + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/RainmeterStudio.Tests/Storage/ProjectStorageTest.cs b/RainmeterStudio.Tests/Storage/ProjectStorageTest.cs new file mode 100644 index 00000000..c96a6275 --- /dev/null +++ b/RainmeterStudio.Tests/Storage/ProjectStorageTest.cs @@ -0,0 +1,110 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using RainmeterEditor.Storage; +using RainmeterEditor.Model; +using System.IO; + +namespace RainmeterStudio.Tests.Storage +{ + /// + /// Tests the ProjectStorage class + /// + [TestClass] + public class ProjectStorageTest + { + private ProjectStorage ProjectStorage = new ProjectStorage(); + + public TestContext TestContext { get; set; } + + [TestInitialize] + public void Initialize() + { + Directory.SetCurrentDirectory(TestContext.DeploymentDirectory); + } + + [TestMethod] + public void ProjectStorageSmokeTest() + { + string filename = TestContext.TestName + ".rsproj"; + + // Create project + Project project = CreateProject(); + + // Save and load + ProjectStorage.Save(filename, project); + Project res = ProjectStorage.Load(filename); + + // Verify results + Assert.IsNotNull(res); + Assert.AreEqual(project, res); + Assert.AreEqual(project.GetHashCode(), res.GetHashCode()); + Assert.AreEqual(project.Author, res.Author); + Assert.AreEqual(project.AutoLoadFile, res.AutoLoadFile); + Assert.AreEqual(project.MinimumRainmeter, res.MinimumRainmeter); + Assert.AreEqual(project.MinimumWindows, res.MinimumWindows); + Assert.AreEqual(project.Name, res.Name); + Assert.AreEqual(project.Root, res.Root); + Assert.IsTrue(project.VariableFiles.SequenceEqual(res.VariableFiles)); + Assert.AreEqual(project.Version, res.Version); + } + + [TestMethod] + public void ProjectStorageEmptyProjectSmokeTest() + { + string filename = TestContext.TestName + ".rsproj"; + + // Create a project + Project project = new Project(); + + // Save and load project + ProjectStorage.Save(filename, project); + Project res = ProjectStorage.Load(filename); + + // Test results + Assert.IsNotNull(res); + Assert.AreEqual(project, res); + Assert.AreEqual(project.GetHashCode(), res.GetHashCode()); + Assert.AreEqual(project.Author, res.Author); + Assert.AreEqual(project.AutoLoadFile, res.AutoLoadFile); + Assert.AreEqual(project.MinimumRainmeter, res.MinimumRainmeter); + Assert.AreEqual(project.MinimumWindows, res.MinimumWindows); + Assert.AreEqual(project.Name, res.Name); + Assert.AreEqual(project.Root, res.Root); + Assert.IsTrue(project.VariableFiles.SequenceEqual(res.VariableFiles)); + Assert.AreEqual(project.Version, res.Version); + } + + private Project CreateProject() + { + // Create some file references + Reference folder1 = new Reference("folder1"); + Reference folder2 = new Reference("folder2"); + Reference file1 = new Reference("file1.txt"); + Reference file2 = new Reference("file2.ini"); + Reference file3 = new Reference("file3.bmp"); + + // Create a project + Project project = new Project(); + project.Author = "Tiberiu Chibici"; + project.MinimumRainmeter = new Version("3.1"); + project.MinimumWindows = new Version("5.1"); + project.Name = "My project"; + project.Version = new Version("1.0.1"); + + project.AutoLoadFile = file2; + project.VariableFiles.Add(file1); + + // Set project references + project.Root.Add(folder1); + project.Root.Add(folder2); + project.Root[0].Add(file1); + project.Root[1].Add(file2); + project.Root.Add(file3); + + return project; + } + } +} diff --git a/RainmeterStudio.sln b/RainmeterStudio.sln index cfc05631..e81ebaa0 100644 --- a/RainmeterStudio.sln +++ b/RainmeterStudio.sln @@ -74,6 +74,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PluginWindowMessage", "Plug EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RainmeterStudio", "RainmeterEditor\RainmeterStudio.csproj", "{438D0136-4A27-4E4D-A617-FFACE4554236}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RainmeterStudio.Tests", "RainmeterStudio.Tests\RainmeterStudio.Tests.csproj", "{845F4BD4-6822-4D92-9DDB-15FD18A47E5A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -514,6 +516,18 @@ Global {438D0136-4A27-4E4D-A617-FFACE4554236}.Release|Mixed Platforms.Build.0 = Release|Any CPU {438D0136-4A27-4E4D-A617-FFACE4554236}.Release|Win32.ActiveCfg = Release|Any CPU {438D0136-4A27-4E4D-A617-FFACE4554236}.Release|x64.ActiveCfg = Release|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Debug|Win32.ActiveCfg = Debug|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Debug|x64.ActiveCfg = Debug|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Release|Any CPU.Build.0 = Release|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Release|Win32.ActiveCfg = Release|Any CPU + {845F4BD4-6822-4D92-9DDB-15FD18A47E5A}.Release|x64.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE