Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Scripting.Views/3.3/VariableStoreView.cs @ 11757

Last change on this file since 11757 was 11734, checked in by jkarder, 10 years ago

#2262:

  • only IDeepCloneables can be dropped on the VariableStoreView
  • renamed CSharpScript.Execute to CSharpScript.ExecuteAsync
File size: 20.0 KB
RevLine 
[10332]1#region License Information
2/* HeuristicLab
[10506]3 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[10332]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
[10506]25using System.Text.RegularExpressions;
[10332]26using System.Windows.Forms;
27using HeuristicLab.Collections;
28using HeuristicLab.Common;
29using HeuristicLab.Core;
30using HeuristicLab.Core.Views;
31using HeuristicLab.MainForm;
32using HeuristicLab.MainForm.WindowsForms;
[10358]33using HeuristicLab.Persistence.Core;
34using HeuristicLab.Persistence.Default.Xml;
[10332]35using HeuristicLab.PluginInfrastructure;
36
[10506]37namespace HeuristicLab.Scripting.Views {
[10332]38  [View("ItemCollection View")]
39  [Content(typeof(VariableStore), true)]
40  public partial class VariableStoreView : AsynchronousContentView {
[11479]41    #region Image Names
42    private const string ErrorImageName = "Error";
43    private const string WarningImageName = "Warning";
44    private const string HeuristicLabObjectImageName = "HeuristicLabObject";
45    private const string ObjectImageName = "Object";
46    private const string NothingImageName = "Nothing";
47    #endregion
48
[11437]49    protected readonly Dictionary<string, ListViewItem> itemListViewItemMapping;
50    protected readonly Dictionary<Type, bool> serializableLookup;
[10332]51    protected TypeSelectorDialog typeSelectorDialog;
52    protected bool validDragOperation;
53
54    public new VariableStore Content {
55      get { return (VariableStore)base.Content; }
56      set { base.Content = value; }
57    }
58
59    public ListView ItemsListView {
60      get { return variableListView; }
61    }
62
63    public VariableStoreView() {
64      InitializeComponent();
65      itemListViewItemMapping = new Dictionary<string, ListViewItem>();
[11437]66      serializableLookup = new Dictionary<Type, bool>();
[11479]67
68      var images = variableListView.SmallImageList.Images;
69      images.Add(ErrorImageName, Common.Resources.VSImageLibrary.Error);
70      images.Add(WarningImageName, Common.Resources.VSImageLibrary.Warning);
71      images.Add(HeuristicLabObjectImageName, Common.Resources.HeuristicLab.Icon.ToBitmap());
72      images.Add(ObjectImageName, Common.Resources.VSImageLibrary.Object);
73      images.Add(NothingImageName, Common.Resources.VSImageLibrary.Nothing);
[10332]74    }
75
76    protected override void Dispose(bool disposing) {
77      if (disposing) {
78        if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
79        if (components != null) components.Dispose();
80      }
81      base.Dispose(disposing);
82    }
83
84    protected override void DeregisterContentEvents() {
85      Content.ItemsAdded -= Content_ItemsAdded;
86      Content.ItemsReplaced -= Content_ItemsReplaced;
87      Content.ItemsRemoved -= Content_ItemsRemoved;
88      Content.CollectionReset -= Content_CollectionReset;
89      base.DeregisterContentEvents();
90    }
91    protected override void RegisterContentEvents() {
92      base.RegisterContentEvents();
93      Content.ItemsAdded += Content_ItemsAdded;
94      Content.ItemsReplaced += Content_ItemsReplaced;
95      Content.ItemsRemoved += Content_ItemsRemoved;
96      Content.CollectionReset += Content_CollectionReset;
97    }
98
99    protected override void OnContentChanged() {
100      base.OnContentChanged();
101      variableListView.Items.Clear();
102      itemListViewItemMapping.Clear();
103      if (Content != null) {
104        Caption += " (" + Content.GetType().Name + ")";
105        foreach (var item in Content)
106          AddVariable(item);
107        AdjustListViewColumnSizes();
108        SortItemsListView(SortOrder.Ascending);
109      }
110    }
111
112    protected override void SetEnabledStateOfControls() {
113      base.SetEnabledStateOfControls();
114      if (Content == null) {
115        addButton.Enabled = false;
116        sortAscendingButton.Enabled = false;
117        sortDescendingButton.Enabled = false;
118        removeButton.Enabled = false;
[11721]119        variableListView.LabelEdit = false;
[10332]120      } else {
[11480]121        bool enabled = !Locked && !ReadOnly;
122        addButton.Enabled = enabled;
[10332]123        sortAscendingButton.Enabled = variableListView.Items.Count > 1;
124        sortDescendingButton.Enabled = variableListView.Items.Count > 1;
[11480]125        removeButton.Enabled = enabled && variableListView.SelectedItems.Count > 0;
126        variableListView.LabelEdit = enabled;
[10332]127      }
128    }
129
130    protected virtual object CreateItem() {
131      if (typeSelectorDialog == null) {
132        typeSelectorDialog = new TypeSelectorDialog { Caption = "Select Item" };
133        typeSelectorDialog.TypeSelector.Caption = "Available Items";
134        typeSelectorDialog.TypeSelector.Configure(typeof(IItem), false, true);
135      }
136
137      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
138        try {
139          return (object)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
140        } catch (Exception ex) {
141          ErrorHandling.ShowErrorDialog(this, ex);
142        }
143      }
144      return null;
145    }
146
147    protected virtual void AddVariable(KeyValuePair<string, object> variable) {
148      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
[10358]149      bool serializable = IsSerializable(variable);
[11479]150
[11480]151      var listViewItem = new ListViewItem();
152      AssignVariableToListViewItem(listViewItem, variable);
[11479]153      SetImageKey(listViewItem, serializable);
154      SetToolTipText(listViewItem, serializable);
[10332]155      variableListView.Items.Add(listViewItem);
[11479]156
[10332]157      itemListViewItemMapping[variable.Key] = listViewItem;
158      sortAscendingButton.Enabled = variableListView.Items.Count > 1;
159      sortDescendingButton.Enabled = variableListView.Items.Count > 1;
160      var item = variable.Value as IItem;
161      if (item != null) item.ToStringChanged += item_ToStringChanged;
162    }
163
164    protected virtual void RemoveVariable(KeyValuePair<string, object> variable) {
165      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
[11480]166
[10332]167      ListViewItem listViewItem;
[11480]168      if (!itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem)) return;
169
170      itemListViewItemMapping.Remove(variable.Key);
171      variableListView.Items.Remove(listViewItem);
172      sortAscendingButton.Enabled = variableListView.Items.Count > 1;
173      sortDescendingButton.Enabled = variableListView.Items.Count > 1;
174      var item = variable.Value as IItem;
175      if (item != null) item.ToStringChanged -= item_ToStringChanged;
[10332]176    }
177
178    protected virtual void UpdateVariable(KeyValuePair<string, object> variable) {
179      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
[11480]180
[10332]181      ListViewItem listViewItem;
[11480]182      if (!itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem))
183        throw new ArgumentException("A variable with the specified name does not exist.", "variable");
[11479]184
[11480]185      bool serializable = IsSerializable(variable);
186      AssignVariableToListViewItem(listViewItem, variable);
187      SetImageKey(listViewItem, serializable);
188      SetToolTipText(listViewItem, serializable);
189
[10332]190    }
191
192    #region ListView Events
193    protected virtual void variableListView_SelectedIndexChanged(object sender, EventArgs e) {
[10358]194      removeButton.Enabled = (Content != null) && !Locked && !ReadOnly && variableListView.SelectedItems.Count > 0;
[10332]195      AdjustListViewColumnSizes();
196    }
197    protected virtual void variableListView_KeyDown(object sender, KeyEventArgs e) {
[11436]198      switch (e.KeyCode) {
199        case Keys.Delete:
200          if ((variableListView.SelectedItems.Count > 0) && !Locked && !ReadOnly) {
201            foreach (ListViewItem item in variableListView.SelectedItems)
202              Content.Remove(item.Text);
203          }
204          break;
205        case Keys.F2:
[11474]206          if (variableListView.SelectedItems.Count != 1) return;
207          var selectedItem = variableListView.SelectedItems[0];
208          if (variableListView.LabelEdit)
209            selectedItem.BeginEdit();
[11436]210          break;
211        case Keys.A:
212          if (e.Modifiers.HasFlag(Keys.Control)) {
213            foreach (ListViewItem item in variableListView.Items)
214              item.Selected = true;
215          }
216          break;
[10332]217      }
218    }
219    protected virtual void variableListView_DoubleClick(object sender, EventArgs e) {
[11480]220      if (variableListView.SelectedItems.Count != 1) return;
221      var item = variableListView.SelectedItems[0].Tag as KeyValuePair<string, object>?;
222      if (item == null) return;
223
224      var value = item.Value.Value as IContent;
225      if (value == null) return;
226
227      IContentView view = MainFormManager.MainForm.ShowContent(value);
228      if (view == null) return;
229
230      view.ReadOnly = ReadOnly;
231      view.Locked = Locked;
[10332]232    }
233    protected virtual void variableListView_ItemDrag(object sender, ItemDragEventArgs e) {
[11480]234      if (Locked || variableListView.SelectedItems.Count != 1) return;
235
236      var listViewItem = variableListView.SelectedItems[0];
237      var item = (KeyValuePair<string, object>)listViewItem.Tag;
238      if (!(item.Value is IDeepCloneable)) return;
239      var data = new DataObject(HeuristicLab.Common.Constants.DragDropDataFormat, item);
240      DoDragDrop(data, DragDropEffects.Copy);
[10332]241    }
242    protected virtual void variableListView_DragEnter(object sender, DragEventArgs e) {
[11734]243      validDragOperation = !Locked && !ReadOnly;
244
245      object item = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
246      if (item is KeyValuePair<string, object>) {
247        var variable = (KeyValuePair<string, object>)item;
248        validDragOperation &= variable.Value is IDeepCloneable;
249      } else {
250        validDragOperation &= item is IDeepCloneable;
251      }
[10332]252    }
253    protected virtual void variableListView_DragOver(object sender, DragEventArgs e) {
254      e.Effect = DragDropEffects.None;
255      if (validDragOperation) {
[11734]256        if (e.AllowedEffect.HasFlag(DragDropEffects.Copy))
257          e.Effect = DragDropEffects.Copy;
[10332]258      }
259    }
260    protected virtual void variableListView_DragDrop(object sender, DragEventArgs e) {
[11480]261      if (e.Effect != DragDropEffects.Copy) return;
262      object item = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
[11474]263
[11480]264      string variableName;
265      bool editLabel;
[11479]266
[11480]267      if (item is KeyValuePair<string, object>) {
268        var variable = (KeyValuePair<string, object>)item;
269        variableName = GenerateNewVariableName(out editLabel, variable.Key, false);
270        item = variable.Value;
271      } else {
272        var namedItem = item as INamedItem;
273        if (namedItem != null)
274          variableName = GenerateNewVariableName(out editLabel, namedItem.Name, false);
275        else
276          variableName = GenerateNewVariableName(out editLabel);
277      }
[11474]278
[11480]279      var cloneable = item as IDeepCloneable;
[11734]280      if (cloneable == null) return;
[11479]281
[11734]282      var clonedItem = cloneable.Clone();
283      Content.Add(variableName, clonedItem);
[11480]284
285      var listViewItem = variableListView.FindItemWithText(variableName);
286      variableListView.SelectedItems.Clear();
287      if (editLabel) listViewItem.BeginEdit();
[10332]288    }
289
[10506]290    private readonly Regex SafeVariableNameRegex = new Regex("^[@]?[_a-zA-Z][_a-zA-Z0-9]*$");
[11436]291    private const string DefaultVariableName = "enter_name";
292
[10332]293    private void variableListView_AfterLabelEdit(object sender, LabelEditEventArgs e) {
[10506]294      string name = e.Label;
[11471]295      if (!string.IsNullOrEmpty(name)) {
[10332]296        var variable = (KeyValuePair<string, object>)variableListView.Items[e.Item].Tag;
[10506]297        if (!Content.ContainsKey(name)) {
[10332]298          Content.Remove(variable.Key);
[10506]299          Content.Add(name, variable.Value);
[10332]300        }
301      }
302      e.CancelEdit = true;
303    }
304    #endregion
305
306    #region Button Events
307    protected virtual void addButton_Click(object sender, EventArgs e) {
[11479]308      object variableValue = CreateItem();
309      if (variableValue == null) return;
[11474]310
[11479]311      string variableName;
312      var namedItem = variableValue as INamedItem;
313      if (namedItem != null)
314        variableName = GenerateNewVariableName(namedItem.Name, false);
315      else
316        variableName = GenerateNewVariableName();
[11474]317
[11479]318      Content.Add(variableName, variableValue);
319
320      var item = variableListView.FindItemWithText(variableName);
321      variableListView.SelectedItems.Clear();
[11474]322      item.BeginEdit();
[10332]323    }
324    protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
325      SortItemsListView(SortOrder.Ascending);
326    }
327    protected virtual void sortDescendingButton_Click(object sender, EventArgs e) {
328      SortItemsListView(SortOrder.Descending);
329    }
330    protected virtual void removeButton_Click(object sender, EventArgs e) {
331      if (variableListView.SelectedItems.Count > 0) {
332        foreach (ListViewItem item in variableListView.SelectedItems)
333          Content.Remove(item.Text);
334        variableListView.SelectedItems.Clear();
335      }
336    }
337    #endregion
338
339    #region Content Events
340    protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
341      if (InvokeRequired)
342        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_ItemsAdded), sender, e);
343      else {
344        foreach (var item in e.Items)
345          AddVariable(item);
346        AdjustListViewColumnSizes();
347      }
348    }
349
350    protected virtual void Content_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
351      if (InvokeRequired)
352        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_ItemsReplaced), sender, e);
353      else {
354        foreach (var item in e.Items)
355          UpdateVariable(item);
356        AdjustListViewColumnSizes();
357      }
358    }
359
360    protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
361      if (InvokeRequired)
362        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_ItemsRemoved), sender, e);
363      else {
364        foreach (var item in e.Items)
365          RemoveVariable(item);
366        AdjustListViewColumnSizes();
367      }
368    }
369    protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
370      if (InvokeRequired)
371        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_CollectionReset), sender, e);
372      else {
373        foreach (var item in e.OldItems)
374          RemoveVariable(item);
375        foreach (var item in e.Items)
376          AddVariable(item);
377        AdjustListViewColumnSizes();
378      }
379    }
380
381    private void item_ToStringChanged(object sender, EventArgs e) {
382      foreach (ListViewItem item in variableListView.Items) {
383        var variable = item.Tag as KeyValuePair<string, object>?;
[11480]384        if (variable == null || variable.Value.Value != sender) continue;
385
386        string value = (variable.Value.Value ?? "null").ToString();
387        item.SubItems[1].Text = value;
388        item.SubItems[2].Text = variable.Value.Value.GetType().ToString();
389        SetToolTipText(item, item.ImageIndex != 0);
[10332]390      }
391    }
392    #endregion
393
394    #region Helpers
395    protected virtual void SortItemsListView(SortOrder sortOrder) {
396      variableListView.Sorting = SortOrder.None;
397      variableListView.Sorting = sortOrder;
398      variableListView.Sorting = SortOrder.None;
399    }
400    protected virtual void AdjustListViewColumnSizes() {
[10506]401      foreach (ColumnHeader ch in variableListView.Columns)
402        ch.Width = -2;
[10332]403    }
404
[11480]405    protected virtual void AssignVariableToListViewItem(ListViewItem listViewItem, KeyValuePair<string, object> variable) {
406      string value = (variable.Value ?? "null").ToString();
407      string type = variable.Value == null ? "null" : variable.Value.GetType().ToString();
408
409      listViewItem.Tag = variable;
410
411      var subItems = listViewItem.SubItems;
412      subItems[0].Text = variable.Key;
413      if (subItems.Count == 1) { // variable information is added; subitems do not exist yet
414        subItems.AddRange(new[] { value, type });
415      } else { // variable information is updated; subitems are changed
416        subItems[1].Text = value;
417        subItems[2].Text = type;
418      }
419    }
420
[11479]421    protected virtual void SetImageKey(ListViewItem listViewItem, bool serializable) {
422      var variable = (KeyValuePair<string, object>)listViewItem.Tag;
423      if (!serializable) listViewItem.ImageKey = ErrorImageName;
424      else if (!SafeVariableNameRegex.IsMatch(variable.Key)) listViewItem.ImageKey = WarningImageName;
425      else if (variable.Value is IItem) listViewItem.ImageKey = HeuristicLabObjectImageName;
426      else if (variable.Value != null) listViewItem.ImageKey = ObjectImageName;
427      else listViewItem.ImageKey = NothingImageName;
428    }
429
430    protected virtual void SetToolTipText(ListViewItem listViewItem, bool serializable) {
431      var variable = (KeyValuePair<string, object>)listViewItem.Tag;
[10332]432      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
[11480]433      string value = listViewItem.SubItems[1].Text;
434      string type = listViewItem.SubItems[2].Text;
435
[10332]436      string[] lines = {
437        "Name: " + variable.Key,
438        "Value: " + value,
[10358]439        "Type: " + type
[10332]440      };
[11480]441
[10358]442      string toolTipText = string.Join(Environment.NewLine, lines);
[11436]443      if (!SafeVariableNameRegex.IsMatch(variable.Key))
444        toolTipText = "Caution: Identifier is no valid C# identifier!" + Environment.NewLine + toolTipText;
[10358]445      if (!serializable)
[10727]446        toolTipText = "Caution: Type is not serializable!" + Environment.NewLine + toolTipText;
[11479]447      listViewItem.ToolTipText = toolTipText;
[10332]448    }
449
[11436]450    private string GenerateNewVariableName(string defaultName = DefaultVariableName, bool generateValidIdentifier = true) {
[11479]451      bool editLabel;
452      return GenerateNewVariableName(out editLabel, defaultName, generateValidIdentifier);
453    }
454
455    private string GenerateNewVariableName(out bool defaultNameExists, string defaultName = DefaultVariableName, bool generateValidIdentifier = true) {
456      if (string.IsNullOrEmpty(defaultName) || generateValidIdentifier && !SafeVariableNameRegex.IsMatch(defaultName))
[11436]457        defaultName = DefaultVariableName;
[10332]458      if (Content.ContainsKey(defaultName)) {
459        int i = 1;
[11436]460        string formatString = generateValidIdentifier ? "{0}{1}" : "{0} ({1})";
[10332]461        string newName;
462        do {
[11436]463          newName = string.Format(formatString, defaultName, i++);
[10332]464        } while (Content.ContainsKey(newName));
[11479]465        defaultNameExists = true;
[10332]466        return newName;
467      }
[11479]468      defaultNameExists = false;
[10332]469      return defaultName;
470    }
[10358]471
472    private bool IsSerializable(KeyValuePair<string, object> variable) {
[11472]473      Type type = null;
[11437]474      bool serializable;
[11472]475
476      if (variable.Value != null) {
477        type = variable.Value.GetType();
478        if (serializableLookup.TryGetValue(type, out serializable))
479          return serializable;
480      }
481
[10358]482      var ser = new Serializer(variable, ConfigurationService.Instance.GetDefaultConfig(new XmlFormat()), "ROOT", true);
483      try {
[11472]484        serializable = ser.Count() > 0; // try to create all serialization tokens
[10358]485      } catch (PersistenceException) {
[11472]486        serializable = false;
[10358]487      }
[11472]488
489      if (type != null)
490        serializableLookup[type] = serializable;
491      return serializable;
[10358]492    }
[10332]493    #endregion
494  }
495}
Note: See TracBrowser for help on using the repository browser.