Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 16528 was 16526, checked in by jkarder, 6 years ago

#2982: registered and deregistered item_ToStringChanged accordingly

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