I18NCommander/I18N Commander/UI WinForms/Components/SectionTree.cs

248 lines
9.2 KiB
C#

using DataModel.Database.Common;
using Microsoft.Extensions.DependencyInjection;
using Processor;
using UI_WinForms.Dialogs;
using UI_WinForms.Resources;
namespace UI_WinForms.Components;
public partial class SectionTree : UserControl
{
private readonly DataContext db;
public SectionTree()
{
this.InitializeComponent();
// Check if we are in the designer:
if(Program.SERVICE_PROVIDER is null)
return;
// Get the DI context from the main form:
this.db = Program.SERVICE_PROVIDER.GetService<DataContext>()!;
// Dispose of the context when the control is disposed:
this.Disposed += (_, _) => this.db.Dispose();
// Create an image list from a resource:
var imgList = new ImageList();
imgList.ImageSize = new Size(45, 45);
imgList.ColorDepth = ColorDepth.Depth32Bit;
imgList.Images.Add(Icons.icons8_documents_folder_512);
// Set the image list to the tree view:
this.treeView.ImageList = imgList;
// Subscribe to the load event:
this.Load += this.LoadNodes;
}
private async void LoadNodes(object? sender, EventArgs e)
{
if(this.DesignMode)
return;
// A dictionary to cache all known tree nodes:
var treeNodes = new Dictionary<string, TreeNode>();
// Get the max. depth of the tree:
var maxDepth = await SectionProcessor.GetDepth(this.db);
// Store nodes, where we cannot find the parents:
var missingParents = new List<TreeNode>();
// Populate the tree view out of the database, layer by layer:
for (var i = 0; i <= maxDepth; i++)
{
await foreach (var section in SectionProcessor.LoadLayer(this.db, i))
{
// Create the tree node:
var node = new TreeNode
{
Name = section.DataKey, // [sic] name is the key
Text = section.Name,
StateImageIndex = 1,
};
// Cache the node:
treeNodes.Add(section.DataKey, node);
// Is this a root node?
if (section.Depth is 0)
// Set the root node:
this.treeView.Nodes.Add(node);
// Otherwise, attach this section to its parent node:
else
{
// Get the parent from our cache within O(1):
treeNodes.TryGetValue(section.Parent?.DataKey ?? string.Empty, out var parent);
// If the parent node is not found, skip this section:
if (parent is null)
{
missingParents.Add(node);
continue;
}
// Add the node to the parent:
parent.Nodes.Add(node);
}
}
}
// If we found any missing parents, show a dialog:
if (missingParents.Any())
{
MessageBox.Show($"In {missingParents.Count} case(s) we could not found the matching parent. We added these nodes to a special root node, though.", "Parent not found", MessageBoxButtons.OK, MessageBoxIcon.Error);
// Create a root node for all missing parents:
var rootMissedParents = new TreeNode
{
Name = "MISSING_PARENTS",
Text = "Missing Parents",
StateImageIndex = 1,
};
// Add the root node to the tree:
this.treeView.Nodes.Add(rootMissedParents);
// Add all missing parents to the root node:
foreach (var node in missingParents)
rootMissedParents.Nodes.Add(node);
}
// Expand the tree:
this.treeView.ExpandAll();
}
private async void buttonAdd_Click(object sender, EventArgs e)
{
if(this.DesignMode)
return;
var result = InputDialog.Show(new InputDialog.Options(
Message: "Please type the desired section name.",
Title: "Add a section",
Placeholder: "My next section",
ShowQuestionCheckbox: true,
QuestionCheckboxText: "Add a root node (i.e. ignoring the selected node)"
));
if(result.DialogResult == DialogResult.Cancel)
return;
var addRootNode = result.AnswerToQuestion;
// Get the currently selected section as parent:
var selectedNode = this.treeView.SelectedNode;
// Add the new section to the database:
var addedSection = await SectionProcessor.AddSection(this.db, result.Text, addRootNode ? null : selectedNode?.Name);
addedSection.ProcessError();
if(!addedSection.Successful)
return;
// Add the new section to the tree control:
var node = new TreeNode
{
Name = addedSection.Result!.DataKey, // [sic] name is the key
Text = addedSection.Result.Name,
StateImageIndex = 1,
};
if(!addRootNode && selectedNode is not null)
selectedNode.Nodes.Add(node);
else
this.treeView.Nodes.Add(node);
// Ensure, that the added node is visible and gets the focus:
node.EnsureVisible();
this.treeView.SelectedNode = node;
}
private async void buttonRemove_Click(object sender, EventArgs e)
{
if(this.DesignMode)
return;
// Get the currently selected section, which will be removed:
var selectedNode = this.treeView.SelectedNode;
// Get the number of children:
// (notice, that the node's name is its key)
var numberChildren = await SectionProcessor.NumberChildren(this.db, selectedNode.Name);
// Ask the user, if he really wants to remove the section:
if(MessageBox.Show(numberChildren > 0 ? $"Are you sure, you want to remove the section '{selectedNode.Text}', its {numberChildren} children and so on?" : $"Are you sure, you want to remove the section '{selectedNode.Text}'?", "Remove section", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.No)
return;
// Remove the section from the database:
// (notice, that the node's name is its key)
await SectionProcessor.RemoveSection(this.db, selectedNode.Name);
// Remove all nodes from the tree control:
this.treeView.Nodes.Clear();
// Reload the tree:
this.LoadNodes(this, EventArgs.Empty);
}
private void treeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if(this.DesignMode)
return;
// Get the currently selected section:
var selectedNode = e.Node;
// If the selected node is not null, enable the remove & edit button:
this.buttonRename.Enabled = this.buttonRemove.Enabled = selectedNode is not null;
// When a section is selected, fire the event:
if (selectedNode is not null)
{
// Get the section from the database:
var section = SectionProcessor.GetSection(this.db, selectedNode.Name);
AppEvents.SectionChanged(section);
}
}
private async void buttonRename_Click(object sender, EventArgs e)
{
if(this.DesignMode)
return;
// Ask the user if he really wants to rename the section:
if(MessageBox.Show("Are you sure, you want to rename the selected section? If you are already using this section in your code, you will need to manually refactor your code after renaming it.", "Rename section", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.No)
return;
// Get the currently selected section:
var selectedNode = this.treeView.SelectedNode;
// Ask the user for the new name:
var result = InputDialog.Show(new InputDialog.Options(
Message: "Please edit the section name.",
PreloadedText: selectedNode.Text,
ShowQuestionCheckbox: false,
Title: "Rename section"
));
// If the user canceled, return:
if(result.DialogResult == DialogResult.Cancel)
return;
// Rename the section:
var alteredSection = await SectionProcessor.RenameSection(this.db, selectedNode.Name, result.Text);
alteredSection.ProcessError();
if(!alteredSection.Successful)
return;
// Rename the selected node:
selectedNode.Text = alteredSection.Result!.Name;
selectedNode.Name = alteredSection.Result.DataKey; // [sic] name is the key
}
}