Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 12088 was 12088, checked in by jkarder, 9 years ago

#2343: fixed value update

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