internal bool MakeOrClearSelection(NodeCategoryViewModel selectedClass) { if (currentClass != null) { if (currentClass != selectedClass) { // If 'itemIndex' is '-1', then the selection will be cleared, // otherwise the selection is set to the same as 'itemIndex'. var itemIndex = collection.IndexOf(selectedClass); classListView.SelectedIndex = itemIndex; return true; // The call is handled. } else { // If current item is selected item, then class button was pressed second time. // Selection should be cleaned. classListView.SelectedIndex = -1; return true; } } else { // No selection, if item is within collection, select it. var itemIndex = collection.IndexOf(selectedClass); if (itemIndex != -1) { classListView.SelectedIndex = itemIndex; return true; // The call is handled. } } // The call is not handled. return false; }
// TODO: classes functionality. internal void AddClassToGroup(NodeCategoryViewModel memberNode) { // TODO: The following limit of displaying only two classes are // temporary, it should be updated whenever the design intent has been finalized. // http://adsk-oss.myjetbrains.com/youtrack/issue/MAGN-6199 //const int maxClassesCount = 2; //if (classes.Count >= maxClassesCount) // return; // Parent should be of 'BrowserInternalElement' type or derived. // Root category can't be added to classes list. // TODO(Vladimir): Implement the logic when classes are shown in search results. }
public void InsertSubCategory(NodeCategoryViewModel newSubCategory) { var list = SubCategories.Where(cat => !(cat is ClassesNodeCategoryViewModel)); var nextLargerItemIndex = FindInsertionPointByName(list, newSubCategory.Name); if (nextLargerItemIndex >= 0) { bool hasClasses = SubCategories.FirstOrDefault() is ClassesNodeCategoryViewModel; var offset = hasClasses ? 1 : 0; SubCategories.Insert(nextLargerItemIndex + offset, newSubCategory); } else { SubCategories.Add(newSubCategory); } }
public void InsertSubCategory(NodeCategoryViewModel newSubCategory) { var first = SubCategories.Select((x, i) => new { x.Name, Idx = i }) .FirstOrDefault( x => string.Compare( x.Name, newSubCategory.Name, StringComparison.Ordinal) >= 0); if (first != null) { SubCategories.Insert(first.Idx, newSubCategory); } else { SubCategories.Add(newSubCategory); } }
private static IEnumerable<NodeSearchElementViewModel> GetVisibleSearchResults(NodeCategoryViewModel category) { foreach (var item in category.Items) { var sub = item as NodeCategoryViewModel; if (sub != null) { foreach (var visible in GetVisibleSearchResults(sub)) yield return visible; } else yield return (NodeSearchElementViewModel)item; } }
private void AddEntryToExistingCategory(NodeCategoryViewModel category, NodeSearchElementViewModel entry) { category.RequestBitmapSource += SearchViewModelRequestBitmapSource; category.Entries.Add(entry); }
private void InsertEntryIntoNewCategory( NodeCategoryViewModel category, NodeSearchElementViewModel entry, IEnumerable<string> categoryNames) { if (!categoryNames.Any()) { AddEntryToExistingCategory(category, entry); return; } // With the example of 'MyAssembly.MyNamespace.MyClass.Foo', 'path' would have been // set to 'MyAssembly' here. The Select statement below would store two entries into // 'newTargets' variable: // // NodeCategoryViewModel("MyAssembly.MyNamespace") // NodeCategoryViewModel("MyAssembly.MyNamespace.MyClass") // var path = category.FullCategoryName; var newTargets = categoryNames.Select(name => { path = MakeFullyQualifiedName(path, name); var cat = new NodeCategoryViewModel(name); cat.FullCategoryName = path; cat.Assembly = entry.Assembly; return cat; }).ToList(); // The last entry 'NodeCategoryViewModel' represents a class. For our example the // entries in 'newTargets' are: // // NodeCategoryViewModel("MyAssembly.MyNamespace") // NodeCategoryViewModel("MyAssembly.MyNamespace.MyClass") // // Since all class entries are contained under a 'ClassesNodeCategoryViewModel', // we need to create a new 'ClassesNodeCategoryViewModel' instance, and insert it // right before the class entry itself to get the following list: // // NodeCategoryViewModel("MyAssembly.MyNamespace") // ClassesNodeCategoryViewModel("Classes") // NodeCategoryViewModel("MyAssembly.MyNamespace.MyClass") // int indexToInsertClass = newTargets.Count - 1; var classParent = indexToInsertClass > 0 ? newTargets[indexToInsertClass - 1] : category; var newClass = new ClassesNodeCategoryViewModel(classParent); newTargets.Insert(indexToInsertClass, newClass); // Here, all the entries in 'newTargets' are added under 'MyAssembly' recursively, // resulting in the following hierarchical structure: // // NodeCategoryViewModel("MyAssembly") // NodeCategoryViewModel("MyAssembly.MyNamespace") // ClassesNodeCategoryViewModel("Classes") // NodeCategoryViewModel("MyAssembly.MyNamespace.MyClass") // foreach (var newTarget in newTargets) { category.SubCategories.Add(newTarget); category = newTarget; } AddEntryToExistingCategory(category, entry); }
private static IEnumerable<NodeCategoryViewModel> GetTreeBranchToNode( NodeCategoryViewModel rootNode, NodeSearchElement leafNode) { var nodesOnBranch = new Stack<NodeCategoryViewModel>(); var nameStack = new Stack<string>(leafNode.Categories.Reverse()); var target = rootNode; bool isCheckedForClassesCategory = false; while (nameStack.Any()) { var next = nameStack.Pop(); var categories = target.SubCategories; var newTarget = categories.FirstOrDefault(c => c.Name == next); if (newTarget == null) { // The last entry in categories list can be a class name. When the desired class // cannot be located with "MyAssembly.MyNamespace.ClassCandidate" pattern, try // searching with "MyAssembly.MyNamespace.Classes.ClassCandidate" instead. This // is because a class always resides under a "ClassesNodeCategoryViewModel" node. // if (!isCheckedForClassesCategory && nameStack.Count == 0) { nameStack.Push(next); nameStack.Push(Configurations.ClassesDefaultName); isCheckedForClassesCategory = true; continue; } return Enumerable.Empty<NodeCategoryViewModel>(); } nodesOnBranch.Push(target); target = newTarget; } nodesOnBranch.Push(target); return nodesOnBranch; }
public void PopulateMemberCollections(NodeCategoryViewModel element) { createMembers.Clear(); actionMembers.Clear(); queryMembers.Clear(); PrimaryHeaderItems.Clear(); ActionHeaderItems.Clear(); QueryHeaderItems.Clear(); foreach (var subElement in element.Entries) { switch (subElement.Model.Group) { case SearchElementGroup.Create: createMembers.Add(subElement); break; case SearchElementGroup.Action: actionMembers.Add(subElement); break; case SearchElementGroup.Query: queryMembers.Add(subElement); break; } } // Populate headers collections. string headerStripText = string.Empty; if (createMembers.Any()) { headerStripText = Configurations.HeaderCreate; } if (actionMembers.Any()) { // As soon as primary headers collection is defined, // add item to secondary headers collection. if (string.IsNullOrEmpty(headerStripText)) headerStripText = Configurations.HeaderAction; else ActionHeaderItems.Add(new HeaderStripItem() { Text = Configurations.HeaderAction }); } if (queryMembers.Any()) { // As soon as primary headers collection is defined, // add item to secondary headers collection. if (string.IsNullOrEmpty(headerStripText)) headerStripText = Configurations.HeaderQuery; else QueryHeaderItems.Add(new HeaderStripItem() { Text = Configurations.HeaderQuery }); } PrimaryHeaderItems.Add(new HeaderStripItem() { Text = headerStripText }); }
public void BrowserInternalElementToBoolConverterTest() { var converter = new NodeCategoryVMToBoolConverter(); var NcVM = new NodeCategoryViewModel(""); var RncVM = new RootNodeCategoryViewModel(""); var CncVM = new ClassesNodeCategoryViewModel(RncVM); object result; //1. Element is null. //2. Element is NodeCategoryViewModel. //2. Element is RootNodeCategoryViewModel. //2. Element is ClassesNodeCategoryViewModel. // 1 case result = converter.Convert(null, null, null, null); Assert.AreEqual(false, result); // 2 case result = converter.Convert(NcVM, null, null, null); Assert.AreEqual(true, result); // 3 case result = converter.Convert(RncVM, null, null, null); Assert.AreEqual(false, result); // 4 case result = converter.Convert(CncVM, null, null, null); Assert.AreEqual(false, result); }
public void PopulateMemberCollections(NodeCategoryViewModel element) { createMembers.Clear(); actionMembers.Clear(); queryMembers.Clear(); PrimaryHeaderItems.Clear(); ActionHeaderItems.Clear(); QueryHeaderItems.Clear(); foreach (var subElement in element.Entries) { switch (subElement.Model.Group) { case SearchElementGroup.Create: createMembers.Add(subElement); break; case SearchElementGroup.Action: actionMembers.Add(subElement); break; case SearchElementGroup.Query: queryMembers.Add(subElement); break; } } // Populate headers collections. string headerStripText = string.Empty; if (createMembers.Any()) { headerStripText = Configurations.HeaderCreate; } if (actionMembers.Any()) { // As soon as primary headers collection is defined, // add item to secondary headers collection. if (string.IsNullOrEmpty(headerStripText)) { headerStripText = Configurations.HeaderAction; } else { ActionHeaderItems.Add(new HeaderStripItem() { Text = Configurations.HeaderAction }); } } if (queryMembers.Any()) { // As soon as primary headers collection is defined, // add item to secondary headers collection. if (string.IsNullOrEmpty(headerStripText)) { headerStripText = Configurations.HeaderQuery; } else { QueryHeaderItems.Add(new HeaderStripItem() { Text = Configurations.HeaderQuery }); } } PrimaryHeaderItems.Add(new HeaderStripItem() { Text = headerStripText }); }
private void OnClassViewSelectionChanged(object sender, SelectionChangedEventArgs e) { var selectedIndex = (sender as ListView).SelectedIndex; // As focus moves within the class details, class button gets selected which // triggers a selection change. During a selection change the items in the // wrap panel gets reordered through "OrderListItems", but this is not always // necessary. Here we determine if the "translatedIndex" is the same as // "selectedClassProspectiveIndex", if so simply returns to avoid a repainting. var translatedIndex = TranslateSelectionIndex(selectedIndex); if (selectedClassProspectiveIndex == translatedIndex) return; selectedClassProspectiveIndex = translatedIndex; int classInfoIndex = GetClassInformationIndex(); // If user clicks on the same item when it is expanded, then 'OnClassButtonCollapse' // is invoked to deselect the item. This causes 'OnClassViewSelectionChanged' to be // called again, with 'SelectedIndex' set to '-1', indicating that no item is selected, // in which case we need to hide the ClassInformationView. if (selectedClassProspectiveIndex == -1) { if (classInfoIndex != -1) { (collection[classInfoIndex] as ClassInformationViewModel).ClassDetailsVisibility = false; currentClass = null; } OrderListItems(); return; } else { (collection[classInfoIndex] as ClassInformationViewModel).ClassDetailsVisibility = true; } currentClass = collection[selectedIndex] as NodeCategoryViewModel; OrderListItems(); // Selection change, we may need to reorder items. }
private bool ExpandCategory(IEnumerable<NodeCategoryViewModel> categories, NodeCategoryViewModel selectedClass) { bool foundSelectedClass = false; // Get all current expanded categories. var allExpandedCategories = categories.Where(cat => { return cat.IsExpanded && (!(cat is ClassesNodeCategoryViewModel)); }).ToList(); var categoryToBeExpanded = categories.FirstOrDefault(cat => cat == selectedClass); // If categoryToBeExpanded is null, that means the clicked item does not // represent a category button, but a class button. In the recursive call // the category in which this clicked class belong will be identified. if (categoryToBeExpanded != null) { categoryToBeExpanded.IsExpanded = !categoryToBeExpanded.IsExpanded; foundSelectedClass = true; } // Get expanded categories that should be collapsed. allExpandedCategories.Remove(categoryToBeExpanded); // Close all expanded categories except the one that contains the target // class button. If the clicked NodeCategoryViewModel represents a category // itself, then expand it and close out other sibling NodeCategoryViewModel. foreach (var expandedCategory in allExpandedCategories) { var searchFurtherInNextLevel = true; // If class button was clicked. if (selectedClass.IsClassButton) { var categoryClasses = expandedCategory.Items[0] as ClassesNodeCategoryViewModel; if (categoryClasses != null) // There are classes under this category... { if (expandedCategory.IsClassButton) { // If the category does not contain any sub category, // then we won't look for the selected class within it. expandedCategory.IsExpanded = false; searchFurtherInNextLevel = false; } else if (categoryClasses.Items.Contains(selectedClass)) { // If the category contains the selected class directly // within, then keep it expanded instead of collapsing it. expandedCategory.IsExpanded = true; // Found the selected class! Collapse all other sub categories. foreach (var ele in expandedCategory.SubCategories) ele.IsExpanded = false; searchFurtherInNextLevel = false; } } } if (searchFurtherInNextLevel) { var childCategories = expandedCategory.Items.OfType<NodeCategoryViewModel>(); expandedCategory.IsExpanded = ExpandCategory(childCategories, selectedClass); } // If the category remains expanded after this, we can // be sure that the selected class was found within it. foundSelectedClass = foundSelectedClass || expandedCategory.IsExpanded; } return foundSelectedClass; }
public ClassesNodeCategoryViewModel(NodeCategoryViewModel parent) : base(Configurations.ClassesDefaultName) { FullCategoryName = Configurations.ClassesDefaultName; Parent = parent; }
public void InsertSubCategory(NodeCategoryViewModel newSubCategory) { var list = SubCategories.Where(cat => !(cat is ClassesNodeCategoryViewModel)); var nextLargerItemIndex = FindInsertionPointByName(list, newSubCategory.Name); if (nextLargerItemIndex >= 0) { bool hasClasses = SubCategories.FirstOrDefault() is ClassesNodeCategoryViewModel; var offset = hasClasses ? 1 : 0; SubCategories.Insert(nextLargerItemIndex + offset, newSubCategory); } else SubCategories.Add(newSubCategory); }
private static NodeCategoryViewModel GetCategoryViewModel(NodeCategoryViewModel rootCategory, IEnumerable<string> categories) { var nameStack = new Stack<string>(categories.Reverse()); NodeCategoryViewModel target = rootCategory; NodeCategoryViewModel newTarget = null; bool isCheckedForClassesCategory = false; while (nameStack.Any()) { var currentCategory = nameStack.Pop(); newTarget = target.SubCategories.FirstOrDefault(c => c.Name == currentCategory); if (newTarget == null) { if (!isCheckedForClassesCategory && !target.IsClassButton && target.SubCategories[0] is ClassesNodeCategoryViewModel) { isCheckedForClassesCategory = true; nameStack.Push(currentCategory); nameStack.Push(Configurations.ClassesDefaultName); continue; } return null; } target = newTarget; } return target; }
public void ElementTypeToBoolConverterTest() { var converter = new ElementTypeToBoolConverter(); var NseVM = new NodeSearchElementViewModel( new NodeModelSearchElement(new TypeLoadData(typeof(Nodes.Symbol))), null); var NcVM = new NodeCategoryViewModel(""); var RncVM = new RootNodeCategoryViewModel(""); var CncVM = new ClassesNodeCategoryViewModel(RncVM); object result; //1. Element is null. //2. Element is NodeSearchElement. //3. Element is NodeCategoryViewModel. //4. Element is RootNodeCategoryViewModel. //5. Element is RootNodeCategoryViewModel with ClassesNodeCategoryViewModel. // 1 case result = converter.Convert(null, null, null, null); Assert.AreEqual(false, result); // 2 case result = converter.Convert(NseVM, null, null, null); Assert.AreEqual(false, result); // 3 case result = converter.Convert(NcVM, null, null, null); Assert.AreEqual(true, result); // 4 case result = converter.Convert(RncVM, null, null, null); Assert.AreEqual(true, result); // 5 case RncVM.SubCategories.Add(CncVM); result = converter.Convert(RncVM, null, null, null); Assert.AreEqual(false, result); }
private void AddEntryToExistingCategory(NodeCategoryViewModel category, NodeSearchElementViewModel entry) { category.RequestBitmapSource += SearchViewModelRequestBitmapSource; // Check if the category exists already. // ex : clockwork package. For clockwork // package the category names in dyf is different from what we show it // on the tree view. so when you click on the category to populate it // triggers an update to category name. on the same instance when you uninstall // and insall the clockwork package, the categories are named correctly but // every install triggers an update that gives a duplicate entry. so check if the // entry is already added (specific to browse). if (category.Entries.All(x => x.FullName != entry.FullName)) { category.Entries.Add(entry); } }
public void LibraryTreeItemsHostVisibilityConverterTest() { var converter = new LibraryTreeItemsHostVisibilityConverter(); var result = converter.Convert(null, null, null, null); Assert.AreEqual(Visibility.Visible, result); var NcVM = new NodeCategoryViewModel(""); result = converter.Convert(NcVM, null, null, null); Assert.AreEqual(Visibility.Visible, result); var RncVM = new ClassesNodeCategoryViewModel(NcVM); result = converter.Convert(RncVM, null, null, null); Assert.AreEqual(Visibility.Collapsed, result); }
private IEnumerable<RootNodeCategoryViewModel> CategorizeEntries(IEnumerable<NodeSearchElement> entries, bool expanded) { var tempRoot = entries.GroupByRecursive<NodeSearchElement, string, NodeCategoryViewModel>( element => element.Categories, (name, subs, es) => { var category = new NodeCategoryViewModel(name, es.OrderBy(en => en.Name).Select(MakeNodeSearchElementVM), subs); category.IsExpanded = expanded; category.RequestBitmapSource += SearchViewModelRequestBitmapSource; return category; }, ""); var result = tempRoot.SubCategories.Select( cat => new RootNodeCategoryViewModel(cat.Name, cat.Entries, cat.SubCategories) { IsExpanded = expanded }); tempRoot.Dispose(); return result.OrderBy(cat => cat.Name); }
internal SearchMemberGroup(string fullyQualifiedName, NodeCategoryViewModel category = null) { FullyQualifiedName = fullyQualifiedName; Category = category; members = new List<NodeSearchElementViewModel>(); }