Procházet zdrojové kódy

Merge branch 'userstory12' of https://github.com/m-edlund/BP-SketchAssistant into userstory12
asdf

Vincenz Mechler před 5 roky
rodič
revize
2908b50cd4

+ 13 - 0
Finished Userstories/userstory10.md

@@ -0,0 +1,13 @@
+# Userstory 10  
+ 
+|**ID**|10|  
+|-|-|
+|**Name**|Rückgängig Funktion|
+|**Beschreibung**|Man kann Aktionen wie das Zeichnen und Löschen von Linien rückgängig machen kann|
+|**Akzeptanzkriterium**|Wenn Elemente im Aktionsprotokoll vorhanden ist, kann man mit Strg+Z oder einen Knopf in der Toolbar, das letzte Ereignis rückgängig machen. D.h. das rechte Bild kehrt zu dem Zustand zurück an dem es vor dem rückgängig gemachten Ereignis war.|
+|Geschätzter Aufwand (Story Points)|2|
+|Entwickler|Martin Edlund|
+|Umgesetzt in Iteration|3|
+|Tatsächlicher Aufwand (Std.)|1.5|
+|Velocity (Std./Story Point)|0.75|
+|Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory11.md

@@ -0,0 +1,13 @@
+# Userstory 11  
+ 
+|**ID**|11|  
+|-|-|
+|**Name**|Aktion wiederholen|
+|**Beschreibung**|Ereignisse die Rückgängig gemacht wurden, können widerhergestellt werden.|
+|**Akzeptanzkriterium**|Wenn Ereignisse im Aktionsprotokoll vorhanden sind, kann man mit Strg+y oder einem Knopf in der Toolbar das verworfene Ereignis wiederherstellen.|
+|Geschätzter Aufwand (Story Points)|2|
+|Entwickler|Martin Edlund|
+|Umgesetzt in Iteration|3|
+|Tatsächlicher Aufwand (Std.)|1.5|
+|Velocity (Std./Story Point)|0.75|
+|Bemerkungen|Keine|

+ 13 - 0
Finished Userstories/userstory7.md

@@ -0,0 +1,13 @@
+# Userstory 7
+
+|**ID**|7|
+|-|-|
+|**Name**|Aktionsprotokoll|
+|**Beschreibung**|Bei jeder Aktion im Zusammenhang mit der Leinwand wird dies in einem Protokoll gespeichert, welches man sich anzeigen kann. Dies soll spätere Funktionalität ermöglichen, bei der man Aktionen rückgängig machen kann. Dieses Protokoll kann sich der Nutzer als Liste anzeigen lassen. |
+|**Akzeptanzkriterium**|Wenn eine Aktion im Bezug zur Leinwand durchgeführt wird, wird dass in einem Protokoll gespeichert. Diese Aktionen wären: Eine Neue Leinwand zu erstellen und verschiedene Interaktionen mit der Leinwand in verschiedenen Modi (Zeichenmodus, Löschmodus, etc.). Es wird die chronologische Reinfolge der Aktionen, die Änderungen die durch diese Aktion stattfand, eine kurze Beschreibung der Aktion, sowie die Art der Aktion gespeichert. Wenn das Programm geschlossen wird, verfällt das Aktionsprotokoll. Jedes mal wenn eine Änderung stattfindet, wird die letzte Aktion im Fenster angezeigt.|
+|Geschätzter Aufwand (Story Points)|20|
+|Entwickler|Martin Edlund|
+|Umgesetzt in Iteration|3|
+|Tatsächlicher Aufwand (Std.)|5|
+|Velocity (Std./Story Point)|0,25|
+|Bemerkungen|Keine|

+ 0 - 25
SketchAssistant/NUnitTestProject1/NUnitTestProject1.csproj

@@ -1,25 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>netcoreapp2.1</TargetFramework>
-
-    <IsPackable>false</IsPackable>
-
-    <ApplicationIcon />
-
-    <OutputType>Exe</OutputType>
-
-    <StartupObject />
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="nunit" Version="3.10.1" />
-    <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\SketchAssistant\SketchAssistant.csproj" />
-  </ItemGroup>
-
-</Project>

+ 20 - 0
SketchAssistant/SketchAssistant.Tests/Properties/AssemblyInfo.cs

