mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-05-03 20:17:08 -05:00
796 lines
28 KiB
C#
796 lines
28 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using PKHeX.Core;
|
|
using PKHeX.WinForms.Controls;
|
|
|
|
namespace PKHeX.WinForms;
|
|
|
|
public interface IEventWorkGrid
|
|
{
|
|
void Load();
|
|
void Save();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Immutable helper for name/hash lookup and reverse lookup.
|
|
/// </summary>
|
|
public sealed class EventWorkLookup(Dictionary<ulong, string> forward)
|
|
{
|
|
private readonly Dictionary<string, ulong> _reverse = forward.ToDictionary(z => z.Value, z => z.Key);
|
|
|
|
public string GetName(ulong hash)
|
|
{
|
|
if (forward.TryGetValue(hash, out var name))
|
|
return name;
|
|
return hash.ToString("X16");
|
|
}
|
|
|
|
public ulong GetHash(string nameOrHex)
|
|
{
|
|
if (_reverse.TryGetValue(nameOrHex, out var h))
|
|
return h;
|
|
if (ulong.TryParse(nameOrHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var parsed))
|
|
return parsed;
|
|
return FnvHash.HashFnv1a_64(nameOrHex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Base class with shared UI helpers and wiring for EventWork grids.
|
|
/// </summary>
|
|
public abstract record EventWorkGridBase : IEventWorkGrid
|
|
{
|
|
private readonly Timer _searchDebounce = new() { Interval = 150 };
|
|
private Action<string>? _searchAction;
|
|
private SplitContainer Container { get; }
|
|
private TextBox Search { get; }
|
|
protected DataGridView Grid { get; }
|
|
|
|
protected EventWorkGridBase(SplitContainer container, DataGridView grid, TextBox search)
|
|
{
|
|
Container = container;
|
|
Grid = grid;
|
|
Search = search;
|
|
|
|
_searchDebounce.Tick += SearchDebounceTick;
|
|
Search.Disposed += (_, _) => _searchDebounce.Dispose();
|
|
}
|
|
|
|
protected void WireSearch(Action<string> applyFilter)
|
|
{
|
|
_searchAction = applyFilter;
|
|
Search.TextChanged += (_, _) => RestartSearchDebounce();
|
|
}
|
|
|
|
private void RestartSearchDebounce()
|
|
{
|
|
if (Search.IsDisposed || Search.Disposing)
|
|
return;
|
|
|
|
_searchDebounce.Stop();
|
|
_searchDebounce.Start();
|
|
}
|
|
|
|
private void SearchDebounceTick(object? sender, EventArgs e)
|
|
{
|
|
_searchDebounce.Stop();
|
|
if (_searchAction is null)
|
|
return;
|
|
if (Search.IsDisposed || Search.Disposing || Grid.IsDisposed || Grid.Disposing || Container.IsDisposed || Container.Disposing)
|
|
return;
|
|
|
|
_searchAction(Search.Text);
|
|
}
|
|
|
|
protected static (SplitContainer Container, TextBox Search) CreateContainerCommon()
|
|
{
|
|
var container = new SplitContainer
|
|
{
|
|
Dock = DockStyle.Fill,
|
|
Orientation = Orientation.Horizontal,
|
|
FixedPanel = FixedPanel.Panel1,
|
|
IsSplitterFixed = true,
|
|
Margin = Padding.Empty,
|
|
};
|
|
|
|
var search = new TextBox
|
|
{
|
|
Dock = DockStyle.Fill,
|
|
Margin = Padding.Empty,
|
|
PlaceholderText = "Search...",
|
|
};
|
|
container.Panel1.Controls.Add(search);
|
|
|
|
var topHeight = Math.Max(search.PreferredHeight + 4, 24);
|
|
container.Panel1MinSize = topHeight;
|
|
container.SplitterDistance = topHeight;
|
|
return (container, search);
|
|
}
|
|
|
|
protected static DataGridViewTextBoxColumn MakeIndexColumn(Font font) => new()
|
|
{
|
|
Name = "Index",
|
|
HeaderText = "Index",
|
|
DefaultCellStyle = new DataGridViewCellStyle { Font = font },
|
|
Width = 40,
|
|
ReadOnly = true,
|
|
};
|
|
|
|
protected static DataGridViewTextBoxColumn MakeKeyColumn(Font font, string title = "Name") => new()
|
|
{
|
|
Name = title,
|
|
HeaderText = title,
|
|
DefaultCellStyle = new DataGridViewCellStyle
|
|
{
|
|
Font = font,
|
|
Alignment = DataGridViewContentAlignment.MiddleLeft,
|
|
},
|
|
};
|
|
|
|
protected static DataGridViewTextBoxColumn MakeValueNumberColumn(Font font, string title) => new()
|
|
{
|
|
Name = title,
|
|
HeaderText = title,
|
|
DefaultCellStyle = new DataGridViewCellStyle
|
|
{
|
|
Font = font,
|
|
Alignment = DataGridViewContentAlignment.MiddleRight,
|
|
NullValue = 0UL,
|
|
},
|
|
};
|
|
|
|
protected static DataGridViewCheckBoxColumn MakeValueBoolColumn(Font font, string title) => new()
|
|
{
|
|
Name = title,
|
|
HeaderText = title,
|
|
DefaultCellStyle = new DataGridViewCellStyle
|
|
{
|
|
Font = font,
|
|
Alignment = DataGridViewContentAlignment.MiddleCenter,
|
|
NullValue = false,
|
|
},
|
|
};
|
|
|
|
public abstract void Load();
|
|
public abstract void Save();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper that creates and manages a DataGridView for editing an <see cref="EventWorkStorage64{T}"/> block.
|
|
/// </summary>
|
|
/// <typeparam name="T">Value type for the storage. Use bool for flags, ulong for values.</typeparam>
|
|
public sealed record EventWorkGrid64<T> : EventWorkGridBase where T : struct, IEquatable<T>
|
|
{
|
|
private readonly EventWorkStorage64<T> Storage;
|
|
private readonly EventWorkLookup Names;
|
|
|
|
private const int ColumnIndex = 0;
|
|
private const int ColumnValue = 1;
|
|
private const int ColumnName = 2;
|
|
|
|
private EventWorkGrid64(EventWorkStorage64<T> storage, EventWorkLookup names, SplitContainer container, DataGridView grid, TextBox search)
|
|
: base(container, grid, search)
|
|
{
|
|
Storage = storage;
|
|
Names = names;
|
|
|
|
if (typeof(T) == typeof(ulong))
|
|
Grid.CellValidated += ValidateU64;
|
|
|
|
WireSearch(ApplyFilter);
|
|
ToggleAutoSize(true);
|
|
}
|
|
|
|
private void ToggleAutoSize(bool state)
|
|
{
|
|
var columns = Grid.Columns;
|
|
columns[ColumnIndex].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.ColumnHeader : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnValue].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnName].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.Fill : DataGridViewAutoSizeColumnMode.None;
|
|
}
|
|
|
|
private void ValidateU64(object? sender, DataGridViewCellEventArgs e)
|
|
{
|
|
if (e.ColumnIndex != ColumnValue)
|
|
return;
|
|
var row = Grid.Rows[e.RowIndex];
|
|
var cell = row.Cells[e.ColumnIndex];
|
|
var text = cell.Value?.ToString();
|
|
if (ulong.TryParse(text, CultureInfo.InvariantCulture, out _))
|
|
return;
|
|
|
|
WinFormsUtil.Alert("Please enter a valid unsigned integer value.");
|
|
var index = Convert.ToInt32(row.Cells[ColumnIndex].Value);
|
|
var saved = Storage.GetValue(index);
|
|
cell.Value = saved.ToString();
|
|
}
|
|
|
|
public static EventWorkGrid64<bool> CreateFlags(TabPage host, EventWorkFlagStorage storage, EventWorkLookup names)
|
|
{
|
|
var (container, search) = CreateContainerCommon();
|
|
host.Controls.Clear();
|
|
host.Controls.Add(container);
|
|
|
|
var font = new Font("Courier New", 8F, FontStyle.Regular, GraphicsUnit.Point, 0);
|
|
var dgv = new DoubleBufferedDataGridView
|
|
{
|
|
AllowUserToAddRows = false,
|
|
AllowUserToDeleteRows = false,
|
|
AllowUserToOrderColumns = true,
|
|
AllowUserToResizeRows = false,
|
|
AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells,
|
|
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
|
|
Dock = DockStyle.Fill,
|
|
RowHeadersVisible = false,
|
|
Margin = Padding.Empty,
|
|
DefaultCellStyle = new DataGridViewCellStyle { Font = font },
|
|
};
|
|
dgv.Columns.AddRange(MakeIndexColumn(font), MakeValueBoolColumn(font, "Value"), MakeKeyColumn(font));
|
|
container.Panel2.Controls.Add(dgv);
|
|
return new EventWorkGrid64<bool>(storage, names, container, dgv, search);
|
|
}
|
|
|
|
public static EventWorkGrid64<ulong> CreateValues(TabPage host, EventWorkValueStorage storage, EventWorkLookup names)
|
|
{
|
|
var (container, search) = CreateContainerCommon();
|
|
host.Controls.Clear();
|
|
host.Controls.Add(container);
|
|
|
|
var font = new Font("Courier New", 8F, FontStyle.Regular, GraphicsUnit.Point, 0);
|
|
var dgv = new DoubleBufferedDataGridView
|
|
{
|
|
AllowUserToAddRows = false,
|
|
AllowUserToDeleteRows = false,
|
|
AllowUserToOrderColumns = true,
|
|
AllowUserToResizeRows = false,
|
|
AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells,
|
|
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
|
|
Dock = DockStyle.Fill,
|
|
RowHeadersVisible = false,
|
|
Margin = Padding.Empty,
|
|
DefaultCellStyle = new DataGridViewCellStyle { Font = font },
|
|
};
|
|
dgv.Columns.AddRange(MakeIndexColumn(font), MakeValueNumberColumn(font, "Value"), MakeKeyColumn(font));
|
|
container.Panel2.Controls.Add(dgv);
|
|
return new EventWorkGrid64<ulong>(storage, names, container, dgv, search);
|
|
}
|
|
|
|
public override void Load()
|
|
{
|
|
var rows = Grid.Rows;
|
|
rows.Clear();
|
|
|
|
var count = Storage.Count;
|
|
rows.Add(count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var hash = Storage.GetKey(i);
|
|
var name = Names.GetName(hash);
|
|
var value = Storage.GetValue(i);
|
|
|
|
var row = rows[i];
|
|
var cells = row.Cells;
|
|
cells[ColumnIndex].Value = i;
|
|
cells[ColumnName].Value = name;
|
|
cells[ColumnValue].Value = value;
|
|
}
|
|
}
|
|
|
|
private void ApplyFilter(string text)
|
|
{
|
|
ToggleAutoSize(false);
|
|
bool has = text.Length != 0;
|
|
foreach (DataGridViewRow r in Grid.Rows)
|
|
{
|
|
if (!has)
|
|
{
|
|
r.Visible = true;
|
|
continue;
|
|
}
|
|
|
|
var name = r.Cells[ColumnName].Value?.ToString() ?? string.Empty;
|
|
r.Visible = name.Contains(text, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
ToggleAutoSize(true);
|
|
}
|
|
|
|
public override void Save()
|
|
{
|
|
var rows = Grid.Rows;
|
|
var count = Math.Min(Storage.Count, rows.Count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var cells = rows[i].Cells;
|
|
try
|
|
{
|
|
var name = cells[ColumnName].Value?.ToString() ?? string.Empty;
|
|
var hash = Names.GetHash(name.Trim());
|
|
var valObj = cells[ColumnValue].Value;
|
|
|
|
if (typeof(T) == typeof(bool))
|
|
{
|
|
var v = Convert.ToBoolean(valObj);
|
|
Storage.SetValue(i, (T)(object)v);
|
|
}
|
|
else if (typeof(T) == typeof(ulong))
|
|
{
|
|
var v = Convert.ToUInt64(valObj);
|
|
Storage.SetValue(i, (T)(object)v);
|
|
}
|
|
else
|
|
{
|
|
Storage.SetValue(i, (T)valObj!);
|
|
}
|
|
Storage.SetKey(i, hash);
|
|
}
|
|
catch
|
|
{
|
|
// Ignore invalid row
|
|
}
|
|
}
|
|
Storage.Compress();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Grid for EventWorkStorage128 with one key columns and two values.
|
|
/// </summary>
|
|
public sealed record EventWorkGridTuple : EventWorkGridBase
|
|
{
|
|
private readonly EventWorkValueStorageKey128 Storage;
|
|
private readonly EventWorkLookup Names;
|
|
|
|
private const int ColumnIndex = 0;
|
|
private const int ColumnKey = 1;
|
|
private const int ColumnValue1 = 2;
|
|
private const int ColumnValue2 = 3;
|
|
|
|
private EventWorkGridTuple(EventWorkValueStorageKey128 storage, EventWorkLookup names, SplitContainer container, DataGridView grid, TextBox search)
|
|
: base(container, grid, search)
|
|
{
|
|
Storage = storage;
|
|
Names = names;
|
|
|
|
Grid.CellValueChanged += ValidateCell;
|
|
WireSearch(ApplyFilter);
|
|
ToggleAutoSize(true);
|
|
}
|
|
|
|
public static EventWorkGridTuple CreateValues(TabPage host, EventWorkValueStorageKey128 storage, EventWorkLookup names)
|
|
{
|
|
var (container, search) = CreateContainerCommon();
|
|
host.Controls.Clear();
|
|
host.Controls.Add(container);
|
|
|
|
var font = new Font("Courier New", 8F, FontStyle.Regular, GraphicsUnit.Point, 0);
|
|
var dgv = new DoubleBufferedDataGridView
|
|
{
|
|
AllowUserToAddRows = false,
|
|
AllowUserToDeleteRows = false,
|
|
AllowUserToOrderColumns = true,
|
|
AllowUserToResizeRows = false,
|
|
AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells,
|
|
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
|
|
Dock = DockStyle.Fill,
|
|
RowHeadersVisible = false,
|
|
Margin = Padding.Empty,
|
|
DefaultCellStyle = new DataGridViewCellStyle { Font = font },
|
|
};
|
|
var c1 = MakeIndexColumn(font);
|
|
var c2 = MakeKeyColumn(font, "Key A");
|
|
var v1 = MakeValueNumberColumn(font, "Value 1");
|
|
var v2 = MakeValueNumberColumn(font, "Value 2");
|
|
|
|
dgv.Columns.AddRange(c1, c2, v1, v2);
|
|
container.Panel2.Controls.Add(dgv);
|
|
return new EventWorkGridTuple(storage, names, container, dgv, search);
|
|
}
|
|
|
|
private void ToggleAutoSize(bool state)
|
|
{
|
|
var columns = Grid.Columns;
|
|
columns[ColumnIndex].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.ColumnHeader : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnKey].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnValue1].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnValue2].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
}
|
|
|
|
private void ValidateCell(object? sender, DataGridViewCellEventArgs e)
|
|
{
|
|
if (e.ColumnIndex is ColumnValue1 or ColumnValue2)
|
|
{
|
|
var row = Grid.Rows[e.RowIndex];
|
|
var cell = row.Cells[e.ColumnIndex];
|
|
var text = cell.Value?.ToString() ?? string.Empty;
|
|
if (e.ColumnIndex is ColumnValue1 && long.TryParse(text, CultureInfo.InvariantCulture, out _))
|
|
return;
|
|
if (e.ColumnIndex is ColumnValue2 && ulong.TryParse(text, CultureInfo.InvariantCulture, out _))
|
|
return;
|
|
WinFormsUtil.Alert("Please enter a valid value.");
|
|
var i = Convert.ToInt32(row.Cells[ColumnIndex].Value);
|
|
var (a, b) = Storage.GetKey(i);
|
|
if (e.ColumnIndex == ColumnKey)
|
|
cell.Value = Names.GetName(a);
|
|
else if (e.ColumnIndex == ColumnValue1)
|
|
cell.Value = ((long)b).ToString();
|
|
else
|
|
cell.Value = Storage.GetValue(i);
|
|
}
|
|
}
|
|
|
|
private void ApplyFilter(string text)
|
|
{
|
|
ToggleAutoSize(false);
|
|
bool has = text.Length != 0;
|
|
foreach (DataGridViewRow r in Grid.Rows)
|
|
{
|
|
if (!has)
|
|
{
|
|
r.Visible = true;
|
|
continue;
|
|
}
|
|
var a = r.Cells[ColumnKey].Value?.ToString() ?? string.Empty;
|
|
r.Visible = a.Contains(text, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
ToggleAutoSize(true);
|
|
}
|
|
|
|
public override void Load()
|
|
{
|
|
var rows = Grid.Rows;
|
|
rows.Clear();
|
|
|
|
var count = Storage.Count;
|
|
rows.Add(count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var (a, b) = Storage.GetKey(i);
|
|
var value = Storage.GetValue(i);
|
|
|
|
var row = rows[i];
|
|
var cells = row.Cells;
|
|
cells[ColumnIndex].Value = i;
|
|
cells[ColumnKey].Value = Names.GetName(a);
|
|
cells[ColumnValue1].Value = ((long)b).ToString();
|
|
cells[ColumnValue2].Value = value.ToString();
|
|
}
|
|
}
|
|
|
|
public override void Save()
|
|
{
|
|
var rows = Grid.Rows;
|
|
var count = Math.Min(Storage.Count, rows.Count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var cells = rows[i].Cells;
|
|
try
|
|
{
|
|
var aText = cells[ColumnKey].Value?.ToString() ?? string.Empty;
|
|
var vText1 = cells[ColumnValue1].Value?.ToString() ?? "0";
|
|
var vText2 = cells[ColumnValue2].Value?.ToString() ?? "0";
|
|
ulong a = Names.GetHash(aText);
|
|
ulong b = long.TryParse(vText1, CultureInfo.InvariantCulture, out var bt) ? (ulong)bt : 0UL;
|
|
ulong v = ulong.TryParse(vText2, CultureInfo.InvariantCulture, out var vt) ? vt : 0UL;
|
|
|
|
Storage.SetValue(i, v);
|
|
Storage.SetKey(i, a, b);
|
|
}
|
|
catch
|
|
{
|
|
// Ignore invalid row
|
|
}
|
|
}
|
|
Storage.Compress();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Grid for EventWorkStorage192 with separate key columns A, B, and C, and one value.
|
|
/// </summary>
|
|
public sealed record EventWorkGrid128 : EventWorkGridBase
|
|
{
|
|
private readonly EventWorkValueStorageKey128 Storage;
|
|
private readonly EventWorkLookup Names;
|
|
|
|
private const int ColumnIndex = 0;
|
|
private const int ColumnKeyA = 1;
|
|
private const int ColumnKeyB = 2;
|
|
private const int ColumnValue = 3;
|
|
|
|
private EventWorkGrid128(EventWorkValueStorageKey128 storage, EventWorkLookup names, SplitContainer container, DataGridView grid, TextBox search)
|
|
: base(container, grid, search)
|
|
{
|
|
Storage = storage;
|
|
Names = names;
|
|
|
|
Grid.CellValidated += ValidateValue;
|
|
WireSearch(ApplyFilter);
|
|
ToggleAutoSize(true);
|
|
}
|
|
|
|
public static EventWorkGrid128 CreateValues(TabPage host, EventWorkValueStorageKey128 storage, EventWorkLookup names)
|
|
{
|
|
var (container, search) = CreateContainerCommon();
|
|
host.Controls.Clear();
|
|
host.Controls.Add(container);
|
|
|
|
var font = new Font("Courier New", 8F, FontStyle.Regular, GraphicsUnit.Point, 0);
|
|
var dgv = new DoubleBufferedDataGridView
|
|
{
|
|
AllowUserToAddRows = false,
|
|
AllowUserToDeleteRows = false,
|
|
AllowUserToOrderColumns = true,
|
|
AllowUserToResizeRows = false,
|
|
AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells,
|
|
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
|
|
Dock = DockStyle.Fill,
|
|
RowHeadersVisible = false,
|
|
Margin = Padding.Empty,
|
|
DefaultCellStyle = new DataGridViewCellStyle { Font = font },
|
|
};
|
|
|
|
var c1 = MakeIndexColumn(font);
|
|
var c2 = MakeKeyColumn(font, "Key A");
|
|
var c3 = MakeKeyColumn(font, "Key B");
|
|
var v1 = MakeValueNumberColumn(font, "Value");
|
|
|
|
dgv.Columns.AddRange(c1, c2, c3, v1);
|
|
container.Panel2.Controls.Add(dgv);
|
|
return new EventWorkGrid128(storage, names, container, dgv, search);
|
|
}
|
|
|
|
private void ToggleAutoSize(bool state)
|
|
{
|
|
var columns = Grid.Columns;
|
|
columns[ColumnIndex].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.ColumnHeader : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnKeyA].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnKeyB].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnValue].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
}
|
|
|
|
private void ValidateValue(object? sender, DataGridViewCellEventArgs e)
|
|
{
|
|
if (e.ColumnIndex is ColumnValue)
|
|
{
|
|
var row = Grid.Rows[e.RowIndex];
|
|
var cell = row.Cells[e.ColumnIndex];
|
|
var text = cell.Value?.ToString() ?? string.Empty;
|
|
if (ulong.TryParse(text, CultureInfo.InvariantCulture, out _))
|
|
return;
|
|
WinFormsUtil.Alert("Please enter a valid value.");
|
|
var i = Convert.ToInt32(row.Cells[ColumnIndex].Value);
|
|
cell.Value = Storage.GetValue(i);
|
|
}
|
|
}
|
|
|
|
private void ApplyFilter(string text)
|
|
{
|
|
ToggleAutoSize(false);
|
|
bool has = text.Length != 0;
|
|
foreach (DataGridViewRow r in Grid.Rows)
|
|
{
|
|
if (!has)
|
|
{
|
|
r.Visible = true;
|
|
continue;
|
|
}
|
|
var a = r.Cells[ColumnKeyA].Value?.ToString() ?? string.Empty;
|
|
var b = r.Cells[ColumnKeyB].Value?.ToString() ?? string.Empty;
|
|
r.Visible = a.Contains(text, StringComparison.OrdinalIgnoreCase) ||
|
|
b.Contains(text, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
ToggleAutoSize(true);
|
|
}
|
|
|
|
public override void Load()
|
|
{
|
|
var rows = Grid.Rows;
|
|
rows.Clear();
|
|
|
|
var count = Storage.Count;
|
|
rows.Add(count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var (a, b) = Storage.GetKey(i);
|
|
var value = Storage.GetValue(i);
|
|
|
|
var row = rows[i];
|
|
var cells = row.Cells;
|
|
cells[ColumnIndex].Value = i;
|
|
cells[ColumnKeyA].Value = Names.GetName(a);
|
|
cells[ColumnKeyB].Value = Names.GetName(b);
|
|
cells[ColumnValue].Value = value.ToString();
|
|
}
|
|
}
|
|
|
|
public override void Save()
|
|
{
|
|
var rows = Grid.Rows;
|
|
var count = Math.Min(Storage.Count, rows.Count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var cells = rows[i].Cells;
|
|
try
|
|
{
|
|
var aText = cells[ColumnKeyA].Value?.ToString() ?? string.Empty;
|
|
var bText = cells[ColumnKeyB].Value?.ToString() ?? string.Empty;
|
|
var vText = cells[ColumnValue].Value?.ToString() ?? "0";
|
|
ulong a = Names.GetHash(aText);
|
|
ulong b = Names.GetHash(bText);
|
|
ulong v = ulong.TryParse(vText, CultureInfo.InvariantCulture, out var vt) ? vt : 0UL;
|
|
|
|
Storage.SetValue(i, v);
|
|
Storage.SetKey(i, a, b);
|
|
}
|
|
catch
|
|
{
|
|
// Ignore invalid row
|
|
}
|
|
}
|
|
Storage.Compress();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Grid for EventWorkStorage192 with separate key columns A, B, and C, and one value.
|
|
/// </summary>
|
|
public sealed record EventWorkGrid192 : EventWorkGridBase
|
|
{
|
|
private readonly EventWorkValueStorageKey192 Storage;
|
|
private readonly EventWorkLookup Names;
|
|
|
|
private const int ColumnIndex = 0;
|
|
private const int ColumnKeyA = 1;
|
|
private const int ColumnKeyB = 2;
|
|
private const int ColumnKeyC = 3;
|
|
private const int ColumnValue = 4;
|
|
|
|
private EventWorkGrid192(EventWorkValueStorageKey192 storage, EventWorkLookup names, SplitContainer container, DataGridView grid, TextBox search)
|
|
: base(container, grid, search)
|
|
{
|
|
Storage = storage;
|
|
Names = names;
|
|
|
|
Grid.CellValidated += ValidateValue;
|
|
WireSearch(ApplyFilter);
|
|
ToggleAutoSize(true);
|
|
}
|
|
|
|
public static EventWorkGrid192 CreateValues(TabPage host, EventWorkValueStorageKey192 storage, EventWorkLookup names)
|
|
{
|
|
var (container, search) = CreateContainerCommon();
|
|
host.Controls.Clear();
|
|
host.Controls.Add(container);
|
|
|
|
var font = new Font("Courier New", 8F, FontStyle.Regular, GraphicsUnit.Point, 0);
|
|
var dgv = new DoubleBufferedDataGridView
|
|
{
|
|
AllowUserToAddRows = false,
|
|
AllowUserToDeleteRows = false,
|
|
AllowUserToOrderColumns = true,
|
|
AllowUserToResizeRows = false,
|
|
AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells,
|
|
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
|
|
Dock = DockStyle.Fill,
|
|
RowHeadersVisible = false,
|
|
Margin = Padding.Empty,
|
|
DefaultCellStyle = new DataGridViewCellStyle { Font = font },
|
|
};
|
|
|
|
var c1 = MakeIndexColumn(font);
|
|
var c2 = MakeKeyColumn(font, "Key A");
|
|
var c3 = MakeKeyColumn(font, "Key B");
|
|
var c4 = MakeKeyColumn(font, "Key C");
|
|
var v1 = MakeValueNumberColumn(font, "Value");
|
|
|
|
dgv.Columns.AddRange(c1, c2, c3, c4, v1);
|
|
container.Panel2.Controls.Add(dgv);
|
|
return new EventWorkGrid192(storage, names, container, dgv, search);
|
|
}
|
|
|
|
private void ToggleAutoSize(bool state)
|
|
{
|
|
var columns = Grid.Columns;
|
|
columns[ColumnIndex].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.ColumnHeader : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnKeyA].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnKeyB].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnKeyC].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
columns[ColumnValue].AutoSizeMode = state ? DataGridViewAutoSizeColumnMode.AllCells : DataGridViewAutoSizeColumnMode.None;
|
|
}
|
|
|
|
private void ValidateValue(object? sender, DataGridViewCellEventArgs e)
|
|
{
|
|
if (e.ColumnIndex is ColumnValue)
|
|
{
|
|
var row = Grid.Rows[e.RowIndex];
|
|
var cell = row.Cells[e.ColumnIndex];
|
|
var text = cell.Value?.ToString() ?? string.Empty;
|
|
if (ulong.TryParse(text, CultureInfo.InvariantCulture, out _))
|
|
return;
|
|
WinFormsUtil.Alert("Please enter a valid value.");
|
|
var i = Convert.ToInt32(row.Cells[ColumnIndex].Value);
|
|
cell.Value = Storage.GetValue(i);
|
|
}
|
|
}
|
|
|
|
private void ApplyFilter(string text)
|
|
{
|
|
ToggleAutoSize(false);
|
|
bool has = text.Length != 0;
|
|
foreach (DataGridViewRow r in Grid.Rows)
|
|
{
|
|
if (!has)
|
|
{
|
|
r.Visible = true;
|
|
continue;
|
|
}
|
|
var a = r.Cells[ColumnKeyA].Value?.ToString() ?? string.Empty;
|
|
var b = r.Cells[ColumnKeyB].Value?.ToString() ?? string.Empty;
|
|
var c = r.Cells[ColumnKeyC].Value?.ToString() ?? string.Empty;
|
|
r.Visible = a.Contains(text, StringComparison.OrdinalIgnoreCase) ||
|
|
b.Contains(text, StringComparison.OrdinalIgnoreCase) ||
|
|
c.Contains(text, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
ToggleAutoSize(true);
|
|
}
|
|
|
|
public override void Load()
|
|
{
|
|
var rows = Grid.Rows;
|
|
rows.Clear();
|
|
|
|
var count = Storage.Count;
|
|
rows.Add(count);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var (a, b, c) = Storage.GetKey(i);
|
|
var value = Storage.GetValue(i);
|
|
|
|
var row = rows[i];
|
|
var cells = row.Cells;
|
|
cells[ColumnIndex].Value = i;
|
|
cells[ColumnKeyA].Value = Names.GetName(a);
|
|
cells[ColumnKeyB].Value = Names.GetName(b);
|
|
cells[ColumnKeyC].Value = Names.GetName(c);
|
|
cells[ColumnValue].Value = value.ToString();
|
|
}
|
|
}
|
|
|
|
public override void Save()
|
|
{
|
|
var rows = Grid.Rows;
|
|
var count = Math.Min(Storage.Count, rows.Count);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
var cells = rows[i].Cells;
|
|
try
|
|
{
|
|
var aText = cells[ColumnKeyA].Value?.ToString() ?? string.Empty;
|
|
var bText = cells[ColumnKeyB].Value?.ToString() ?? string.Empty;
|
|
var cText = cells[ColumnKeyC].Value?.ToString() ?? string.Empty;
|
|
var vText = cells[ColumnValue].Value?.ToString() ?? "0";
|
|
ulong a = Names.GetHash(aText);
|
|
ulong b = Names.GetHash(bText);
|
|
ulong c = Names.GetHash(cText);
|
|
ulong v = ulong.TryParse(vText, CultureInfo.InvariantCulture, out var vt) ? vt : 0UL;
|
|
|
|
Storage.SetValue(i, v);
|
|
Storage.SetKey(i, a, b, c);
|
|
}
|
|
catch
|
|
{
|
|
// Ignore invalid row
|
|
}
|
|
}
|
|
Storage.Compress();
|
|
}
|
|
}
|