/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.Windows.Forms; using JetBrains.Omea.OpenAPI; using JetBrains.Omea.ResourceTools; namespace JetBrains.Omea.Categories { /** * A set of static methods for working with resource categories. */ public class CategoryManager: ICategoryManager { private static int _propCategory; private static int _propCategoryExpanded; private static int _propCategoryExpandedInSelector; private static int _propShowContentsRecursively; private IResource _rootCategory; private IResourceTreeManager _resourceTreeManager; private IResourceStore _store; /** * Registers the resource and property types related to categories. */ public CategoryManager( IResourceStore store, IResourceTreeManager resourceTreeManager ) { _store = store; _resourceTreeManager = resourceTreeManager; _store.ResourceTypes.Register( "Category", "Category", "Name", ResourceTypeFlags.ResourceContainer | ResourceTypeFlags.Internal | ResourceTypeFlags.NoIndex ); _store.ResourceTypes.Register( "ResourceTreeRoot", "ResourceTreeRoot", "", ResourceTypeFlags.Internal | ResourceTypeFlags.NoIndex ); _propCategory = ResourceTypeHelper.UpdatePropTypeRegistration( "Category", PropDataType.Link, PropTypeFlags.CountUnread ); _propCategoryExpanded = _store.PropTypes.Register( "CategoryExpanded", PropDataType.Int, PropTypeFlags.Internal ); _propCategoryExpandedInSelector = _store.PropTypes.Register( "CategoryExpandedInSelector", PropDataType.Int, PropTypeFlags.Internal ); _propShowContentsRecursively = _store.PropTypes.Register( "ShowContentsRecursively", PropDataType.Bool, PropTypeFlags.Internal ); UpdateCategoryRoot(); if ( _store.ResourceTypes.Exist( "CategoryIntersection" ) ) { _store.GetAllResources( "CategoryIntersection" ).DeleteAll(); _store.ResourceTypes.Delete( "CategoryIntersection" ); } } public int PropCategory { get { return _propCategory; } } public int PropCategoryExpanded { get { return _propCategoryExpanded; } } public int PropCategoryExpandedInSelector { get { return _propCategoryExpandedInSelector; } } public int PropShowContentsRecursively { get { return _propShowContentsRecursively; } } public IResource RootCategory { get { return _rootCategory; } } /** * Creates the new category root resource (which is the standard root of * resource type Category) and, if necessary, deletes the old root (which * was a category marked with the IsRoot property). */ private void UpdateCategoryRoot() { _rootCategory = _resourceTreeManager.GetRootForType( "Category" ); _rootCategory.DisplayName = "Categories"; _rootCategory.SetProp( Core.Props.Open, 1 ); _resourceTreeManager.SetResourceNodeSort( _rootCategory, "Name" ); _resourceTreeManager.LinkToResourceRoot( _rootCategory, 20 ); IResourceList typedCategories = _store.FindResourcesWithProp("Category", Core.Props.ContentType ); foreach( IResource res in typedCategories ) { if ( res.GetLinkProp( "Parent" ) == _rootCategory ) { IResource rootForType = GetRootForTypedCategory(res.GetStringProp( Core.Props.ContentType )); res.SetProp( "Parent", rootForType ); } } } /** * Returns the root of categories with the specified content type. */ public IResource GetRootForTypedCategory( string resType ) { IResource root = _resourceTreeManager.GetRootForType( "Category:" + resType ); string displayName = _store.ResourceTypes [resType].DisplayName + " Categories"; if ( root.DisplayName != displayName ) { ResourceProxy proxy = new ResourceProxy( root ); proxy.BeginUpdate(); proxy.SetDisplayName( displayName ); proxy.SetProp( Core.Props.ContentType, resType ); proxy.SetProp( Core.Props.Open, 1 ); proxy.EndUpdate(); } _resourceTreeManager.SetResourceNodeSort( root, "Name" ); _resourceTreeManager.LinkToResourceRoot( root, 21 ); return root; } public IResource FindRootForTypedCategory( string resType ) { return Core.ResourceStore.FindUniqueResource( "ResourceTreeRoot", "RootResourceType", "Category:" + resType ); } /** * Returns a categories list to which the * resource belongs. */ public IResourceList GetResourceCategories( IResource resource ) { return resource.GetLinksOfType( "Category", _propCategory ); } /** * Adds a category link between a resource and a category and updates * the category intersections with all other categories to which the * resource belongs. */ public void AddResourceCategory( IResource res, IResource category ) { if ( res.HasLink( _propCategory, category ) ) return; new ResourceProxy( res ).AddLink( _propCategory, category ); } /** * Sets a category link between a resource and a category. The category * becomes the only one for the specified resource. */ public void SetResourceCategory( IResource res, IResource category ) { new ResourceProxy( res ).SetProp( _propCategory, category ); } /** * Removes a category link between a resource and a category and updates the category * intersections. */ public void RemoveResourceCategory( IResource res, IResource category ) { if ( !res.HasLink( _propCategory, category ) ) return; new ResourceProxy( res ).DeleteLink( _propCategory, category ); } /** * Checks if a category with the specified name already exists. */ public bool CategoryExists( IResource parent, string name ) { #region Preconditions if ( name == null ) throw new ArgumentNullException( "name" ); #endregion Preconditions IResourceList nameList = Core.ResourceStore.FindResources( "Category", Core.Props.Name, name ); if ( parent == null ) { parent = RootCategory; } IResourceList parentList = parent.GetLinksTo( null, Core.Props.Parent ); return nameList.Intersect( parentList, true ).Count > 0; } /** * Returns the list of resources that belong to all of the specified categories. */ public static IResourceList GetResourcesInCategories( ArrayList categories ) { IResourceList result = null; foreach( IResource category in categories ) { result = category.GetLinksOfTypeLive( null, _propCategory ).Intersect( result, true ); } return result; } /** * If there are any resources or subcategories connected to the category, * shows a confirmation dialog. */ public static bool ConfirmDeleteCategories( IWin32Window ownerWindow, IResourceList categoryList ) { int linkCount = 0, subCategoryCount = 0; foreach( IResource category in categoryList ) { IResourceList linked = category.GetLinksOfType( null, _propCategory ); linked = linked.Minus( Core.ResourceStore.FindResourcesWithProp( null, Core.Props.IsDeleted )); linkCount += linked.Count; subCategoryCount += category.GetLinksTo( null, Core.Props.Parent ).Count; } if ( linkCount > 0 || subCategoryCount > 0 ) { string msg = "There are "; if ( linkCount > 0 ) { msg += linkCount + " resources "; if ( subCategoryCount > 0 ) { msg += "and "; } } if ( subCategoryCount > 0 ) { msg += subCategoryCount + " subcategories "; } if ( categoryList.Count == 1 ) { msg += "in the category " + categoryList [0].DisplayName + ". Are you sure you wish to delete the category?"; } else { msg += "in the " + categoryList.Count + " selected categories" + ". Are you sure you wish to delete the categories?"; } DialogResult dr = MessageBox.Show( ownerWindow, msg, "Delete Category", MessageBoxButtons.YesNo ); if ( dr == DialogResult.No ) return false; } return true; } /** * Deletes a category and its subcategories. */ public static void DeleteCategory( IResource category ) { IResourceList subcategories = category.GetLinksTo( "Category", Core.Props.Parent ); category.Delete(); foreach( IResource subcategory in subcategories ) { DeleteCategory( subcategory ); } } public static void DeleteCategories( IResourceList categoryList ) { foreach( IResource category in categoryList ) { if ( category.Type == "Category" ) { DeleteCategory( category ); } } DeleteUnusedCategoryRoots(); } public IResource FindOrCreateCategory( IResource parentCategory, string name ) { if ( parentCategory == null ) parentCategory = RootCategory; IResource category = FindCategory( parentCategory, name ); if ( category != null ) { return category; } return CreateCategory(name, parentCategory, parentCategory.GetStringProp( Core.Props.ContentType )); } public IResource FindCategory( IResource parentCategory, string name ) { if ( parentCategory == null ) parentCategory = RootCategory; IResourceList categories = parentCategory.GetLinksTo( "Category", Core.Props.Parent ); foreach ( IResource category in categories ) { if ( String.Compare( category.GetStringProp( Core.Props.Name ), name, true ) == 0 ) { return category; } } return null; } /** * Creates a category with the specified name and parent. */ public static IResource CreateCategory( string name, IResource parent ) { return CreateCategory( name, parent, null ); } /** * Creates a category with the specified name, parent and content type. */ public static IResource CreateCategory( string name, IResource parent, string contentType ) { if ( parent == null ) throw new ArgumentNullException( "parent" ); if( parent.GetStringProp( Core.Props.ContentType ) != contentType ) { throw new ArgumentException( "Content type does not match parent content type" ); } IResource category = Core.ResourceStore.BeginNewResource( "Category" ); category.SetProp( Core.Props.Name, name ); category.SetProp( Core.Props.Parent, parent ); if ( contentType != null ) { category.SetProp( Core.Props.ContentType, contentType ); } category.EndUpdate(); return category; } /** * Checks if the new name of the category is valid, and if it is, renames * the category to the new name. */ public bool CheckRenameCategory( IWin32Window parentWindow, IResource category, string newName ) { if ( newName == null ) return false; if ( newName.Trim().Length == 0 ) { MessageBox.Show( parentWindow, "A category name may not be empty", "Rename Category", MessageBoxButtons.OK ); return false; } IResource existingCategory = FindCategory( category.GetLinkProp( Core.Props.Parent ), newName ); if ( existingCategory != null && existingCategory != category ) { MessageBox.Show( parentWindow, "A category named " + newName + " already exists", "Rename Category", MessageBoxButtons.OK ); return false; } new ResourceProxy( category ).SetProp( Core.Props.Name, newName ); return true; } /** * Returns the number of resources not matching the specified content type * in the specified category and its subcategories. */ public int GetUnmatchingResources( IResource category, string contentType ) { int result = 0; foreach( IResource res in category.GetLinksOfType( null, _propCategory ) ) { if ( res.Type != contentType ) result++; } foreach( IResource child in category.GetLinksTo( "Category", Core.Props.Parent ) ) { result += GetUnmatchingResources( child, contentType ); } return result; } /** * Sets the specified content type for a category and its subcategories. */ public void SetContentTypeRecursive( IResource res, string contentType ) { if ( contentType != null ) { new ResourceProxy( res ).SetProp( Core.Props.ContentType, contentType ); } else { new ResourceProxy( res ).DeleteProp( Core.Props.ContentType ); } foreach( IResource child in res.GetLinksTo( "Category", Core.Props.Parent ) ) { SetContentTypeRecursive( child, contentType ); } } /// /// Deletes resource-specific category roots which do not have any categories under them. /// public static void DeleteUnusedCategoryRoots() { foreach( IResource res in Core.ResourceStore.GetAllResources( "ResourceTreeRoot" ) ) { string contentType = res.GetStringProp( "RootResourceType" ); if ( contentType != null && contentType.StartsWith( "Category:" ) && res.GetLinksTo( null, Core.Props.Parent ).Count == 0 ) { res.Delete(); } } } } }