///
/// 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.Drawing;
using System.Threading;
using JetBrains.DataStructures;
using JetBrains.JetListViewLibrary;
using JetBrains.Omea.OpenAPI;
using JetBrains.UI.RichText;
namespace JetBrains.Omea.GUIControls
{
///
/// A column which draws rich text in JetListView.
///
public class RichTextColumn: JetListViewColumn
{
private ArrayList _decorators = new ArrayList();
private IntHashTable _richTextCache = new IntHashTable();
private HashSet _decorationChangedNodes = new HashSet();
private LocalDataStoreSlot _itemBeingDecoratedSlot;
private ReaderWriterLock _decoratorLock = new ReaderWriterLock();
public RichTextColumn()
{
Core.ResourceAP.JobFinished += HandleResourceJobFinished;
_itemBeingDecoratedSlot = Thread.AllocateDataSlot();
}
public override void Dispose()
{
Core.ResourceAP.JobFinished -= HandleResourceJobFinished;
_decoratorLock.AcquireWriterLock( -1 );
try
{
foreach( IResourceNodeDecorator decorator in _decorators )
{
decorator.DecorationChanged -= HandleDecorationChanged;
}
_decorators.Clear();
}
finally
{
_decoratorLock.ReleaseWriterLock();
}
base.Dispose();
}
public void AddNodeDecorator( IResourceNodeDecorator decorator )
{
_decoratorLock.AcquireWriterLock( -1 );
try
{
_decorators.Add( decorator );
}
finally
{
_decoratorLock.ReleaseWriterLock();
}
decorator.DecorationChanged += HandleDecorationChanged;
}
public void InsertNodeDecorator( IResourceNodeDecorator decorator, int pos )
{
_decoratorLock.AcquireWriterLock( -1 );
try
{
_decorators.Insert( pos, decorator );
}
finally
{
_decoratorLock.ReleaseWriterLock();
}
decorator.DecorationChanged += HandleDecorationChanged;
}
private void HandleDecorationChanged( object sender, ResourceEventArgs e )
{
object itemBeingDecorated = Thread.GetData( _itemBeingDecoratedSlot );
if ( itemBeingDecorated != null && (int) itemBeingDecorated == e.Resource.Id )
{
return;
}
if ( Core.ResourceStore.IsOwnerThread() )
{
_decorationChangedNodes.Add( e.Resource );
}
else
{
UpdateDecoration( e.Resource );
}
}
private void UpdateDecoration( IResource res )
{
OwnerControl.UpdateItemSafe( res );
}
protected override void DrawItemText( Graphics g, Rectangle rcText, object item,
Color textColor, RowState state, string highlightText )
{
IResource res = (IResource) item;
RichText text = GetRichText( res );
FormatRowRichText( ref text, textColor, state, highlightText );
text.DrawClipped( g, rcText );
}
public static void FormatRowRichText( ref RichText text, Color textColor, RowState state,
string highlightText )
{
if ( textColor != SystemColors.WindowText || highlightText != null ||
(state & RowState.InactiveSelected) != 0 )
{
text = (RichText) text.Clone();
if ( textColor != SystemColors.WindowText || (state & RowState.InactiveSelected) != 0 )
{
text.SetColors( textColor, Color.Transparent );
}
if ( highlightText != null )
{
text.SetColors( textColor, Color.Transparent );
text.SetColors( SystemColors.HighlightText, Color.Transparent, 0, highlightText.Length );
}
}
}
protected override int GetTextWidth( object item )
{
IResource res = (IResource) item;
RichText text = GetRichText( res );
return text.GetSize().Width;
}
protected override int GetHighlightWidth( Graphics g, object item, string highlightText )
{
RichText text = GetRichText( (IResource) item );
RichText[] strings = text.Split( highlightText.Length );
return strings [0].GetSize().Width;
}
private RichText GetRichText( IResource res )
{
lock( _richTextCache )
{
RichText richText = (RichText) _richTextCache [res.Id];
if ( richText != null )
{
return richText;
}
}
TextStyle defaultStyle = new TextStyle( FontStyle.Regular,
SystemColors.WindowText, Color.Transparent );
RichTextParameters defaultParams = new RichTextParameters( OwnerControl.Font,
defaultStyle );
if ( Core.State == CoreState.ShuttingDown )
{
return new RichText( "", defaultParams );
}
Thread.SetData( _itemBeingDecoratedSlot, res.Id );
RichText newRichText = new RichText( res.DisplayName, defaultParams );
_decoratorLock.AcquireReaderLock( -1 );
try
{
HashSet processedDecorators = new HashSet();
for( int i=_decorators.Count-1; i >= 0; i-- )
{
IResourceNodeDecorator decorator = (IResourceNodeDecorator) _decorators [i];
string key = decorator.DecorationKey;
if ( key != null && processedDecorators.Contains( key ) )
{
continue;
}
if ( decorator.DecorateNode( res, newRichText ) )
{
if ( key != null )
{
processedDecorators.Add( key );
}
}
}
}
finally
{
_decoratorLock.ReleaseReaderLock();
}
Thread.SetData( _itemBeingDecoratedSlot, -1 );
lock( _richTextCache )
{
_richTextCache [res.Id] = newRichText;
}
return newRichText;
}
protected override void UpdateItem( object item )
{
lock( _richTextCache )
{
IResource res = (IResource) item;
_richTextCache.Remove( res.Id );
}
}
public override string GetToolTip( JetListViewNode node, Rectangle rc, ref bool needPlace )
{
IResource res = (IResource) node.Data;
string toolTip = null;
if ( _itemToolTipCallback != null )
{
toolTip = _itemToolTipCallback( node.Data );
}
RichText richText = null;
if ( OwnerControl != null && OwnerControl.AutoToolTips )
{
richText = GetRichText( res );
}
if ( richText != null && richText.GetSize().Width > rc.Width )
{
if ( toolTip == null || toolTip.Length == 0 )
{
toolTip = richText.Text;
}
else
{
toolTip = richText.Text + " (" + toolTip + ")";
}
}
else
{
needPlace = false;
}
return toolTip;
}
public void InvalidateRichText()
{
lock( _richTextCache )
{
_richTextCache.Clear();
}
}
private void HandleResourceJobFinished( object sender, EventArgs e )
{
if( _decorationChangedNodes.Count > 0 )
{
foreach( HashSet.Entry entry in _decorationChangedNodes )
{
IResource res = (IResource) entry.Key;
UpdateDecoration( res );
}
_decorationChangedNodes.Clear();
}
}
}
}