using LinqToDB.Tools; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using Pf2eModel; using Pf2eModel.Model; using Pf2eModel.Model.Base; using Pf2eModel.Model.Enum; using static LinqToDB.Common.Configuration; namespace Pf2eViewer; public partial class MainViewForm : Form { public Pf2eDbContext Context = new(); public MainViewForm() { InitializeComponent(); Reset(); EntityGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect; } public void Reset() { Context.Dispose(); Context = new(); TypesListBox.DataSource = Context.Entities.Select(e => e.Type).Distinct().ToList(); TypesListBox.SelectedIndex = 0; } public const int PerPage = 30; public int Count; public int Page { get => Convert.ToInt32(PageTextBox.Text); set { PageTextBox.Text = value.ToString(); UpdateTreeView(); } } public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage)); private void UpdateTreeView() { EntityTreeView.Nodes.Clear(); EntityType type = (EntityType) TypesListBox.SelectedItem!; Count = Context.Entities.Count(e => e.Type == type); if (Page < 1 || Page > MaxPage) { Page = Math.Min(MaxPage, Math.Max(1, Page)); return; } EntityGridView.DataSource = Context.Entities .Where(e => e.Type == (EntityType) TypesListBox.SelectedItem) .OrderBy(e => e.Id) .Select(e => new EntityGridRow(e)) .Skip((Page - 1) * PerPage) .Take(PerPage) .ToList(); EntityGridView.Rows[0].Selected = true; PaginationLabel.Text = $"Всего: {Count} объектов, {MaxPage} страниц"; FirstPageButton.Enabled = Page > 1; PrevPageButton.Enabled = Page > 1; NextPageButton.Enabled = Page < MaxPage; LastPageButton.Enabled = Page < MaxPage; } private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _) { if (TypesListBox.SelectedItem is null) { return; } Page = 1; } private void FirstPageButton_Click(object sender, EventArgs e) { Page = 1; } private void PrevPageButton_Click(object sender, EventArgs e) { Page--; } private void NextPageButton_Click(object sender, EventArgs e) { Page++; } private void LastPageButton_Click(object sender, EventArgs e) { Page = MaxPage; } private void PageTextBox_KeyPress(object sender, KeyPressEventArgs e) { if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) { e.Handled = true; } if (e.KeyChar == (char) Keys.Return) { UpdateTreeView(); } } private void EntityGridView_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e) { int rowsCount = EntityGridView.SelectedRows.Count; if (rowsCount is 0 or > 1) { return; } DataGridViewRow row = EntityGridView.SelectedRows[0]; EntityGridRow data = (EntityGridRow) row.DataBoundItem; ViewEntity(data.Id); } public void ViewEntity(int id) { EntityTreeView.Nodes.Clear(); Entity? entity = Context.Entities .Where(e => e.Id == id && e.Type == (EntityType) TypesListBox.SelectedItem!) .Include(e => e.Sources) .ThenInclude(s => s.Source) .Include(e => e.Aliases) .Include(e => e.Keywords) .Include(e => e.Externals) .ThenInclude(x => x.Target) .Include(e => e.Traits) .ThenInclude(t => t.Entity) .FirstOrDefault() ; if (entity == null) { DescriptionTextBox.Text = "Not found"; return; } string json = SerializeEntity(entity); JsonToTreeview(EntityTreeView, json, "Entity"); DescriptionTextBox.Text = entity.Description.Replace("\\r", "").Replace("\\n", "\\r\\n"); } public string SerializeEntity(Entity entity) { JsonSerializerSettings settings = new() { Converters = [ new EntityConverter(), new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }, ], ReferenceLoopHandling = ReferenceLoopHandling.Ignore, }; string result = JsonConvert.SerializeObject(entity, settings); Type type = EntityOfTypeAttribute.Resolve(entity); string subResult = JsonConvert.SerializeObject(ResolveSubEntity(entity), settings); return result[..^1] + $",\"SubEntity\":{subResult}}}"; } public IEntity? ResolveSubEntity(Entity entity) { switch (entity.Type) { case EntityType.Ancestry: return Context.Ancestries .Include(a => a.AncestryBoosts) .FirstOrDefault(e => e.Id == entity.Id); case EntityType.Archetype: return Context.Archetypes .Include(a => a.DedicationFeat) .ThenInclude(f => f.Entity) .Include(a => a.Feats) .ThenInclude(f => f.Entity) .FirstOrDefault(a => a.Id == entity.Id); case EntityType.Background: return Context.Backgrounds .Include(b => b.BackgroundBoosts) .Include(b => b.BackgroundSkills) .ThenInclude(t => t.Training) .Include(b => b.BackgroundItems) .FirstOrDefault(e => e.Id == entity.Id); case EntityType.Class: return Context.Classes .Include(c => c.ClassKeyAbilities) .Include(c => c.ClassTrainings) .ThenInclude(t => t.Training) .Include(c => c.ClassProgressions) .FirstOrDefault(c => c.Id == entity.Id); case EntityType.Feat: return Context.Feats .Include(f => f.Archetypes) .ThenInclude(a => a.Entity) .FirstOrDefault(f => f.Id == entity.Id); default: Type type = EntityOfTypeAttribute.Resolve(entity); return (IEntity?) Context.Find(type, entity.Id); } } public static void JsonToTreeview(TreeView treeView, string jsonString, string rootName) { object json = jsonString.StartsWith('[') ? JArray.Parse(jsonString) : JObject.Parse(jsonString); treeView.Nodes.Clear(); foreach (TreeNode node in Element2TreeNode(json, rootName).Nodes) { treeView.Nodes.Add(node); } treeView.ExpandAll(); } private static TreeNode Element2TreeNode(object element, string text = "") { List subNodes = []; switch (element.GetType().Name) { case "JObject": JObject jObject = (JObject) element; foreach (KeyValuePair token in jObject) { subNodes.Add(Element2TreeNode(token.Value, token.Key)); } break; case "JArray": JArray jArray = (JArray) element; int i = 0; subNodes.AddRange(jArray.Select(token => Element2TreeNode(token, $"[{i++}]"))); if (i == 0) { text += ": []"; } break; case "JValue": JValue jValue = (JValue) element; text += $": ({jValue.Type}) {jValue}"; break; default: throw new Exception("clsJSON2Treeview can't interpret object:" + element.GetType().Name); } TreeNode treeNode = new(text); if (subNodes.Count > 0) { treeNode.Nodes.AddRange(subNodes .OrderBy(node => node.Nodes.Count > 0 ? 1 : 0) .ToArray() ); } return treeNode; } private class EntityGridRow(Entity e) { public int Id { get; set; } = e.Id; public string Name { get; set; } = e.Name.ToString(); public EntityRarity Rarity { get; set; } = e.Rarity; private readonly Entity _e = e; public Entity GetEntity() => _e; } }