diff --git a/PROMS/VEPROMS User Interface/frmVEPROMS.Designer.cs b/PROMS/VEPROMS User Interface/frmVEPROMS.Designer.cs index e2f0b575..e94335f3 100644 --- a/PROMS/VEPROMS User Interface/frmVEPROMS.Designer.cs +++ b/PROMS/VEPROMS User Interface/frmVEPROMS.Designer.cs @@ -71,9 +71,12 @@ namespace VEPROMS this.labelItem10 = new DevComponents.DotNetBar.LabelItem(); this.bottomProgBar = new DevComponents.DotNetBar.ProgressBarItem(); this.labelItem9 = new DevComponents.DotNetBar.LabelItem(); + this.lblPreEditView = new DevComponents.DotNetBar.LabelItem(); this.lblEditView = new DevComponents.DotNetBar.LabelItem(); this.labelItem11 = new DevComponents.DotNetBar.LabelItem(); this.btnItemInfo = new DevComponents.DotNetBar.ButtonItem(); + this.btnFilter = new DevComponents.DotNetBar.ButtonItem(); + this.txtFilter = new DevComponents.DotNetBar.TextBoxItem(); this.lblItemID = new DevComponents.DotNetBar.LabelItem(); this.lblResolution = new DevComponents.DotNetBar.LabelItem(); this.btnEditItem = new DevComponents.DotNetBar.ButtonItem(); @@ -524,19 +527,22 @@ namespace VEPROMS this.bottomBar.Font = new System.Drawing.Font("Segoe UI", 9F); this.bottomBar.IsMaximized = false; this.bottomBar.Items.AddRange(new DevComponents.DotNetBar.BaseItem[] { - this.labelItem10, + this.btnFilter, + this.txtFilter, + this.labelItem10, this.bottomProgBar, this.labelItem9, - this.lblEditView, - this.labelItem11, - this.btnItemInfo, - this.lblItemID, - this.lblResolution, - this.btnEditItem, - this.lblUser, - this.lblLastChange, - this.btnStepRTF, - this.btnFixMSWord}); + this.lblPreEditView, + this.lblEditView, + this.labelItem11, + this.btnItemInfo, + this.lblItemID, + this.lblResolution, + this.btnEditItem, + this.lblUser, + this.lblLastChange, + this.btnStepRTF, + this.btnFixMSWord}); this.bottomBar.Location = new System.Drawing.Point(5, 573); this.bottomBar.Name = "bottomBar"; this.bottomBar.Size = new System.Drawing.Size(1185, 25); @@ -546,6 +552,21 @@ namespace VEPROMS this.bottomBar.TabStop = false; this.bottomBar.Text = "bar1"; // + // btnFilter + // + this.btnFilter.Name = "btnFilter"; + this.btnFilter.Text = "Tree View Search:"; + this.btnFilter.Click += SubmitFilter; + // + // txtFilter + // + this.txtFilter.TextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F); + this.txtFilter.Name = "txtFilter"; + this.txtFilter.TextBoxWidth = 120; + this.txtFilter.TextBox.TabIndex = 0; + this.txtFilter.TextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.FilterEnterKey); + this.superTooltip1.SetSuperTooltip(this.txtFilter, new DevComponents.DotNetBar.SuperTooltipInfo("Filter", "", "This will filter the Procedure Tree in PROMS to Procedures containing entered words in the Procedure Number or Title. Press enter or the Tree View Search button to submit.", null, null, DevComponents.DotNetBar.eTooltipColor.Gray)); + // // txtSearch // this.txtSearch.Dock = System.Windows.Forms.DockStyle.Right; @@ -647,6 +668,11 @@ namespace VEPROMS this.lblEditView.ForeColor = System.Drawing.SystemColors.MenuText; this.lblEditView.Name = "lblEditView"; this.lblEditView.Text = "Edit"; + // lblEditView + // + this.lblPreEditView.BorderSide = DevComponents.DotNetBar.eBorderSide.Left; + this.lblPreEditView.BorderType = DevComponents.DotNetBar.eBorderType.Bump; + this.lblPreEditView.Name = "lblPreEditView"; // // labelItem11 // @@ -1646,9 +1672,9 @@ namespace VEPROMS this.ResumeLayout(false); } - #endregion + #endregion - private DevComponents.DotNetBar.RibbonControl ribbonControl1; + private DevComponents.DotNetBar.RibbonControl ribbonControl1; private DevComponents.DotNetBar.Office2007StartButton office2007StartButton1; private DevComponents.DotNetBar.ItemContainer itemContainer1; private DevComponents.DotNetBar.ItemContainer itemContainer2; @@ -1744,8 +1770,11 @@ namespace VEPROMS private DevComponents.DotNetBar.TabItem tabItemLibDocs; private Volian.Controls.Library.DisplayTags displayTags; private Volian.Controls.Library.DisplaySearch displaySearch1; + private DevComponents.DotNetBar.LabelItem lblPreEditView; private DevComponents.DotNetBar.LabelItem lblEditView; private DevComponents.DotNetBar.ButtonItem btnItemInfo; + private DevComponents.DotNetBar.ButtonItem btnFilter; + private DevComponents.DotNetBar.TextBoxItem txtFilter; private DevComponents.DotNetBar.ButtonItem btnFixMSWord; private Volian.Controls.Library.DisplayBookMarks displayBookMarks; //private DevComponents.DotNetBar.LabelItem lblLocked; diff --git a/PROMS/VEPROMS User Interface/frmVEPROMS.cs b/PROMS/VEPROMS User Interface/frmVEPROMS.cs index 732c68a3..4e8c5079 100644 --- a/PROMS/VEPROMS User Interface/frmVEPROMS.cs +++ b/PROMS/VEPROMS User Interface/frmVEPROMS.cs @@ -22,6 +22,9 @@ using Volian.Base.Library; using Volian.Print.Library; using JR.Utils.GUI.Forms; using System.Diagnostics; +using System.Linq; +using System.Collections.Concurrent; +using System.Threading.Tasks; [assembly: log4net.Config.XmlConfigurator(Watch = true)] @@ -2771,6 +2774,199 @@ namespace VEPROMS } } + //C2024-036 IntelliSense + // Recursively Expand the Tree + // up to the procedure level + // Since items are only loaded as they are expanded, + // this is needed to search for matching text. + private void LoadAllProcedures(VETreeNode tn) + { + if (tn == null || tn.VEObject.IsProcedure) + return; + + if (!tn.IsExpanded) + { tn.Expand(); } + + foreach (var nde in tn.Nodes) + LoadAllProcedures((VETreeNode)nde); + } + + //C2024-036 IntelliSense + // Return a list containing all + // items in the Tree that match the entered text + // + any item that is a parent of those. + private List GetMatchingTreeNodes(VETreeNode tn, string texttomatch) + { + List matches = new List(); + + //add any item that matches text + //and all parents back up to the top most item + if (tn.Text.ToLower().Contains(texttomatch.ToLower())) + { + matches.Add(tn.VEObject); + VETreeNode parent_tn = (VETreeNode) tn.Parent; + while (parent_tn != null) + { + if (!matches.Contains(parent_tn.VEObject)) + { matches.Add(parent_tn.VEObject); } + parent_tn = (VETreeNode)parent_tn.Parent; + } + } + + if (!tn.VEObject.IsProcedure) + { + //if not at a procedure level, + //loop through sub items + //to check those for matches + foreach (VETreeNode nde in tn.Nodes.OfType()) + { + if (nde.Text != "Dummy VETreeNode(IVEDrillDownReadOnly o)") + { + List tmpmatches = GetMatchingTreeNodes(nde, texttomatch); + matches.AddRange(from mtch in tmpmatches + where !matches.Contains(mtch) + select mtch); + } + } + } + + return matches; + } + + //C2024-036 IntelliSense + // Recursively loop through the Child Nodes + // in the TreeView + // remove anything not in the list + private bool FilterTreeNodes(VETreeNode tn, List filterlist) + { + if (tn == null) + return false; + + if (!filterlist.Contains(tn.VEObject) && (tn != (VETreeNode) tv.Nodes[0])) //Note:Always keep the top node + { + return true; + } + else if (!tn.VEObject.IsProcedure) + { + //if not a procedure + //mark the sub-items that need removal + List lst = (from VETreeNode nde in tn.Nodes.OfType() + where FilterTreeNodes(nde, filterlist) + select (TreeNode) nde).ToList(); + + //remove all items in the removal list + //need to do this separately as can't modify the collection while looping through it above + while (lst.Count > 0) + { + tn.Nodes.Remove(lst[0]); + lst.Remove(lst[0]); + } + + //found a folder that matched that has no matching procedures + //if this is case, allow folder to be expanded + if (tn.Nodes.Count == 0 && filterlist.Contains(tn.VEObject)) + { + tn.ChildrenLoaded = false; + tn.RefreshNode(); + } + } + + return false; + + } + + //C2024-036 IntelliSense + //Collection to block for task to complete + private BlockingCollection blockingQueueFilter = new BlockingCollection(); + + //C2024-036 IntelliSense + // When Text Changes, Perform the filtering + // Note that uses a blocking collection + // in case multiple events fire at the same time + // this is to prevent incorrect behavior when + //doing something like hitting backspace a bunch + private void SubmitFilter(object sender, EventArgs e) + { + //Consumer + Task.Run(() => + { + //Blocks until a new filter is available + while (!blockingQueueFilter.IsCompleted) + { + string fltrtxt = blockingQueueFilter.Take(); + ProcessFilter(fltrtxt); + } + }); + + //Producer + Task.Run(() => { blockingQueueFilter.Add(txtFilter?.Text); }); + + } + + //C2024-036 IntelliSense + // When Enter Key is pressed in the TextBox + private void FilterEnterKey(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == '\r') // enter key pressed + { + e.Handled = true; + SubmitFilter(sender, e); + } + } + + //C2024-036 IntelliSense + // When Text Changes, Perform the filtering + // Note that uses Invoke to + // avoid multi-threaded problems + //with the blocking collection + private void ProcessFilter(string fltrtxt) + { + //Step 1: reload the base tree + VETreeNode tbase = (VETreeNode)tv.Nodes[0]; + tbase.ChildrenLoaded = false; + this.Invoke((Action) (() => { tbase.RefreshNode(); })); + + if (!string.IsNullOrEmpty(fltrtxt)) + { + //Step 2: Expand all TreeNodes + this.Invoke((Action)(() => { LoadAllProcedures(tbase); })); + + //Step 3: get items that match filter to those containing the title or number + List filterlist = GetMatchingTreeNodes(tbase, fltrtxt); + + //Step 4: filter to those containing the title or number + this.Invoke((Action)(() => {FilterTreeNodes(tbase, filterlist);})); + + //Step 5: refresh the view + this.Invoke((Action)(() => {tv.Update();})); + + //Step 6: select 1st procedure in tree view + this.Invoke((Action)(() => {SelectFirstProcedure(filterlist);})); + } + + //Step 7: set progress bar as done + this.Invoke((Action)(() => {ProgBarText = "Filtering Complete";})); + + } + + //C2024-036 IntelliSense + //Gives Focus to last child in first section of treeview + //then given focus to treeview + //so that can click enter and open 1st found item/procedure + void SelectFirstProcedure(List filterlist) + { + TreeNode tn = tv.Nodes[0]; + while (tn.Nodes.Count > 0 && filterlist.Contains(((VETreeNode) tn).VEObject)) + { + tn = tn.Nodes[0]; + } + + tv.SelectedNode = tn; + tv.Enabled = true; + tv.SelectedNode.Collapse(); + tv.Focus(); + } + void btnPrevious_Click(object sender, EventArgs e) { CurrentID = FindPreviousLine();