using Microsoft.Win32; using Qrakhen.Qamp.Editor.Commands; using Qrakhen.Qamp.Editor.Primitives; using Qrakhen.Qamp.Memory; using System; using System.Collections.ObjectModel; using System.IO; using System.Text; using System.Windows.Documents; using System.Windows.Input; namespace Qrakhen.Qamp.Editor.ViewModel; public class SelectableLineBuffer(LineBuffer buffer) { public LineBuffer Buffer { get; private set; } = buffer; public BufferRegion Selection { get; private set; } = new(); } public class EditorFrameViewModel : ObservableObject { private Encoding _encoding; public Encoding Encoding { get => _encoding; set => SetProperty(ref _encoding, value); } private FileInfo _fileInfo; public FileInfo FileInfo { get => _fileInfo; set => SetProperty(ref _fileInfo, value); } private BufferPosition _cursorPosition; public BufferPosition CursorPosition { get => _cursorPosition; set => SetProperty(ref _cursorPosition, value); } private BufferRegion _currentSelection; public BufferRegion CurrentSelection { get => _currentSelection; set => SetProperty(ref _currentSelection, value); } public int CursorLine => _cursorPosition.Line; public int CursorColumn => _cursorPosition.Column; public SelectableLineBuffer CurrentLine => Lines[CursorLine]; public ICommand MoveCaretCommand { get; } public ICommand MoveBlockCommand { get; } public ICommand InsertCommand { get; } public ICommand DeleteCommand { get; } public ICommand ClipBoardCommand { get; } public ObservableCollection Lines { get; private set; } = new() { new SelectableLineBuffer(new LineBuffer("test {")), new SelectableLineBuffer(new LineBuffer(" is very good;")), new SelectableLineBuffer(new LineBuffer(" jaja();")), new SelectableLineBuffer(new LineBuffer("}")) }; public EditorFrameViewModel() { Encoding = Encoding.ASCII; MoveCaretCommand = new RelayCommand(ExecuteMoveCaret); MoveBlockCommand = new RelayCommand(ExecuteMoveBlock); InsertCommand = new RelayCommand(ExecuteInsert); DeleteCommand = new RelayCommand(ExecuteDelete); ClipBoardCommand = new RelayCommand(() => { }); } public void SetCursor(int line, int column) => SetCursor(new BufferPosition(line, column)); public void SetCursor(BufferPosition position) { CursorPosition = AlignBufferPosition(position); OnPropertyChanged(nameof(CurrentLine)); } public void MoveCursor(int lines, int columns) => MoveCursor(new BufferPosition(lines, columns)); public void MoveCursor(BufferPosition relative, bool jump = false) { SetCursor(CursorPosition + relative); OnPropertyChanged(nameof(CurrentLine)); } public void Delete(BufferRegion region, bool follow = true) { if (follow) SetCursor(new BufferPosition(region.From.Line, region.From.Column)); for (int line = region.From.Line; line < region.To.Line; line++) { } } public void Delete(int count = 1, bool follow = true) { if (count < 1) return; if (follow) { // remove newline if (CursorColumn - count < 0 && CursorLine > 0) { int position = Lines[CursorLine - 1].Buffer.Tail; Lines[CursorLine - 1].Buffer.Insert( position, CurrentLine.Buffer.Slice(0)); Lines.RemoveAt(CursorLine); SetCursor(CursorLine - 1, position); } else { MoveCursor(0, -count); CurrentLine.Buffer.Delete(CursorColumn, count); } } else { if (CursorColumn + count > CurrentLine.Buffer.Tail && CursorLine < Lines.Count - 1) { CurrentLine.Buffer.Insert( CurrentLine.Buffer.Tail, Lines[CursorLine + 1].Buffer.Slice(0)); Lines.RemoveAt(CursorLine + 1); } CurrentLine.Buffer.Delete(CursorColumn, count); } } public void Select(BufferPosition relativePosition, bool jump = false) { if (CurrentSelection.IsVoid) { CurrentSelection = new BufferRegion(CursorPosition, CursorPosition); } CurrentSelection += relativePosition; } public void Deselect() { CurrentSelection = BufferRegion.Void; } public void Insert(string data, bool follow = true) { data = data.Replace("\t", " "); var lines = data.Split(Environment.NewLine, StringSplitOptions.None); for (int i = 0; i < lines.Length; i++) { if (i > 0) { LineBuffer split = new LineBuffer( CurrentLine.Buffer.Slice(CursorColumn), CurrentLine.Buffer.Encoding); Lines.Insert(CursorLine + 1, new SelectableLineBuffer(split)); SetCursor(new BufferPosition(CursorLine + 1, 0)); } CurrentLine.Buffer.Insert(CursorColumn, lines[i]); if (follow) MoveCursor(new BufferPosition(0, lines[i].Length)); } } private BufferPosition AlignBufferPosition(BufferPosition position) { int line = Math.Max(0, Math.Min(position.Line, Lines.Count - 1)); LineBuffer buffer = Lines[line].Buffer; int column = Math.Max(0, Math.Min(position.Column, buffer.Tail)); return new BufferPosition(line, column); } #region Commands private void ExecuteMoveCaret(object? parameter) { if (parameter is MoveCaretOptions options) { if (options.Relative) MoveCursor(options.Position, options.Jump); else SetCursor(options.Position); } } private void ExecuteMoveBlock(object? parameter) { if (parameter is BufferPosition relative) MoveCursor(relative); } private void ExecuteInsert(object? parameter) { if (parameter is string str) Insert(str, true); if (parameter is char c) Insert(c + "", true); } private void ExecuteDelete(object? parameter) { if (parameter is int count) Delete(Math.Abs(count), count < 0); if (parameter is BufferRegion region) Delete(region); } #endregion public string Serialize(string? lineTerminator = null) { lineTerminator ??= Environment.NewLine; string result = ""; foreach (var line in Lines) { result += $"{line.Buffer.ToString()}{lineTerminator}"; } return result; } public void Deserialize(string source, Encoding? encoding = null, string? lineTerminator = null) { encoding ??= Encoding.ASCII; lineTerminator ??= Environment.NewLine; Lines.Clear(); string[] lines = source.Split(lineTerminator); foreach (var line in lines) { Lines.Add(new SelectableLineBuffer(new LineBuffer(line, encoding))); } } }