Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 16724 was 16565, checked in by gkronber, 6 years ago

#2520: merged changes from PersistenceOverhaul branch (r16451:16564) into trunk

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