@@ -0,0 +1,20 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("SketchAssistant.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SketchAssistant.Tests")]
+[assembly: AssemblyCopyright("Copyright ©  2018")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: Guid("7dcdc31a-8291-4b05-93d6-dcc5de27a4a0")]
+
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 77 - 0
SketchAssistant/SketchAssistant.Tests/SketchAssistant.Tests.csproj

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" />
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>SketchAssistant.Tests</RootNamespace>
+    <AssemblyName>SketchAssistant.Tests</AssemblyName>
+    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+    <IsCodedUITest>False</IsCodedUITest>
+    <TestProjectType>UnitTest</TestProjectType>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
+    </Reference>
+    <Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\MSTest.TestFramework.1.4.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="UnitTest1.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="app.config" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\SketchAssistant\SketchAssistant.csproj">
+      <Project>{0336f628-a2f7-4170-8b2e-9277c23118d4}</Project>
+      <Name>SketchAssistant</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.props'))" />
+    <Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets'))" />
+  </Target>
+  <Import Project="..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.4.0\build\net45\MSTest.TestAdapter.targets')" />
+</Project>

+ 87 - 13
SketchAssistant/NUnitTestProject1/UnitTest1.cs → SketchAssistant/SketchAssistant.Tests/UnitTest1.cs

@@ -1,18 +1,20 @@
-using NUnit.Framework;
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
 using System.Drawing;
-using System;
 using System.Collections.Generic;
 using SketchAssistant;
+using System.Windows.Forms;
 
 namespace Tests
 {
-    class LineTests
+    [TestClass]
+    public class LineTests
     {
         //========================//
         //= Bresenham Line Tests =//
         //========================//
 
-        [Test]
+        [TestMethod]
         public void BresenhamLineTest1()
         {
             //Test point
@@ -26,7 +28,7 @@ namespace Tests
             }
         }
 
-        [Test]
+        [TestMethod]
         public void BresenhamLineTest2()
         {
             //Test line going from left to right
@@ -40,7 +42,7 @@ namespace Tests
             }
         }
 
-        [Test]
+        [TestMethod]
         public void BresenhamLineTest3()
         {
             //Test line going from right to left
@@ -54,7 +56,7 @@ namespace Tests
             }
         }
 
-        [Test]
+        [TestMethod]
         public void BresenhamLineTest4()
         {
             //Test line going from top to bottom
@@ -68,7 +70,7 @@ namespace Tests
             }
         }
 
-        [Test]
+        [TestMethod]
         public void BresenhamLineTest5()
         {
             //Test line going from bottom to top
@@ -82,7 +84,7 @@ namespace Tests
             }
         }
 
-        [Test]
+        [TestMethod]
         public void BresenhamLineTest6()
         {
             //Test exactly diagonal line from top left to bottom right
@@ -96,7 +98,7 @@ namespace Tests
             }
         }
 
-        [Test]
+        [TestMethod]
         public void BresenhamLineTest7()
         {
             //Test exactly diagonal line from bottom right to top left
@@ -114,7 +116,7 @@ namespace Tests
         //= Matrix Population Tests =//
         //===========================//
 
-        [Test]
+        [TestMethod]
         public void MatrixTest1()
         {
             //Populate Matrix for temporary Line
@@ -137,7 +139,7 @@ namespace Tests
             }
         }
 
-        [Test]
+        [TestMethod]
         public void MatrixTest2()
         {
             //Populate Matrix for non-temporary Line
@@ -177,7 +179,7 @@ namespace Tests
         //= Line Constructor Test =//
         //=========================//
 
-        [Test]
+        [TestMethod]
         public void ConstructorTest()
         {
             //Create non-temporary Line and check points
@@ -199,4 +201,76 @@ namespace Tests
             }
         }
     }
