Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.Scripting.Views/3.3/VariableStoreView.cs

Last change on this file was 17181, checked in by swagner, 5 years ago

#2875: Merged r17180 from trunk to stable

File size: 21.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 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 HEAL.Attic;
35using HeuristicLab.Persistence.Default.Xml;
36using HeuristicLab.PluginInfrastructure;
37using System.IO;
38
39namespace HeuristicLab.Scripting.Views {
40  [View("ItemCollection View")]
41  [Content(typeof(VariableStore), true)]
42  public partial class VariableStoreView : AsynchronousContentView {
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
51    private readonly Regex SafeVariableNameRegex = new Regex("^[@]?[_a-zA-Z][_a-zA-Z0-9]*$");
52    private const string DefaultVariableName = "enter_name";
53    protected readonly Dictionary<string, ListViewItem> itemListViewItemMapping;
54    protected readonly Dictionary<Type, bool> serializableLookup;
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>();
70      serializableLookup = new Dictionary<Type, bool>();
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);
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;
93
94      foreach (var variable in Content) {
95        var item = variable.Value as IItem;
96        if (item != null) item.ToStringChanged -= item_ToStringChanged;
97      }
98
99      base.DeregisterContentEvents();
100    }
101    protected override void RegisterContentEvents() {
102      base.RegisterContentEvents();
103
104      Content.ItemsAdded += Content_ItemsAdded;
105      Content.ItemsReplaced += Content_ItemsReplaced;
106      Content.ItemsRemoved += Content_ItemsRemoved;
107      Content.CollectionReset += Content_CollectionReset;
108
109      foreach (var variable in Content) {
110        var item = variable.Value as IItem;
111        if (item != null) item.ToStringChanged += item_ToStringChanged;
112      }
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;
135        variableListView.LabelEdit = false;
136      } else {
137        bool enabled = !Locked && !ReadOnly;
138        addButton.Enabled = enabled;
139        sortAscendingButton.Enabled = variableListView.Items.Count > 1;
140        sortDescendingButton.Enabled = variableListView.Items.Count > 1;
141        removeButton.Enabled = enabled && variableListView.SelectedItems.Count > 0;
142        variableListView.LabelEdit = enabled;
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");
165      bool serializable = IsSerializable(variable);
166
167      var listViewItem = new ListViewItem();
168      AssignVariableToListViewItem(listViewItem, variable);
169      SetImageKey(listViewItem, serializable);
170      SetToolTipText(listViewItem, serializable);
171      variableListView.Items.Add(listViewItem);
172
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");
180
181      ListViewItem listViewItem;
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;
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");
192
193      ListViewItem listViewItem;
194      if (!itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem))
195        throw new ArgumentException("A variable with the specified name does not exist.", "variable");
196
197      bool serializable = IsSerializable(variable);
198      AssignVariableToListViewItem(listViewItem, variable);
199      SetImageKey(listViewItem, serializable);
200      SetToolTipText(listViewItem, serializable);
201
202    }
203
204    #region ListView Events
205    protected virtual void variableListView_SelectedIndexChanged(object sender, EventArgs e) {
206      removeButton.Enabled = (Content != null) && !Locked && !ReadOnly && variableListView.SelectedItems.Count > 0;
207      AdjustListViewColumnSizes();
208    }
209    protected virtual void variableListView_KeyDown(object sender, KeyEventArgs e) {
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:
218          if (variableListView.SelectedItems.Count != 1) return;
219          var selectedItem = variableListView.SelectedItems[0];
220          if (variableListView.LabelEdit)
221            selectedItem.BeginEdit();
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;
229      }
230    }
231    protected virtual void variableListView_DoubleClick(object sender, EventArgs e) {
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;
244    }
245    protected virtual void variableListView_ItemDrag(object sender, ItemDragEventArgs e) {
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);
252      DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link);
253    }
254    protected virtual void variableListView_DragEnter(object sender, DragEventArgs e) {
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      }
264    }
265    protected virtual void variableListView_DragOver(object sender, DragEventArgs e) {
266      e.Effect = DragDropEffects.None;
267      if (validDragOperation) {
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;
271      }
272    }
273    protected virtual void variableListView_DragDrop(object sender, DragEventArgs e) {
274      if (e.Effect == DragDropEffects.None) return;
275
276      object item = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
277
278      string variableName;
279      bool editLabel;
280
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      }
292
293      var cloneable = item as IDeepCloneable;
294      if (cloneable == null) return;
295
296      Content.Add(variableName, e.Effect.HasFlag(DragDropEffects.Copy) ? cloneable.Clone() : cloneable);
297
298      var listViewItem = variableListView.FindItemWithText(variableName);
299      variableListView.SelectedItems.Clear();
300      if (editLabel) listViewItem.BeginEdit();
301    }
302
303    private void variableListView_AfterLabelEdit(object sender, LabelEditEventArgs e) {
304      string name = e.Label;
305      if (!string.IsNullOrEmpty(name)) {
306        var variable = (KeyValuePair<string, object>)variableListView.Items[e.Item].Tag;
307        if (!Content.ContainsKey(name)) {
308          Content.Remove(variable.Key);
309          Content.Add(name, variable.Value);
310        }
311      }
312      e.CancelEdit = true;
313    }
314    #endregion
315
316    #region Button Events
317    protected virtual void addButton_Click(object sender, EventArgs e) {
318      object variableValue = CreateItem();
319      if (variableValue == null) return;
320
321      string variableName;
322      var namedItem = variableValue as INamedItem;
323      if (namedItem != null)
324        variableName = GenerateNewVariableName(namedItem.Name, false);
325      else
326        variableName = GenerateNewVariableName();
327
328      Content.Add(variableName, variableValue);
329
330      var item = variableListView.FindItemWithText(variableName);
331      variableListView.SelectedItems.Clear();
332      item.BeginEdit();
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) {
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;
355      }
356      InvokeVariableAction(AddVariable, variables);
357    }
358
359    protected virtual void Content_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
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;
364      }
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);
371    }
372
373    protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
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;
378      }
379      InvokeVariableAction(RemoveVariable, variables);
380    }
381
382    protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
383      if (InvokeRequired) Invoke((Action<object, CollectionItemsChangedEventArgs<KeyValuePair<string, object>>>)Content_CollectionReset, sender, e);
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) {
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;
399
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        }
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() {
416      foreach (ColumnHeader ch in variableListView.Columns)
417        ch.Width = -2;
418    }
419
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
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;
447      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
448      string value = listViewItem.SubItems[1].Text;
449      string type = listViewItem.SubItems[2].Text;
450
451      string[] lines = {
452        "Name: " + variable.Key,
453        "Value: " + value,
454        "Type: " + type
455      };
456
457      string toolTipText = string.Join(Environment.NewLine, lines);
458      if (!SafeVariableNameRegex.IsMatch(variable.Key))
459        toolTipText = "Caution: Identifier is no valid C# identifier!" + Environment.NewLine + toolTipText;
460      if (!serializable)
461        toolTipText = "Caution: Type is not serializable!" + Environment.NewLine + toolTipText;
462      listViewItem.ToolTipText = toolTipText;
463    }
464
465    private string GenerateNewVariableName(string defaultName = DefaultVariableName, bool generateValidIdentifier = true) {
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))
472        defaultName = DefaultVariableName;
473      if (Content.ContainsKey(defaultName)) {
474        int i = 1;
475        string formatString = generateValidIdentifier ? "{0}{1}" : "{0} ({1})";
476        string newName;
477        do {
478          newName = string.Format(formatString, defaultName, i++);
479        } while (Content.ContainsKey(newName));
480        defaultNameExists = true;
481        return newName;
482      }
483      defaultNameExists = false;
484      return defaultName;
485    }
486
487    private bool IsSerializable(KeyValuePair<string, object> variable) {
488      Type type = null;
489      bool serializable = false;
490
491      if (variable.Value != null) {
492        type = variable.Value.GetType();
493        if (serializableLookup.TryGetValue(type, out serializable)) return serializable;
494        if (StorableTypeAttribute.IsStorableType(type)) return serializableLookup[type] = true;
495      }
496
497      var ser = new ProtoBufSerializer();
498      try {
499        using (var memStream = new MemoryStream()) {
500          ser.Serialize(variable.Value, memStream); // try to serialize to memory stream
501          serializable = true;
502        }
503      } catch (PersistenceException) {
504        serializable = false;
505      }
506
507      if (type != null)
508        serializableLookup[type] = serializable;
509      return serializable;
510    }
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    }
521    #endregion
522  }
523}
Note: See TracBrowser for help on using the repository browser.