Korero – Diff between revs 1 and 2
?pathlinks?
Rev 1 | Rev 2 | |||
---|---|---|---|---|
1 | using System; |
1 | using System; |
|
2 | using System.Collections.Generic; |
2 | using System.Collections.Generic; |
|
3 | using System.ComponentModel; |
3 | using System.ComponentModel; |
|
4 | using System.Linq; |
4 | using System.Linq; |
|
5 | using System.Threading; |
5 | using System.Threading; |
|
6 | using System.Threading.Tasks; |
6 | using System.Threading.Tasks; |
|
7 | using System.Windows.Forms; |
7 | using System.Windows.Forms; |
|
8 | using Korero.Communication; |
8 | using Korero.Communication; |
|
9 | using Korero.Properties; |
9 | using Korero.Properties; |
|
10 | using Korero.Selection; |
10 | using Korero.Selection; |
|
11 | using Korero.Serialization; |
11 | using Korero.Serialization; |
|
12 | using Korero.Utilities; |
12 | using Korero.Utilities; |
|
13 | using Serilog; |
13 | using Serilog; |
|
14 | |
14 | |
|
15 | namespace Korero.Inventory |
15 | namespace Korero.Inventory |
|
16 | { |
16 | { |
|
17 | public partial class InventoryForm : Form |
17 | public partial class InventoryForm : Form |
|
18 | { |
18 | { |
|
19 | #region Private Delegates, Events, Enums, Properties, Indexers and Fields |
19 | #region Private Delegates, Events, Enums, Properties, Indexers and Fields |
|
20 | |
20 | |
|
21 | private readonly CancellationToken _cancellationToken; |
21 | private readonly CancellationToken _cancellationToken; |
|
22 | |
22 | |
|
23 | private readonly CancellationTokenSource _cancellationTokenSource; |
23 | private readonly CancellationTokenSource _cancellationTokenSource; |
|
24 | |
24 | |
|
25 | private readonly MainForm _mainForm; |
25 | private readonly MainForm _mainForm; |
|
26 | |
26 | |
|
27 | private readonly MqttCommunication _mqttCommunication; |
27 | private readonly MqttCommunication _mqttCommunication; |
|
28 | |
28 | |
|
29 | private AvatarSelectionForm _avatarSelectionForm; |
29 | private AvatarSelectionForm _avatarSelectionForm; |
|
30 | |
30 | |
|
31 | private CancellationToken _refreshCancellationToken; |
31 | private CancellationToken _refreshCancellationToken; |
|
32 | |
32 | |
|
33 | private CancellationTokenSource _refreshCancellationTokenSource; |
33 | private CancellationTokenSource _refreshCancellationTokenSource; |
|
34 | |
34 | |
|
35 | private Task _refreshTask; |
35 | private Task _refreshTask; |
|
36 | |
36 | |
|
37 | #endregion |
37 | #endregion |
|
38 | |
38 | |
|
39 | #region Constructors, Destructors and Finalizers |
39 | #region Constructors, Destructors and Finalizers |
|
40 | |
40 | |
|
41 | public InventoryForm() |
41 | public InventoryForm() |
|
42 | { |
42 | { |
|
43 | InitializeComponent(); |
43 | InitializeComponent(); |
|
44 | Utilities.WindowState.FormTracker.Track(this); |
44 | Utilities.WindowState.FormTracker.Track(this); |
|
45 | |
45 | |
|
46 | treeView1.InvokeIfRequired(treeView => |
46 | treeView1.InvokeIfRequired(treeView => |
|
47 | { |
47 | { |
|
48 | treeView.Nodes[0].Nodes.Add(Guid.Empty.ToString(), "Loading..."); |
48 | treeView.Nodes[0].Nodes.Add(Guid.Empty.ToString(), "Loading..."); |
|
49 | treeView.Nodes[1].Nodes.Add(Guid.Empty.ToString(), "Loading..."); |
49 | treeView.Nodes[1].Nodes.Add(Guid.Empty.ToString(), "Loading..."); |
|
50 | }); |
50 | }); |
|
51 | |
51 | |
|
52 | _cancellationTokenSource = new CancellationTokenSource(); |
52 | _cancellationTokenSource = new CancellationTokenSource(); |
|
53 | _cancellationToken = _cancellationTokenSource.Token; |
53 | _cancellationToken = _cancellationTokenSource.Token; |
|
54 | |
54 | |
|
55 | _refreshCancellationTokenSource = new CancellationTokenSource(); |
55 | _refreshCancellationTokenSource = new CancellationTokenSource(); |
|
56 | _refreshCancellationToken = _refreshCancellationTokenSource.Token; |
56 | _refreshCancellationToken = _refreshCancellationTokenSource.Token; |
|
57 | } |
57 | } |
|
58 | |
58 | |
|
59 | public InventoryForm(MainForm form, MqttCommunication mqttCommunication) : this() |
59 | public InventoryForm(MainForm form, MqttCommunication mqttCommunication) : this() |
|
60 | { |
60 | { |
|
61 | _mainForm = form; |
61 | _mainForm = form; |
|
62 | _mqttCommunication = mqttCommunication; |
62 | _mqttCommunication = mqttCommunication; |
|
63 | } |
63 | } |
|
64 | |
64 | |
|
65 | /// <summary> |
65 | /// <summary> |
|
66 | /// Clean up any resources being used. |
66 | /// Clean up any resources being used. |
|
67 | /// </summary> |
67 | /// </summary> |
|
68 | /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |
68 | /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> |
|
69 | protected override void Dispose(bool disposing) |
69 | protected override void Dispose(bool disposing) |
|
70 | { |
70 | { |
|
71 | if (disposing && components != null) |
71 | if (disposing && components != null) |
|
72 | { |
72 | { |
|
73 | components.Dispose(); |
73 | components.Dispose(); |
|
74 | } |
74 | } |
|
75 | |
75 | |
|
76 | base.Dispose(disposing); |
76 | base.Dispose(disposing); |
|
77 | } |
77 | } |
|
78 | |
78 | |
|
79 | #endregion |
79 | #endregion |
|
80 | |
80 | |
|
81 | #region Event Handlers |
81 | #region Event Handlers |
|
- | 82 | private void InventoryForm_Load(object sender, EventArgs e) |
||
- | 83 | { |
||
- | 84 | Utilities.WindowState.FormTracker.Track(this); |
||
82 | |
85 | } |
|
83 | private async void TreeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e) |
86 | private async void TreeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e) |
|
84 | { |
87 | { |
|
85 | await UpdateTreeNode(e.Node); |
88 | await UpdateTreeNode(e.Node); |
|
86 | } |
89 | } |
|
87 | |
90 | |
|
88 | private async void InventoryForm_Shown(object sender, EventArgs e) |
91 | private async void InventoryForm_Shown(object sender, EventArgs e) |
|
89 | { |
92 | { |
|
90 | _refreshTask = RefreshInventory(); |
93 | _refreshTask = RefreshInventory(); |
|
91 | |
94 | |
|
92 | await _refreshTask; |
95 | await _refreshTask; |
|
93 | } |
96 | } |
|
94 | |
97 | |
|
95 | private async void RefreshToolStripMenuItem_Click(object sender, EventArgs e) |
98 | private async void RefreshToolStripMenuItem_Click(object sender, EventArgs e) |
|
96 | { |
99 | { |
|
97 | _refreshCancellationTokenSource.Cancel(); |
100 | _refreshCancellationTokenSource.Cancel(); |
|
98 | await _refreshTask; |
101 | await _refreshTask; |
|
99 | |
102 | |
|
100 | _refreshCancellationTokenSource = new CancellationTokenSource(); |
103 | _refreshCancellationTokenSource = new CancellationTokenSource(); |
|
101 | _refreshCancellationToken = _refreshCancellationTokenSource.Token; |
104 | _refreshCancellationToken = _refreshCancellationTokenSource.Token; |
|
102 | |
105 | |
|
103 | _refreshTask = RefreshInventory(); |
106 | _refreshTask = RefreshInventory(); |
|
104 | await _refreshTask; |
107 | await _refreshTask; |
|
105 | } |
108 | } |
|
106 | |
109 | |
|
107 | private void GiveToolStripMenuItem_Click(object sender, EventArgs e) |
110 | private void GiveToolStripMenuItem_Click(object sender, EventArgs e) |
|
108 | { |
111 | { |
|
109 | if (_avatarSelectionForm != null) |
112 | if (_avatarSelectionForm != null) |
|
110 | { |
113 | { |
|
111 | return; |
114 | return; |
|
112 | } |
115 | } |
|
113 | |
116 | |
|
114 | var path = treeView1.SelectedNode.FullPath; |
117 | var path = treeView1.SelectedNode.FullPath; |
|
115 | |
118 | |
|
116 | _avatarSelectionForm = new AvatarSelectionForm(_mqttCommunication, new {Path = path, Action = "give"}); |
119 | _avatarSelectionForm = new AvatarSelectionForm(_mqttCommunication, new {Path = path, Action = "give"}); |
|
117 | _avatarSelectionForm.AvatarSelected += AvatarSelectionForm_AvatarSelected; |
120 | _avatarSelectionForm.AvatarSelected += AvatarSelectionForm_AvatarSelected; |
|
118 | _avatarSelectionForm.Closing += AvatarSelectionForm_Closing; |
121 | _avatarSelectionForm.Closing += AvatarSelectionForm_Closing; |
|
119 | _avatarSelectionForm.Show(); |
122 | _avatarSelectionForm.Show(); |
|
120 | } |
123 | } |
|
121 | |
124 | |
|
122 | private void AvatarSelectionForm_Closing(object sender, CancelEventArgs e) |
125 | private void AvatarSelectionForm_Closing(object sender, CancelEventArgs e) |
|
123 | { |
126 | { |
|
124 | if (_avatarSelectionForm == null) |
127 | if (_avatarSelectionForm == null) |
|
125 | { |
128 | { |
|
126 | return; |
129 | return; |
|
127 | } |
130 | } |
|
128 | |
131 | |
|
129 | _avatarSelectionForm.AvatarSelected -= AvatarSelectionForm_AvatarSelected; |
132 | _avatarSelectionForm.AvatarSelected -= AvatarSelectionForm_AvatarSelected; |
|
130 | _avatarSelectionForm.Closing -= AvatarSelectionForm_Closing; |
133 | _avatarSelectionForm.Closing -= AvatarSelectionForm_Closing; |
|
131 | _avatarSelectionForm.Dispose(); |
134 | _avatarSelectionForm.Dispose(); |
|
132 | _avatarSelectionForm = null; |
135 | _avatarSelectionForm = null; |
|
133 | } |
136 | } |
|
134 | |
137 | |
|
135 | private async void AvatarSelectionForm_AvatarSelected(object sender, AvatarSelectedEventArgs e) |
138 | private async void AvatarSelectionForm_AvatarSelected(object sender, AvatarSelectedEventArgs e) |
|
136 | { |
139 | { |
|
137 | switch (e.Data.Action) |
140 | switch (e.Data.Action) |
|
138 | { |
141 | { |
|
139 | case "give": |
142 | case "give": |
|
140 | var data = new Dictionary<string, string> |
143 | var data = new Dictionary<string, string> |
|
141 | { |
144 | { |
|
142 | {"command", "give"}, |
145 | {"command", "give"}, |
|
143 | {"group", Settings.Default.Group}, |
146 | {"group", Settings.Default.Group}, |
|
144 | {"password", Settings.Default.Password}, |
147 | {"password", Settings.Default.Password}, |
|
145 | {"entity", "avatar"}, |
148 | {"entity", "avatar"}, |
|
146 | {"item", e.Data.Path}, |
149 | {"item", e.Data.Path}, |
|
147 | {"firstname", e.AvatarSelection.FirstName}, |
150 | {"firstname", e.AvatarSelection.FirstName}, |
|
148 | {"lastname", e.AvatarSelection.LastName} |
151 | {"lastname", e.AvatarSelection.LastName} |
|
149 | }; |
152 | }; |
|
150 | |
153 | |
|
151 | var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken); |
154 | var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken); |
|
152 | |
155 | |
|
153 | if (callback == null || !callback.Success) |
156 | if (callback == null || !callback.Success) |
|
154 | { |
157 | { |
|
155 | if (callback != null) |
158 | if (callback != null) |
|
156 | { |
159 | { |
|
157 | Log.Warning("Command {Command} has failed with {Error}.", |
160 | Log.Warning("Command {Command} has failed with {Error}.", |
|
158 | callback.Command, callback.Error); |
161 | callback.Command, callback.Error); |
|
159 | } |
162 | } |
|
160 | |
163 | |
|
161 | toolStripStatusLabel1.Text = |
164 | toolStripStatusLabel1.Text = |
|
162 | $"Unable to give inventory item {e.Data.Path} to {e.AvatarSelection.FirstName} {e.AvatarSelection.LastName}"; |
165 | $"Unable to give inventory item {e.Data.Path} to {e.AvatarSelection.FirstName} {e.AvatarSelection.LastName}"; |
|
163 | return; |
166 | return; |
|
164 | } |
167 | } |
|
165 | |
168 | |
|
166 | toolStripStatusLabel1.Text = |
169 | toolStripStatusLabel1.Text = |
|
167 | $"Gave {e.Data.Path} to {e.AvatarSelection.FirstName} {e.AvatarSelection.LastName}"; |
170 | $"Gave {e.Data.Path} to {e.AvatarSelection.FirstName} {e.AvatarSelection.LastName}"; |
|
168 | break; |
171 | break; |
|
169 | } |
172 | } |
|
170 | } |
173 | } |
|
171 | |
174 | |
|
172 | private void InventoryForm_FormClosing(object sender, FormClosingEventArgs e) |
175 | private void InventoryForm_FormClosing(object sender, FormClosingEventArgs e) |
|
173 | { |
176 | { |
|
174 | _refreshCancellationTokenSource.Cancel(); |
177 | _refreshCancellationTokenSource.Cancel(); |
|
175 | _cancellationTokenSource.Cancel(); |
178 | _cancellationTokenSource.Cancel(); |
|
176 | } |
179 | } |
|
177 | |
180 | |
|
178 | #endregion |
181 | #endregion |
|
179 | |
182 | |
|
180 | #region Private Methods |
183 | #region Private Methods |
|
181 | |
184 | |
|
182 | private async Task RefreshInventory() |
185 | private async Task RefreshInventory() |
|
183 | { |
186 | { |
|
184 | using (var combinedCancellationTokenSource = |
187 | using (var combinedCancellationTokenSource = |
|
185 | CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _refreshCancellationToken)) |
188 | CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, _refreshCancellationToken)) |
|
186 | { |
189 | { |
|
187 | var cancellationToken = combinedCancellationTokenSource.Token; |
190 | var cancellationToken = combinedCancellationTokenSource.Token; |
|
188 | |
191 | |
|
189 | // Breadth-first traversal. |
192 | // Breadth-first traversal. |
|
190 | var stack = new Queue<TreeNode>(); |
193 | var stack = new Queue<TreeNode>(); |
|
191 | stack.Enqueue(treeView1.Nodes[0]); |
194 | stack.Enqueue(treeView1.Nodes[0]); |
|
192 | stack.Enqueue(treeView1.Nodes[1]); |
195 | stack.Enqueue(treeView1.Nodes[1]); |
|
193 | |
196 | |
|
194 | do |
197 | do |
|
195 | { |
198 | { |
|
196 | var node = stack.Dequeue(); |
199 | var node = stack.Dequeue(); |
|
197 | |
200 | |
|
198 | await UpdateTreeNode(node); |
201 | await UpdateTreeNode(node); |
|
199 | |
202 | |
|
200 | foreach (var child in node.Nodes.OfType<TreeNode>()) |
203 | foreach (var child in node.Nodes.OfType<TreeNode>()) |
|
201 | { |
204 | { |
|
202 | if (child.Nodes.Count != 0) |
205 | if (child.Nodes.Count != 0) |
|
203 | { |
206 | { |
|
204 | stack.Enqueue(child); |
207 | stack.Enqueue(child); |
|
205 | } |
208 | } |
|
206 | } |
209 | } |
|
207 | |
210 | |
|
208 | toolStripStatusLabel1.Text = $"Updating {stack.Count} folders..."; |
211 | toolStripStatusLabel1.Text = $"Updating {stack.Count} folders..."; |
|
209 | } while (!cancellationToken.IsCancellationRequested && stack.Count != 0); |
212 | } while (!cancellationToken.IsCancellationRequested && stack.Count != 0); |
|
210 | } |
213 | } |
|
211 | } |
214 | } |
|
212 | |
215 | |
|
213 | private async Task UpdateTreeNode(TreeNode node) |
216 | private async Task UpdateTreeNode(TreeNode node) |
|
214 | { |
217 | { |
|
215 | // Skip nodes that have already been updated. |
218 | // Skip nodes that have already been updated. |
|
216 | if (!node.Nodes.ContainsKey(Guid.Empty.ToString())) |
219 | if (!node.Nodes.ContainsKey(Guid.Empty.ToString())) |
|
217 | { |
220 | { |
|
218 | return; |
221 | return; |
|
219 | } |
222 | } |
|
220 | |
223 | |
|
221 | var data = new Dictionary<string, string> |
224 | var data = new Dictionary<string, string> |
|
222 | { |
225 | { |
|
223 | {"command", "inventory"}, |
226 | {"command", "inventory"}, |
|
224 | {"group", Settings.Default.Group}, |
227 | {"group", Settings.Default.Group}, |
|
225 | {"password", Settings.Default.Password}, |
228 | {"password", Settings.Default.Password}, |
|
226 | {"action", "ls"}, |
229 | {"action", "ls"}, |
|
227 | {"path", node.FullPath} |
230 | {"path", node.FullPath} |
|
228 | }; |
231 | }; |
|
229 | |
232 | |
|
230 | var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken); |
233 | var callback = await _mqttCommunication.SendCommand(new Command(data), _cancellationToken); |
|
231 | |
234 | |
|
232 | if (callback == null || !callback.Success) |
235 | if (callback == null || !callback.Success) |
|
233 | { |
236 | { |
|
234 | if (callback != null) |
237 | if (callback != null) |
|
235 | { |
238 | { |
|
236 | Log.Warning("Command {Command} has failed with {Error}.", |
239 | Log.Warning("Command {Command} has failed with {Error}.", |
|
237 | callback.Command, callback.Error); |
240 | callback.Command, callback.Error); |
|
238 | } |
241 | } |
|
239 | |
242 | |
|
240 | return; |
243 | return; |
|
241 | } |
244 | } |
|
242 | |
245 | |
|
243 | // Remove folder dummy node. |
246 | // Remove folder dummy node. |
|
244 | node.Nodes.RemoveByKey(Guid.Empty.ToString()); |
247 | node.Nodes.RemoveByKey(Guid.Empty.ToString()); |
|
245 | |
248 | |
|
246 | foreach (var item in CsvStride(callback.Data, 10)) |
249 | foreach (var item in CsvStride(callback.Data, 10)) |
|
247 | { |
250 | { |
|
248 | var name = item[1]; |
251 | var name = item[1]; |
|
249 | var type = item[5]; |
252 | var type = item[5]; |
|
250 | var guid = item[3]; |
253 | var guid = item[3]; |
|
251 | |
254 | |
|
252 | // If the node already exists in the tree then skip adding nodes. |
255 | // If the node already exists in the tree then skip adding nodes. |
|
253 | if (node.Nodes.ContainsKey(guid)) |
256 | if (node.Nodes.ContainsKey(guid)) |
|
254 | { |
257 | { |
|
255 | continue; |
258 | continue; |
|
256 | } |
259 | } |
|
257 | |
260 | |
|
258 | // TODO: different menu strips depending on the item type. |
261 | // TODO: different menu strips depending on the item type. |
|
259 | var childNode = node.Nodes.Add(guid, name); |
262 | var childNode = node.Nodes.Add(guid, name); |
|
260 | childNode.ContextMenuStrip = contextMenuStrip1; |
263 | childNode.ContextMenuStrip = contextMenuStrip1; |
|
261 | |
264 | |
|
262 | // Set the icon depending on the inventory type. |
265 | // Set the icon depending on the inventory type. |
|
263 | var typeIndex = imageList1.Images.IndexOfKey(type); |
266 | var typeIndex = imageList1.Images.IndexOfKey(type); |
|
264 | switch (typeIndex) |
267 | switch (typeIndex) |
|
265 | { |
268 | { |
|
266 | case -1: |
269 | case -1: |
|
267 | childNode.ImageIndex = 0; |
270 | childNode.ImageIndex = 0; |
|
268 | childNode.SelectedImageIndex = 0; |
271 | childNode.SelectedImageIndex = 0; |
|
269 | break; |
272 | break; |
|
270 | default: |
273 | default: |
|
271 | childNode.ImageIndex = typeIndex; |
274 | childNode.ImageIndex = typeIndex; |
|
272 | childNode.SelectedImageIndex = typeIndex; |
275 | childNode.SelectedImageIndex = typeIndex; |
|
273 | break; |
276 | break; |
|
274 | } |
277 | } |
|
275 | |
278 | |
|
276 | switch (type) |
279 | switch (type) |
|
277 | { |
280 | { |
|
278 | case "Folder": |
281 | case "Folder": |
|
279 | // Add folder dummy node. |
282 | // Add folder dummy node. |
|
280 | childNode.Nodes.Add(Guid.Empty.ToString(), "Loading..."); |
283 | childNode.Nodes.Add(Guid.Empty.ToString(), "Loading..."); |
|
281 | break; |
284 | break; |
|
282 | } |
285 | } |
|
283 | } |
286 | } |
|
284 | } |
287 | } |
|
285 | |
288 | |
|
286 | private static IEnumerable<string[]> CsvStride(string input, int stride) |
289 | private static IEnumerable<string[]> CsvStride(string input, int stride) |
|
287 | { |
290 | { |
|
288 | var i = 0; |
291 | var i = 0; |
|
289 | return new CSV(input).Select(s => new {s, num = i++}) |
292 | return new CSV(input).Select(s => new {s, num = i++}) |
|
290 | .GroupBy(t => t.num / stride, t => t.s) |
293 | .GroupBy(t => t.num / stride, t => t.s) |
|
291 | .Select(g => g.ToArray()); |
294 | .Select(g => g.ToArray()); |
|
292 | } |
295 | } |
|
293 | |
296 | |
|
294 | #endregion |
297 | #endregion |
|
- | 298 | |
||
- | 299 | |
||
295 | } |
300 | } |
|
296 | } |
301 | } |
|
297 | |
302 | |
|
298 |
|
303 |
|
|
299 | |
304 | |
|
300 | |
305 | |
|
301 | |
306 | |