mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-03-21 17:48:28 -05:00
505 lines
17 KiB
C#
505 lines
17 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using PKHeX.Core;
|
|
using PKHeX.WinForms.Controls;
|
|
using static PKHeX.Core.MessageStrings;
|
|
|
|
namespace PKHeX.WinForms;
|
|
|
|
public sealed partial class SAV_EventFlags : Form
|
|
{
|
|
private readonly EventWorkspace<IEventFlag37, ushort> Editor;
|
|
private readonly Dictionary<int, NumericUpDown> WorkDict = [];
|
|
private readonly Dictionary<int, (DataGridView Grid, int RowIndex)> FlagDict = [];
|
|
|
|
private bool editing;
|
|
|
|
public SAV_EventFlags(IEventFlag37 sav, GameVersion version)
|
|
{
|
|
InitializeComponent();
|
|
WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage);
|
|
|
|
var editor = Editor = new EventWorkspace<IEventFlag37, ushort>(sav, version);
|
|
AllowDrop = true;
|
|
DragEnter += Main_DragEnter;
|
|
DragDrop += Main_DragDrop;
|
|
tabControl1.SelectedTab = GB_Research; // hack to get the first tab to render in dark mode correctly
|
|
|
|
editing = true;
|
|
CB_Stats.Items.Clear();
|
|
for (int i = 0; i < editor.Values.Length; i++)
|
|
CB_Stats.Items.Add(i.ToString());
|
|
|
|
AddFlagList(editor.Labels, editor.Flags);
|
|
AddConstList(editor.Labels, editor.Values);
|
|
|
|
if (Application.IsDarkModeEnabled)
|
|
{
|
|
WinFormsTranslator.ReformatDark(TC_Flags);
|
|
WinFormsTranslator.ReformatDark(TC_Const);
|
|
}
|
|
|
|
Text = $"{Text} ({version})";
|
|
|
|
if (CB_Stats.Items.Count > 0)
|
|
{
|
|
CB_Stats.SelectedIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
L_Stats.Visible = CB_Stats.Visible = MT_Stat.Visible = false;
|
|
tabControl1.TabPages.Remove(GB_Constants);
|
|
}
|
|
NUD_Flag.Maximum = editor.Flags.Length - 1;
|
|
NUD_Flag.Value = 0;
|
|
c_CustomFlag.Checked = editor.Flags[0];
|
|
editing = false;
|
|
}
|
|
|
|
protected override void OnShown(EventArgs e)
|
|
{
|
|
base.OnShown(e);
|
|
tabControl1.SelectedIndex = 0;
|
|
}
|
|
|
|
private void B_Cancel_Click(object sender, EventArgs e)
|
|
{
|
|
Close();
|
|
}
|
|
|
|
private void B_Save_Click(object sender, EventArgs e)
|
|
{
|
|
Editor.Save();
|
|
Close();
|
|
}
|
|
|
|
private void AddFlagList(EventLabelCollection list, bool[] values)
|
|
{
|
|
FlagDict.Clear();
|
|
TC_Flags.TabPages.Clear();
|
|
|
|
var labels = list.Flag;
|
|
if (labels.Count == 0)
|
|
{
|
|
TC_Flags.Visible = false;
|
|
var research = new Label { Text = MsgResearchRequired, Name = "TLP_Flags_Research", ForeColor = WinFormsUtil.ColorWarn, AutoSize = true, Location = new Point(20, 20) };
|
|
GB_Flags.Controls.Add(research);
|
|
return;
|
|
}
|
|
|
|
foreach (var group in labels.GroupBy(z => z.Type).OrderBy(z => (int)z.Key))
|
|
{
|
|
var tab = new TabPage
|
|
{
|
|
Name = $"Tab_F{group.Key}",
|
|
Text = WinFormsTranslator.TranslateEnum(group.Key, Main.CurrentLanguage),
|
|
};
|
|
|
|
var grid = CreateFlagGrid();
|
|
var search = CreateSearchBox(text => ApplyGridFilter(grid, text));
|
|
var host = CreateSearchHost(search, grid);
|
|
tab.Controls.Add(host);
|
|
TC_Flags.TabPages.Add(tab);
|
|
|
|
var cFlag = new DataGridViewCheckBoxColumn
|
|
{
|
|
DisplayIndex = 0,
|
|
Width = 20,
|
|
SortMode = DataGridViewColumnSortMode.NotSortable,
|
|
};
|
|
|
|
var cLabel = new DataGridViewTextBoxColumn
|
|
{
|
|
DisplayIndex = 1,
|
|
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
|
|
ReadOnly = true,
|
|
SortMode = DataGridViewColumnSortMode.NotSortable,
|
|
};
|
|
|
|
cFlag.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
|
|
cLabel.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;
|
|
|
|
grid.Columns.Add(cFlag);
|
|
grid.Columns.Add(cLabel);
|
|
|
|
var grouped = group.ToList();
|
|
grid.Rows.Add(grouped.Count);
|
|
|
|
for (var i = 0; i < grouped.Count; i++)
|
|
FlagDict[grouped[i].Index] = (grid, i);
|
|
|
|
for (var i = 0; i < grouped.Count; i++)
|
|
{
|
|
var (name, index, _) = grouped[i];
|
|
var cells = grid.Rows[i].Cells;
|
|
cells[0].Value = values[index];
|
|
cells[1].Value = name;
|
|
}
|
|
|
|
ApplyGridFilter(grid, string.Empty);
|
|
|
|
grid.CellValueChanged += (_, e) =>
|
|
{
|
|
if (e.ColumnIndex != 0 || e.RowIndex == -1)
|
|
return;
|
|
|
|
var chk = (bool)grid.Rows[e.RowIndex].Cells[0].Value!;
|
|
var index = grouped[e.RowIndex].Index;
|
|
values[index] = chk;
|
|
if (NUD_Flag.Value == index)
|
|
c_CustomFlag.Checked = chk;
|
|
};
|
|
grid.CellMouseUp += (_, e) =>
|
|
{
|
|
if (e.RowIndex == -1)
|
|
return;
|
|
|
|
if (e.ColumnIndex == 0)
|
|
{
|
|
grid.EndEdit();
|
|
return;
|
|
}
|
|
|
|
if (e.ColumnIndex != 1)
|
|
return;
|
|
|
|
var chk = (bool)grid.Rows[e.RowIndex].Cells[0].Value!;
|
|
grid.Rows[e.RowIndex].Cells[0].Value = !chk;
|
|
var index = grouped[e.RowIndex].Index;
|
|
values[index] = !chk;
|
|
if (NUD_Flag.Value == index)
|
|
c_CustomFlag.Checked = !chk;
|
|
};
|
|
}
|
|
}
|
|
|
|
private static void ApplyGridFilter(DataGridView grid, string text)
|
|
{
|
|
var query = text.Trim();
|
|
var hasQuery = query.Length != 0;
|
|
foreach (DataGridViewRow row in grid.Rows)
|
|
{
|
|
if (row.Cells.Count < 2)
|
|
continue;
|
|
if (!hasQuery)
|
|
{
|
|
row.Visible = true;
|
|
continue;
|
|
}
|
|
|
|
var value = row.Cells[1].Value?.ToString() ?? string.Empty;
|
|
row.Visible = value.Contains(query, StringComparison.CurrentCultureIgnoreCase);
|
|
}
|
|
}
|
|
|
|
private static DoubleBufferedDataGridView CreateFlagGrid() => new()
|
|
{
|
|
AllowUserToAddRows = false,
|
|
AllowUserToDeleteRows = false,
|
|
AllowUserToResizeColumns = false,
|
|
AllowUserToResizeRows = false,
|
|
BackgroundColor = SystemColors.ControlLightLight,
|
|
BorderStyle = BorderStyle.None,
|
|
ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single,
|
|
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
|
|
ColumnHeadersVisible = false,
|
|
Dock = DockStyle.Fill,
|
|
EditMode = DataGridViewEditMode.EditOnEnter,
|
|
Margin = Padding.Empty,
|
|
MultiSelect = false,
|
|
RowHeadersVisible = false,
|
|
SelectionMode = DataGridViewSelectionMode.CellSelect,
|
|
ShowEditingIcon = false,
|
|
};
|
|
|
|
private void AddConstList(EventLabelCollection list, ushort[] values)
|
|
{
|
|
WorkDict.Clear();
|
|
TC_Const.TabPages.Clear();
|
|
|
|
var labels = list.Work;
|
|
if (labels.Count == 0)
|
|
{
|
|
TC_Const.Visible = false;
|
|
GB_Constants.Controls.Add(new Label { Text = MsgResearchRequired, Name = "TLP_Const_Research", ForeColor = WinFormsUtil.ColorWarn, AutoSize = true, Location = new Point(20, 20) });
|
|
return;
|
|
}
|
|
|
|
foreach (var group in labels.GroupBy(z => z.Type).OrderBy(z => (int)z.Key))
|
|
{
|
|
var tab = new TabPage
|
|
{
|
|
Name = $"Tab_W{group.Key}",
|
|
Text = WinFormsTranslator.TranslateEnum(group.Key, Main.CurrentLanguage),
|
|
};
|
|
|
|
var panel = CreateConstPanel();
|
|
TC_Const.TabPages.Add(tab);
|
|
|
|
var grouped = group.ToList();
|
|
var rows = new List<(string Search, Label Label, ComboBox Combo, NumericUpDown Numeric)>(grouped.Count);
|
|
for (var i = 0; i < grouped.Count; i++)
|
|
{
|
|
var entry = grouped[i];
|
|
var lbl = new Label { Text = entry.Name, Margin = Padding.Empty, AutoSize = true };
|
|
var mtb = new NumericUpDown
|
|
{
|
|
Maximum = ushort.MaxValue,
|
|
Minimum = ushort.MinValue,
|
|
Margin = Padding.Empty,
|
|
Width = 50,
|
|
};
|
|
|
|
var map = entry.PredefinedValues.Select(z => new ComboItem(z.Name, z.Value)).ToList();
|
|
var cb = new ComboBox
|
|
{
|
|
Margin = Padding.Empty,
|
|
Width = 150,
|
|
DropDownStyle = ComboBoxStyle.DropDownList,
|
|
BindingContext = BindingContext,
|
|
};
|
|
if (Application.IsDarkModeEnabled)
|
|
WinFormsTranslator.ReformatDark(cb);
|
|
var font = cb.Font;
|
|
cb.DropDownWidth = entry.PredefinedValues.Max(z => TextRenderer.MeasureText(z.Name, font).Width);
|
|
cb.InitializeBinding();
|
|
cb.DataSource = map;
|
|
|
|
lbl.Click += (_, _) => mtb.Value = 0;
|
|
bool updating = false;
|
|
mtb.ValueChanged += ChangeConstValue;
|
|
void ChangeConstValue(object? sender, EventArgs e)
|
|
{
|
|
if (updating)
|
|
return;
|
|
|
|
updating = true;
|
|
var value = (ushort)mtb.Value;
|
|
var (_, valueID) = map.Find(z => z.Value == value) ?? map[0];
|
|
if (WinFormsUtil.GetIndex(cb) != valueID)
|
|
cb.SelectedValue = valueID;
|
|
|
|
Editor.Values[entry.Index] = value;
|
|
if (CB_Stats.SelectedIndex == entry.Index)
|
|
MT_Stat.Text = ((int)mtb.Value).ToString();
|
|
updating = false;
|
|
}
|
|
cb.SelectedValueChanged += (_, _) =>
|
|
{
|
|
if (editing || updating)
|
|
return;
|
|
var value = WinFormsUtil.GetIndex(cb);
|
|
mtb.Value = value == NamedEventConst.CustomMagicValue ? 0 : value;
|
|
};
|
|
|
|
mtb.Value = values[entry.Index];
|
|
if (mtb.Value == 0)
|
|
ChangeConstValue(this, EventArgs.Empty);
|
|
|
|
rows.Add((entry.Name, lbl, cb, mtb));
|
|
WorkDict.Add(entry.Index, mtb);
|
|
}
|
|
|
|
ApplyConstFilter(panel, rows, string.Empty);
|
|
var search = CreateSearchBox(text => ApplyConstFilter(panel, rows, text));
|
|
var host = CreateSearchHost(search, panel);
|
|
tab.Controls.Add(host);
|
|
}
|
|
}
|
|
|
|
private static void ApplyConstFilter(TableLayoutPanel panel, IReadOnlyList<(string Search, Label Label, ComboBox Combo, NumericUpDown Numeric)> rows, string text)
|
|
{
|
|
var query = text.Trim();
|
|
var hasQuery = query.Length != 0;
|
|
|
|
panel.SuspendLayout();
|
|
panel.Controls.Clear();
|
|
var rowIndex = 0;
|
|
for (var i = 0; i < rows.Count; i++)
|
|
{
|
|
var row = rows[i];
|
|
if (hasQuery && !row.Search.Contains(query, StringComparison.CurrentCultureIgnoreCase))
|
|
continue;
|
|
|
|
panel.Controls.Add(row.Label, 0, rowIndex);
|
|
panel.Controls.Add(row.Combo, 1, rowIndex);
|
|
panel.Controls.Add(row.Numeric, 2, rowIndex);
|
|
rowIndex++;
|
|
}
|
|
panel.ResumeLayout();
|
|
}
|
|
|
|
private TextBox CreateSearchBox(Action<string> applyFilter)
|
|
{
|
|
var box = new TextBox
|
|
{
|
|
PlaceholderText = "Search...",
|
|
Dock = DockStyle.Top,
|
|
Margin = Padding.Empty,
|
|
};
|
|
if (Application.IsDarkModeEnabled)
|
|
WinFormsTranslator.ReformatDark(box);
|
|
|
|
var timer = new Timer { Interval = 150 };
|
|
timer.Tick += (_, _) =>
|
|
{
|
|
timer.Stop();
|
|
applyFilter(box.Text);
|
|
};
|
|
box.TextChanged += (_, _) =>
|
|
{
|
|
timer.Stop();
|
|
timer.Start();
|
|
};
|
|
box.Disposed += (_, _) => timer.Dispose();
|
|
return box;
|
|
}
|
|
|
|
private static Panel CreateSearchHost(TextBox search, Control content)
|
|
{
|
|
var host = new Panel
|
|
{
|
|
Dock = DockStyle.Fill,
|
|
Margin = Padding.Empty,
|
|
};
|
|
content.Dock = DockStyle.Fill;
|
|
host.Controls.Add(content);
|
|
host.Controls.Add(search);
|
|
return host;
|
|
}
|
|
|
|
private static TableLayoutPanel CreateConstPanel()
|
|
{
|
|
var panel = new TableLayoutPanel
|
|
{
|
|
AutoScroll = true,
|
|
ColumnCount = 3,
|
|
Dock = DockStyle.Fill,
|
|
Margin = new Padding(4),
|
|
Name = "TLP_Const",
|
|
RowCount = 1,
|
|
};
|
|
panel.ColumnStyles.Add(new ColumnStyle());
|
|
panel.ColumnStyles.Add(new ColumnStyle());
|
|
panel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 485F));
|
|
panel.RowStyles.Add(new RowStyle());
|
|
panel.RowStyles.Add(new RowStyle());
|
|
panel.Scroll += WinFormsUtil.PanelScroll;
|
|
return panel;
|
|
}
|
|
|
|
private void ChangeCustomBool(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
editing = true;
|
|
var index = (int)NUD_Flag.Value;
|
|
Editor.Flags[index] = c_CustomFlag.Checked;
|
|
if (FlagDict.TryGetValue(index, out var result))
|
|
result.Grid.Rows[result.RowIndex].Cells[0].Value = c_CustomFlag.Checked;
|
|
editing = false;
|
|
}
|
|
|
|
private void ChangeCustomFlag(object sender, EventArgs e)
|
|
{
|
|
int flag = (int)NUD_Flag.Value;
|
|
c_CustomFlag.Checked = Editor.Flags[flag];
|
|
}
|
|
|
|
private void ChangeCustomFlag(object sender, KeyEventArgs e) => ChangeCustomFlag(sender, (EventArgs)e);
|
|
|
|
private void ChangeConstantIndex(object sender, EventArgs e)
|
|
{
|
|
var constants = Editor.Values;
|
|
var index = CB_Stats.SelectedIndex;
|
|
MT_Stat.Text = constants[index].ToString();
|
|
}
|
|
|
|
private void ChangeCustomConst(object sender, EventArgs e)
|
|
{
|
|
if (editing)
|
|
return;
|
|
editing = true;
|
|
var index = CB_Stats.SelectedIndex;
|
|
var parse = ushort.TryParse(MT_Stat.Text, out var value) ? value : (ushort)0;
|
|
Editor.Values[index] = parse;
|
|
if (WorkDict.TryGetValue(index, out var mtb))
|
|
mtb.Value = parse;
|
|
editing = false;
|
|
}
|
|
|
|
private void ChangeSAV(object sender, EventArgs e) => ChangeSAV();
|
|
|
|
private void ChangeSAV()
|
|
{
|
|
if (TB_NewSAV.Text.Length != 0 && TB_OldSAV.Text.Length != 0)
|
|
DiffSaves();
|
|
}
|
|
|
|
private void OpenSAV(object sender, EventArgs e)
|
|
{
|
|
using var ofd = new OpenFileDialog();
|
|
ofd.Title = MsgFileLoadSaveSelectGame;
|
|
if (ofd.ShowDialog() == DialogResult.OK)
|
|
LoadSAV(sender, ofd.FileName);
|
|
}
|
|
|
|
private void LoadSAV(object sender, string path)
|
|
{
|
|
var dest = sender == B_LoadOld ? TB_OldSAV : TB_NewSAV;
|
|
dest.Text = path;
|
|
}
|
|
|
|
private void DiffSaves()
|
|
{
|
|
var diff = new EventBlockDiff<IEventFlag37, ushort>(TB_OldSAV.Text, TB_NewSAV.Text);
|
|
if (diff.Message != EventWorkDiffCompatibility.Valid)
|
|
{
|
|
WinFormsUtil.Alert(diff.Message.GetMessage());
|
|
return;
|
|
}
|
|
|
|
TB_IsSet.Text = string.Join(", ", diff.SetFlags.Select(z => $"{z:0000}"));
|
|
TB_UnSet.Text = string.Join(", ", diff.ClearedFlags.Select(z => $"{z:0000}"));
|
|
|
|
if (diff.WorkDiff.Count == 0)
|
|
{
|
|
WinFormsUtil.Alert("No Event Constant diff found.");
|
|
return;
|
|
}
|
|
|
|
var promptCopy = WinFormsUtil.Prompt(MessageBoxButtons.YesNo, "Copy Event Constant diff to clipboard?");
|
|
if (promptCopy == DialogResult.Yes)
|
|
WinFormsUtil.SetClipboardText(string.Join(Environment.NewLine, diff.WorkDiff));
|
|
}
|
|
|
|
private static void Main_DragEnter(object? sender, DragEventArgs? e)
|
|
{
|
|
if (e?.Data is null)
|
|
return;
|
|
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
|
e.Effect = DragDropEffects.Copy;
|
|
}
|
|
|
|
private void Main_DragDrop(object? sender, DragEventArgs? e)
|
|
{
|
|
if (e?.Data?.GetData(DataFormats.FileDrop) is not string[] { Length: not 0 } files)
|
|
return;
|
|
|
|
foreach (var file in files)
|
|
{
|
|
var result = this.SelectNewOld(file, B_LoadNew.Text, B_LoadOld.Text);
|
|
if (result == DualDiffSelection.New)
|
|
TB_NewSAV.Text = file;
|
|
else if (result == DualDiffSelection.Old)
|
|
TB_OldSAV.Text = file;
|
|
else if (ModifierKeys == Keys.Escape)
|
|
return;
|
|
}
|
|
ChangeSAV();
|
|
}
|
|
}
|