using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace JFileManager
{
/// <summary>
/// Class based on a TreeView
/// </summary>
public class cSysTreeView : TreeView
{
private const int TV_FIRST = 0x1100;
private const int TVM_SETITEMW = (TV_FIRST + 63);
private bool m_blnDirectoryIsValid = false;
private bool m_blnRaisePathChange = true;
private bool m_blnExpandOnCreate = true;
private bool m_blnSelectOnCreate = true;
private ContextMenu m_ctxTVDragDrop;
private DateTime m_dtHoverStart = DateTime.Now;
private DragEventArgs m_dea = null;
private Hashtable m_htDrives = new Hashtable();
// Waiting period (in seconds) before TreeView expands on drag drop
private int m_intExpandDelay = 4;
private IContextMenu3 m_IContextMenu3 = null;
private string m_strSearchNode = "";
private cTNode m_ctnDrop = null;
private cTNode m_ctnSearchNode = null;
// Events
public delegate void CommandKey(object sender, KeyEventArgs e);
public event CommandKey OnCommandKey;
private void FireCommandKey(KeyEventArgs e)
{
if (OnCommandKey != null)
{
OnCommandKey(this, e);
}
}
public delegate void MessageHandler(object sender, string Message, bool Error);
public event MessageHandler OnMessage;
private void FireMessageHandler(string Message, bool Error)
{
if (OnMessage != null)
{
OnMessage(this, Message, Error);
}
}
public delegate void PathChanged(object sender, string NewPath);
public event PathChanged OnPathChanged;
private void FirePathChanged(string NewPath)
{
if (m_blnRaisePathChange)
{
if (OnPathChanged != null)
{
OnPathChanged(this, NewPath);
}
}
}
public delegate void WaitCursor(object sender, bool Wait);
public event WaitCursor OnWaitCursor;
private void FireWaitCursor(bool Wait)
{
if (OnWaitCursor != null)
{
OnWaitCursor(this, Wait);
}
}
public cSysTreeView()
{
if (!this.DesignMode)
{
cSysTreeViewInitialise();
}
}
private void cSysTreeView_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
{
string strErr = "";
// Check for valid label
if (e.Label == null || e.Label == string.Empty || e.Label == "")
{
e.Node.EndEdit(true);
e.CancelEdit = true;
return;
}
// Stop any more editing
this.LabelEdit = false;
this.BeginUpdate();
e.Node.EndEdit(false);
e.CancelEdit = true;
// Try the rename
cTNode tNode = (cTNode)e.Node;
string strCurrentPath = tNode.Path;
if (cCommon.RenameFolder(strCurrentPath, e.Label, out strErr))
{
if (e.Node.Parent != null)
{
Application.DoEvents();
cTNode tnSelected = TVFindNodeByName((cTNode)e.Node.Parent, e.Label);
Application.DoEvents();
if (tnSelected != null)
this.SelectedNode = tnSelected;
}
}
else
{
FireMessageHandler(strErr, true);
}
// re-enable editing
this.LabelEdit = true;
this.EndUpdate();
}
private void cSysTreeView_AfterSelect(object sender, TreeViewEventArgs e)
{
cTNode tNode = (cTNode)e.Node;
m_blnDirectoryIsValid = tNode.CanDropOnNode;
FirePathChanged(tNode.Path);
}
void cSysTreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
// Delete children for refresh here - it may be 'Dummy'
FireWaitCursor(true);
FireMessageHandler("Updating Tree View...", false);
e.Node.Nodes.Clear();
TVEnumerateChildNodes((cTNode)e.Node);
FireMessageHandler("", false);
FireWaitCursor(false);
}
void cSysTreeView_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e)
{
// Is It re-Nameable?
if (!TVItemIsEditable((cTNode)e.Node))
{
FireMessageHandler("Can Only Rename Folders in TreeView (Use context menu for other items)", true);
e.CancelEdit = true;
}
}
void cSysTreeView_DragDrop(object sender, DragEventArgs e)
{
if (m_ctnDrop != null)
{
m_ctnDrop.BackColor = this.BackColor;
m_ctnDrop.ForeColor = this.ForeColor;
}
m_dea = e;
Point pos = this.PointToClient(MousePosition);
m_ctxTVDragDrop.Show(this, pos);
}
void cSysTreeView_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent(DataFormats.Text))
e.Effect = DragDropEffects.Move | DragDropEffects.Copy | DragDropEffects.Link;
else
e.Effect = DragDropEffects.None;
m_dtHoverStart = DateTime.Now;
}
void cSysTreeView_DragLeave(object sender, EventArgs e)
{
if (m_ctnDrop != null)
{
m_ctnDrop.BackColor = this.BackColor;
m_ctnDrop.ForeColor = this.ForeColor;
}
}
private void cSysTreeView_DragOver(object sender, DragEventArgs e)
{
// const int WM_HSCROLL = 0x0114;
// const int SB_LEFT = 6;
// const int SB_RIGHT = 7;
Point pt = this.PointToClient(new Point(e.X, e.Y));
cTNode ctnOver;
TimeSpan tsTemp;
// Make sure we are within the TreeView's boundary
if ((pt.Y > 0) && (pt.Y < this.ClientSize.Height) && (pt.X > 0) && (pt.X < this.ClientSize.Width))
{
ctnOver = GetCTNodeAt(pt);
// Make sure Node is valid
if (ctnOver != null)
{
if (!ctnOver.CanDropOnNode)
{
e.Effect = DragDropEffects.None;
}
else
{
e.Effect = DragDropEffects.Move | DragDropEffects.Copy | DragDropEffects.Link;
}
// Remove previous colouring only if we are over a new node (otherwise it flickers)
if (m_ctnDrop != null && m_ctnDrop != ctnOver)
{
m_ctnDrop.BackColor = this.BackColor;
m_ctnDrop.ForeColor = this.ForeColor;
}
// If we can drop here then Colour the node we are over
if (e.Effect != DragDropEffects.None)
{
ctnOver.BackColor = Color.LightGray;
ctnOver.ForeColor = Color.White;
}
// Save the drop node
m_ctnDrop = ctnOver;
// Hovering - do we need to expand
tsTemp = DateTime.Now.Subtract(m_dtHoverStart);
if (tsTemp.Seconds > m_intExpandDelay)
{
m_dtHoverStart = DateTime.Now;
if (ctnOver != null && e.Effect != DragDropEffects.None) // Don't expand non writeable drives/folders
ctnOver.Expand();
}
// Do we need to vertical scroll
int delta = this.Height - pt.Y;
if ((delta < this.Height / 2) && (delta > 0))
{
if (ctnOver.NextVisibleNode != null)
ctnOver.NextVisibleNode.EnsureVisible();
}
if ((delta > this.Height / 2) && (delta < this.Height))
{
if (ctnOver.PrevVisibleNode != null)
ctnOver.PrevVisibleNode.EnsureVisible();
}
// Horizontal Scrolling - if the TV is quite narrow you can't see where you are if it keeps scrolling right
// This makes it very jumpy though
// if(pt.X < this.Width / 3)
// cCommon.SendMessage(this.Handle, WM_HSCROLL, SB_LEFT, 0);
// else if(pt.X > this.Width / 3)
// cCommon.SendMessage(this.Handle, WM_HSCROLL, SB_RIGHT, 0);
}
else // Not over a Node
{
e.Effect = DragDropEffects.None;
}
}
}
private void cSysTreeView_ItemDrag(object sender, ItemDragEventArgs e)
{
cTNode tnDrag = (cTNode)e.Item;
if (tnDrag.ObjectType != ObjectType.FOLDER)
{
FireMessageHandler("Can Only Drag Folders", true);
return;
}
string[] strDrag = new string[1];
strDrag[0] = tnDrag.Path;
Rectangle rectTemp = tnDrag.Bounds;
Point pt = new Point(rectTemp.Left, rectTemp.Top);
pt = this.PointToScreen(pt);
POINT apipt;
apipt.x = pt.X;
apipt.y = pt.Y;
DataObject doDrag = cDropFiles.CopyFilesToClipboardForDragDrop(strDrag, apipt);
if (doDrag != null)
this.DoDragDrop(doDrag, DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
else
FireMessageHandler("Unable to copy to ClipBoard", true);
}
void cSysTreeView_KeyDown(object sender, KeyEventArgs e)
{
FireCommandKey(e);
}
private void cSysTreeView_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Right)
return;
Point pt = new Point(e.X, e.Y);
cTNode ctnClicked = GetCTNodeAt(e.X, e.Y);
if (ctnClicked != null)
{
pt = this.PointToScreen(pt);
string strPath = ctnClicked.Path;
if (strPath == SystemInformation.ComputerName)
{
IntPtr ipDrives = cCommon.GetSpecialFolderPIDL(CSIDL.CSIDL_DRIVES);
cContextMenu.ShowContextMenu(this.Handle, ipDrives, pt, ref m_IContextMenu3);
}
else if (strPath != "")
{
cContextMenu.ShowContextMenu(this.Handle, strPath, pt, ref m_IContextMenu3);
}
}
}
private void cSysTreeViewInitialise()
{
this.DoubleBuffered = true;
this.AfterLabelEdit += new NodeLabelEditEventHandler(cSysTreeView_AfterLabelEdit);
this.AfterSelect += new TreeViewEventHandler(cSysTreeView_AfterSelect);
this.BeforeLabelEdit += new NodeLabelEditEventHandler(cSysTreeView_BeforeLabelEdit);
this.BeforeExpand += new TreeViewCancelEventHandler(cSysTreeView_BeforeExpand);
this.DragDrop += new DragEventHandler(cSysTreeView_DragDrop);
this.DragEnter += new DragEventHandler(cSysTreeView_DragEnter);
this.DragLeave += new EventHandler(cSysTreeView_DragLeave);
this.DragOver += new DragEventHandler(cSysTreeView_DragOver);
this.ItemDrag += new ItemDragEventHandler(cSysTreeView_ItemDrag);
this.KeyDown += new KeyEventHandler(cSysTreeView_KeyDown);
this.MouseUp += new MouseEventHandler(cSysTreeView_MouseUp);
this.NodeMouseHover += new TreeNodeMouseHoverEventHandler(cSysTreeView_NodeMouseHover);
this.AllowDrop = true;
this.HideSelection = false;
this.LabelEdit = true;
TVCreateDragDropMenus();
}
void cSysTreeView_NodeMouseHover(object sender, TreeNodeMouseHoverEventArgs e)
{
bool blnChild = false;
cTNode tNodeHover = (cTNode)e.Node;
if (tNodeHover.DriveType != DriveType.CDRom && tNodeHover.DriveType != DriveType.Removable)
return;
cJDriveInfo jDriveInfo = new cJDriveInfo(tNodeHover.Path);
cTNode tnSelected = this.SelectedCTNode;
// This is not fool-proof, since some removable drives don't have a Volume Label
if (jDriveInfo.VolumeLabel != tNodeHover.VolumeLabel)
{
blnChild = tnSelected.IsChildOf(tNodeHover);
tNodeHover.UpdateDriveData(jDriveInfo);
if (blnChild)
{
this.SelectedNode = tNodeHover;
FirePathChanged(tNodeHover.Path);
}
}
}
private void ctxTVCancelAction_Click(object sender, System.EventArgs e)
{
m_dea = null;
}
private void ctxTVCreateShortcut_Click(object sender, System.EventArgs e)
{
// Dropping objects to create shortcuts
if (m_ctnDrop == null)
return;
TVCopyOrMoveDroppedFiles(FileOps.FO_JSHORTCUT);
}
private void ctxTVDropCopy_Click(object sender, System.EventArgs e)
{
// Dropping objects to be copied
if (m_ctnDrop == null)
return;
TVCopyOrMoveDroppedFiles(FileOps.FO_COPY);
}
private void ctxTVDropMove_Click(object sender, System.EventArgs e)
{
// Dropping objects to be moved
if (m_ctnDrop == null)
return;
TVCopyOrMoveDroppedFiles(FileOps.FO_MOVE);
}
private cTNode GetCTNodeAt(Point pt)
{
return (cTNode)this.GetNodeAt(pt);
}
private cTNode GetCTNodeAt(int X, int Y)
{
return (cTNode)this.GetNodeAt(X, Y);
}
protected override void OnHandleCreated(EventArgs e)
{
// Need a handle to set the image list
base.OnHandleCreated(e);
cCommon.SetSmallSystemImageList(this);
TVInsertRootNode();
}
private cTNode SelectedCTNode
{
get
{
if (this.SelectedNode == null)
return null;
return (cTNode)this.SelectedNode;
}
}
private void TVAddDrives(ref cTNode tnParent)
{
cTNode tnNew;
cJDriveInfo[] jDriveInfo = cJDriveInfoCollection.DriveInfoArray;
m_htDrives.Clear();
foreach (cJDriveInfo currentDrive in jDriveInfo)
{
tnNew = new cTNode(currentDrive);
m_htDrives.Add(tnNew.Path, tnNew);
tnParent.Nodes.Add(tnNew);
TVSetIcon(tnNew);
}
}
private void TVAddFolders(string strPath, ref cTNode tnParent)
{
DirectoryInfo diTop = new DirectoryInfo(strPath);
cTNode tnNew;
if (diTop.Exists)
{
DirectoryInfo[] diSubs = diTop.GetDirectories();
cCommon.SortDirectoriesAlpha(ref diSubs);
foreach (DirectoryInfo diCurrent in diSubs)
{
tnNew = new cTNode(diCurrent);
tnParent.Nodes.Add(tnNew);
TVSetIcon(tnNew);
if (m_strSearchNode != "" && tnNew.Text == m_strSearchNode)
m_ctnSearchNode = tnNew;
}
}
}
private bool TVCopyOrMoveDroppedFiles(FileOps foAction)
{
bool blnResult = true;
DirectoryInfo diDestination;
if (m_ctnDrop == null)
return false;
string strDestDir = m_ctnDrop.Path;
if (strDestDir == "")
return false;
try
{
diDestination = new DirectoryInfo(strDestDir);
if (diDestination.Exists)
{
strDestDir = diDestination.FullName;
if (foAction == FileOps.FO_JSHORTCUT)
cCommon.CreateShortcuts(m_dea, strDestDir);
else
cCommon.DropFiles(this.Handle, m_dea, foAction, strDestDir);
if (m_ctnDrop.Parent != null)
{
m_ctnDrop.Parent.Collapse();
m_ctnDrop.Parent.Expand();
}
}
}
catch
{
blnResult = false;
}
return blnResult;
}
private void TVCreateDragDropMenus()
{
MenuItem[] mDragDrop = {new MenuItem("Copy", new EventHandler(ctxTVDropCopy_Click)),
new MenuItem("Move", new EventHandler(ctxTVDropMove_Click)),
new MenuItem("Create Shortcut", new EventHandler(ctxTVCreateShortcut_Click)),
new MenuItem("Cancel", new EventHandler(ctxTVCancelAction_Click))
};
m_ctxTVDragDrop = new ContextMenu(mDragDrop);
}
private bool TVCreateFolder()
{
if (!this.SelectedCTNode.CanDropOnNode) // Same requirements as dropping
{
FireMessageHandler("Cannot Create New Folder Here", true);
return false;
}
cTNode tnSelected = this.SelectedCTNode;
string strParentFolderPath = tnSelected.Path;
if (strParentFolderPath == null || strParentFolderPath == "")
{
FireMessageHandler("No Parent Folder Selected", true);
return false;
}
string strErr = "";
cInputBox frmNewFolder = new cInputBox("Create New Folder", "Enter name of folder to create in " + strParentFolderPath, "");
if (frmNewFolder.ShowDialog() != DialogResult.OK)
{
strErr = "Action Cancelled";
return false;
}
string strNewFolder = frmNewFolder.Result;
bool blnOK = cCommon.CreateFolder(strParentFolderPath, strNewFolder, out strErr);
bool blnExpand = false;
if (!blnOK)
{
FireMessageHandler(strErr, true);
return false;
}
else
{
blnExpand = this.SelectedNode.IsExpanded;
this.SelectedNode.Collapse();
cTNode tnExpand = this.SelectedCTNode;
TVEnumerateChildNodes(tnExpand);
this.SelectedNode.Expand();
if (!blnExpand)
this.SelectedNode.Collapse();
return true;
}
}
private bool TVDeleteFolder()
{
if (!TVItemIsEditable(this.SelectedCTNode))
{
FireMessageHandler("Cannot Delete That Node!", true);
return false;
}
cTNode tnParent = null;
string strPath = this.SelectedCTNode.Path;
try
{
tnParent = (cTNode)this.SelectedNode.Parent;
}
catch
{
tnParent = (cTNode)this.Nodes[0];
}
string strFileList = strPath + '\0' + '\0';
if (cCommon.SendToRecycler(this.Handle, strFileList))
{
this.SelectedNode = tnParent;
tnParent.Collapse();
Application.DoEvents();
tnParent.Expand();
return true;
}
else
{
FireMessageHandler("Unable to Delete Folder", true);
return false;
}
}
private void TVEnumerateChildNodes(cTNode tnParent)
{
if (tnParent.Path == SystemInformation.ComputerName)
{
TVAddDrives(ref tnParent);
}
else
{
TVAddFolders(tnParent.Path, ref tnParent);
}
}
private cTNode TVFindNodeByName(cTNode tnTop, string strName)
{
// Iterate Through Nodes to Find the one we want
// It may be a new folder or node may be collapsed
// Add Dummy Node to ensure expand works
tnTop.Collapse();
tnTop.Nodes.Add("Dummy");
// Expand Event will delete all nodes and re-add them up to date
// If m_strSearchNode != "" then m_ctnSearchNode will be set in TVAddFolders
m_strSearchNode = strName;
tnTop.Expand();
m_strSearchNode = "";
return m_ctnSearchNode;
}
private void TVInsertRootNode()
{
this.Nodes.Clear();
cTNode tnNew = new cTNode(ObjectType.COMPUTER);
this.Nodes.Add(tnNew);
TVSetIcon(tnNew);
tnNew.Nodes.Add("Dummy");
if (m_blnSelectOnCreate)
{
this.SelectedNode = tnNew;
if (m_blnExpandOnCreate)
tnNew.Expand();
}
}
private bool TVItemIsEditable(cTNode ctnEdit)
{
if (ctnEdit == null)
return false;
return ctnEdit.IsEditable;
}
private void TVSetIcon(cTNode tNode)
{
// Needed to get the correct Icon Mask
TVITEM TV_ITEM = new TVITEM();
TV_ITEM.hItem = tNode.Handle;
TV_ITEM.mask = tNode.Mask;
TV_ITEM.state = tNode.State;
TV_ITEM.stateMask = tNode.StateMask;
TV_ITEM.iImage = tNode.ImageIndex;
TV_ITEM.iSelectedImage = tNode.SelectedImageIndex;
// Set the icon with its mask(s)
cCommon.SendMessage(this.Handle, TVM_SETITEMW, 0, ref TV_ITEM);
}
internal void TVSetShowPath(string strPath)
{
m_blnRaisePathChange = false;
cTNode tnNew = TVSetToPath(strPath);
if (tnNew != null)
{
this.SelectedNode = tnNew;
}
m_blnRaisePathChange = true;
}
private cTNode TVSetToPath(string strFQPath)
{
// This is a potential bottle neck so any way of speeding it up will be beneficial
// strFQPath is the Path required (i.e. C:\ not DFI_BOOT (C:))
if (strFQPath == "")
return null;
// Is it the root Node?
if (strFQPath == SystemInformation.ComputerName)
return (cTNode)this.Nodes[0];
this.BeginUpdate();
int intCount;
string[] strPaths = strFQPath.Split('\\');
int intPathLen = strPaths.Length;
// strPaths[0] needs to be of form 'E:\'
if (!strPaths[0].EndsWith("\\"))
strPaths[0] += "\\";
// Find the Node for the Drive from the HashTable
cTNode ctnFind = (cTNode)m_htDrives[strPaths[0]];
if (ctnFind != null)
{
for (intCount = 1; intCount < intPathLen; intCount++)
{
if (strPaths[intCount] != "")
ctnFind = TVFindNodeByName(ctnFind, strPaths[intCount]);
}
}
this.EndUpdate();
return ctnFind;
}
protected override void WndProc(ref Message message)
{
// Required for the Owner Draw items on the Explorer Context Menu (Open With, Send To)
const int WM_DRAWITEM = 0x002B;
const int WM_MEASUREITEM = 0x002C;
const int WM_INITMENUPOPUP = 0x0117;
const int WM_MENUCHAR = 0x0120;
IntPtr lpResult = IntPtr.Zero;
switch (message.Msg)
{
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_INITMENUPOPUP:
if (m_IContextMenu3 != null)
m_IContextMenu3.HandleMenuMsg((uint)message.Msg, message.WParam, message.LParam);
break;
case WM_MENUCHAR:
if (m_IContextMenu3 != null)
m_IContextMenu3.HandleMenuMsg2((uint)message.Msg, message.WParam, message.LParam, ref lpResult);
break;
default:
base.WndProc(ref message);
break;
}
}
#region Public Properties / Functions
// ************************************* Public Properties / Functions
/// <summary>
/// Create A New Folder In The Currently Selected Directory
/// </summary>
/// <returns></returns>
public bool CreateFolder()
{
return TVCreateFolder();
}
/// <summary>
/// Gets/Sets The CurrentPath For The TreeView
/// </summary>
public string CurrentPath
{
get
{
cTNode tNode = SelectedCTNode;
if (tNode != null)
return tNode.Path;
else
return "";
}
set
{
cTNode tnNew = TVSetToPath(value);
if (tnNew != null)
this.SelectedNode = tnNew;
}
}
/// <summary>
/// Deletes The Currently Selected Folder And Its Sub-Folders
/// </summary>
/// <returns></returns>
public bool DeleteSelectedObjects()
{
return TVDeleteFolder();
}
/// <summary>
/// Returns True If The Currently Selected Object Is A Folder or a Drive
/// </summary>
public bool DirectoryIsValid
{
get
{
return m_blnDirectoryIsValid;
}
}
/// <summary>
/// Puts The Selected Node In Edit Node If It Is Editable
/// </summary>
public void EditItem()
{
if (this.SelectedNode != null)
this.SelectedNode.BeginEdit();
}
/// <summary>
/// The delay before the node expands in a Drag and Drop operation
/// </summary>
public int ExpandDelay
{
get
{
return m_intExpandDelay;
}
set
{
m_intExpandDelay = value;
}
}
public bool ExpandOnCreate
{
get
{
return this.m_blnExpandOnCreate;
}
set
{
this.m_blnExpandOnCreate = value;
}
}
/// <summary>
/// Refreshes The Tree View Drive Data
/// </summary>
public void RefreshDriveData()
{
cJDriveInfoCollection.RefreshDriveData();
this.Nodes[0].Collapse();
this.Nodes[0].Expand();
}
public bool SelectOnCreate
{
get
{
return this.m_blnSelectOnCreate;
}
set
{
this.m_blnSelectOnCreate = value;
}
}
/// <summary>
/// Returns true if the selected item can be edited, false otherwise
/// </summary>
public bool TVSelectedItemIsEditable
{
get
{
return TVItemIsEditable(this.SelectedCTNode);
}
}
/// <summary>
/// Returns the Selected Node as a cTNode
/// </summary>
public cTNode TVSelectedNode
{
get
{
return this.SelectedCTNode;
}
}
/// <summary>
/// Returns The Name Of The Currently Selected Node
/// </summary>
public string SelectedNodeName
{
get
{
return this.SelectedNode.Text;
}
}
// Set the path without firing events
public string ShowPath
{
set
{
TVSetShowPath(value);
}
}
#endregion
}
}