#region License Information
/* HeuristicLab
* Copyright (C) 2002-2016 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using HeuristicLab.Collections;
using HeuristicLab.Core;
using HeuristicLab.Core.Views;
using HeuristicLab.MainForm;
using HeuristicLab.PluginInfrastructure;
namespace HeuristicLab.ItemTreeViews.Views {
[View("ItemCollection Tree View")]
[Content(typeof(ItemCollection<>), false)]
[Content(typeof(IItemCollection<>), false)]
[Content(typeof(ReadOnlyItemCollection<>), false)]
public partial class ItemCollectionTreeView : ItemView where T : class, IItem {
#region properties
protected Dictionary itemTreeViewItemMapping;
protected Dictionary treeViewChildrenMapping;
protected TypeSelectorDialog typeSelectorDialog;
protected bool validDragOperation;
public new IItemCollection Content
{
get { return (IItemCollection)base.Content; }
set
{
base.Content = value;
OnContentChanged();
}
}
public ObservableCollection ItemCollection { get { return Content as ObservableCollection; } }
public bool ShowDetails
{
get { return showDetailsCheckBox.Checked; }
set { showDetailsCheckBox.Checked = value; }
}
public TreeView ItemsTreeView { get { return itemsTreeView; } }
#endregion
public ItemCollectionTreeView() {
InitializeComponent();
itemTreeViewItemMapping = new Dictionary();
treeViewChildrenMapping = new Dictionary();
}
protected override void Dispose(bool disposing) {
if (disposing) {
if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
if (components != null) components.Dispose();
}
base.Dispose(disposing);
}
#region eventregistration
protected override void DeregisterContentEvents() {
Content.ItemsAdded -= Content_ItemsAdded;
Content.ItemsRemoved -= Content_ItemsRemoved;
Content.CollectionReset -= Content_CollectionReset;
base.DeregisterContentEvents();
}
protected override void RegisterContentEvents() {
DeregisterContentEvents(); //prevent multiattaching
base.RegisterContentEvents();
Content.ItemsAdded += Content_ItemsAdded;
Content.ItemsRemoved += Content_ItemsRemoved;
Content.CollectionReset += Content_CollectionReset;
}
protected virtual void DeregisterItemEvents(T item) {
item.ItemImageChanged -= Item_ItemImageChanged;
item.ToStringChanged -= Item_ToStringChanged;
}
protected virtual void RegisterItemEvents(T item) {
DeregisterItemEvents(item); //prevent multiattaching
item.ItemImageChanged += Item_ItemImageChanged;
item.ToStringChanged += Item_ToStringChanged;
}
protected virtual void DeregisterViewEvents() {
ItemsTreeView.AfterSelect -= itemsTreeView_SelectedIndexChanged;
ItemsTreeView.DoubleClick -= itemsTreeView_DoubleClick;
ItemsTreeView.KeyDown -= itemsTreeView_KeyDown;
}
protected virtual void RegisterViewEvents() {
DeregisterViewEvents(); //prevent multiattaching
ItemsTreeView.AfterSelect += itemsTreeView_SelectedIndexChanged;
ItemsTreeView.DoubleClick += itemsTreeView_DoubleClick;
ItemsTreeView.KeyDown += itemsTreeView_KeyDown;
}
protected override void OnContentChanged() {
ItemsTreeView.BeginUpdate();
base.OnContentChanged();
ItemsTreeView.Nodes.Clear();
itemTreeViewItemMapping.Clear();
RebuildTreeImageList();
viewHost.Content = null;
if (Content == null) { ItemsTreeView.EndUpdate(); return; }
RegisterViewEvents();
Caption += " (" + Content.GetType().Name + ")";
foreach (T item in Content)
AddTreeNodeRecursive(item, null);
SortItemsTreeView();
ItemsTreeView.EndUpdate();
}
#endregion
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
if (Content == null) {
addButton.Enabled = false;
sortAscendingButton.Enabled = false;
sortDescendingButton.Enabled = false;
removeButton.Enabled = false;
ItemsTreeView.Enabled = false;
detailsGroupBox.Enabled = false;
} else {
addButton.Enabled = !Content.IsReadOnly && !ReadOnly;
sortAscendingButton.Enabled = false;
sortDescendingButton.Enabled = false;
removeButton.Enabled = !Content.IsReadOnly && !ReadOnly && ItemsTreeView.Nodes.Count > 0;
ItemsTreeView.Enabled = true;
detailsGroupBox.Enabled = true;
}
}
protected virtual T CreateItem() {
if (typeSelectorDialog == null) {
typeSelectorDialog = new TypeSelectorDialog();
typeSelectorDialog.Caption = "Select Item";
typeSelectorDialog.TypeSelector.Caption = "Available Items";
typeSelectorDialog.TypeSelector.Configure(typeof(T), false, true);
}
if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
try {
return (T)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
}
catch (Exception ex) {
ErrorHandling.ShowErrorDialog(this, ex);
}
}
return null;
}
protected virtual TreeNode CreateTreeViewNode(T item) {
var treeViewNode = new TreeNode("noName");
treeViewNode.NodeFont = DefaultFont;
if (item == null) {
treeViewNode.Text = "null";
ItemsTreeView.ImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Nothing);
treeViewNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
} else {
treeViewNode.Text = item.ToString();
treeViewNode.ToolTipText = item.ItemName + ": " + item.ItemDescription;
ItemsTreeView.ImageList.Images.Add(item.ItemImage);
treeViewNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
treeViewNode.Tag = item;
}
return treeViewNode;
}
#region adding
protected virtual void AddTreeNode(T item) {
if (item == null || itemTreeViewItemMapping.ContainsKey(item) || !treeViewChildrenMapping.ContainsKey(item))
return;
RegisterItemEvents(item);
var node = CreateTreeViewNode(item);
itemTreeViewItemMapping[item] = node;
var p = GetParent(item);
(p == null ? ItemsTreeView.Nodes : itemTreeViewItemMapping[p].Nodes).Add(node);
}
protected void AddTreeNodeRecursive(T item, TreeNode parent) {
if (item == null) return;
Queue queue = new Queue();
queue.Enqueue(item);
if (!treeViewChildrenMapping.ContainsKey(item))
treeViewChildrenMapping.Add(item, parent != null ? parent.Tag as T : null); //Set explicitly to null in some cases
while (queue.Count > 0 && (item = queue.Dequeue()) != null) {
AddTreeNode(item);
foreach (var child in ExpandTreeViewItem(item)) { //Expand queue
if (!treeViewChildrenMapping.ContainsKey(child))
treeViewChildrenMapping.Add(child, item);
queue.Enqueue(child);
}
}
}
protected void UpdateTreeNodeRecursive(T item, TreeNode parent) {
if (item == null) return;
Queue queue = new Queue();
queue.Enqueue(item);
while (queue.Count > 0 && (item = queue.Dequeue()) != null) {
AddTreeNode(item);
var newChildren = ExpandTreeViewItem(item);
var deadChildren =
itemTreeViewItemMapping[item].Nodes.Cast()
.Select(n => n.Tag as T)
.Where(t => !newChildren.Contains(t)).Select(t => itemTreeViewItemMapping[t])
.ToList();
foreach (var child in newChildren) { //Expand queue
if (!treeViewChildrenMapping.ContainsKey(child))
treeViewChildrenMapping.Add(child, item);
queue.Enqueue(child);
}
foreach (var child in deadChildren) {
RemoveTreeViewItem(child);
}
}
}
///
/// This function defines which subnodes are created.
/// Override this to change which items are used in the creation of new nodes
///
///
///
protected virtual List ExpandTreeViewItem(object parent) {
//unpack enumerables
var p = parent as IEnumerable;
if (p != null) return p.ToList();
//exclude LookupParameters from Value unpacking
if (parent is ILookupParameter) return new List();
//unpack "value"/"Value"/"ActualValue" relationship if possible
object prop = GetValue(parent);
//return nothing if failed
return (prop != null) ? ExpandTreeViewItem(prop) : new List();
}
#endregion
#region removal
private void RemoveTreeViewItem2(TreeNode treeViewItem) {
if (treeViewItem == null) return;
T item = treeViewItem.Tag as T;
if (item != null) {
treeViewChildrenMapping.Remove(item);
itemTreeViewItemMapping.Remove(item);
DeregisterItemEvents(item);
}
var children = GetChildren(item);
foreach (var node in children)
if (node != null && itemTreeViewItemMapping.ContainsKey(node))
RemoveTreeViewItem(itemTreeViewItemMapping[node]);
treeViewItem.Remove();
}
protected void RemoveTreeViewItem(TreeNode treeViewNode) {
var queue = new Queue(new[] { treeViewNode });
treeViewNode.Remove();
while (queue.Count > 0) {
treeViewNode = queue.Dequeue();
T item = treeViewNode.Tag as T;
if (item != null) {
treeViewChildrenMapping.Remove(item);
itemTreeViewItemMapping.Remove(item);
DeregisterItemEvents(item);
}
var children = GetChildren(item);
foreach (var node in children)
if (node != null && itemTreeViewItemMapping.ContainsKey(node)) queue.Enqueue(itemTreeViewItemMapping[node]);
}
}
#endregion
protected virtual void UpdateTreeViewItemImage(TreeNode treeViewNode) {
if (treeViewNode == null) throw new ArgumentNullException();
var item = treeViewNode.Tag as T;
var i = treeViewNode.ImageIndex;
ItemsTreeView.ImageList.Images[i] = item == null ? Common.Resources.VSImageLibrary.Nothing : item.ItemImage;
treeViewNode.ImageIndex = i;
treeViewNode.SelectedImageIndex = treeViewNode.ImageIndex;
}
protected virtual void UpdateTreeViewItemText(TreeNode treeViewNode) {
if (treeViewNode == null) throw new ArgumentNullException();
var item = treeViewNode.Tag as T;
treeViewNode.Text = item == null ? "null" : item.ToString();
treeViewNode.ToolTipText = item == null ? string.Empty : item.ItemName + ": " + item.ItemDescription;
}
protected virtual TreeNode GetTreeNodeForItem(T item) {
if (item == null)
return GetAllNodes(ItemsTreeView).First(x => x.Tag == null);
TreeNode viewItem;
itemTreeViewItemMapping.TryGetValue(item, out viewItem);
return viewItem;
}
#region View Events
protected virtual void itemsTreeView_SelectedIndexChanged(object sender, EventArgs e) {
removeButton.Enabled = (Content != null) && !Content.IsReadOnly && !ReadOnly && ItemsTreeView.SelectedNode != null;
if (!showDetailsCheckBox.Checked) return;
var item = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
detailsGroupBox.Enabled = true;
viewHost.Content = item;
}
protected virtual void itemsTreeView_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyCode == Keys.Delete) {
if ((ItemsTreeView.SelectedNode != null) && !Content.IsReadOnly && !ReadOnly) {
var selected = GetAllNodes(ItemsTreeView.SelectedNode);
if (ItemCollection != null) ItemCollection.RemoveRange(selected.Select(i => (T)i.Tag));
else {
ItemsTreeView.BeginUpdate();
foreach (var item in selected) Content.Remove((T)item.Tag);
ItemsTreeView.EndUpdate();
}
}
}
}
protected virtual void itemsTreeView_DoubleClick(object sender, EventArgs e) {
T item = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
if (item == null) return;
IContentView view = MainFormManager.MainForm.ShowContent(item);
if (view == null) return;
view.ReadOnly = ReadOnly;
view.Locked = Locked;
}
#endregion
#region Button Events
protected virtual void addButton_Click(object sender, EventArgs e) {
T item = CreateItem();
if (item != null) Content.Add(item);
}
protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
SortItemsTreeView();
}
protected virtual void sortDescendingButton_Click(object sender, EventArgs e) { //DO nothing for Treeview (no custom sort order possible)
}
protected virtual void removeButton_Click(object sender, EventArgs e) {
if (ItemsTreeView.SelectedNode == null) return;
var selected = GetAllNodes(ItemsTreeView.SelectedNode);
if (ItemCollection != null) {
ItemCollection.RemoveRange(selected.Select(i => (T)i.Tag));
} else {
foreach (TreeNode item in selected)
Content.Remove((T)item.Tag);
}
ItemsTreeView.SelectedNode = null;
}
#endregion
#region CheckBox Events
protected virtual void showDetailsCheckBox_CheckedChanged(object sender, EventArgs e) {
if (showDetailsCheckBox.Checked) {
splitContainer.Panel2Collapsed = false;
detailsGroupBox.Enabled = ItemsTreeView.SelectedNode != null;
viewHost.Content = ItemsTreeView.SelectedNode != null ? ItemsTreeView.SelectedNode.Tag as T : null;
} else {
splitContainer.Panel2Collapsed = true;
viewHost.Content = null;
}
}
#endregion
#region Content Events
protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs e) {
if (InvokeRequired)
Invoke(new CollectionItemsChangedEventHandler(Content_ItemsAdded), sender, e);
else {
ItemsTreeView.BeginUpdate();
foreach (T item in e.Items) AddTreeNodeRecursive(item, null);
RebuildTreeImageList();
ItemsTreeView.EndUpdate();
}
}
protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs e) {
if (InvokeRequired)
Invoke(new CollectionItemsChangedEventHandler(Content_ItemsRemoved), sender, e);
else {
ItemsTreeView.BeginUpdate();
foreach (T item in e.Items) {
var treeNode = GetTreeNodeForItem(item);
if (treeNode != null) RemoveTreeViewItem(treeNode);
}
RebuildTreeImageList();
ItemsTreeView.EndUpdate();
}
}
protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs e) {
if (InvokeRequired)
Invoke(new CollectionItemsChangedEventHandler(Content_CollectionReset), sender, e);
else {
ItemsTreeView.BeginUpdate();
foreach (T item in e.OldItems) {
var treeNode = GetTreeNodeForItem(item);
if (treeNode != null) RemoveTreeViewItem(treeNode);
}
RebuildTreeImageList();
foreach (T item in e.Items)
AddTreeNodeRecursive(item, null);
ItemsTreeView.EndUpdate();
}
}
#endregion
#region Item Events
protected virtual void Item_ItemImageChanged(object sender, EventArgs e) {
if (InvokeRequired)
Invoke(new EventHandler(Item_ItemImageChanged), sender, e);
else {
var node = GetTreeNodeForItem(sender as T);
if (node != null) UpdateTreeViewItemImage(node);
}
}
protected virtual void Item_ToStringChanged(object sender, EventArgs e) {
if (InvokeRequired)
Invoke(new EventHandler(Item_ToStringChanged), sender, e);
else {
var item = sender as T;
var itemNode = GetTreeNodeForItem(item);
if (itemNode == null) return; //nothing to remove
var parent = itemNode.Parent;
ItemsTreeView.BeginUpdate();
UpdateTreeNodeRecursive(item, parent);
UpdateTreeViewItemText(itemNode);
ItemsTreeView.EndUpdate();
}
}
#endregion
#region Helpers
protected virtual void SortItemsTreeView() {
ItemsTreeView.Sort();
ItemsTreeView.Sorted = true;
}
protected virtual void RebuildTreeImageList() {
if (ItemsTreeView.ImageList == null) ItemsTreeView.ImageList = new ImageList();
ItemsTreeView.ImageList.Images.Clear();
foreach (TreeNode treeNode in GetAllNodes(ItemsTreeView)) {
T item = treeNode.Tag as T;
try {
ItemsTreeView.ImageList.Images.Add(item == null ? Common.Resources.VSImageLibrary.Nothing : item.ItemImage);
treeNode.ImageIndex = ItemsTreeView.ImageList.Images.Count - 1;
treeNode.SelectedImageIndex = treeNode.ImageIndex;
}
catch (ArgumentException) {
treeNode.SelectedImageIndex = ItemsTreeView.ImageList.Images.Count + 10000;
}
}
}
protected List GetAllNodes(TreeView view) {
var n = new List();
foreach (TreeNode node in view.Nodes) {
AddAllNodes(n, node);
}
return n;
}
private List GetAllNodes(TreeNode node) {
var n = new List();
AddAllNodes(n, node);
return n;
}
private static void AddAllNodes(List list, TreeNode node) {
var queue = new Queue(new[] { node });
while (queue.Count > 0) {
node = queue.Dequeue();
if (!list.Contains(node)) list.Add(node);
foreach (TreeNode n in node.Nodes)
queue.Enqueue(n);
}
}
protected T GetParent(T child) {
return treeViewChildrenMapping.ContainsKey(child) ? treeViewChildrenMapping[child] : null;
}
private static IItem GetValue(object parent) {
var props = parent.GetType().GetProperties();
foreach (var prop in props) {
if (prop.Name.Equals("Value", StringComparison.InvariantCultureIgnoreCase) || prop.Name.Equals("ActualValue", StringComparison.InvariantCultureIgnoreCase)) {
if (typeof(IItem).IsAssignableFrom(prop.PropertyType)) {
try { return prop.GetValue(parent) as IItem; }
catch (TargetInvocationException) { } //if getting the value is not an option (LookUpParameters,...) try other properties
}
}
}
return null;
}
protected List GetChildren(T parent) {
return treeViewChildrenMapping.Where(x => x.Value == parent).Select(x => x.Key).ToList();
}
#endregion
}
}