Actions can now have children.
1.1 --- a/Server/FormMain.cs Wed Aug 31 17:28:30 2016 +0200
1.2 +++ b/Server/FormMain.cs Wed Aug 31 20:20:32 2016 +0200
1.3 @@ -309,6 +309,65 @@
1.4
1.5 }
1.6
1.7 +
1.8 + private static void AddActionsToTreeNode(TreeNode aParentNode, Ear.Object aObject)
1.9 + {
1.10 + foreach (Ear.Action a in aObject.Objects.OfType<Ear.Action>())
1.11 + {
1.12 + //Create action node
1.13 + TreeNode actionNode = aParentNode.Nodes.Add(a.Brief());
1.14 + actionNode.Tag = a;
1.15 + //Use color from parent unless our action itself is disabled
1.16 + actionNode.ForeColor = a.Enabled ? aParentNode.ForeColor : Color.DimGray;
1.17 + //Go recursive
1.18 + AddActionsToTreeNode(actionNode,a);
1.19 + }
1.20 + }
1.21 +
1.22 +
1.23 + /// <summary>
1.24 + ///
1.25 + /// </summary>
1.26 + /// <param name="aObject"></param>
1.27 + /// <param name="aNode"></param>
1.28 + private static TreeNode FindTreeNodeForEarObject(Ear.Object aObject, TreeNode aNode)
1.29 + {
1.30 + if (aNode.Tag == aObject)
1.31 + {
1.32 + return aNode;
1.33 + }
1.34 +
1.35 + foreach (TreeNode n in aNode.Nodes)
1.36 + {
1.37 + TreeNode found = FindTreeNodeForEarObject(aObject,n);
1.38 + if (found != null)
1.39 + {
1.40 + return found;
1.41 + }
1.42 + }
1.43 +
1.44 + return null;
1.45 + }
1.46 +
1.47 +
1.48 + /// <summary>
1.49 + ///
1.50 + /// </summary>
1.51 + /// <param name="aObject"></param>
1.52 + private void SelectEarObject(Ear.Object aObject)
1.53 + {
1.54 + foreach (TreeNode n in iTreeViewEvents.Nodes)
1.55 + {
1.56 + TreeNode found = FindTreeNodeForEarObject(aObject, n);
1.57 + if (found != null)
1.58 + {
1.59 + iTreeViewEvents.SelectedNode=found;
1.60 + iTreeViewEvents.Focus();
1.61 + return;
1.62 + }
1.63 + }
1.64 + }
1.65 +
1.66 /// <summary>
1.67 /// Populate tree view with events and actions
1.68 /// </summary>
1.69 @@ -318,9 +377,6 @@
1.70 buttonActionAdd.Enabled = false;
1.71 buttonActionDelete.Enabled = false;
1.72
1.73 - Ear.Event currentEvent = CurrentEvent();
1.74 - Ear.Action currentAction = CurrentAction();
1.75 - TreeNode treeNodeToSelect = null;
1.76
1.77 //Reset our tree
1.78 iTreeViewEvents.Nodes.Clear();
1.79 @@ -352,43 +408,14 @@
1.80 //Add event description as child node
1.81 eventNode.Nodes.Add(e.AttributeDescription).ForeColor = eventNode.ForeColor;
1.82 //Create child node for actions root
1.83 - TreeNode actionsNodes = eventNode.Nodes.Add("Actions");
1.84 - actionsNodes.ForeColor = eventNode.ForeColor;
1.85 -
1.86 - // Add our actions for that event
1.87 - foreach (Ear.Action a in e.Actions)
1.88 - {
1.89 - TreeNode actionNode = actionsNodes.Nodes.Add(a.Brief());
1.90 - actionNode.Tag = a;
1.91 - //Use color from event unless our action itself is disabled
1.92 - actionNode.ForeColor = a.Enabled? eventNode.ForeColor: Color.DimGray;
1.93 - if (a == currentAction)
1.94 - {
1.95 - treeNodeToSelect = actionNode;
1.96 - }
1.97 - }
1.98 + TreeNode actionsNode = eventNode.Nodes.Add("Actions");
1.99 + actionsNode.ForeColor = eventNode.ForeColor;
1.100 +
1.101 + // Recursively add our actions for that event
1.102 + AddActionsToTreeNode(actionsNode,e);
1.103 }
1.104
1.105 iTreeViewEvents.ExpandAll();
1.106 - SelectEvent(currentEvent);
1.107 -
1.108 - if (treeNodeToSelect != null)
1.109 - {
1.110 - iTreeViewEvents.SelectedNode = treeNodeToSelect;
1.111 - }
1.112 - else if (iTreeViewEvents.SelectedNode != null && iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) > 0)
1.113 - {
1.114 - //Select the last action if any
1.115 - iTreeViewEvents.SelectedNode =
1.116 - iTreeViewEvents.SelectedNode.Nodes[1].Nodes[
1.117 - iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) - 1];
1.118 - }
1.119 - else if (iTreeViewEvents.SelectedNode == null && iTreeViewEvents.Nodes.Count > 0)
1.120 - {
1.121 - //Still no selected node select the first one then
1.122 - iTreeViewEvents.SelectedNode = iTreeViewEvents.Nodes[0];
1.123 - }
1.124 -
1.125
1.126 UpdateEventView();
1.127 }
1.128 @@ -2650,26 +2677,6 @@
1.129 }
1.130
1.131
1.132 - /// <summary>
1.133 - ///
1.134 - /// </summary>
1.135 - /// <param name="aEvent"></param>
1.136 - private void SelectEvent(Ear.Event aEvent)
1.137 - {
1.138 - if (aEvent == null)
1.139 - {
1.140 - return;
1.141 - }
1.142 -
1.143 - foreach (TreeNode node in iTreeViewEvents.Nodes)
1.144 - {
1.145 - if (node.Tag == aEvent)
1.146 - {
1.147 - iTreeViewEvents.SelectedNode = node;
1.148 - iTreeViewEvents.Focus();
1.149 - }
1.150 - }
1.151 - }
1.152
1.153
1.154
1.155 @@ -2713,14 +2720,58 @@
1.156 /// <summary>
1.157 ///
1.158 /// </summary>
1.159 + /// <returns></returns>
1.160 + private Ear.Object CurrentEarObject()
1.161 + {
1.162 + Ear.Action a = CurrentAction();
1.163 + Ear.Event e = CurrentEvent();
1.164 +
1.165 + if (a != null)
1.166 + {
1.167 + return a;
1.168 + }
1.169 +
1.170 + return e;
1.171 + }
1.172 +
1.173 + /// <summary>
1.174 + /// Get the current action based on event tree view selection
1.175 + /// </summary>
1.176 + /// <returns></returns>
1.177 + private Ear.Object CurrentEarParent()
1.178 + {
1.179 + TreeNode node = iTreeViewEvents.SelectedNode;
1.180 + if (node == null || node.Parent == null)
1.181 + {
1.182 + return null;
1.183 + }
1.184 +
1.185 + if (node.Parent.Tag is Ear.Object)
1.186 + {
1.187 + return (Ear.Object)node.Parent.Tag;
1.188 + }
1.189 +
1.190 + if (node.Parent.Parent != null && node.Parent.Parent.Tag is Ear.Object)
1.191 + {
1.192 + //Can be the case for events
1.193 + return (Ear.Object)node.Parent.Parent.Tag;
1.194 + }
1.195 +
1.196 + return null;
1.197 + }
1.198 +
1.199 +
1.200 + /// <summary>
1.201 + ///
1.202 + /// </summary>
1.203 /// <param name="sender"></param>
1.204 /// <param name="e"></param>
1.205 private void buttonActionAdd_Click(object sender, EventArgs e)
1.206 {
1.207 - Ear.Event selectedEvent = CurrentEvent();
1.208 - if (selectedEvent == null)
1.209 + Ear.Object parent = CurrentEarObject();
1.210 + if (parent == null )
1.211 {
1.212 - //We did not find a corresponding event
1.213 + //We did not find a corresponding event or action
1.214 return;
1.215 }
1.216
1.217 @@ -2729,9 +2780,10 @@
1.218 DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
1.219 if (res == DialogResult.OK)
1.220 {
1.221 - selectedEvent.Actions.Add(ea.Object);
1.222 + parent.Objects.Add(ea.Object);
1.223 Properties.Settings.Default.Save();
1.224 PopulateTreeViewEvents();
1.225 + SelectEarObject(ea.Object);
1.226 }
1.227 }
1.228
1.229 @@ -2742,11 +2794,12 @@
1.230 /// <param name="e"></param>
1.231 private void buttonActionEdit_Click(object sender, EventArgs e)
1.232 {
1.233 - Ear.Event selectedEvent = CurrentEvent();
1.234 Ear.Action selectedAction = CurrentAction();
1.235 - if (selectedEvent == null || selectedAction == null)
1.236 + Ear.Object parent = CurrentEarParent()
1.237 + ;
1.238 + if (parent == null || selectedAction == null)
1.239 {
1.240 - //We did not find a corresponding event
1.241 + //We did not find a corresponding parent
1.242 return;
1.243 }
1.244
1.245 @@ -2757,11 +2810,14 @@
1.246 DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
1.247 if (res == DialogResult.OK)
1.248 {
1.249 + //Make sure we keep the same children as before
1.250 + ea.Object.Objects = parent.Objects[actionIndex].Objects;
1.251 //Update our action
1.252 - selectedEvent.Actions[actionIndex]=ea.Object;
1.253 + parent.Objects[actionIndex]=ea.Object;
1.254 //Save and rebuild our event tree view
1.255 Properties.Settings.Default.Save();
1.256 PopulateTreeViewEvents();
1.257 + SelectEarObject(ea.Object);
1.258 }
1.259 }
1.260
1.261 @@ -2772,7 +2828,6 @@
1.262 /// <param name="e"></param>
1.263 private void buttonActionDelete_Click(object sender, EventArgs e)
1.264 {
1.265 -
1.266 Ear.Action action = CurrentAction();
1.267 if (action == null)
1.268 {
1.269 @@ -2816,16 +2871,17 @@
1.270 }
1.271
1.272 //Swap actions in event's action list
1.273 - Ear.Event currentEvent = CurrentEvent();
1.274 + Ear.Object parent = CurrentEarParent();
1.275 int currentIndex = iTreeViewEvents.SelectedNode.Index;
1.276 - Ear.Action movingUp = currentEvent.Actions[currentIndex];
1.277 - Ear.Action movingDown = currentEvent.Actions[currentIndex-1];
1.278 - currentEvent.Actions[currentIndex] = movingDown;
1.279 - currentEvent.Actions[currentIndex-1] = movingUp;
1.280 + Ear.Action movingUp = parent.Objects[currentIndex] as Ear.Action;
1.281 + Ear.Action movingDown = parent.Objects[currentIndex-1] as Ear.Action;
1.282 + parent.Objects[currentIndex] = movingDown;
1.283 + parent.Objects[currentIndex-1] = movingUp;
1.284
1.285 //Save and populate our tree again
1.286 Properties.Settings.Default.Save();
1.287 PopulateTreeViewEvents();
1.288 + SelectEarObject(a);
1.289
1.290 }
1.291
1.292 @@ -2845,16 +2901,17 @@
1.293 }
1.294
1.295 //Swap actions in event's action list
1.296 - Ear.Event currentEvent = CurrentEvent();
1.297 + Ear.Object parent = CurrentEarParent();
1.298 int currentIndex = iTreeViewEvents.SelectedNode.Index;
1.299 - Ear.Action movingDown = currentEvent.Actions[currentIndex];
1.300 - Ear.Action movingUp = currentEvent.Actions[currentIndex + 1];
1.301 - currentEvent.Actions[currentIndex] = movingUp;
1.302 - currentEvent.Actions[currentIndex + 1] = movingDown;
1.303 + Ear.Action movingDown = parent.Objects[currentIndex] as Ear.Action;
1.304 + Ear.Action movingUp = parent.Objects[currentIndex + 1] as Ear.Action;
1.305 + parent.Objects[currentIndex] = movingUp;
1.306 + parent.Objects[currentIndex + 1] = movingDown;
1.307
1.308 //Save and populate our tree again
1.309 Properties.Settings.Default.Save();
1.310 PopulateTreeViewEvents();
1.311 + SelectEarObject(a);
1.312 }
1.313
1.314
1.315 @@ -2930,7 +2987,7 @@
1.316 Properties.Settings.Default.EarManager.Events.Add(ea.Object);
1.317 Properties.Settings.Default.Save();
1.318 PopulateTreeViewEvents();
1.319 - SelectEvent(ea.Object);
1.320 + SelectEarObject(ea.Object);
1.321 }
1.322 }
1.323
1.324 @@ -2975,12 +3032,13 @@
1.325 if (res == DialogResult.OK)
1.326 {
1.327 //Make sure we keep the same actions as before
1.328 - ea.Object.Actions = Properties.Settings.Default.EarManager.Events[index].Actions;
1.329 + ea.Object.Objects = Properties.Settings.Default.EarManager.Events[index].Objects;
1.330 //Update our event
1.331 Properties.Settings.Default.EarManager.Events[index] = ea.Object;
1.332 //Save and rebuild our event tree view
1.333 Properties.Settings.Default.Save();
1.334 PopulateTreeViewEvents();
1.335 + SelectEarObject(ea.Object);
1.336 }
1.337 }
1.338
2.1 --- a/SharpLibEar/Action.cs Wed Aug 31 17:28:30 2016 +0200
2.2 +++ b/SharpLibEar/Action.cs Wed Aug 31 20:20:32 2016 +0200
2.3 @@ -4,6 +4,7 @@
2.4 using System;
2.5 using System.Collections.Generic;
2.6 using System.Diagnostics;
2.7 +using System.Linq;
2.8 using System.Runtime.Serialization;
2.9 using System.Threading;
2.10 using System.Threading.Tasks;
2.11 @@ -11,7 +12,8 @@
2.12 namespace SharpLib.Ear
2.13 {
2.14 [DataContract]
2.15 - public abstract class Action: Object
2.16 + [AttributeObject(Id = "Action", Name = "Action", Description = "An empty action.")]
2.17 + public class Action: Object
2.18 {
2.19 [DataMember]
2.20 [AttributeObjectProperty
2.21 @@ -34,7 +36,14 @@
2.22 get { return Iterations > 0; }
2.23 }
2.24
2.25 - protected abstract Task DoExecute();
2.26 + /// <summary>
2.27 + /// Basic action just does nothing
2.28 + /// </summary>
2.29 + /// <returns></returns>
2.30 + protected virtual async Task DoExecute()
2.31 + {
2.32 +
2.33 + }
2.34
2.35 /// <summary>
2.36 /// Allows testing from generic edit dialog.
2.37 @@ -66,14 +75,25 @@
2.38 for (int i = Iterations; i > 0; i--)
2.39 {
2.40 Trace.WriteLine($"EAR: Action.Execute: [{Iterations - i + 1}/{Iterations}] - {BriefBase()}");
2.41 + //For each iteration
2.42 + //We first execute ourselves
2.43 await DoExecute();
2.44 +
2.45 + //Then our children
2.46 + foreach (Action a in Objects.OfType<Action>())
2.47 + {
2.48 + await a.Execute();
2.49 + }
2.50 }
2.51 }
2.52
2.53 -
2.54 + /// <summary>
2.55 + /// For inherited classes to override.
2.56 + /// </summary>
2.57 + /// <returns></returns>
2.58 public virtual string BriefBase()
2.59 {
2.60 - return base.Brief();
2.61 + return Name;
2.62 }
2.63
2.64 /// <summary>
3.1 --- a/SharpLibEar/ActionDelay.cs Wed Aug 31 17:28:30 2016 +0200
3.2 +++ b/SharpLibEar/ActionDelay.cs Wed Aug 31 20:20:32 2016 +0200
3.3 @@ -8,7 +8,7 @@
3.4 namespace SharpLib.Ear
3.5 {
3.6 [DataContract]
3.7 - [AttributeObject(Id = "Task.Delay", Name = "Delay", Description = "Delay the execution of the next task by the specified amount of milliseconds.")]
3.8 + [AttributeObject(Id = "Action.Task.Delay", Name = "Delay", Description = "Delay the execution of the next task.")]
3.9 public class ActionDelay : Action
3.10 {
3.11 [DataMember]
3.12 @@ -16,7 +16,7 @@
3.13 (
3.14 Id = "Task.Delay.Milliseconds",
3.15 Name = "Delay (ms)",
3.16 - Description = "Specifies our delay in milliseconds.",
3.17 + Description = "Specifies the delay in milliseconds.",
3.18 Minimum = "0",
3.19 Maximum = "60000",
3.20 Increment = "1000"
4.1 --- a/SharpLibEar/Event.cs Wed Aug 31 17:28:30 2016 +0200
4.2 +++ b/SharpLibEar/Event.cs Wed Aug 31 20:20:32 2016 +0200
4.3 @@ -4,6 +4,7 @@
4.4 using System;
4.5 using System.Collections.Generic;
4.6 using System.Diagnostics;
4.7 +using System.Linq;
4.8 using System.Runtime.Serialization;
4.9 using System.Threading.Tasks;
4.10
4.11 @@ -24,41 +25,6 @@
4.12 public bool Enabled { get; set; } = true;
4.13
4.14
4.15 - /// <summary>
4.16 - /// TODO: Should the name property be moved to our EAR Object?
4.17 - /// </summary>
4.18 - [DataMember]
4.19 - [AttributeObjectProperty
4.20 - (
4.21 - Id = "Event.Name",
4.22 - Name = "Name",
4.23 - Description = "Given event name. Can be used to trigger it."
4.24 - )
4.25 - ]
4.26 - public string Name { get; set; } = "";
4.27 -
4.28 -
4.29 - [DataMember]
4.30 - public List<Action> Actions = new List<Action>();
4.31 -
4.32 -
4.33 - protected override void DoConstruct()
4.34 - {
4.35 - base.DoConstruct();
4.36 -
4.37 - //Make sure our name is not null
4.38 - if (Name == null)
4.39 - {
4.40 - Name = "";
4.41 - }
4.42 -
4.43 - // TODO: Construct properties too
4.44 - foreach (Action a in Actions)
4.45 - {
4.46 - a.Construct();
4.47 - }
4.48 -
4.49 - }
4.50
4.51
4.52 /// <summary>
4.53 @@ -74,7 +40,7 @@
4.54 public async Task Trigger()
4.55 {
4.56 Trace.WriteLine("Event triggered: " + AttributeName);
4.57 - foreach (Action action in Actions)
4.58 + foreach (Action action in Objects.OfType<Action>())
4.59 {
4.60 await action.Execute();
4.61 }
5.1 --- a/SharpLibEar/Manager.cs Wed Aug 31 17:28:30 2016 +0200
5.2 +++ b/SharpLibEar/Manager.cs Wed Aug 31 20:20:32 2016 +0200
5.3 @@ -102,12 +102,38 @@
5.4 {
5.5 foreach (Event e in Events)
5.6 {
5.7 - if (e.Actions.Remove(aAction))
5.8 + if (RemoveObject(e,aAction))
5.9 {
5.10 //We removed our action, we are done here.
5.11 return;
5.12 }
5.13 }
5.14 }
5.15 +
5.16 + /// <summary>
5.17 + /// Remove the specified action from the event it belongs too.
5.18 + /// </summary>
5.19 + /// <param name="aAction"></param>
5.20 + private static bool RemoveObject(Object aCurrent, Object aToRemove)
5.21 + {
5.22 + //Exit condition
5.23 + if (aCurrent.Objects.Remove(aToRemove))
5.24 + {
5.25 + //We removed our action, we are done here.
5.26 + return true;
5.27 + }
5.28 +
5.29 + foreach (Object o in aCurrent.Objects)
5.30 + {
5.31 + bool done = RemoveObject(o, aToRemove);
5.32 + if (done)
5.33 + {
5.34 + return true;
5.35 + }
5.36 + }
5.37 +
5.38 + return false;
5.39 + }
5.40 +
5.41 }
5.42 }
5.43 \ No newline at end of file
6.1 --- a/SharpLibEar/Object.cs Wed Aug 31 17:28:30 2016 +0200
6.2 +++ b/SharpLibEar/Object.cs Wed Aug 31 20:20:32 2016 +0200
6.3 @@ -19,6 +19,22 @@
6.4 {
6.5 private bool iConstructed = false;
6.6
6.7 + [DataMember]
6.8 + public List<Object> Objects = new List<Object>();
6.9 +
6.10 + /// <summary>
6.11 + /// </summary>
6.12 + [DataMember]
6.13 + [AttributeObjectProperty
6.14 + (
6.15 + Id = "Object.Name",
6.16 + Name = "Name",
6.17 + Description = "Given object name."
6.18 + )
6.19 + ]
6.20 + public string Name { get; set; } = "";
6.21 +
6.22 +
6.23 protected Object()
6.24 {
6.25 Construct();
6.26 @@ -29,11 +45,18 @@
6.27 /// </summary>
6.28 public void Construct()
6.29 {
6.30 + //Construct ourselves first
6.31 if (!iConstructed)
6.32 {
6.33 DoConstruct();
6.34 iConstructed = true;
6.35 }
6.36 +
6.37 + //Then construct our children
6.38 + foreach (Object o in Objects)
6.39 + {
6.40 + o.Construct();
6.41 + }
6.42 }
6.43
6.44 /// <summary>
6.45 @@ -41,10 +64,19 @@
6.46 /// </summary>
6.47 protected virtual void DoConstruct()
6.48 {
6.49 + //Make sure our name is not null
6.50 + if (Name == null)
6.51 + {
6.52 + Name = "";
6.53 + }
6.54
6.55 + // Makes sure our objects are not null
6.56 + if (Objects == null)
6.57 + {
6.58 + Objects = new List<Object>();
6.59 + }
6.60 }
6.61
6.62 -
6.63 public enum State
6.64 {
6.65 Rest=0,