aboutsummaryrefslogtreecommitdiff
path: root/src/samples/Dtf/Inventory/Inventory.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/samples/Dtf/Inventory/Inventory.cs')
-rw-r--r--src/samples/Dtf/Inventory/Inventory.cs1231
1 files changed, 1231 insertions, 0 deletions
diff --git a/src/samples/Dtf/Inventory/Inventory.cs b/src/samples/Dtf/Inventory/Inventory.cs
new file mode 100644
index 00000000..02793be8
--- /dev/null
+++ b/src/samples/Dtf/Inventory/Inventory.cs
@@ -0,0 +1,1231 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System;
4using System.IO;
5using System.Drawing;
6using System.Collections;
7using System.ComponentModel;
8using System.Diagnostics.CodeAnalysis;
9using System.Windows.Forms;
10using System.Globalization;
11using System.Reflection;
12using System.Resources;
13using System.Threading;
14using System.Security.Permissions;
15using System.Data;
16
17
18[assembly: AssemblyDescription("Shows a hierarchical, relational, searchable " +
19 " view of all of the product, feature, component, file, and patch data managed " +
20 "by MSI, for all products installed on the system.")]
21
22[assembly: SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode=true)]
23
24
25namespace WixToolset.Dtf.Samples.Inventory
26{
27 public class Inventory : System.Windows.Forms.Form
28 {
29 [STAThread]
30 public static void Main()
31 {
32 if (WixToolset.Dtf.WindowsInstaller.Installer.Version < new Version(3, 0))
33 {
34 MessageBox.Show("This application requires Windows Installer version 3.0 or later.",
35 "Inventory", MessageBoxButtons.OK, MessageBoxIcon.Error);
36 return;
37 }
38
39 Application.Run(new Inventory());
40 }
41
42 private IInventoryDataProvider[] dataProviders;
43 private Hashtable dataProviderMap;
44 private Hashtable data;
45 private ArrayList tablesLoading;
46 private bool searching;
47 private bool stopSearch;
48 private bool navigating;
49 private string continueSearchRoot;
50 private string continueSearchPath;
51 private DataGridCell continueSearchCell;
52 private DataGridCell continueSearchEndCell;
53 private bool mouseOverGridLink = false;
54 private Stack historyBack;
55 private Stack historyForward;
56 private Stack cellHistoryBack;
57 private Stack cellHistoryForward;
58 private static readonly DataGridCell anyCell = new DataGridCell(-1,-1);
59 private static readonly DataGridCell zeroCell = new DataGridCell(0,0);
60 private static object syncRoot = new object();
61
62 private System.Windows.Forms.DataGrid dataGrid;
63 private System.Windows.Forms.TreeView treeView;
64 private System.Windows.Forms.Panel toolPanel;
65 private System.Windows.Forms.Splitter splitter;
66 private System.Windows.Forms.Panel dataPanel;
67 private System.Windows.Forms.Button backButton;
68 private System.Windows.Forms.Button forwardButton;
69 private System.Windows.Forms.Button findButton;
70 private System.Windows.Forms.TextBox findTextBox;
71 private System.Windows.Forms.Button refreshButton;
72 private System.Windows.Forms.Button findStopButton;
73 private System.Windows.Forms.CheckBox searchTreeCheckBox;
74 private System.Windows.Forms.ToolTip gridLinkTip;
75 private System.ComponentModel.IContainer components;
76
77 public Inventory()
78 {
79 InitializeComponent();
80
81 this.gridLinkTip.InitialDelay = 0;
82 this.gridLinkTip.ReshowDelay = 0;
83
84 this.dataProviderMap = new Hashtable();
85 this.data = new Hashtable();
86 this.tablesLoading = new ArrayList();
87 this.historyBack = new Stack();
88 this.historyForward = new Stack();
89 this.cellHistoryBack = new Stack();
90 this.cellHistoryForward = new Stack();
91 }
92
93 protected override void Dispose(bool disposing)
94 {
95 if(disposing)
96 {
97 if(components != null)
98 {
99 components.Dispose();
100 }
101 }
102 base.Dispose(disposing);
103 }
104
105 #region Windows Form Designer generated code
106 /// <summary>
107 /// Required method for Designer support - do not modify
108 /// the contents of this method with the code editor.
109 /// </summary>
110 private void InitializeComponent()
111 {
112 this.components = new System.ComponentModel.Container();
113 this.dataGrid = new System.Windows.Forms.DataGrid();
114 this.treeView = new System.Windows.Forms.TreeView();
115 this.toolPanel = new System.Windows.Forms.Panel();
116 this.findStopButton = new System.Windows.Forms.Button();
117 this.findButton = new System.Windows.Forms.Button();
118 this.searchTreeCheckBox = new System.Windows.Forms.CheckBox();
119 this.findTextBox = new System.Windows.Forms.TextBox();
120 this.refreshButton = new System.Windows.Forms.Button();
121 this.forwardButton = new System.Windows.Forms.Button();
122 this.backButton = new System.Windows.Forms.Button();
123 this.dataPanel = new System.Windows.Forms.Panel();
124 this.splitter = new System.Windows.Forms.Splitter();
125 this.gridLinkTip = new System.Windows.Forms.ToolTip(this.components);
126 ((System.ComponentModel.ISupportInitialize)(this.dataGrid)).BeginInit();
127 this.toolPanel.SuspendLayout();
128 this.dataPanel.SuspendLayout();
129 this.SuspendLayout();
130 //
131 // dataGrid
132 //
133 this.dataGrid.DataMember = "";
134 this.dataGrid.Dock = System.Windows.Forms.DockStyle.Fill;
135 this.dataGrid.HeaderForeColor = System.Drawing.SystemColors.ControlText;
136 this.dataGrid.Location = new System.Drawing.Point(230, 0);
137 this.dataGrid.Name = "dataGrid";
138 this.dataGrid.ReadOnly = true;
139 this.dataGrid.SelectionBackColor = System.Drawing.SystemColors.Highlight;
140 this.dataGrid.Size = new System.Drawing.Size(562, 432);
141 this.dataGrid.TabIndex = 1;
142 this.dataGrid.KeyDown += new System.Windows.Forms.KeyEventHandler(this.dataGrid_KeyDown);
143 this.dataGrid.MouseDown += new System.Windows.Forms.MouseEventHandler(this.dataGrid_MouseDown);
144 this.dataGrid.KeyUp += new System.Windows.Forms.KeyEventHandler(this.dataGrid_KeyUp);
145 this.dataGrid.MouseMove += new System.Windows.Forms.MouseEventHandler(this.dataGrid_MouseMove);
146 this.dataGrid.MouseLeave += new System.EventHandler(this.dataGrid_MouseLeave);
147 //
148 // treeView
149 //
150 this.treeView.Dock = System.Windows.Forms.DockStyle.Left;
151 this.treeView.HideSelection = false;
152 this.treeView.ImageIndex = -1;
153 this.treeView.Location = new System.Drawing.Point(0, 0);
154 this.treeView.Name = "treeView";
155 this.treeView.SelectedImageIndex = -1;
156 this.treeView.Size = new System.Drawing.Size(224, 432);
157 this.treeView.TabIndex = 0;
158 this.treeView.KeyDown += new System.Windows.Forms.KeyEventHandler(this.treeView_KeyDown);
159 this.treeView.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Inventory_MouseDown);
160 this.treeView.KeyUp += new System.Windows.Forms.KeyEventHandler(this.treeView_KeyUp);
161 this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
162 //
163 // toolPanel
164 //
165 this.toolPanel.Controls.Add(this.findStopButton);
166 this.toolPanel.Controls.Add(this.findButton);
167 this.toolPanel.Controls.Add(this.searchTreeCheckBox);
168 this.toolPanel.Controls.Add(this.findTextBox);
169 this.toolPanel.Controls.Add(this.refreshButton);
170 this.toolPanel.Controls.Add(this.forwardButton);
171 this.toolPanel.Controls.Add(this.backButton);
172 this.toolPanel.Dock = System.Windows.Forms.DockStyle.Top;
173 this.toolPanel.Location = new System.Drawing.Point(0, 0);
174 this.toolPanel.Name = "toolPanel";
175 this.toolPanel.Size = new System.Drawing.Size(792, 40);
176 this.toolPanel.TabIndex = 2;
177 //
178 // findStopButton
179 //
180 this.findStopButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
181 this.findStopButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
182 this.findStopButton.Location = new System.Drawing.Point(704, 8);
183 this.findStopButton.Name = "findStopButton";
184 this.findStopButton.Size = new System.Drawing.Size(72, 25);
185 this.findStopButton.TabIndex = 6;
186 this.findStopButton.Text = "Stop";
187 this.findStopButton.Visible = false;
188 this.findStopButton.Click += new System.EventHandler(this.findStopButton_Click);
189 //
190 // findButton
191 //
192 this.findButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
193 this.findButton.Enabled = false;
194 this.findButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
195 this.findButton.Location = new System.Drawing.Point(624, 8);
196 this.findButton.Name = "findButton";
197 this.findButton.Size = new System.Drawing.Size(72, 25);
198 this.findButton.TabIndex = 4;
199 this.findButton.Text = "Find";
200 this.findButton.Click += new System.EventHandler(this.findButton_Click);
201 //
202 // searchTreeCheckBox
203 //
204 this.searchTreeCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
205 this.searchTreeCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System;
206 this.searchTreeCheckBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
207 this.searchTreeCheckBox.Location = new System.Drawing.Point(704, 10);
208 this.searchTreeCheckBox.Name = "searchTreeCheckBox";
209 this.searchTreeCheckBox.Size = new System.Drawing.Size(80, 22);
210 this.searchTreeCheckBox.TabIndex = 5;
211 this.searchTreeCheckBox.Text = "In Subtree";
212 this.searchTreeCheckBox.CheckedChanged += new System.EventHandler(this.searchTreeCheckBox_CheckedChanged);
213 //
214 // findTextBox
215 //
216 this.findTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
217 this.findTextBox.Location = new System.Drawing.Point(344, 10);
218 this.findTextBox.Name = "findTextBox";
219 this.findTextBox.Size = new System.Drawing.Size(272, 20);
220 this.findTextBox.TabIndex = 3;
221 this.findTextBox.Text = "";
222 this.findTextBox.TextChanged += new System.EventHandler(this.findTextBox_TextChanged);
223 this.findTextBox.Enter += new System.EventHandler(this.findTextBox_Enter);
224 //
225 // refreshButton
226 //
227 this.refreshButton.Enabled = false;
228 this.refreshButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
229 this.refreshButton.Location = new System.Drawing.Point(160, 8);
230 this.refreshButton.Name = "refreshButton";
231 this.refreshButton.Size = new System.Drawing.Size(72, 25);
232 this.refreshButton.TabIndex = 2;
233 this.refreshButton.Text = "Refresh";
234 this.refreshButton.Click += new System.EventHandler(this.refreshButton_Click);
235 //
236 // forwardButton
237 //
238 this.forwardButton.Enabled = false;
239 this.forwardButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
240 this.forwardButton.Location = new System.Drawing.Point(80, 8);
241 this.forwardButton.Name = "forwardButton";
242 this.forwardButton.Size = new System.Drawing.Size(72, 25);
243 this.forwardButton.TabIndex = 1;
244 this.forwardButton.Text = "Forward";
245 this.forwardButton.Click += new System.EventHandler(this.forwardButton_Click);
246 //
247 // backButton
248 //
249 this.backButton.Enabled = false;
250 this.backButton.FlatStyle = System.Windows.Forms.FlatStyle.System;
251 this.backButton.Location = new System.Drawing.Point(8, 8);
252 this.backButton.Name = "backButton";
253 this.backButton.Size = new System.Drawing.Size(72, 25);
254 this.backButton.TabIndex = 0;
255 this.backButton.Text = "Back";
256 this.backButton.Click += new System.EventHandler(this.backButton_Click);
257 //
258 // dataPanel
259 //
260 this.dataPanel.Controls.Add(this.dataGrid);
261 this.dataPanel.Controls.Add(this.splitter);
262 this.dataPanel.Controls.Add(this.treeView);
263 this.dataPanel.Dock = System.Windows.Forms.DockStyle.Fill;
264 this.dataPanel.Location = new System.Drawing.Point(0, 40);
265 this.dataPanel.Name = "dataPanel";
266 this.dataPanel.Size = new System.Drawing.Size(792, 432);
267 this.dataPanel.TabIndex = 1;
268 //
269 // splitter
270 //
271 this.splitter.Location = new System.Drawing.Point(224, 0);
272 this.splitter.Name = "splitter";
273 this.splitter.Size = new System.Drawing.Size(6, 432);
274 this.splitter.TabIndex = 2;
275 this.splitter.TabStop = false;
276 //
277 // Inventory
278 //
279 this.AcceptButton = this.findButton;
280 this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
281 this.ClientSize = new System.Drawing.Size(792, 472);
282 this.Controls.Add(this.dataPanel);
283 this.Controls.Add(this.toolPanel);
284 this.MinimumSize = new System.Drawing.Size(700, 0);
285 this.Name = "Inventory";
286 this.Text = "MSI Inventory";
287 this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Inventory_KeyDown);
288 this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Inventory_MouseDown);
289 this.Load += new System.EventHandler(this.Inventory_Load);
290 this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.Inventory_KeyUp);
291 ((System.ComponentModel.ISupportInitialize)(this.dataGrid)).EndInit();
292 this.toolPanel.ResumeLayout(false);
293 this.dataPanel.ResumeLayout(false);
294 this.ResumeLayout(false);
295
296 }
297 #endregion
298
299
300 #region DataProviders
301
302 private IInventoryDataProvider[] DataProviders
303 {
304 get
305 {
306 if(this.dataProviders == null)
307 {
308 ArrayList providerList = new ArrayList();
309 providerList.AddRange(FindDataProviders(Assembly.GetExecutingAssembly()));
310
311 Uri codebase = new Uri(Assembly.GetExecutingAssembly().CodeBase);
312 if(codebase.IsFile)
313 {
314 foreach(string module in Directory.GetFiles(Path.GetDirectoryName(codebase.LocalPath), "*Inventory.dll"))
315 {
316 try
317 {
318 providerList.AddRange(FindDataProviders(Assembly.LoadFrom(module)));
319 }
320 catch(Exception) { }
321 }
322 }
323
324 this.dataProviders = (IInventoryDataProvider[]) providerList.ToArray(typeof(IInventoryDataProvider));
325 }
326 return this.dataProviders;
327 }
328 }
329
330 private static IList FindDataProviders(Assembly assembly)
331 {
332 ArrayList providerList = new ArrayList();
333 foreach(Type type in assembly.GetTypes())
334 {
335 if(type.IsClass)
336 {
337 foreach(Type implementedInterface in type.GetInterfaces())
338 {
339 if(implementedInterface.Equals(typeof(IInventoryDataProvider)))
340 {
341 try
342 {
343 providerList.Add(assembly.CreateInstance(type.FullName));
344 }
345 catch(Exception)
346 {
347 // Data provider's constructor threw an exception for some reason.
348 // Well, now we can't get any data from that one.
349 }
350 }
351 }
352 }
353 }
354 return providerList;
355 }
356
357 #endregion
358
359 private void GoTo(string nodePath, DataGridCell cell)
360 {
361 lock(syncRoot)
362 {
363 if(this.tablesLoading == null) return; // The tree is being loaded
364 if(this.navigating) return; // This method is already on the callstack
365
366 DataView table = (DataView) this.data[nodePath];
367 if(table != null && table == this.dataGrid.DataSource)
368 {
369 // Grid is already in view
370 if(!cell.Equals(anyCell)) this.dataGrid.CurrentCell = cell;
371 return;
372 }
373 if(cell.Equals(anyCell)) cell = zeroCell;
374
375 if(this.historyBack.Count == 0 || nodePath != (string) this.historyBack.Peek())
376 {
377 this.historyBack.Push(nodePath);
378 if(this.cellHistoryBack.Count > 0 && this.historyForward != null)
379 {
380 this.cellHistoryBack.Pop();
381 this.cellHistoryBack.Push(this.dataGrid.CurrentCell);
382 }
383 this.cellHistoryBack.Push(cell);
384 }
385 if(this.historyForward != null)
386 {
387 this.historyForward.Clear();
388 this.cellHistoryForward.Clear();
389 }
390
391 if(table != null || nodePath.Length == 0 || this.dataProviderMap[nodePath] == null)
392 {
393 this.dataGrid.CaptionText = nodePath;
394 this.dataGrid.CaptionBackColor = SystemColors.ActiveCaption;
395 this.dataGrid.CaptionForeColor = SystemColors.ActiveCaptionText;
396 this.dataGrid.DataSource = table;
397 this.dataGrid.CurrentCell = cell;
398 this.dataGrid.Focus();
399 }
400 else
401 {
402 this.dataGrid.CaptionText = nodePath + " (loading...)";
403 this.dataGrid.CaptionBackColor = SystemColors.InactiveCaption;
404 this.dataGrid.CaptionForeColor = SystemColors.InactiveCaptionText;
405 this.dataGrid.DataSource = table;
406 if(!this.tablesLoading.Contains(nodePath))
407 {
408 this.tablesLoading.Add(nodePath);
409 this.SetCursor();
410 #if SINGLETHREAD
411 this.LoadTable(nodePath);
412 #else
413 new WaitCallback(this.LoadTable).BeginInvoke(nodePath, null, null);
414 #endif
415 }
416 }
417
418 this.findButton.Enabled = this.findTextBox.Text.Length > 0 && !searching;
419
420 TreeNode treeNode = this.FindNode(nodePath);
421 if(treeNode != this.treeView.SelectedNode)
422 {
423 this.navigating = true;
424 this.treeView.SelectedNode = treeNode;
425 this.navigating = false;
426 }
427 }
428 }
429
430 private void LoadTable(object nodePathObj)
431 {
432 string nodePath = (string) nodePathObj;
433 IInventoryDataProvider dataProvider = (IInventoryDataProvider) this.dataProviderMap[nodePath];
434 DataView table = null;
435 if(dataProvider != null)
436 {
437 try
438 {
439 table = dataProvider.GetData(nodePath);
440 }
441 catch(Exception)
442 {
443 // Data provider threw an exception for some reason.
444 // Treat it like it returned no data.
445 }
446 }
447
448 lock(syncRoot)
449 {
450 if(this.tablesLoading == null || !tablesLoading.Contains(nodePath)) return;
451 if(table == null)
452 {
453 this.dataProviderMap.Remove(nodePath);
454 }
455 else
456 {
457 this.data[nodePath] = table;
458 }
459 this.tablesLoading.Remove(nodePath);
460 }
461 #if SINGLETHREAD
462 this.TableLoaded(nodePath);
463 #else
464 this.Invoke(new WaitCallback(this.TableLoaded), new object[] { nodePath });
465 #endif
466 }
467
468 private void TableLoaded(object nodePathObj)
469 {
470 string nodePath = (string) nodePathObj;
471 lock(syncRoot)
472 {
473 this.LoadTableStyle(nodePath);
474 if(nodePath == this.CurrentNodePath)
475 {
476 this.dataGrid.CaptionBackColor = SystemColors.ActiveCaption;
477 this.dataGrid.CaptionForeColor = SystemColors.ActiveCaptionText;
478 this.dataGrid.CaptionText = nodePath;
479 this.dataGrid.DataSource = this.CurrentTable;
480 this.dataGrid.CurrentCell = (DataGridCell) this.cellHistoryBack.Peek();
481 this.dataGrid.Focus();
482 }
483 this.SetCursor();
484 }
485 }
486
487 private void RefreshData()
488 {
489 lock(syncRoot)
490 {
491 this.GoTo("", zeroCell);
492 this.treeView.Nodes.Clear();
493 this.dataGrid.TableStyles.Clear();
494 this.dataGrid.CaptionBackColor = SystemColors.InactiveCaption;
495 this.dataGrid.CaptionForeColor = SystemColors.InactiveCaptionText;
496 this.SetControlsEnabled(false);
497 this.treeView.BeginUpdate();
498 #if SINGLETHREAD
499 this.LoadTree();
500 #else
501 new ThreadStart(this.LoadTree).BeginInvoke(null, null);
502 #endif
503 }
504 }
505
506 private void SetControlsEnabled(bool enabled)
507 {
508 this.backButton.Enabled = enabled && this.historyBack.Count > 1;
509 this.forwardButton.Enabled = enabled && this.historyForward.Count > 0;
510 this.refreshButton.Enabled = enabled;
511 this.findButton.Enabled = enabled && this.findTextBox.Text.Length > 0 && !searching;
512 }
513
514 private WaitCallback treeStatusCallback;
515 private int treeNodesLoaded;
516 private int treeNodesLoadedBase;
517 private string treeNodesLoading;
518 private void TreeLoadDataProviderStatus(int status, string currentNode)
519 {
520 if (currentNode != null)
521 {
522 this.treeNodesLoading = currentNode;
523 }
524
525 this.treeNodesLoaded = treeNodesLoadedBase + status;
526 string statusString = String.Format("Loading tree... " + this.treeNodesLoaded);
527 if (!String.IsNullOrEmpty(this.treeNodesLoading))
528 {
529 statusString += ": " + treeNodesLoading;
530 }
531
532 #if SINGLETHREAD
533 treeStatusCallback(statusString);
534 #else
535 this.Invoke(treeStatusCallback, new object[] { statusString });
536 #endif
537 }
538
539 private void UpdateTreeLoadStatus(object status)
540 {
541 if(status == null)
542 {
543 // Loading is complete.
544 this.treeView.EndUpdate();
545 this.SetCursor();
546 this.GoTo("Products", new DataGridCell(0, 0));
547 this.SetControlsEnabled(true);
548 }
549 else
550 {
551 this.dataGrid.CaptionText = (string) status;
552 }
553 }
554
555 private void LoadTree()
556 {
557 lock(syncRoot)
558 {
559 if(this.tablesLoading == null) return;
560 this.tablesLoading = null;
561 this.dataProviderMap.Clear();
562 this.data.Clear();
563 this.Invoke(new ThreadStart(this.SetCursor));
564 }
565
566 this.treeStatusCallback = new WaitCallback(UpdateTreeLoadStatus);
567 this.LoadTreeNodes();
568 this.RenderTreeNodes();
569
570 lock(syncRoot)
571 {
572 this.tablesLoading = new ArrayList();
573 }
574 // Use a status of null to signal loading complete.
575 #if SINGLETHREAD
576 this.UpdateTreeLoadStatus(null);
577 #else
578 this.Invoke(new WaitCallback(this.UpdateTreeLoadStatus), new object[] { null });
579 #endif
580 }
581
582 private void LoadTreeNodes()
583 {
584 #if SINGLETHREAD
585 this.treeStatusCallback("Loading tree... ");
586 #else
587 this.Invoke(this.treeStatusCallback, new object[] { "Loading tree... " });
588 #endif
589 this.treeNodesLoaded = 0;
590 this.treeNodesLoading = null;
591 foreach(IInventoryDataProvider dataProvider in this.DataProviders)
592 {
593 this.treeNodesLoadedBase = this.treeNodesLoaded;
594 string[] nodePaths = null;
595 try
596 {
597 nodePaths = dataProvider.GetNodes(new InventoryDataLoadStatusCallback(this.TreeLoadDataProviderStatus));
598 }
599 catch(Exception)
600 {
601 // Data provider threw an exception for some reason.
602 // Treat it like it returned no data.
603 }
604 if(nodePaths != null)
605 {
606 foreach(string nodePath in nodePaths)
607 {
608 if(!this.dataProviderMap.Contains(nodePath))
609 {
610 this.dataProviderMap.Add(nodePath, dataProvider);
611 }
612 }
613 }
614 }
615 }
616
617 private void RenderTreeNodes()
618 {
619 #if SINGLETHREAD
620 this.treeStatusCallback("Rendering tree... ");
621 #else
622 this.Invoke(this.treeStatusCallback, new object[] { "Rendering tree... " });
623 #endif
624 this.treeNodesLoaded = 0;
625 foreach(DictionaryEntry nodePathAndProvider in this.dataProviderMap)
626 {
627 string nodePath = (string) nodePathAndProvider.Key;
628 #if SINGLETHREAD
629 this.AddNode(nodePath);
630 #else
631 this.Invoke(new WaitCallback(this.AddNode), new object[] { nodePath });
632 #endif
633 }
634 }
635
636 private void LoadTableStyle(string nodePath)
637 {
638 DataView table = (DataView) this.data[nodePath];
639 if(table != null)
640 {
641 DataGridTableStyle tableStyle = this.dataGrid.TableStyles[table.Table.TableName];
642 if(tableStyle == null)
643 {
644 tableStyle = new DataGridTableStyle();
645 tableStyle.MappingName = table.Table.TableName;
646 tableStyle.RowHeadersVisible = true;
647 this.dataGrid.TableStyles.Add(tableStyle);
648 }
649 foreach(DataColumn column in table.Table.Columns)
650 {
651 if(!tableStyle.GridColumnStyles.Contains(column.ColumnName))
652 {
653 string colStyle = (string) ColumnResources.GetObject(column.ColumnName, CultureInfo.InvariantCulture);
654 if(colStyle != null)
655 {
656 string[] colStyleParts = colStyle.Split(',');
657 DataGridColumnStyle columnStyle = (colStyleParts.Length > 2 && colStyleParts[2] == "bool"
658 ? (DataGridColumnStyle) new DataGridBoolColumn() : (DataGridColumnStyle) new DataGridTextBoxColumn());
659 try { if(colStyleParts.Length > 1) columnStyle.Width = Int32.Parse(colStyleParts[1]); }
660 catch(FormatException) { }
661 columnStyle.HeaderText = colStyleParts[0];
662 columnStyle.MappingName = column.ColumnName;
663 tableStyle.GridColumnStyles.Add(columnStyle);
664 }
665 }
666 }
667 }
668 }
669
670 private static ResourceManager ColumnResources
671 {
672 get
673 {
674 if(columnResources == null)
675 {
676 columnResources = new ResourceManager(typeof(Inventory).Name + ".Columns", typeof(Inventory).Assembly);
677 }
678 return columnResources;
679 }
680 }
681 private static ResourceManager columnResources;
682
683 private void AddNode(object nodePathObj)
684 {
685 string nodePath = (string) nodePathObj;
686 string[] path = nodePath.Split('\\');
687 TreeNodeCollection nodes = this.treeView.Nodes;
688 TreeNode node = null;
689 foreach(string pathPart in path)
690 {
691 node = null;
692 for(int i = 0; i < nodes.Count; i++)
693 {
694 int c = string.CompareOrdinal(nodes[i].Text, pathPart);
695 if(c == 0)
696 {
697 node = nodes[i];
698 break;
699 }
700 else if(c > 0)
701 {
702 node = new TreeNode(pathPart);
703 nodes.Insert(i, node);
704 break;
705 }
706 }
707 if(node == null)
708 {
709 node = new TreeNode(pathPart);
710 nodes.Add(node);
711 }
712 nodes = node.Nodes;
713 }
714 if(++this.treeNodesLoaded % 1000 == 0)
715 {
716 this.UpdateTreeLoadStatus("Rendering tree... " +
717 (100 * this.treeNodesLoaded / this.dataProviderMap.Count) + "%");
718 }
719 }
720
721 public string CurrentNodePath
722 {
723 get
724 {
725 TreeNode currentNode = this.treeView.SelectedNode;
726 return currentNode != null ? currentNode.FullPath : null;
727 }
728 }
729
730 public DataView CurrentTable
731 {
732 get
733 {
734 string currentNodePath = this.CurrentNodePath;
735 return currentNodePath != null ? (DataView) this.data[this.CurrentNodePath] : null;
736 }
737 }
738
739 private TreeNode FindNode(string nodePath)
740 {
741 if(nodePath == null) return null;
742 string[] path = nodePath.Split('\\');
743 TreeNodeCollection nodes = this.treeView.Nodes;
744 TreeNode node = null;
745 foreach(string pathPart in path)
746 {
747 node = null;
748 for(int i = 0; i < nodes.Count; i++)
749 {
750 if(nodes[i].Text == pathPart)
751 {
752 node = nodes[i];
753 break;
754 }
755 }
756 if(node != null)
757 {
758 nodes = node.Nodes;
759 }
760 }
761 return node;
762 }
763
764 private void dataGrid_MouseDown(object sender, MouseEventArgs e)
765 {
766 Keys modKeys = Control.ModifierKeys;
767 if(e.Button == MouseButtons.Left && (modKeys & (Keys.Shift | Keys.Control)) == 0)
768 {
769 DataGrid.HitTestInfo hit = this.dataGrid.HitTest(e.X, e.Y);
770 string link = this.GetLinkForGridHit(hit);
771 if(link != null)
772 {
773 TreeNode node = this.FindNode(link);
774 if(node != null)
775 {
776 this.treeView.SelectedNode = node;
777 node.Expand();
778 }
779 }
780 }
781 this.Inventory_MouseDown(sender, e);
782 }
783
784 private void dataGrid_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
785 {
786 //this.gridLinkTip.SetToolTip(this.dataGrid, null);
787 DataGrid.HitTestInfo hit = this.dataGrid.HitTest(e.X, e.Y);
788 if(hit.Type == DataGrid.HitTestType.RowHeader)
789 {
790 string link = this.GetLinkForGridHit(hit);
791 if(link != null)
792 {
793 this.mouseOverGridLink = true;
794 this.SetCursor();
795 return;
796 }
797 }
798 else if(this.mouseOverGridLink)
799 {
800 this.mouseOverGridLink = false;
801 this.SetCursor();
802 }
803 }
804
805 private void dataGrid_MouseLeave(object sender, System.EventArgs e)
806 {
807 this.mouseOverGridLink = false;
808 this.SetCursor();
809 }
810
811 private string GetLinkForGridHit(DataGrid.HitTestInfo hit)
812 {
813 if(hit.Type == DataGrid.HitTestType.RowHeader && this.tablesLoading != null)
814 {
815 string nodePath = this.CurrentNodePath;
816 DataView table = (DataView) this.data[nodePath];
817 if(table != null)
818 {
819 DataRow row = table[hit.Row].Row;
820 IInventoryDataProvider dataProvider = (IInventoryDataProvider) this.dataProviderMap[nodePath];
821 return dataProvider.GetLink(nodePath, table[hit.Row].Row);
822 }
823 }
824 return null;
825 }
826
827 private void HistoryBack()
828 {
829 lock(syncRoot)
830 {
831 if(this.historyBack.Count > 1)
832 {
833 string nodePath = (string) this.historyBack.Pop();
834 this.cellHistoryBack.Pop();
835 DataGridCell cell = this.dataGrid.CurrentCell;
836 Stack saveForward = this.historyForward;
837 this.historyForward = null;
838 this.GoTo((string) this.historyBack.Pop(), (DataGridCell) this.cellHistoryBack.Pop());
839 this.historyForward = saveForward;
840 this.historyForward.Push(nodePath);
841 this.cellHistoryForward.Push(cell);
842 this.backButton.Enabled = this.historyBack.Count > 1;
843 this.forwardButton.Enabled = this.historyForward.Count > 0;
844 }
845 }
846 }
847
848 private void HistoryForward()
849 {
850 lock(syncRoot)
851 {
852 if(this.historyForward.Count > 0)
853 {
854 string nodePath = (string) this.historyForward.Pop();
855 DataGridCell cell = (DataGridCell) this.cellHistoryForward.Pop();
856 Stack saveForward = this.historyForward;
857 this.historyForward = null;
858 this.GoTo(nodePath, cell);
859 this.historyForward = saveForward;
860 this.backButton.Enabled = this.historyBack.Count > 1;
861 this.forwardButton.Enabled = this.historyForward.Count > 0;
862 }
863 }
864 }
865
866 #region Find
867
868 private void Find()
869 {
870 this.BeginFind();
871 object[] findNextArgs = new object[] { this.CurrentNodePath, this.dataGrid.CurrentCell, this.treeView.SelectedNode };
872 #if SINGLETHREAD
873 this.FindNext(findNextArgs);
874 #else
875 new WaitCallback(this.FindNext).BeginInvoke(findNextArgs, null, null);
876 #endif
877 }
878
879 [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
880 private void FindNext(object start)
881 {
882 string nodePath = (string) ((object[]) start)[0];
883 DataGridCell startCell = (DataGridCell) ((object[]) start)[1];
884 TreeNode searchNode = (TreeNode) ((object[]) start)[2];
885 DataGridCell endCell = startCell;
886
887 string searchString = this.findTextBox.Text;
888 if(searchString.Length == 0) return;
889
890 bool ignoreCase = true; // TODO: make this a configurable option?
891 if(ignoreCase) searchString = searchString.ToLowerInvariant();
892
893 if(!this.searchTreeCheckBox.Checked)
894 {
895 DataGridCell foundCell;
896 startCell.ColumnNumber++;
897 if(FindInTable((DataView) this.data[nodePath], searchString, ignoreCase,
898 startCell, startCell, true, out foundCell))
899 {
900 #if SINGLETHREAD
901 this.EndFind(new object[] { nodePath, foundCell });
902 #else
903 this.Invoke(new WaitCallback(this.EndFind), new object[] { new object[] { nodePath, foundCell } });
904 #endif
905 return;
906 }
907 }
908 else
909 {
910 if(this.continueSearchRoot != null)
911 {
912 searchNode = this.FindNode(this.continueSearchRoot);
913 startCell = this.continueSearchCell;
914 endCell = this.continueSearchEndCell;
915 }
916 else
917 {
918 this.continueSearchRoot = searchNode.FullPath;
919 this.continueSearchPath = this.continueSearchRoot;
920 this.continueSearchEndCell = endCell;
921 }
922 //if(searchNode == null) return;
923 ArrayList nodesList = new ArrayList();
924 nodesList.Add(searchNode);
925 this.GetFlatTreeNodes(searchNode.Nodes, nodesList, true, this.continueSearchRoot);
926 TreeNode[] nodes = (TreeNode[]) nodesList.ToArray(typeof(TreeNode));
927 int startNode = nodesList.IndexOf(this.FindNode(this.continueSearchPath));
928 DataGridCell foundCell;
929 startCell.ColumnNumber++;
930 for(int i = startNode; i < nodes.Length; i++)
931 {
932 if(this.stopSearch) break;
933 DataGridCell startCellOnThisNode = zeroCell;
934 if(i == startNode) startCellOnThisNode = startCell;
935 DataView table = this.GetTableForSearch(nodes[i].FullPath);
936 if(table != null)
937 {
938 if(FindInTable(table, searchString, ignoreCase, startCellOnThisNode, zeroCell, false, out foundCell))
939 {
940 #if SINGLETHREAD
941 this.EndFind(new object[] { nodes[i].FullPath, foundCell });
942 #else
943 this.Invoke(new WaitCallback(this.EndFind), new object[] { new object[] { nodes[i].FullPath, foundCell } });
944 #endif
945 return;
946 }
947 }
948 }
949 if(!this.stopSearch)
950 {
951 DataView table = this.GetTableForSearch(searchNode.FullPath);
952 if(table != null)
953 {
954 if(FindInTable(table, searchString, ignoreCase, zeroCell, endCell, false, out foundCell))
955 {
956 #if SINGLETHREAD
957 this.EndFind(new object[] { searchNode.FullPath, foundCell });
958 #else
959 this.Invoke(new WaitCallback(this.EndFind), new object[] { new object[] { searchNode.FullPath, foundCell } });
960 #endif
961 return;
962 }
963 }
964 }
965 }
966 #if SINGLETHREAD
967 this.EndFind(null);
968 #else
969 this.Invoke(new WaitCallback(this.EndFind), new object[] { null });
970 #endif
971 }
972
973 private DataView GetTableForSearch(string nodePath)
974 {
975 DataView table = (DataView) this.data[nodePath];
976 string status = nodePath;
977 if(table == null) status = status + " (loading)";
978 #if SINGLETHREAD
979 this.FindStatus(nodePath);
980 #else
981 this.Invoke(new WaitCallback(this.FindStatus), new object[] { status });
982 #endif
983 if(table == null)
984 {
985 this.tablesLoading.Add(nodePath);
986 this.Invoke(new ThreadStart(this.SetCursor));
987 this.LoadTable(nodePath);
988 table = (DataView) this.data[nodePath];
989 }
990 return table;
991 }
992
993 private void GetFlatTreeNodes(TreeNodeCollection nodes, IList resultsList, bool searchable, string searchRoot)
994 {
995 foreach(TreeNode node in nodes)
996 {
997 string nodePath = node.FullPath;
998 IInventoryDataProvider dataProvider = (IInventoryDataProvider) this.dataProviderMap[nodePath];
999 if(!searchable || (dataProvider != null && dataProvider.IsNodeSearchable(searchRoot, nodePath)))
1000 {
1001 resultsList.Add(node);
1002 }
1003 GetFlatTreeNodes(node.Nodes, resultsList, searchable, searchRoot);
1004 }
1005 }
1006
1007 [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
1008 private bool FindInTable(DataView table, string searchString, bool lowerCase,
1009 DataGridCell startCell, DataGridCell endCell, bool wrap, out DataGridCell foundCell)
1010 {
1011 foundCell = new DataGridCell(-1, -1);
1012 if(table == null) return false;
1013 if(startCell.RowNumber < 0) startCell.RowNumber = 0;
1014 if(startCell.ColumnNumber < 0) startCell.ColumnNumber = 0;
1015 for(int searchRow = startCell.RowNumber; searchRow < table.Count; searchRow++)
1016 {
1017 if(this.stopSearch) break;
1018 if(endCell.RowNumber > startCell.RowNumber && searchRow > endCell.RowNumber) break;
1019
1020 DataRowView tableRow = table[searchRow];
1021 for(int searchCol = (searchRow == startCell.RowNumber
1022 ? startCell.ColumnNumber : 0); searchCol < table.Table.Columns.Count; searchCol++)
1023 {
1024 if(this.stopSearch) break;
1025 if(endCell.RowNumber > startCell.RowNumber && searchRow == endCell.RowNumber
1026 && searchCol >= endCell.ColumnNumber) break;
1027
1028 string value = tableRow[searchCol].ToString();
1029 if(lowerCase) value = value.ToLowerInvariant();
1030 if(value.IndexOf(searchString, StringComparison.Ordinal) >= 0)
1031 {
1032 foundCell.RowNumber = searchRow;
1033 foundCell.ColumnNumber = searchCol;
1034 return true;
1035 }
1036 }
1037 }
1038 if(wrap)
1039 {
1040 for(int searchRow = 0; searchRow <= endCell.RowNumber; searchRow++)
1041 {
1042 if(this.stopSearch) break;
1043 DataRowView tableRow = table[searchRow];
1044 for(int searchCol = 0; searchCol < (searchRow == endCell.RowNumber
1045 ? endCell.ColumnNumber : table.Table.Columns.Count); searchCol++)
1046 {
1047 if(this.stopSearch) break;
1048 string value = tableRow[searchCol].ToString();
1049 if(lowerCase) value = value.ToLowerInvariant();
1050 if(value.IndexOf(searchString, StringComparison.Ordinal) >= 0)
1051 {
1052 foundCell.RowNumber = searchRow;
1053 foundCell.ColumnNumber = searchCol;
1054 return true;
1055 }
1056 }
1057 }
1058 }
1059 return false;
1060 }
1061
1062 private void BeginFind()
1063 {
1064 lock(syncRoot)
1065 {
1066 this.findButton.Enabled = false;
1067 this.findButton.Text = "Searching...";
1068 this.findTextBox.Enabled = false;
1069 this.searchTreeCheckBox.Visible = false;
1070 this.findStopButton.Visible = true;
1071 this.refreshButton.Enabled = false;
1072 this.searching = true;
1073 this.stopSearch = false;
1074 this.SetCursor();
1075 }
1076 }
1077
1078 private void FindStatus(object status)
1079 {
1080 lock(syncRoot)
1081 {
1082 this.dataGrid.CaptionText = "Searching... " + (string) status;
1083 this.dataGrid.CaptionBackColor = SystemColors.InactiveCaption;
1084 this.dataGrid.CaptionForeColor = SystemColors.InactiveCaptionText;
1085 }
1086 }
1087
1088 private void EndFind(object result)
1089 {
1090 lock(syncRoot)
1091 {
1092 this.searching = false;
1093 this.refreshButton.Enabled = true;
1094 this.findStopButton.Visible = false;
1095 this.searchTreeCheckBox.Visible = true;
1096 this.findTextBox.Enabled = true;
1097 this.findButton.Text = "Find";
1098 this.findButton.Enabled = true;
1099 this.dataGrid.CaptionBackColor = SystemColors.ActiveCaption;
1100 this.dataGrid.CaptionForeColor = SystemColors.ActiveCaptionText;
1101 this.dataGrid.CaptionText = this.CurrentNodePath;
1102 if(result != null)
1103 {
1104 string nodePath = (string) ((object[]) result)[0];
1105 DataGridCell foundCell = (DataGridCell) ((object[]) result)[1];
1106 this.GoTo(nodePath, foundCell);
1107 this.dataGrid.Focus();
1108 this.continueSearchPath = nodePath;
1109 this.continueSearchCell = foundCell;
1110 if(this.searchTreeCheckBox.Checked) this.searchTreeCheckBox.Text = "Continue";
1111 }
1112 else
1113 {
1114 this.continueSearchRoot = null;
1115 this.continueSearchPath = null;
1116 this.searchTreeCheckBox.Text = "In Subtree";
1117 }
1118 this.SetCursor();
1119 }
1120 }
1121
1122 private void SetCursor()
1123 {
1124 if(this.mouseOverGridLink)
1125 {
1126 Keys modKeys = Control.ModifierKeys;
1127 if((modKeys & (Keys.Shift | Keys.Control)) == 0)
1128 {
1129 this.Cursor = Cursors.Hand;
1130 return;
1131 }
1132 }
1133 if(this.tablesLoading == null || this.tablesLoading.Count > 0 || this.searching)
1134 {
1135 this.Cursor = Cursors.AppStarting;
1136 return;
1137 }
1138 this.Cursor = Cursors.Arrow;
1139 }
1140
1141 #endregion
1142
1143 #region EventHandlers
1144
1145 private void Inventory_Load(object sender, System.EventArgs e)
1146 {
1147 this.RefreshData();
1148 }
1149 private void refreshButton_Click(object sender, System.EventArgs e)
1150 {
1151 this.RefreshData();
1152 }
1153 private void Inventory_MouseDown(object sender, MouseEventArgs e)
1154 {
1155 if(e.Button == MouseButtons.XButton1) this.HistoryBack();
1156 else if(e.Button == MouseButtons.XButton2) this.HistoryForward();
1157 }
1158 private void Inventory_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
1159 {
1160 this.SetCursor();
1161 if(e.KeyCode == Keys.F3) this.Find();
1162 else if(e.KeyCode == Keys.F && (e.Modifiers | Keys.Control) != 0) this.findTextBox.Focus();
1163 else if(e.KeyCode == Keys.BrowserBack) this.HistoryBack();
1164 else if(e.KeyCode == Keys.BrowserForward) this.HistoryForward();
1165 else return;
1166 e.Handled = true;
1167 }
1168 private void treeView_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
1169 {
1170 this.Inventory_KeyDown(sender, e);
1171 }
1172 private void dataGrid_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
1173 {
1174 this.Inventory_KeyDown(sender, e);
1175 }
1176 private void Inventory_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
1177 {
1178 this.SetCursor();
1179 }
1180 private void treeView_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
1181 {
1182 this.Inventory_KeyDown(sender, e);
1183 }
1184 private void dataGrid_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
1185 {
1186 this.Inventory_KeyDown(sender, e);
1187 }
1188 private void treeView_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
1189 {
1190 this.GoTo(e.Node.FullPath, anyCell);
1191 }
1192 private void backButton_Click(object sender, System.EventArgs e)
1193 {
1194 this.HistoryBack();
1195 }
1196 private void forwardButton_Click(object sender, System.EventArgs e)
1197 {
1198 this.HistoryForward();
1199 }
1200 private void findTextBox_TextChanged(object sender, System.EventArgs e)
1201 {
1202 this.findButton.Enabled = this.findTextBox.Text.Length > 0 &&
1203 this.tablesLoading != null && this.treeView.SelectedNode != null && !searching;
1204 this.searchTreeCheckBox.Text = "In Subtree";
1205 this.continueSearchRoot = null;
1206 }
1207 private void findButton_Click(object sender, System.EventArgs e)
1208 {
1209 this.Find();
1210 }
1211 private void findTextBox_Enter(object sender, System.EventArgs e)
1212 {
1213 findTextBox.SelectAll();
1214 }
1215 private void findStopButton_Click(object sender, System.EventArgs e)
1216 {
1217 this.stopSearch = true;
1218 }
1219
1220 private void searchTreeCheckBox_CheckedChanged(object sender, System.EventArgs e)
1221 {
1222 if(!searchTreeCheckBox.Checked && searchTreeCheckBox.Text == "Continue")
1223 {
1224 this.searchTreeCheckBox.Text = "In Subtree";
1225 }
1226 }
1227
1228 #endregion
1229
1230 }
1231}