mirror of
https://github.com/chibicitiberiu/rainmeter-studio.git
synced 2024-02-24 04:33:31 +00:00
Rewrote references
This commit is contained in:
@ -4,6 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using RainmeterStudio.Core.Storage;
|
||||
using RainmeterStudio.Core.Utils;
|
||||
using Version = RainmeterStudio.Core.Utils.Version;
|
||||
|
||||
namespace RainmeterStudio.Core.Model
|
||||
{
|
||||
@ -12,85 +14,114 @@ namespace RainmeterStudio.Core.Model
|
||||
/// </summary>
|
||||
public class Project
|
||||
{
|
||||
#region Private fields
|
||||
|
||||
private string _name;
|
||||
private string _path;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the project
|
||||
/// </summary>
|
||||
[XmlElement(ElementName = "name", Order = 1)]
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
return Root.Name;
|
||||
}
|
||||
set
|
||||
{
|
||||
_name = value;
|
||||
|
||||
if (Root != null)
|
||||
Root.Data = new Reference(Name, Path);
|
||||
Root.Name = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file path of this project
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return _path;
|
||||
return Root.StoragePath;
|
||||
}
|
||||
set
|
||||
{
|
||||
_path = value;
|
||||
|
||||
if (Root != null)
|
||||
Root.Data = new Reference(Name, Path);
|
||||
Root.StoragePath = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the author of the project
|
||||
/// </summary>
|
||||
[XmlElement(ElementName = "author", Order = 2)]
|
||||
public string Author { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the version of the project
|
||||
/// </summary>
|
||||
[XmlElement(ElementName = "version", Order = 3)]
|
||||
public Version Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reference to the file to automatically load at package installation
|
||||
/// Gets or sets the reference to the file that automatically loads at package installation
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public Reference AutoLoadFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the qualified name of the auto load file
|
||||
/// </summary>
|
||||
[XmlElement(ElementName = "autoLoadFile", Order = 7)]
|
||||
public string AutoLoadFileQualifiedName
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((AutoLoadFile == null) ? null : AutoLoadFile.QualifiedName);
|
||||
}
|
||||
set
|
||||
{
|
||||
AutoLoadFile = Root.GetReference(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of variable files
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public List<Reference> VariableFiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of variable files qualified names
|
||||
/// </summary>
|
||||
[XmlArray(ElementName = "variableFiles", Order = 8)]
|
||||
public string[] VariableFilesQualifiedNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return VariableFiles.Select(x => x.QualifiedName).ToArray();
|
||||
}
|
||||
set
|
||||
{
|
||||
VariableFiles.Clear();
|
||||
VariableFiles.AddRange(value.Select(x => Root.GetReference(x)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum rainmeter version
|
||||
/// </summary>
|
||||
[XmlElement(ElementName = "minimumRainmeter", Order = 4)]
|
||||
public Version MinimumRainmeter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum Windows version
|
||||
/// </summary>
|
||||
[XmlElement(ElementName = "minimumWindows", Order = 5)]
|
||||
public Version MinimumWindows { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the root node
|
||||
/// </summary>
|
||||
public Tree<Reference> Root { get; set; }
|
||||
[XmlElement(ElementName = "root", Order = 6)]
|
||||
public Reference Root { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@ -101,7 +132,7 @@ namespace RainmeterStudio.Core.Model
|
||||
/// </summary>
|
||||
public Project()
|
||||
{
|
||||
Root = new Tree<Reference>();
|
||||
Root = new Reference(String.Empty);
|
||||
VariableFiles = new List<Reference>();
|
||||
Version = new Version();
|
||||
MinimumRainmeter = new Version("3.1");
|
||||
@ -109,5 +140,20 @@ namespace RainmeterStudio.Core.Model
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Operations
|
||||
|
||||
/// <summary>
|
||||
/// Looks for reference in project
|
||||
/// </summary>
|
||||
/// <param name="reference">Reference</param>
|
||||
/// <returns>True if reference was found</returns>
|
||||
public bool Contains(Reference reference)
|
||||
{
|
||||
return Root.TreeContains(reference);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -1,90 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using RainmeterStudio.Core.Utils;
|
||||
|
||||
namespace RainmeterStudio.Core.Model
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference to a file or folder
|
||||
/// </summary>
|
||||
[DebuggerDisplay("ProjectPath = {ProjectPath}, StoragePath = {StoragePath}")]
|
||||
public struct Reference
|
||||
[DebuggerDisplay("QualifiedName = {QualifiedName}, StoragePath = {StoragePath}")]
|
||||
public class Reference : INotifyCollectionChanged
|
||||
{
|
||||
private string[] _projectPath;
|
||||
private string _storagePath;
|
||||
private Dictionary<string, Reference> _children;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent of this reference
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public Reference Parent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children references
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public ReadOnlyDictionary<string, Reference> ChildrenDictionary
|
||||
{
|
||||
get
|
||||
{
|
||||
return new ReadOnlyDictionary<string,Reference>(_children);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets children
|
||||
/// </summary>
|
||||
[XmlArray("children")]
|
||||
public Reference[] Children
|
||||
{
|
||||
get
|
||||
{
|
||||
return _children.Values.ToArray();
|
||||
}
|
||||
set
|
||||
{
|
||||
Clear();
|
||||
value.ForEach(Add);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the reference
|
||||
/// </summary>
|
||||
[XmlAttribute("name")]
|
||||
public string Name
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full qualified name of this reference
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public string QualifiedName
|
||||
{
|
||||
get
|
||||
{
|
||||
// Try to get the last item from the project path
|
||||
if (_projectPath != null && _projectPath.Length > 0)
|
||||
return _projectPath[_projectPath.Length - 1];
|
||||
if (Parent == null)
|
||||
{
|
||||
// Return name
|
||||
return Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it has a parent, get the parent's name
|
||||
return Parent.QualifiedName + '/' + Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// None found, return null
|
||||
return null;
|
||||
/// <summary>
|
||||
/// Gets the parts of the full qualified name of this reference
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public IEnumerable<string> QualifiedParts
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Parent == null)
|
||||
{
|
||||
return Enumerable.Repeat(Name, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Parent.QualifiedParts.Append(Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the file on the disk. If reference is in a project, the path should be relative.
|
||||
/// </summary>
|
||||
[XmlAttribute("storagePath")]
|
||||
public string StoragePath
|
||||
{
|
||||
get
|
||||
{
|
||||
return _storagePath;
|
||||
}
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the reference
|
||||
/// </summary>
|
||||
public Reference()
|
||||
: this(null, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the qualified path
|
||||
/// Initializes the reference
|
||||
/// </summary>
|
||||
public string ProjectPath
|
||||
/// <param name="projectPath">Project path to item referenced</param>
|
||||
public Reference(string name)
|
||||
: this(name, null)
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_projectPath != null)
|
||||
{
|
||||
return _projectPath.Aggregate(String.Empty, (a, b) => a + "/" + b);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the reference
|
||||
/// </summary>
|
||||
/// <param name="name">Name of reference</param>
|
||||
/// <param name="path">Path to item referenced</param>
|
||||
public Reference(string filePath, string projectPath = null)
|
||||
/// <param name="storagePath">Path to item on disk</param>
|
||||
public Reference(string name, string storagePath)
|
||||
{
|
||||
_storagePath = filePath;
|
||||
|
||||
if (projectPath != null)
|
||||
{
|
||||
_projectPath = projectPath.Split('/').Skip(1).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
_projectPath = null;
|
||||
}
|
||||
StoragePath = storagePath;
|
||||
Name = name;
|
||||
_children = new Dictionary<string, Reference>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the reference points to a project item
|
||||
/// </summary>
|
||||
public bool IsInProject()
|
||||
{
|
||||
return (_projectPath != null);
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the reference has a file on disk
|
||||
@ -92,7 +155,90 @@ namespace RainmeterStudio.Core.Model
|
||||
/// <returns></returns>
|
||||
public bool IsOnStorage()
|
||||
{
|
||||
return (_storagePath != null);
|
||||
return (StoragePath != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a child reference
|
||||
/// </summary>
|
||||
/// <param name="reference"></param>
|
||||
public void Add(Reference reference)
|
||||
{
|
||||
// Make sure object is not parented yet
|
||||
if (reference.Parent != null)
|
||||
throw new ArgumentException("Reference must be removed from its current parent first.");
|
||||
|
||||
// Add and parent
|
||||
reference.Parent = this;
|
||||
_children.Add(reference.Name, reference);
|
||||
|
||||
// Trigger event
|
||||
if (CollectionChanged != null)
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, reference));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a reference
|
||||
/// </summary>
|
||||
/// <param name="reference">Reference to remove</param>
|
||||
/// <returns>True if removed successfully</returns>
|
||||
public bool Remove(Reference reference)
|
||||
{
|
||||
// Make sure we are the parent
|
||||
if (reference.Parent != this)
|
||||
return false;
|
||||
|
||||
// Remove
|
||||
reference.Parent = null;
|
||||
bool res = _children.Remove(reference.Name);
|
||||
|
||||
// Trigger event
|
||||
if (CollectionChanged != null)
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, reference));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes this reference from its parent
|
||||
/// </summary>
|
||||
/// <returns>True if unparented successfully</returns>
|
||||
public bool Unparent()
|
||||
{
|
||||
if (Parent != null)
|
||||
return Parent.Remove(this);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all children
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
// Unparent
|
||||
foreach (var pair in _children)
|
||||
{
|
||||
pair.Value.Parent = null;
|
||||
}
|
||||
|
||||
// Clear
|
||||
_children.Clear();
|
||||
|
||||
// Trigger event
|
||||
if (CollectionChanged != null)
|
||||
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of children
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return _children.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -105,14 +251,7 @@ namespace RainmeterStudio.Core.Model
|
||||
if (obj is Reference)
|
||||
{
|
||||
Reference other = (Reference)obj;
|
||||
|
||||
// 2 references are equal if they point to the same project item
|
||||
if (_projectPath != null && other._projectPath != null)
|
||||
return _projectPath.SequenceEqual(other._projectPath);
|
||||
|
||||
// If there is no project item, compare storage paths
|
||||
if (_projectPath == null && other._projectPath == null)
|
||||
return String.Equals(_storagePath, other._storagePath);
|
||||
return (String.Equals(QualifiedName, other.QualifiedName));
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -124,28 +263,114 @@ namespace RainmeterStudio.Core.Model
|
||||
/// <returns>Hash code</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = (_projectPath == null) ? 0 : _projectPath.GetHashCode();
|
||||
|
||||
if (_projectPath != null)
|
||||
{
|
||||
foreach (var item in _projectPath)
|
||||
hash = hash * 7 + item.GetHashCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
hash = hash * 2113 + ((_storagePath == null) ? 0 : _storagePath.GetHashCode());
|
||||
}
|
||||
|
||||
return hash;
|
||||
return QualifiedName.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the string representation of this reference
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns>String representation</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return ProjectPath;
|
||||
return QualifiedName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when children are added or removed.
|
||||
/// </summary>
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides useful methods for references
|
||||
/// </summary>
|
||||
public static class ReferenceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to get a reference from the same tree having specified qualified name
|
||||
/// </summary>
|
||||
/// <param name="this">Reference contained in the tree</param>
|
||||
/// <param name="qualifiedName">Full qualified name</param>
|
||||
/// <param name="output">Found reference</param>
|
||||
/// <returns>True if succeeded to find the reference</returns>
|
||||
public static bool TryGetReference(this Reference @this, string qualifiedName, out Reference output)
|
||||
{
|
||||
var thisQualifiedName = @this.QualifiedName;
|
||||
|
||||
// Am I the reference? return myself
|
||||
if (qualifiedName.Equals(thisQualifiedName))
|
||||
{
|
||||
output = @this;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Qualified name is a child, look child up
|
||||
else if (qualifiedName.StartsWith(thisQualifiedName))
|
||||
{
|
||||
int startIndex = thisQualifiedName.Length + 1;
|
||||
int endIndex = qualifiedName.IndexOf('/', startIndex);
|
||||
|
||||
string child;
|
||||
Reference childRef;
|
||||
|
||||
if (endIndex < 0)
|
||||
{
|
||||
child = qualifiedName.Substring(startIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
child = qualifiedName.Substring(startIndex, endIndex - startIndex);
|
||||
}
|
||||
|
||||
// Try to get child
|
||||
if (@this.ChildrenDictionary.TryGetValue(child, out childRef))
|
||||
{
|
||||
return childRef.TryGetReference(qualifiedName, out output);
|
||||
}
|
||||
}
|
||||
|
||||
// Qualified name is not a child and not 'this', so ask parent to find it
|
||||
else if (@this.Parent != null)
|
||||
{
|
||||
return @this.Parent.TryGetReference(qualifiedName, out output);
|
||||
}
|
||||
|
||||
// Failed to find child
|
||||
output = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference from the same tree having specified qualified name
|
||||
/// </summary>
|
||||
/// <param name="this">Reference contained in the tree</param>
|
||||
/// <param name="qualifiedName">Full qualified name</param>
|
||||
/// <returns>Found reference</returns>
|
||||
/// <exception cref="ArgumentException">If qualified name not found</exception>
|
||||
public static Reference GetReference(this Reference @this, string qualifiedName)
|
||||
{
|
||||
Reference res;
|
||||
|
||||
if (TryGetReference(@this, qualifiedName, out res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Could not find reference.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a reference is in the same tree as this
|
||||
/// </summary>
|
||||
/// <param name="this">Reference that is in the tree we search in</param>
|
||||
/// <param name="other">Reference to search</param>
|
||||
/// <returns>True if the tree contains the reference.</returns>
|
||||
public static bool TreeContains(this Reference @this, Reference reference)
|
||||
{
|
||||
Reference temp;
|
||||
return TryGetReference(@this, reference.QualifiedName, out temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,5 +153,10 @@ namespace RainmeterStudio.Core.Model
|
||||
{
|
||||
return Children.GetEnumerator();
|
||||
}
|
||||
|
||||
public void TreeExpand(bool p)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user