+
+    [TestClass]
+    public class ActionHistoryTests
+    {
+        ToolStripStatusLabel testLabel = new ToolStripStatusLabel();
+
+        private ActionHistory GetActionHistory()
+        {
+            return new ActionHistory(testLabel);
+        }
+
+        [DataTestMethod]
+        [DataRow(SketchAction.ActionType.Start, 5, -1, "A new canvas was created.")]
+        [DataRow(SketchAction.ActionType.Draw, 5, 5, "Line number 5 was drawn.")]
+        [DataRow(SketchAction.ActionType.Delete, 10, 10, "Line number 10 was deleted.")]
+        public void ScetchActionTest1(SketchAction.ActionType type, int id, int exit, String response)
+        {
+            HashSet<int> actualResult = new HashSet<int>();
+            if (!type.Equals(SketchAction.ActionType.Start)) { actualResult.Add(id); }
+            SketchAction testAction = new SketchAction(type, id);
+            Assert.AreEqual(type, testAction.GetActionType());
+            Assert.AreEqual(true, actualResult.SetEquals(testAction.GetLineIDs()));
+            Assert.AreEqual(response, testAction.GetActionInformation());
+        }
+
+        [DataTestMethod]
+        [DataRow(SketchAction.ActionType.Start, 1, 2, 3, "A new canvas was created.")]
+        [DataRow(SketchAction.ActionType.Draw, 3, 3, 3, "Line number 3 was drawn.")]
+        [DataRow(SketchAction.ActionType.Delete, 20, 30, 40, "Several Lines were deleted.")]
+        public void ScetchActionTest2(SketchAction.ActionType type, int id1, int id2, int id3, String response)
+        {
+            HashSet<int> actualResult = new HashSet<int>();
+            if (!type.Equals(SketchAction.ActionType.Start))
+            {
+                actualResult.Add(id1);
+                actualResult.Add(id2);
+                actualResult.Add(id3);
+            }
+            SketchAction testAction = new SketchAction(type, actualResult);
+            Assert.AreEqual(type, testAction.GetActionType());
+            Assert.AreEqual(true, actualResult.SetEquals(testAction.GetLineIDs()));
+            Assert.AreEqual(response, testAction.GetActionInformation());
+        }
+
+        [DataTestMethod]
+        [DataRow(SketchAction.ActionType.Start, SketchAction.ActionType.Start, true)]
+        [DataRow(SketchAction.ActionType.Draw, SketchAction.ActionType.Delete, false)]
+        public void ActionHistoryTest1(SketchAction.ActionType action1, SketchAction.ActionType action2, bool isEmpty)
+        {
+            ActionHistory testHistory = GetActionHistory();
+            if (!action1.Equals(SketchAction.ActionType.Start)) { testHistory.AddNewAction(new SketchAction(action1, 5)); }
+            if (!action2.Equals(SketchAction.ActionType.Start)) { testHistory.AddNewAction(new SketchAction(action2, 5)); }
+            Assert.AreEqual(isEmpty, testHistory.IsEmpty());
+        }
+
+        [DataTestMethod]
+        [DataRow(SketchAction.ActionType.Draw, "Last Action: Line number 0 was drawn.")]
+        [DataRow(SketchAction.ActionType.Delete, "Last Action: Line number 0 was deleted.")]
+        public void ActionHistoryUndoRedoTest(SketchAction.ActionType actionType, String message)
+        {
+            ActionHistory testHistory = GetActionHistory();
+            SketchAction testAction = new SketchAction(actionType, 0);
+            testHistory.AddNewAction(testAction);
+            Assert.AreEqual(true, testHistory.CanUndo());
+            testHistory.MoveAction(true);
+            Assert.AreEqual(true, testHistory.CanRedo());
+            testHistory.MoveAction(false);
+            Assert.AreEqual(actionType, testHistory.GetCurrentAction().GetActionType());
+            String currLabel = testLabel.Text;
+            Assert.AreEqual(currLabel, message);
+        }
+    }
 }

+ 11 - 0
SketchAssistant/SketchAssistant.Tests/app.config

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
+</configuration>

+ 5 - 0
SketchAssistant/SketchAssistant.Tests/packages.config

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="MSTest.TestAdapter" version="1.4.0" targetFramework="net461" />
+  <package id="MSTest.TestFramework" version="1.4.0" targetFramework="net461" />
+</packages>

+ 5 - 5
SketchAssistant/SketchAssistant.sln

@@ -5,7 +5,7 @@ VisualStudioVersion = 15.0.28010.2050
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistant", "SketchAssistant\SketchAssistant.csproj", "{0336F628-A2F7-4170-8B2E-9277C23118D4}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUnitTestProject1", "NUnitTestProject1\NUnitTestProject1.csproj", "{F94A09DE-004E-421A-A4BE-961AD5CEC809}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SketchAssistant.Tests", "SketchAssistant.Tests\SketchAssistant.Tests.csproj", "{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -17,10 +17,10 @@ Global
 		{0336F628-A2F7-4170-8B2E-9277C23118D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{0336F628-A2F7-4170-8B2E-9277C23118D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{0336F628-A2F7-4170-8B2E-9277C23118D4}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F94A09DE-004E-421A-A4BE-961AD5CEC809}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F94A09DE-004E-421A-A4BE-961AD5CEC809}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F94A09DE-004E-421A-A4BE-961AD5CEC809}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F94A09DE-004E-421A-A4BE-961AD5CEC809}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7DCDC31A-8291-4B05-93D6-DCC5DE27A4A0}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 110 - 0
SketchAssistant/SketchAssistant/ActionHistory.cs

