Files
NethackHelper/NethackHelper/ItemDisplay.cs

290 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using static System.Windows.Forms.ListViewItem;
using System.ComponentModel;
namespace NethackHelper {
public partial class ItemDisplay : UserControl {
public const string UNIDENTIFIED = "[randomized]";
public const string TOOLTIP_SUFFIX = " *";
public static readonly Color SELECTED_COLOR = Color.Black;
public static readonly Color UNSELECTED_COLOR = Color.Gray;
private static readonly Dictionary<ItemAttribute, Func<Item, string>> COLUMN_MAP = new() {
[ItemAttribute.Name] = item => item.Name,
[ItemAttribute.Cost] = item => item.Cost.ToString(),
[ItemAttribute.Ink] = item => item.Ink.ToString(),
[ItemAttribute.School] = item => item.School.ToString(),
[ItemAttribute.Appearance] = item => item.Appearance ?? UNIDENTIFIED,
[ItemAttribute.UsuallyCursed] = item => item.UsuallyCursed ? "C" : string.Empty,
[ItemAttribute.Slot] = item => item.Slot.ToString(),
};
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func<int, string> CostFormatter { get; set; } = cost => cost.ToString();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public List<ItemDisplayColumn> Columns { get; set; } = new();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ItemAttribute Grouping { get; set; } = ItemAttribute.Cost;
private List<int> GroupCosts { get; set; } = [];
private Dictionary<string, string?> AppearanceMap { get; set; } = new();
private Dictionary<string, Item> ItemMap { get; set; } = new();
private Dictionary<string, ContextMenuStrip> appearanceContextMenus = new();
public ItemDisplay() {
InitializeComponent();
this.itemListView.ListViewItemSorter = Comparer<ListViewItem>.Create(this.CompareItems);
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
if (this.Columns != null) {
this.itemListView.Columns.Clear();
foreach (var column in this.Columns) {
var header = column.Attribute.ToString();
if (column.Attribute == ItemAttribute.UsuallyCursed) {
header = string.Empty;
}
this.itemListView.Columns.Add(header, column.Width);
}
}
}
public void DisplayItemList(ItemList itemList, IdentificationRecord? idRecord) {
this.itemListView.Groups.Clear();
this.itemListView.Items.Clear();
this.AppearanceMap.Clear();
this.ItemMap.Clear();
this.appearanceContextMenus.Clear();
var appearanceSlots = itemList.Appearances.Select(app => app.Slot).Distinct().ToList();
var appearanceMap = itemList.Appearances.ToDictionary(appearance => appearance.Appearance);
foreach (var slot in appearanceSlots) {
ContextMenuStrip contextMenuStrip = new();
var appearances = itemList.Appearances.Where(app => app.Slot == slot).OrderBy(appearance => appearance.Appearance).ToList();
foreach (var appearance in appearances) {
contextMenuStrip.Items.Add(appearance.Appearance, null, (sender, e) => this.PickAppearance(appearance));
}
this.appearanceContextMenus.Add(slot, contextMenuStrip);
}
var groups = itemList.Items.Select(item => COLUMN_MAP[this.Grouping].Invoke(item)).Distinct().Order().ToList();
if (this.Grouping == ItemAttribute.Cost) {
this.GroupCosts = itemList.Items.Select(item => item.Cost).Distinct().Order().ToList();
groups = this.GroupCosts.Select(cost => cost.ToString()).ToList();
}
var groupDictionary = new Dictionary<string, ListViewGroup>();
foreach (var group in groups) {
var listViewGroup = new ListViewGroup(group, HorizontalAlignment.Center);
this.itemListView.Groups.Add(listViewGroup);
groupDictionary.Add(group, listViewGroup);
}
foreach (var item in itemList.Items) {
bool identified = false;
ItemRecord record = default;
if (idRecord != null) {
if (idRecord.TryGetValue(item.Name, out var identity)) {
record = identity;
identified = true;
}
} else if (item.Appearance != null) {
record = new ItemRecord() {
Name = item.Name,
Appearance = item.Appearance,
};
identified = item.UniqueAppearance;
}
this.ItemMap[item.Name] = item;
this.AppearanceMap[item.Name] = record.Appearance;
var columns = this.Columns.Select(col => COLUMN_MAP[col.Attribute].Invoke(item)).ToArray();
var groupName = COLUMN_MAP[this.Grouping].Invoke(item);
var listViewItem = new ListViewItem(columns, groupDictionary[groupName]);
listViewItem.UseItemStyleForSubItems = false;
var baseFont = new Font(this.itemListView.Font, FontStyle.Regular);
var baseColor = UNSELECTED_COLOR;
if (!identified) {
baseFont = new Font(baseFont, FontStyle.Bold);
baseColor = SELECTED_COLOR;
}
listViewItem.Checked = identified;
listViewItem.Font = baseFont;
listViewItem.ForeColor = baseColor;
foreach (ListViewSubItem subItem in listViewItem.SubItems) {
subItem.Font = baseFont;
subItem.ForeColor = baseColor;
}
if (record.Appearance == null) {
var fontStyle = FontStyle.Italic;
if (!identified) {
fontStyle |= FontStyle.Bold;
}
for (var i = 0; i < this.Columns.Count; i++) {
if (this.Columns[i].Attribute == ItemAttribute.Appearance) {
listViewItem.SubItems[i].Font = new Font(baseFont, fontStyle);
}
}
} else {
for (var i = 0; i < this.Columns.Count; i++) {
if (this.Columns[i].Attribute == ItemAttribute.Appearance) {
listViewItem.SubItems[i].Text = record.Appearance;
if (appearanceMap.TryGetValue(record.Appearance, out var appearance) && appearance.Notes != string.Empty) {
listViewItem.ToolTipText = appearance.Notes;
listViewItem.SubItems[i].Text += TOOLTIP_SUFFIX;
}
}
}
}
this.itemListView.Items.Add(listViewItem);
}
this.UpdateCostHeaders();
}
public IdentificationRecord Export() {
List<ItemRecord> records = [];
foreach (ListViewItem item in this.itemListView.Items) {
if (item.Checked) {
string? appearance = this.AppearanceMap[item.Text];
records.Add(new() {
Name = item.Text,
Appearance = appearance,
});
}
}
return new(records);
}
public void UpdateCostHeaders() {
if (this.Grouping == ItemAttribute.Cost) {
for (int i = 0; i < this.GroupCosts.Count; i++) {
this.itemListView.Groups[i].Header = this.CostFormatter.Invoke(this.GroupCosts[i]);
}
}
}
private void itemListView_MouseDoubleClick(object sender, MouseEventArgs e) {
if (!e.Button.HasFlag(MouseButtons.Left)) {
return;
}
if (this.itemListView.SelectedItems.Count == 1) {
var item = this.itemListView.SelectedItems[0];
item.Checked = !item.Checked;
var baseFont = item.Font;
for (var i = 0; i < this.Columns.Count; i++) {
var subItem = item.SubItems[i];
if (item.Checked) {
if (subItem.Font.Italic) {
subItem.Font = new Font(baseFont, FontStyle.Italic);
} else {
subItem.Font = new Font(baseFont, FontStyle.Regular);
}
subItem.ForeColor = UNSELECTED_COLOR;
} else {
if (this.Columns[i].Attribute == ItemAttribute.Appearance && this.ItemMap[item.Text].Appearance == null) {
this.AppearanceMap[item.Text] = null;
subItem.Text = UNIDENTIFIED;
subItem.Font = new Font(baseFont, FontStyle.Bold | FontStyle.Italic);
} else {
subItem.Font = new Font(baseFont, FontStyle.Bold | FontStyle.Regular);
}
subItem.ForeColor = SELECTED_COLOR;
}
}
if (!item.Checked) {
item.ToolTipText = "";
}
this.itemListView.Sort();
this.ProcessCurrentItemContextMenu();
}
}
private void PickAppearance(ItemAppearance appearance) {
if (itemListView.SelectedItems.Count == 1) {
var item = this.itemListView.SelectedItems[0];
var name = item.Text;
this.AppearanceMap[name] = appearance.Appearance;
item.Checked = true;
var baseFont = item.Font;
for (var i = 0; i < this.Columns.Count; i++) {
var subItem = item.SubItems[i];
subItem.Font = new Font(baseFont, FontStyle.Regular);
subItem.ForeColor = UNSELECTED_COLOR;
if (this.Columns[i].Attribute == ItemAttribute.Appearance) {
subItem.Text = appearance.Appearance;
if (appearance.Notes != string.Empty) {
subItem.Text += TOOLTIP_SUFFIX;
}
}
}
item.ToolTipText = appearance.Notes;
this.itemListView.Sort();
this.ProcessCurrentItemContextMenu();
}
}
private int CompareItems(ListViewItem a, ListViewItem b) {
if (a.Checked != b.Checked) {
return a.Checked.CompareTo(b.Checked);
} else if (this.ItemMap[a.Text].Cost != this.ItemMap[b.Text].Cost) {
return this.ItemMap[a.Text].Cost.CompareTo(this.ItemMap[b.Text].Cost);
} else {
return a.Text.CompareTo(b.Text);
}
}
private void ProcessCurrentItemContextMenu() {
if (itemListView.SelectedItems.Count == 1) {
var item = this.itemListView.SelectedItems[0];
if (this.ItemMap[item.Text].Appearance == null) {
var slot = this.ItemMap[item.Text].Slot;
if (this.appearanceContextMenus.TryGetValue(slot, out var menu)) {
itemListView.ContextMenuStrip = menu;
var inUse = this.AppearanceMap.Values.Distinct().ToHashSet();
string? appearance = null;
if (this.AppearanceMap.TryGetValue(item.Text, out appearance) && appearance != null) {
inUse.Remove(appearance);
}
foreach (ToolStripMenuItem menuItem in menu.Items) {
menuItem.Enabled = !inUse.Contains(menuItem.Text);
menuItem.Checked = menuItem.Text == appearance;
}
return;
}
}
}
itemListView.ContextMenuStrip = null;
}
private void itemListView_SelectedIndexChanged(object sender, EventArgs e) {
this.ProcessCurrentItemContextMenu();
}
}
[Serializable]
public class ItemDisplayColumn {
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public ItemAttribute Attribute { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int Width { get; set; }
}
}