// Copyright � 2004 by Christoph Richner. All rights are reserved.
//
// If you like this code then feel free to go ahead and use it.
// The only thing I ask is that you don't remove or alter my copyright notice.
//
// Your use of this software is entirely at your own risk. I make no claims or
// warrantees about the reliability or fitness of this code for any particular purpose.
// If you make changes or additions to this code please mark your code as being yours.
//
// website http://raccoom.sytes.net, email microweb@bluewin.ch, msn chrisdarebell@msn.com
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;
using Raccoom.Xml;
namespace Raccoom.Windows.Forms
{
/// <summary>
/// Summary description for ComboboxOpml.
/// </summary>
public class TreeViewOpmlDocument : System.Windows.Forms.TreeView
{
#region fields
public delegate void DocumentPropertyChangedDelegate(object sender, TreeNodeOpmlOutline node, PropertyChangedEventArgs e);
public enum ContentType{ FoldersAndContent, Folders }
public enum ViewStyle { Document,Flat }
private enum DragDropActions{None, FavFo, FavFav, FoFo, FoFav, FavRoot, FoRoot}
private ContentType _contentType;
private ViewStyle _viewStyle;
private TreeNodeCollection _rootCollection;
private System.Windows.Forms.ImageList _imageList;
private System.ComponentModel.IContainer components;
private OpmlDocument _document;
#endregion
#region constructors
public TreeViewOpmlDocument(System.ComponentModel.IContainer container)
{
container.Add(this);
InitializeComponent();
}
public TreeViewOpmlDocument()
{
InitializeComponent();
}
#endregion
#region public interface
[Category("View")]
public ContentType Type
{
get
{
return _contentType;
}
set
{
_contentType = value;
}
}
[Category("View")]
public ViewStyle Style
{
get
{
return _viewStyle;
}
set
{
_viewStyle = value;
}
}
public void Populate(OpmlDocument document)
{
bool changed = (_document != document);
//
if(changed && _document!=null) _document.PropertyChanged -= new PropertyChangedEventHandler(DocumentPropertyChanged);
_document = document;
//
if(changed) _document.PropertyChanged += new PropertyChangedEventHandler(DocumentPropertyChanged);
//
this.Cursor = Cursors.WaitCursor;
BeginUpdate();
this.Nodes.Clear();
try
{
switch (_viewStyle)
{
case ViewStyle.Document:
TreeNodeOpmlOutline favoriteRoot = new TreeNodeOpmlOutline(null,0,1);
favoriteRoot.Text = document.Head.Title;
_rootCollection = favoriteRoot.Nodes;
Populate(_rootCollection, document.Body.Items);
this.Nodes.Add(favoriteRoot);
favoriteRoot.Expand();
//
break;
case ViewStyle.Flat:
_rootCollection = this.Nodes;
Populate(_rootCollection, document.Body.Items);
break;
}
}
catch (System.Exception e)
{
throw e;
}
finally
{
this.Cursor = Cursors.Default;
EndUpdate();
}
}
public TreeNodeOpmlOutline CreateNewFolder(TreeNodeOpmlOutline parent, OpmlOutline outline)
{
return CreateNewFolder(parent, outline, null);
}
public TreeNodeOpmlOutline CreateNewFolder(TreeNodeOpmlOutline parent, OpmlOutline outline, Uri url)
{
TreeNodeOpmlOutline node = CreateTreeNode(outline,true);
if(parent!=null)
{
parent.OpmlOutlineItem.Items.Add(outline);
parent.Nodes.Add(node);
}
else
{
this._document.Body.Items.Add(outline);
_rootCollection.Add(node);
}
//
if(url!=null)
{
node.OpmlOutlineItem.Type = OpmlOutline.OpmlTypeLinkReference;
}
//
return node;
}
public void DeleteNode(TreeNodeOpmlOutline node)
{
if(node==null || node.Nodes.Equals(this._rootCollection)) return;
//
TreeNodeOpmlOutline parentNode = node.Parent as TreeNodeOpmlOutline;
if(parentNode != null && ! parentNode.Nodes.Equals(this._rootCollection))
{
parentNode.OpmlOutlineItem.Items.Remove(node.OpmlOutlineItem);
parentNode.Nodes.Remove(node);
}
else
{
this._document.Body.Items.Remove(node.OpmlOutlineItem);
Nodes.Remove(node);
}
}
#endregion
#region private interface
private void MoveNode(TreeNodeOpmlOutline sourceNode, TreeNode targetNode)
{
DragDropActions ddaction = DragDropActions.None;
//
TreeNodeOpmlOutline targetNodeOpml = targetNode as TreeNodeOpmlOutline;
// Favorite -> Favorite
if(!sourceNode.IsFolder && targetNodeOpml!=null && !targetNodeOpml.IsFolder)
{
ddaction = DragDropActions.FavFav;
}
// Favorite -> Folder
else if(!sourceNode.IsFolder && targetNodeOpml!=null && targetNodeOpml.IsFolder)
{
ddaction = DragDropActions.FavFo;
}
// Folder -> Favorite
else if(sourceNode.IsFolder && targetNodeOpml!=null && !targetNodeOpml.IsFolder)
{
ddaction = DragDropActions.FoFav;
}
// Folder -> Folder
else if(sourceNode.IsFolder && targetNodeOpml!=null && targetNodeOpml.IsFolder)
{
ddaction = DragDropActions.FoFo;
}
// Folder -> Root
else if(sourceNode.IsFolder && targetNodeOpml.OpmlOutlineItem==null && targetNode.Nodes.Equals(_rootCollection))
{
ddaction = DragDropActions.FoRoot;
}
// Favorite -> Root
else if(!sourceNode.IsFolder && targetNodeOpml.OpmlOutlineItem==null && targetNode.Nodes.Equals(_rootCollection))
{
ddaction = DragDropActions.FavRoot;
}
//
switch (ddaction)
{
case DragDropActions.FavFav:
// mode sibling
TreeNodeOpmlOutline parentParent = targetNodeOpml.Parent as TreeNodeOpmlOutline;
// Parent seems to be root
if(parentParent==null) goto case DragDropActions.FavRoot;
//
DeleteNode(sourceNode);
parentParent.OpmlOutlineItem.Items.Add(sourceNode.OpmlOutlineItem);
if(parentParent.IsExpanded)
{
parentParent.Nodes.Insert(parentParent.Nodes.IndexOf(targetNodeOpml),sourceNode);
}
else
{
parentParent.Expand();
}
break;
case DragDropActions.FoFo:
DeleteNode(sourceNode);
targetNodeOpml.OpmlOutlineItem.Items.Add(sourceNode.OpmlOutlineItem);
if(targetNodeOpml.IsExpanded)
{
targetNodeOpml.Nodes.Insert(0,sourceNode);
}
else
{
targetNodeOpml.Expand();
}
break;
case DragDropActions.FavFo:
DeleteNode(sourceNode);
targetNodeOpml.OpmlOutlineItem.Items.Add(sourceNode.OpmlOutlineItem);
if(targetNodeOpml.IsExpanded)
{
targetNodeOpml.Nodes.Insert(0,sourceNode);
}
else
{
targetNodeOpml.Expand();
}
targetNodeOpml.Nodes.Add(sourceNode);
break;
case DragDropActions.FavRoot:
// folder and favorite -> root
goto case DragDropActions.FoRoot;
case DragDropActions.FoFav:
break;
case DragDropActions.FoRoot:
DeleteNode(sourceNode);
_rootCollection.Add(sourceNode);
_document.Body.Items.Add(sourceNode.OpmlOutlineItem);
break;
}
}
private void Populate(TreeNodeCollection nodeCollection, OpmlOutlineCollection itemCollection)
{
foreach(OpmlOutline item in itemCollection)
{
// linked favorites
if (item.Type == OpmlOutline.OpmlTypeLinkReference)
{
TreeNodeOpmlOutline favoriteNode = CreateTreeNode(item, true);
nodeCollection.Add(favoriteNode);
}
// folder
else if(item.Items.Count > 0 && item.Type != OpmlOutline.OpmlTypeLinkReference)
{
TreeNodeOpmlOutline node = CreateTreeNode(item, true);
nodeCollection.Add(node);
}
// favorite
else if(_contentType==ContentType.FoldersAndContent)
{
TreeNodeOpmlOutline favoriteNode = CreateTreeNode(item, false);
nodeCollection.Add(favoriteNode);
}
}
}
private TreeNodeOpmlOutline CreateTreeNode(OpmlOutline outline, bool isParent)
{
TreeNodeOpmlOutline node = new TreeNodeOpmlOutline(outline,4,5);
//
if(isParent)
{
node.ImageIndex = 2;
node.SelectedImageIndex = 3;
}
//
if(isParent) node.AddDummyNode();
//
return node;
}
private TreeNodeOpmlOutline FindNodeRecursive(OpmlOutline outline, TreeNodeCollection nodes)
{
foreach(TreeNodeOpmlOutline node in nodes)
{
if(node.OpmlOutlineItem!=null)
{
if(node.OpmlOutlineItem.Equals(outline)) return node;
}
TreeNodeOpmlOutline subNode =FindNodeRecursive(outline, node.Nodes);
if(subNode!=null) return subNode;
}
return null;
}
#endregion
#region events
private void DocumentPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(this.InvokeRequired)
{
this.Invoke(new DocumentPropertyChangedDelegate(OnDocumentPropertyChanged),new object[]{sender, FindNodeRecursive(sender as OpmlOutline, this.Nodes),e});
}
else
{
OnDocumentPropertyChanged(sender, FindNodeRecursive(sender as OpmlOutline, this.Nodes) ,e);
}
}
protected virtual void OnDocumentPropertyChanged(object sender, TreeNodeOpmlOutline node, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Items")
{
if(sender is OpmlOutline)
{
OpmlOutline outline = (OpmlOutline) sender;
if(node==null) return;
// refresh
bool expand = node.IsExpanded;
node.Nodes.Clear();
this.Populate(node.Nodes, outline.Items);
if(expand) node.Expand();
}
else if(sender is OpmlBody)
{
this.Nodes.Clear();
this.Populate(this._document);
}
}
else if(e.PropertyName == "Text")
{
OpmlOutline outline = (OpmlOutline) sender;
if(node==null || node.IsEditing) return;
node.Text = outline.Text;
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
switch(e.KeyCode)
{
case Keys.Delete:
this.DeleteNode(this.SelectedNode as Raccoom.Windows.Forms.TreeViewOpmlDocument.TreeNodeOpmlOutline);
e.Handled = true;
break;
case Keys.F2:
this.SelectedNode.BeginEdit();
e.Handled = true;
break;
}
//
base.OnKeyUp (e);
}
protected override void OnBeforeExpand(TreeViewCancelEventArgs e)
{
TreeNodeOpmlOutline nodeOutline = e.Node as TreeNodeOpmlOutline;
if(nodeOutline==null || nodeOutline.OpmlOutlineItem == null) return;
// it's only permitted to show real items, no reference items
if(nodeOutline.OpmlOutlineItem.Type != OpmlOutline.OpmlTypeLinkReference)
{
// populate sub items
Cursor.Current = Cursors.WaitCursor;
try
{
nodeOutline.RemoveDummyNode();
// do we need a refresh ?
if(e.Node.Nodes.Count == nodeOutline.OpmlOutlineItem.Items.Count) return;
// clear nodes and refresh view
nodeOutline.Nodes.Clear();
// populate
this.Populate(e.Node.Nodes, nodeOutline.OpmlOutlineItem.Items);
}
catch( System.Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
finally
{
Cursor.Current = Cursors.Default;
}
}
else
{
e.Cancel = true;
}
//
base.OnBeforeExpand (e);
}
protected override void OnItemDrag(ItemDragEventArgs e)
{
if(e.Item is TreeNodeOpmlOutline)
{
this.DoDragDrop(e.Item,DragDropEffects.All);
}
//
base.OnItemDrag (e);
}
protected override void OnDragOver(DragEventArgs drgevent)
{
TreeNodeOpmlOutline sourceNode = drgevent.Data.GetData(typeof(TreeNodeOpmlOutline).FullName) as TreeNodeOpmlOutline;
TreeNode targetNode = this.GetNodeAt(this.PointToClient(new System.Drawing.Point(drgevent.X, drgevent.Y)));
drgevent.Effect = (object.Equals(sourceNode, targetNode)) ? DragDropEffects.None : DragDropEffects.Move;
base.OnDragOver (drgevent);
}
protected override void OnDragDrop(DragEventArgs drgevent)
{
TreeNodeOpmlOutline sourceNode = drgevent.Data.GetData(typeof(TreeNodeOpmlOutline).FullName) as TreeNodeOpmlOutline;
TreeNode targetNode = this.GetNodeAt(this.PointToClient(new System.Drawing.Point(drgevent.X, drgevent.Y)));
if(sourceNode==null || targetNode ==null || object.Equals(sourceNode, targetNode)) return;
//
MoveNode(sourceNode, targetNode);
//
base.OnDragDrop (drgevent);
}
protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
{
// do not edit the root node
e.CancelEdit = e.Node.Nodes.Equals(this._rootCollection);
base.OnBeforeLabelEdit (e);
}
protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
TreeNodeOpmlOutline node = e.Node as TreeNodeOpmlOutline;
if(node!=null && !e.CancelEdit && e.Label != null)
{
node.OpmlOutlineItem.Text = e.Label;
}
//
base.OnAfterLabelEdit (e);
}
#endregion
#region Component Designer generated code
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(TreeViewOpmlDocument));
this._imageList = new System.Windows.Forms.ImageList(this.components);
//
// _imageList
//
this._imageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit;
this._imageList.ImageSize = new System.Drawing.Size(16, 16);
this._imageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("_imageList.ImageStream")));
this._imageList.TransparentColor = System.Drawing.Color.Transparent;
//
// TreeViewOpmlDocument
//
this.ImageIndex = 0;
this.ImageList = this._imageList;
this.SelectedImageIndex = 0;
}
#endregion
#region nested classes
public class TreeNodeOpmlOutline : TreeNode
{
private OpmlOutline _outline;
public TreeNodeOpmlOutline(OpmlOutline outline, int imageIndex, int selectedImageIndex)
{
_outline = outline;
if(_outline!=null) this.Text = _outline.Text;
this.ImageIndex = imageIndex;
this.SelectedImageIndex = selectedImageIndex;
}
public OpmlOutline OpmlOutlineItem
{
get
{
return _outline;
}
set
{
_outline = value;
}
}
public bool IsFolder
{
get
{
if(_outline==null) return true;
//
return _outline.Items.Count>0 || _outline.XmlUrl==null;
}
}
/// <summary>
/// Adds a dummy node to the parent node
/// </summary>
public virtual void AddDummyNode()
{
TreeNodeOpmlOutline node = new TreeNodeOpmlOutline(null,-1,-1);
node.Text = "@@Dummy@@";
Nodes.Add(node);
}
/// <summary>
/// Removes the dummy node from the parent node.
/// </summary>
public virtual void RemoveDummyNode()
{
if ((Nodes.Count == 1 ) & (Nodes[0].Text == "@@Dummy@@"))
{
Nodes[0].Remove();
}
}
}
#endregion
}
}