Added project item commands, work on project manager, implemented project manager tests

This commit is contained in:
2014-09-16 21:57:15 +03:00
parent 425d7d62f1
commit 7691a3c326
30 changed files with 2526 additions and 321 deletions

View File

@ -223,7 +223,7 @@ namespace RainmeterStudio.Core.Model
/// </summary>
public Project()
{
Root = new Reference(String.Empty, Reference.ReferenceTargetKind.Project);
Root = new Reference(String.Empty, ReferenceTargetKind.Project);
VariableFiles = new ObservableCollection<Reference>();
Version = new Version();
MinimumRainmeter = new Version("3.1");

View File

@ -12,38 +12,38 @@ using RainmeterStudio.Core.Utils;
namespace RainmeterStudio.Core.Model
{
/// <summary>
/// The kind of item the reference points to
/// </summary>
public enum ReferenceTargetKind
{
/// <summary>
/// Invalid state
/// </summary>
None,
/// <summary>
/// Reference points to a file
/// </summary>
File,
/// <summary>
/// Reference points to a directory
/// </summary>
Directory,
/// <summary>
/// Reference points to a project
/// </summary>
Project
}
/// <summary>
/// Reference to a file or folder
/// </summary>
[DebuggerDisplay("QualifiedName = {QualifiedName}, StoragePath = {StoragePath}")]
public class Reference : INotifyCollectionChanged, INotifyPropertyChanged
public class Reference : INotifyCollectionChanged, INotifyPropertyChanged, ICloneable
{
/// <summary>
/// The kind of item the reference points to
/// </summary>
public enum ReferenceTargetKind
{
/// <summary>
/// Invalid state
/// </summary>
None,
/// <summary>
/// Reference points to a file
/// </summary>
File,
/// <summary>
/// Reference points to a directory
/// </summary>
Directory,
/// <summary>
/// Reference points to a project
/// </summary>
Project
}
private Dictionary<string, Reference> _children;
private Reference _parent;
private string _name, _storagePath;
@ -90,7 +90,10 @@ namespace RainmeterStudio.Core.Model
// Notify
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Parent"));
PropertyChanged(this, new PropertyChangedEventArgs("QualifiedName"));
}
}
}
@ -138,7 +141,10 @@ namespace RainmeterStudio.Core.Model
_name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
PropertyChanged(this, new PropertyChangedEventArgs("QualifiedName"));
}
}
}
@ -216,7 +222,7 @@ namespace RainmeterStudio.Core.Model
_kind = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Kind"));
PropertyChanged(this, new PropertyChangedEventArgs("TargetKind"));
}
}
@ -374,7 +380,7 @@ namespace RainmeterStudio.Core.Model
private void Parent_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (PropertyChanged != null && (e.PropertyName == "Parent" || e.PropertyName == "Name" || e.PropertyName == "QualifiedName"))
if (PropertyChanged != null && e.PropertyName == "QualifiedName")
PropertyChanged(this, new PropertyChangedEventArgs("QualifiedName"));
}
@ -437,6 +443,23 @@ namespace RainmeterStudio.Core.Model
}
#endregion
/// <summary>
/// Creates a clone of this reference
/// </summary>
/// <returns>The clone</returns>
/// <remarks>The clone doesn't keep the parent.</remarks>
public object Clone()
{
var cloneReference = new Reference(Name, StoragePath, TargetKind);
foreach (var r in Children)
{
cloneReference.Add((Reference)r.Clone());
}
return cloneReference;
}
}
/// <summary>

View File