@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace SketchAssistant
+{
+    public class ActionHistory
+    {
+        //History of Actions taken
+        List<SketchAction> actionHistory;
+        //The current position in the actionHistory
+        Tuple<int, SketchAction> currentAction;
+        //The label in which the current action is displayed
+        ToolStripStatusLabel displayLabel;
+
+        public ActionHistory(ToolStripStatusLabel displayPosition)
+        {
+            displayLabel = displayPosition;
+            actionHistory = new List<SketchAction>();
+            currentAction = new Tuple<int, SketchAction>(-1, null);
+            AddNewAction(new SketchAction(SketchAction.ActionType.Start, -1));
+        }
+
+        /// <summary>
+        /// Adds a new action to the action history.
+        /// </summary>
+        /// <param name="newAction">The newly added action.</param>
+        public void AddNewAction(SketchAction newAction)
+        {
+            //The current Action is before the last action taken, delete everything after the current action.
+            if (currentAction.Item1 < actionHistory.Count - 1)
+            {
+                actionHistory.RemoveRange(currentAction.Item1 + 1, actionHistory.Count - (currentAction.Item1 + 1));
+            }
+            actionHistory.Add(newAction);
+            currentAction = new Tuple<int, SketchAction>(actionHistory.Count - 1, newAction);
+            UpdateStatusLabel();
+        }
+
+        /// <summary>
+        /// Changes the currentAction.
+        /// </summary>
+        /// <param name="moveBack">If True, moves the current action back one slot, if False, moves it forward.</param>
+        public void MoveAction(bool moveBack)
+        {
+            if(moveBack && CanUndo())
+            {
+                currentAction = new Tuple<int, SketchAction>(currentAction.Item1 - 1, actionHistory[currentAction.Item1 - 1]);
+            }
+            if(!moveBack && CanRedo())
+            {
+                currentAction = new Tuple<int, SketchAction>(currentAction.Item1 + 1, actionHistory[currentAction.Item1 + 1]);
+            }
+            UpdateStatusLabel();
+        }
+
+        /// <summary>
+        /// Returns the current action.
+        /// </summary>
+        /// <returns>The current action.</returns>
+        public SketchAction GetCurrentAction()
+        {
+            return currentAction.Item2;
+        }
+
+        /// <summary>
+        /// Return whether or not an action can be undone.
+        /// </summary>
+        /// <returns>True if an action can be undone.</returns>
+        public bool CanUndo()
+        {
+            if (currentAction.Item1 > 0) { return true; }
+            else { return false; }
+        }
+
+        /// <summary>
+        /// Return whether or not an action can be redone.
+        /// </summary>
+        /// <returns>True if an action can be redone.</returns>
+        public bool CanRedo()
+        {
+            if (currentAction.Item1 < actionHistory.Count - 1) { return true; }
+            else { return false; }
+        }
+
+        /// <summary>
+        /// Returns whether or not the history is empty.
+        /// </summary>
+        /// <returns>true if the history is empty, otherwise false</returns>
+        public bool IsEmpty()
+        {
+            if (actionHistory.Count == 1) { return true; }
+            else { return false; }
+        }
+
+        /// <summary>
+        /// Updates the status label if there is one given.
+        /// </summary>
+        private void UpdateStatusLabel()
+        {
+            if (displayLabel != null)
+            {
+                displayLabel.Text = "Last Action: " + currentAction.Item2.GetActionInformation();
+            }
+        }
+    }
+}

+ 9 - 1
SketchAssistant/SketchAssistant/App.config

@@ -1,6 +1,14 @@
-<?xml version="1.0" encoding="utf-8" ?>
+<?xml version="1.0" encoding="utf-8"?>
 <configuration>
     <startup> 
         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
     </startup>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
 </configuration>

+ 44 - 6
SketchAssistant/SketchAssistant/Form1.Designer.cs

@@ -47,6 +47,9 @@ namespace SketchAssistant
             this.toolStripLoadStatus = new System.Windows.Forms.ToolStripStatusLabel();
             this.backgroundWorker2 = new System.ComponentModel.BackgroundWorker();
             this.mouseTimer = new System.Windows.Forms.Timer(this.components);
+            this.lastActionTakenLabel = new System.Windows.Forms.ToolStripStatusLabel();
+            this.undoButton = new System.Windows.Forms.ToolStripButton();
+            this.redoButton = new System.Windows.Forms.ToolStripButton();
             this.tableLayoutPanel1.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.pictureBoxRight)).BeginInit();
             ((System.ComponentModel.ISupportInitialize)(this.pictureBoxLeft)).BeginInit();
@@ -84,7 +87,6 @@ namespace SketchAssistant
             this.pictureBoxRight.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
             this.pictureBoxRight.TabIndex = 6;
             this.pictureBoxRight.TabStop = false;
-            this.pictureBoxRight.Click += new System.EventHandler(this.pictureBoxRight_Click);
             this.pictureBoxRight.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBoxRight_MouseDown);
             this.pictureBoxRight.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBoxRight_MouseMove);
             this.pictureBoxRight.MouseUp += new System.Windows.Forms.MouseEventHandler(this.pictureBoxRight_MouseUp);
@@ -148,7 +150,9 @@ namespace SketchAssistant
             this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.canvasButton,
             this.drawButton,
