From b54be19e9e1f2d8199a3e1eb230dad362f84babf Mon Sep 17 00:00:00 2001 From: adelikat Date: Sat, 26 Oct 2019 16:49:52 -0500 Subject: [PATCH] InputRoll - reorg - put in its own folder, break out column and cell classes into separate files --- .../BizHawk.Client.EmuHawk.csproj | 8 +- .../CustomControls/InputRoll/Cell.cs | 71 + .../CustomControls/InputRoll/ColumnType.cs | 10 + .../{ => InputRoll}/InputRoll.Drawing.cs | 1220 ++--- .../{ => InputRoll}/InputRoll.cs | 4382 ++++++++--------- .../CustomControls/InputRoll/RollColumn.cs | 29 + .../CustomControls/InputRoll/RollColumns.cs | 152 + BizHawk.Client.EmuHawk/tools/CDL.cs | 28 +- BizHawk.Client.EmuHawk/tools/Cheats/Cheats.cs | 26 +- .../Debugger/GenericDebugger.Disassembler.cs | 4 +- .../tools/Debugger/GenericDebugger.cs | 8 +- .../Lua/Libraries/EmuLuaLibrary.Tastudio.cs | 2 +- .../tools/Lua/LuaConsole.cs | 16 +- .../tools/TAStudio/BookmarksBranchesBox.cs | 10 +- .../tools/TAStudio/MarkerControl.cs | 8 +- .../tools/TAStudio/TAStudio.Callbacks.cs | 6 +- .../tools/TAStudio/TAStudio.ListView.cs | 10 +- .../tools/TAStudio/TAStudio.MenuItems.cs | 2 +- .../tools/TAStudio/TAStudio.cs | 10 +- .../tools/TAStudio/UndoHistoryForm.cs | 8 +- BizHawk.Client.EmuHawk/tools/TraceLogger.cs | 12 +- .../tools/Watch/RamSearch.cs | 18 +- .../tools/Watch/RamWatch.cs | 26 +- 23 files changed, 3043 insertions(+), 3023 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/CustomControls/InputRoll/Cell.cs create mode 100644 BizHawk.Client.EmuHawk/CustomControls/InputRoll/ColumnType.cs rename BizHawk.Client.EmuHawk/CustomControls/{ => InputRoll}/InputRoll.Drawing.cs (96%) rename BizHawk.Client.EmuHawk/CustomControls/{ => InputRoll}/InputRoll.cs (87%) create mode 100644 BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumn.cs create mode 100644 BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumns.cs diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 70a0bf936e..2251812747 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -587,13 +587,17 @@ Form - + + + Component - + InputRoll.cs Component + + Component diff --git a/BizHawk.Client.EmuHawk/CustomControls/InputRoll/Cell.cs b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/Cell.cs new file mode 100644 index 0000000000..3273aeef46 --- /dev/null +++ b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/Cell.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; + +namespace BizHawk.Client.EmuHawk +{ + /// + /// Represents a single cell of the + /// + public class Cell + { + public RollColumn Column { get; internal set; } + public int? RowIndex { get; internal set; } + public string CurrentText { get; internal set; } + + public Cell() { } + + public Cell(Cell cell) + { + Column = cell.Column; + RowIndex = cell.RowIndex; + } + + public bool IsDataCell => Column != null && RowIndex.HasValue; + + public override bool Equals(object obj) + { + if (obj is Cell) + { + var cell = obj as Cell; + return this.Column == cell.Column && this.RowIndex == cell.RowIndex; + } + + return base.Equals(obj); + } + + public override int GetHashCode() + { + return Column.GetHashCode() + RowIndex.GetHashCode(); + } + } + + internal class SortCell : IComparer + { + int IComparer.Compare(Cell a, Cell b) + { + Cell c1 = a as Cell; + Cell c2 = b as Cell; + if (c1.RowIndex.HasValue) + { + if (c2.RowIndex.HasValue) + { + int row = c1.RowIndex.Value.CompareTo(c2.RowIndex.Value); + if (row == 0) + { + return c1.Column.Name.CompareTo(c2.Column.Name); + } + + return row; + } + + return 1; + } + + if (c2.RowIndex.HasValue) + { + return -1; + } + + return c1.Column.Name.CompareTo(c2.Column.Name); + } + } +} diff --git a/BizHawk.Client.EmuHawk/CustomControls/InputRoll/ColumnType.cs b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/ColumnType.cs new file mode 100644 index 0000000000..b0fb0ce845 --- /dev/null +++ b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/ColumnType.cs @@ -0,0 +1,10 @@ +namespace BizHawk.Client.EmuHawk +{ + /// + /// Specifies the type of column of a + /// + public enum ColumnType + { + Boolean, Float, Text, Image + } +} diff --git a/BizHawk.Client.EmuHawk/CustomControls/InputRoll.Drawing.cs b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.Drawing.cs similarity index 96% rename from BizHawk.Client.EmuHawk/CustomControls/InputRoll.Drawing.cs rename to BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.Drawing.cs index a5fa6e87b7..ebe9209f53 100644 --- a/BizHawk.Client.EmuHawk/CustomControls/InputRoll.Drawing.cs +++ b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.Drawing.cs @@ -1,610 +1,610 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Windows.Forms; -using BizHawk.Client.EmuHawk.WinFormExtensions; - -namespace BizHawk.Client.EmuHawk -{ - public partial class InputRoll - { - protected override void OnPaint(PaintEventArgs e) - { - using (_renderer.LockGraphics(e.Graphics, Width, Height)) - { - // White Background - _renderer.SetBrush(Color.White); - _renderer.SetSolidPen(Color.White); - _renderer.FillRectangle(0, 0, Width, Height); - - // Lag frame calculations - SetLagFramesArray(); - - var visibleColumns = _columns.VisibleColumns.ToList(); - - if (visibleColumns.Any()) - { - DrawColumnBg(visibleColumns); - DrawColumnText(visibleColumns); - } - - // Background - DrawBg(visibleColumns); - - // Foreground - DrawData(visibleColumns); - - DrawColumnDrag(); - DrawCellDrag(); - } - } - - private void DrawString(string text, int? width, Point point) - { - if (string.IsNullOrWhiteSpace(text)) - { - return; - } - - if (width.HasValue) - { - var max = (width.Value - CellWidthPadding) / _charSize.Width; - if (text.Length >= max) - { - text = text.Substring(0, max); - } - } - - _renderer.DrawString(text, point); - } - - protected override void OnPaintBackground(PaintEventArgs e) - { - // Do nothing, and this should never be called - } - - private void DrawColumnDrag() - { - if (_columnDown != null && _columnDownMoved && _currentX.HasValue && _currentY.HasValue && IsHoveringOnColumnCell) - { - int x1 = _currentX.Value - (_columnDown.Width.Value / 2); - int y1 = _currentY.Value - (CellHeight / 2); - int x2 = x1 + _columnDown.Width.Value; - int y2 = y1 + CellHeight; - - _renderer.SetSolidPen(_backColor); - _renderer.DrawRectangle(x1, y1, x2, y2); - _renderer.PrepDrawString(_font, _foreColor); - _renderer.DrawString(_columnDown.Text, new Point(x1 + CellWidthPadding, y1 + CellHeightPadding)); - } - } - - private void DrawCellDrag() - { - if (_draggingCell != null) - { - var text = ""; - int offsetX = 0; - int offsetY = 0; - QueryItemText?.Invoke(_draggingCell.RowIndex.Value, _draggingCell.Column, out text, ref offsetX, ref offsetY); - - Color bgColor = _backColor; - QueryItemBkColor?.Invoke(_draggingCell.RowIndex.Value, _draggingCell.Column, ref bgColor); - - int x1 = _currentX.Value - (_draggingCell.Column.Width.Value / 2); - int y1 = _currentY.Value - (CellHeight / 2); - int x2 = x1 + _draggingCell.Column.Width.Value; - int y2 = y1 + CellHeight; - - - _renderer.SetBrush(bgColor); - _renderer.FillRectangle(x1, y1, x2 - x1, y2 - y1); - _renderer.PrepDrawString(_font, _foreColor); - _renderer.DrawString(text, new Point(x1 + CellWidthPadding + offsetX, y1 + CellHeightPadding + offsetY)); - } - } - - private void DrawColumnText(List visibleColumns) - { - if (HorizontalOrientation) - { - int start = -_vBar.Value; - - _renderer.PrepDrawString(_font, _foreColor); - - foreach (var column in visibleColumns) - { - var point = new Point(CellWidthPadding, start + CellHeightPadding); - - if (IsHoveringOnColumnCell && column == CurrentCell.Column) - { - _renderer.PrepDrawString(_font, SystemColors.HighlightText); - DrawString(column.Text, column.Width, point); - _renderer.PrepDrawString(_font, _foreColor); - } - else - { - DrawString(column.Text, column.Width, point); - } - - start += CellHeight; - } - } - else - { - _renderer.PrepDrawString(_font, _foreColor); - - foreach (var column in visibleColumns) - { - var point = new Point(column.Left.Value + 2 * CellWidthPadding - _hBar.Value, CellHeightPadding); // TODO: fix this CellPadding issue (2 * CellPadding vs just CellPadding) - - if (IsHoveringOnColumnCell && column == CurrentCell.Column) - { - _renderer.PrepDrawString(_font, SystemColors.HighlightText); - DrawString(column.Text, column.Width, point); - _renderer.PrepDrawString(_font, _foreColor); - } - else - { - DrawString(column.Text, column.Width, point); - } - } - } - } - - private void DrawData(List visibleColumns) - { - // Prevent exceptions with small TAStudio windows - if (visibleColumns.Count == 0) - { - return; - } - - if (QueryItemText != null) - { - if (HorizontalOrientation) - { - int startRow = FirstVisibleRow; - int range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1; - - _renderer.PrepDrawString(_font, _foreColor); - for (int i = 0, f = 0; f < range; i++, f++) - { - f += _lagFrames[i]; - int lastVisible = LastVisibleColumnIndex; - for (int j = FirstVisibleColumn; j <= lastVisible; j++) - { - Bitmap image = null; - int x; - int y; - int bitmapOffsetX = 0; - int bitmapOffsetY = 0; - - QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY); - - if (image != null) - { - x = RowsToPixels(i) + CellWidthPadding + bitmapOffsetX; - y = (j * CellHeight) + (CellHeightPadding * 2) + bitmapOffsetY; - _renderer.DrawBitmap(image, new Point(x, y)); - } - - string text; - int strOffsetX = 0; - int strOffsetY = 0; - QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY); - - // Center Text - x = RowsToPixels(i) + ((CellWidth - (text.Length * _charSize.Width)) / 2); - y = (j * CellHeight) + CellHeightPadding - _vBar.Value; - var point = new Point(x + strOffsetX, y + strOffsetY); - - if (visibleColumns[j].Name == "FrameColumn") // TODO: don't do this hack - { - _renderer.PrepDrawString(_font, _foreColor, rotate: true); - DrawString(text, ColumnWidth, new Point(point.X + _charSize.Height + CellWidthPadding, point.Y + CellHeightPadding)); - _renderer.PrepDrawString(_font, _foreColor, rotate: false); - } - else - { - DrawString(text, ColumnWidth, point); - } - } - } - } - else - { - int startRow = FirstVisibleRow; - int range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1; - - _renderer.PrepDrawString(_font, _foreColor); - int xPadding = CellWidthPadding + 1 - _hBar.Value; - for (int i = 0, f = 0; f < range; i++, f++) // Vertical - { - f += _lagFrames[i]; - int lastVisible = LastVisibleColumnIndex; - for (int j = FirstVisibleColumn; j <= lastVisible; j++) // Horizontal - { - RollColumn col = visibleColumns[j]; - - string text; - int strOffsetX = 0; - int strOffsetY = 0; - Point point = new Point(col.Left.Value + xPadding, RowsToPixels(i) + CellHeightPadding); - - Bitmap image = null; - int bitmapOffsetX = 0; - int bitmapOffsetY = 0; - - QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY); - - if (image != null) - { - _renderer.DrawBitmap(image, new Point(point.X + bitmapOffsetX, point.Y + bitmapOffsetY + CellHeightPadding)); - } - - QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY); - - bool rePrep = false; - if (_selectedItems.Contains(new Cell { Column = visibleColumns[j], RowIndex = f + startRow })) - { - _renderer.PrepDrawString(_font, SystemColors.HighlightText); - rePrep = true; - } - - DrawString(text, col.Width, new Point(point.X + strOffsetX, point.Y + strOffsetY)); - - if (rePrep) - { - _renderer.PrepDrawString(_font, _foreColor); - } - } - } - } - } - } - - private void DrawColumnBg(List visibleColumns) - { - _renderer.SetBrush(SystemColors.ControlLight); - _renderer.SetSolidPen(Color.Black); - - if (HorizontalOrientation) - { - _renderer.FillRectangle(0, 0, ColumnWidth + 1, DrawHeight + 1); - _renderer.Line(0, 0, 0, visibleColumns.Count * CellHeight + 1); - _renderer.Line(ColumnWidth, 0, ColumnWidth, visibleColumns.Count * CellHeight + 1); - - int start = -_vBar.Value; - foreach (var column in visibleColumns) - { - _renderer.Line(1, start, ColumnWidth, start); - start += CellHeight; - } - - if (visibleColumns.Any()) - { - _renderer.Line(1, start, ColumnWidth, start); - } - } - else - { - int bottomEdge = RowsToPixels(0); - - // Gray column box and black line underneath - _renderer.FillRectangle(0, 0, Width + 1, bottomEdge + 1); - _renderer.Line(0, 0, TotalColWidth.Value + 1, 0); - _renderer.Line(0, bottomEdge, TotalColWidth.Value + 1, bottomEdge); - - // Vertical black separators - foreach (var column in visibleColumns) - { - int pos = column.Left.Value - _hBar.Value; - _renderer.Line(pos, 0, pos, bottomEdge); - } - - // Draw right most line - if (visibleColumns.Any()) - { - int right = TotalColWidth.Value - _hBar.Value; - _renderer.Line(right, 0, right, bottomEdge); - } - } - - // Emphasis - foreach (var column in visibleColumns.Where(c => c.Emphasis)) - { - _renderer.SetBrush(SystemColors.ActiveBorder); - if (HorizontalOrientation) - { - _renderer.FillRectangle(1, visibleColumns.IndexOf(column) * CellHeight + 1, ColumnWidth - 1, ColumnHeight - 1); - } - else - { - _renderer.FillRectangle(column.Left.Value + 1 - _hBar.Value, 1, column.Width.Value - 1, ColumnHeight - 1); - } - } - - // If the user is hovering over a column - if (IsHoveringOnColumnCell) - { - if (HorizontalOrientation) - { - for (int i = 0; i < visibleColumns.Count; i++) - { - if (visibleColumns[i] != CurrentCell.Column) - { - continue; - } - - _renderer.SetBrush(CurrentCell.Column.Emphasis - ? SystemColors.Highlight.Add(0x00222222) - : SystemColors.Highlight); - - _renderer.FillRectangle(1, i * CellHeight + 1, ColumnWidth - 1, ColumnHeight - 1); - } - } - else - { - // TODO multiple selected columns - foreach (var column in visibleColumns) - { - if (column == CurrentCell.Column) - { - // Left of column is to the right of the viewable area or right of column is to the left of the viewable area - if (column.Left.Value - _hBar.Value > Width || column.Right.Value - _hBar.Value < 0) - { - continue; - } - - int left = column.Left.Value - _hBar.Value; - int width = column.Right.Value - _hBar.Value - left; - - _renderer.SetBrush(CurrentCell.Column.Emphasis - ? SystemColors.Highlight.Add(0x00550000) - : SystemColors.Highlight); - - _renderer.FillRectangle(left + 1, 1, width - 1, ColumnHeight - 1); - } - } - } - } - } - - // TODO refactor this and DoBackGroundCallback functions. - /// - /// Draw Gridlines and background colors using QueryItemBkColor. - /// - private void DrawBg(List visibleColumns) - { - if (UseCustomBackground && QueryItemBkColor != null) - { - DoBackGroundCallback(visibleColumns); - } - - if (GridLines) - { - _renderer.SetSolidPen(SystemColors.ControlLight); - if (HorizontalOrientation) - { - // Columns - for (int i = 1; i < VisibleRows + 1; i++) - { - int x = RowsToPixels(i); - _renderer.Line(x, 1, x, DrawHeight); - } - - // Rows - for (int i = 0; i < visibleColumns.Count + 1; i++) - { - _renderer.Line(RowsToPixels(0) + 1, i * CellHeight - _vBar.Value, DrawWidth, i * CellHeight - _vBar.Value); - } - } - else - { - // Columns - int y = ColumnHeight + 1; - int? totalColWidth = TotalColWidth; - foreach (var column in visibleColumns) - { - int x = column.Left.Value - _hBar.Value; - _renderer.Line(x, y, x, Height - 1); - } - - if (visibleColumns.Any()) - { - _renderer.Line(totalColWidth.Value - _hBar.Value, y, totalColWidth.Value - _hBar.Value, Height - 1); - } - - // Rows - for (int i = 1; i < VisibleRows + 1; i++) - { - _renderer.Line(0, RowsToPixels(i), Width + 1, RowsToPixels(i)); - } - } - } - - if (_selectedItems.Any()) - { - DoSelectionBG(visibleColumns); - } - } - - private void DoSelectionBG(List visibleColumns) - { - // SuuperW: This allows user to see other colors in selected frames. - Color rowColor = Color.White; - int lastVisibleRow = LastVisibleRow; - int lastRow = -1; - foreach (Cell cell in _selectedItems) - { - if (cell.RowIndex > lastVisibleRow || cell.RowIndex < FirstVisibleRow || !VisibleColumns.Contains(cell.Column)) - { - continue; - } - - Cell relativeCell = new Cell - { - RowIndex = cell.RowIndex - FirstVisibleRow, - Column = cell.Column, - }; - relativeCell.RowIndex -= CountLagFramesAbsolute(relativeCell.RowIndex.Value); - - if (QueryRowBkColor != null && lastRow != cell.RowIndex.Value) - { - QueryRowBkColor(cell.RowIndex.Value, ref rowColor); - lastRow = cell.RowIndex.Value; - } - - Color cellColor = rowColor; - QueryItemBkColor?.Invoke(cell.RowIndex.Value, cell.Column, ref cellColor); - - // Alpha layering for cell before selection - float alpha = (float)cellColor.A / 255; - if (cellColor.A != 255 && cellColor.A != 0) - { - cellColor = Color.FromArgb(rowColor.R - (int)((rowColor.R - cellColor.R) * alpha), - rowColor.G - (int)((rowColor.G - cellColor.G) * alpha), - rowColor.B - (int)((rowColor.B - cellColor.B) * alpha)); - } - - // Alpha layering for selection - alpha = 0.33f; - cellColor = Color.FromArgb(cellColor.R - (int)((cellColor.R - SystemColors.Highlight.R) * alpha), - cellColor.G - (int)((cellColor.G - SystemColors.Highlight.G) * alpha), - cellColor.B - (int)((cellColor.B - SystemColors.Highlight.B) * alpha)); - DrawCellBG(cellColor, relativeCell, visibleColumns); - } - } - - /// - /// Given a cell with rowindex inbetween 0 and VisibleRows, it draws the background color specified. Do not call with absolute rowindices. - /// - private void DrawCellBG(Color color, Cell cell, List visibleColumns) - { - int x, y, w, h; - - if (HorizontalOrientation) - { - x = RowsToPixels(cell.RowIndex.Value) + 1; - w = CellWidth - 1; - y = (CellHeight * visibleColumns.IndexOf(cell.Column)) + 1 - _vBar.Value; // We can't draw without row and column, so assume they exist and fail catastrophically if they don't - h = CellHeight - 1; - if (x < ColumnWidth) - { - return; - } - } - else - { - w = cell.Column.Width.Value - 1; - x = cell.Column.Left.Value - _hBar.Value + 1; - y = RowsToPixels(cell.RowIndex.Value) + 1; // We can't draw without row and column, so assume they exist and fail catastrophically if they don't - h = CellHeight - 1; - if (y < ColumnHeight) - { - return; - } - } - - if (x > DrawWidth || y > DrawHeight) - { - return; - } // Don't draw if off screen. - - _renderer.SetBrush(color); - _renderer.FillRectangle(x, y, w, h); - } - - /// - /// Calls QueryItemBkColor callback for all visible cells and fills in the background of those cells. - /// - private void DoBackGroundCallback(List visibleColumns) - { - int startIndex = FirstVisibleRow; - int range = Math.Min(LastVisibleRow, RowCount - 1) - startIndex + 1; - int lastVisible = LastVisibleColumnIndex; - int firstVisibleColumn = FirstVisibleColumn; - // Prevent exceptions with small TAStudio windows - if (firstVisibleColumn < 0) - { - return; - } - if (HorizontalOrientation) - { - for (int i = 0, f = 0; f < range; i++, f++) - { - f += _lagFrames[i]; - - Color rowColor = Color.White; - QueryRowBkColor?.Invoke(f + startIndex, ref rowColor); - - for (int j = firstVisibleColumn; j <= lastVisible; j++) - { - Color itemColor = Color.White; - QueryItemBkColor?.Invoke(f + startIndex, visibleColumns[j], ref itemColor); - if (itemColor == Color.White) - { - itemColor = rowColor; - } - else if (itemColor.A != 255 && itemColor.A != 0) - { - float alpha = (float)itemColor.A / 255; - itemColor = Color.FromArgb(rowColor.R - (int)((rowColor.R - itemColor.R) * alpha), - rowColor.G - (int)((rowColor.G - itemColor.G) * alpha), - rowColor.B - (int)((rowColor.B - itemColor.B) * alpha)); - } - - if (itemColor != Color.White) // An easy optimization, don't draw unless the user specified something other than the default - { - var cell = new Cell - { - Column = visibleColumns[j], - RowIndex = i - }; - DrawCellBG(itemColor, cell, visibleColumns); - } - } - } - } - else - { - for (int i = 0, f = 0; f < range; i++, f++) // Vertical - { - f += _lagFrames[i]; - - Color rowColor = Color.White; - QueryRowBkColor?.Invoke(f + startIndex, ref rowColor); - - for (int j = FirstVisibleColumn; j <= lastVisible; j++) // Horizontal - { - Color itemColor = Color.White; - QueryItemBkColor?.Invoke(f + startIndex, visibleColumns[j], ref itemColor); - if (itemColor == Color.White) - { - itemColor = rowColor; - } - else if (itemColor.A != 255 && itemColor.A != 0) - { - float alpha = (float)itemColor.A / 255; - itemColor = Color.FromArgb(rowColor.R - (int)((rowColor.R - itemColor.R) * alpha), - rowColor.G - (int)((rowColor.G - itemColor.G) * alpha), - rowColor.B - (int)((rowColor.B - itemColor.B) * alpha)); - } - - if (itemColor != Color.White) // An easy optimization, don't draw unless the user specified something other than the default - { - var cell = new Cell - { - Column = visibleColumns[j], - RowIndex = i - }; - DrawCellBG(itemColor, cell, visibleColumns); - } - } - } - } - } - } -} +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; +using BizHawk.Client.EmuHawk.WinFormExtensions; + +namespace BizHawk.Client.EmuHawk +{ + public partial class InputRoll + { + protected override void OnPaint(PaintEventArgs e) + { + using (_renderer.LockGraphics(e.Graphics, Width, Height)) + { + // White Background + _renderer.SetBrush(Color.White); + _renderer.SetSolidPen(Color.White); + _renderer.FillRectangle(0, 0, Width, Height); + + // Lag frame calculations + SetLagFramesArray(); + + var visibleColumns = _columns.VisibleColumns.ToList(); + + if (visibleColumns.Any()) + { + DrawColumnBg(visibleColumns); + DrawColumnText(visibleColumns); + } + + // Background + DrawBg(visibleColumns); + + // Foreground + DrawData(visibleColumns); + + DrawColumnDrag(); + DrawCellDrag(); + } + } + + private void DrawString(string text, int? width, Point point) + { + if (string.IsNullOrWhiteSpace(text)) + { + return; + } + + if (width.HasValue) + { + var max = (width.Value - CellWidthPadding) / _charSize.Width; + if (text.Length >= max) + { + text = text.Substring(0, max); + } + } + + _renderer.DrawString(text, point); + } + + protected override void OnPaintBackground(PaintEventArgs e) + { + // Do nothing, and this should never be called + } + + private void DrawColumnDrag() + { + if (_columnDown != null && _columnDownMoved && _currentX.HasValue && _currentY.HasValue && IsHoveringOnColumnCell) + { + int x1 = _currentX.Value - (_columnDown.Width.Value / 2); + int y1 = _currentY.Value - (CellHeight / 2); + int x2 = x1 + _columnDown.Width.Value; + int y2 = y1 + CellHeight; + + _renderer.SetSolidPen(_backColor); + _renderer.DrawRectangle(x1, y1, x2, y2); + _renderer.PrepDrawString(_font, _foreColor); + _renderer.DrawString(_columnDown.Text, new Point(x1 + CellWidthPadding, y1 + CellHeightPadding)); + } + } + + private void DrawCellDrag() + { + if (_draggingCell != null) + { + var text = ""; + int offsetX = 0; + int offsetY = 0; + QueryItemText?.Invoke(_draggingCell.RowIndex.Value, _draggingCell.Column, out text, ref offsetX, ref offsetY); + + Color bgColor = _backColor; + QueryItemBkColor?.Invoke(_draggingCell.RowIndex.Value, _draggingCell.Column, ref bgColor); + + int x1 = _currentX.Value - (_draggingCell.Column.Width.Value / 2); + int y1 = _currentY.Value - (CellHeight / 2); + int x2 = x1 + _draggingCell.Column.Width.Value; + int y2 = y1 + CellHeight; + + + _renderer.SetBrush(bgColor); + _renderer.FillRectangle(x1, y1, x2 - x1, y2 - y1); + _renderer.PrepDrawString(_font, _foreColor); + _renderer.DrawString(text, new Point(x1 + CellWidthPadding + offsetX, y1 + CellHeightPadding + offsetY)); + } + } + + private void DrawColumnText(List visibleColumns) + { + if (HorizontalOrientation) + { + int start = -_vBar.Value; + + _renderer.PrepDrawString(_font, _foreColor); + + foreach (var column in visibleColumns) + { + var point = new Point(CellWidthPadding, start + CellHeightPadding); + + if (IsHoveringOnColumnCell && column == CurrentCell.Column) + { + _renderer.PrepDrawString(_font, SystemColors.HighlightText); + DrawString(column.Text, column.Width, point); + _renderer.PrepDrawString(_font, _foreColor); + } + else + { + DrawString(column.Text, column.Width, point); + } + + start += CellHeight; + } + } + else + { + _renderer.PrepDrawString(_font, _foreColor); + + foreach (var column in visibleColumns) + { + var point = new Point(column.Left.Value + 2 * CellWidthPadding - _hBar.Value, CellHeightPadding); // TODO: fix this CellPadding issue (2 * CellPadding vs just CellPadding) + + if (IsHoveringOnColumnCell && column == CurrentCell.Column) + { + _renderer.PrepDrawString(_font, SystemColors.HighlightText); + DrawString(column.Text, column.Width, point); + _renderer.PrepDrawString(_font, _foreColor); + } + else + { + DrawString(column.Text, column.Width, point); + } + } + } + } + + private void DrawData(List visibleColumns) + { + // Prevent exceptions with small TAStudio windows + if (visibleColumns.Count == 0) + { + return; + } + + if (QueryItemText != null) + { + if (HorizontalOrientation) + { + int startRow = FirstVisibleRow; + int range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1; + + _renderer.PrepDrawString(_font, _foreColor); + for (int i = 0, f = 0; f < range; i++, f++) + { + f += _lagFrames[i]; + int lastVisible = LastVisibleColumnIndex; + for (int j = FirstVisibleColumn; j <= lastVisible; j++) + { + Bitmap image = null; + int x; + int y; + int bitmapOffsetX = 0; + int bitmapOffsetY = 0; + + QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY); + + if (image != null) + { + x = RowsToPixels(i) + CellWidthPadding + bitmapOffsetX; + y = (j * CellHeight) + (CellHeightPadding * 2) + bitmapOffsetY; + _renderer.DrawBitmap(image, new Point(x, y)); + } + + string text; + int strOffsetX = 0; + int strOffsetY = 0; + QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY); + + // Center Text + x = RowsToPixels(i) + ((CellWidth - (text.Length * _charSize.Width)) / 2); + y = (j * CellHeight) + CellHeightPadding - _vBar.Value; + var point = new Point(x + strOffsetX, y + strOffsetY); + + if (visibleColumns[j].Name == "FrameColumn") // TODO: don't do this hack + { + _renderer.PrepDrawString(_font, _foreColor, rotate: true); + DrawString(text, ColumnWidth, new Point(point.X + _charSize.Height + CellWidthPadding, point.Y + CellHeightPadding)); + _renderer.PrepDrawString(_font, _foreColor, rotate: false); + } + else + { + DrawString(text, ColumnWidth, point); + } + } + } + } + else + { + int startRow = FirstVisibleRow; + int range = Math.Min(LastVisibleRow, RowCount - 1) - startRow + 1; + + _renderer.PrepDrawString(_font, _foreColor); + int xPadding = CellWidthPadding + 1 - _hBar.Value; + for (int i = 0, f = 0; f < range; i++, f++) // Vertical + { + f += _lagFrames[i]; + int lastVisible = LastVisibleColumnIndex; + for (int j = FirstVisibleColumn; j <= lastVisible; j++) // Horizontal + { + RollColumn col = visibleColumns[j]; + + string text; + int strOffsetX = 0; + int strOffsetY = 0; + Point point = new Point(col.Left.Value + xPadding, RowsToPixels(i) + CellHeightPadding); + + Bitmap image = null; + int bitmapOffsetX = 0; + int bitmapOffsetY = 0; + + QueryItemIcon?.Invoke(f + startRow, visibleColumns[j], ref image, ref bitmapOffsetX, ref bitmapOffsetY); + + if (image != null) + { + _renderer.DrawBitmap(image, new Point(point.X + bitmapOffsetX, point.Y + bitmapOffsetY + CellHeightPadding)); + } + + QueryItemText(f + startRow, visibleColumns[j], out text, ref strOffsetX, ref strOffsetY); + + bool rePrep = false; + if (_selectedItems.Contains(new Cell { Column = visibleColumns[j], RowIndex = f + startRow })) + { + _renderer.PrepDrawString(_font, SystemColors.HighlightText); + rePrep = true; + } + + DrawString(text, col.Width, new Point(point.X + strOffsetX, point.Y + strOffsetY)); + + if (rePrep) + { + _renderer.PrepDrawString(_font, _foreColor); + } + } + } + } + } + } + + private void DrawColumnBg(List visibleColumns) + { + _renderer.SetBrush(SystemColors.ControlLight); + _renderer.SetSolidPen(Color.Black); + + if (HorizontalOrientation) + { + _renderer.FillRectangle(0, 0, ColumnWidth + 1, DrawHeight + 1); + _renderer.Line(0, 0, 0, visibleColumns.Count * CellHeight + 1); + _renderer.Line(ColumnWidth, 0, ColumnWidth, visibleColumns.Count * CellHeight + 1); + + int start = -_vBar.Value; + foreach (var column in visibleColumns) + { + _renderer.Line(1, start, ColumnWidth, start); + start += CellHeight; + } + + if (visibleColumns.Any()) + { + _renderer.Line(1, start, ColumnWidth, start); + } + } + else + { + int bottomEdge = RowsToPixels(0); + + // Gray column box and black line underneath + _renderer.FillRectangle(0, 0, Width + 1, bottomEdge + 1); + _renderer.Line(0, 0, TotalColWidth.Value + 1, 0); + _renderer.Line(0, bottomEdge, TotalColWidth.Value + 1, bottomEdge); + + // Vertical black separators + foreach (var column in visibleColumns) + { + int pos = column.Left.Value - _hBar.Value; + _renderer.Line(pos, 0, pos, bottomEdge); + } + + // Draw right most line + if (visibleColumns.Any()) + { + int right = TotalColWidth.Value - _hBar.Value; + _renderer.Line(right, 0, right, bottomEdge); + } + } + + // Emphasis + foreach (var column in visibleColumns.Where(c => c.Emphasis)) + { + _renderer.SetBrush(SystemColors.ActiveBorder); + if (HorizontalOrientation) + { + _renderer.FillRectangle(1, visibleColumns.IndexOf(column) * CellHeight + 1, ColumnWidth - 1, ColumnHeight - 1); + } + else + { + _renderer.FillRectangle(column.Left.Value + 1 - _hBar.Value, 1, column.Width.Value - 1, ColumnHeight - 1); + } + } + + // If the user is hovering over a column + if (IsHoveringOnColumnCell) + { + if (HorizontalOrientation) + { + for (int i = 0; i < visibleColumns.Count; i++) + { + if (visibleColumns[i] != CurrentCell.Column) + { + continue; + } + + _renderer.SetBrush(CurrentCell.Column.Emphasis + ? SystemColors.Highlight.Add(0x00222222) + : SystemColors.Highlight); + + _renderer.FillRectangle(1, i * CellHeight + 1, ColumnWidth - 1, ColumnHeight - 1); + } + } + else + { + // TODO multiple selected columns + foreach (var column in visibleColumns) + { + if (column == CurrentCell.Column) + { + // Left of column is to the right of the viewable area or right of column is to the left of the viewable area + if (column.Left.Value - _hBar.Value > Width || column.Right.Value - _hBar.Value < 0) + { + continue; + } + + int left = column.Left.Value - _hBar.Value; + int width = column.Right.Value - _hBar.Value - left; + + _renderer.SetBrush(CurrentCell.Column.Emphasis + ? SystemColors.Highlight.Add(0x00550000) + : SystemColors.Highlight); + + _renderer.FillRectangle(left + 1, 1, width - 1, ColumnHeight - 1); + } + } + } + } + } + + // TODO refactor this and DoBackGroundCallback functions. + /// + /// Draw Gridlines and background colors using QueryItemBkColor. + /// + private void DrawBg(List visibleColumns) + { + if (UseCustomBackground && QueryItemBkColor != null) + { + DoBackGroundCallback(visibleColumns); + } + + if (GridLines) + { + _renderer.SetSolidPen(SystemColors.ControlLight); + if (HorizontalOrientation) + { + // Columns + for (int i = 1; i < VisibleRows + 1; i++) + { + int x = RowsToPixels(i); + _renderer.Line(x, 1, x, DrawHeight); + } + + // Rows + for (int i = 0; i < visibleColumns.Count + 1; i++) + { + _renderer.Line(RowsToPixels(0) + 1, i * CellHeight - _vBar.Value, DrawWidth, i * CellHeight - _vBar.Value); + } + } + else + { + // Columns + int y = ColumnHeight + 1; + int? totalColWidth = TotalColWidth; + foreach (var column in visibleColumns) + { + int x = column.Left.Value - _hBar.Value; + _renderer.Line(x, y, x, Height - 1); + } + + if (visibleColumns.Any()) + { + _renderer.Line(totalColWidth.Value - _hBar.Value, y, totalColWidth.Value - _hBar.Value, Height - 1); + } + + // Rows + for (int i = 1; i < VisibleRows + 1; i++) + { + _renderer.Line(0, RowsToPixels(i), Width + 1, RowsToPixels(i)); + } + } + } + + if (_selectedItems.Any()) + { + DoSelectionBG(visibleColumns); + } + } + + private void DoSelectionBG(List visibleColumns) + { + // SuuperW: This allows user to see other colors in selected frames. + Color rowColor = Color.White; + int lastVisibleRow = LastVisibleRow; + int lastRow = -1; + foreach (Cell cell in _selectedItems) + { + if (cell.RowIndex > lastVisibleRow || cell.RowIndex < FirstVisibleRow || !VisibleColumns.Contains(cell.Column)) + { + continue; + } + + Cell relativeCell = new Cell + { + RowIndex = cell.RowIndex - FirstVisibleRow, + Column = cell.Column, + }; + relativeCell.RowIndex -= CountLagFramesAbsolute(relativeCell.RowIndex.Value); + + if (QueryRowBkColor != null && lastRow != cell.RowIndex.Value) + { + QueryRowBkColor(cell.RowIndex.Value, ref rowColor); + lastRow = cell.RowIndex.Value; + } + + Color cellColor = rowColor; + QueryItemBkColor?.Invoke(cell.RowIndex.Value, cell.Column, ref cellColor); + + // Alpha layering for cell before selection + float alpha = (float)cellColor.A / 255; + if (cellColor.A != 255 && cellColor.A != 0) + { + cellColor = Color.FromArgb(rowColor.R - (int)((rowColor.R - cellColor.R) * alpha), + rowColor.G - (int)((rowColor.G - cellColor.G) * alpha), + rowColor.B - (int)((rowColor.B - cellColor.B) * alpha)); + } + + // Alpha layering for selection + alpha = 0.33f; + cellColor = Color.FromArgb(cellColor.R - (int)((cellColor.R - SystemColors.Highlight.R) * alpha), + cellColor.G - (int)((cellColor.G - SystemColors.Highlight.G) * alpha), + cellColor.B - (int)((cellColor.B - SystemColors.Highlight.B) * alpha)); + DrawCellBG(cellColor, relativeCell, visibleColumns); + } + } + + /// + /// Given a cell with rowindex inbetween 0 and VisibleRows, it draws the background color specified. Do not call with absolute rowindices. + /// + private void DrawCellBG(Color color, Cell cell, List visibleColumns) + { + int x, y, w, h; + + if (HorizontalOrientation) + { + x = RowsToPixels(cell.RowIndex.Value) + 1; + w = CellWidth - 1; + y = (CellHeight * visibleColumns.IndexOf(cell.Column)) + 1 - _vBar.Value; // We can't draw without row and column, so assume they exist and fail catastrophically if they don't + h = CellHeight - 1; + if (x < ColumnWidth) + { + return; + } + } + else + { + w = cell.Column.Width.Value - 1; + x = cell.Column.Left.Value - _hBar.Value + 1; + y = RowsToPixels(cell.RowIndex.Value) + 1; // We can't draw without row and column, so assume they exist and fail catastrophically if they don't + h = CellHeight - 1; + if (y < ColumnHeight) + { + return; + } + } + + if (x > DrawWidth || y > DrawHeight) + { + return; + } // Don't draw if off screen. + + _renderer.SetBrush(color); + _renderer.FillRectangle(x, y, w, h); + } + + /// + /// Calls QueryItemBkColor callback for all visible cells and fills in the background of those cells. + /// + private void DoBackGroundCallback(List visibleColumns) + { + int startIndex = FirstVisibleRow; + int range = Math.Min(LastVisibleRow, RowCount - 1) - startIndex + 1; + int lastVisible = LastVisibleColumnIndex; + int firstVisibleColumn = FirstVisibleColumn; + // Prevent exceptions with small TAStudio windows + if (firstVisibleColumn < 0) + { + return; + } + if (HorizontalOrientation) + { + for (int i = 0, f = 0; f < range; i++, f++) + { + f += _lagFrames[i]; + + Color rowColor = Color.White; + QueryRowBkColor?.Invoke(f + startIndex, ref rowColor); + + for (int j = firstVisibleColumn; j <= lastVisible; j++) + { + Color itemColor = Color.White; + QueryItemBkColor?.Invoke(f + startIndex, visibleColumns[j], ref itemColor); + if (itemColor == Color.White) + { + itemColor = rowColor; + } + else if (itemColor.A != 255 && itemColor.A != 0) + { + float alpha = (float)itemColor.A / 255; + itemColor = Color.FromArgb(rowColor.R - (int)((rowColor.R - itemColor.R) * alpha), + rowColor.G - (int)((rowColor.G - itemColor.G) * alpha), + rowColor.B - (int)((rowColor.B - itemColor.B) * alpha)); + } + + if (itemColor != Color.White) // An easy optimization, don't draw unless the user specified something other than the default + { + var cell = new Cell + { + Column = visibleColumns[j], + RowIndex = i + }; + DrawCellBG(itemColor, cell, visibleColumns); + } + } + } + } + else + { + for (int i = 0, f = 0; f < range; i++, f++) // Vertical + { + f += _lagFrames[i]; + + Color rowColor = Color.White; + QueryRowBkColor?.Invoke(f + startIndex, ref rowColor); + + for (int j = FirstVisibleColumn; j <= lastVisible; j++) // Horizontal + { + Color itemColor = Color.White; + QueryItemBkColor?.Invoke(f + startIndex, visibleColumns[j], ref itemColor); + if (itemColor == Color.White) + { + itemColor = rowColor; + } + else if (itemColor.A != 255 && itemColor.A != 0) + { + float alpha = (float)itemColor.A / 255; + itemColor = Color.FromArgb(rowColor.R - (int)((rowColor.R - itemColor.R) * alpha), + rowColor.G - (int)((rowColor.G - itemColor.G) * alpha), + rowColor.B - (int)((rowColor.B - itemColor.B) * alpha)); + } + + if (itemColor != Color.White) // An easy optimization, don't draw unless the user specified something other than the default + { + var cell = new Cell + { + Column = visibleColumns[j], + RowIndex = i + }; + DrawCellBG(itemColor, cell, visibleColumns); + } + } + } + } + } + } +} diff --git a/BizHawk.Client.EmuHawk/CustomControls/InputRoll.cs b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs similarity index 87% rename from BizHawk.Client.EmuHawk/CustomControls/InputRoll.cs rename to BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs index 4c97607d1c..a7c1a5a6e5 100644 --- a/BizHawk.Client.EmuHawk/CustomControls/InputRoll.cs +++ b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/InputRoll.cs @@ -1,2314 +1,2068 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Linq; -using System.Windows.Forms; - -using BizHawk.Client.Common; -using BizHawk.Client.EmuHawk.CustomControls; -using BizHawk.Common; - -namespace BizHawk.Client.EmuHawk -{ - // Row width depends on font size and padding - // Column width is specified in column headers - // Row width is specified for horizontal orientation - public partial class InputRoll : Control - { - private readonly IControlRenderer _renderer; - private readonly SortedSet _selectedItems = new SortedSet(new SortCell()); - - // scrollbar location(s) are calculated later (e.g. on resize) - private readonly VScrollBar _vBar = new VScrollBar { Visible = false }; - private readonly HScrollBar _hBar = new HScrollBar { Visible = false }; - - private readonly Timer _hoverTimer = new Timer(); - private readonly byte[] _lagFrames = new byte[256]; // Large enough value that it shouldn't ever need resizing. // apparently not large enough for 4K - - private readonly Color _foreColor; - private readonly Color _backColor; - - private RollColumns _columns = new RollColumns(); - private bool _horizontalOrientation; - private bool _programmaticallyUpdatingScrollBarValues; - private int _maxCharactersInHorizontal = 1; - - private int _rowCount; - private Size _charSize; - - private RollColumn _columnDown; - private RollColumn _columnResizing; - - private int? _currentX; - private int? _currentY; - - // Hiding lag frames (Mainly intended for < 60fps play.) - public int LagFramesToHide { get; set; } - public bool HideWasLagFrames { get; set; } - - public bool AllowRightClickSelecton { get; set; } - public bool LetKeysModifySelection { get; set; } - public bool SuspendHotkeys { get; set; } - - private Font _font = new Font("Arial", 8, FontStyle.Bold); - - public InputRoll() - { - UseCustomBackground = true; - GridLines = true; - CellWidthPadding = 3; - CellHeightPadding = 0; - CurrentCell = null; - ScrollMethod = "near"; - - SetStyle(ControlStyles.AllPaintingInWmPaint, true); - SetStyle(ControlStyles.UserPaint, true); - SetStyle(ControlStyles.SupportsTransparentBackColor, true); - SetStyle(ControlStyles.Opaque, true); - SetStyle(ControlStyles.OptimizedDoubleBuffer, true); - - if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows) - { - _renderer = new GdiRenderer(); - } - else - { - _renderer = new GdiPlusRenderer(); - } - - using (var g = CreateGraphics()) - using (_renderer.LockGraphics(g, Width, Height)) - { - _charSize = _renderer.MeasureString("A", _font); // TODO make this a property so changing it updates other values. - } - - UpdateCellSize(); - ColumnWidth = CellWidth; - ColumnHeight = CellHeight + 2; - - _vBar.SmallChange = CellHeight; - _vBar.LargeChange = CellHeight * 20; - - _hBar.SmallChange = CellWidth; - _hBar.LargeChange = 20; - - Controls.Add(_vBar); - Controls.Add(_hBar); - - _vBar.ValueChanged += VerticalBar_ValueChanged; - _hBar.ValueChanged += HorizontalBar_ValueChanged; - - RecalculateScrollBars(); - _columns.ChangedCallback = ColumnChangedCallback; - - _hoverTimer.Interval = 750; - _hoverTimer.Tick += HoverTimerEventProcessor; - _hoverTimer.Stop(); - - _foreColor = ForeColor; - _backColor = BackColor; - } - - private void HoverTimerEventProcessor(object sender, EventArgs e) - { - _hoverTimer.Stop(); - - CellHovered?.Invoke(this, new CellEventArgs(LastCell, CurrentCell)); - } - - protected override void Dispose(bool disposing) - { - _renderer.Dispose(); - base.Dispose(disposing); - } - - protected override void OnDoubleClick(EventArgs e) - { - if (IsHoveringOnColumnEdge) - { - if (HorizontalOrientation) - { - // TODO - } - else - { - var maxLength = CurrentCell.Column.Text?.Length ?? 0; - - - for (int i = 0; i < RowCount; i++) - { - string text = ""; - int offSetX = 0, offSetY = 0; - QueryItemText?.Invoke(i, CurrentCell.Column, out text, ref offSetX, ref offSetY); - if (text.Length > maxLength) - { - maxLength = text.Length; - } - } - - var newWidth = (maxLength * _charSize.Width) + (CellWidthPadding * 2); - CurrentCell.Column.Width = newWidth; - _columns.ColumnsChanged(); - Refresh(); - } - - } - - base.OnDoubleClick(e); - } - - #region Properties - - /// - /// Gets or sets the amount of left and right padding on the text inside a cell - /// - [DefaultValue(3)] - [Category("Behavior")] - public int CellWidthPadding { get; set; } - - /// - /// Gets or sets the amount of top and bottom padding on the text inside a cell - /// - [DefaultValue(1)] - [Category("Behavior")] - public int CellHeightPadding { get; set; } - - /// - /// Gets or sets a value indicating whether grid lines are displayed around cells - /// - [Category("Appearance")] - [DefaultValue(true)] - public bool GridLines { get; set; } - - /// - /// Gets or sets a value indicating whether the control is horizontal or vertical - /// - [Category("Behavior")] - public bool HorizontalOrientation - { - get - { - return _horizontalOrientation; - } - set - { - if (_horizontalOrientation != value) - { - int temp = ScrollSpeed; - _horizontalOrientation = value; - OrientationChanged(); - _hBar.SmallChange = CellWidth; - _vBar.SmallChange = CellHeight; - ScrollSpeed = temp; - } - } - } - - /// - /// Gets or sets the scrolling speed - /// - [Category("Behavior")] - public int ScrollSpeed - { - get - { - if (HorizontalOrientation) - { - return _hBar.SmallChange / CellWidth; - } - - return _vBar.SmallChange / CellHeight; - } - - set - { - if (HorizontalOrientation) - { - _hBar.SmallChange = value * CellWidth; - } - else - { - _vBar.SmallChange = value * CellHeight; - } - } - } - - /// - /// Gets or sets the sets the virtual number of rows to be displayed. Does not include the column header row. - /// - [Category("Behavior")] - public int RowCount - { - get - { - return _rowCount; - } - - set - { - _rowCount = value; - RecalculateScrollBars(); - } - } - - /// - /// Gets or sets a value indicating whether columns can be resized - /// - [Category("Behavior")] - public bool AllowColumnResize { get; set; } - - /// - /// Gets or sets a value indicating whether columns can be reordered - /// - [Category("Behavior")] - public bool AllowColumnReorder { get; set; } - - /// - /// Gets or sets a value indicating whether the entire row will always be selected - /// - [Category("Appearance")] - [DefaultValue(false)] - public bool FullRowSelect { get; set; } - - /// - /// Gets or sets a value indicating whether multiple items can to be selected - /// - [Category("Behavior")] - [DefaultValue(true)] - public bool MultiSelect { get; set; } - - /// - /// Gets or sets a value indicating whether the control is in input painting mode - /// - [Category("Behavior")] - [DefaultValue(false)] - public bool InputPaintingMode { get; set; } - - /// - /// All visible columns - /// - [Category("Behavior")] - public IEnumerable VisibleColumns => _columns.VisibleColumns; - - /// - /// Gets or sets how the InputRoll scrolls when calling ScrollToIndex. - /// - [DefaultValue("near")] - [Category("Behavior")] - public string ScrollMethod { get; set; } - - /// - /// Gets or sets a value indicating how the Intever for the hover event - /// - [Category("Behavior")] - public bool AlwaysScroll { get; set; } - - /// - /// Gets or sets the lowest seek interval to activate the progress bar - /// - [Category("Behavior")] - public int SeekingCutoffInterval { get; set; } - - /// - /// Returns all columns including those that are not visible - /// - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public RollColumns AllColumns => _columns; - - [DefaultValue(750)] - [Category("Behavior")] - public int HoverInterval - { - get { return _hoverTimer.Interval; } - set { _hoverTimer.Interval = value; } - } - - #endregion - - #region Event Handlers - - /// - /// Fire the event which requests the text for the passed cell - /// - [Category("Virtual")] - public event QueryItemTextHandler QueryItemText; - - /// - /// Fire the event which requests the background color for the passed cell - /// - [Category("Virtual")] - public event QueryItemBkColorHandler QueryItemBkColor; - - [Category("Virtual")] - public event QueryRowBkColorHandler QueryRowBkColor; - - /// - /// Fire the event which requests an icon for a given cell - /// - [Category("Virtual")] - public event QueryItemIconHandler QueryItemIcon; - - /// - /// Fire the QueryFrameLag event which checks if a given frame is a lag frame - /// - [Category("Virtual")] - public event QueryFrameLagHandler QueryFrameLag; - - /// - /// Fires when the mouse moves from one cell to another (including column header cells) - /// - [Category("Mouse")] - public event CellChangeEventHandler PointedCellChanged; - - /// - /// Fires when a cell is hovered on - /// - [Category("Mouse")] - public event HoverEventHandler CellHovered; - - /// - /// Occurs when a column header is clicked - /// - [Category("Action")] - public event ColumnClickEventHandler ColumnClick; - - /// - /// Occurs when a column header is right-clicked - /// - [Category("Action")] - public event ColumnClickEventHandler ColumnRightClick; - - /// - /// Occurs whenever the 'SelectedItems' property for this control changes - /// - [Category("Behavior")] - public event EventHandler SelectedIndexChanged; - - /// - /// Occurs whenever the mouse wheel is scrolled while the right mouse button is held - /// - [Category("Behavior")] - public event RightMouseScrollEventHandler RightMouseScrolled; - - [Category("Property Changed")] - [Description("Occurs when the column header has been reordered")] - public event ColumnReorderedEventHandler ColumnReordered; - - [Category("Action")] - [Description("Occurs when the scroll value of the visible rows change (in vertical orientation this is the vertical scroll bar change, and in horizontal it is the horizontal scroll bar)")] - public event RowScrollEvent RowScroll; - - [Category("Action")] - [Description("Occurs when the scroll value of the columns (in vertical orientation this is the horizontal scroll bar change, and in horizontal it is the vertical scroll bar)")] - public event ColumnScrollEvent ColumnScroll; - - [Category("Action")] - [Description("Occurs when a cell is dragged and then dropped into a new cell, old cell is the cell that was being dragged, new cell is its new destination")] - public event CellDroppedEvent CellDropped; - - /// - /// Retrieve the text for a cell - /// - public delegate void QueryItemTextHandler(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY); - - /// - /// Retrieve the background color for a cell - /// - public delegate void QueryItemBkColorHandler(int index, RollColumn column, ref Color color); - public delegate void QueryRowBkColorHandler(int index, ref Color color); - - /// - /// Retrieve the image for a given cell - /// - public delegate void QueryItemIconHandler(int index, RollColumn column, ref Bitmap icon, ref int offsetX, ref int offsetY); - - /// - /// Check if a given frame is a lag frame - /// - public delegate bool QueryFrameLagHandler(int index, bool hideWasLag); - - public delegate void CellChangeEventHandler(object sender, CellEventArgs e); - - public delegate void HoverEventHandler(object sender, CellEventArgs e); - - public delegate void RightMouseScrollEventHandler(object sender, MouseEventArgs e); - - public delegate void ColumnClickEventHandler(object sender, ColumnClickEventArgs e); - - public delegate void ColumnReorderedEventHandler(object sender, ColumnReorderedEventArgs e); - - public delegate void RowScrollEvent(object sender, EventArgs e); - - public delegate void ColumnScrollEvent(object sender, EventArgs e); - - public delegate void CellDroppedEvent(object sender, CellEventArgs e); - - public class CellEventArgs - { - public CellEventArgs(Cell oldCell, Cell newCell) - { - OldCell = oldCell; - NewCell = newCell; - } - - public Cell OldCell { get; private set; } - public Cell NewCell { get; private set; } - } - - public class ColumnClickEventArgs - { - public ColumnClickEventArgs(RollColumn column) - { - Column = column; - } - - public RollColumn Column { get; private set; } - } - - public class ColumnReorderedEventArgs - { - public ColumnReorderedEventArgs(int oldDisplayIndex, int newDisplayIndex, RollColumn column) - { - Column = column; - OldDisplayIndex = oldDisplayIndex; - NewDisplayIndex = newDisplayIndex; - } - - public RollColumn Column { get; private set; } - public int OldDisplayIndex { get; private set; } - public int NewDisplayIndex { get; private set; } - } - - #endregion - - #region Api - - public void SelectRow(int index, bool val) - { - if (_columns.VisibleColumns.Any()) - { - if (val) - { - SelectCell(new Cell - { - RowIndex = index, - Column = _columns[0] - }); - } - else - { - IEnumerable items = _selectedItems.Where(cell => cell.RowIndex == index); - _selectedItems.RemoveWhere(items.Contains); - } - } - } - - public void SelectAll() - { - var oldFullRowVal = FullRowSelect; - FullRowSelect = true; - for (int i = 0; i < RowCount; i++) - { - SelectRow(i, true); - } - - FullRowSelect = oldFullRowVal; - } - - public void DeselectAll() - { - _selectedItems.Clear(); - } - - public void TruncateSelection(int index) - { - _selectedItems.RemoveWhere(cell => cell.RowIndex > index); - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public bool IsPointingAtColumnHeader => IsHoveringOnColumnCell; - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int? FirstSelectedIndex - { - get - { - if (AnyRowsSelected) - { - return SelectedRows.Min(); - } - - return null; - } - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int? LastSelectedIndex - { - get - { - if (AnyRowsSelected) - { - return SelectedRows.Max(); - } - - return null; - } - } - - /// - /// Gets or sets the current Cell that the mouse was in. - /// - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public Cell CurrentCell { get; set; } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public bool CurrentCellIsDataCell => CurrentCell?.RowIndex != null && CurrentCell.Column != null; - - /// - /// Gets or sets the previous Cell that the mouse was in. - /// - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public Cell LastCell { get; private set; } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public bool IsPaintDown { get; private set; } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public bool UseCustomBackground { get; set; } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int DrawHeight { get; private set; } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int DrawWidth { get; private set; } - - /// - /// Gets or sets the width of data cells when in Horizontal orientation. - /// - public int MaxCharactersInHorizontal - { - get - { - return _maxCharactersInHorizontal; - } - - set - { - _maxCharactersInHorizontal = value; - UpdateCellSize(); - } - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public bool RightButtonHeld { get; private set; } - - public string UserSettingsSerialized() - { - var settings = ConfigService.SaveWithType(Settings); - return settings; - } - - public void LoadSettingsSerialized(string settingsJson) - { - var settings = ConfigService.LoadWithType(settingsJson); - - // TODO: don't silently fail, inform the user somehow - if (settings is InputRollSettings) - { - var rollSettings = settings as InputRollSettings; - _columns = rollSettings.Columns; - _columns.ChangedCallback = ColumnChangedCallback; - HorizontalOrientation = rollSettings.HorizontalOrientation; - LagFramesToHide = rollSettings.LagFramesToHide; - HideWasLagFrames = rollSettings.HideWasLagFrames; - } - } - - private InputRollSettings Settings => new InputRollSettings - { - Columns = _columns, - HorizontalOrientation = HorizontalOrientation, - LagFramesToHide = LagFramesToHide, - HideWasLagFrames = HideWasLagFrames - }; - - public class InputRollSettings - { - public RollColumns Columns { get; set; } - public bool HorizontalOrientation { get; set; } - public int LagFramesToHide { get; set; } - public bool HideWasLagFrames { get; set; } - } - - /// - /// Gets or sets the first visible row index, if scrolling is needed - /// - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int FirstVisibleRow - { - get // SuuperW: This was checking if the scroll bars were needed, which is useless because their Value is 0 if they aren't needed. - { - if (HorizontalOrientation) - { - return _hBar.Value / CellWidth; - } - - return _vBar.Value / CellHeight; - } - - set - { - if (HorizontalOrientation) - { - if (NeedsHScrollbar) - { - _programmaticallyUpdatingScrollBarValues = true; - if (value * CellWidth <= _hBar.Maximum) - { - _hBar.Value = value * CellWidth; - } - else - { - _hBar.Value = _hBar.Maximum; - } - - _programmaticallyUpdatingScrollBarValues = false; - } - } - else - { - if (NeedsVScrollbar) - { - _programmaticallyUpdatingScrollBarValues = true; - if (value * CellHeight <= _vBar.Maximum) - { - _vBar.Value = value * CellHeight; - } - else - { - _vBar.Value = _vBar.Maximum; - } - - _programmaticallyUpdatingScrollBarValues = false; - } - } - } - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - private int LastFullyVisibleRow - { - get - { - int halfRow = 0; - if ((DrawHeight - ColumnHeight - 3) % CellHeight < CellHeight / 2) - { - halfRow = 1; - } - - return FirstVisibleRow + VisibleRows - halfRow + CountLagFramesDisplay(VisibleRows - halfRow); - } - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int LastVisibleRow - { - get - { - return FirstVisibleRow + VisibleRows + CountLagFramesDisplay(VisibleRows); - } - - set - { - int halfRow = 0; - if ((DrawHeight - ColumnHeight - 3) % CellHeight < CellHeight / 2) - { - halfRow = 1; - } - - if (LagFramesToHide == 0) - { - FirstVisibleRow = Math.Max(value - (VisibleRows - halfRow), 0); - } - else - { - if (Math.Abs(LastFullyVisibleRow - value) > VisibleRows) // Big jump - { - FirstVisibleRow = Math.Max(value - (ExpectedDisplayRange() - halfRow), 0); - SetLagFramesArray(); - } - - // Small jump, more accurate - int lastVisible = LastFullyVisibleRow; - do - { - if ((lastVisible - value) / (LagFramesToHide + 1) != 0) - { - FirstVisibleRow = Math.Max(FirstVisibleRow - ((lastVisible - value) / (LagFramesToHide + 1)), 0); - } - else - { - FirstVisibleRow -= Math.Sign(lastVisible - value); - } - - SetLagFramesArray(); - lastVisible = LastFullyVisibleRow; - } - while ((lastVisible - value < 0 || lastVisible - value > _lagFrames[VisibleRows - halfRow]) && FirstVisibleRow != 0); - } - } - } - - public bool IsVisible(int index) - { - return (index >= FirstVisibleRow) && (index <= LastFullyVisibleRow); - } - - public bool IsPartiallyVisible(int index) - { - return index >= FirstVisibleRow && index <= LastVisibleRow; - } - - /// - /// Gets the number of rows currently visible including partially visible rows. - /// - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int VisibleRows - { - get - { - if (HorizontalOrientation) - { - return (DrawWidth - ColumnWidth) / CellWidth; - } - - return (DrawHeight - ColumnHeight - 3) / CellHeight; // Minus three makes it work - } - } - - /// - /// Gets the first visible column index, if scrolling is needed - /// - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int FirstVisibleColumn - { - get - { - if (HorizontalOrientation) - { - return _vBar.Value / CellHeight; - } - - var columnList = VisibleColumns.ToList(); - return columnList.FindIndex(c => c.Right > _hBar.Value); - } - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public int LastVisibleColumnIndex - { - get - { - List columnList = VisibleColumns.ToList(); - int ret; - if (HorizontalOrientation) - { - ret = (_vBar.Value + DrawHeight) / CellHeight; - if (ret >= columnList.Count) - { - ret = columnList.Count - 1; - } - } - else - { - ret = columnList.FindLastIndex(c => c.Left <= DrawWidth + _hBar.Value); - } - - return ret; - } - } - - private Cell _draggingCell; - - public void DragCurrentCell() - { - _draggingCell = CurrentCell; - } - - public void ReleaseCurrentCell() - { - if (_draggingCell != null) - { - var draggedCell = _draggingCell; - _draggingCell = null; - - if (CurrentCell != draggedCell) - { - CellDropped?.Invoke(this, new CellEventArgs(draggedCell, CurrentCell)); - } - } - } - - /// - /// Scrolls to the given index, according to the scroll settings. - /// - public void ScrollToIndex(int index) - { - if (ScrollMethod == "near") - { - MakeIndexVisible(index); - } - - if (!IsVisible(index) || AlwaysScroll) - { - if (ScrollMethod == "top") - { - FirstVisibleRow = index; - } - else if (ScrollMethod == "bottom") - { - LastVisibleRow = index; - } - else if (ScrollMethod == "center") - { - if (LagFramesToHide == 0) - { - FirstVisibleRow = Math.Max(index - (VisibleRows / 2), 0); - } - else - { - if (Math.Abs(FirstVisibleRow + CountLagFramesDisplay(VisibleRows / 2) - index) > VisibleRows) // Big jump - { - FirstVisibleRow = Math.Max(index - (ExpectedDisplayRange() / 2), 0); - SetLagFramesArray(); - } - - // Small jump, more accurate - int lastVisible = FirstVisibleRow + CountLagFramesDisplay(VisibleRows / 2); - do - { - if ((lastVisible - index) / (LagFramesToHide + 1) != 0) - { - FirstVisibleRow = Math.Max(FirstVisibleRow - ((lastVisible - index) / (LagFramesToHide + 1)), 0); - } - else - { - FirstVisibleRow -= Math.Sign(lastVisible - index); - } - - SetLagFramesArray(); - lastVisible = FirstVisibleRow + CountLagFramesDisplay(VisibleRows / 2); - } - while ((lastVisible - index < 0 || lastVisible - index > _lagFrames[VisibleRows]) && FirstVisibleRow != 0); - } - } - } - } - - /// - /// Scrolls so that the given index is visible, if it isn't already; doesn't use scroll settings. - /// - public void MakeIndexVisible(int index) - { - if (!IsVisible(index)) - { - if (FirstVisibleRow > index) - { - FirstVisibleRow = index; - } - else - { - LastVisibleRow = index; - } - } - } - - [Browsable(false)] - [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public IEnumerable SelectedRows - { - get - { - return _selectedItems - .Where(cell => cell.RowIndex.HasValue) - .Select(cell => cell.RowIndex.Value) - .Distinct(); - } - } - - public bool AnyRowsSelected - { - get - { - return _selectedItems.Any(cell => cell.RowIndex.HasValue); - } - } - - public void ClearSelectedRows() - { - _selectedItems.Clear(); - } - - public IEnumerable GenerateContextMenuItems() - { - yield return new ToolStripSeparator(); - - var rotate = new ToolStripMenuItem - { - Name = "RotateMenuItem", - Text = "Rotate", - ShortcutKeyDisplayString = RotateHotkeyStr, - }; - - rotate.Click += (o, ev) => - { - HorizontalOrientation ^= true; - }; - - yield return rotate; - } - - public string RotateHotkeyStr => "Ctrl+Shift+F"; - - #endregion - - #region Mouse and Key Events - - private bool _columnDownMoved; - private int _previousX = 0; // TODO: move me - - protected override void OnMouseMove(MouseEventArgs e) - { - _previousX = _currentX ?? 0; - _currentX = e.X; - _currentY = e.Y; - - if (_columnResizing != null) - { - if (_currentX != _previousX) - { - _columnResizing.Width += _currentX - _previousX; - _columns.ColumnsChanged(); - Refresh(); - } - } - else if (_columnDown != null) - { - _columnDownMoved = true; - } - - Cell newCell = CalculatePointedCell(_currentX.Value, _currentY.Value); - - // SuuperW: Hide lag frames - if (QueryFrameLag != null && newCell.RowIndex.HasValue) - { - newCell.RowIndex += CountLagFramesDisplay(newCell.RowIndex.Value); - } - - newCell.RowIndex += FirstVisibleRow; - if (newCell.RowIndex < 0) - { - newCell.RowIndex = 0; - } - - if (!newCell.Equals(CurrentCell)) - { - CellChanged(newCell); - - if (IsHoveringOnColumnCell || - (WasHoveringOnColumnCell && !IsHoveringOnColumnCell)) - { - Refresh(); - } - else if (_columnDown != null) - { - Refresh(); - } - } - else if (_columnDown != null) // Kind of silly feeling to have this check twice, but the only alternative I can think of has it refreshing twice when pointed column changes with column down, and speed matters - { - Refresh(); - } - - Cursor = IsHoveringOnColumnEdge || _columnResizing != null - ? Cursors.VSplit - : Cursors.Default; - - base.OnMouseMove(e); - } - - protected override void OnMouseEnter(EventArgs e) - { - CurrentCell = new Cell - { - Column = null, - RowIndex = null - }; - - base.OnMouseEnter(e); - } - - protected override void OnMouseLeave(EventArgs e) - { - _currentX = null; - _currentY = null; - CurrentCell = null; - IsPaintDown = false; - _columnResizing = null; - _hoverTimer.Stop(); - Refresh(); - base.OnMouseLeave(e); - } - - // TODO add query callback of whether to select the cell or not - protected override void OnMouseDown(MouseEventArgs e) - { - if (!GlobalWin.MainForm.EmulatorPaused && _currentX.HasValue) - { - // copypaste from OnMouseMove() - Cell newCell = CalculatePointedCell(_currentX.Value, _currentY.Value); - if (QueryFrameLag != null && newCell.RowIndex.HasValue) - { - newCell.RowIndex += CountLagFramesDisplay(newCell.RowIndex.Value); - } - - newCell.RowIndex += FirstVisibleRow; - if (newCell.RowIndex < 0) - { - newCell.RowIndex = 0; - } - - if (!newCell.Equals(CurrentCell)) - { - CellChanged(newCell); - - if (IsHoveringOnColumnCell || - (WasHoveringOnColumnCell && !IsHoveringOnColumnCell)) - { - Refresh(); - } - else if (_columnDown != null) - { - Refresh(); - } - } - else if (_columnDown != null) - { - Refresh(); - } - } - - if (e.Button == MouseButtons.Left) - { - if (IsHoveringOnColumnEdge) - { - _columnResizing = CurrentCell.Column; - } - if (IsHoveringOnColumnCell) - { - _columnDown = CurrentCell.Column; - } - else if (InputPaintingMode) - { - IsPaintDown = true; - } - } - - if (e.Button == MouseButtons.Right) - { - if (!IsHoveringOnColumnCell) - { - RightButtonHeld = true; - } - } - - if (e.Button == MouseButtons.Left) - { - if (IsHoveringOnDataCell) - { - if (ModifierKeys == Keys.Alt) - { - // do marker drag here - } - else if (ModifierKeys == Keys.Shift && (CurrentCell.Column.Name == "FrameColumn" || CurrentCell.Column.Type == RollColumn.InputType.Text)) - { - if (_selectedItems.Any()) - { - if (FullRowSelect) - { - var selected = _selectedItems.Any(c => c.RowIndex.HasValue && CurrentCell.RowIndex.HasValue && c.RowIndex == CurrentCell.RowIndex); - - if (!selected) - { - var rowIndices = _selectedItems - .Where(c => c.RowIndex.HasValue) - .Select(c => c.RowIndex ?? -1) - .Where(c => c >= 0) // Hack to avoid possible Nullable exceptions - .Distinct() - .ToList(); - - var firstIndex = rowIndices.Min(); - var lastIndex = rowIndices.Max(); - - if (CurrentCell.RowIndex.Value < firstIndex) - { - for (int i = CurrentCell.RowIndex.Value; i < firstIndex; i++) - { - SelectCell(new Cell - { - RowIndex = i, - Column = CurrentCell.Column - }); - } - } - else if (CurrentCell.RowIndex.Value > lastIndex) - { - for (int i = lastIndex + 1; i <= CurrentCell.RowIndex.Value; i++) - { - SelectCell(new Cell - { - RowIndex = i, - Column = CurrentCell.Column - }); - } - } - else // Somewhere in between, a scenario that can happen with ctrl-clicking, find the previous and highlight from there - { - var nearest = rowIndices - .Where(x => x < CurrentCell.RowIndex.Value) - .Max(); - - for (int i = nearest + 1; i <= CurrentCell.RowIndex.Value; i++) - { - SelectCell(new Cell - { - RowIndex = i, - Column = CurrentCell.Column - }); - } - } - } - } - else - { - MessageBox.Show("Shift click logic for individual cells has not yet implemented"); - } - } - else - { - SelectCell(CurrentCell); - } - } - else if (ModifierKeys == Keys.Control && (CurrentCell.Column.Name == "FrameColumn" || CurrentCell.Column.Type == RollColumn.InputType.Text)) - { - SelectCell(CurrentCell, toggle: true); - } - else if (ModifierKeys != Keys.Shift) - { - var hadIndex = _selectedItems.Any(); - _selectedItems.Clear(); - SelectCell(CurrentCell); - } - - Refresh(); - - SelectedIndexChanged?.Invoke(this, new EventArgs()); - } - } - - base.OnMouseDown(e); - - if (AllowRightClickSelecton && e.Button == MouseButtons.Right) - { - if (!IsHoveringOnColumnCell && CurrentCell != null) - { - _currentX = e.X; - _currentY = e.Y; - Cell newCell = CalculatePointedCell(_currentX.Value, _currentY.Value); - newCell.RowIndex += FirstVisibleRow; - CellChanged(newCell); - SelectCell(CurrentCell); - } - } - } - - protected override void OnMouseUp(MouseEventArgs e) - { - if (_columnResizing == null && IsHoveringOnColumnCell) - { - if (_columnDown != null && _columnDownMoved) - { - DoColumnReorder(); - _columnDown = null; - Refresh(); - } - else if (e.Button == MouseButtons.Left) - { - ColumnClickEvent(ColumnAtX(e.X)); - } - else if (e.Button == MouseButtons.Right) - { - ColumnRightClickEvent(ColumnAtX(e.X)); - } - } - - _columnResizing = null; - _columnDown = null; - _columnDownMoved = false; - RightButtonHeld = false; - IsPaintDown = false; - base.OnMouseUp(e); - } - - private void IncrementScrollBar(ScrollBar bar, bool increment) - { - int newVal; - if (increment) - { - newVal = bar.Value + bar.SmallChange; - if (newVal > bar.Maximum - bar.LargeChange) - { - newVal = bar.Maximum - bar.LargeChange; - } - } - else - { - newVal = bar.Value - bar.SmallChange; - if (newVal < 0) - { - newVal = 0; - } - } - - _programmaticallyUpdatingScrollBarValues = true; - bar.Value = newVal; - _programmaticallyUpdatingScrollBarValues = false; - } - - protected override void OnMouseWheel(MouseEventArgs e) - { - if (RightButtonHeld) - { - DoRightMouseScroll(this, e); - } - else - { - if (HorizontalOrientation) - { - do - { - IncrementScrollBar(_hBar, e.Delta < 0); - SetLagFramesFirst(); - } - while (_lagFrames[0] != 0 && _hBar.Value != 0 && _hBar.Value != _hBar.Maximum); - } - else - { - do - { - IncrementScrollBar(_vBar, e.Delta < 0); - SetLagFramesFirst(); - } - while (_lagFrames[0] != 0 && _vBar.Value != 0 && _vBar.Value != _vBar.Maximum); - } - - if (_currentX != null) - { - OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, _currentX.Value, _currentY.Value, 0)); - } - - Refresh(); - } - } - - private void DoRightMouseScroll(object sender, MouseEventArgs e) - { - RightMouseScrolled?.Invoke(sender, e); - } - - private void ColumnClickEvent(RollColumn column) - { - ColumnClick?.Invoke(this, new ColumnClickEventArgs(column)); - } - - private void ColumnRightClickEvent(RollColumn column) - { - ColumnRightClick?.Invoke(this, new ColumnClickEventArgs(column)); - } - - protected override void OnKeyDown(KeyEventArgs e) - { - if (!SuspendHotkeys) - { - if (e.Control && !e.Alt && e.Shift && e.KeyCode == Keys.F) // Ctrl+Shift+F - { - HorizontalOrientation ^= true; - } - // Scroll - else if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.PageUp) // Page Up - { - if (FirstVisibleRow > 0) - { - LastVisibleRow = FirstVisibleRow; - Refresh(); - } - } - else if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.PageDown) // Page Down - { - var totalRows = LastVisibleRow - FirstVisibleRow; - if (totalRows <= RowCount) - { - var final = LastVisibleRow + totalRows; - if (final > RowCount) - { - final = RowCount; - } - - LastVisibleRow = final; - Refresh(); - } - } - else if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.Home) // Home - { - FirstVisibleRow = 0; - Refresh(); - } - else if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.End) // End - { - LastVisibleRow = RowCount; - Refresh(); - } - else if (!e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Up) // Up - { - if (FirstVisibleRow > 0) - { - FirstVisibleRow--; - Refresh(); - } - } - else if (!e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Down) // Down - { - if (FirstVisibleRow < RowCount - 1) - { - FirstVisibleRow++; - Refresh(); - } - } - // Selection courser - else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Up) // Ctrl + Up - { - if (SelectedRows.Any() && LetKeysModifySelection && SelectedRows.First() > 0) - { - foreach (var row in SelectedRows.ToList()) // clones SelectedRows - { - SelectRow(row - 1, true); - SelectRow(row, false); - } - } - } - else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Down) // Ctrl + Down - { - if (SelectedRows.Any() && LetKeysModifySelection) - { - foreach (var row in SelectedRows.Reverse()) // clones SelectedRows - { - SelectRow(row + 1, true); - SelectRow(row, false); - } - } - } - else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Left) // Ctrl + Left - { - if (SelectedRows.Any() && LetKeysModifySelection) - { - SelectRow(SelectedRows.Last(), false); - } - } - else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Right) // Ctrl + Right - { - if (SelectedRows.Any() && LetKeysModifySelection && SelectedRows.Last() < _rowCount - 1) - { - SelectRow(SelectedRows.Last() + 1, true); - } - } - else if (e.Control && e.Shift && !e.Alt && e.KeyCode == Keys.Left) // Ctrl + Shift + Left - { - if (SelectedRows.Any() && LetKeysModifySelection && SelectedRows.First() > 0) - { - SelectRow(SelectedRows.First() - 1, true); - } - } - else if (e.Control && e.Shift && !e.Alt && e.KeyCode == Keys.Right) // Ctrl + Shift + Right - { - if (SelectedRows.Any() && LetKeysModifySelection) - { - SelectRow(SelectedRows.First(), false); - } - } - else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.PageUp) // Ctrl + Page Up - { - //jump to above marker with selection courser - if (LetKeysModifySelection) - { - - } - } - else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.PageDown) // Ctrl + Page Down - { - //jump to below marker with selection courser - if (LetKeysModifySelection) - { - - } - - } - else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Home) // Ctrl + Home - { - //move selection courser to frame 0 - if (LetKeysModifySelection) - { - DeselectAll(); - SelectRow(0, true); - } - } - else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.End) // Ctrl + End - { - //move selection courser to end of movie - if (LetKeysModifySelection) - { - DeselectAll(); - SelectRow(RowCount-1, true); - } - } - - } - - base.OnKeyDown(e); - } - - #endregion - - #region Change Events - - protected override void OnResize(EventArgs e) - { - RecalculateScrollBars(); - base.OnResize(e); - Refresh(); - } - - private void OrientationChanged() - { - RecalculateScrollBars(); - - // TODO scroll to correct positions - ColumnChangedCallback(); - RecalculateScrollBars(); - - Refresh(); - } - - /// - /// Call this function to change the CurrentCell to newCell - /// - private void CellChanged(Cell newCell) - { - LastCell = CurrentCell; - CurrentCell = newCell; - - if (PointedCellChanged != null && - (LastCell.Column != CurrentCell.Column || LastCell.RowIndex != CurrentCell.RowIndex)) - { - PointedCellChanged(this, new CellEventArgs(LastCell, CurrentCell)); - } - - if (CurrentCell?.Column != null && CurrentCell.RowIndex.HasValue) - { - _hoverTimer.Start(); - } - else - { - _hoverTimer.Stop(); - } - } - - private void VerticalBar_ValueChanged(object sender, EventArgs e) - { - if (!_programmaticallyUpdatingScrollBarValues) - { - Refresh(); - } - - if (_horizontalOrientation) - { - ColumnScroll?.Invoke(_vBar, e); - } - else - { - RowScroll?.Invoke(_vBar, e); - } - } - - private void HorizontalBar_ValueChanged(object sender, EventArgs e) - { - if (!_programmaticallyUpdatingScrollBarValues) - { - Refresh(); - } - - if (_horizontalOrientation) - { - RowScroll?.Invoke(_hBar, e); - } - else - { - ColumnScroll?.Invoke(_vBar, e); - } - } - - private void ColumnChangedCallback() - { - RecalculateScrollBars(); - if (_columns.VisibleColumns.Any()) - { - ColumnWidth = _columns.VisibleColumns.Max(c => c.Width.Value) + CellWidthPadding * 4; - } - } - - #endregion - - #region Helpers - - private void DoColumnReorder() - { - if (_columnDown != CurrentCell.Column) - { - var oldIndex = _columns.IndexOf(_columnDown); - var newIndex = _columns.IndexOf(CurrentCell.Column); - - ColumnReordered?.Invoke(this, new ColumnReorderedEventArgs(oldIndex, newIndex, _columnDown)); - - _columns.Remove(_columnDown); - _columns.Insert(newIndex, _columnDown); - } - } - - // ScrollBar.Maximum = DesiredValue + ScrollBar.LargeChange - 1 - // See MSDN Page for more information on the dumb ScrollBar.Maximum Property - private void RecalculateScrollBars() - { - UpdateDrawSize(); - - var columns = _columns.VisibleColumns.ToList(); - - if (HorizontalOrientation) - { - NeedsVScrollbar = columns.Count > DrawHeight / CellHeight; - NeedsHScrollbar = RowCount > 1; - } - else - { - NeedsVScrollbar = ColumnHeight + (RowCount * RowHeight) > Height; - NeedsHScrollbar = TotalColWidth.HasValue && TotalColWidth.Value - DrawWidth + 1 > 0; - } - - UpdateDrawSize(); - if (VisibleRows > 0) - { - if (HorizontalOrientation) - { - _vBar.LargeChange = DrawHeight / 2; - _hBar.Maximum = Math.Max((VisibleRows - 1) * CellHeight, _hBar.Maximum); - _hBar.LargeChange = (VisibleRows - 1) * CellHeight; - } - else - { - _vBar.Maximum = Math.Max((VisibleRows - 1) * CellHeight, _vBar.Maximum); // ScrollBar.Maximum is dumb - _vBar.LargeChange = (VisibleRows - 1) * CellHeight; - // DrawWidth can be negative if the TAStudio window is small enough - // Clamp LargeChange to 0 here to prevent exceptions - _hBar.LargeChange = Math.Max(0, DrawWidth / 2); - } - } - - // Update VBar - if (NeedsVScrollbar) - { - if (HorizontalOrientation) - { - _vBar.Maximum = ((columns.Count() * CellHeight) - DrawHeight) + _vBar.LargeChange; - if (_vBar.Maximum < 0) - { - _vBar.Maximum = 0; - } - } - else - { - _vBar.Maximum = RowsToPixels(RowCount + 1) - (CellHeight * 3) + _vBar.LargeChange - 1; - - if (_vBar.Maximum < 0) - { - _vBar.Maximum = 0; - } - } - - _vBar.Location = new Point(Width - _vBar.Width, 0); - _vBar.Height = Height; - _vBar.Visible = true; - } - else - { - _vBar.Visible = false; - _vBar.Value = 0; - } - - // Update HBar - if (NeedsHScrollbar) - { - if (HorizontalOrientation) - { - _hBar.Maximum = RowsToPixels(RowCount + 1) - (CellHeight * 3) + _hBar.LargeChange - 1; - } - else - { - _hBar.Maximum = TotalColWidth.Value - DrawWidth + _hBar.LargeChange; - } - - _hBar.Location = new Point(0, Height - _hBar.Height); - _hBar.Width = Width - (NeedsVScrollbar ? (_vBar.Width + 1) : 0); - _hBar.Visible = true; - } - else - { - _hBar.Visible = false; - _hBar.Value = 0; - } - } - - private void UpdateDrawSize() - { - if (NeedsVScrollbar) - { - DrawWidth = Width - _vBar.Width; - } - else - { - DrawWidth = Width; - } - if (NeedsHScrollbar) - { - DrawHeight = Height - _hBar.Height; - } - else - { - DrawHeight = Height; - } - } - - /// - /// If FullRowSelect is enabled, selects all cells in the row that contains the given cell. Otherwise only given cell is added. - /// - /// The cell to select. - private void SelectCell(Cell cell, bool toggle = false) - { - if (cell.RowIndex.HasValue && cell.RowIndex < RowCount) - { - if (!MultiSelect) - { - _selectedItems.Clear(); - } - - if (FullRowSelect) - { - if (toggle && _selectedItems.Any(x => x.RowIndex.HasValue && x.RowIndex == cell.RowIndex)) - { - _selectedItems.RemoveWhere(x => x.RowIndex.HasValue && x.RowIndex == cell.RowIndex); - } - else - { - foreach (var column in _columns) - { - _selectedItems.Add(new Cell - { - RowIndex = cell.RowIndex, - Column = column - }); - } - } - } - else - { - if (toggle && _selectedItems.Any(x => x.RowIndex.HasValue && x.RowIndex == cell.RowIndex)) - { - var item = _selectedItems - .FirstOrDefault(x => x.Equals(cell)); - - if (item != null) - { - _selectedItems.Remove(item); - } - } - else - { - _selectedItems.Add(CurrentCell); - } - } - } - } - - private bool IsHoveringOnColumnCell => CurrentCell?.Column != null && !CurrentCell.RowIndex.HasValue; - - private bool IsHoveringOnColumnEdge => AllowColumnResize && IsHoveringOnColumnCell && IsPointingOnCellEdge(_currentX); - - private bool IsHoveringOnDataCell => CurrentCell?.Column != null && CurrentCell.RowIndex.HasValue; - - private bool WasHoveringOnColumnCell => LastCell?.Column != null && !LastCell.RowIndex.HasValue; - - private bool WasHoveringOnDataCell => LastCell?.Column != null && LastCell.RowIndex.HasValue; - - private bool IsPointingOnCellEdge(int? x) - { - if (x.HasValue) - { - if (HorizontalOrientation) - { - return false; // TODO: support column resize in horizontal orientation - } - - foreach (RollColumn column in _columns.VisibleColumns) - { - if (column.Left - _hBar.Value + (column.Width - column.Width / 6) <= x.Value && column.Right - _hBar.Value >= x.Value) - { - return true; - } - } - } - - return false; - } - - /// - /// Finds the specific cell that contains the (x, y) coordinate. - /// - /// The row number that it returns will be between 0 and VisibleRows, NOT the absolute row number. - /// X coordinate point. - /// Y coordinate point. - /// The cell with row number and RollColumn reference, both of which can be null. - private Cell CalculatePointedCell(int x, int y) - { - var newCell = new Cell(); - var columns = _columns.VisibleColumns.ToList(); - - // If pointing to a column header - if (columns.Any()) - { - if (HorizontalOrientation) - { - newCell.RowIndex = PixelsToRows(x); - - int colIndex = (y + _vBar.Value) / CellHeight; - if (colIndex >= 0 && colIndex < columns.Count) - { - newCell.Column = columns[colIndex]; - } - } - else - { - newCell.RowIndex = PixelsToRows(y); - newCell.Column = ColumnAtX(x); - } - } - - if (!(IsPaintDown || RightButtonHeld) && newCell.RowIndex <= -1) // -2 if we're entering from the top - { - newCell.RowIndex = null; - } - - return newCell; - } - - // A boolean that indicates if the InputRoll is too large vertically and requires a vertical scrollbar. - private bool NeedsVScrollbar { get; set; } - - // A boolean that indicates if the InputRoll is too large horizontally and requires a horizontal scrollbar. - private bool NeedsHScrollbar { get; set; } - - /// - /// Gets the total width of all the columns by using the last column's Right property. - /// - /// A nullable Int representing total width. - private int? TotalColWidth - { - get - { - if (_columns.VisibleColumns.Any()) - { - return _columns.VisibleColumns.Last().Right; - } - - return null; - } - } - - /// - /// Returns the RollColumn object at the specified visible x coordinate. Coordinate should be between 0 and Width of the InputRoll Control. - /// - /// The x coordinate. - /// RollColumn object that contains the x coordinate or null if none exists. - private RollColumn ColumnAtX(int x) - { - foreach (RollColumn column in _columns.VisibleColumns) - { - if (column.Left.Value - _hBar.Value <= x && column.Right.Value - _hBar.Value >= x) - { - return column; - } - } - - return null; - } - - /// - /// Converts a row number to a horizontal or vertical coordinate. - /// - /// A vertical coordinate if Vertical Oriented, otherwise a horizontal coordinate. - private int RowsToPixels(int index) - { - if (_horizontalOrientation) - { - return (index * CellWidth) + ColumnWidth; - } - - return (index * CellHeight) + ColumnHeight; - } - - /// - /// Converts a horizontal or vertical coordinate to a row number. - /// - /// A vertical coordinate if Vertical Oriented, otherwise a horizontal coordinate. - /// A row number between 0 and VisibleRows if it is a Datarow, otherwise a negative number if above all Datarows. - private int PixelsToRows(int pixels) - { - // Using Math.Floor and float because integer division rounds towards 0 but we want to round down. - if (_horizontalOrientation) - { - return (int)Math.Floor((float)(pixels - ColumnWidth) / CellWidth); - } - return (int)Math.Floor((float)(pixels - ColumnHeight) / CellHeight); - } - - // The width of the largest column cell in Horizontal Orientation - - private int ColumnWidth { get; set; } - - // The height of a column cell in Vertical Orientation. - private int ColumnHeight { get; set; } - - // The width of a cell in Horizontal Orientation. Only can be changed by changing the Font or CellPadding. - private int CellWidth { get; set; } - - [Browsable(false)] - public int RowHeight => CellHeight; - - /// - /// Gets or sets a value indicating the height of a cell in Vertical Orientation. Only can be changed by changing the Font or CellPadding. - /// - private int CellHeight { get; set; } = 8; - - /// - /// Call when _charSize, MaxCharactersInHorizontal, or CellPadding is changed. - /// - private void UpdateCellSize() - { - CellHeight = _charSize.Height + (CellHeightPadding * 2); - CellWidth = (_charSize.Width * MaxCharactersInHorizontal) + (CellWidthPadding * 4); // Double the padding for horizontal because it looks better - } - - // SuuperW: Count lag frames between FirstDisplayed and given display position - private int CountLagFramesDisplay(int relativeIndex) - { - if (QueryFrameLag != null && LagFramesToHide != 0) - { - int count = 0; - for (int i = 0; i <= relativeIndex; i++) - { - count += _lagFrames[i]; - } - - return count; - } - - return 0; - } - - // Count lag frames between FirstDisplayed and given relative frame index - private int CountLagFramesAbsolute(int relativeIndex) - { - if (QueryFrameLag != null && LagFramesToHide != 0) - { - int count = 0; - for (int i = 0; i + count <= relativeIndex; i++) - { - count += _lagFrames[i]; - } - - return count; - } - - return 0; - } - - private void SetLagFramesArray() - { - if (QueryFrameLag != null && LagFramesToHide != 0) - { - bool showNext = false; - - // First one needs to check BACKWARDS for lag frame count. - SetLagFramesFirst(); - int f = _lagFrames[0]; - if (QueryFrameLag(FirstVisibleRow + f, HideWasLagFrames)) - { - showNext = true; - } - - for (int i = 1; i <= VisibleRows; i++) - { - _lagFrames[i] = 0; - if (!showNext) - { - for (; _lagFrames[i] < LagFramesToHide; _lagFrames[i]++) - { - if (!QueryFrameLag(FirstVisibleRow + i + f, HideWasLagFrames)) - { - break; - } - - f++; - } - } - else - { - if (!QueryFrameLag(FirstVisibleRow + i + f, HideWasLagFrames)) - { - showNext = false; - } - } - - if (_lagFrames[i] == LagFramesToHide && QueryFrameLag(FirstVisibleRow + i + f, HideWasLagFrames)) - { - showNext = true; - } - } - } - else - { - for (int i = 0; i <= VisibleRows; i++) - { - _lagFrames[i] = 0; - } - } - } - private void SetLagFramesFirst() - { - if (QueryFrameLag != null && LagFramesToHide != 0) - { - // Count how many lag frames are above displayed area. - int count = 0; - do - { - count++; - } - while (QueryFrameLag(FirstVisibleRow - count, HideWasLagFrames) && count <= LagFramesToHide); - count--; - - // Count forward - int fCount = -1; - do - { - fCount++; - } - while (QueryFrameLag(FirstVisibleRow + fCount, HideWasLagFrames) && count + fCount < LagFramesToHide); - _lagFrames[0] = (byte)fCount; - } - else - { - _lagFrames[0] = 0; - } - } - - // Number of displayed + hidden frames, if fps is as expected - private int ExpectedDisplayRange() - { - return (VisibleRows + 1) * LagFramesToHide; - } - - #endregion - - #region Classes - - public class RollColumns : List - { - public RollColumn this[string name] - { - get - { - return this.SingleOrDefault(column => column.Name == name); - } - } - - public IEnumerable VisibleColumns - { - get - { - return this.Where(c => c.Visible); - } - } - - public Action ChangedCallback { get; set; } - - private void DoChangeCallback() - { - // no check will make it crash for user too, not sure which way of alarm we prefer. no alarm at all will cause all sorts of subtle bugs - if (ChangedCallback == null) - { - System.Diagnostics.Debug.Fail($"{nameof(ColumnChangedCallback)} has died!"); - } - else - { - ChangedCallback(); - } - } - - // TODO: this shouldn't be exposed. But in order to not expose it, each RollColumn must have a change callback, and all property changes must call it, it is quicker and easier to just call this when needed - public void ColumnsChanged() - { - int pos = 0; - - foreach (var col in VisibleColumns) - { - col.Left = pos; - pos += col.Width.Value; - col.Right = pos; - } - - DoChangeCallback(); - } - - public new void Add(RollColumn column) - { - if (this.Any(c => c.Name == column.Name)) - { - // The designer sucks, doing nothing for now - return; - //throw new InvalidOperationException("A column with this name already exists."); - } - - base.Add(column); - ColumnsChanged(); - } - - public new void AddRange(IEnumerable collection) - { - foreach (var column in collection) - { - if (this.Any(c => c.Name == column.Name)) - { - // The designer sucks, doing nothing for now - return; - - throw new InvalidOperationException("A column with this name already exists."); - } - } - - base.AddRange(collection); - ColumnsChanged(); - } - - public new void Insert(int index, RollColumn column) - { - if (this.Any(c => c.Name == column.Name)) - { - throw new InvalidOperationException("A column with this name already exists."); - } - - base.Insert(index, column); - ColumnsChanged(); - } - - public new void InsertRange(int index, IEnumerable collection) - { - foreach (var column in collection) - { - if (this.Any(c => c.Name == column.Name)) - { - throw new InvalidOperationException("A column with this name already exists."); - } - } - - base.InsertRange(index, collection); - ColumnsChanged(); - } - - public new bool Remove(RollColumn column) - { - var result = base.Remove(column); - ColumnsChanged(); - return result; - } - - public new int RemoveAll(Predicate match) - { - var result = base.RemoveAll(match); - ColumnsChanged(); - return result; - } - - public new void RemoveAt(int index) - { - base.RemoveAt(index); - ColumnsChanged(); - } - - public new void RemoveRange(int index, int count) - { - base.RemoveRange(index, count); - ColumnsChanged(); - } - - public new void Clear() - { - base.Clear(); - ColumnsChanged(); - } - - public IEnumerable Groups - { - get - { - return this - .Select(x => x.Group) - .Distinct(); - } - } - } - - public class RollColumn - { - public enum InputType { Boolean, Float, Text, Image } - - public string Group { get; set; } - public int? Width { get; set; } - public int? Left { get; set; } - public int? Right { get; set; } - public string Name { get; set; } - public string Text { get; set; } - public InputType Type { get; set; } - public bool Visible { get; set; } - - /// - /// Column will be drawn with an emphasized look, if true - /// - private bool _emphasis; - public bool Emphasis - { - get { return _emphasis; } - set { _emphasis = value; } - } - - public RollColumn() - { - Visible = true; - } - } - - /// - /// Represents a single cell of the - /// - public class Cell - { - public RollColumn Column { get; internal set; } - public int? RowIndex { get; internal set; } - public string CurrentText { get; internal set; } - - public Cell() { } - - public Cell(Cell cell) - { - Column = cell.Column; - RowIndex = cell.RowIndex; - } - - public bool IsDataCell => Column != null && RowIndex.HasValue; - - public override bool Equals(object obj) - { - if (obj is Cell) - { - var cell = obj as Cell; - return this.Column == cell.Column && this.RowIndex == cell.RowIndex; - } - - return base.Equals(obj); - } - - public override int GetHashCode() - { - return Column.GetHashCode() + RowIndex.GetHashCode(); - } - } - - private class SortCell : IComparer - { - int IComparer.Compare(Cell a, Cell b) - { - Cell c1 = a as Cell; - Cell c2 = b as Cell; - if (c1.RowIndex.HasValue) - { - if (c2.RowIndex.HasValue) - { - int row = c1.RowIndex.Value.CompareTo(c2.RowIndex.Value); - if (row == 0) - { - return c1.Column.Name.CompareTo(c2.Column.Name); - } - - return row; - } - - return 1; - } - - if (c2.RowIndex.HasValue) - { - return -1; - } - - return c1.Column.Name.CompareTo(c2.Column.Name); - } - } - - #endregion - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Client.EmuHawk.CustomControls; +using BizHawk.Common; + +namespace BizHawk.Client.EmuHawk +{ + // Row width depends on font size and padding + // Column width is specified in column headers + // Row width is specified for horizontal orientation + public partial class InputRoll : Control + { + private readonly IControlRenderer _renderer; + private readonly SortedSet _selectedItems = new SortedSet(new SortCell()); + + // scrollbar location(s) are calculated later (e.g. on resize) + private readonly VScrollBar _vBar = new VScrollBar { Visible = false }; + private readonly HScrollBar _hBar = new HScrollBar { Visible = false }; + + private readonly Timer _hoverTimer = new Timer(); + private readonly byte[] _lagFrames = new byte[256]; // Large enough value that it shouldn't ever need resizing. // apparently not large enough for 4K + + private readonly Color _foreColor; + private readonly Color _backColor; + + private RollColumns _columns = new RollColumns(); + private bool _horizontalOrientation; + private bool _programmaticallyUpdatingScrollBarValues; + private int _maxCharactersInHorizontal = 1; + + private int _rowCount; + private Size _charSize; + + private RollColumn _columnDown; + private RollColumn _columnResizing; + + private int? _currentX; + private int? _currentY; + + // Hiding lag frames (Mainly intended for < 60fps play.) + public int LagFramesToHide { get; set; } + public bool HideWasLagFrames { get; set; } + + public bool AllowRightClickSelecton { get; set; } + public bool LetKeysModifySelection { get; set; } + public bool SuspendHotkeys { get; set; } + + private Font _font = new Font("Arial", 8, FontStyle.Bold); + + public InputRoll() + { + UseCustomBackground = true; + GridLines = true; + CellWidthPadding = 3; + CellHeightPadding = 0; + CurrentCell = null; + ScrollMethod = "near"; + + SetStyle(ControlStyles.AllPaintingInWmPaint, true); + SetStyle(ControlStyles.UserPaint, true); + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + SetStyle(ControlStyles.Opaque, true); + SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + + if (OSTailoredCode.CurrentOS == OSTailoredCode.DistinctOS.Windows) + { + _renderer = new GdiRenderer(); + } + else + { + _renderer = new GdiPlusRenderer(); + } + + using (var g = CreateGraphics()) + using (_renderer.LockGraphics(g, Width, Height)) + { + _charSize = _renderer.MeasureString("A", _font); // TODO make this a property so changing it updates other values. + } + + UpdateCellSize(); + ColumnWidth = CellWidth; + ColumnHeight = CellHeight + 2; + + _vBar.SmallChange = CellHeight; + _vBar.LargeChange = CellHeight * 20; + + _hBar.SmallChange = CellWidth; + _hBar.LargeChange = 20; + + Controls.Add(_vBar); + Controls.Add(_hBar); + + _vBar.ValueChanged += VerticalBar_ValueChanged; + _hBar.ValueChanged += HorizontalBar_ValueChanged; + + RecalculateScrollBars(); + _columns.ChangedCallback = ColumnChangedCallback; + + _hoverTimer.Interval = 750; + _hoverTimer.Tick += HoverTimerEventProcessor; + _hoverTimer.Stop(); + + _foreColor = ForeColor; + _backColor = BackColor; + } + + private void HoverTimerEventProcessor(object sender, EventArgs e) + { + _hoverTimer.Stop(); + + CellHovered?.Invoke(this, new CellEventArgs(LastCell, CurrentCell)); + } + + protected override void Dispose(bool disposing) + { + _renderer.Dispose(); + base.Dispose(disposing); + } + + protected override void OnDoubleClick(EventArgs e) + { + if (IsHoveringOnColumnEdge) + { + if (HorizontalOrientation) + { + // TODO + } + else + { + var maxLength = CurrentCell.Column.Text?.Length ?? 0; + + + for (int i = 0; i < RowCount; i++) + { + string text = ""; + int offSetX = 0, offSetY = 0; + QueryItemText?.Invoke(i, CurrentCell.Column, out text, ref offSetX, ref offSetY); + if (text.Length > maxLength) + { + maxLength = text.Length; + } + } + + var newWidth = (maxLength * _charSize.Width) + (CellWidthPadding * 2); + CurrentCell.Column.Width = newWidth; + _columns.ColumnsChanged(); + Refresh(); + } + + } + + base.OnDoubleClick(e); + } + + #region Properties + + /// + /// Gets or sets the amount of left and right padding on the text inside a cell + /// + [DefaultValue(3)] + [Category("Behavior")] + public int CellWidthPadding { get; set; } + + /// + /// Gets or sets the amount of top and bottom padding on the text inside a cell + /// + [DefaultValue(1)] + [Category("Behavior")] + public int CellHeightPadding { get; set; } + + /// + /// Gets or sets a value indicating whether grid lines are displayed around cells + /// + [Category("Appearance")] + [DefaultValue(true)] + public bool GridLines { get; set; } + + /// + /// Gets or sets a value indicating whether the control is horizontal or vertical + /// + [Category("Behavior")] + public bool HorizontalOrientation + { + get + { + return _horizontalOrientation; + } + set + { + if (_horizontalOrientation != value) + { + int temp = ScrollSpeed; + _horizontalOrientation = value; + OrientationChanged(); + _hBar.SmallChange = CellWidth; + _vBar.SmallChange = CellHeight; + ScrollSpeed = temp; + } + } + } + + /// + /// Gets or sets the scrolling speed + /// + [Category("Behavior")] + public int ScrollSpeed + { + get + { + if (HorizontalOrientation) + { + return _hBar.SmallChange / CellWidth; + } + + return _vBar.SmallChange / CellHeight; + } + + set + { + if (HorizontalOrientation) + { + _hBar.SmallChange = value * CellWidth; + } + else + { + _vBar.SmallChange = value * CellHeight; + } + } + } + + /// + /// Gets or sets the sets the virtual number of rows to be displayed. Does not include the column header row. + /// + [Category("Behavior")] + public int RowCount + { + get + { + return _rowCount; + } + + set + { + _rowCount = value; + RecalculateScrollBars(); + } + } + + /// + /// Gets or sets a value indicating whether columns can be resized + /// + [Category("Behavior")] + public bool AllowColumnResize { get; set; } + + /// + /// Gets or sets a value indicating whether columns can be reordered + /// + [Category("Behavior")] + public bool AllowColumnReorder { get; set; } + + /// + /// Gets or sets a value indicating whether the entire row will always be selected + /// + [Category("Appearance")] + [DefaultValue(false)] + public bool FullRowSelect { get; set; } + + /// + /// Gets or sets a value indicating whether multiple items can to be selected + /// + [Category("Behavior")] + [DefaultValue(true)] + public bool MultiSelect { get; set; } + + /// + /// Gets or sets a value indicating whether the control is in input painting mode + /// + [Category("Behavior")] + [DefaultValue(false)] + public bool InputPaintingMode { get; set; } + + /// + /// All visible columns + /// + [Category("Behavior")] + public IEnumerable VisibleColumns => _columns.VisibleColumns; + + /// + /// Gets or sets how the InputRoll scrolls when calling ScrollToIndex. + /// + [DefaultValue("near")] + [Category("Behavior")] + public string ScrollMethod { get; set; } + + /// + /// Gets or sets a value indicating how the Intever for the hover event + /// + [Category("Behavior")] + public bool AlwaysScroll { get; set; } + + /// + /// Gets or sets the lowest seek interval to activate the progress bar + /// + [Category("Behavior")] + public int SeekingCutoffInterval { get; set; } + + /// + /// Returns all columns including those that are not visible + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public RollColumns AllColumns => _columns; + + [DefaultValue(750)] + [Category("Behavior")] + public int HoverInterval + { + get { return _hoverTimer.Interval; } + set { _hoverTimer.Interval = value; } + } + + #endregion + + #region Event Handlers + + /// + /// Fire the event which requests the text for the passed cell + /// + [Category("Virtual")] + public event QueryItemTextHandler QueryItemText; + + /// + /// Fire the event which requests the background color for the passed cell + /// + [Category("Virtual")] + public event QueryItemBkColorHandler QueryItemBkColor; + + [Category("Virtual")] + public event QueryRowBkColorHandler QueryRowBkColor; + + /// + /// Fire the event which requests an icon for a given cell + /// + [Category("Virtual")] + public event QueryItemIconHandler QueryItemIcon; + + /// + /// Fire the QueryFrameLag event which checks if a given frame is a lag frame + /// + [Category("Virtual")] + public event QueryFrameLagHandler QueryFrameLag; + + /// + /// Fires when the mouse moves from one cell to another (including column header cells) + /// + [Category("Mouse")] + public event CellChangeEventHandler PointedCellChanged; + + /// + /// Fires when a cell is hovered on + /// + [Category("Mouse")] + public event HoverEventHandler CellHovered; + + /// + /// Occurs when a column header is clicked + /// + [Category("Action")] + public event ColumnClickEventHandler ColumnClick; + + /// + /// Occurs when a column header is right-clicked + /// + [Category("Action")] + public event ColumnClickEventHandler ColumnRightClick; + + /// + /// Occurs whenever the 'SelectedItems' property for this control changes + /// + [Category("Behavior")] + public event EventHandler SelectedIndexChanged; + + /// + /// Occurs whenever the mouse wheel is scrolled while the right mouse button is held + /// + [Category("Behavior")] + public event RightMouseScrollEventHandler RightMouseScrolled; + + [Category("Property Changed")] + [Description("Occurs when the column header has been reordered")] + public event ColumnReorderedEventHandler ColumnReordered; + + [Category("Action")] + [Description("Occurs when the scroll value of the visible rows change (in vertical orientation this is the vertical scroll bar change, and in horizontal it is the horizontal scroll bar)")] + public event RowScrollEvent RowScroll; + + [Category("Action")] + [Description("Occurs when the scroll value of the columns (in vertical orientation this is the horizontal scroll bar change, and in horizontal it is the vertical scroll bar)")] + public event ColumnScrollEvent ColumnScroll; + + [Category("Action")] + [Description("Occurs when a cell is dragged and then dropped into a new cell, old cell is the cell that was being dragged, new cell is its new destination")] + public event CellDroppedEvent CellDropped; + + /// + /// Retrieve the text for a cell + /// + public delegate void QueryItemTextHandler(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY); + + /// + /// Retrieve the background color for a cell + /// + public delegate void QueryItemBkColorHandler(int index, RollColumn column, ref Color color); + public delegate void QueryRowBkColorHandler(int index, ref Color color); + + /// + /// Retrieve the image for a given cell + /// + public delegate void QueryItemIconHandler(int index, RollColumn column, ref Bitmap icon, ref int offsetX, ref int offsetY); + + /// + /// Check if a given frame is a lag frame + /// + public delegate bool QueryFrameLagHandler(int index, bool hideWasLag); + + public delegate void CellChangeEventHandler(object sender, CellEventArgs e); + + public delegate void HoverEventHandler(object sender, CellEventArgs e); + + public delegate void RightMouseScrollEventHandler(object sender, MouseEventArgs e); + + public delegate void ColumnClickEventHandler(object sender, ColumnClickEventArgs e); + + public delegate void ColumnReorderedEventHandler(object sender, ColumnReorderedEventArgs e); + + public delegate void RowScrollEvent(object sender, EventArgs e); + + public delegate void ColumnScrollEvent(object sender, EventArgs e); + + public delegate void CellDroppedEvent(object sender, CellEventArgs e); + + public class CellEventArgs + { + public CellEventArgs(Cell oldCell, Cell newCell) + { + OldCell = oldCell; + NewCell = newCell; + } + + public Cell OldCell { get; private set; } + public Cell NewCell { get; private set; } + } + + public class ColumnClickEventArgs + { + public ColumnClickEventArgs(RollColumn column) + { + Column = column; + } + + public RollColumn Column { get; private set; } + } + + public class ColumnReorderedEventArgs + { + public ColumnReorderedEventArgs(int oldDisplayIndex, int newDisplayIndex, RollColumn column) + { + Column = column; + OldDisplayIndex = oldDisplayIndex; + NewDisplayIndex = newDisplayIndex; + } + + public RollColumn Column { get; private set; } + public int OldDisplayIndex { get; private set; } + public int NewDisplayIndex { get; private set; } + } + + #endregion + + #region Api + + public void SelectRow(int index, bool val) + { + if (_columns.VisibleColumns.Any()) + { + if (val) + { + SelectCell(new Cell + { + RowIndex = index, + Column = _columns[0] + }); + } + else + { + IEnumerable items = _selectedItems.Where(cell => cell.RowIndex == index); + _selectedItems.RemoveWhere(items.Contains); + } + } + } + + public void SelectAll() + { + var oldFullRowVal = FullRowSelect; + FullRowSelect = true; + for (int i = 0; i < RowCount; i++) + { + SelectRow(i, true); + } + + FullRowSelect = oldFullRowVal; + } + + public void DeselectAll() + { + _selectedItems.Clear(); + } + + public void TruncateSelection(int index) + { + _selectedItems.RemoveWhere(cell => cell.RowIndex > index); + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool IsPointingAtColumnHeader => IsHoveringOnColumnCell; + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int? FirstSelectedIndex + { + get + { + if (AnyRowsSelected) + { + return SelectedRows.Min(); + } + + return null; + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int? LastSelectedIndex + { + get + { + if (AnyRowsSelected) + { + return SelectedRows.Max(); + } + + return null; + } + } + + /// + /// Gets or sets the current Cell that the mouse was in. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Cell CurrentCell { get; set; } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool CurrentCellIsDataCell => CurrentCell?.RowIndex != null && CurrentCell.Column != null; + + /// + /// Gets or sets the previous Cell that the mouse was in. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Cell LastCell { get; private set; } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool IsPaintDown { get; private set; } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool UseCustomBackground { get; set; } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int DrawHeight { get; private set; } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int DrawWidth { get; private set; } + + /// + /// Gets or sets the width of data cells when in Horizontal orientation. + /// + public int MaxCharactersInHorizontal + { + get + { + return _maxCharactersInHorizontal; + } + + set + { + _maxCharactersInHorizontal = value; + UpdateCellSize(); + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool RightButtonHeld { get; private set; } + + public string UserSettingsSerialized() + { + var settings = ConfigService.SaveWithType(Settings); + return settings; + } + + public void LoadSettingsSerialized(string settingsJson) + { + var settings = ConfigService.LoadWithType(settingsJson); + + // TODO: don't silently fail, inform the user somehow + if (settings is InputRollSettings) + { + var rollSettings = settings as InputRollSettings; + _columns = rollSettings.Columns; + _columns.ChangedCallback = ColumnChangedCallback; + HorizontalOrientation = rollSettings.HorizontalOrientation; + LagFramesToHide = rollSettings.LagFramesToHide; + HideWasLagFrames = rollSettings.HideWasLagFrames; + } + } + + private InputRollSettings Settings => new InputRollSettings + { + Columns = _columns, + HorizontalOrientation = HorizontalOrientation, + LagFramesToHide = LagFramesToHide, + HideWasLagFrames = HideWasLagFrames + }; + + public class InputRollSettings + { + public RollColumns Columns { get; set; } + public bool HorizontalOrientation { get; set; } + public int LagFramesToHide { get; set; } + public bool HideWasLagFrames { get; set; } + } + + /// + /// Gets or sets the first visible row index, if scrolling is needed + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int FirstVisibleRow + { + get // SuuperW: This was checking if the scroll bars were needed, which is useless because their Value is 0 if they aren't needed. + { + if (HorizontalOrientation) + { + return _hBar.Value / CellWidth; + } + + return _vBar.Value / CellHeight; + } + + set + { + if (HorizontalOrientation) + { + if (NeedsHScrollbar) + { + _programmaticallyUpdatingScrollBarValues = true; + if (value * CellWidth <= _hBar.Maximum) + { + _hBar.Value = value * CellWidth; + } + else + { + _hBar.Value = _hBar.Maximum; + } + + _programmaticallyUpdatingScrollBarValues = false; + } + } + else + { + if (NeedsVScrollbar) + { + _programmaticallyUpdatingScrollBarValues = true; + if (value * CellHeight <= _vBar.Maximum) + { + _vBar.Value = value * CellHeight; + } + else + { + _vBar.Value = _vBar.Maximum; + } + + _programmaticallyUpdatingScrollBarValues = false; + } + } + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + private int LastFullyVisibleRow + { + get + { + int halfRow = 0; + if ((DrawHeight - ColumnHeight - 3) % CellHeight < CellHeight / 2) + { + halfRow = 1; + } + + return FirstVisibleRow + VisibleRows - halfRow + CountLagFramesDisplay(VisibleRows - halfRow); + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int LastVisibleRow + { + get + { + return FirstVisibleRow + VisibleRows + CountLagFramesDisplay(VisibleRows); + } + + set + { + int halfRow = 0; + if ((DrawHeight - ColumnHeight - 3) % CellHeight < CellHeight / 2) + { + halfRow = 1; + } + + if (LagFramesToHide == 0) + { + FirstVisibleRow = Math.Max(value - (VisibleRows - halfRow), 0); + } + else + { + if (Math.Abs(LastFullyVisibleRow - value) > VisibleRows) // Big jump + { + FirstVisibleRow = Math.Max(value - (ExpectedDisplayRange() - halfRow), 0); + SetLagFramesArray(); + } + + // Small jump, more accurate + int lastVisible = LastFullyVisibleRow; + do + { + if ((lastVisible - value) / (LagFramesToHide + 1) != 0) + { + FirstVisibleRow = Math.Max(FirstVisibleRow - ((lastVisible - value) / (LagFramesToHide + 1)), 0); + } + else + { + FirstVisibleRow -= Math.Sign(lastVisible - value); + } + + SetLagFramesArray(); + lastVisible = LastFullyVisibleRow; + } + while ((lastVisible - value < 0 || lastVisible - value > _lagFrames[VisibleRows - halfRow]) && FirstVisibleRow != 0); + } + } + } + + public bool IsVisible(int index) + { + return (index >= FirstVisibleRow) && (index <= LastFullyVisibleRow); + } + + public bool IsPartiallyVisible(int index) + { + return index >= FirstVisibleRow && index <= LastVisibleRow; + } + + /// + /// Gets the number of rows currently visible including partially visible rows. + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int VisibleRows + { + get + { + if (HorizontalOrientation) + { + return (DrawWidth - ColumnWidth) / CellWidth; + } + + return (DrawHeight - ColumnHeight - 3) / CellHeight; // Minus three makes it work + } + } + + /// + /// Gets the first visible column index, if scrolling is needed + /// + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int FirstVisibleColumn + { + get + { + if (HorizontalOrientation) + { + return _vBar.Value / CellHeight; + } + + var columnList = VisibleColumns.ToList(); + return columnList.FindIndex(c => c.Right > _hBar.Value); + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int LastVisibleColumnIndex + { + get + { + List columnList = VisibleColumns.ToList(); + int ret; + if (HorizontalOrientation) + { + ret = (_vBar.Value + DrawHeight) / CellHeight; + if (ret >= columnList.Count) + { + ret = columnList.Count - 1; + } + } + else + { + ret = columnList.FindLastIndex(c => c.Left <= DrawWidth + _hBar.Value); + } + + return ret; + } + } + + private Cell _draggingCell; + + public void DragCurrentCell() + { + _draggingCell = CurrentCell; + } + + public void ReleaseCurrentCell() + { + if (_draggingCell != null) + { + var draggedCell = _draggingCell; + _draggingCell = null; + + if (CurrentCell != draggedCell) + { + CellDropped?.Invoke(this, new CellEventArgs(draggedCell, CurrentCell)); + } + } + } + + /// + /// Scrolls to the given index, according to the scroll settings. + /// + public void ScrollToIndex(int index) + { + if (ScrollMethod == "near") + { + MakeIndexVisible(index); + } + + if (!IsVisible(index) || AlwaysScroll) + { + if (ScrollMethod == "top") + { + FirstVisibleRow = index; + } + else if (ScrollMethod == "bottom") + { + LastVisibleRow = index; + } + else if (ScrollMethod == "center") + { + if (LagFramesToHide == 0) + { + FirstVisibleRow = Math.Max(index - (VisibleRows / 2), 0); + } + else + { + if (Math.Abs(FirstVisibleRow + CountLagFramesDisplay(VisibleRows / 2) - index) > VisibleRows) // Big jump + { + FirstVisibleRow = Math.Max(index - (ExpectedDisplayRange() / 2), 0); + SetLagFramesArray(); + } + + // Small jump, more accurate + int lastVisible = FirstVisibleRow + CountLagFramesDisplay(VisibleRows / 2); + do + { + if ((lastVisible - index) / (LagFramesToHide + 1) != 0) + { + FirstVisibleRow = Math.Max(FirstVisibleRow - ((lastVisible - index) / (LagFramesToHide + 1)), 0); + } + else + { + FirstVisibleRow -= Math.Sign(lastVisible - index); + } + + SetLagFramesArray(); + lastVisible = FirstVisibleRow + CountLagFramesDisplay(VisibleRows / 2); + } + while ((lastVisible - index < 0 || lastVisible - index > _lagFrames[VisibleRows]) && FirstVisibleRow != 0); + } + } + } + } + + /// + /// Scrolls so that the given index is visible, if it isn't already; doesn't use scroll settings. + /// + public void MakeIndexVisible(int index) + { + if (!IsVisible(index)) + { + if (FirstVisibleRow > index) + { + FirstVisibleRow = index; + } + else + { + LastVisibleRow = index; + } + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IEnumerable SelectedRows + { + get + { + return _selectedItems + .Where(cell => cell.RowIndex.HasValue) + .Select(cell => cell.RowIndex.Value) + .Distinct(); + } + } + + public bool AnyRowsSelected + { + get + { + return _selectedItems.Any(cell => cell.RowIndex.HasValue); + } + } + + public void ClearSelectedRows() + { + _selectedItems.Clear(); + } + + public IEnumerable GenerateContextMenuItems() + { + yield return new ToolStripSeparator(); + + var rotate = new ToolStripMenuItem + { + Name = "RotateMenuItem", + Text = "Rotate", + ShortcutKeyDisplayString = RotateHotkeyStr, + }; + + rotate.Click += (o, ev) => + { + HorizontalOrientation ^= true; + }; + + yield return rotate; + } + + public string RotateHotkeyStr => "Ctrl+Shift+F"; + + #endregion + + #region Mouse and Key Events + + private bool _columnDownMoved; + private int _previousX = 0; // TODO: move me + + protected override void OnMouseMove(MouseEventArgs e) + { + _previousX = _currentX ?? 0; + _currentX = e.X; + _currentY = e.Y; + + if (_columnResizing != null) + { + if (_currentX != _previousX) + { + _columnResizing.Width += _currentX - _previousX; + _columns.ColumnsChanged(); + Refresh(); + } + } + else if (_columnDown != null) + { + _columnDownMoved = true; + } + + Cell newCell = CalculatePointedCell(_currentX.Value, _currentY.Value); + + // SuuperW: Hide lag frames + if (QueryFrameLag != null && newCell.RowIndex.HasValue) + { + newCell.RowIndex += CountLagFramesDisplay(newCell.RowIndex.Value); + } + + newCell.RowIndex += FirstVisibleRow; + if (newCell.RowIndex < 0) + { + newCell.RowIndex = 0; + } + + if (!newCell.Equals(CurrentCell)) + { + CellChanged(newCell); + + if (IsHoveringOnColumnCell || + (WasHoveringOnColumnCell && !IsHoveringOnColumnCell)) + { + Refresh(); + } + else if (_columnDown != null) + { + Refresh(); + } + } + else if (_columnDown != null) // Kind of silly feeling to have this check twice, but the only alternative I can think of has it refreshing twice when pointed column changes with column down, and speed matters + { + Refresh(); + } + + Cursor = IsHoveringOnColumnEdge || _columnResizing != null + ? Cursors.VSplit + : Cursors.Default; + + base.OnMouseMove(e); + } + + protected override void OnMouseEnter(EventArgs e) + { + CurrentCell = new Cell + { + Column = null, + RowIndex = null + }; + + base.OnMouseEnter(e); + } + + protected override void OnMouseLeave(EventArgs e) + { + _currentX = null; + _currentY = null; + CurrentCell = null; + IsPaintDown = false; + _columnResizing = null; + _hoverTimer.Stop(); + Refresh(); + base.OnMouseLeave(e); + } + + // TODO add query callback of whether to select the cell or not + protected override void OnMouseDown(MouseEventArgs e) + { + if (!GlobalWin.MainForm.EmulatorPaused && _currentX.HasValue) + { + // copypaste from OnMouseMove() + Cell newCell = CalculatePointedCell(_currentX.Value, _currentY.Value); + if (QueryFrameLag != null && newCell.RowIndex.HasValue) + { + newCell.RowIndex += CountLagFramesDisplay(newCell.RowIndex.Value); + } + + newCell.RowIndex += FirstVisibleRow; + if (newCell.RowIndex < 0) + { + newCell.RowIndex = 0; + } + + if (!newCell.Equals(CurrentCell)) + { + CellChanged(newCell); + + if (IsHoveringOnColumnCell || + (WasHoveringOnColumnCell && !IsHoveringOnColumnCell)) + { + Refresh(); + } + else if (_columnDown != null) + { + Refresh(); + } + } + else if (_columnDown != null) + { + Refresh(); + } + } + + if (e.Button == MouseButtons.Left) + { + if (IsHoveringOnColumnEdge) + { + _columnResizing = CurrentCell.Column; + } + if (IsHoveringOnColumnCell) + { + _columnDown = CurrentCell.Column; + } + else if (InputPaintingMode) + { + IsPaintDown = true; + } + } + + if (e.Button == MouseButtons.Right) + { + if (!IsHoveringOnColumnCell) + { + RightButtonHeld = true; + } + } + + if (e.Button == MouseButtons.Left) + { + if (IsHoveringOnDataCell) + { + if (ModifierKeys == Keys.Alt) + { + // do marker drag here + } + else if (ModifierKeys == Keys.Shift && (CurrentCell.Column.Name == "FrameColumn" || CurrentCell.Column.Type == ColumnType.Text)) + { + if (_selectedItems.Any()) + { + if (FullRowSelect) + { + var selected = _selectedItems.Any(c => c.RowIndex.HasValue && CurrentCell.RowIndex.HasValue && c.RowIndex == CurrentCell.RowIndex); + + if (!selected) + { + var rowIndices = _selectedItems + .Where(c => c.RowIndex.HasValue) + .Select(c => c.RowIndex ?? -1) + .Where(c => c >= 0) // Hack to avoid possible Nullable exceptions + .Distinct() + .ToList(); + + var firstIndex = rowIndices.Min(); + var lastIndex = rowIndices.Max(); + + if (CurrentCell.RowIndex.Value < firstIndex) + { + for (int i = CurrentCell.RowIndex.Value; i < firstIndex; i++) + { + SelectCell(new Cell + { + RowIndex = i, + Column = CurrentCell.Column + }); + } + } + else if (CurrentCell.RowIndex.Value > lastIndex) + { + for (int i = lastIndex + 1; i <= CurrentCell.RowIndex.Value; i++) + { + SelectCell(new Cell + { + RowIndex = i, + Column = CurrentCell.Column + }); + } + } + else // Somewhere in between, a scenario that can happen with ctrl-clicking, find the previous and highlight from there + { + var nearest = rowIndices + .Where(x => x < CurrentCell.RowIndex.Value) + .Max(); + + for (int i = nearest + 1; i <= CurrentCell.RowIndex.Value; i++) + { + SelectCell(new Cell + { + RowIndex = i, + Column = CurrentCell.Column + }); + } + } + } + } + else + { + MessageBox.Show("Shift click logic for individual cells has not yet implemented"); + } + } + else + { + SelectCell(CurrentCell); + } + } + else if (ModifierKeys == Keys.Control && (CurrentCell.Column.Name == "FrameColumn" || CurrentCell.Column.Type == ColumnType.Text)) + { + SelectCell(CurrentCell, toggle: true); + } + else if (ModifierKeys != Keys.Shift) + { + var hadIndex = _selectedItems.Any(); + _selectedItems.Clear(); + SelectCell(CurrentCell); + } + + Refresh(); + + SelectedIndexChanged?.Invoke(this, new EventArgs()); + } + } + + base.OnMouseDown(e); + + if (AllowRightClickSelecton && e.Button == MouseButtons.Right) + { + if (!IsHoveringOnColumnCell && CurrentCell != null) + { + _currentX = e.X; + _currentY = e.Y; + Cell newCell = CalculatePointedCell(_currentX.Value, _currentY.Value); + newCell.RowIndex += FirstVisibleRow; + CellChanged(newCell); + SelectCell(CurrentCell); + } + } + } + + protected override void OnMouseUp(MouseEventArgs e) + { + if (_columnResizing == null && IsHoveringOnColumnCell) + { + if (_columnDown != null && _columnDownMoved) + { + DoColumnReorder(); + _columnDown = null; + Refresh(); + } + else if (e.Button == MouseButtons.Left) + { + ColumnClickEvent(ColumnAtX(e.X)); + } + else if (e.Button == MouseButtons.Right) + { + ColumnRightClickEvent(ColumnAtX(e.X)); + } + } + + _columnResizing = null; + _columnDown = null; + _columnDownMoved = false; + RightButtonHeld = false; + IsPaintDown = false; + base.OnMouseUp(e); + } + + private void IncrementScrollBar(ScrollBar bar, bool increment) + { + int newVal; + if (increment) + { + newVal = bar.Value + bar.SmallChange; + if (newVal > bar.Maximum - bar.LargeChange) + { + newVal = bar.Maximum - bar.LargeChange; + } + } + else + { + newVal = bar.Value - bar.SmallChange; + if (newVal < 0) + { + newVal = 0; + } + } + + _programmaticallyUpdatingScrollBarValues = true; + bar.Value = newVal; + _programmaticallyUpdatingScrollBarValues = false; + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + if (RightButtonHeld) + { + DoRightMouseScroll(this, e); + } + else + { + if (HorizontalOrientation) + { + do + { + IncrementScrollBar(_hBar, e.Delta < 0); + SetLagFramesFirst(); + } + while (_lagFrames[0] != 0 && _hBar.Value != 0 && _hBar.Value != _hBar.Maximum); + } + else + { + do + { + IncrementScrollBar(_vBar, e.Delta < 0); + SetLagFramesFirst(); + } + while (_lagFrames[0] != 0 && _vBar.Value != 0 && _vBar.Value != _vBar.Maximum); + } + + if (_currentX != null) + { + OnMouseMove(new MouseEventArgs(MouseButtons.None, 0, _currentX.Value, _currentY.Value, 0)); + } + + Refresh(); + } + } + + private void DoRightMouseScroll(object sender, MouseEventArgs e) + { + RightMouseScrolled?.Invoke(sender, e); + } + + private void ColumnClickEvent(RollColumn column) + { + ColumnClick?.Invoke(this, new ColumnClickEventArgs(column)); + } + + private void ColumnRightClickEvent(RollColumn column) + { + ColumnRightClick?.Invoke(this, new ColumnClickEventArgs(column)); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + if (!SuspendHotkeys) + { + if (e.Control && !e.Alt && e.Shift && e.KeyCode == Keys.F) // Ctrl+Shift+F + { + HorizontalOrientation ^= true; + } + // Scroll + else if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.PageUp) // Page Up + { + if (FirstVisibleRow > 0) + { + LastVisibleRow = FirstVisibleRow; + Refresh(); + } + } + else if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.PageDown) // Page Down + { + var totalRows = LastVisibleRow - FirstVisibleRow; + if (totalRows <= RowCount) + { + var final = LastVisibleRow + totalRows; + if (final > RowCount) + { + final = RowCount; + } + + LastVisibleRow = final; + Refresh(); + } + } + else if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.Home) // Home + { + FirstVisibleRow = 0; + Refresh(); + } + else if (!e.Control && !e.Alt && !e.Shift && e.KeyCode == Keys.End) // End + { + LastVisibleRow = RowCount; + Refresh(); + } + else if (!e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Up) // Up + { + if (FirstVisibleRow > 0) + { + FirstVisibleRow--; + Refresh(); + } + } + else if (!e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Down) // Down + { + if (FirstVisibleRow < RowCount - 1) + { + FirstVisibleRow++; + Refresh(); + } + } + // Selection courser + else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Up) // Ctrl + Up + { + if (SelectedRows.Any() && LetKeysModifySelection && SelectedRows.First() > 0) + { + foreach (var row in SelectedRows.ToList()) // clones SelectedRows + { + SelectRow(row - 1, true); + SelectRow(row, false); + } + } + } + else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Down) // Ctrl + Down + { + if (SelectedRows.Any() && LetKeysModifySelection) + { + foreach (var row in SelectedRows.Reverse()) // clones SelectedRows + { + SelectRow(row + 1, true); + SelectRow(row, false); + } + } + } + else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Left) // Ctrl + Left + { + if (SelectedRows.Any() && LetKeysModifySelection) + { + SelectRow(SelectedRows.Last(), false); + } + } + else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Right) // Ctrl + Right + { + if (SelectedRows.Any() && LetKeysModifySelection && SelectedRows.Last() < _rowCount - 1) + { + SelectRow(SelectedRows.Last() + 1, true); + } + } + else if (e.Control && e.Shift && !e.Alt && e.KeyCode == Keys.Left) // Ctrl + Shift + Left + { + if (SelectedRows.Any() && LetKeysModifySelection && SelectedRows.First() > 0) + { + SelectRow(SelectedRows.First() - 1, true); + } + } + else if (e.Control && e.Shift && !e.Alt && e.KeyCode == Keys.Right) // Ctrl + Shift + Right + { + if (SelectedRows.Any() && LetKeysModifySelection) + { + SelectRow(SelectedRows.First(), false); + } + } + else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.PageUp) // Ctrl + Page Up + { + //jump to above marker with selection courser + if (LetKeysModifySelection) + { + + } + } + else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.PageDown) // Ctrl + Page Down + { + //jump to below marker with selection courser + if (LetKeysModifySelection) + { + + } + + } + else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.Home) // Ctrl + Home + { + //move selection courser to frame 0 + if (LetKeysModifySelection) + { + DeselectAll(); + SelectRow(0, true); + } + } + else if (e.Control && !e.Shift && !e.Alt && e.KeyCode == Keys.End) // Ctrl + End + { + //move selection courser to end of movie + if (LetKeysModifySelection) + { + DeselectAll(); + SelectRow(RowCount-1, true); + } + } + + } + + base.OnKeyDown(e); + } + + #endregion + + #region Change Events + + protected override void OnResize(EventArgs e) + { + RecalculateScrollBars(); + base.OnResize(e); + Refresh(); + } + + private void OrientationChanged() + { + RecalculateScrollBars(); + + // TODO scroll to correct positions + ColumnChangedCallback(); + RecalculateScrollBars(); + + Refresh(); + } + + /// + /// Call this function to change the CurrentCell to newCell + /// + private void CellChanged(Cell newCell) + { + LastCell = CurrentCell; + CurrentCell = newCell; + + if (PointedCellChanged != null && + (LastCell.Column != CurrentCell.Column || LastCell.RowIndex != CurrentCell.RowIndex)) + { + PointedCellChanged(this, new CellEventArgs(LastCell, CurrentCell)); + } + + if (CurrentCell?.Column != null && CurrentCell.RowIndex.HasValue) + { + _hoverTimer.Start(); + } + else + { + _hoverTimer.Stop(); + } + } + + private void VerticalBar_ValueChanged(object sender, EventArgs e) + { + if (!_programmaticallyUpdatingScrollBarValues) + { + Refresh(); + } + + if (_horizontalOrientation) + { + ColumnScroll?.Invoke(_vBar, e); + } + else + { + RowScroll?.Invoke(_vBar, e); + } + } + + private void HorizontalBar_ValueChanged(object sender, EventArgs e) + { + if (!_programmaticallyUpdatingScrollBarValues) + { + Refresh(); + } + + if (_horizontalOrientation) + { + RowScroll?.Invoke(_hBar, e); + } + else + { + ColumnScroll?.Invoke(_vBar, e); + } + } + + private void ColumnChangedCallback() + { + RecalculateScrollBars(); + if (_columns.VisibleColumns.Any()) + { + ColumnWidth = _columns.VisibleColumns.Max(c => c.Width.Value) + CellWidthPadding * 4; + } + } + + #endregion + + #region Helpers + + private void DoColumnReorder() + { + if (_columnDown != CurrentCell.Column) + { + var oldIndex = _columns.IndexOf(_columnDown); + var newIndex = _columns.IndexOf(CurrentCell.Column); + + ColumnReordered?.Invoke(this, new ColumnReorderedEventArgs(oldIndex, newIndex, _columnDown)); + + _columns.Remove(_columnDown); + _columns.Insert(newIndex, _columnDown); + } + } + + // ScrollBar.Maximum = DesiredValue + ScrollBar.LargeChange - 1 + // See MSDN Page for more information on the dumb ScrollBar.Maximum Property + private void RecalculateScrollBars() + { + UpdateDrawSize(); + + var columns = _columns.VisibleColumns.ToList(); + + if (HorizontalOrientation) + { + NeedsVScrollbar = columns.Count > DrawHeight / CellHeight; + NeedsHScrollbar = RowCount > 1; + } + else + { + NeedsVScrollbar = ColumnHeight + (RowCount * RowHeight) > Height; + NeedsHScrollbar = TotalColWidth.HasValue && TotalColWidth.Value - DrawWidth + 1 > 0; + } + + UpdateDrawSize(); + if (VisibleRows > 0) + { + if (HorizontalOrientation) + { + _vBar.LargeChange = DrawHeight / 2; + _hBar.Maximum = Math.Max((VisibleRows - 1) * CellHeight, _hBar.Maximum); + _hBar.LargeChange = (VisibleRows - 1) * CellHeight; + } + else + { + _vBar.Maximum = Math.Max((VisibleRows - 1) * CellHeight, _vBar.Maximum); // ScrollBar.Maximum is dumb + _vBar.LargeChange = (VisibleRows - 1) * CellHeight; + // DrawWidth can be negative if the TAStudio window is small enough + // Clamp LargeChange to 0 here to prevent exceptions + _hBar.LargeChange = Math.Max(0, DrawWidth / 2); + } + } + + // Update VBar + if (NeedsVScrollbar) + { + if (HorizontalOrientation) + { + _vBar.Maximum = ((columns.Count() * CellHeight) - DrawHeight) + _vBar.LargeChange; + if (_vBar.Maximum < 0) + { + _vBar.Maximum = 0; + } + } + else + { + _vBar.Maximum = RowsToPixels(RowCount + 1) - (CellHeight * 3) + _vBar.LargeChange - 1; + + if (_vBar.Maximum < 0) + { + _vBar.Maximum = 0; + } + } + + _vBar.Location = new Point(Width - _vBar.Width, 0); + _vBar.Height = Height; + _vBar.Visible = true; + } + else + { + _vBar.Visible = false; + _vBar.Value = 0; + } + + // Update HBar + if (NeedsHScrollbar) + { + if (HorizontalOrientation) + { + _hBar.Maximum = RowsToPixels(RowCount + 1) - (CellHeight * 3) + _hBar.LargeChange - 1; + } + else + { + _hBar.Maximum = TotalColWidth.Value - DrawWidth + _hBar.LargeChange; + } + + _hBar.Location = new Point(0, Height - _hBar.Height); + _hBar.Width = Width - (NeedsVScrollbar ? (_vBar.Width + 1) : 0); + _hBar.Visible = true; + } + else + { + _hBar.Visible = false; + _hBar.Value = 0; + } + } + + private void UpdateDrawSize() + { + if (NeedsVScrollbar) + { + DrawWidth = Width - _vBar.Width; + } + else + { + DrawWidth = Width; + } + if (NeedsHScrollbar) + { + DrawHeight = Height - _hBar.Height; + } + else + { + DrawHeight = Height; + } + } + + /// + /// If FullRowSelect is enabled, selects all cells in the row that contains the given cell. Otherwise only given cell is added. + /// + /// The cell to select. + private void SelectCell(Cell cell, bool toggle = false) + { + if (cell.RowIndex.HasValue && cell.RowIndex < RowCount) + { + if (!MultiSelect) + { + _selectedItems.Clear(); + } + + if (FullRowSelect) + { + if (toggle && _selectedItems.Any(x => x.RowIndex.HasValue && x.RowIndex == cell.RowIndex)) + { + _selectedItems.RemoveWhere(x => x.RowIndex.HasValue && x.RowIndex == cell.RowIndex); + } + else + { + foreach (var column in _columns) + { + _selectedItems.Add(new Cell + { + RowIndex = cell.RowIndex, + Column = column + }); + } + } + } + else + { + if (toggle && _selectedItems.Any(x => x.RowIndex.HasValue && x.RowIndex == cell.RowIndex)) + { + var item = _selectedItems + .FirstOrDefault(x => x.Equals(cell)); + + if (item != null) + { + _selectedItems.Remove(item); + } + } + else + { + _selectedItems.Add(CurrentCell); + } + } + } + } + + private bool IsHoveringOnColumnCell => CurrentCell?.Column != null && !CurrentCell.RowIndex.HasValue; + + private bool IsHoveringOnColumnEdge => AllowColumnResize && IsHoveringOnColumnCell && IsPointingOnCellEdge(_currentX); + + private bool IsHoveringOnDataCell => CurrentCell?.Column != null && CurrentCell.RowIndex.HasValue; + + private bool WasHoveringOnColumnCell => LastCell?.Column != null && !LastCell.RowIndex.HasValue; + + private bool WasHoveringOnDataCell => LastCell?.Column != null && LastCell.RowIndex.HasValue; + + private bool IsPointingOnCellEdge(int? x) + { + if (x.HasValue) + { + if (HorizontalOrientation) + { + return false; // TODO: support column resize in horizontal orientation + } + + foreach (RollColumn column in _columns.VisibleColumns) + { + if (column.Left - _hBar.Value + (column.Width - column.Width / 6) <= x.Value && column.Right - _hBar.Value >= x.Value) + { + return true; + } + } + } + + return false; + } + + /// + /// Finds the specific cell that contains the (x, y) coordinate. + /// + /// The row number that it returns will be between 0 and VisibleRows, NOT the absolute row number. + /// X coordinate point. + /// Y coordinate point. + /// The cell with row number and RollColumn reference, both of which can be null. + private Cell CalculatePointedCell(int x, int y) + { + var newCell = new Cell(); + var columns = _columns.VisibleColumns.ToList(); + + // If pointing to a column header + if (columns.Any()) + { + if (HorizontalOrientation) + { + newCell.RowIndex = PixelsToRows(x); + + int colIndex = (y + _vBar.Value) / CellHeight; + if (colIndex >= 0 && colIndex < columns.Count) + { + newCell.Column = columns[colIndex]; + } + } + else + { + newCell.RowIndex = PixelsToRows(y); + newCell.Column = ColumnAtX(x); + } + } + + if (!(IsPaintDown || RightButtonHeld) && newCell.RowIndex <= -1) // -2 if we're entering from the top + { + newCell.RowIndex = null; + } + + return newCell; + } + + // A boolean that indicates if the InputRoll is too large vertically and requires a vertical scrollbar. + private bool NeedsVScrollbar { get; set; } + + // A boolean that indicates if the InputRoll is too large horizontally and requires a horizontal scrollbar. + private bool NeedsHScrollbar { get; set; } + + /// + /// Gets the total width of all the columns by using the last column's Right property. + /// + /// A nullable Int representing total width. + private int? TotalColWidth + { + get + { + if (_columns.VisibleColumns.Any()) + { + return _columns.VisibleColumns.Last().Right; + } + + return null; + } + } + + /// + /// Returns the RollColumn object at the specified visible x coordinate. Coordinate should be between 0 and Width of the InputRoll Control. + /// + /// The x coordinate. + /// RollColumn object that contains the x coordinate or null if none exists. + private RollColumn ColumnAtX(int x) + { + foreach (RollColumn column in _columns.VisibleColumns) + { + if (column.Left.Value - _hBar.Value <= x && column.Right.Value - _hBar.Value >= x) + { + return column; + } + } + + return null; + } + + /// + /// Converts a row number to a horizontal or vertical coordinate. + /// + /// A vertical coordinate if Vertical Oriented, otherwise a horizontal coordinate. + private int RowsToPixels(int index) + { + if (_horizontalOrientation) + { + return (index * CellWidth) + ColumnWidth; + } + + return (index * CellHeight) + ColumnHeight; + } + + /// + /// Converts a horizontal or vertical coordinate to a row number. + /// + /// A vertical coordinate if Vertical Oriented, otherwise a horizontal coordinate. + /// A row number between 0 and VisibleRows if it is a Datarow, otherwise a negative number if above all Datarows. + private int PixelsToRows(int pixels) + { + // Using Math.Floor and float because integer division rounds towards 0 but we want to round down. + if (_horizontalOrientation) + { + return (int)Math.Floor((float)(pixels - ColumnWidth) / CellWidth); + } + return (int)Math.Floor((float)(pixels - ColumnHeight) / CellHeight); + } + + // The width of the largest column cell in Horizontal Orientation + + private int ColumnWidth { get; set; } + + // The height of a column cell in Vertical Orientation. + private int ColumnHeight { get; set; } + + // The width of a cell in Horizontal Orientation. Only can be changed by changing the Font or CellPadding. + private int CellWidth { get; set; } + + [Browsable(false)] + public int RowHeight => CellHeight; + + /// + /// Gets or sets a value indicating the height of a cell in Vertical Orientation. Only can be changed by changing the Font or CellPadding. + /// + private int CellHeight { get; set; } = 8; + + /// + /// Call when _charSize, MaxCharactersInHorizontal, or CellPadding is changed. + /// + private void UpdateCellSize() + { + CellHeight = _charSize.Height + (CellHeightPadding * 2); + CellWidth = (_charSize.Width * MaxCharactersInHorizontal) + (CellWidthPadding * 4); // Double the padding for horizontal because it looks better + } + + // SuuperW: Count lag frames between FirstDisplayed and given display position + private int CountLagFramesDisplay(int relativeIndex) + { + if (QueryFrameLag != null && LagFramesToHide != 0) + { + int count = 0; + for (int i = 0; i <= relativeIndex; i++) + { + count += _lagFrames[i]; + } + + return count; + } + + return 0; + } + + // Count lag frames between FirstDisplayed and given relative frame index + private int CountLagFramesAbsolute(int relativeIndex) + { + if (QueryFrameLag != null && LagFramesToHide != 0) + { + int count = 0; + for (int i = 0; i + count <= relativeIndex; i++) + { + count += _lagFrames[i]; + } + + return count; + } + + return 0; + } + + private void SetLagFramesArray() + { + if (QueryFrameLag != null && LagFramesToHide != 0) + { + bool showNext = false; + + // First one needs to check BACKWARDS for lag frame count. + SetLagFramesFirst(); + int f = _lagFrames[0]; + if (QueryFrameLag(FirstVisibleRow + f, HideWasLagFrames)) + { + showNext = true; + } + + for (int i = 1; i <= VisibleRows; i++) + { + _lagFrames[i] = 0; + if (!showNext) + { + for (; _lagFrames[i] < LagFramesToHide; _lagFrames[i]++) + { + if (!QueryFrameLag(FirstVisibleRow + i + f, HideWasLagFrames)) + { + break; + } + + f++; + } + } + else + { + if (!QueryFrameLag(FirstVisibleRow + i + f, HideWasLagFrames)) + { + showNext = false; + } + } + + if (_lagFrames[i] == LagFramesToHide && QueryFrameLag(FirstVisibleRow + i + f, HideWasLagFrames)) + { + showNext = true; + } + } + } + else + { + for (int i = 0; i <= VisibleRows; i++) + { + _lagFrames[i] = 0; + } + } + } + private void SetLagFramesFirst() + { + if (QueryFrameLag != null && LagFramesToHide != 0) + { + // Count how many lag frames are above displayed area. + int count = 0; + do + { + count++; + } + while (QueryFrameLag(FirstVisibleRow - count, HideWasLagFrames) && count <= LagFramesToHide); + count--; + + // Count forward + int fCount = -1; + do + { + fCount++; + } + while (QueryFrameLag(FirstVisibleRow + fCount, HideWasLagFrames) && count + fCount < LagFramesToHide); + _lagFrames[0] = (byte)fCount; + } + else + { + _lagFrames[0] = 0; + } + } + + // Number of displayed + hidden frames, if fps is as expected + private int ExpectedDisplayRange() + { + return (VisibleRows + 1) * LagFramesToHide; + } + + #endregion + } +} diff --git a/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumn.cs b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumn.cs new file mode 100644 index 0000000000..8375120d3d --- /dev/null +++ b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumn.cs @@ -0,0 +1,29 @@ +namespace BizHawk.Client.EmuHawk +{ + public class RollColumn + { + public string Group { get; set; } + public int? Width { get; set; } + public int? Left { get; set; } + public int? Right { get; set; } + public string Name { get; set; } + public string Text { get; set; } + public ColumnType Type { get; set; } + public bool Visible { get; set; } + + /// + /// Column will be drawn with an emphasized look, if true + /// + private bool _emphasis; + public bool Emphasis + { + get { return _emphasis; } + set { _emphasis = value; } + } + + public RollColumn() + { + Visible = true; + } + } +} diff --git a/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumns.cs b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumns.cs new file mode 100644 index 0000000000..95fa56788a --- /dev/null +++ b/BizHawk.Client.EmuHawk/CustomControls/InputRoll/RollColumns.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Client.EmuHawk +{ + public class RollColumns : List + { + public RollColumn this[string name] + { + get + { + return this.SingleOrDefault(column => column.Name == name); + } + } + + public IEnumerable VisibleColumns + { + get + { + return this.Where(c => c.Visible); + } + } + + public Action ChangedCallback { get; set; } + + private void DoChangeCallback() + { + // no check will make it crash for user too, not sure which way of alarm we prefer. no alarm at all will cause all sorts of subtle bugs + if (ChangedCallback == null) + { + System.Diagnostics.Debug.Fail($"{nameof(ChangedCallback)} has died!"); + } + else + { + ChangedCallback(); + } + } + + // TODO: this shouldn't be exposed. But in order to not expose it, each RollColumn must have a change callback, and all property changes must call it, it is quicker and easier to just call this when needed + public void ColumnsChanged() + { + int pos = 0; + + foreach (var col in VisibleColumns) + { + col.Left = pos; + pos += col.Width.Value; + col.Right = pos; + } + + DoChangeCallback(); + } + + public new void Add(RollColumn column) + { + if (this.Any(c => c.Name == column.Name)) + { + // The designer sucks, doing nothing for now + return; + //throw new InvalidOperationException("A column with this name already exists."); + } + + base.Add(column); + ColumnsChanged(); + } + + public new void AddRange(IEnumerable collection) + { + foreach (var column in collection) + { + if (this.Any(c => c.Name == column.Name)) + { + // The designer sucks, doing nothing for now + return; + + throw new InvalidOperationException("A column with this name already exists."); + } + } + + base.AddRange(collection); + ColumnsChanged(); + } + + public new void Insert(int index, RollColumn column) + { + if (this.Any(c => c.Name == column.Name)) + { + throw new InvalidOperationException("A column with this name already exists."); + } + + base.Insert(index, column); + ColumnsChanged(); + } + + public new void InsertRange(int index, IEnumerable collection) + { + foreach (var column in collection) + { + if (this.Any(c => c.Name == column.Name)) + { + throw new InvalidOperationException("A column with this name already exists."); + } + } + + base.InsertRange(index, collection); + ColumnsChanged(); + } + + public new bool Remove(RollColumn column) + { + var result = base.Remove(column); + ColumnsChanged(); + return result; + } + + public new int RemoveAll(Predicate match) + { + var result = base.RemoveAll(match); + ColumnsChanged(); + return result; + } + + public new void RemoveAt(int index) + { + base.RemoveAt(index); + ColumnsChanged(); + } + + public new void RemoveRange(int index, int count) + { + base.RemoveRange(index, count); + ColumnsChanged(); + } + + public new void Clear() + { + base.Clear(); + ColumnsChanged(); + } + + public IEnumerable Groups + { + get + { + return this + .Select(x => x.Group) + .Distinct(); + } + } + } +} diff --git a/BizHawk.Client.EmuHawk/tools/CDL.cs b/BizHawk.Client.EmuHawk/tools/CDL.cs index 47354229de..af444b8e08 100644 --- a/BizHawk.Client.EmuHawk/tools/CDL.cs +++ b/BizHawk.Client.EmuHawk/tools/CDL.cs @@ -64,19 +64,19 @@ namespace BizHawk.Client.EmuHawk lvCDL.AllColumns.Clear(); lvCDL.AllColumns.AddRange(new [] { - new InputRoll.RollColumn { Name = "CDLFile", Text = "CDL File @", Width = 107, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "Domain", Text = "Domain", Width = 126, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "Percent", Text = "%", Width = 58, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "Mapped", Text = "Mapped", Width = 64, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "Size", Text = "Size", Width = 112, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "0x01", Text = "0x01", Width = 56, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "0x02", Text = "0x02", Width = 56, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "0x04", Text = "0x04", Width = 56, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "0x08", Text = "0x08", Width = 56, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "0x10", Text = "0x10", Width = 56, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "0x20", Text = "0x20", Width = 56, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "0x40", Text = "0x40", Width = 56, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = "0x80", Text = "0x80", Width = 56, Type = InputRoll.RollColumn.InputType.Text } + new RollColumn { Name = "CDLFile", Text = "CDL File @", Width = 107, Type = ColumnType.Text }, + new RollColumn { Name = "Domain", Text = "Domain", Width = 126, Type = ColumnType.Text }, + new RollColumn { Name = "Percent", Text = "%", Width = 58, Type = ColumnType.Text }, + new RollColumn { Name = "Mapped", Text = "Mapped", Width = 64, Type = ColumnType.Text }, + new RollColumn { Name = "Size", Text = "Size", Width = 112, Type = ColumnType.Text }, + new RollColumn { Name = "0x01", Text = "0x01", Width = 56, Type = ColumnType.Text }, + new RollColumn { Name = "0x02", Text = "0x02", Width = 56, Type = ColumnType.Text }, + new RollColumn { Name = "0x04", Text = "0x04", Width = 56, Type = ColumnType.Text }, + new RollColumn { Name = "0x08", Text = "0x08", Width = 56, Type = ColumnType.Text }, + new RollColumn { Name = "0x10", Text = "0x10", Width = 56, Type = ColumnType.Text }, + new RollColumn { Name = "0x20", Text = "0x20", Width = 56, Type = ColumnType.Text }, + new RollColumn { Name = "0x40", Text = "0x40", Width = 56, Type = ColumnType.Text }, + new RollColumn { Name = "0x80", Text = "0x80", Width = 56, Type = ColumnType.Text } }); } @@ -576,7 +576,7 @@ namespace BizHawk.Client.EmuHawk CodeDataLogger.SetCDL(null); } - private void lvCDL_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void lvCDL_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { var subItem = lvCDL.AllColumns.IndexOf(column); text = listContents[index][subItem]; diff --git a/BizHawk.Client.EmuHawk/tools/Cheats/Cheats.cs b/BizHawk.Client.EmuHawk/tools/Cheats/Cheats.cs index c53d2c9e8b..9fff2eadc5 100644 --- a/BizHawk.Client.EmuHawk/tools/Cheats/Cheats.cs +++ b/BizHawk.Client.EmuHawk/tools/Cheats/Cheats.cs @@ -239,7 +239,7 @@ namespace BizHawk.Client.EmuHawk SetColumns(); } - private void CheatListView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void CheatListView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = ""; if (index >= Global.CheatList.Count || Global.CheatList[index].IsSeparator) @@ -311,7 +311,7 @@ namespace BizHawk.Client.EmuHawk } } - private void CheatListView_QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void CheatListView_QueryItemBkColor(int index, RollColumn column, ref Color color) { if (index < Global.CheatList.Count) { @@ -737,21 +737,21 @@ namespace BizHawk.Client.EmuHawk { public CheatsSettings() { - Columns = new List + Columns = new List { - new InputRoll.RollColumn { Text = "Names", Name = NameColumn, Visible = true, Width = 128, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Address", Name = AddressColumn, Visible = true, Width = 60, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Value", Name = ValueColumn, Visible = true, Width = 59, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Compare", Name = CompareColumn, Visible = true, Width = 63, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Compare Type", Name = ComparisonTypeColumn, Visible = true, Width = 98, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "On", Name = OnColumn, Visible = false, Width = 28, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Size", Name = SizeColumn, Visible = true, Width = 55, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Endian", Name = EndianColumn, Visible = false, Width = 55, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Display Type", Name = TypeColumn, Visible = false, Width = 88, Type = InputRoll.RollColumn.InputType.Text } + new RollColumn { Text = "Names", Name = NameColumn, Visible = true, Width = 128, Type = ColumnType.Text }, + new RollColumn { Text = "Address", Name = AddressColumn, Visible = true, Width = 60, Type = ColumnType.Text }, + new RollColumn { Text = "Value", Name = ValueColumn, Visible = true, Width = 59, Type = ColumnType.Text }, + new RollColumn { Text = "Compare", Name = CompareColumn, Visible = true, Width = 63, Type = ColumnType.Text }, + new RollColumn { Text = "Compare Type", Name = ComparisonTypeColumn, Visible = true, Width = 98, Type = ColumnType.Text }, + new RollColumn { Text = "On", Name = OnColumn, Visible = false, Width = 28, Type = ColumnType.Text }, + new RollColumn { Text = "Size", Name = SizeColumn, Visible = true, Width = 55, Type = ColumnType.Text }, + new RollColumn { Text = "Endian", Name = EndianColumn, Visible = false, Width = 55, Type = ColumnType.Text }, + new RollColumn { Text = "Display Type", Name = TypeColumn, Visible = false, Width = 88, Type = ColumnType.Text } }; } - public List Columns { get; set; } + public List Columns { get; set; } } } } diff --git a/BizHawk.Client.EmuHawk/tools/Debugger/GenericDebugger.Disassembler.cs b/BizHawk.Client.EmuHawk/tools/Debugger/GenericDebugger.Disassembler.cs index 0fd2c0a047..c86f93f188 100644 --- a/BizHawk.Client.EmuHawk/tools/Debugger/GenericDebugger.Disassembler.cs +++ b/BizHawk.Client.EmuHawk/tools/Debugger/GenericDebugger.Disassembler.cs @@ -67,7 +67,7 @@ namespace BizHawk.Client.EmuHawk } } - private void DisassemblerView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void DisassemblerView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = ""; @@ -84,7 +84,7 @@ namespace BizHawk.Client.EmuHawk } } - private void DisassemblerView_QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void DisassemblerView_QueryItemBkColor(int index, RollColumn column, ref Color color) { if (_disassemblyLines.Any() && index < _disassemblyLines.Count) { diff --git a/BizHawk.Client.EmuHawk/tools/Debugger/GenericDebugger.cs b/BizHawk.Client.EmuHawk/tools/Debugger/GenericDebugger.cs index 58fce366dc..4f180134ff 100644 --- a/BizHawk.Client.EmuHawk/tools/Debugger/GenericDebugger.cs +++ b/BizHawk.Client.EmuHawk/tools/Debugger/GenericDebugger.cs @@ -23,19 +23,19 @@ namespace BizHawk.Client.EmuHawk DisassemblerView.AllColumns.Clear(); DisassemblerView.AllColumns.AddRange(new[] { - new InputRoll.RollColumn + new RollColumn { Name = AddressColumnName, Text = AddressColumnName, Width = 94, - Type = InputRoll.RollColumn.InputType.Text + Type = ColumnType.Text }, - new InputRoll.RollColumn + new RollColumn { Name = InstructionColumnName, Text = InstructionColumnName, Width = 291, - Type = InputRoll.RollColumn.InputType.Text + Type = ColumnType.Text } }); } diff --git a/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Tastudio.cs b/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Tastudio.cs index bdba64d3a1..24e92d1f42 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Tastudio.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Tastudio.cs @@ -587,7 +587,7 @@ namespace BizHawk.Client.EmuHawk { if (Engaged()) { - Tastudio.AddColumn(name, text, width, InputRoll.RollColumn.InputType.Text); + Tastudio.AddColumn(name, text, width, ColumnType.Text); } } } diff --git a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs index fed33dd0b8..9353b10901 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs @@ -38,15 +38,15 @@ namespace BizHawk.Client.EmuHawk { public LuaConsoleSettings() { - Columns = new List + Columns = new List { - new InputRoll.RollColumn { Name = IconColumnName, Text = " ", Visible = true, Width = 22, Type = InputRoll.RollColumn.InputType.Image }, - new InputRoll.RollColumn { Name = ScriptColumnName, Text = "Script", Visible = true, Width = 92, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = PathColumnName, Text = "Path", Visible = true, Width = 300, Type = InputRoll.RollColumn.InputType.Text } + new RollColumn { Name = IconColumnName, Text = " ", Visible = true, Width = 22, Type = ColumnType.Image }, + new RollColumn { Name = ScriptColumnName, Text = "Script", Visible = true, Width = 92, Type = ColumnType.Text }, + new RollColumn { Name = PathColumnName, Text = "Path", Visible = true, Width = 300, Type = ColumnType.Text } }; } - public List Columns { get; set; } + public List Columns { get; set; } } [ConfigPersist] @@ -362,7 +362,7 @@ namespace BizHawk.Client.EmuHawk Path.GetFileName(LuaImp.ScriptList.Filename); } - private void LuaListView_QueryItemImage(int index, InputRoll.RollColumn column, ref Bitmap bitmap, ref int offsetX, ref int offsetY) + private void LuaListView_QueryItemImage(int index, RollColumn column, ref Bitmap bitmap, ref int offsetX, ref int offsetY) { if (column.Name != IconColumnName) { @@ -388,7 +388,7 @@ namespace BizHawk.Client.EmuHawk } } - private void LuaListView_QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void LuaListView_QueryItemBkColor(int index, RollColumn column, ref Color color) { if (LuaImp.ScriptList[index].IsSeparator) { @@ -404,7 +404,7 @@ namespace BizHawk.Client.EmuHawk } } - private void LuaListView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void LuaListView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = ""; diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs index fd6d903991..049d8c006d 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs @@ -63,19 +63,19 @@ namespace BizHawk.Client.EmuHawk BranchView.AllColumns.AddRange(new[] { - new InputRoll.RollColumn + new RollColumn { Name = BranchNumberColumnName, Text = "#", Width = 30 }, - new InputRoll.RollColumn + new RollColumn { Name = FrameColumnName, Text = "Frame", Width = 64 }, - new InputRoll.RollColumn + new RollColumn { Name = UserTextColumnName, Text = "UserText", @@ -89,7 +89,7 @@ namespace BizHawk.Client.EmuHawk #region Query callbacks - private void QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = ""; @@ -112,7 +112,7 @@ namespace BizHawk.Client.EmuHawk } } - private void QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void QueryItemBkColor(int index, RollColumn column, ref Color color) { TasBranch branch = GetBranch(index); if (branch != null) diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.cs index 14b9acce8a..525812c8b5 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/MarkerControl.cs @@ -21,13 +21,13 @@ namespace BizHawk.Client.EmuHawk MarkerView.AllColumns.AddRange(new[] { - new InputRoll.RollColumn + new RollColumn { Name = "FrameColumn", Text = "Frame", Width = 52 }, - new InputRoll.RollColumn + new RollColumn { Name = "LabelColumn", Text = "", @@ -45,7 +45,7 @@ namespace BizHawk.Client.EmuHawk public InputRoll MarkerInputRoll => MarkerView; - private void MarkerView_QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void MarkerView_QueryItemBkColor(int index, RollColumn column, ref Color color) { var prev = Markers.PreviousOrCurrent(Tastudio.Emulator.Frame); @@ -81,7 +81,7 @@ namespace BizHawk.Client.EmuHawk } } - private void MarkerView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void MarkerView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = ""; diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Callbacks.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Callbacks.cs index b93e0f2a1a..920056988e 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Callbacks.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Callbacks.cs @@ -15,17 +15,17 @@ namespace BizHawk.Client.EmuHawk public Action BranchSavedCallback { get; set; } public Action BranchRemovedCallback { get; set; } - private Color? GetColorOverride(int index, InputRoll.RollColumn column) + private Color? GetColorOverride(int index, RollColumn column) { return QueryItemBgColorCallback?.Invoke(index, column.Name); } - private string GetTextOverride(int index, InputRoll.RollColumn column) + private string GetTextOverride(int index, RollColumn column) { return QueryItemTextCallback?.Invoke(index, column.Name); } - private Bitmap GetIconOverride(int index, InputRoll.RollColumn column) + private Bitmap GetIconOverride(int index, RollColumn column) { return QueryItemIconCallback?.Invoke(index, column.Name); } diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs index f5cf9410a4..dc707692c6 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs @@ -157,7 +157,7 @@ namespace BizHawk.Client.EmuHawk private Bitmap icon_anchor_lag => Properties.Resources.icon_anchor_lag; private Bitmap icon_anchor => Properties.Resources.icon_anchor; - private void TasView_QueryItemIcon(int index, InputRoll.RollColumn column, ref Bitmap bitmap, ref int offsetX, ref int offsetY) + private void TasView_QueryItemIcon(int index, RollColumn column, ref Bitmap bitmap, ref int offsetX, ref int offsetY) { var overrideIcon = GetIconOverride(index, column); @@ -214,7 +214,7 @@ namespace BizHawk.Client.EmuHawk } } - private void TasView_QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void TasView_QueryItemBkColor(int index, RollColumn column, ref Color color) { Color? overrideColor = GetColorOverride(index, column); @@ -296,7 +296,7 @@ namespace BizHawk.Client.EmuHawk } } - private void TasView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void TasView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { var overrideText = GetTextOverride(index, column); if (overrideText != null) @@ -333,7 +333,7 @@ namespace BizHawk.Client.EmuHawk else if (index < CurrentTasMovie.InputLogLength) { text = CurrentTasMovie.DisplayValue(index, columnName); - if (column.Type == InputRoll.RollColumn.InputType.Float) + if (column.Type == ColumnType.Float) { // feos: this could be cashed, but I don't notice any slowdown this way either ControllerDefinition.FloatRange range = Global.MovieSession.MovieControllerAdapter.Definition.FloatRanges @@ -601,7 +601,7 @@ namespace BizHawk.Client.EmuHawk _selectionDragState = TasView.SelectedRows.Contains(frame); } } - else if (TasView.CurrentCell.Column.Type != InputRoll.RollColumn.InputType.Text) // User changed input + else if (TasView.CurrentCell.Column.Type != ColumnType.Text) // User changed input { bool wasPaused = Mainform.EmulatorPaused; diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs index a4f90146ae..52ae09bd32 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.MenuItems.cs @@ -1267,7 +1267,7 @@ namespace BizHawk.Client.EmuHawk playerMenus[i] = new ToolStripMenuItem($"Player {i}"); } - foreach (InputRoll.RollColumn column in columns) + foreach (var column in columns) { ToolStripMenuItem menuItem = new ToolStripMenuItem { diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs index 97375fdf25..19136e07e7 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs @@ -416,18 +416,18 @@ namespace BizHawk.Client.EmuHawk var columnNames = GenerateColumnNames(); foreach (var kvp in columnNames) { - InputRoll.RollColumn.InputType type; + ColumnType type; int digits; if (Global.MovieSession.MovieControllerAdapter.Definition.FloatControls.Contains(kvp.Key)) { ControllerDefinition.FloatRange range = Global.MovieSession.MovieControllerAdapter.Definition.FloatRanges [Global.MovieSession.MovieControllerAdapter.Definition.FloatControls.IndexOf(kvp.Key)]; - type = InputRoll.RollColumn.InputType.Float; + type = ColumnType.Float; digits = Math.Max(kvp.Value.Length, range.MaxDigits()); } else { - type = InputRoll.RollColumn.InputType.Boolean; + type = ColumnType.Boolean; digits = kvp.Value.Length; } @@ -494,11 +494,11 @@ namespace BizHawk.Client.EmuHawk SetUpToolStripColumns(); } - public void AddColumn(string columnName, string columnText, int columnWidth, InputRoll.RollColumn.InputType columnType = InputRoll.RollColumn.InputType.Boolean) + public void AddColumn(string columnName, string columnText, int columnWidth, ColumnType columnType = ColumnType.Boolean) { if (TasView.AllColumns[columnName] == null) { - var column = new InputRoll.RollColumn + var column = new RollColumn { Name = columnName, Text = columnText, diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/UndoHistoryForm.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/UndoHistoryForm.cs index b08c0e49db..7e5ed8f466 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/UndoHistoryForm.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/UndoHistoryForm.cs @@ -26,21 +26,21 @@ namespace BizHawk.Client.EmuHawk HistoryView.AllColumns.Clear(); HistoryView.AllColumns.AddRange(new[] { - new InputRoll.RollColumn { Name = IdColumnName, Text = IdColumnName, Width = 40, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Name = UndoColumnName, Text = UndoColumnName, Width = 280, Type = InputRoll.RollColumn.InputType.Text } + new RollColumn { Name = IdColumnName, Text = IdColumnName, Width = 40, Type = ColumnType.Text }, + new RollColumn { Name = UndoColumnName, Text = UndoColumnName, Width = 280, Type = ColumnType.Text } }); MaxStepsNum.Value = Log.MaxSteps; } - private void HistoryView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void HistoryView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = column.Name == UndoColumnName ? Log.Names[index] : index.ToString(); } - private void HistoryView_QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void HistoryView_QueryItemBkColor(int index, RollColumn column, ref Color color) { if (index == Log.UndoIndex) { diff --git a/BizHawk.Client.EmuHawk/tools/TraceLogger.cs b/BizHawk.Client.EmuHawk/tools/TraceLogger.cs index 85eb746632..1c0cb24a42 100644 --- a/BizHawk.Client.EmuHawk/tools/TraceLogger.cs +++ b/BizHawk.Client.EmuHawk/tools/TraceLogger.cs @@ -24,7 +24,7 @@ namespace BizHawk.Client.EmuHawk private int FileSizeCap { get; set; } [ConfigPersist] - private List Columns + private List Columns { get { return TraceView.AllColumns; } set @@ -78,19 +78,19 @@ namespace BizHawk.Client.EmuHawk _splitFile = FileSizeCap != 0; TraceView.AllColumns.Clear(); - TraceView.AllColumns.Add(new InputRoll.RollColumn + TraceView.AllColumns.Add(new RollColumn { Name = DisasmColumnName, Text = DisasmColumnName, Width = 239, - Type = InputRoll.RollColumn.InputType.Text + Type = ColumnType.Text }); - TraceView.AllColumns.Add(new InputRoll.RollColumn + TraceView.AllColumns.Add(new RollColumn { Name = RegistersColumnName, Text = RegistersColumnName, Width = 357, - Type = InputRoll.RollColumn.InputType.Text + Type = ColumnType.Text }); } @@ -109,7 +109,7 @@ namespace BizHawk.Client.EmuHawk //Tracer.Enabled = LoggingEnabled.Checked; } - private void TraceView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void TraceView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = ""; if (index < _instructions.Count) diff --git a/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs b/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs index a7be2ebb0e..c398b89730 100644 --- a/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs +++ b/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs @@ -166,7 +166,7 @@ namespace BizHawk.Client.EmuHawk ErrorIconButton.Visible = _searches.OutOfRangeAddress.Any(); } - private void ListView_QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void ListView_QueryItemBkColor(int index, RollColumn column, ref Color color) { if (_searches.Count > 0) { @@ -195,7 +195,7 @@ namespace BizHawk.Client.EmuHawk } } - private void ListView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void ListView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = ""; @@ -974,13 +974,13 @@ namespace BizHawk.Client.EmuHawk { public RamSearchSettings() { - Columns = new List + Columns = new List { - new InputRoll.RollColumn { Text = "Address", Name = WatchList.ADDRESS, Visible = true, Width = 60, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Value", Name = WatchList.VALUE, Visible = true, Width = 59, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Prev", Name = WatchList.PREV, Visible = true, Width = 59, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Changes", Name = WatchList.CHANGES, Visible = true, Width = 60, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Diff", Name = WatchList.DIFF, Visible = false, Width = 59, Type = InputRoll.RollColumn.InputType.Text }, + new RollColumn { Text = "Address", Name = WatchList.ADDRESS, Visible = true, Width = 60, Type = ColumnType.Text }, + new RollColumn { Text = "Value", Name = WatchList.VALUE, Visible = true, Width = 59, Type = ColumnType.Text }, + new RollColumn { Text = "Prev", Name = WatchList.PREV, Visible = true, Width = 59, Type = ColumnType.Text }, + new RollColumn { Text = "Changes", Name = WatchList.CHANGES, Visible = true, Width = 60, Type = ColumnType.Text }, + new RollColumn { Text = "Diff", Name = WatchList.DIFF, Visible = false, Width = 59, Type = ColumnType.Text }, }; PreviewMode = true; @@ -988,7 +988,7 @@ namespace BizHawk.Client.EmuHawk AutoSearchTakeLagFramesIntoAccount = true; } - public List Columns { get; set; } + public List Columns { get; set; } public bool PreviewMode { get; set; } public bool AlwaysExcludeRamWatch { get; set; } public bool AutoSearchTakeLagFramesIntoAccount { get; set; } diff --git a/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs b/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs index 7377d33b4c..b29c6137e2 100644 --- a/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs +++ b/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs @@ -79,20 +79,20 @@ namespace BizHawk.Client.EmuHawk { public RamWatchSettings() { - Columns = new List + Columns = new List { - new InputRoll.RollColumn { Text = "Address", Name = WatchList.ADDRESS, Visible = true, Width = 60, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Value", Name = WatchList.VALUE, Visible = true, Width = 59, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Prev", Name = WatchList.PREV, Visible = false, Width = 59, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Changes", Name = WatchList.CHANGES, Visible = true, Width = 60, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Diff", Name = WatchList.DIFF, Visible = false, Width = 59, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Type", Name = WatchList.TYPE, Visible = false, Width = 55, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Domain", Name = WatchList.DOMAIN, Visible = true, Width = 55, Type = InputRoll.RollColumn.InputType.Text }, - new InputRoll.RollColumn { Text = "Notes", Name = WatchList.NOTES, Visible = true, Width = 128, Type = InputRoll.RollColumn.InputType.Text } + new RollColumn { Text = "Address", Name = WatchList.ADDRESS, Visible = true, Width = 60, Type = ColumnType.Text }, + new RollColumn { Text = "Value", Name = WatchList.VALUE, Visible = true, Width = 59, Type = ColumnType.Text }, + new RollColumn { Text = "Prev", Name = WatchList.PREV, Visible = false, Width = 59, Type = ColumnType.Text }, + new RollColumn { Text = "Changes", Name = WatchList.CHANGES, Visible = true, Width = 60, Type = ColumnType.Text }, + new RollColumn { Text = "Diff", Name = WatchList.DIFF, Visible = false, Width = 59, Type = ColumnType.Text }, + new RollColumn { Text = "Type", Name = WatchList.TYPE, Visible = false, Width = 55, Type = ColumnType.Text }, + new RollColumn { Text = "Domain", Name = WatchList.DOMAIN, Visible = true, Width = 55, Type = ColumnType.Text }, + new RollColumn { Text = "Notes", Name = WatchList.NOTES, Visible = true, Width = 128, Type = ColumnType.Text } }; } - public List Columns { get; set; } + public List Columns { get; set; } } private IEnumerable SelectedIndices => WatchListView.SelectedRows; @@ -553,7 +553,7 @@ namespace BizHawk.Client.EmuHawk } } - private void OrderColumn(InputRoll.RollColumn column) + private void OrderColumn(RollColumn column) { if (column.Name != _sortedColumn) { @@ -621,7 +621,7 @@ namespace BizHawk.Client.EmuHawk WatchCountLabel.Text = _watches.WatchCount + (_watches.WatchCount == 1 ? " watch" : " watches"); } - private void WatchListView_QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) + private void WatchListView_QueryItemBkColor(int index, RollColumn column, ref Color color) { if (index >= _watches.Count) { @@ -642,7 +642,7 @@ namespace BizHawk.Client.EmuHawk } } - private void WatchListView_QueryItemText(int index, InputRoll.RollColumn column, out string text, ref int offsetX, ref int offsetY) + private void WatchListView_QueryItemText(int index, RollColumn column, out string text, ref int offsetX, ref int offsetY) { text = ""; if (index >= _watches.Count)