@ -159,4 +159,158 @@ namespace RainmeterStudio.Core.Model
throw new NotImplementedException();
}
}
/// <summary>
/// Extension methods for trees
/// </summary>
public static class TreeExtensions
{
/// <summary>
/// Tree traversal orders
/// </summary>
public enum TreeTraversalOrder
{
BreadthFirst,
DepthFirst,
DepthFirstPreOrder = DepthFirst,
DepthFirstPostOrder
}
/// <summary>
/// Traverses a tree
/// </summary>
/// <typeparam name="T">Tree data type</typeparam>
/// <param name="root">Root node of tree</param>
/// <param name="order">Traversal order</param>
/// <returns>An enumeration of the nodes in the specified traverse order</returns>
public static IEnumerable<Tree<T>> Traverse<T>(this Tree<T> root, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst)
{
if (order == TreeTraversalOrder.BreadthFirst)
return TraverseBF(root);
else return TraverseDF(root, order);
}
private static IEnumerable<Tree<T>> TraverseDF<T>(this Tree<T> root, TreeTraversalOrder order)
{
// Preorder - return root first
if (order == TreeTraversalOrder.DepthFirstPreOrder)
yield return root;
// Return children
foreach (var child in root.Children)
foreach (var node in TraverseDF(child, order))
yield return node;
// Postorder - return root last
if (order == TreeTraversalOrder.DepthFirstPostOrder)
yield return root;
}
private static IEnumerable<Tree<T>> TraverseBF<T>(this Tree<T> root)
{
// Create a queue containing the root
Queue<Tree<T>> queue = new Queue<Tree<T>>();
queue.Enqueue(root);
// While there are elements in the queue
while (queue.Count > 0)
{
// Return next node in tree
var node = queue.Dequeue();
yield return node;
// Enqueue node's children
foreach (var child in node.Children)
queue.Enqueue(child);
}
}
/// <summary>
/// Applies an action to every node of the tree
/// </summary>
/// <typeparam name="T">Tree data type</typeparam>
/// <param name="root">Root node of tree</param>
/// <param name="action">Action to apply</param>
/// <param name="order">Traversal order</param>
public static void Apply<T>(this Tree<T> root, Action<Tree<T>> action, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst)
{
// Safety check
if (action == null)
return;
// Apply action
foreach (var node in Traverse(root, order))
action(node);
}
/// <summary>
/// Applies an action to every node of the tree
/// </summary>
/// <typeparam name="T">Tree data type</typeparam>
/// <param name="root">Root node of tree</param>
/// <param name="action">Action to apply</param>
/// <param name="order">Traversal order</param>
public static void ApplyToData<T>(this Tree<T> root, Action<T> action, TreeTraversalOrder order = TreeTraversalOrder.BreadthFirst)
where T : class
{
// Safety check
if (action == null)
return;
// Apply action
foreach (var node in Traverse(root, order))
action(node.Data);
}
/// <summary>
/// Rebuilds the tree by applying the specified transform function
/// </summary>
/// <typeparam name="T">Data type of tree</typeparam>
/// <typeparam name="TResult">Data type of rebuilt tree</typeparam>
/// <param name="root">The root node</param>
/// <param name="transformFunction">The transform function</param>
/// <returns>The transformed tree</returns>
public static Tree<TResult> Transform<T, TResult>(this Tree<T> root, Func<Tree<T>, Tree<TResult>> transformFunction)
{
// Safety check
if (transformFunction == null)
throw new ArgumentNullException("Transform function cannot be null.");
// Build root
Tree<TResult> resRoot = transformFunction(root);
// Add children
foreach (var node in root.Children)
resRoot.Children.Add(Transform(node, transformFunction));
// Return
return resRoot;
}
/// <summary>
/// Rebuilds the tree by applying the specified transform function
/// </summary>
/// <typeparam name="T">Data type of tree</typeparam>
/// <typeparam name="TResult">Data type of rebuilt tree</typeparam>
/// <param name="root">The root node</param>
/// <param name="transformFunction">The transform function</param>
/// <returns>The transformed tree</returns>
public static Tree<TResult> TransformData<T, TResult>(this Tree<T> root, Func<T, TResult> transformFunction)
{
// Safety check
if (transformFunction == null)
throw new ArgumentNullException("Transform function cannot be null.");
// Build root
Tree<TResult> resRoot = new Tree<TResult>(transformFunction(root.Data));
// Add children
foreach (var node in root.Children)
resRoot.Children.Add(TransformData(node, transformFunction));
// Return
return resRoot;
}
}
}