-            this.deleteButton});
+            this.deleteButton,
+            this.undoButton,
+            this.redoButton});
             this.toolStrip1.Location = new System.Drawing.Point(0, 24);
             this.toolStrip1.Name = "toolStrip1";
             this.toolStrip1.Size = new System.Drawing.Size(696, 25);
@@ -188,17 +192,18 @@ namespace SketchAssistant
             // statusStrip1
             // 
             this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
-            this.toolStripLoadStatus});
-            this.statusStrip1.Location = new System.Drawing.Point(0, 493);
+            this.toolStripLoadStatus,
+            this.lastActionTakenLabel});
+            this.statusStrip1.Location = new System.Drawing.Point(0, 491);
             this.statusStrip1.Name = "statusStrip1";
-            this.statusStrip1.Size = new System.Drawing.Size(696, 22);
+            this.statusStrip1.Size = new System.Drawing.Size(696, 24);
             this.statusStrip1.TabIndex = 4;
             this.statusStrip1.Text = "statusStrip1";
             // 
             // toolStripLoadStatus
             // 
             this.toolStripLoadStatus.Name = "toolStripLoadStatus";
-            this.toolStripLoadStatus.Size = new System.Drawing.Size(40, 17);
+            this.toolStripLoadStatus.Size = new System.Drawing.Size(40, 19);
             this.toolStripLoadStatus.Text = "no file";
             // 
             // mouseTimer
@@ -206,6 +211,34 @@ namespace SketchAssistant
             this.mouseTimer.Interval = 1;
             this.mouseTimer.Tick += new System.EventHandler(this.mouseTimer_Tick);
             // 
+            // lastActionTakenLabel
+            // 
+            this.lastActionTakenLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left;
+            this.lastActionTakenLabel.Name = "lastActionTakenLabel";
+            this.lastActionTakenLabel.Size = new System.Drawing.Size(38, 19);
+            this.lastActionTakenLabel.Text = "none";
+            this.lastActionTakenLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
+            // 
+            // undoButton
+            // 
+            this.undoButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+            this.undoButton.Image = ((System.Drawing.Image)(resources.GetObject("undoButton.Image")));
+            this.undoButton.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.undoButton.Name = "undoButton";
+            this.undoButton.Size = new System.Drawing.Size(40, 22);
+            this.undoButton.Text = "Undo";
+            this.undoButton.Click += new System.EventHandler(this.undoButton_Click);
+            // 
+            // redoButton
+            // 
+            this.redoButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
+            this.redoButton.Image = ((System.Drawing.Image)(resources.GetObject("redoButton.Image")));
+            this.redoButton.ImageTransparentColor = System.Drawing.Color.Magenta;
+            this.redoButton.Name = "redoButton";
+            this.redoButton.Size = new System.Drawing.Size(38, 22);
+            this.redoButton.Text = "Redo";
+            this.redoButton.Click += new System.EventHandler(this.redoButton_Click);
+            // 
             // Form1
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -216,6 +249,7 @@ namespace SketchAssistant
             this.Controls.Add(this.toolStrip1);
             this.Controls.Add(this.tableLayoutPanel1);
             this.Controls.Add(this.menuStrip1);
+            this.KeyPreview = true;
             this.MainMenuStrip = this.menuStrip1;
             this.Margin = new System.Windows.Forms.Padding(2);
             this.Name = "Form1";
@@ -224,6 +258,7 @@ namespace SketchAssistant
             this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
             this.Load += new System.EventHandler(this.Form1_Load);
             this.SizeChanged += new System.EventHandler(this.Form1_Resize);
+            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
             this.tableLayoutPanel1.ResumeLayout(false);
             ((System.ComponentModel.ISupportInitialize)(this.pictureBoxRight)).EndInit();
             ((System.ComponentModel.ISupportInitialize)(this.pictureBoxLeft)).EndInit();
@@ -256,6 +291,9 @@ namespace SketchAssistant
         private System.Windows.Forms.ToolStripButton deleteButton;
         private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
         private System.Windows.Forms.ToolStripMenuItem examplePictureToolStripMenuItem;
+        private System.Windows.Forms.ToolStripStatusLabel lastActionTakenLabel;
+        private System.Windows.Forms.ToolStripButton undoButton;
+        private System.Windows.Forms.ToolStripButton redoButton;
     }
 }
 

+ 124 - 18
SketchAssistant/SketchAssistant/Form1.cs

@@ -71,6 +71,8 @@ namespace SketchAssistant
         HashSet<int>[,] linesMatrix;
         //Size of deletion area
         uint deletionSize = 2;
+        //History of Actions
+        ActionHistory historyOfActions;
 
         /******************************************/
         /*** FORM SPECIFIC FUNCTIONS START HERE ***/
