#region License Information
/* HeuristicLab
* Copyright (C) 2002-2014 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.Drawing;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using HeuristicLab.Collections;
using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Core.Views;
using HeuristicLab.MainForm;
using HeuristicLab.MainForm.WindowsForms;
using HeuristicLab.Persistence.Core;
using HeuristicLab.Persistence.Default.Xml;
using HeuristicLab.PluginInfrastructure;
namespace HeuristicLab.Scripting.Views {
[View("ItemCollection View")]
[Content(typeof(VariableStore), true)]
public partial class VariableStoreView : AsynchronousContentView {
protected readonly Dictionary itemListViewItemMapping;
protected readonly Dictionary serializableLookup;
protected TypeSelectorDialog typeSelectorDialog;
protected bool validDragOperation;
public new VariableStore Content {
get { return (VariableStore)base.Content; }
set { base.Content = value; }
}
public ListView ItemsListView {
get { return variableListView; }
}
public VariableStoreView() {
InitializeComponent();
itemListViewItemMapping = new Dictionary();
serializableLookup = new Dictionary();
variableListView.SmallImageList.Images.AddRange(new Image[] {
HeuristicLab.Common.Resources.VSImageLibrary.Error,
HeuristicLab.Common.Resources.VSImageLibrary.Warning,
HeuristicLab.Common.Resources.VSImageLibrary.Object,
HeuristicLab.Common.Resources.VSImageLibrary.Nothing
});
}
protected override void Dispose(bool disposing) {
if (disposing) {
if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
if (components != null) components.Dispose();
}
base.Dispose(disposing);
}
protected override void DeregisterContentEvents() {
Content.ItemsAdded -= Content_ItemsAdded;
Content.ItemsReplaced -= Content_ItemsReplaced;
Content.ItemsRemoved -= Content_ItemsRemoved;
Content.CollectionReset -= Content_CollectionReset;
base.DeregisterContentEvents();
}
protected override void RegisterContentEvents() {
base.RegisterContentEvents();
Content.ItemsAdded += Content_ItemsAdded;
Content.ItemsReplaced += Content_ItemsReplaced;
Content.ItemsRemoved += Content_ItemsRemoved;
Content.CollectionReset += Content_CollectionReset;
}
protected override void OnContentChanged() {
base.OnContentChanged();
variableListView.Items.Clear();
itemListViewItemMapping.Clear();
if (Content != null) {
Caption += " (" + Content.GetType().Name + ")";
foreach (var item in Content)
AddVariable(item);
AdjustListViewColumnSizes();
SortItemsListView(SortOrder.Ascending);
}
}
protected override void SetEnabledStateOfControls() {
base.SetEnabledStateOfControls();
if (Content == null) {
addButton.Enabled = false;
sortAscendingButton.Enabled = false;
sortDescendingButton.Enabled = false;
removeButton.Enabled = false;
variableListView.Enabled = false;
} else {
addButton.Enabled = !Locked && !ReadOnly;
sortAscendingButton.Enabled = variableListView.Items.Count > 1;
sortDescendingButton.Enabled = variableListView.Items.Count > 1;
removeButton.Enabled = !Locked && !ReadOnly && variableListView.SelectedItems.Count > 0;
variableListView.Enabled = true;
variableListView.LabelEdit = !Locked && !ReadOnly;
}
}
protected virtual object CreateItem() {
if (typeSelectorDialog == null) {
typeSelectorDialog = new TypeSelectorDialog { Caption = "Select Item" };
typeSelectorDialog.TypeSelector.Caption = "Available Items";
typeSelectorDialog.TypeSelector.Configure(typeof(IItem), false, true);
}
if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
try {
return (object)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
} catch (Exception ex) {
ErrorHandling.ShowErrorDialog(this, ex);
}
}
return null;
}
protected virtual void AddVariable(KeyValuePair variable) {
if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
string value = (variable.Value ?? "null").ToString();
string type = variable.Value == null ? "null" : variable.Value.GetType().ToString();
bool serializable = IsSerializable(variable);
var listViewItem = new ListViewItem(new[] { variable.Key, value, type }) { ToolTipText = GetToolTipText(variable, serializable), Tag = variable };
bool validIdentifier = SafeVariableNameRegex.IsMatch(variable.Key);
if (!serializable) listViewItem.ImageIndex = 0;
else if (!validIdentifier) listViewItem.ImageIndex = 1;
else if (variable.Value != null) listViewItem.ImageIndex = 2;
else listViewItem.ImageIndex = 3;
variableListView.Items.Add(listViewItem);
itemListViewItemMapping[variable.Key] = listViewItem;
sortAscendingButton.Enabled = variableListView.Items.Count > 1;
sortDescendingButton.Enabled = variableListView.Items.Count > 1;
var item = variable.Value as IItem;
if (item != null) item.ToStringChanged += item_ToStringChanged;
}
protected virtual void RemoveVariable(KeyValuePair variable) {
if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
ListViewItem listViewItem;
if (itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem)) {
itemListViewItemMapping.Remove(variable.Key);
variableListView.Items.Remove(listViewItem);
sortAscendingButton.Enabled = variableListView.Items.Count > 1;
sortDescendingButton.Enabled = variableListView.Items.Count > 1;
var item = variable.Value as IItem;
if (item != null) item.ToStringChanged -= item_ToStringChanged;
}
}
protected virtual void UpdateVariable(KeyValuePair variable) {
if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
ListViewItem listViewItem;
if (itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem)) {
string value = (variable.Value ?? "null").ToString();
string type = variable.Value == null ? "null" : variable.Value.GetType().ToString();
bool serializable = IsSerializable(variable);
bool validIdentifier = SafeVariableNameRegex.IsMatch(variable.Key);
if (!serializable) listViewItem.ImageIndex = 0;
else if (!validIdentifier) listViewItem.ImageIndex = 1;
else if (variable.Value != null) listViewItem.ImageIndex = 2;
else listViewItem.ImageIndex = 3;
listViewItem.SubItems[1].Text = value;
listViewItem.SubItems[2].Text = type;
listViewItem.ToolTipText = GetToolTipText(variable, serializable);
listViewItem.Tag = variable;
} else throw new ArgumentException("A variable with the specified name does not exist.", "variable");
}
#region ListView Events
protected virtual void variableListView_SelectedIndexChanged(object sender, EventArgs e) {
removeButton.Enabled = (Content != null) && !Locked && !ReadOnly && variableListView.SelectedItems.Count > 0;
AdjustListViewColumnSizes();
}
protected virtual void variableListView_KeyDown(object sender, KeyEventArgs e) {
switch (e.KeyCode) {
case Keys.Delete:
if ((variableListView.SelectedItems.Count > 0) && !Locked && !ReadOnly) {
foreach (ListViewItem item in variableListView.SelectedItems)
Content.Remove(item.Text);
}
break;
case Keys.F2:
var focusedItem = variableListView.FocusedItem;
if (variableListView.LabelEdit && focusedItem.Selected)
focusedItem.BeginEdit();
break;
case Keys.A:
if (e.Modifiers.HasFlag(Keys.Control)) {
foreach (ListViewItem item in variableListView.Items)
item.Selected = true;
}
break;
}
}
protected virtual void variableListView_DoubleClick(object sender, EventArgs e) {
if (variableListView.SelectedItems.Count == 1) {
var item = variableListView.SelectedItems[0].Tag as KeyValuePair?;
if (item != null) {
var value = item.Value.Value as IContent;
if (value != null) {
IContentView view = MainFormManager.MainForm.ShowContent(value);
if (view != null) {
view.ReadOnly = ReadOnly;
view.Locked = Locked;
}
}
}
}
}
protected virtual void variableListView_ItemDrag(object sender, ItemDragEventArgs e) {
if (!Locked && variableListView.SelectedItems.Count == 1) {
var listViewItem = variableListView.SelectedItems[0];
var item = (KeyValuePair)listViewItem.Tag;
var data = new DataObject(HeuristicLab.Common.Constants.DragDropDataFormat, item.Value);
DoDragDrop(data, DragDropEffects.Copy);
}
}
protected virtual void variableListView_DragEnter(object sender, DragEventArgs e) {
validDragOperation = !Locked && !ReadOnly && e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) != null;
}
protected virtual void variableListView_DragOver(object sender, DragEventArgs e) {
e.Effect = DragDropEffects.None;
if (validDragOperation) {
if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy;
}
}
protected virtual void variableListView_DragDrop(object sender, DragEventArgs e) {
if (e.Effect == DragDropEffects.Copy) {
object item = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
var cloner = new Cloner();
var dc = item as IDeepCloneable;
if (dc != null) item = cloner.Clone(dc);
var namedItem = item as INamedItem;
bool nameValid = namedItem != null && !string.IsNullOrEmpty(namedItem.Name);
string name = nameValid ? GenerateNewVariableName(namedItem.Name, false) : GenerateNewVariableName();
Content.Add(name, item);
var listViewItem = variableListView.FindItemWithText(name);
if (!nameValid) listViewItem.BeginEdit();
}
}
private readonly Regex SafeVariableNameRegex = new Regex("^[@]?[_a-zA-Z][_a-zA-Z0-9]*$");
private const string DefaultVariableName = "enter_name";
private void variableListView_AfterLabelEdit(object sender, LabelEditEventArgs e) {
string name = e.Label;
if (!string.IsNullOrEmpty(name) && SafeVariableNameRegex.IsMatch(name)) {
var variable = (KeyValuePair)variableListView.Items[e.Item].Tag;
if (!Content.ContainsKey(name)) {
Content.Remove(variable.Key);
Content.Add(name, variable.Value);
}
}
e.CancelEdit = true;
}
#endregion
#region Button Events
protected virtual void addButton_Click(object sender, EventArgs e) {
object newVar = CreateItem();
if (newVar != null) {
string name = GenerateNewVariableName();
Content.Add(name, newVar);
var item = variableListView.FindItemWithText(name);
item.BeginEdit();
}
}
protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
SortItemsListView(SortOrder.Ascending);
}
protected virtual void sortDescendingButton_Click(object sender, EventArgs e) {
SortItemsListView(SortOrder.Descending);
}
protected virtual void removeButton_Click(object sender, EventArgs e) {
if (variableListView.SelectedItems.Count > 0) {
foreach (ListViewItem item in variableListView.SelectedItems)
Content.Remove(item.Text);
variableListView.SelectedItems.Clear();
}
}
#endregion
#region Content Events
protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs> e) {
if (InvokeRequired)
Invoke(new CollectionItemsChangedEventHandler>(Content_ItemsAdded), sender, e);
else {
foreach (var item in e.Items)
AddVariable(item);
AdjustListViewColumnSizes();
}
}
protected virtual void Content_ItemsReplaced(object sender, CollectionItemsChangedEventArgs> e) {
if (InvokeRequired)
Invoke(new CollectionItemsChangedEventHandler>(Content_ItemsReplaced), sender, e);
else {
foreach (var item in e.Items)
UpdateVariable(item);
AdjustListViewColumnSizes();
}
}
protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs> e) {
if (InvokeRequired)
Invoke(new CollectionItemsChangedEventHandler>(Content_ItemsRemoved), sender, e);
else {
foreach (var item in e.Items)
RemoveVariable(item);
AdjustListViewColumnSizes();
}
}
protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs> e) {
if (InvokeRequired)
Invoke(new CollectionItemsChangedEventHandler>(Content_CollectionReset), sender, e);
else {
foreach (var item in e.OldItems)
RemoveVariable(item);
foreach (var item in e.Items)
AddVariable(item);
AdjustListViewColumnSizes();
}
}
private void item_ToStringChanged(object sender, EventArgs e) {
foreach (ListViewItem item in variableListView.Items) {
var variable = item.Tag as KeyValuePair?;
if (variable != null && variable.Value.Value == sender) {
string value = (variable.Value.Value ?? "null").ToString();
item.SubItems[1].Text = value;
item.SubItems[2].Text = variable.Value.Value.GetType().ToString();
item.ToolTipText = GetToolTipText(variable.Value, item.ImageIndex != 0);
}
}
}
#endregion
#region Helpers
protected virtual void SortItemsListView(SortOrder sortOrder) {
variableListView.Sorting = SortOrder.None;
variableListView.Sorting = sortOrder;
variableListView.Sorting = SortOrder.None;
}
protected virtual void AdjustListViewColumnSizes() {
foreach (ColumnHeader ch in variableListView.Columns)
ch.Width = -2;
}
private string GetToolTipText(KeyValuePair variable, bool serializable) {
if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
string value = (variable.Value ?? "null").ToString();
string type = variable.Value == null ? "null" : variable.Value.GetType().ToString();
string[] lines = {
"Name: " + variable.Key,
"Value: " + value,
"Type: " + type
};
string toolTipText = string.Join(Environment.NewLine, lines);
if (!SafeVariableNameRegex.IsMatch(variable.Key))
toolTipText = "Caution: Identifier is no valid C# identifier!" + Environment.NewLine + toolTipText;
if (!serializable)
toolTipText = "Caution: Type is not serializable!" + Environment.NewLine + toolTipText;
return toolTipText;
}
private string GenerateNewVariableName(string defaultName = DefaultVariableName, bool generateValidIdentifier = true) {
if (generateValidIdentifier && !SafeVariableNameRegex.IsMatch(defaultName))
defaultName = DefaultVariableName;
if (Content.ContainsKey(defaultName)) {
int i = 1;
string formatString = generateValidIdentifier ? "{0}{1}" : "{0} ({1})";
string newName;
do {
newName = string.Format(formatString, defaultName, i++);
} while (Content.ContainsKey(newName));
return newName;
}
return defaultName;
}
private bool IsSerializable(KeyValuePair variable) {
var type = variable.Value.GetType();
bool serializable;
if (serializableLookup.TryGetValue(type, out serializable))
return serializable;
var ser = new Serializer(variable, ConfigurationService.Instance.GetDefaultConfig(new XmlFormat()), "ROOT", true);
try {
return serializableLookup[type] = ser.Count() > 0;
} catch (PersistenceException) {
return serializableLookup[type] = false;
}
}
#endregion
}
}