/* 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.Linq;
using System.Text;
using System.Windows.Forms;
using XenAdmin.Core;
using XenAdmin.Network;
using XenAPI;
namespace XenAdmin.Controls
{
class FilterLocationToolStripDropDownButton : ToolStripDropDownButton
{
[Browsable(true)]
public event Action FilterChanged;
///
/// Maintain a list of all the objects we currently have events on for clearing out on rebuild
///
private List connectionsWithEvents = new List();
private List poolsWithEvents = new List();
private List hostsWithEvents = new List();
///
/// Store only host check states because the pools can be in an
/// indeterminate state.
///
private Dictionary HostCheckStates = new Dictionary();
private readonly CollectionChangeEventHandler m_hostCollectionChangedWithInvoke;
private bool inFilterListUpdate;
private bool retryFilterListUpdate;
private ToolStripMenuItem toolStripMenuItemAll;
public FilterLocationToolStripDropDownButton()
{
ConnectionsManager.XenConnections.CollectionChanged += XenConnections_CollectionChanged;
m_hostCollectionChangedWithInvoke = Program.ProgramInvokeHandler(Host_CollectionChanged);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
DeregisterEvents();
ConnectionsManager.XenConnections.CollectionChanged -= XenConnections_CollectionChanged;
}
private void OnFilterChanged()
{
if (FilterChanged != null)
FilterChanged();
}
///
/// Build the list of hosts to filter by for the first time and set all
/// of them to be checked
///
public void InitializeHostList()
{
foreach (IXenConnection c in ConnectionsManager.XenConnectionsCopy)
{
foreach (Host h in c.Cache.Hosts)
HostCheckStates[h.uuid] = true;
}
}
public void RefreshLists()
{
BuildFilterList();
OnFilterChanged();
}
public void BuildFilterList()
{
Program.AssertOnEventThread();
if (inFilterListUpdate)
{
// queue up an update after the current one has finished,
// in case the update has missed the relevant change
retryFilterListUpdate = true;
return;
}
inFilterListUpdate = true;
try
{
DropDownItems.Clear();
DeregisterEvents();
RegisterEvents();
foreach (IXenConnection c in ConnectionsManager.XenConnectionsCopy)
{
Pool p = Helpers.GetPool(c);
if (p == null)// Stand alone host
{
foreach (Host h in c.Cache.Hosts)
{
var item = GenerateFilterItem(h, h.uuid);
item.Checked = HostCheckStates.ContainsKey(h.uuid);
DropDownItems.Add(item);
break;
}
}
else
DropDownItems.Add(GeneratePoolFilterItem(p));
}
if (DropDownItems.Count > 0)
{
toolStripMenuItemAll = new ToolStripMenuItem
{
Text = Messages.FILTER_SHOW_ALL,
Enabled = FilterIsOn
};
DropDownItems.AddRange(new ToolStripItem[]
{
new ToolStripSeparator(),
toolStripMenuItemAll
});
}
Enabled = DropDownItems.Count > 0;
}
finally
{
inFilterListUpdate = false;
if (retryFilterListUpdate)
{
// there was a request to update while we were building,
// rebuild in case we missed something
retryFilterListUpdate = false;
BuildFilterList();
}
}
}
public bool HideByLocation(string hostUuid)
{
if (hostUuid == null)
return false;
if (HostCheckStates.ContainsKey(hostUuid) && !HostCheckStates[hostUuid])
return true;
return false;
}
public bool HideByLocation(List hostUuids)
{
if (hostUuids.Count == 0)
return false;
return hostUuids.TrueForAll(uuid => HostCheckStates.ContainsKey(uuid) && !HostCheckStates[uuid]);
}
public bool FilterIsOn
{
get { return HostCheckStates.ContainsValue(false); }
}
private ToolStripMenuItem GeneratePoolFilterItem(Pool p)
{
List subItems = new List();
foreach (Host h in p.Connection.Cache.Hosts)
{
var hostItem = GenerateFilterItem(h, h.uuid);
hostItem.Checked = HostCheckStates.ContainsKey(h.uuid);
subItems.Add(hostItem);
}
var poolItem = GenerateFilterItem(p, p.uuid);
poolItem.DropDownItems.AddRange(subItems.ToArray());
poolItem.CheckState = subItems.TrueForAll(item => item.Checked)
? CheckState.Checked
: subItems.TrueForAll(item => !item.Checked)
? CheckState.Unchecked
: CheckState.Indeterminate;
poolItem.DropDownItemClicked += poolItem_DropDownItemClicked;
return poolItem;
}
private ToolStripMenuItem GenerateFilterItem(IXenObject xenObject, string xenObjectUuid)
{
var item = new ToolStripMenuItem
{
Text = Helpers.GetName(xenObject),
Tag = xenObjectUuid
};
return item;
}
private void RegisterEvents()
{
foreach (IXenConnection c in ConnectionsManager.XenConnectionsCopy)
{
c.ConnectionStateChanged += connection_ConnectionStateChanged;
c.Cache.RegisterCollectionChanged(m_hostCollectionChangedWithInvoke);
c.CachePopulated += connection_CachePopulated;
connectionsWithEvents.Add(c);
foreach (var pool in c.Cache.Pools)
{
pool.PropertyChanged += pool_PropertyChanged;
poolsWithEvents.Add(pool);
}
foreach (Host host in c.Cache.Hosts)
{
RegisterHostEvents(host);
hostsWithEvents.Add(host);
}
}
}
private void DeregisterEvents()
{
foreach (IXenConnection c in connectionsWithEvents)
{
c.ConnectionStateChanged -= connection_ConnectionStateChanged;
c.Cache.DeregisterCollectionChanged(m_hostCollectionChangedWithInvoke);
c.CachePopulated -= connection_CachePopulated;
}
foreach (var pool in poolsWithEvents)
pool.PropertyChanged -= pool_PropertyChanged;
foreach (Host h in hostsWithEvents)
DeregisterHostEvents(h);
connectionsWithEvents.Clear();
poolsWithEvents.Clear();
hostsWithEvents.Clear();
}
private void RegisterHostEvents(Host host)
{
Host_metrics metrics = host.Connection.Resolve(host.metrics);
if (metrics != null)
metrics.PropertyChanged += hostMetrics_PropertyChanged;
host.PropertyChanged += host_PropertyChanged;
}
private void DeregisterHostEvents(Host host)
{
Host_metrics metrics = host.Connection.Resolve(host.metrics);
if (metrics != null)
metrics.PropertyChanged -= hostMetrics_PropertyChanged;
host.PropertyChanged -= host_PropertyChanged;
}
private void connection_ConnectionStateChanged(object sender, EventArgs e)
{
Program.Invoke(Parent, RefreshLists);
}
private void connection_CachePopulated(object sender, EventArgs e)
{
Program.Invoke(Parent, RefreshLists);
}
private void XenConnections_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
if (e.Action == CollectionChangeAction.Add)
{
IXenConnection connection = e.Element as IXenConnection;
foreach (Host host in connection.Cache.Hosts)
HostCheckStates[host.uuid] = true;
}
}
private void Host_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
if (e.Action == CollectionChangeAction.Add)
HostCheckStates[((Host)e.Element).uuid] = true;
Program.Invoke(Parent, RefreshLists);
}
private void pool_PropertyChanged(object obj, PropertyChangedEventArgs e)
{
if (e.PropertyName == "other_config" || e.PropertyName == "name_label")
Program.Invoke(Parent, RefreshLists);
}
private void hostMetrics_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "live")
Program.Invoke(Parent, RefreshLists);
}
private void host_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "name_label" || e.PropertyName == "metrics")
Program.Invoke(Parent, RefreshLists);
}
protected override void OnDropDownItemClicked(ToolStripItemClickedEventArgs e)
{
//this method pertains to pool or stand alone host items
base.OnDropDownItemClicked(e);
HandleItemClicked(e.ClickedItem as ToolStripMenuItem);
}
private void poolItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
HandleItemClicked(e.ClickedItem as ToolStripMenuItem);
}
private void HandleItemClicked(ToolStripMenuItem item)
{
if (item == null)
return;
string uuid = (string)item.Tag;
if (item.HasDropDownItems)
{
//this is a pool node
item.CheckState = item.CheckState == CheckState.Checked
? CheckState.Unchecked
: CheckState.Checked;
foreach (ToolStripMenuItem child in item.DropDownItems)
{
child.Checked = item.Checked;
string hostUuid = (string)child.Tag;
HostCheckStates[hostUuid] = child.Checked;
}
toolStripMenuItemAll.Enabled = FilterIsOn;
}
else if (item == toolStripMenuItemAll)
{
toolStripMenuItemAll.Enabled = false;
InitializeHostList();
BuildFilterList();
}
else
{
//this is a host node
item.Checked = !item.Checked;
HostCheckStates[uuid] = item.Checked;
ToolStripMenuItem poolItem = item.OwnerItem as ToolStripMenuItem;
if (poolItem != null)
{
//this is not a standalone host; change the parent pool's check state
var itemArray = new ToolStripMenuItem[poolItem.DropDownItems.Count];
poolItem.DropDownItems.CopyTo(itemArray, 0);
poolItem.CheckState = Array.TrueForAll(itemArray, i => i.Checked)
? CheckState.Checked
: Array.TrueForAll(itemArray, i => !i.Checked)
? CheckState.Unchecked
: CheckState.Indeterminate;
}
toolStripMenuItemAll.Enabled = FilterIsOn;
}
OnFilterChanged();
}
}
}