Korero – Rev 2
?pathlinks?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Korero.Communication;
using Korero.Properties;
using Korero.Selection;
using Korero.Serialization;
using Korero.Utilities;
using Serilog;
namespace Korero.Inventory
{
public partial class InventoryForm : Form
{
#region Private Delegates, Events, Enums, Properties, Indexers and Fields
private readonly CancellationToken _cancellationToken;
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly MainForm _mainForm;
private readonly MqttCommunication _mqttCommunication;
private AvatarSelectionForm _avatarSelectionForm;
private CancellationToken _refreshCancellationToken;
private CancellationTokenSource _refreshCancellationTokenSource;
private Task _refreshTask;
#endregion
#region Constructors, Destructors and Finalizers
public InventoryForm()
{
InitializeComponent();
Utilities.WindowState.FormTracker.Track(this);
treeView1.InvokeIfRequired(treeView =>
{
treeView.Nodes[0].Nodes.Add(Guid.Empty.ToString(), "Loading...");
treeView.Nodes[1].Nodes.Add(Guid.Empty.ToString(), "Loading...");
});
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
_refreshCancellationTokenSource = new CancellationTokenSource();
_refreshCancellationToken = _refreshCancellationTokenSource.Token;
}
public InventoryForm(MainForm form, MqttCommunication mqttCommunication) : this()
{
_mainForm = form;
_mqttCommunication = mqttCommunication;
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && components != null)
{
components.Dispose();
}
base.Dispose(disposing);
}
#endregion
#region Event Handlers
private void InventoryForm_Load(object sender, EventArgs e)
{
Utilities.WindowState.FormTracker.Track(this);
}
private async void TreeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
await UpdateTreeNode(e.Node);
}
private async void InventoryForm_Shown(object sender, EventArgs e)
{
_refreshTask = RefreshInventory();
await _refreshTask;
}
private async void RefreshToolStripMenuItem_Click(object sender, EventArgs e)
{
_refreshCancellationTokenSource.Cancel();
await _refreshTask;
_refreshCancellationTokenSource = new CancellationTokenSource();
_refreshCancellationToken = _refreshCancellationTokenSource.Token;
_refreshTask = RefreshInventory();
await _refreshTask;
}
private void GiveToolStripMenuItem_Click(object sender, EventArgs e)
{
if (_avatarSelectionForm != null)
{
return;
}
var path = treeView1.SelectedNode.FullPath;
_avatarSelectionForm = new AvatarSelectionForm(_mqttCommunication, new {Path = path, Action = "give"});
_avatarSelectionForm.AvatarSelected += AvatarSelectionForm_AvatarSelected;
_avatarSelectionForm.Closing += AvatarSelectionForm_Closing;
_avatarSelectionForm.Show();
}
private void AvatarSelectionForm_Closing(object sender, CancelEventArgs e)
{
if (_avatarSelectionForm == null)
{
return;
}
_avatarSelectionForm.AvatarSelected -= AvatarSelectionForm_AvatarSelected;
_avatarSelectionForm.Closing -= AvatarSelectionForm_Closing;
_avatarSelectionForm.Dispose();
_avatarSelectionForm = null;
}
private async void AvatarSelectionForm_AvatarSelected(object sender, AvatarSelectedEventArgs e)
{
switch (e.Data.Action)
{
case "give":
var data = new Dictionary<string, string>
{
{"command", "give"},
{"group", Settings.Default.Group},
{"password", Settings.Default.Password},
{"entity", "avatar"},
{"item", e.Data.Path},
{"firstname", e.AvatarSelection.FirstName},
{"lastname", e.AvatarSelection.LastName}
};
var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);
if (callback == null || !callback.Success)
{
if (callback != null)
{
Log.Warning("Command {Command} has failed with {Error}.",
callback.Command, callback.Error);
}
toolStripStatusLabel1.Text =
$"Unable to give inventory item {e.Data.Path} to {e.AvatarSelection.FirstName} {e.AvatarSelection.LastName}";
return;
}
toolStripStatusLabel1.Text =
$"Gave {e.Data.Path} to {e.AvatarSelection.FirstName} {e.AvatarSelection.LastName}";
break;
}
}
private void InventoryForm_FormClosing(object sender, FormClosingEventArgs e)
{
_refreshCancellationTokenSource.Cancel();
_cancellationTokenSource.Cancel();
}
#endregion
#region Private Methods
private async Task RefreshInventory()
{
using (var combinedCancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _refreshCancellationToken))
{
var cancellationToken = combinedCancellationTokenSource.Token;
// Breadth-first traversal.
var stack = new Queue<TreeNode>();
stack.Enqueue(treeView1.Nodes[0]);
stack.Enqueue(treeView1.Nodes[1]);
do
{
var node = stack.Dequeue();
await UpdateTreeNode(node);
foreach (var child in node.Nodes.OfType<TreeNode>())
{
if (child.Nodes.Count != 0)
{
stack.Enqueue(child);
}
}
toolStripStatusLabel1.Text = $"Updating {stack.Count} folders...";
} while (!cancellationToken.IsCancellationRequested && stack.Count != 0);
}
}
private async Task UpdateTreeNode(TreeNode node)
{
// Skip nodes that have already been updated.
if (!node.Nodes.ContainsKey(Guid.Empty.ToString()))
{
return;
}
var data = new Dictionary<string, string>
{
{"command", "inventory"},
{"group", Settings.Default.Group},
{"password", Settings.Default.Password},
{"action", "ls"},
{"path", node.FullPath}
};
var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken);
if (callback == null || !callback.Success)
{
if (callback != null)
{
Log.Warning("Command {Command} has failed with {Error}.",
callback.Command, callback.Error);
}
return;
}
// Remove folder dummy node.
node.Nodes.RemoveByKey(Guid.Empty.ToString());
foreach (var item in CsvStride(callback.Data, 10))
{
var name = item[1];
var type = item[5];
var guid = item[3];
// If the node already exists in the tree then skip adding nodes.
if (node.Nodes.ContainsKey(guid))
{
continue;
}
// TODO: different menu strips depending on the item type.
var childNode = node.Nodes.Add(guid, name);
childNode.ContextMenuStrip = contextMenuStrip1;
// Set the icon depending on the inventory type.
var typeIndex = imageList1.Images.IndexOfKey(type);
switch (typeIndex)
{
case -1:
childNode.ImageIndex = 0;
childNode.SelectedImageIndex = 0;
break;
default:
childNode.ImageIndex = typeIndex;
childNode.SelectedImageIndex = typeIndex;
break;
}
switch (type)
{
case "Folder":
// Add folder dummy node.
childNode.Nodes.Add(Guid.Empty.ToString(), "Loading...");
break;
}
}
}
private static IEnumerable<string[]> CsvStride(string input, int stride)
{
var i = 0;
return new CSV(input).Select(s => new {s, num = i++})
.GroupBy(t => t.num / stride, t => t.s)
.Select(g => g.ToArray());
}
#endregion
}
}
Generated by GNU Enscript 1.6.5.90.