using HavenSoft.HexManiac.Core.Models; using HavenSoft.HexManiac.Core.Models.Runs; using HavenSoft.HexManiac.Core.ViewModels.Tools; using HavenSoft.HexManiac.Core.ViewModels.Visitors; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Windows.Input; namespace HavenSoft.HexManiac.Core.ViewModels { /// /// exactly replaces a ViewPort, except that it also has a known parent view /// public class ChildViewPort : ViewPort, IChildViewPort { public IViewPort Parent { get; } public ChildViewPort(IViewPort viewPort, IWorkDispatcher dispatcher, Singletons singletons) : base(viewPort.FileName, viewPort.Model, dispatcher, singletons, null, null, viewPort.ChangeHistory) { Parent = viewPort; Width = Parent.Width; } } public class CompositeChildViewPort : List, IChildViewPort { public static bool TryCombine(IChildViewPort a, IChildViewPort b, out CompositeChildViewPort result) { result = null; if (b is CompositeChildViewPort) return false; result = a is CompositeChildViewPort composite ? composite : new CompositeChildViewPort { a }; if (a.Parent != b.Parent) return false; if (a.Width != b.Width) return false; if (b.DataOffset < a.DataOffset) return false; if ((b.DataOffset - a.DataOffset) % b.Width != 0) return false; var bSelectionLine = (GetFirstVisibleSelectedAddress(b) - a.DataOffset) / b.Width; var aSelectionLine = (GetLastVisibleSelectedAddress(a) - a.DataOffset) / b.Width; var lineDif = bSelectionLine - aSelectionLine; if (lineDif > 3) return false; a.Height += lineDif; b.ScrollValue -= (b.DataOffset - a.DataOffset) / b.Width; b.Height = a.Height; Debug.Assert(a.DataOffset == b.DataOffset, "Data Offsets should be the same at this point!"); result.Add(b); return true; } public static int GetFirstVisibleSelectedAddress(IViewPort viewPort) { for (int y = 0; y < viewPort.Height; y++) { for (int x = 0; x < viewPort.Width; x++) { if (!viewPort.IsSelected(new Point(x, y))) continue; return viewPort.DataOffset + y * viewPort.Width + x; } } return -1; } public static int GetLastVisibleSelectedAddress(IViewPort viewPort) { for (int y = viewPort.Height - 1; y >= 0; y--) { for (int x = viewPort.Width - 1; x >= 0; x--) { if (!viewPort.IsSelected(new Point(x, y))) continue; return viewPort.DataOffset + y * viewPort.Width + x; } } return -1; } public HexElement this[int x, int y] => this[0][x, y]; public IViewPort Parent => this[0].Parent; public double ToolPanelWidth { get; set; } = 500; public string FileName => this[0].FileName; public string FullFileName => this[0].FullFileName; public int PreferredWidth { get => this[0].PreferredWidth; set => ForEach(child => child.PreferredWidth = value); } public int Width { get => this[0].Width; set => ForEach(child => child.Width = value); } public int Height { get => this[0].Height; set => ForEach(child => child.Height = value); } public bool AutoAdjustDataWidth { get => this[0].AutoAdjustDataWidth; set => ForEach(child => child.AutoAdjustDataWidth = value); } public bool StretchData { get => this[0].StretchData; set => ForEach(child => child.StretchData = value); } public bool AllowMultipleElementsPerLine { get => this[0].AllowMultipleElementsPerLine; set => ForEach(child => child.AllowMultipleElementsPerLine = value); } public bool UseCustomHeaders { get => this[0].UseCustomHeaders; set => ForEach(child => child.UseCustomHeaders = value); } public bool Base10Length { get => this[0].Base10Length; set => ForEach(child => child.Base10Length = value); } public int MinimumScroll => this[0].MinimumScroll; public int ScrollValue { get => this[0].ScrollValue; set => ForEach(child => child.ScrollValue = value); } public int MaximumScroll => this[0].MaximumScroll; public ObservableCollection Headers => this[0].Headers; public ObservableCollection ColumnHeaders => this[0].ColumnHeaders; public int DataOffset => this[0].DataOffset; public ICommand Scroll => this[0].Scroll; public double Progress => this[0].Progress; public bool UpdateInProgress => this[0].UpdateInProgress; public string SelectedAddress { get => this[0].SelectedAddress; set => this[0].SelectedAddress = value; } public string SelectedBytes => this[0].SelectedBytes; public string AnchorText => this[0].AnchorText; public bool AnchorTextVisible => this[0].AnchorTextVisible; public IDataModel Model => this[0].Model; public bool HasTools => this[0].HasTools; public ChangeHistory ChangeHistory => this[0].ChangeHistory; public IToolTrayViewModel Tools => this[0].Tools; public string Name => this[0].Name; public bool IsMetadataOnlyChange => false; public byte[] FindBytes { get; set; } public ICommand Save => this[0].Save; public ICommand SaveAs => this[0].SaveAs; public ICommand ExportBackup => this[0].ExportBackup; public ICommand Undo => this[0].Undo; public ICommand Redo => this[0].Redo; public ICommand Copy => this[0].Copy; public ICommand DeepCopy => this[0].DeepCopy; public ICommand Diff => this[0].Diff; public ICommand DiffLeft => this[0].DiffLeft; public ICommand DiffRight => this[0].DiffRight; public ICommand SelectAll => this[0].SelectAll; public ICommand Goto => this[0].Goto; public ICommand ResetAlignment => this[0].ResetAlignment; public ICommand Back => this[0].Back; public ICommand Forward => this[0].Forward; public ICommand Close => this[0].Close; ICommand ITabContent.Clear => this[0].Clear; public bool CanDuplicate => false; public void Duplicate() { } public event EventHandler PreviewScrollChanged { add => ForEach(child => child.PreviewScrollChanged += value); remove => ForEach(child => child.PreviewScrollChanged -= value); } public event EventHandler OnError { add => ForEach(child => child.OnError += value); remove => ForEach(child => child.OnError -= value); } public event EventHandler OnMessage { add => ForEach(child => child.OnMessage += value); remove => ForEach(child => child.OnMessage -= value); } public event EventHandler ClearMessage { add => ForEach(child => child.ClearMessage += value); remove => ForEach(child => child.ClearMessage -= value); } public event EventHandler Closed { add => ForEach(child => child.Closed += value); remove => ForEach(child => child.Closed -= value); } public event EventHandler RequestTabChange { add => ForEach(child => child.RequestTabChange += value); remove => ForEach(child => child.RequestTabChange -= value); } public event EventHandler RequestCloseOtherViewports { add => ForEach(child => child.RequestCloseOtherViewports += value); remove => ForEach(child => child.RequestCloseOtherViewports -= value); } public event EventHandler RequestDelayedWork { add => ForEach(child => child.RequestDelayedWork += value); remove => ForEach(child => child.RequestDelayedWork -= value); } public event EventHandler RequestMenuClose { add => ForEach(child => child.RequestMenuClose += value); remove => ForEach(child => child.RequestMenuClose -= value); } public event EventHandler RequestDiff { add => ForEach(child => child.RequestDiff += value); remove => ForEach(child => child.RequestDiff -= value); } public event EventHandler RequestCanDiff { add => ForEach(child => child.RequestCanDiff += value); remove => ForEach(child => child.RequestCanDiff -= value); } event EventHandler ITabContent.RequestCanCreatePatch { add { } remove { } } event EventHandler ITabContent.RequestCreatePatch { add { } remove { } } public event PropertyChangedEventHandler PropertyChanged { add => ForEach(child => child.PropertyChanged += value); remove => ForEach(child => child.PropertyChanged -= value); } public event NotifyCollectionChangedEventHandler CollectionChanged { add => ForEach(child => child.CollectionChanged += value); remove => ForEach(child => child.CollectionChanged -= value); } public void ConsiderReload(IFileSystem fileSystem) => throw new NotImplementedException(); public IChildViewPort CreateChildView(int startAddress, int endAddress) { throw new NotImplementedException(); } public void ExpandSelection(int x, int y) => ForEach(child => child.ExpandSelection(x, y)); public IReadOnlyList<(int start, int end)> Find(string search, bool matchExactCase = false) => new (int, int)[0]; public bool CanFindFreeSpace => false; public void FindFreeSpace(IFileSystem fs) { } public void FindAllSources(int x, int y) { } public void FollowLink(int x, int y) { } public IReadOnlyList GetContextMenuItems(Point point, IFileSystem fileSystem) => new IContextItem[0]; public bool IsSelected(Point point) => this.Any(child => child.IsSelected(point)); public bool IsTable(Point point) => this[0].IsTable(point); public void Refresh() => ForEach(child => child.Refresh()); public bool TryImport(LoadedFile file, IFileSystem fileSystem) => false; } }