Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PersistenceOverhaul/HeuristicLab.Scripting.Views/3.3/VariableStoreView.cs @ 14711

Last change on this file since 14711 was 14711, checked in by gkronber, 7 years ago

#2520

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