/* 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.Core;
using XenAdmin.Model;
using XenAdmin.Network;
using XenAdmin.XenSearch;
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;
#endregion
#region Accessors
public NavigationView()
{
InitializeComponent();
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);
n.NodeFont = Program.DefaultFont;
treeView.Nodes.Add(n);
treeView.SelectedNode = treeView.Nodes[0];
treeBuilder = new MainWindowTreeBuilder(treeView);
treeViewUpdateManager.Update += treeViewUpdateManager_Update;
}
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 = (IXenConnection)e.Element;
if (connection == null)
return;
if (e.Action == CollectionChangeAction.Add)
{
connection.BeforeMajorChange += Connection_BeforeMajorChange;
connection.AfterMajorChange += Connection_AfterMajorChange;
}
else if (e.Action == CollectionChangeAction.Remove)
{
connection.BeforeMajorChange -= Connection_BeforeMajorChange;
connection.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();
}
///
/// Selects the specified object in the treeview.
///
/// The object to be selected.
/// 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.
/// A value indicating whether selection was successful.
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