From 6f1845f5fe25dbf824ced331a864a22aae48d155 Mon Sep 17 00:00:00 2001 From: Benjamin Popp Date: Thu, 3 Jan 2019 21:23:44 -0600 Subject: [PATCH] make string tool interact with main viewport in the expected ways. --- src/Gen3Hex.Core/Models/FormattedRun.cs | 2 +- src/Gen3Hex.Core/Models/PCSString.cs | 2 +- src/Gen3Hex.Core/ViewModels/IToolViewModel.cs | 16 ++++- src/Gen3Hex.Core/ViewModels/ViewPort.cs | 19 +++++- src/Gen3Hex.Tests/ToolTests.cs | 59 ++++++++++++++++++- src/Gen3Hex.WPF/Windows/MainWindow.xaml | 2 +- 6 files changed, 92 insertions(+), 8 deletions(-) diff --git a/src/Gen3Hex.Core/Models/FormattedRun.cs b/src/Gen3Hex.Core/Models/FormattedRun.cs index ffb7b300..730645c4 100644 --- a/src/Gen3Hex.Core/Models/FormattedRun.cs +++ b/src/Gen3Hex.Core/Models/FormattedRun.cs @@ -94,7 +94,7 @@ namespace HavenSoft.Gen3Hex.Core.Models { bool isEscaped = index > Start && data[index - 1] == PCSString.Escape; var fullString = PCSString.Convert(data, Start, Length); if (isEscaped) { - return new EscapedPCS(Start, index-Start, fullString, data[Start + index]); + return new EscapedPCS(Start, index-Start, fullString, data[index]); } else { var character = PCSString.Convert(data, index, 1).Substring(1); // trim leading " if (index == Start) character = '"' + character; // include the opening quotation mark, only for the first character diff --git a/src/Gen3Hex.Core/Models/PCSString.cs b/src/Gen3Hex.Core/Models/PCSString.cs index a48c1c30..4cf13676 100644 --- a/src/Gen3Hex.Core/Models/PCSString.cs +++ b/src/Gen3Hex.Core/Models/PCSString.cs @@ -61,7 +61,7 @@ namespace HavenSoft.Gen3Hex.Core.Models { result.Add((byte)i); index += PCS[i].Length - 1; if (i == Escape) { - result.Add(byte.Parse(input.Substring(index, 2), NumberStyles.HexNumber)); + result.Add(byte.Parse(input.Substring(index + 1, 2), NumberStyles.HexNumber)); index += 2; } break; diff --git a/src/Gen3Hex.Core/ViewModels/IToolViewModel.cs b/src/Gen3Hex.Core/ViewModels/IToolViewModel.cs index 0e841012..bbce5b30 100644 --- a/src/Gen3Hex.Core/ViewModels/IToolViewModel.cs +++ b/src/Gen3Hex.Core/ViewModels/IToolViewModel.cs @@ -1,5 +1,6 @@ using HavenSoft.Gen3Hex.Core.Models; using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -64,8 +65,18 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels { if (run == null) return; var bytes = PCSString.Convert(content); var newRun = model.RelocateForExpansion(run, bytes.Count); + if (run.Start != newRun.Start) ModelDataMoved?.Invoke(this, (run.Start, newRun.Start)); + + // clear out excess bytes that are no longer in use + if (run.Start == newRun.Start) { + for (int i = bytes.Count; i < run.Length; i++) model[run.Start + i] = 0xFF; + } + for (int i = 0; i < bytes.Count; i++) model[newRun.Start + i] = bytes[i]; - model.ObserveRunWritten(new PCSRun(newRun.Start, bytes.Count, newRun.PointerSources)); + run = new PCSRun(newRun.Start, bytes.Count, newRun.PointerSources); + model.ObserveRunWritten(run); + ModelDataChanged?.Invoke(this, run); + TryUpdate(ref address, newRun.Start, nameof(Address)); } } } @@ -82,6 +93,9 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels { } } + public event EventHandler ModelDataChanged; + public event EventHandler<(int originalLocation, int newLocation)> ModelDataMoved; + public PCSTool(IModel model) => this.model = model; } } diff --git a/src/Gen3Hex.Core/ViewModels/ViewPort.cs b/src/Gen3Hex.Core/ViewModels/ViewPort.cs index a0bcaf77..3e8381d2 100644 --- a/src/Gen3Hex.Core/ViewModels/ViewPort.cs +++ b/src/Gen3Hex.Core/ViewModels/ViewPort.cs @@ -226,6 +226,8 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels { Model = model ?? new BasicModel(file.Contents); FileName = file.Name; Tools = new ToolTray(Model); + Tools.StringTool.ModelDataChanged += ModelChangedByTool; + Tools.StringTool.ModelDataMoved += ModelDataMovedByTool; scroll = new ScrollRegion { DataLength = Model.Count }; scroll.PropertyChanged += ScrollPropertyChanged; @@ -748,7 +750,7 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels { // last character edit: might require relocation var newRun = Model.RelocateForExpansion(run, run.Length + extraBytesNeeded); if (newRun != run) { - var offset = memoryLocation - scroll.ViewPointToDataIndex(new Point(0, 0)); + var offset = memoryLocation - scroll.DataIndex; selection.GotoAddress(newRun.Start + pcs.Position - offset); memoryLocation += newRun.Start - run.Start; run = newRun; @@ -805,6 +807,21 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels { selection.PropertyChanged += SelectionPropertyChanged; } + private void ModelChangedByTool(object sender, IFormattedRun run) { + if (run.Start < scroll.ViewPointToDataIndex(new Point(Width - 1, Height - 1)) || run.Start + run.Length > scroll.DataIndex) { + // there's some visible data that changed + RefreshBackingData(); + } + } + + private void ModelDataMovedByTool(object sender, (int originalLocation, int newLocation) locations) { + if (scroll.DataIndex <= locations.originalLocation && locations.originalLocation < scroll.ViewPointToDataIndex(new Point(Width - 1, Height - 1))) { + // data was moved from onscreen: follow it + int offset = locations.originalLocation - scroll.DataIndex; + selection.GotoAddress(locations.newLocation - offset); + } + } + private void RefreshBackingData() { currentView = new HexElement[Width, Height]; IFormattedRun run = null; diff --git a/src/Gen3Hex.Tests/ToolTests.cs b/src/Gen3Hex.Tests/ToolTests.cs index d553c0f6..25e66e18 100644 --- a/src/Gen3Hex.Tests/ToolTests.cs +++ b/src/Gen3Hex.Tests/ToolTests.cs @@ -1,6 +1,8 @@ using HavenSoft.Gen3Hex.Core.Models; using HavenSoft.Gen3Hex.Core.ViewModels; +using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Xunit; @@ -8,12 +10,12 @@ namespace HavenSoft.Gen3Hex.Tests { public class ToolTests { [Fact] public void ViewPortHasTools() { - var viewPort = new ViewPort(new Core.Models.LoadedFile("file.txt", new byte[100])); + var viewPort = new ViewPort(new LoadedFile("file.txt", new byte[100])); Assert.True(viewPort.HasTools); } [Fact] - public void StringToolCanMoveData() { + public void StringToolCanOpenOnChosenData() { var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); var model = new PointerAndStringModel(buffer); var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 }; @@ -26,6 +28,57 @@ namespace HavenSoft.Gen3Hex.Tests { Assert.IsType(viewPort.Tools[viewPort.Tools.SelectedIndex]); } - // TODO tool changes should be immediately reflected in the ViewPort + [Fact] + public void StringToolEditsAreReflectedInViewPort() { + var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); + var model = new PointerAndStringModel(buffer); + var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 }; + viewPort.Edit("^bob\"\" \"Some Text\" 00 <000100>"); + viewPort.Tools.StringTool.Address = 0; + + viewPort.Tools.StringTool.Content = "Some Test"; // Text -> Test + var pcs = (PCS)viewPort[7, 0].Format; + Assert.Equal("s", pcs.ThisCharacter); + } + + [Fact] + public void StringToolCanMoveData() { + var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); + var model = new PointerAndStringModel(buffer); + var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 }; + viewPort.Edit("^bob\"\" \"Some Text\" 00 <000100>"); + var toolProperties = new List(); + viewPort.Tools.StringTool.PropertyChanged += (sender, e) => toolProperties.Add(e.PropertyName); + viewPort.Tools.StringTool.Address = 0; + + toolProperties.Clear(); + viewPort.Tools.StringTool.Content = "Some More Text"; + Assert.Contains("Address", toolProperties); + } + + [Fact] + public void ViewPortMovesWhenStringToolMovesData() { + var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); + var model = new PointerAndStringModel(buffer); + var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 }; + viewPort.Edit("^bob\"\" \"Some Text\" 00 <000100>"); + viewPort.Tools.StringTool.Address = 0; + + viewPort.Tools.StringTool.Content = "Some More Text"; + Assert.NotEqual(0, int.Parse(viewPort.Headers[0], NumberStyles.HexNumber)); + } + + [Fact] + public void StringToolMultiCharacterDeleteCleansUpUnusedBytes() { + var buffer = Enumerable.Repeat((byte)0xFF, 0x200).ToArray(); + var model = new PointerAndStringModel(buffer); + var viewPort = new ViewPort(new LoadedFile("test.txt", buffer), model) { Width = 0x10, Height = 0x10 }; + viewPort.Edit("^bob\"\" \"Some Text\" 00 <000100>"); + viewPort.Tools.StringTool.Address = 0; + + viewPort.Tools.StringTool.Content = "Some "; // removed 'Text' from the end + + Assert.Equal(0xFF, model[7]); + } } } diff --git a/src/Gen3Hex.WPF/Windows/MainWindow.xaml b/src/Gen3Hex.WPF/Windows/MainWindow.xaml index 3f77d77f..4f6442d6 100644 --- a/src/Gen3Hex.WPF/Windows/MainWindow.xaml +++ b/src/Gen3Hex.WPF/Windows/MainWindow.xaml @@ -209,7 +209,7 @@ - +