290 lines
13 KiB
C#
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; }
|
|
}
|
|
}
|