///
/// 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.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using JetBrains.Interop.WinApi;
using JetBrains.UI.Components.CustomTreeView;
using JetBrains.UI.Interop;
namespace JetBrains.UI.Components.RichTextTreeView
{
///
/// Paints nodes using rich text
///
public class RichTextNodePainter : INodePainter
{
///
/// List of handled nodes
///
private Hashtable myNodes = new Hashtable();
///
/// Adds text to a tree node
///
public void Add( TreeNode node, RichText.RichText text )
{
myNodes [node] = text;
InvalidateNode(node);
}
///
/// Removes a node
///
public void Remove( TreeNode node )
{
myNodes.Remove(node);
}
///
/// Gets rich text by a tree node
///
public RichText.RichText this[ TreeNode node ] { get { return (RichText.RichText)myNodes[node]; } set { myNodes[node] = value; } }
#region INodePainter Members
public bool IsHandled( TreeNode node )
{
return myNodes.Contains(node);
}
public void Draw( TreeNode node, IntPtr hdc, Rectangle rect )
{
if (!IsHandled(node) || node.Bounds.IsEmpty || node.IsEditing)
return;
CustomTreeView.CustomTreeView treeView = (CustomTreeView.CustomTreeView)node.TreeView;
int offset = CalculateOffset(node);
RichText.RichText text = (RichText.RichText)myNodes[node];
Rectangle contentRect = CalculateContentRectangle(node, hdc, offset);
rect.X += offset;
rect.Width -= offset;
Rectangle fullRect = new Rectangle(contentRect.X - 1, node.Bounds.Top, contentRect.Width + 5, node.Bounds.Height);
//g.SetClip(fullRect);
TreeNode dropHiliteNode = treeView.DraggingOver ? treeView.DropHighlightedNode : null;
bool hasFocus = node.TreeView.Focused;
bool drawSelected = treeView.DraggingOver
? (node == dropHiliteNode)
: node.IsSelected && hasFocus;
bool drawNonfocusedSelection = node.IsSelected && !node.TreeView.HideSelection &&
(!hasFocus || treeView.DraggingOver);
Color backColor;
if (drawSelected)
{
backColor = SystemColors.Highlight;
}
else if (drawNonfocusedSelection)
{
backColor = SystemColors.Control;
}
else
backColor = treeView.BackColor;
IntPtr hBrush = Win32Declarations.CreateSolidBrush(Win32Declarations.ColorToRGB(backColor));
RECT lineRect = new RECT(fullRect.Left-1, node.Bounds.Top, fullRect.Right+1, node.Bounds.Bottom);
Win32Declarations.FillRect(hdc, ref lineRect, hBrush);
Win32Declarations.DeleteObject(hBrush);
if (drawSelected)
{
text = (RichText.RichText)text.Clone();
text.SetColors(SystemColors.HighlightText, SystemColors.Highlight);
}
else if (drawNonfocusedSelection)
{
text = (RichText.RichText)text.Clone();
text.SetColors(SystemColors.WindowText, SystemColors.Control);
}
text.Draw(hdc, contentRect);
if (hasFocus && treeView.SelectedNode == node && treeView.NeedFocusRect() && dropHiliteNode == null)
{
RECT rc = new RECT(fullRect.Left-1, fullRect.Top, fullRect.Right+1, fullRect.Bottom);
Win32Declarations.DrawFocusRect(hdc, ref rc);
}
}
public TreeNode GetNodeAt( TreeView treeView, Point point )
{
TVHITTESTINFO hti = new TVHITTESTINFO();
hti.pt = new POINT(point);
Win32Declarations.SendMessage(treeView.Handle, TreeViewMessage.TVM_HITTEST, 0, ref hti);
if ((hti.flags & TreeViewHitTestFlags.ONITEM) != 0)
{
return TreeNode.FromHandle(treeView, hti.hItem);
}
if ((hti.flags & TreeViewHitTestFlags.ONITEMRIGHT) != 0)
{
using (Graphics g = treeView.CreateGraphics())
{
IntPtr hdc = g.GetHdc();
try
{
TreeNode node = TreeNode.FromHandle(treeView, hti.hItem);
int offset = CalculateOffset(node);
Rectangle content = CalculateContentRectangle(node, hdc, offset);
if (content.Contains(point))
return node;
}
finally
{
g.ReleaseHdc(hdc);
}
}
}
return null;
}
public void InvalidateNode( TreeNode node )
{
try
{
Rectangle rect = new Rectangle(node.Bounds.Location, new Size(node.TreeView.ClientSize.Width, node.Bounds.Height));
node.TreeView.Invalidate(rect);
}
catch (Exception ex )
{
Trace.WriteLine( "Error invalidating in RichTextNodePainter: " + ex.ToString() );
}
}
#endregion
#region Private methods
///
/// Calculated content rectangle of node
///
private Rectangle CalculateContentRectangle( TreeNode node, IntPtr hdc, int offset )
{
Rectangle rect = new Rectangle(node.TreeView.ClientRectangle.Left, node.Bounds.Top, node.TreeView.ClientSize.Width, node.Bounds.Height);
RichText.RichText text = (RichText.RichText)myNodes[node];
rect.X += offset;
rect.Width -= offset;
return new Rectangle(rect.Location, text.GetSize(hdc).ToSize());
}
///
/// Calculates offset of a given node from the left border
///
private int CalculateOffset( TreeNode node )
{
TreeView tv = node.TreeView;
int delta = tv.Nodes [0].Bounds.Left - tv.Indent;
int level = GetNodeLevel(node);
int offset;
if (node.TreeView.ShowRootLines)
offset = node.TreeView.Indent*(level + 1);
else
offset = node.TreeView.Indent*level;
if (node.TreeView.ImageList != null && node.TreeView.ImageList.Images.Count > 0)
{
if (node.ImageIndex >= 0)
offset += tv.ImageList.ImageSize.Width;
//if ( tv.Nodes [0].ImageIndex >= 0 )
delta -= tv.ImageList.ImageSize.Width;
}
if ((node.TreeView.CheckBoxes || (node.TreeView as CustomTreeView.CustomTreeView).ThreeStateCheckboxes) && (node.TreeView as CustomTreeView.CustomTreeView).GetNodeCheckState(node) != NodeCheckState.None)
offset += 17;
offset += delta;
offset += 2;
return offset;
}
///
/// Gets level of a given node
///
/// The node to get level of
private int GetNodeLevel( TreeNode node )
{
int level = 0;
while (node.Parent != null)
{
node = node.Parent;
level++;
}
return level;
}
#endregion
}
}