From ecb810aa2e43fd7fc52b3fb89368de55c05f19f1 Mon Sep 17 00:00:00 2001 From: Benjamin Popp Date: Tue, 19 Mar 2019 21:17:58 -0500 Subject: [PATCH] adding column headers --- src/HexManiac.Core/Models/Runs/ArrayRun.cs | 64 +++++++++++++++++++ src/HexManiac.Core/ViewModels/IViewPort.cs | 2 + .../ViewModels/SearchResultsViewPort.cs | 3 + src/HexManiac.Core/ViewModels/ViewPort.cs | 18 ++++++ .../Controls/MultiplyConverter.cs | 16 +++++ src/HexManiac.WPF/HexManiac.WPF.csproj | 1 + src/HexManiac.WPF/Windows/MainWindow.xaml | 36 ++++++++++- 7 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 src/HexManiac.WPF/Controls/MultiplyConverter.cs diff --git a/src/HexManiac.Core/Models/Runs/ArrayRun.cs b/src/HexManiac.Core/Models/Runs/ArrayRun.cs index 2e2e0ec7..79ecf397 100644 --- a/src/HexManiac.Core/Models/Runs/ArrayRun.cs +++ b/src/HexManiac.Core/Models/Runs/ArrayRun.cs @@ -156,6 +156,53 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { } } + public class ColumnHeader { + public string ColumnTitle { get; } + public int ByteWidth { get; } + public ColumnHeader(string title, int byteWidth = 1) => (ColumnTitle, ByteWidth) = (title, byteWidth); + } + + /// + /// Represents a horizontal row of labels. + /// Each entry is meant to label a column. + /// + public class HeaderRow { + public IReadOnlyList ColumnHeaders { get; } + + public HeaderRow(ArrayRun source, int byteStart, int length) { + var headers = new List(); + // we know which 'byte' to start at, but we want to know what 'index' to start at + // basically, count off each element to figure out how big it is + int currentByte = 0; + int startIndex = 0; + while (currentByte < byteStart) { + currentByte += source.ElementContent[startIndex].Length; + startIndex++; + } + currentByte = 0; + for (int i = 0; currentByte < length; i++) { + var segment = source.ElementContent[(startIndex + i) % source.ElementContent.Count]; + headers.Add(new ColumnHeader(segment.Name, segment.Length)); + currentByte += segment.Length; + } + ColumnHeaders = headers; + } + + public HeaderRow(int start, int length) { + var hex = "0123456789ABCDEF"; + var headers = new ColumnHeader[length]; + for (int i = 0; i < length; i++) headers[i] = new ColumnHeader(hex[(start + i) % 0x10].ToString()); + ColumnHeaders = headers; + } + + public static IReadOnlyList GetDefaultColumnHeaders(int columnCount) { + if (columnCount > 0x10 && columnCount % 0x10 != 0) return new List(); + if (columnCount < 0x10 && 0x10 % columnCount != 0) return new List(); + if (columnCount >= 0x10) return new[] { new HeaderRow(0, columnCount) }; + return Enumerable.Range(0, 0x10 / columnCount).Select(i => new HeaderRow(columnCount * i, columnCount)).ToList(); + } + } + public class ArrayRun : BaseRun { public const char ExtendArray = '+'; public const char ArrayStart = '['; @@ -386,6 +433,23 @@ namespace HavenSoft.HexManiac.Core.Models.Runs { return new ArrayRun(owner, FormatString, Start, ElementCount, ElementContent, PointerSources, pointerSourcesForInnerElements); } + /// + /// Arrays might want custom column headers. + /// If so, this method can get them. + /// + public IReadOnlyList GetColumnHeaders(int columnCount) { + // check if it's a multiple of the array width + if (columnCount >= ElementLength) { + if (columnCount % ElementLength != 0) return null; + return new[] { new HeaderRow(this, 0, columnCount) }; + } + + // check if it's a divisor of the array width + if (ElementLength % columnCount != 0) return null; + var segments = ElementLength / columnCount; + return Enumerable.Range(0, segments).Select(i => new HeaderRow(this, columnCount * i, columnCount)).ToList(); + } + public void AppendTo(IDataModel data, StringBuilder text) { for (int i = 0; i < ElementCount; i++) { var offset = Start + i * ElementLength; diff --git a/src/HexManiac.Core/ViewModels/IViewPort.cs b/src/HexManiac.Core/ViewModels/IViewPort.cs index f2902e80..1a5ce3c1 100644 --- a/src/HexManiac.Core/ViewModels/IViewPort.cs +++ b/src/HexManiac.Core/ViewModels/IViewPort.cs @@ -1,4 +1,5 @@ using HavenSoft.HexManiac.Core.Models; +using HavenSoft.HexManiac.Core.Models.Runs; using HavenSoft.HexManiac.Core.ViewModels.Tools; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -16,6 +17,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels { int ScrollValue { get; set; } int MaximumScroll { get; } ObservableCollection Headers { get; } + ObservableCollection ColumnHeaders { get; } int DataOffset { get; } ICommand Scroll { get; } // parameter: Direction to scroll diff --git a/src/HexManiac.Core/ViewModels/SearchResultsViewPort.cs b/src/HexManiac.Core/ViewModels/SearchResultsViewPort.cs index 796b3747..f479868a 100644 --- a/src/HexManiac.Core/ViewModels/SearchResultsViewPort.cs +++ b/src/HexManiac.Core/ViewModels/SearchResultsViewPort.cs @@ -1,4 +1,5 @@ using HavenSoft.HexManiac.Core.Models; +using HavenSoft.HexManiac.Core.Models.Runs; using HavenSoft.HexManiac.Core.ViewModels.Tools; using System; using System.Collections.Generic; @@ -78,6 +79,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels { public bool AnchorTextVisible => false; + public ObservableCollection ColumnHeaders { get; } + #pragma warning disable 0067 // it's ok if events are never used public event EventHandler OnError; public event EventHandler OnMessage; diff --git a/src/HexManiac.Core/ViewModels/ViewPort.cs b/src/HexManiac.Core/ViewModels/ViewPort.cs index 5bdfe41d..5f6ba5d3 100644 --- a/src/HexManiac.Core/ViewModels/ViewPort.cs +++ b/src/HexManiac.Core/ViewModels/ViewPort.cs @@ -70,6 +70,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels { public int MaximumScroll => scroll.MaximumScroll; public ObservableCollection Headers => scroll.Headers; + public ObservableCollection ColumnHeaders { get; } public int DataOffset => scroll.DataIndex; public ICommand Scroll => scroll.Scroll; @@ -290,6 +291,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels { Model = model; FileName = fileName; + ColumnHeaders = new ObservableCollection(); scroll = new ScrollRegion(model.TryGetUsefulHeader) { DataLength = Model.Count }; scroll.PropertyChanged += ScrollPropertyChanged; @@ -1225,9 +1227,25 @@ namespace HavenSoft.HexManiac.Core.ViewModels { } } + if (ColumnHeaders.Count == 0 || ColumnHeaders[0].ColumnHeaders.Count != Width) UpdateColumnHeaders(); + NotifyCollectionChanged(ResetArgs); } + private void UpdateColumnHeaders() { + var index = scroll.ViewPointToDataIndex(new Point(0, 0)); + var run = Model.GetNextRun(index) as ArrayRun; + if (run != null && run.Start > index) run = null; // only use the run if it starts _before_ the screen + var headers = run?.GetColumnHeaders(Width) ?? HeaderRow.GetDefaultColumnHeaders(Width); + + for (int i = 0; i < headers.Count; i++) { + if (i < ColumnHeaders.Count) ColumnHeaders[i] = headers[i]; + else ColumnHeaders.Add(headers[i]); + } + + while (ColumnHeaders.Count > headers.Count) ColumnHeaders.RemoveAt(ColumnHeaders.Count - 1); + } + private IDataFormat WrapFormat(IFormattedRun run, IDataFormat format, int dataIndex) { if (run.PointerSources != null && run.Start == dataIndex) { var name = Model.GetAnchorFromAddress(-1, run.Start); diff --git a/src/HexManiac.WPF/Controls/MultiplyConverter.cs b/src/HexManiac.WPF/Controls/MultiplyConverter.cs new file mode 100644 index 00000000..0381bc08 --- /dev/null +++ b/src/HexManiac.WPF/Controls/MultiplyConverter.cs @@ -0,0 +1,16 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace HavenSoft.HexManiac.WPF.Controls { + public class MultiplyConverter : IValueConverter { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + if (value is int number && parameter is double param) return number * param; + return 0.0; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { + throw new NotImplementedException(); + } + } +} diff --git a/src/HexManiac.WPF/HexManiac.WPF.csproj b/src/HexManiac.WPF/HexManiac.WPF.csproj index 9fbc118f..4d941d16 100644 --- a/src/HexManiac.WPF/HexManiac.WPF.csproj +++ b/src/HexManiac.WPF/HexManiac.WPF.csproj @@ -72,6 +72,7 @@ + StartScreen.xaml diff --git a/src/HexManiac.WPF/Windows/MainWindow.xaml b/src/HexManiac.WPF/Windows/MainWindow.xaml index 3cd7f9b1..6b41e097 100644 --- a/src/HexManiac.WPF/Windows/MainWindow.xaml +++ b/src/HexManiac.WPF/Windows/MainWindow.xaml @@ -13,6 +13,7 @@ + @@ -257,7 +258,7 @@ - + @@ -277,7 +278,38 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +