///
/// 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.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using JetBrains.JetListViewLibrary;
using JetBrains.Omea.Containers;
using JetBrains.Omea.OpenAPI;
using JetBrains.Omea.ResourceTools;
namespace JetBrains.Omea.GUIControls
{
///
/// The almighty ResourceListView 2.0.
///
public class ResourceListView2 : UserControl, IContextProvider, ICommandProcessor
{
protected JetListView _jetListView = new JetListView();
protected bool _executeDoubleClickAction = true;
protected bool _allowDrag = true;
protected IContextProvider _contextProvider;
protected IResourceDataProvider _dataProvider;
protected ExpandedPropManager _expandedPropManager;
protected InPlaceTextEditor _inPlaceEditor;
protected IResourceDragDropHandler _rootResourceDropHandler;
protected bool _selectAddedItems = false;
protected IResource _emptyDropResource;
protected Timer _keyNavigationTimer;
protected bool _keyNavigation;
private ItemFormatCache _itemFormatCache;
protected ContextMenu _headerContextMenu;
protected bool _allowSameViewDrag = true;
protected bool _showContextMenu = true;
protected bool _initialFill = false;
protected bool _inResourceOperation = false;
public delegate bool LocateMatchCallback( IResource res );
///
/// The root resource.
/// In case of a tree, its semantic is evident.
/// If we have a plain list, then it's a "virtual" resource that handles drag-drops to the empty space and holds the items ordering.
///
protected IResource _resRoot = null;
public ResourceListView2()
{
_jetListView.Dock = DockStyle.Fill;
_jetListView.Font = new Font( "Tahoma", 8 );
_jetListView.ControlPainter = new GdiControlPainter();
_jetListView.BackColor = SystemColors.Window;
_jetListView.AllowDrop = true;
_jetListView.DoubleClick += HandleDoubleClick;
_jetListView.ItemDrag += HandleItemDrag;
_jetListView.DragOver += HandleDragOver;
_jetListView.DragDrop += HandleDragDrop;
_jetListView.KeyDown += HandleKeyDown;
_jetListView.KeyUp += HandleKeyUp;
_jetListView.ContextMenuInvoked += HandleContextMenuInvoked;
_jetListView.ItemUpdated += HandleItemUpdated;
_jetListView.ActiveNodeChanged += HandleActiveNodeChanged;
_jetListView.SelectionStateChanged += HandleSelectionStateChanged;
_jetListView.NodeCollection.NodeAdded += HandleNodeAdded;
_jetListView.ControlMethodInvoker = new OmeaMethodInvoker();
Controls.Add( _jetListView );
_inPlaceEditor = new InPlaceTextEditor();
_jetListView.InPlaceEditor = _inPlaceEditor;
_inPlaceEditor.BeforeItemEdit += HandleBeforeItemEdit;
_inPlaceEditor.AfterItemEdit += HandleAfterItemEdit;
_keyNavigationTimer = new Timer();
_keyNavigationTimer.Interval = 200;
_keyNavigationTimer.Tick += HandleKeyNavigationTimer;
_itemFormatCache = new ItemFormatCache();
if( Core.State != CoreState.Initializing )
{
HookFormattingRulesChange();
}
Core.ResourceAP.JobStarting += HandleResourceJobStarting;
Core.ResourceAP.JobFinished += HandleResourceJobFinished;
_contextProvider = this;
}
protected override void Dispose( bool disposing )
{
if( _dataProvider != null )
{
_dataProvider.Dispose();
_dataProvider = null;
}
if( disposing )
{
_keyNavigationTimer.Dispose();
}
base.Dispose( disposing );
}
public event ResourceDragEventHandler ResourceDragOver;
public event ResourceDragEventHandler ResourceDrop;
public event EventHandler KeyNavigationCompleted;
new public event JetListViewLibrary.HandledEventHandler DoubleClick;
public event EventHandler ActiveResourceChanged;
///
/// Occurs when a new resource appears in the list.
///
public event ResourceEventHandler ResourceAdded;
///
/// Occurs when the list of selected resources in the resource browser is changed.
///
public event EventHandler SelectionChanged;
///
/// Occurs when the user drags the column splitter to change the size of the column.
///
public event EventHandler ColumnSizeChanged
{
add { _jetListView.ColumnResized += value; }
remove { _jetListView.ColumnResized -= value; }
}
///
/// Occurs when the user drags the column header to change the order of the columns.
///
public event EventHandler ColumnOrderChanged
{
add { _jetListView.ColumnOrderChanged += value; }
remove { _jetListView.ColumnOrderChanged -= value; }
}
///
/// Occurs when the user starts in-place editing an item.
///
public event ResourceItemEditEventHandler BeforeItemEdit;
///
/// Occurs when the item is edited by the user.
///
public event ResourceItemEditEventHandler AfterItemEdit;
public JetListView JetListView
{
get { return _jetListView; }
}
///
/// Enables or disables executing the double-click action when an item is double-clicked
/// or Enter is pressed on an item.
///
[DefaultValue( true )]
public bool ExecuteDoubleClickAction
{
get { return _executeDoubleClickAction; }
set { _executeDoubleClickAction = value; }
}
///
/// The provider which provides the context for keyboard and context menu actions
/// invoked from the ResourceListView2.
///
[DefaultValue( null )]
public IContextProvider ContextProvider
{
get { return _contextProvider; }
set
{
if( value == null )
{
_contextProvider = this;
}
else
{
_contextProvider = value;
}
foreach( JetListViewColumn col in _jetListView.Columns )
{
ResourceListViewCustomColumn customColumn = col as ResourceListViewCustomColumn;
if( customColumn != null )
{
customColumn.ContextProvider = _contextProvider;
}
}
}
}
///
/// The which handles drag & drop on the empty space in the list.
///
[DefaultValue( null )]
public IResourceDragDropHandler EmptyDropHandler
{
get { return _rootResourceDropHandler; }
set { _rootResourceDropHandler = value; }
}
///
/// Gets or sets the IResourceDataProvider which provides resources to show in the
/// ResourceListView.
///
[Browsable( false )]
[DefaultValue( null )]
public IResourceDataProvider DataProvider
{
get { return _dataProvider; }
set
{
if( _dataProvider != value )
{
if( _dataProvider != null )
{
_dataProvider.Dispose();
}
_dataProvider = value;
_itemFormatCache.Clear();
_jetListView.NodeCollection.BeginUpdate();
try
{
_jetListView.Nodes.Clear();
if( _dataProvider != null )
{
_initialFill = true;
try
{
_dataProvider.FillResources( this );
}
finally
{
_initialFill = false;
}
}
}
finally
{
_jetListView.NodeCollection.EndUpdate();
}
}
}
}
///
/// Gets or sets the column scheme provider for displaying list view contents
/// in multiline mode.
///
[DefaultValue( null )]
public IColumnSchemeProvider ColumnSchemeProvider
{
get { return _jetListView.ColumnSchemeProvider; }
set { _jetListView.ColumnSchemeProvider = value; }
}
///
/// Gets or sets the value indicating whether the list view contents is drawn in
/// multiline mode.
///
[DefaultValue( false )]
public bool MultiLineView
{
get { return _jetListView.MultiLineView; }
set { _jetListView.MultiLineView = value; }
}
///
/// Gets or sets the ID of the int property which stores the expanded state of folders in the view.
///
[Browsable( false )]
[DefaultValue( -1 )]
public int OpenProperty
{
get
{
if( _expandedPropManager != null )
{
return _expandedPropManager.PropId;
}
return -1;
}
set
{
if( _expandedPropManager != null )
{
_expandedPropManager.Dispose();
}
if( value == -1 )
{
_expandedPropManager = null;
}
else
{
_expandedPropManager = new ExpandedPropManager( _jetListView, value );
}
}
}
[DefaultValue( false )]
public bool HideSelection
{
get { return _jetListView.HideSelection; }
set { _jetListView.HideSelection = value; }
}
///
/// Gets or sets a value indicating whether the selection highlight covers all
/// columns in the list or just the first column.
///
public bool FullRowSelect
{
get { return _jetListView.FullRowSelect; }
set { _jetListView.FullRowSelect = value; }
}
[DefaultValue( false )]
public bool SelectAddedItems
{
get { return _selectAddedItems; }
set { _selectAddedItems = value; }
}
public ColumnHeaderStyle HeaderStyle
{
get { return _jetListView.HeaderStyle; }
set { _jetListView.HeaderStyle = value; }
}
///
/// Gets or sets a value indicating whether the user can drag column headers
/// to reorder columns in the control.
///
public bool AllowColumnReorder
{
get { return _jetListView.AllowColumnReorder; }
set { _jetListView.AllowColumnReorder = value; }
}
///
/// Gets or sets a value indicating whether the user can drag items in the view.
///
[DefaultValue( true )]
public bool AllowDrag
{
get { return _allowDrag; }
set { _allowDrag = value; }
}
public bool KeyNavigation
{
get { return _keyNavigation; }
}
public JetListViewFilterCollection Filters
{
get { return _jetListView.Filters; }
}
public SelectionModel Selection
{
get { return _jetListView.Selection; }
}
///
/// Gets or sets the context menu which is shown when a column header is right-clicked.
///
[DefaultValue( null )]
public ContextMenu HeaderContextMenu
{
get { return _headerContextMenu; }
set { _headerContextMenu = value; }
}
///
/// Gets or sets a value indicating whether it's allowed to perform drag & drop from a view
/// to the same view.
///
[DefaultValue( true )]
public bool AllowSameViewDrag
{
get { return _allowSameViewDrag; }
set { _allowSameViewDrag = value; }
}
///
/// Gets or sets the value indicating whether multiple items can be selected in the list.
///
[DefaultValue( true )]
public bool MultiSelect
{
get { return _jetListView.MultiSelect; }
set { _jetListView.MultiSelect = value; }
}
///
/// Gets or sets the value indicating whether delimiter lines are drawn between
/// lines in the list.
///
[DefaultValue( false )]
public bool RowDelimiters
{
get { return _jetListView.RowDelimiters; }
set { _jetListView.RowDelimiters = value; }
}
///
/// Gets or sets the value indicating whether a context menu is displayed when
/// a resource is right-clicked in the list.
///
[DefaultValue( true )]
public bool ShowContextMenu
{
get { return _showContextMenu; }
set { _showContextMenu = value; }
}
public BorderStyle BorderStyle
{
get { return _jetListView.BorderStyle; }
set { _jetListView.BorderStyle = value; }
}
///
/// The color used for drawing the JetListView border when BorderStyle.FixedSingle is used.
///
public Color BorderColor
{
get { return _jetListView.BorderColor; }
set { _jetListView.BorderColor = value; }
}
///
/// Gets or sets the background color of unselected group headers in the view.
///
public Color GroupHeaderColor
{
get { return _jetListView.GroupHeaderColor; }
set { _jetListView.GroupHeaderColor = value; }
}
///
/// The text shown in the view when there are no visible items.
///
[DefaultValue( "There are no items in this view." )]
[Category( "Appearance" )]
public string EmptyText
{
get { return _jetListView.EmptyText; }
set { _jetListView.EmptyText = value; }
}
///
/// Gets or sets whether resources can be dropped in Insert mode to allow reordering them, when dragging within the same view.
///
/// Same view drag must be allowed by setting to True in order for this function to work.
[DefaultValue( true )]
[Category( "Behavior" )]
public bool AllowReorder
{
get { return _jetListView.AllowDragInsert; }
set { _jetListView.AllowDragInsert = value; }
}
public ResourceIconColumn AddIconColumn()
{
ResourceIconColumn column = new ResourceIconColumn();
_jetListView.Columns.Add( column );
return column;
}
public CheckBoxColumn AddCheckBoxColumn()
{
CheckBoxColumn column = new CheckBoxColumn();
column.Width = 20;
_jetListView.Columns.Add( column );
return column;
}
public TreeStructureColumn AddTreeStructureColumn()
{
TreeStructureColumn column = new TreeStructureColumn();
_jetListView.Columns.Add( column );
return column;
}
public ResourceListView2Column AddColumn( int propId )
{
return AddColumn( new int[] {propId} );
}
public ResourceListView2Column AddColumn( int[] propIds )
{
ResourceListView2Column column = new ResourceListView2Column( propIds );
column.FontCallback = _itemFormatCache.GetItemFont;
column.ForeColorCallback = _itemFormatCache.GetItemForeColor;
column.BackColorCallback = _itemFormatCache.GetItemBackColor;
_jetListView.Columns.Add( column );
return column;
}
public ResourceListViewCustomColumn AddCustomColumn( int[] propIds, ICustomColumn[] customColumns )
{
ResourceListViewCustomColumn column = new ResourceListViewCustomColumn( propIds, customColumns );
column.Width = 20;
column.ContextProvider = _contextProvider;
column.BackColorCallback = _itemFormatCache.GetItemBackColor;
_jetListView.Columns.Add( column );
return column;
}
private void HandleDoubleClick(object sender, JetListViewLibrary.HandledEventArgs e)
{
if( DoubleClick != null )
{
DoubleClick( this, e );
}
if( !e.Handled && Core.State == CoreState.Running && _executeDoubleClickAction )
{
IResourceList resList = GetSelectedResources();
if( resList.Count > 0 )
{
bool haveActions = false;
for( int i = 0; i < resList.Count; i++ )
{
IResource res;
try
{
res = resList[ i ];
}
catch( StorageException )
{
continue;
}
if( Core.ActionManager.GetDoubleClickAction( res ) != null )
{
haveActions = true;
break;
}
}
if( haveActions )
{
Core.ActionManager.ExecuteDoubleClickAction( resList );
e.Handled = true;
}
}
}
}
private void HandleItemDrag( object sender, ItemDragEventArgs e )
{
if( !_allowDrag )
{
return;
}
DataObject dataObj = new DataObject();
IResourceList selectedResources = GetSelectedResources();
dataObj.SetData( typeof(IResourceList), selectedResources );
dataObj.SetData( typeof(ResourceListView2), this );
string[] dragResTypes = selectedResources.GetAllTypes();
if( dragResTypes.Length == 1 )
{
IResourceDragDropHandler handler = Core.PluginLoader.GetResourceDragDropHandler( selectedResources[ 0 ] );
if( handler != null )
{
handler.AddResourceDragData( selectedResources, dataObj );
}
}
DoDragDrop( dataObj, DragDropEffects.All | DragDropEffects.Move | DragDropEffects.Link );
}
///
/// Handles the DnD event from the underlying , and fires the own event for the external handlers (if none available, invokes the default handler).
///
private void HandleDragOver( object sender, JetListViewDragEventArgs args )
{
try
{
IResource targetRes;
if( !CheckDragDropMode( args, out targetRes ) )
return; // Operation cancelled
// Call the outer handler for the drag-over operation
if( (ResourceDragOver != null) && (args.Data.GetDataPresent( typeof(IResourceList) )) )
{ // Call the custom handler
IResourceList dragResources = (IResourceList)args.Data.GetData( typeof(IResourceList) );
ResourceDragEventArgs resourceDragEventArgs = new ResourceDragEventArgs( targetRes, dragResources );
ResourceDragOver( this, resourceDragEventArgs );
args.Effect = resourceDragEventArgs.Effect;
}
else
{ // No custom handler defined, invoke the default handler
if( (targetRes == RootResource) && (_rootResourceDropHandler != null) ) // Empty/Root special handler
args.Effect = _rootResourceDropHandler.DragOver( targetRes, args.Data, args.AllowedEffect, args.KeyState );
else // Normal d'n'd handler
args.Effect = Core.UIManager.ProcessDragOver( targetRes, args.Data, args.AllowedEffect, args.KeyState, (args.Data.GetData( typeof(ResourceListView2) ) == this) );
}
}
catch( Exception ex )
{
Core.ReportException( ex, ExceptionReportFlags.AttachLog );
}
}
///
/// Handles the DnD event from the underlying , and fires the own event for the external handlers (if none available, invokes the default handler).
///
private void HandleDragDrop( object sender, JetListViewDragEventArgs args )
{
try
{
////////////
// Prepare
JetListViewNode nodeInsertionPoint = args.DropTargetNode; // In tree-insertion-mode, we drop to the parent, and here we store the original target to use as an insertion point
IResource targetRes;
if( !CheckDragDropMode( args, out targetRes ) )
return; // Operation cancelled
// The resources being dragged
IResourceList resDragged = args.Data.GetDataPresent( typeof(IResourceList) ) ? (IResourceList)args.Data.GetData( typeof(IResourceList) ) : Core.ResourceStore.EmptyResourceList;
lock( resDragged ) // Kill the resource list to avoid the changes
resDragged = Core.ResourceStore.ListFromIds( resDragged.ResourceIds, false );
////////////
// Reorder
// If we were dragging in the insertion mode, change the order
// Do it beforehand to avoid “jumping” the item when it first gets added to the end and then jumps to the proper place
if( ((args.DropTargetRenderMode & DropTargetRenderMode.InsertAny) != 0) && (nodeInsertionPoint != null) )
{
if( nodeInsertionPoint.Parent != null )
{
// Collect IDs of the current set of resources under the node (they may be useful if there's no existing order yet)
// TODO: lock?
IntArrayList arOld = new IntArrayList( nodeInsertionPoint.Parent.Nodes.Count );
foreach( JetListViewNode node in nodeInsertionPoint.Parent.Nodes )
arOld.Add( ((IResource)node.Data).OriginalId );
UserResourceOrder uro = new UserResourceOrder(
nodeInsertionPoint.Parent.Data != null ? (IResource)nodeInsertionPoint.Parent.Data : _resRoot
, JobPriority.Immediate );
uro.Insert(
((IResource)nodeInsertionPoint.Data).OriginalId
, resDragged.ResourceIds
, ((args.DropTargetRenderMode & DropTargetRenderMode.InsertBelow) != 0)
, arOld );
}
}
/////////
// Drop
// Invoke the external handler for the drop event
if( (ResourceDrop != null) && (resDragged.Count > 0) ) // A custom handler is available
ResourceDrop( this, new ResourceDragEventArgs( targetRes, resDragged ) );
else
{ // No custom handler, invoke the default one
if( (targetRes == RootResource) && (_rootResourceDropHandler != null) ) // Empty/Root special handler
_rootResourceDropHandler.Drop( targetRes, args.Data, args.AllowedEffect, args.KeyState );
else // Normal d'n'd handler
Core.UIManager.ProcessDragDrop( targetRes, args.Data, args.AllowedEffect, args.KeyState );
}
}
catch( Exception ex )
{
Core.ReportException( ex, ExceptionReportFlags.AttachLog );
}
}
///
/// Checks if the dragging mode could be switched to Insertion or not, and changes the droptarget to the parent node, if necessary, for the Insertion case.
///
///
/// The original drag'n'drop event arguents coming from the list.
///
/// Returns the resource that is either dropped over,
/// or a parent resource in case of the Insertion mode.
/// Null for the top-level nodes when the root resource is not defined.
/// Whether the drag-drop operation is allowed (True) or not.
protected bool CheckDragDropMode( JetListViewDragEventArgs args, out IResource resDropTarget )
{
// Dragging within the same control?
bool sameView = args.Data.GetData( typeof(ResourceListView2) ) == this;
// Prohibit dragging items within the same control
if( (!AllowSameViewDrag) && (sameView) )
{
args.DropTargetRenderMode = DropTargetRenderMode.Restricted;
args.Effect = DragDropEffects.None;
resDropTarget = null;
return false; // Prohibit
}
// Assume the target won't change
JetListViewNode nodeDropTarget = args.DropTargetNode;
// Check for special processing for the Insertion mode
if( args.DropTargetNode != null )
{ // Over some item
if( sameView )
{ // Same view drag, check for possible insertion
if( (args.DropTargetRenderMode & DropTargetRenderMode.InsertAny) != 0 ) // If it's set to Insert by the list, then reordering is allowed
{ // In the insertion mode, the drop action applies to the parent of the node over which the cursor is located
if( args.DropTargetNode.Parent != null ) // Reordering in the tree — switch to parent
nodeDropTarget = args.DropTargetNode.Parent;
else // Dragging not in a tree but in a plain list; such scenario is not yet supported
args.DropTargetRenderMode = DropTargetRenderMode.Over;
}
}
else // Not same view, no insertion allowed
args.DropTargetRenderMode = DropTargetRenderMode.Over;
}
else // Over empty space
args.DropTargetRenderMode = DropTargetRenderMode.Over;
// Get the target resource and ensure it's OK
object dataDropTarget = ((nodeDropTarget == null) || (nodeDropTarget.Data == null)) ? _resRoot : nodeDropTarget.Data;
resDropTarget = dataDropTarget as IResource;
if( (resDropTarget == null) != (dataDropTarget == null) ) // If the data type is not IResource
throw new InvalidOperationException( String.Format( "Invalid data type attached to a resource list/tree view node." ) );
// If the drop is over the empty space, and the empty-drop-resource (Root) has not been defined
if( resDropTarget == null )
{
args.DropTargetRenderMode = DropTargetRenderMode.Restricted;
args.Effect = DragDropEffects.None;
resDropTarget = null;
return false; // Prohibit
}
return true; // Allow
}
private void HandleContextMenuInvoked( object sender, MouseEventArgs e )
{
if( !IsHandleCreated || !Visible )
{
return;
}
if( _jetListView.IsColumnHeaderAt( e.X, e.Y ) )
{
if( _headerContextMenu != null )
{
_headerContextMenu.Show( this, new Point( e.X, e.Y ) );
}
}
else if( _showContextMenu )
{
IActionContext actionContext = _contextProvider.GetContext( ActionContextKind.ContextMenu );
if( _jetListView.GetNodeAt( e.X, e.Y ) == null )
{
ActionContext context = actionContext as ActionContext;
if( context != null )
{
context.SetSelectedResources( Core.ResourceStore.EmptyResourceList );
}
}
Core.ActionManager.ShowResourceContextMenu( actionContext,
this, e.X, e.Y );
}
}
private void HandleKeyDown( object sender, KeyEventArgs e )
{
if( Core.State == CoreState.ShuttingDown )
return;
OnKeyDown( e );
if( e.KeyCode == Keys.Up || e.KeyCode == Keys.Down ||
e.KeyCode == Keys.PageUp || e.KeyCode == Keys.PageDown ||
e.KeyCode == Keys.Home || e.KeyCode == Keys.End )
{
_keyNavigation = true;
_keyNavigationTimer.Stop();
}
if( !e.Handled )
{
IActionContext context = _contextProvider.GetContext( ActionContextKind.Keyboard );
if( Core.ActionManager.ExecuteKeyboardAction( context, e.KeyData ) )
{
e.Handled = true;
}
else if( e.KeyCode == Keys.Enter && e.Modifiers == 0 && _executeDoubleClickAction )
{
Core.ActionManager.ExecuteDoubleClickAction( context.SelectedResources );
}
}
}
private void HandleKeyUp( object sender, KeyEventArgs e )
{
if( _keyNavigation )
{
_keyNavigation = false;
_keyNavigationTimer.Stop();
_keyNavigationTimer.Start();
}
}
private void HandleKeyNavigationTimer( object sender, EventArgs e )
{
_keyNavigationTimer.Stop();
if( KeyNavigationCompleted != null )
{
KeyNavigationCompleted( this, EventArgs.Empty );
}
}
public IActionContext GetContext( ActionContextKind kind )
{
if( _contextProvider != null && _contextProvider != this )
{
return _contextProvider.GetContext( kind );
}
ActionContext context = new ActionContext( kind, null, GetSelectedResources() );
context.SetOwnerForm( FindForm() );
return context;
}
public IResourceList GetSelectedResources()
{
List ids = new List ();
lock( _jetListView.Selection.SelectionLock )
{
foreach( IResource res in _jetListView.Selection )
{
if( !res.IsDeleted )
{
ids.Add( res.Id );
}
}
}
return Core.ResourceStore.ListFromIds( ids, false );
}
public JetListViewColumnCollection Columns
{
get { return _jetListView.Columns; }
}
///
/// Gets or sets a value indicating whether the user can edit the texts of resources in the tree.
///
public bool InPlaceEdit
{
get { return _jetListView.InPlaceEditor != null; }
set
{
if( value )
{
_jetListView.InPlaceEditor = _inPlaceEditor;
}
else
{
_jetListView.InPlaceEditor = null;
}
}
}
///
/// Begins in-place editing of the text of the specified resource.
///
/// The resource to edit.
public void EditResourceLabel( IResource res )
{
if( res == null )
{
throw new ArgumentNullException( "res" );
}
JetListViewNode node = _jetListView.NodeCollection.NodeFromItem( res );
if( node == null )
{
if( !_dataProvider.FindResourceNode( res ) )
{
throw new ArgumentException( "Resource not found in tree", "res" );
}
node = _jetListView.NodeCollection.NodeFromItem( res );
}
_jetListView.InPlaceEditNode( node );
}
///
/// Checks if the text of the specified resource can be in-place edited.
///
/// The resource to edit.
/// The text that will be displayed in the in-place edit box.
/// true if the resource can be edited, false otherwise.
public bool CanEditResourceLabel( IResource res, ref string text )
{
IResourceRenameHandler renameHandler = Core.PluginLoader.GetResourceRenameHandler( res );
if( renameHandler != null )
{
return renameHandler.CanRenameResource( res, ref text );
}
IResourceUIHandler uiHandler = Core.PluginLoader.GetResourceUIHandler( res );
if( uiHandler != null )
{
return uiHandler.CanRenameResource( res );
}
return false;
}
private void HandleBeforeItemEdit( object sender, JetItemEditEventArgs e )
{
if( BeforeItemEdit != null )
{
ResourceItemEditEventArgs args = new ResourceItemEditEventArgs( e.Text, (IResource)e.Item, e.Column );
BeforeItemEdit( this, args );
e.Text = args.Text;
e.CancelEdit = args.CancelEdit;
}
else if( AfterItemEdit == null )
{
string text = e.Text;
if( CanEditResourceLabel( (IResource)e.Item, ref text ) )
{
e.Text = text;
}
else
{
e.CancelEdit = true;
}
}
}
private void HandleAfterItemEdit( object sender, JetItemEditEventArgs e )
{
if( e.Text != null )
{
IResource res = (IResource)e.Item;
if( AfterItemEdit != null )
{
ResourceItemEditEventArgs args = new ResourceItemEditEventArgs( e.Text, res, e.Column );
AfterItemEdit( this, args );
}
else if( BeforeItemEdit == null )
{
IResourceRenameHandler renameHandler = Core.PluginLoader.GetResourceRenameHandler( res );
if( renameHandler != null )
{
renameHandler.ResourceRenamed( res, e.Text );
}
else
{
IResourceUIHandler uiHandler = Core.PluginLoader.GetResourceUIHandler( res );
if( uiHandler != null )
{
uiHandler.ResourceRenamed( res, e.Text );
}
}
}
}
}
private void HandleNodeAdded( object sender, JetListViewNodeEventArgs e )
{
if( _selectAddedItems && !_initialFill )
{
_jetListView.Selection.Clear();
_jetListView.Selection.Add( e.Node.Data );
}
if( ResourceAdded != null )
{
ResourceAdded( this, new ResourceEventArgs( (IResource)e.Node.Data ) );
}
}
public IResource ActiveResource
{
get
{
JetListViewNode node = _jetListView.Selection.ActiveNode;
if( node != null )
{
return (IResource)node.Data;
}
return null;
}
}
public JetListViewNode NodeFromItem( IResource item )
{
return _jetListView.NodeCollection.NodeFromItem( item );
}
public IEnumerator EnumerateItemsForward( JetListViewNode node )
{
if( node == null )
{
node = _jetListView.Nodes[ 0 ];
}
return _jetListView.NodeCollection.EnumerateNodesForward( node );
}
public IEnumerator EnumerateItemsBackward( JetListViewNode node )
{
if( node == null )
{
node = _jetListView.Nodes[ 0 ];
}
return _jetListView.NodeCollection.EnumerateNodesBackward( node );
}
private void HandleItemUpdated( object sender, ItemEventArgs e )
{
_itemFormatCache.InvalidateFormat( e.Item );
}
private void HandleActiveNodeChanged( object sender, JetListViewNodeEventArgs e )
{
if( Core.UserInterfaceAP.IsOwnerThread )
{
OnActiveResourceChanged();
}
else
{
Core.UserInterfaceAP.QueueJob( JobPriority.Immediate, new MethodInvoker( OnActiveResourceChanged ) );
}
}
private void OnActiveResourceChanged()
{
if( ActiveResourceChanged != null )
{
ActiveResourceChanged( this, EventArgs.Empty );
}
}
private void HandleSelectionStateChanged( object sender, StateChangeEventArgs e )
{
if( SelectionChanged != null )
{
SelectionChanged( this, EventArgs.Empty );
}
}
//---------------------------------------------------------------------
// This method is just a facade for outsiders in order to hide the
// JetListView and SelectionModel as much as possible. By 8.02.07 there
// are many other methods which should be implemented to hide the JLV
// completely.
//---------------------------------------------------------------------
public void SelectSingleItem( IResource res )
{
_jetListView.Selection.SelectSingleItem( res );
}
public void SelectAll()
{
_jetListView.Selection.SelectAll();
}
public int VisibleItemCount
{
get { return _jetListView.NodeCollection.VisibleItemCount; }
}
///
/// Calls the specified delegate for every node in the view.
///
/// The delegate to call.
public void ForEachNode( ResourceDelegate resourceDelegate )
{
if( _jetListView.Nodes.Count > 0 )
{
IEnumerator enumerator = _jetListView.NodeCollection.EnumerateNodesForward( _jetListView.Nodes[ 0 ] );
while( enumerator.MoveNext() )
{
JetListViewNode node = (JetListViewNode)enumerator.Current;
resourceDelegate( (IResource)node.Data );
}
}
}
public IResource LocateNextResource( IResource startResource, LocateMatchCallback callback,
bool skipFirst, bool searchBackAlso )
{
return LocateNextResourceInDirection( true, startResource, callback, skipFirst, searchBackAlso );
}
public IResource LocatePrevResource( IResource startResource, LocateMatchCallback callback,
bool skipFirst, bool searchBackAlso )
{
return LocateNextResourceInDirection( false, startResource, callback, skipFirst, searchBackAlso );
}
private IResource LocateNextResourceInDirection( bool isForward, IResource start, LocateMatchCallback callback,
bool skipFirst, bool searchBackAlso )
{
IResource result = null;
JetListViewNode node = NodeFromItem( start );
if( node != null )
{
lock( JetListView.NodeCollection )
{
result = SearchEnumerator( isForward, node, null, callback, skipFirst );
if( result == null && searchBackAlso )
result = SearchEnumerator( isForward, null, node, callback, false );
}
}
return result;
}
private IResource SearchEnumerator( bool forward, JetListViewNode node, JetListViewNode stopNode,
LocateMatchCallback callback, bool skipFirst )
{
IEnumerator enumerator = forward? EnumerateItemsForward( node ) : EnumerateItemsBackward( node ) ;
if( skipFirst )
{
enumerator.MoveNext();
}
while( enumerator.MoveNext() )
{
JetListViewNode childNode = (JetListViewNode)enumerator.Current;
if( childNode == stopNode )
{
return null;
}
IResource childResource = (IResource)childNode.Data;
if( callback( childResource ) )
{
return childResource;
}
}
return null;
}
public void HookFormattingRulesChange()
{
_itemFormatCache.HookFormattingRulesChange();
}
public bool CanExecuteCommand( string command )
{
string text = "";
switch( command )
{
case DisplayPaneCommands.SelectAll:
return true;
case DisplayPaneCommands.RenameSelection:
return Selection.Count == 1 && CanEditResourceLabel( SelectedResource, ref text );
case DisplayPaneCommands.Copy:
return Selection.Count > 0;
}
return false;
}
public void ExecuteCommand( string command )
{
switch( command )
{
case DisplayPaneCommands.SelectAll:
SelectAll();
break;
case DisplayPaneCommands.RenameSelection:
if( Selection.Count == 1 )
{
EditResourceLabel( SelectedResource );
}
break;
case DisplayPaneCommands.Copy:
_jetListView.CopySelection();
break;
}
}
private IResource SelectedResource
{
get
{
IEnumerator enumerator = Selection.GetEnumerator();
enumerator.MoveNext();
return (IResource)enumerator.Current;
}
}
private void HandleResourceJobStarting( object sender, EventArgs e )
{
if( !_inResourceOperation )
{
_jetListView.NodeCollection.BeginUpdate();
_inResourceOperation = true;
}
}
private void HandleResourceJobFinished( object sender, EventArgs e )
{
if( _inResourceOperation )
{
try
{
_jetListView.NodeCollection.EndUpdate();
}
finally
{
_inResourceOperation = false;
}
}
}
///
/// Gets or sets the resource that is treated as a root for the tree or a list view.
///
public virtual IResource RootResource
{
get { return _resRoot; }
set
{
_resRoot = value; // TODO: empty-drop-handler sync
}
}
}
///
/// A column which displays a number of resource properties.
///
public class ResourcePropsColumn : JetListViewColumn
{
protected int[] _propIds;
private IResourceComparer _customComparer;
private IGroupProvider _groupProvider;
protected ResourcePropsColumn( int[] propIds )
{
_propIds = propIds;
}
public int[] PropIds
{
get { return _propIds; }
}
public IResourceComparer CustomComparer
{
get { return _customComparer; }
set { _customComparer = value; }
}
///
/// The provider which is used for grouping data in the column.
///
public IGroupProvider GroupProvider
{
get { return _groupProvider; }
set { _groupProvider = value; }
}
///
/// Compares the specified array of property IDs to the array of property IDs displayed
/// in the column.
///
/// The property IDs to compare with.
/// true if the column shows the specified properties, false if the list of properties
/// is different.
public bool PropIdsEqual( int[] propIds )
{
if( propIds.Length != _propIds.Length )
{
return false;
}
for( int i = 0; i < propIds.Length && i < _propIds.Length; i++ )
{
if( propIds[ i ] != _propIds[ i ] )
{
return false;
}
}
return true;
}
}
public interface IResourceDataProvider : IDisposable
{
void FillResources( ResourceListView2 resourceListView );
bool FindResourceNode( IResource res );
}
public class OmeaMethodInvoker : IControlMethodInvoker
{
public void BeginInvoke( Delegate method, params object[] args )
{
Core.UIManager.QueueUIJob( method, args );
}
public bool InvokeRequired
{
get { return !Core.UserInterfaceAP.IsOwnerThread; }
}
}
public class ResourceItemEditEventArgs
{
private readonly IResource _resource;
private readonly JetListViewColumn _column;
public ResourceItemEditEventArgs( string text, IResource resource, JetListViewColumn column )
{
Text = text;
_resource = resource;
_column = column;
CancelEdit = false;
}
public string Text { get; set; }
public IResource Resource { get { return _resource; } }
public JetListViewColumn Column { get { return _column; } }
public bool CancelEdit { get; set; }
}
public delegate void ResourceItemEditEventHandler( object sender, ResourceItemEditEventArgs e );
}