@@ -80,6 +82,8 @@ namespace SketchAssistant
         {
             currentState = ProgramState.Idle;
             this.DoubleBuffered = true;
+            historyOfActions = new ActionHistory(null);
+            UpdateButtonStatus();
         }
 
         //Resize Function connected to the form resize event, will refresh the form when it is resized
@@ -100,6 +104,7 @@ namespace SketchAssistant
                 //Refresh the left image box when the content is changed
                 this.Refresh();
             }
+            UpdateButtonStatus();
         }
 
         /// <summary>
@@ -136,6 +141,7 @@ namespace SketchAssistant
                     ChangeState(ProgramState.Draw);
                 }
             }
+            UpdateButtonStatus();
         }
 
         //Changes the state of the program to deletion
@@ -152,6 +158,70 @@ namespace SketchAssistant
                     ChangeState(ProgramState.Delete);
                 }
             }
+            UpdateButtonStatus();
+        }
+
+        //Undo an action
+        private void undoButton_Click(object sender, EventArgs e)
+        {
+            if (historyOfActions.CanUndo())
+            {
+                HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
+                SketchAction.ActionType  undoAction = historyOfActions.GetCurrentAction().GetActionType();
+                switch (undoAction)
+                {
+                    case SketchAction.ActionType.Delete:
+                        //Deleted Lines need to be shown
+                        ChangeLines(affectedLines, true);
+                        break;
+                    case SketchAction.ActionType.Draw:
+                        //Drawn lines need to be hidden
+                        ChangeLines(affectedLines, false);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            historyOfActions.MoveAction(true);
+            UpdateButtonStatus();
+        }
+
+        //Redo an action
+        private void redoButton_Click(object sender, EventArgs e)
+        {
+            if (historyOfActions.CanRedo())
+            {
+                historyOfActions.MoveAction(false);
+                HashSet<int> affectedLines = historyOfActions.GetCurrentAction().GetLineIDs();
+                SketchAction.ActionType redoAction = historyOfActions.GetCurrentAction().GetActionType();
+                switch (redoAction)
+                {
+                    case SketchAction.ActionType.Delete:
+                        //Deleted Lines need to be redeleted
+                        ChangeLines(affectedLines, false);
+                        break;
+                    case SketchAction.ActionType.Draw:
+                        //Drawn lines need to be redrawn
+                        ChangeLines(affectedLines, true);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            UpdateButtonStatus();
+        }
+
+        //Detect Keyboard Shortcuts
+        private void Form1_KeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.Modifiers == Keys.Control && e.KeyCode == Keys.Z)
+            {
+                undoButton_Click(sender, e);
+            }
+            if (e.Modifiers == Keys.Control && e.KeyCode == Keys.Y)
+            {
+                redoButton_Click(sender, e);
+            }
         }
 
         //get current Mouse positon within the right picture box
@@ -170,19 +240,6 @@ namespace SketchAssistant
             }
         }
 
-        //when the picture box is clicked, add a point to the current line. Occurs f.ex. when using drawing tablets
-        private void pictureBoxRight_Click(object sender, EventArgs e)
-        {
-            if (currentState.Equals(ProgramState.Draw))
-            {
-                List<Point> singlePoint = new List<Point> { currentCursorPosition };
-                Line singlePointLine = new Line(singlePoint, lineList.Count);
-                singlePointLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
-                singlePointLine.DrawLine(graph);
-                pictureBoxRight.Image = rightImage;
-            }
-        }
-
         //Lift left mouse button to stop drawing and add a new Line.
         private void pictureBoxRight_MouseUp(object sender, MouseEventArgs e)
         {
@@ -192,7 +249,9 @@ namespace SketchAssistant
                 Line newLine = new Line(currentLine, lineList.Count);
                 lineList.Add(new Tuple<bool, Line>(true, newLine));
                 newLine.PopulateMatrixes(isFilledMatrix, linesMatrix);
+                historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Draw, newLine.GetID()));
             }
+            UpdateButtonStatus();
         }
 
         //Button to create a new Canvas. Will create an empty image 
