public TreeBuilderContext(TreeBuilderContext other) { CurrentCriteria = new HashSet <string>(other.CurrentCriteria, StringComparer.OrdinalIgnoreCase); MatchedItems = new HashSet <ItemDescriptor <TItem> >(); }
private static DecisionTreeNode <TItem> GenerateNode( TreeBuilderContext context, DecisionCriterionValueEqualityComparer comparer, List <ItemDescriptor <TItem> > items) { // The extreme use of generics here is intended to reduce the number of intermediate // allocations of wrapper classes. Performance testing found that building these trees allocates // significant memory that we can avoid and that it has a real impact on startup. var criteria = new Dictionary <string, Criterion>(StringComparer.OrdinalIgnoreCase); // Matches are items that have no remaining criteria - at this point in the tree // they are considered accepted. var matches = new List <TItem>(); // For each item in the working set, we want to map it to it's possible criteria-branch // pairings, then reduce that tree to the minimal set. foreach (var item in items) { var unsatisfiedCriteria = 0; foreach (var kvp in item.Criteria) { // context.CurrentCriteria is the logical 'stack' of criteria that we've already processed // on this branch of the tree. if (context.CurrentCriteria.Contains(kvp.Key)) { continue; } unsatisfiedCriteria++; if (!criteria.TryGetValue(kvp.Key, out var criterion)) { criterion = new Criterion(comparer); criteria.Add(kvp.Key, criterion); } if (!criterion.TryGetValue(kvp.Value, out var branch)) { branch = new List <ItemDescriptor <TItem> >(); criterion.Add(kvp.Value, branch); } branch.Add(item); } // If all of the criteria on item are satisfied by the 'stack' then this item is a match. if (unsatisfiedCriteria == 0) { matches.Add(item.Item); } } // Iterate criteria in order of branchiness to determine which one to explore next. If a criterion // has no 'new' matches under it then we can just eliminate that part of the tree. var reducedCriteria = new List <DecisionCriterion <TItem> >(); foreach (var criterion in criteria.OrderByDescending(c => c.Value.Count)) { var reducedBranches = new Dictionary <object, DecisionTreeNode <TItem> >(comparer.InnerComparer); foreach (var branch in criterion.Value) { bool hasReducedItems = false; foreach (var item in branch.Value) { if (context.MatchedItems.Add(item)) { hasReducedItems = true; } } if (hasReducedItems) { var childContext = new TreeBuilderContext(context); childContext.CurrentCriteria.Add(criterion.Key); var newBranch = GenerateNode(childContext, comparer, branch.Value); reducedBranches.Add(branch.Key.Value, newBranch); } } if (reducedBranches.Count > 0) { var newCriterion = new DecisionCriterion <TItem>() { Key = criterion.Key, Branches = reducedBranches, }; reducedCriteria.Add(newCriterion); } } return(new DecisionTreeNode <TItem>() { Criteria = reducedCriteria, Matches = matches, }); }
public virtual void Initialize(string label, string icon, NodeBuilder[] builders, TreePadOption[] options) { // Create default options this.options = options; globalOptions = new TreeOptions (); foreach (TreePadOption op in options) globalOptions [op.Id] = op.DefaultValue; globalOptions.Pad = this; // Check that there is only one node builder per type Hashtable bc = new Hashtable (); foreach (NodeBuilder nb in builders) { TypeNodeBuilder tnb = nb as TypeNodeBuilder; if (tnb != null) { TypeNodeBuilder other = (TypeNodeBuilder) bc [tnb.NodeDataType]; if (other != null) throw new ApplicationException (string.Format ("The type node builder {0} can't be used in this context because the type {1} is already handled by {2}", nb.GetType(), tnb.NodeDataType, other.GetType())); bc [tnb.NodeDataType] = tnb; } else if (!(nb is NodeBuilderExtension)) throw new InvalidOperationException (string.Format ("Invalid NodeBuilder type: {0}. NodeBuilders must inherit either from TypeNodeBuilder or NodeBuilderExtension", nb.GetType())); } NodeBuilders = builders; Title = label; Icon = icon; builderContext = new TreeBuilderContext (this); tree = new Gtk.TreeView (); /* 0 -- Text 1 -- Icon (Open) 2 -- Icon (Closed) 3 -- Node Data 4 -- Builder chain 5 -- Pango weight 6 -- Expanded */ store = new Gtk.TreeStore (typeof (string), typeof (Gdk.Pixbuf), typeof (Gdk.Pixbuf), typeof (object), typeof (object), typeof(int), typeof(bool)); tree.Model = store; tree.EnableModelDragDest (target_table, Gdk.DragAction.Copy | Gdk.DragAction.Move); Gtk.Drag.SourceSet (tree, Gdk.ModifierType.Button1Mask, target_table, Gdk.DragAction.Copy | Gdk.DragAction.Move); store.DefaultSortFunc = new Gtk.TreeIterCompareFunc (CompareNodes); store.SetSortColumnId (/* GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID */ -1, Gtk.SortType.Ascending); tree.HeadersVisible = false; tree.SearchColumn = 0; tree.EnableSearch = true; complete_column = new Gtk.TreeViewColumn (); complete_column.Title = "column"; Gtk.CellRendererPixbuf pix_render = new Gtk.CellRendererPixbuf (); complete_column.PackStart (pix_render, false); complete_column.AddAttribute (pix_render, "pixbuf", OpenIconColumn); complete_column.AddAttribute (pix_render, "pixbuf-expander-open", OpenIconColumn); complete_column.AddAttribute (pix_render, "pixbuf-expander-closed", ClosedIconColumn); text_render = new Gtk.CellRendererText (); text_render.Edited += new Gtk.EditedHandler (HandleOnEdit); complete_column.PackStart (text_render, true); complete_column.AddAttribute (text_render, "text", TextColumn); complete_column.AddAttribute (text_render, "weight", WeightColumn); tree.AppendColumn (complete_column); Gtk.ScrolledWindow sw = new Gtk.ScrolledWindow (); sw.Add(tree); contentPanel = new Gtk.Frame (); contentPanel.Add(sw); tree.TestExpandRow += new Gtk.TestExpandRowHandler (OnTestExpandRow); tree.RowActivated += new Gtk.RowActivatedHandler(OnNodeActivated); contentPanel.ButtonReleaseEvent += new Gtk.ButtonReleaseEventHandler(OnButtonRelease); contentPanel.PopupMenu += new Gtk.PopupMenuHandler (OnPopupMenu); foreach (NodeBuilder nb in builders) nb.SetContext (builderContext); workNode = new TreeNodeNavigator (this); compareNode1 = new TreeNodeNavigator (this); compareNode2 = new TreeNodeNavigator (this); tree.DragBegin += new Gtk.DragBeginHandler (OnDragBegin); tree.DragDataReceived += new Gtk.DragDataReceivedHandler (OnDragDataReceived); tree.DragDrop += new Gtk.DragDropHandler (OnDragDrop); tree.DragEnd += new Gtk.DragEndHandler (OnDragEnd); tree.DragMotion += new Gtk.DragMotionHandler (OnDragMotion); tree.CursorChanged += new EventHandler (OnSelectionChanged); contentPanel.ShowAll (); }