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)
| | | | | | | | | |