@@ -200,10 +259,29 @@ namespace SketchAssistant
         //If there is no image loaded the canvas will be the size of the right picture box
         private void canvasButton_Click(object sender, EventArgs e)
         {
-            DrawEmptyCanvas();
-            //The following lines cannot be in DrawEmptyCanvas()
-            isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
-            linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
+            if (!historyOfActions.IsEmpty())
+            {
+                if (MessageBox.Show("You have unsaved changes, creating a new canvas will discard these.", 
+                    "Attention", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
+                {
+                    historyOfActions = new ActionHistory(lastActionTakenLabel);
+                    DrawEmptyCanvas();
+                    //The following lines cannot be in DrawEmptyCanvas()
+                    isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
+                    linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
+                    lineList = new List<Tuple<bool, Line>>();
+                }
+            }
+            else
+            {
+                historyOfActions = new ActionHistory(lastActionTakenLabel);
+                DrawEmptyCanvas();
+                //The following lines cannot be in DrawEmptyCanvas()
+                isFilledMatrix = new bool[rightImage.Width, rightImage.Height];
+                linesMatrix = new HashSet<int>[rightImage.Width, rightImage.Height];
+                lineList = new List<Tuple<bool, Line>>();
+            }
+            UpdateButtonStatus();
         }
 
         //add a Point on every tick to the Drawpath
@@ -211,7 +289,6 @@ namespace SketchAssistant
         {
             cursorPositions.Enqueue(currentCursorPosition);
             previousCursorPosition = cursorPositions.Dequeue();
-
             if (currentState.Equals(ProgramState.Draw) && mousePressed)
             {
                 currentLine.Add(currentCursorPosition);
@@ -227,6 +304,7 @@ namespace SketchAssistant
                     HashSet<int> linesToDelete = CheckDeletionMatrixesAroundPoint(currPoint, deletionSize);
                     if (linesToDelete.Count > 0)
                     {
+                        historyOfActions.AddNewAction(new SketchAction(SketchAction.ActionType.Delete, linesToDelete));
                         foreach (int lineID in linesToDelete)
                         {
                             lineList[lineID] = new Tuple<bool, Line>(false, lineList[lineID].Item2);
@@ -303,6 +381,34 @@ namespace SketchAssistant
             pictureBoxRight.Refresh();
         }
 
+        /// <summary>
+        /// Change the status of whether or not the lines are shown.
+        /// </summary>
+        /// <param name="lines">The HashSet containing the affected Line IDs.</param>
+        /// <param name="shown">True if the lines should be shown, false if they should be hidden.</param>
+        private void ChangeLines(HashSet<int> lines, bool shown)
+        {
+            foreach (int lineId in lines)
+            {
+                if (lineId <= lineList.Count - 1 && lineId >= 0)
+                {
+                    lineList[lineId] = new Tuple<bool, Line>(shown, lineList[lineId].Item2);
+                }
+            }
+            RedrawRightImage();
+        }
+
+        /// <summary>
+        /// Updates the active status of buttons. Currently draw, delete, undo and redo button.
+        /// </summary>
+        private void UpdateButtonStatus()
+        {
+            undoButton.Enabled = historyOfActions.CanUndo();
+            redoButton.Enabled = historyOfActions.CanRedo();
+            drawButton.Enabled = (rightImage != null);
+            deleteButton.Enabled = (rightImage != null);
+        }
+
         /// <summary>
         /// A helper function which handles tasks associated witch changing states, 
         /// such as checking and unchecking buttons and changing the state.

+ 30 - 0
SketchAssistant/SketchAssistant/Form1.resx

@@ -181,6 +181,36 @@
   <metadata name="mouseTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>786, 17</value>
   </metadata>
+  <data name="undoButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
+  <data name="redoButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+    <value>
+        iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+        YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
+        YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
+        0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
+        bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
+        VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
+        c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
+        Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
+        mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
+        kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
+        TgDQASA1MVpwzwAAAABJRU5ErkJggg==
+</value>
+  </data>
   <metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
     <value>47</value>
   </metadata>

+ 5 - 0
SketchAssistant/SketchAssistant/Line.cs

@@ -53,6 +53,11 @@ namespace SketchAssistant
             return linePoints;
         }
 
+        public int GetID()
+        {
+            return identifier;
+        }
+
         /// <summary>
         /// A function that takes a Graphics element and returns it with
         /// the line drawn on it.

+ 98 - 0
SketchAssistant/SketchAssistant/SketchAction.cs

@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SketchAssistant
+{
+    public class SketchAction
+    {
+        //Types of possible actions
+        public enum ActionType
+        {
+            Draw,
+            Delete,
+            Start
+        }
+        //Type of this action
+        private ActionType thisAction;
+        //ID of the Line affected
+        private HashSet<int> lineIDs;
+
+        /// <summary>
+        /// Constructor for a new action with multiple lines affected.
+        /// </summary>
+        /// <param name="theAction">The type of action, if it is ActionType.Start the affectedIDs will be ignored.</param>
+        /// <param name="affectedID">The IDs of the lines affected.</param>
+        public SketchAction(ActionType theAction, HashSet<int> affectedIDs)
+        {
+            thisAction = theAction;
+            if (theAction.Equals(ActionType.Start)) { lineIDs = new HashSet<int>(); }
+            else { lineIDs = new HashSet<int>(affectedIDs); }
+        }
+
+        /// <summary>
+        /// Constructor for a new action with one line affected.
+        /// </summary>
+        /// <param name="theAction">The type of action, if it is ActionType.Start the affectedID will be ignored.</param>
+        /// <param name="affectedID">The ID of the affected line.</param>
+        public SketchAction(ActionType theAction, int affectedID)
+        {
+            thisAction = theAction;
+            if (theAction.Equals(ActionType.Start)) { lineIDs = new HashSet<int>(); }
+            else
+            {
+                lineIDs = new HashSet<int>();
+                lineIDs.Add(affectedID);
+            }
+        }
+
+        /// <summary>
+        /// Fetches the type of this action.
+        /// </summary>
+        /// <returns>The type of this action.</returns>
+        public ActionType GetActionType()
+        {
+            return thisAction;
+        }
+
+        /// <summary>
+        /// Fetches the IDs of the lines affected by this action.
+        /// </summary>
+        /// <returns>The IDs of the lines affected by this action. An empty set if there is no line affected.</returns>
+        public HashSet<int> GetLineIDs()
+        {
+            return lineIDs;
+        }
+
+        /// <summary>
+        /// Get the information about this action.
+        /// </summary>
+        /// <returns>A String describing what happend at this action.</returns>
+        public String GetActionInformation()
+        {
+            String returnString;
+            switch (thisAction)
+            {
+                case ActionType.Start:
+                    returnString = "A new canvas was created.";
+                    break;
+                case ActionType.Draw:
+                    returnString = "Line number " + lineIDs.First().ToString() + " was drawn.";
+                    break;
+                case ActionType.Delete:
+                    if (lineIDs.Count == 1) { returnString = "Line number " + lineIDs.First().ToString() + " was deleted."; }
+                    else
+                    {
+                        returnString = "Several Lines were deleted.";
+                    }
+                    break;
+                default:
+                    returnString = "There is no information available for this action.";
+                    break;
+            }
+            return returnString;
+        }
+    }
+}

+ 12 - 13
SketchAssistant/SketchAssistant/SketchAssistant.csproj

@@ -1,7 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="..\packages\NUnit3TestAdapter.3.11.2\build\net35\NUnit3TestAdapter.props" Condition="Exists('..\packages\NUnit3TestAdapter.3.11.2\build\net35\NUnit3TestAdapter.props')" />
-  <Import Project="..\packages\NUnit.3.11.0\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.11.0\build\NUnit.props')" />
   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -51,11 +49,15 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="nunit.framework, Version=3.11.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
-      <HintPath>..\packages\NUnit.3.11.0\lib\net45\nunit.framework.dll</HintPath>
-    </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
+    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -67,6 +69,8 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="SketchAction.cs" />
+    <Compile Include="ActionHistory.cs" />
     <Compile Include="Line.cs" />
     <Compile Include="Form1.cs">
       <SubType>Form</SubType>
@@ -100,7 +104,9 @@
     </Compile>
   </ItemGroup>
   <ItemGroup>
-    <None Include="App.config" />
+    <None Include="App.config">
+      <SubType>Designer</SubType>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
@@ -115,11 +121,4 @@
     </BootstrapperPackage>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
-  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
-    <PropertyGroup>
-      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
-    </PropertyGroup>
-    <Error Condition="!Exists('..\packages\NUnit.3.11.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.11.0\build\NUnit.props'))" />
-    <Error Condition="!Exists('..\packages\NUnit3TestAdapter.3.11.2\build\net35\NUnit3TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit3TestAdapter.3.11.2\build\net35\NUnit3TestAdapter.props'))" />
-  </Target>
 </Project>

+ 3 - 11
SketchAssistant/SketchAssistant/packages.config

@@ -1,14 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="NUnit" version="3.11.0" targetFramework="net461" />
-  <package id="NUnit.Console" version="3.9.0" targetFramework="net461" />
-  <package id="NUnit.ConsoleRunner" version="3.9.0" targetFramework="net461" />
-  <package id="NUnit.Extension.NUnitProjectLoader" version="3.6.0" targetFramework="net461" />
-  <package id="NUnit.Extension.NUnitV2Driver" version="3.7.0" targetFramework="net461" />
-  <package id="NUnit.Extension.NUnitV2ResultWriter" version="3.6.0" targetFramework="net461" />
-  <package id="NUnit.Extension.TeamCityEventListener" version="1.0.5" targetFramework="net461" />
-  <package id="NUnit.Extension.VSProjectLoader" version="3.8.0" targetFramework="net461" />
-  <package id="NUnit3TestAdapter" version="3.11.2" targetFramework="net461" />
-  <package id="Svg" version="2.3.0" targetFramework="net461" />
-  <package id="SvgNet" version="1.0.8" targetFramework="net461" />
+  <package id="OpenCover" version="4.6.519" targetFramework="net461" />
+  <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net461" />
+  <package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net461" />
 </packages>