///
/// 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 JetBrains.DataStructures;
using JetBrains.JetListViewLibrary;
using JetBrains.Omea.OpenAPI;
using JetBrains.Omea.ResourceStore;
using JetBrains.Omea.ResourceTools;
namespace JetBrains.Omea.GUIControls
{
///
/// Fills ResourceListView2 with data from a tree of resources, given the root
/// resource and the ID of the parent property.
///
public class ResourceTreeDataProvider: IResourceDataProvider
{
#region ResourceTreeDataNode Class — A class that's created for each tree node and listens for its changes
///
/// A class that's created for each tree node and listens for its changes.
///
private class ResourceTreeDataNode: IDisposable, IResourceListListener
{
private ResourceTreeDataProvider _owner;
private JetListView _listView;
private IResource _parentResource;
private int _parentProp;
private IResourceList _childResources;
private bool _handlersAttached = false;
///
/// Creates a node displaying the children of the specified resource.
///
/// The data provider to which the node belongs.
/// The list view displaying the nodes.
/// The node whose children are displayed.
/// The ID of the link property between the node and its children,
/// or 0 if a custom list of children is used.
/// The list of child resources.
public ResourceTreeDataNode( ResourceTreeDataProvider owner, JetListView listView,
IResource parentResource, int parentProp, IResourceList childResources )
{
_listView = listView;
_owner = owner;
_parentResource = parentResource;
_parentProp = parentProp;
_childResources = childResources;
}
internal void AttachHandlers()
{
if ( !_handlersAttached )
{
_handlersAttached = true;
if ( _parentProp != 0 )
{
Core.ResourceTreeManager.RegisterTreeListener( _parentResource, _parentProp, this );
_childResources.Dispose();
}
else
{
_childResources.ResourceAdded += new ResourceIndexEventHandler( HandleResourceAdded );
_childResources.ResourceChanged += new ResourcePropIndexEventHandler( HandleResourceChanged );
_childResources.ResourceDeleting += new ResourceIndexEventHandler( HandleResourceDeleting );
}
}
}
public void Dispose()
{
if ( _handlersAttached )
{
if ( _parentProp != 0 )
{
Core.ResourceTreeManager.UnregisterTreeListener( _parentResource, _parentProp, this );
}
else
{
_childResources.ResourceAdded -= new ResourceIndexEventHandler( HandleResourceAdded );
_childResources.ResourceChanged -= new ResourcePropIndexEventHandler( HandleResourceChanged ) ;
_childResources.ResourceDeleting -= new ResourceIndexEventHandler( HandleResourceDeleting );
}
}
}
public void ResourceAdded( IResource res )
{
_owner.AddResource( _parentResource, res );
}
public void ResourceDeleting( IResource res )
{
_owner.RemoveResource( _parentResource, res );
}
public void ResourceChanged( IResource res, IPropertyChangeSet cs )
{
Core.UIManager.QueueUIJob( new ResourceDelegate( DoUpdateResource ), res );
if(cs.IsPropertyChanged( Core.Props.UserResourceOrder ))
Core.UserInterfaceAP.QueueJob( "Rearrange Children", new RearrangeChildrenDelegate(_owner.RearrangeChildren), res );
}
private void HandleResourceAdded( object sender, ResourceIndexEventArgs e )
{
ResourceAdded( e.Resource );
}
private void HandleResourceChanged( object sender, ResourcePropIndexEventArgs e )
{
ResourceChanged( e.Resource, e.ChangeSet );
}
private void DoUpdateResource( IResource res )
{
if ( Core.State != CoreState.ShuttingDown )
{
// the resource change is queued asynchronously, so the item may
// have been removed from the list
_listView.UpdateItemSafe( res );
}
}
private void HandleResourceDeleting( object sender, ResourceIndexEventArgs e )
{
ResourceDeleting( e.Resource );
}
internal IResourceList ChildResources
{
get { return _childResources; }
}
}
#endregion
private IResource _rootResource;
private int _parentProp;
private JetListView _listView;
///
/// Maps Resource IDs of tree non-leaf nodes to the instances.
///
private IntHashTable _dataNodes = new IntHashTable();
private IJetResourceChildProvider _resourceChildProvider;
///
/// A resource list that consists of a single root resource and provides for listening to the changes in it, particularily in the user-sort-order property.
///
private IResourceList _listRoot = null;
public ResourceTreeDataProvider()
{
}
public ResourceTreeDataProvider( IResource rootResource, int parentProp )
{
if ( rootResource == null )
throw new ArgumentNullException( "rootResource" );
if ( Core.ResourceStore.PropTypes [parentProp].DataType != PropDataType.Link )
throw new ArgumentException( "parentProp is not a link property", "parentProp" );
SetRootResourceImpl( rootResource);
_parentProp = parentProp;
}
///
/// Assigns a new root resource, and also catches up with the resource list listening to its changes.
///
protected void SetRootResourceImpl( IResource value)
{
if(value == _rootResource)
return; // No change
if(value == null)
throw new ArgumentNullException("value");
if(_listRoot != null)
{
_listRoot.ResourceChanged -= new ResourcePropIndexEventHandler(OnRootResourceChanged);
_listRoot = null;
}
_rootResource = value;
_listRoot = value.ToResourceListLive();
_listRoot.ResourceChanged += new ResourcePropIndexEventHandler(OnRootResourceChanged);
}
private void OnRootResourceChanged( object sender, ResourcePropIndexEventArgs e )
{
// If the sorting-order has changed, reapply the sorting
if(e.ChangeSet.IsPropertyChanged( Core.Props.UserResourceOrder ))
Core.UserInterfaceAP.QueueJob( "Rearrange Children", new RearrangeChildrenDelegate(RearrangeChildren), e.Resource );
}
public IJetResourceChildProvider ResourceChildProvider
{
get { return _resourceChildProvider; }
set { _resourceChildProvider = value; }
}
public int ParentProperty
{
get { return _parentProp; }
}
public void FillResources( ResourceListView2 listView )
{
_listView = listView.JetListView;
_listView.ChildrenRequested += new RequestChildrenEventHandler( HandleChildrenRequested );
// Set the proper root
listView.RootResource = _rootResource;
ExpandResource( _listView.Root, _rootResource );
}
///
/// For an existing tree node (incl. the originaly-present root), sets up the sorting for that node,
/// and expands it by adding all the "child" resources as child nodes.
///
private void ExpandResource( JetListViewNode parentNode, IResource parentResource )
{
//////////////////
// Apply sorting
UserOrderSortSettings settings = null;
// Get the per-property sort settings as a string (written to each node)
string nodeSort = Core.ResourceTreeManager.GetResourceNodeSort( parentResource );
if(nodeSort != null) // Apply, if available
settings = UserOrderSortSettings.Parse( Core.ResourceStore, nodeSort );
else
settings = new UserOrderSortSettings();
// Apply the user-order sorting (it's writter as a property on the parent with an ordered list of child resources)
settings.SetUserOrder( parentResource );
// Submit the sorting order to the node
_listView.NodeCollection.SetItemComparer(
parentNode.Data
, new ResourceComparer( null, new UserOrderResourceComparer( settings ), true ) );
/////////////////
// Get Children
ResourceTreeDataNode dataNode;
lock( _dataNodes )
{
dataNode = (ResourceTreeDataNode) _dataNodes [parentResource.Id];
if ( dataNode == null )
{
IResourceList childResources = null;
int parentProp = _parentProp;
if ( _resourceChildProvider != null )
{
childResources = _resourceChildProvider.GetChildResources( parentResource );
}
if ( childResources == null )
{
childResources = parentResource.GetLinksToLive( null, _parentProp );
}
else
{
parentProp = 0;
}
dataNode = new ResourceTreeDataNode( this, _listView, parentResource, parentProp, childResources );
_dataNodes [parentResource.Id] = dataNode;
}
}
////////////////////
// Submit Children
lock( dataNode.ChildResources )
{
// Add 'em!
foreach( IResource res in dataNode.ChildResources.ValidResources )
{
AddNode( parentNode, res ); // This also calls an ExpandResource for that node
}
// Listen for changes to the children resources of the node (not to the node resource itself)
dataNode.AttachHandlers();
}
}
private void AddNode( JetListViewNode parentNode, IResource child )
{
JetListViewNode node = parentNode.Nodes.Add( child );
if ( IsResourceContainer( child ) )
{
if ( child.GetLinkCount( -_parentProp ) > 0 )
{
node.HasChildren = true;
}
else
{
// we have no children now, but may get some later
ExpandResource( node, child );
}
}
}
private void AddResource( IResource parentResource, IResource resource )
{
JetListViewNode[] nodes = _listView.NodeCollection.NodesFromItem( parentResource );
for( int i=0; i
/// Checks if the specified resource is a resource container.
///
private bool IsResourceContainer( IResource child )
{
return Core.ResourceStore.ResourceTypes [child.Type].HasFlag( ResourceTypeFlags.ResourceContainer );
}
private void HandleChildrenRequested( object sender, RequestChildrenEventArgs e )
{
IResource res = (IResource) e.Node.Data;
if ( e.Node.Nodes.Count == 0 )
{
ExpandResource( e.Node, res );
if ( e.Node.Nodes.Count == 0 )
{
e.Node.HasChildren = false;
}
}
}
public void Dispose()
{
DisposeDataNodes();
if(_listRoot != null)
{
_listRoot.ResourceChanged -= new ResourcePropIndexEventHandler(OnRootResourceChanged);
_listRoot = null;
}
}
private void DisposeDataNodes()
{
lock( _dataNodes )
{
foreach( IntHashTable.Entry e in _dataNodes )
{
ResourceTreeDataNode node = (ResourceTreeDataNode) e.Value;
node.Dispose();
}
_dataNodes.Clear();
}
}
public JetListView ListView
{
get { return _listView; }
}
public bool SelectResource( IResource res )
{
if ( res == null )
{
return true;
}
JetListViewNode node = _listView.NodeCollection.NodeFromItem( res );
if ( node == null )
{
if ( !FindResourceNode( res ) )
{
return false;
}
node = _listView.NodeCollection.NodeFromItem( res );
}
if ( node != null && !node.FiltersAccept )
{
return false;
}
if ( _listView.Selection.Count == 1 && _listView.Selection [0] == res )
{
return true;
}
_listView.Selection.SelectSingleItem( res );
return true;
}
public bool FindResourceNode( IResource res )
{
ArrayList parentStack = new ArrayList();
IResource parent = res;
do
{
parentStack.Insert( 0, parent );
parent = parent.GetLinkProp( _parentProp );
if ( parent == null )
return false;
} while( parent != _rootResource );
JetListViewNode node = null;
foreach( IResource parentRes in parentStack )
{
node = _listView.NodeCollection.NodeFromItem( node, parentRes );
if ( node == null )
break;
if ( parentRes == res )
{
return true;
}
node.Expanded = true;
}
return false;
}
public void RebuildTree()
{
if ( _listView != null )
{
DisposeDataNodes();
_listView.Nodes.Clear();
ExpandResource( _listView.Root, _rootResource );
}
}
public void SetRootResource( IResource resource, int parentProperty )
{
if ( _rootResource != resource || _parentProp != parentProperty )
{
SetRootResourceImpl(resource);
_parentProp = parentProperty;
if ( _listView != null )
{
RebuildTree();
}
}
}
///
/// When the user-resource-order property changes on a node, reapplies the sorting.
///
protected void RearrangeChildren(IResource res)
{
_listView.NodeCollection.Sort();
}
///
/// A delegate type for the function.
///
protected delegate void RearrangeChildrenDelegate(IResource res);
}
public interface IJetResourceChildProvider
{
IResourceList GetChildResources( IResource parent );
}
}