mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-25 06:16:37 +01:00
fb8434819d
Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
904 lines
32 KiB
C#
904 lines
32 KiB
C#
/* Copyright (c) Citrix Systems, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms,
|
|
* with or without modification, are permitted provided
|
|
* that the following conditions are met:
|
|
*
|
|
* * Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
|
|
using XenAdmin.Commands;
|
|
using XenAdmin.Model;
|
|
using XenAdmin.Network;
|
|
using XenAdmin.XenSearch;
|
|
using XenCenterLib;
|
|
using XenAPI;
|
|
|
|
namespace XenAdmin.Controls.MainWindowControls
|
|
{
|
|
public partial class NavigationView : UserControl
|
|
{
|
|
#region Private fields
|
|
|
|
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
private readonly SelectionManager selectionManager = new SelectionManager();
|
|
private readonly MainWindowTreeBuilder treeBuilder;
|
|
private readonly UpdateManager treeViewUpdateManager = new UpdateManager(30 * 1000);
|
|
|
|
private VirtualTreeNode _highlightedDragTarget;
|
|
private int ignoreRefreshTreeView;
|
|
private bool calledRefreshTreeView;
|
|
private bool inMajorChange;
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
[Browsable(true)]
|
|
public event Action TreeViewSelectionChanged;
|
|
|
|
[Browsable(true)]
|
|
public event Action TreeNodeBeforeSelected;
|
|
|
|
[Browsable(true)]
|
|
public event Action TreeNodeClicked;
|
|
|
|
[Browsable(true)]
|
|
public event Action TreeNodeRightClicked;
|
|
|
|
[Browsable(true)]
|
|
public event Action TreeViewRefreshed;
|
|
|
|
[Browsable(true)]
|
|
public event Action TreeViewRefreshSuspended;
|
|
|
|
[Browsable(true)]
|
|
public event Action TreeViewRefreshResumed;
|
|
|
|
internal event Action<string> DragDropCommandActivated;
|
|
|
|
#endregion
|
|
|
|
public NavigationView()
|
|
{
|
|
InitializeComponent();
|
|
|
|
panel1.BackColor = Program.TitleBarBorderColor;
|
|
|
|
treeView.ImageList = Images.ImageList16;
|
|
if (treeView.ItemHeight < 18)
|
|
treeView.ItemHeight = 18;
|
|
//otherwise it's too close together on XP and the icons crash into each other
|
|
|
|
VirtualTreeNode n = new VirtualTreeNode(Messages.XENCENTER);
|
|
treeView.Nodes.Add(n);
|
|
treeView.SelectedNode = treeView.Nodes[0];
|
|
|
|
treeBuilder = new MainWindowTreeBuilder(treeView);
|
|
treeViewUpdateManager.Update += treeViewUpdateManager_Update;
|
|
}
|
|
|
|
#region Accessors
|
|
|
|
public NavigationPane.NavigationMode NavigationMode { get; set; }
|
|
|
|
public Search CurrentSearch { get; set; }
|
|
|
|
public bool InSearchMode { get; set; }
|
|
|
|
internal SelectionBroadcaster SelectionManager
|
|
{
|
|
get { return selectionManager; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Connection
|
|
|
|
public void XenConnectionCollectionChanged(CollectionChangeEventArgs e)
|
|
{
|
|
IXenConnection connection = e.Element as IXenConnection;
|
|
|
|
if (e.Action == CollectionChangeAction.Add)
|
|
{
|
|
if (connection == null)
|
|
return;
|
|
|
|
connection.BeforeMajorChange += Connection_BeforeMajorChange;
|
|
connection.AfterMajorChange += Connection_AfterMajorChange;
|
|
}
|
|
else if (e.Action == CollectionChangeAction.Remove)
|
|
{
|
|
var range = new List<IXenConnection>();
|
|
if (connection != null)
|
|
{
|
|
range.Add(connection);
|
|
}
|
|
else
|
|
{
|
|
var r = e.Element as List<IXenConnection>;
|
|
if (r != null)
|
|
range = r;
|
|
else
|
|
return;
|
|
}
|
|
|
|
foreach (var con in range)
|
|
{
|
|
con.BeforeMajorChange -= Connection_BeforeMajorChange;
|
|
con.AfterMajorChange -= Connection_AfterMajorChange;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Connection_BeforeMajorChange(object sender, ConnectionMajorChangeEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
Program.Invoke(this, () =>
|
|
{
|
|
if (!e.Background)
|
|
{
|
|
if (inMajorChange)
|
|
return;
|
|
inMajorChange = true;
|
|
}
|
|
SuspendRefreshTreeView();
|
|
});
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error(exn, exn);
|
|
// Can do nothing more about this.
|
|
}
|
|
}
|
|
|
|
private void Connection_AfterMajorChange(object sender, ConnectionMajorChangeEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
Program.Invoke(this, () =>
|
|
{
|
|
ResumeRefreshTreeView();
|
|
|
|
if (!e.Background)
|
|
inMajorChange = false;
|
|
});
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error(exn, exn);
|
|
// Can do nothing more about this.
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region SearchBox
|
|
|
|
public void ResetSeachBox()
|
|
{
|
|
searchTextBox.Reset();
|
|
}
|
|
|
|
private void searchTextBox_TextChanged(object sender, EventArgs e)
|
|
{
|
|
RequestRefreshTreeView();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TreeView
|
|
|
|
private void treeViewUpdateManager_Update(object sender, EventArgs e)
|
|
{
|
|
Program.AssertOffEventThread();
|
|
RefreshTreeView();
|
|
}
|
|
|
|
|
|
public void MajorChange(MethodInvoker f)
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
if (inMajorChange)
|
|
{
|
|
f();
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
inMajorChange = true;
|
|
SuspendRefreshTreeView();
|
|
f();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.Debug("Exception thrown by target of MajorChange.", e);
|
|
log.Debug(e, e);
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
ResumeRefreshTreeView();
|
|
inMajorChange = false;
|
|
}
|
|
}
|
|
|
|
public void SaveAndRestoreTreeViewFocus(MethodInvoker f)
|
|
{
|
|
bool treeViewHasFocus = treeView.ContainsFocus;
|
|
searchTextBox.SaveState();
|
|
|
|
f();
|
|
|
|
if (!InSearchMode && treeViewHasFocus)
|
|
FocusTreeView();
|
|
|
|
searchTextBox.RestoreState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Selects the specified object in the treeview.
|
|
/// </summary>
|
|
/// <param name="o">The object to be selected.</param>
|
|
/// <param name="expand">A value specifying whether the node should be expanded when it's found.
|
|
/// If false, the node is left in the state it's found in.</param>
|
|
/// <returns>A value indicating whether selection was successful.</returns>
|
|
public bool SelectObject(IXenObject o, bool expand)
|
|
{
|
|
bool cancelled = false;
|
|
if (treeView.Nodes.Count == 0)
|
|
return false;
|
|
|
|
bool success = treeView.SelectObject(o, treeView.Nodes[0], expand, ref cancelled);
|
|
|
|
if (!success && !cancelled && searchTextBox.Text.Length > 0)
|
|
{
|
|
// if the node could not be found and the node *is* selectable then it means that
|
|
// node isn't visible with the current search. So clear the search and try and
|
|
// select the object again.
|
|
|
|
// clear search.
|
|
ResetSeachBox();
|
|
|
|
// update the treeview
|
|
RefreshTreeView();
|
|
|
|
// and try again.
|
|
return treeView.SelectObject(o, treeView.Nodes[0], expand, ref cancelled);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
public bool TryToSelectNewNode(Predicate<object> tagMatch, bool selectNode, bool expandNode, bool ensureNodeVisible)
|
|
{
|
|
return treeView.TryToSelectNewNode(tagMatch, selectNode, expandNode, ensureNodeVisible);
|
|
}
|
|
|
|
public void EditSelectedNode()
|
|
{
|
|
SuspendRefreshTreeView();
|
|
treeView.LabelEdit = true;
|
|
treeView.SelectedNode.BeginEdit();
|
|
}
|
|
|
|
public void FocusTreeView()
|
|
{
|
|
treeView.Focus();
|
|
}
|
|
|
|
public void ForceTreeViewSelectionsChanged()
|
|
{
|
|
treeView.ForceSelectionsChanged();
|
|
}
|
|
|
|
public void RequestRefreshTreeView()
|
|
{
|
|
Program.BeginInvoke(Program.MainWindow, treeViewUpdateManager.RequestUpdate);
|
|
}
|
|
|
|
private void SuspendRefreshTreeView()
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
if (ignoreRefreshTreeView == 0)
|
|
calledRefreshTreeView = false;
|
|
ignoreRefreshTreeView++;
|
|
|
|
if (TreeViewRefreshSuspended != null)
|
|
TreeViewRefreshSuspended();
|
|
}
|
|
|
|
private void ResumeRefreshTreeView()
|
|
{
|
|
Program.AssertOnEventThread();
|
|
|
|
ignoreRefreshTreeView--;
|
|
if (ignoreRefreshTreeView == 0 && calledRefreshTreeView)
|
|
RequestRefreshTreeView();
|
|
|
|
if (TreeViewRefreshResumed != null)
|
|
TreeViewRefreshResumed();
|
|
}
|
|
|
|
// Normally you should call RequestRefreshTreeView() instead of this function
|
|
public void RefreshTreeView()
|
|
{
|
|
if (Disposing || IsDisposed || Program.Exiting)
|
|
return;
|
|
|
|
Program.Invoke(Program.MainWindow, () =>
|
|
{
|
|
if (ignoreRefreshTreeView > 0)
|
|
{
|
|
calledRefreshTreeView = true;
|
|
return;
|
|
}
|
|
|
|
ignoreRefreshTreeView++; // Some events can be ignored while rebuilding the tree
|
|
|
|
try
|
|
{
|
|
var newRootNode = treeBuilder.CreateNewRootNode(CurrentSearch.AddFullTextFilter(searchTextBox.Text), NavigationMode);
|
|
treeBuilder.RefreshTreeView(newRootNode, searchTextBox.Text, NavigationMode);
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error(exn, exn);
|
|
#if DEBUG
|
|
if (Debugger.IsAttached)
|
|
throw;
|
|
#endif
|
|
}
|
|
finally
|
|
{
|
|
ignoreRefreshTreeView--;
|
|
}
|
|
|
|
if (TreeViewRefreshed != null)
|
|
TreeViewRefreshed();
|
|
});
|
|
}
|
|
|
|
private static Pool PoolAncestorOfNode(VirtualTreeNode node)
|
|
{
|
|
while (node != null)
|
|
{
|
|
var pool = node.Tag as Pool;
|
|
if (pool != null)
|
|
return pool;
|
|
|
|
node = node.Parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static Host HostAncestorOfNode(VirtualTreeNode node)
|
|
{
|
|
while (node != null)
|
|
{
|
|
var host = node.Tag as Host;
|
|
if (host != null)
|
|
return host;
|
|
|
|
node = node.Parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static GroupingTag GroupAncestorOfNode(VirtualTreeNode node)
|
|
{
|
|
while (node != null)
|
|
{
|
|
var group = node.Tag as GroupingTag;
|
|
if (group != null)
|
|
return group;
|
|
|
|
node = node.Parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private bool CanDrag()
|
|
{
|
|
if (NavigationMode == NavigationPane.NavigationMode.Infrastructure
|
|
|| NavigationMode == NavigationPane.NavigationMode.SavedSearch)
|
|
{
|
|
return Program.MainWindow.SelectionManager.Selection.AllItemsAre<Host>()
|
|
|| Program.MainWindow.SelectionManager.Selection.AllItemsAre<VM>(vm => !vm.is_a_template);
|
|
}
|
|
|
|
foreach (SelectedItem item in Program.MainWindow.SelectionManager.Selection)
|
|
{
|
|
if (item.XenObject == null || item.Connection == null || !item.Connection.IsConnected)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private List<DragDropCommand> GetDragDropCommands(VirtualTreeNode targetNode, IDataObject dragData)
|
|
{
|
|
List<DragDropCommand> commands = new List<DragDropCommand>();
|
|
commands.Add(new DragDropAddHostToPoolCommand(Program.MainWindow, targetNode, dragData));
|
|
commands.Add(new DragDropMigrateVMCommand(Program.MainWindow, targetNode, dragData));
|
|
commands.Add(new DragDropRemoveHostFromPoolCommand(Program.MainWindow, targetNode, dragData));
|
|
commands.Add(new DragDropCrossPoolMoveHaltedVMCommand(Program.MainWindow, targetNode, dragData));
|
|
|
|
if (NavigationMode == NavigationPane.NavigationMode.Tags
|
|
|| NavigationMode == NavigationPane.NavigationMode.Folders
|
|
|| NavigationMode == NavigationPane.NavigationMode.CustomFields
|
|
|| NavigationMode == NavigationPane.NavigationMode.vApps)
|
|
{
|
|
commands.Add(new DragDropTagCommand(Program.MainWindow, targetNode, dragData));
|
|
commands.Add(new DragDropIntoFolderCommand(Program.MainWindow, targetNode, dragData));
|
|
}
|
|
|
|
return commands;
|
|
}
|
|
|
|
private void ClearHighlightedDragTarget()
|
|
{
|
|
if (_highlightedDragTarget != null)
|
|
{
|
|
_highlightedDragTarget.BackColor = treeView.BackColor;
|
|
_highlightedDragTarget.ForeColor = treeView.ForeColor;
|
|
_highlightedDragTarget = null;
|
|
treeBuilder.HighlightedDragTarget = null;
|
|
|
|
if (DragDropCommandActivated != null)
|
|
DragDropCommandActivated(null);
|
|
}
|
|
}
|
|
|
|
private void HandleNodeRightClick(VirtualTreeNodeMouseClickEventArgs e)
|
|
{
|
|
if (e.Button != MouseButtons.Right)
|
|
return;
|
|
|
|
if (treeView.SelectedNodes.Count == 0)
|
|
{
|
|
treeView.SelectedNode = e.Node;
|
|
|
|
if (treeView.SelectedNode != e.Node) // if node is unselectable in TreeView_BeforeSelect: CA-26615
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (TreeNodeRightClicked != null)
|
|
TreeNodeRightClicked();
|
|
|
|
TreeContextMenu.Items.Clear();
|
|
|
|
if (e.Node == treeView.Nodes[0] && treeView.SelectedNodes.Count == 1)
|
|
{
|
|
if (e.Node.Tag == null)// XenCenter (top most)
|
|
{
|
|
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(new AddHostCommand(Program.MainWindow), true));
|
|
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(new NewPoolCommand(Program.MainWindow, new SelectedItem[0]), true));
|
|
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(new ConnectAllHostsCommand(Program.MainWindow), true));
|
|
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(new DisconnectAllHostsCommand(Program.MainWindow), true));
|
|
}
|
|
else
|
|
{
|
|
var groupingTag = e.Node.Tag as GroupingTag;
|
|
if (groupingTag != null && groupingTag.Grouping as OrganizationViewFolders != null)
|
|
TreeContextMenu.Items.Add(new CommandToolStripMenuItem(
|
|
new NewFolderCommand(Program.MainWindow, new[] { new SelectedItem(groupingTag, e.Node) }),
|
|
true));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TreeContextMenu.Items.AddRange(Program.MainWindow.ContextMenuBuilder.Build(Program.MainWindow.SelectionManager.Selection));
|
|
}
|
|
|
|
int insertIndex = TreeContextMenu.Items.Count;
|
|
|
|
if (TreeContextMenu.Items.Count > 0)
|
|
{
|
|
CommandToolStripMenuItem lastItem = TreeContextMenu.Items[TreeContextMenu.Items.Count - 1] as CommandToolStripMenuItem;
|
|
|
|
if (lastItem != null && lastItem.Command is PropertiesCommand)
|
|
insertIndex--;
|
|
}
|
|
|
|
AddExpandCollapseItems(insertIndex, treeView.SelectedNodes);
|
|
AddOrgViewItems(insertIndex, treeView.SelectedNodes);
|
|
|
|
if (TreeContextMenu.Items.Count > 0)
|
|
{
|
|
TreeContextMenu.Show(treeView.PointToScreen(Point.Empty) + (Size)treeView.LastMouseDownEventPosition);
|
|
}
|
|
}
|
|
|
|
private void AddExpandCollapseItems(int insertIndex, IList<VirtualTreeNode> nodes)
|
|
{
|
|
if (nodes.Count == 1 && nodes[0].Nodes.Count == 0)
|
|
return;
|
|
|
|
Command cmd = new CollapseChildTreeNodesCommand(Program.MainWindow, nodes);
|
|
if (cmd.CanExecute())
|
|
TreeContextMenu.Items.Insert(insertIndex, new CommandToolStripMenuItem(cmd, true));
|
|
|
|
cmd = new ExpandTreeNodesCommand(Program.MainWindow, nodes);
|
|
if (cmd.CanExecute())
|
|
TreeContextMenu.Items.Insert(insertIndex, new CommandToolStripMenuItem(cmd, true));
|
|
}
|
|
|
|
private void AddOrgViewItems(int insertIndex, IList<VirtualTreeNode> nodes)
|
|
{
|
|
if (NavigationMode == NavigationPane.NavigationMode.Infrastructure
|
|
|| NavigationMode == NavigationPane.NavigationMode.SavedSearch
|
|
|| NavigationMode == NavigationPane.NavigationMode.Notifications
|
|
|| nodes.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Command cmd = new RemoveFromFolderCommand(Program.MainWindow, nodes);
|
|
|
|
if (cmd.CanExecute())
|
|
TreeContextMenu.Items.Insert(insertIndex, new CommandToolStripMenuItem(cmd, true));
|
|
|
|
cmd = new UntagCommand(Program.MainWindow, nodes);
|
|
|
|
if (cmd.CanExecute())
|
|
TreeContextMenu.Items.Insert(insertIndex, new CommandToolStripMenuItem(cmd, true));
|
|
}
|
|
|
|
|
|
private void treeView_AfterLabelEdit(object sender, VirtualNodeLabelEditEventArgs e)
|
|
{
|
|
VirtualTreeNode node = e.Node;
|
|
treeView.LabelEdit = false;
|
|
Folder folder = e.Node.Tag as Folder;
|
|
GroupingTag groupingTag = e.Node.Tag as GroupingTag;
|
|
Command command = null;
|
|
object newTag = null;
|
|
|
|
EventHandler<RenameCompletedEventArgs> completed = delegate(object s, RenameCompletedEventArgs ee)
|
|
{
|
|
Program.Invoke(this, delegate
|
|
{
|
|
ResumeRefreshTreeView();
|
|
|
|
if (ee.Success)
|
|
{
|
|
// the new tag is updated on the node here. This ensures that the node stays selected
|
|
// when the treeview is refreshed. If you don't set the tag like this, the treeview refresh code notices
|
|
// that the tags are different and selects the parent node instead of this node.
|
|
|
|
// if the command fails for some reason, the refresh code will correctly revert the tag back to the original.
|
|
node.Tag = newTag;
|
|
RefreshTreeView();
|
|
|
|
// since the selected node doesn't actually change, then a selectionsChanged message doesn't get fired
|
|
// and the selection doesn't get updated to be the new tag/folder. Do it manually here instead.
|
|
treeView_SelectionsChanged(treeView, EventArgs.Empty);
|
|
}
|
|
});
|
|
};
|
|
|
|
if (!string.IsNullOrEmpty(e.Label))
|
|
{
|
|
if (folder != null)
|
|
{
|
|
RenameFolderCommand cmd = new RenameFolderCommand(Program.MainWindow, folder, e.Label);
|
|
command = cmd;
|
|
cmd.Completed += completed;
|
|
newTag = new Folder(null, e.Label);
|
|
}
|
|
else if (groupingTag != null)
|
|
{
|
|
RenameTagCommand cmd = new RenameTagCommand(Program.MainWindow, groupingTag.Group.ToString(), e.Label);
|
|
command = cmd;
|
|
cmd.Completed += completed;
|
|
newTag = new GroupingTag(groupingTag.Grouping, groupingTag.Parent, e.Label);
|
|
}
|
|
}
|
|
|
|
if (command != null && command.CanExecute())
|
|
{
|
|
command.Execute();
|
|
}
|
|
else
|
|
{
|
|
ResumeRefreshTreeView();
|
|
e.CancelEdit = true;
|
|
}
|
|
}
|
|
|
|
private void treeView_BeforeSelect(object sender, VirtualTreeViewCancelEventArgs e)
|
|
{
|
|
if (e.Node == null)
|
|
return;
|
|
|
|
if (!treeView.CanSelectNode(e.Node))
|
|
{
|
|
e.Cancel = true;
|
|
return;
|
|
}
|
|
|
|
if (TreeNodeBeforeSelected != null)
|
|
TreeNodeBeforeSelected();
|
|
}
|
|
|
|
private void treeView_ItemDrag(object sender, VirtualTreeViewItemDragEventArgs e)
|
|
{
|
|
foreach (VirtualTreeNode node in e.Nodes)
|
|
{
|
|
if (node == null || node.TreeView == null)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// select the node if it isn't already selected
|
|
if (e.Nodes.Count == 1 && treeView.SelectedNode != e.Nodes[0])
|
|
{
|
|
treeView.SelectedNode = e.Nodes[0];
|
|
}
|
|
|
|
if (CanDrag())
|
|
{
|
|
DoDragDrop(new List<VirtualTreeNode>(e.Nodes).ToArray(), DragDropEffects.Move);
|
|
}
|
|
}
|
|
|
|
private void treeView_DragDrop(object sender, DragEventArgs e)
|
|
{
|
|
ClearHighlightedDragTarget();
|
|
|
|
VirtualTreeNode targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
|
|
|
|
foreach (DragDropCommand cmd in GetDragDropCommands(targetNode, e.Data))
|
|
{
|
|
if (cmd.CanExecute())
|
|
{
|
|
cmd.Execute();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void treeView_DragEnter(object sender, DragEventArgs e)
|
|
{
|
|
e.Effect = DragDropEffects.None;
|
|
VirtualTreeNode targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
|
|
foreach (DragDropCommand cmd in GetDragDropCommands(targetNode, e.Data))
|
|
{
|
|
if (cmd.CanExecute())
|
|
{
|
|
e.Effect = DragDropEffects.Move;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void treeView_DragLeave(object sender, EventArgs e)
|
|
{
|
|
ClearHighlightedDragTarget();
|
|
}
|
|
|
|
private void treeView_DragOver(object sender, DragEventArgs e)
|
|
{
|
|
// CA-11457: When dragging in resource tree, view doesn't scroll
|
|
// http://www.fmsinc.com/freE/NewTips/NET/NETtip21.asp
|
|
|
|
const int SCROLL_REGION = 20;
|
|
|
|
Point pt = treeView.PointToClient(Cursor.Position);
|
|
VirtualTreeNode targetNode = treeView.GetNodeAt(treeView.PointToClient(new Point(e.X, e.Y)));
|
|
|
|
if ((pt.Y + SCROLL_REGION) > treeView.Height)
|
|
{
|
|
Win32.SendMessage(treeView.Handle, Win32.WM_VSCROLL, new IntPtr(1), IntPtr.Zero);
|
|
}
|
|
else if (pt.Y < SCROLL_REGION)
|
|
{
|
|
Win32.SendMessage(treeView.Handle, Win32.WM_VSCROLL, IntPtr.Zero, IntPtr.Zero);
|
|
}
|
|
|
|
VirtualTreeNode targetToHighlight = null;
|
|
|
|
string statusBarText = null;
|
|
foreach (DragDropCommand cmd in GetDragDropCommands(targetNode, e.Data))
|
|
{
|
|
if (cmd.CanExecute())
|
|
targetToHighlight = cmd.HighlightNode;
|
|
|
|
if (cmd.StatusBarText != null)
|
|
statusBarText = cmd.StatusBarText;
|
|
}
|
|
|
|
if (DragDropCommandActivated != null)
|
|
DragDropCommandActivated(statusBarText);
|
|
|
|
if (targetToHighlight != null)
|
|
{
|
|
if (_highlightedDragTarget != targetToHighlight)
|
|
{
|
|
ClearHighlightedDragTarget();
|
|
treeBuilder.HighlightedDragTarget = targetToHighlight.Tag;
|
|
_highlightedDragTarget = targetToHighlight;
|
|
_highlightedDragTarget.BackColor = SystemColors.Highlight;
|
|
_highlightedDragTarget.ForeColor = SystemColors.HighlightText;
|
|
}
|
|
e.Effect = DragDropEffects.Move;
|
|
}
|
|
else
|
|
{
|
|
ClearHighlightedDragTarget();
|
|
e.Effect = DragDropEffects.None;
|
|
}
|
|
}
|
|
|
|
private void treeView_KeyUp(object sender, KeyEventArgs e)
|
|
{
|
|
switch (e.KeyCode)
|
|
{
|
|
case Keys.Apps:
|
|
if (treeView.SelectedNode != null)
|
|
{
|
|
treeView.SelectedNode.EnsureVisible();
|
|
HandleNodeRightClick(new VirtualTreeNodeMouseClickEventArgs(treeView.SelectedNode,
|
|
MouseButtons.Right, 1,
|
|
treeView.SelectedNode.Bounds.X,
|
|
treeView.SelectedNode.Bounds.Y + treeView.SelectedNode.Bounds.Height));
|
|
}
|
|
break;
|
|
|
|
case Keys.F2:
|
|
var cmd = new PropertiesCommand(Program.MainWindow, Program.MainWindow.SelectionManager.Selection);
|
|
if (cmd.CanExecute())
|
|
cmd.Execute();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void treeView_NodeMouseClick(object sender, VirtualTreeNodeMouseClickEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (treeView.Nodes.Count > 0)
|
|
HandleNodeRightClick(e);
|
|
|
|
if (TreeNodeClicked != null)
|
|
TreeNodeClicked();
|
|
}
|
|
catch (Exception exn)
|
|
{
|
|
log.Error(exn, exn);
|
|
// Swallow this exception -- there's no point throwing it on.
|
|
#if DEBUG
|
|
throw;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private void treeView_NodeMouseDoubleClick(object sender, VirtualTreeNodeMouseClickEventArgs e)
|
|
{
|
|
if (e.Button != MouseButtons.Left)
|
|
return;
|
|
if (e.Node == null)
|
|
return;
|
|
|
|
IXenConnection conn = e.Node.Tag as IXenConnection;
|
|
if (conn == null)
|
|
{
|
|
var obj = e.Node.Tag as IXenObject;
|
|
if (obj != null)
|
|
conn = obj.Connection;
|
|
}
|
|
if (conn != null && !conn.IsConnected)
|
|
{
|
|
new ReconnectHostCommand(Program.MainWindow, conn).Execute();
|
|
return;
|
|
}
|
|
|
|
VM vm = e.Node.Tag as VM;
|
|
if (vm != null)
|
|
{
|
|
Command cmd = null;
|
|
|
|
if (vm.is_a_template)
|
|
{
|
|
cmd = new NewVMCommand(Program.MainWindow, Program.MainWindow.SelectionManager.Selection);
|
|
}
|
|
else if (vm.power_state == vm_power_state.Halted && vm.allowed_operations.Contains(vm_operations.start))
|
|
{
|
|
cmd = new StartVMCommand(Program.MainWindow, Program.MainWindow.SelectionManager.Selection);
|
|
}
|
|
else if (vm.power_state == vm_power_state.Suspended && vm.allowed_operations.Contains(vm_operations.resume))
|
|
{
|
|
cmd = new ResumeVMCommand(Program.MainWindow, Program.MainWindow.SelectionManager.Selection);
|
|
}
|
|
|
|
if (cmd != null && cmd.CanExecute())
|
|
{
|
|
treeView.SelectedNode = e.Node;
|
|
cmd.Execute();
|
|
}
|
|
return;
|
|
}
|
|
|
|
Host host = e.Node.Tag as Host;
|
|
if (host != null)
|
|
{
|
|
Command cmd = new PowerOnHostCommand(Program.MainWindow, host);
|
|
if (cmd.CanExecute())
|
|
{
|
|
treeView.SelectedNode = e.Node;
|
|
cmd.Execute();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void treeView_SelectionsChanged(object sender, EventArgs e)
|
|
{
|
|
// this is fired when the selection of the main treeview changes.
|
|
VirtualTreeNode rootNode = treeView.Nodes.Count > 0 ? treeView.Nodes[0] : null;
|
|
List<SelectedItem> items = new List<SelectedItem>();
|
|
foreach (VirtualTreeNode node in treeView.SelectedNodes)
|
|
{
|
|
GroupingTag groupingTag = node.Tag as GroupingTag;
|
|
IXenObject xenObject = node.Tag as IXenObject;
|
|
|
|
if (xenObject != null)
|
|
{
|
|
items.Add(new SelectedItem(xenObject, xenObject.Connection,
|
|
HostAncestorOfNode(node), PoolAncestorOfNode(node),
|
|
GroupAncestorOfNode(node), rootNode));
|
|
}
|
|
else
|
|
{
|
|
items.Add(new SelectedItem(groupingTag, rootNode));
|
|
}
|
|
}
|
|
|
|
selectionManager.SetSelection(items);
|
|
|
|
if (TreeViewSelectionChanged != null)
|
|
TreeViewSelectionChanged();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|