Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 11471 was 11471, checked in by jkarder, 10 years ago

#2262: allow invalid c# identifiers after label edits

File size: 17.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 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.Drawing;
25using System.Linq;
26using System.Text.RegularExpressions;
27using System.Windows.Forms;
28using HeuristicLab.Collections;
29using HeuristicLab.Common;
30using HeuristicLab.Core;
31using HeuristicLab.Core.Views;
32using HeuristicLab.MainForm;
33using HeuristicLab.MainForm.WindowsForms;
34using HeuristicLab.Persistence.Core;
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    protected readonly Dictionary<string, ListViewItem> itemListViewItemMapping;
43    protected readonly Dictionary<Type, bool> serializableLookup;
44    protected TypeSelectorDialog typeSelectorDialog;
45    protected bool validDragOperation;
46
47    public new VariableStore Content {
48      get { return (VariableStore)base.Content; }
49      set { base.Content = value; }
50    }
51
52    public ListView ItemsListView {
53      get { return variableListView; }
54    }
55
56    public VariableStoreView() {
57      InitializeComponent();
58      itemListViewItemMapping = new Dictionary<string, ListViewItem>();
59      serializableLookup = new Dictionary<Type, bool>();
60      variableListView.SmallImageList.Images.AddRange(new Image[] {
61        HeuristicLab.Common.Resources.VSImageLibrary.Error,
62        HeuristicLab.Common.Resources.VSImageLibrary.Warning,
63        HeuristicLab.Common.Resources.VSImageLibrary.Object,
64        HeuristicLab.Common.Resources.VSImageLibrary.Nothing
65      });
66    }
67
68    protected override void Dispose(bool disposing) {
69      if (disposing) {
70        if (typeSelectorDialog != null) typeSelectorDialog.Dispose();
71        if (components != null) components.Dispose();
72      }
73      base.Dispose(disposing);
74    }
75
76    protected override void DeregisterContentEvents() {
77      Content.ItemsAdded -= Content_ItemsAdded;
78      Content.ItemsReplaced -= Content_ItemsReplaced;
79      Content.ItemsRemoved -= Content_ItemsRemoved;
80      Content.CollectionReset -= Content_CollectionReset;
81      base.DeregisterContentEvents();
82    }
83    protected override void RegisterContentEvents() {
84      base.RegisterContentEvents();
85      Content.ItemsAdded += Content_ItemsAdded;
86      Content.ItemsReplaced += Content_ItemsReplaced;
87      Content.ItemsRemoved += Content_ItemsRemoved;
88      Content.CollectionReset += Content_CollectionReset;
89    }
90
91    protected override void OnContentChanged() {
92      base.OnContentChanged();
93      variableListView.Items.Clear();
94      itemListViewItemMapping.Clear();
95      if (Content != null) {
96        Caption += " (" + Content.GetType().Name + ")";
97        foreach (var item in Content)
98          AddVariable(item);
99        AdjustListViewColumnSizes();
100        SortItemsListView(SortOrder.Ascending);
101      }
102    }
103
104    protected override void SetEnabledStateOfControls() {
105      base.SetEnabledStateOfControls();
106      if (Content == null) {
107        addButton.Enabled = false;
108        sortAscendingButton.Enabled = false;
109        sortDescendingButton.Enabled = false;
110        removeButton.Enabled = false;
111        variableListView.Enabled = false;
112      } else {
113        addButton.Enabled = !Locked && !ReadOnly;
114        sortAscendingButton.Enabled = variableListView.Items.Count > 1;
115        sortDescendingButton.Enabled = variableListView.Items.Count > 1;
116        removeButton.Enabled = !Locked && !ReadOnly && variableListView.SelectedItems.Count > 0;
117        variableListView.Enabled = true;
118        variableListView.LabelEdit = !Locked && !ReadOnly;
119      }
120    }
121
122    protected virtual object CreateItem() {
123      if (typeSelectorDialog == null) {
124        typeSelectorDialog = new TypeSelectorDialog { Caption = "Select Item" };
125        typeSelectorDialog.TypeSelector.Caption = "Available Items";
126        typeSelectorDialog.TypeSelector.Configure(typeof(IItem), false, true);
127      }
128
129      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
130        try {
131          return (object)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType();
132        } catch (Exception ex) {
133          ErrorHandling.ShowErrorDialog(this, ex);
134        }
135      }
136      return null;
137    }
138
139    protected virtual void AddVariable(KeyValuePair<string, object> variable) {
140      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
141      string value = (variable.Value ?? "null").ToString();
142      string type = variable.Value == null ? "null" : variable.Value.GetType().ToString();
143      bool serializable = IsSerializable(variable);
144      var listViewItem = new ListViewItem(new[] { variable.Key, value, type }) { ToolTipText = GetToolTipText(variable, serializable), Tag = variable };
145      bool validIdentifier = SafeVariableNameRegex.IsMatch(variable.Key);
146      if (!serializable) listViewItem.ImageIndex = 0;
147      else if (!validIdentifier) listViewItem.ImageIndex = 1;
148      else if (variable.Value != null) listViewItem.ImageIndex = 2;
149      else listViewItem.ImageIndex = 3;
150      variableListView.Items.Add(listViewItem);
151      itemListViewItemMapping[variable.Key] = listViewItem;
152      sortAscendingButton.Enabled = variableListView.Items.Count > 1;
153      sortDescendingButton.Enabled = variableListView.Items.Count > 1;
154      var item = variable.Value as IItem;
155      if (item != null) item.ToStringChanged += item_ToStringChanged;
156    }
157
158    protected virtual void RemoveVariable(KeyValuePair<string, object> variable) {
159      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
160      ListViewItem listViewItem;
161      if (itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem)) {
162        itemListViewItemMapping.Remove(variable.Key);
163        variableListView.Items.Remove(listViewItem);
164        sortAscendingButton.Enabled = variableListView.Items.Count > 1;
165        sortDescendingButton.Enabled = variableListView.Items.Count > 1;
166        var item = variable.Value as IItem;
167        if (item != null) item.ToStringChanged -= item_ToStringChanged;
168      }
169    }
170
171    protected virtual void UpdateVariable(KeyValuePair<string, object> variable) {
172      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
173      ListViewItem listViewItem;
174      if (itemListViewItemMapping.TryGetValue(variable.Key, out listViewItem)) {
175        string value = (variable.Value ?? "null").ToString();
176        string type = variable.Value == null ? "null" : variable.Value.GetType().ToString();
177        bool serializable = IsSerializable(variable);
178        bool validIdentifier = SafeVariableNameRegex.IsMatch(variable.Key);
179        if (!serializable) listViewItem.ImageIndex = 0;
180        else if (!validIdentifier) listViewItem.ImageIndex = 1;
181        else if (variable.Value != null) listViewItem.ImageIndex = 2;
182        else listViewItem.ImageIndex = 3;
183        listViewItem.SubItems[1].Text = value;
184        listViewItem.SubItems[2].Text = type;
185        listViewItem.ToolTipText = GetToolTipText(variable, serializable);
186        listViewItem.Tag = variable;
187      } else throw new ArgumentException("A variable with the specified name does not exist.", "variable");
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          var focusedItem = variableListView.FocusedItem;
205          if (variableListView.LabelEdit && focusedItem.Selected)
206            focusedItem.BeginEdit();
207          break;
208        case Keys.A:
209          if (e.Modifiers.HasFlag(Keys.Control)) {
210            foreach (ListViewItem item in variableListView.Items)
211              item.Selected = true;
212          }
213          break;
214      }
215    }
216    protected virtual void variableListView_DoubleClick(object sender, EventArgs e) {
217      if (variableListView.SelectedItems.Count == 1) {
218        var item = variableListView.SelectedItems[0].Tag as KeyValuePair<string, object>?;
219        if (item != null) {
220          var value = item.Value.Value as IContent;
221          if (value != null) {
222            IContentView view = MainFormManager.MainForm.ShowContent(value);
223            if (view != null) {
224              view.ReadOnly = ReadOnly;
225              view.Locked = Locked;
226            }
227          }
228        }
229      }
230    }
231    protected virtual void variableListView_ItemDrag(object sender, ItemDragEventArgs e) {
232      if (!Locked && variableListView.SelectedItems.Count == 1) {
233        var listViewItem = variableListView.SelectedItems[0];
234        var item = (KeyValuePair<string, object>)listViewItem.Tag;
235        var data = new DataObject(HeuristicLab.Common.Constants.DragDropDataFormat, item.Value);
236        DoDragDrop(data, DragDropEffects.Copy);
237      }
238    }
239    protected virtual void variableListView_DragEnter(object sender, DragEventArgs e) {
240      validDragOperation = !Locked && !ReadOnly && e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) != null;
241    }
242    protected virtual void variableListView_DragOver(object sender, DragEventArgs e) {
243      e.Effect = DragDropEffects.None;
244      if (validDragOperation) {
245        if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy;
246      }
247    }
248    protected virtual void variableListView_DragDrop(object sender, DragEventArgs e) {
249      if (e.Effect == DragDropEffects.Copy) {
250        object item = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat);
251        var cloner = new Cloner();
252        var dc = item as IDeepCloneable;
253        if (dc != null) item = cloner.Clone(dc);
254        var namedItem = item as INamedItem;
255        bool nameValid = namedItem != null && !string.IsNullOrEmpty(namedItem.Name);
256        string name = nameValid ? GenerateNewVariableName(namedItem.Name, false) : GenerateNewVariableName();
257        Content.Add(name, item);
258        var listViewItem = variableListView.FindItemWithText(name);
259        if (!nameValid) listViewItem.BeginEdit();
260      }
261    }
262
263    private readonly Regex SafeVariableNameRegex = new Regex("^[@]?[_a-zA-Z][_a-zA-Z0-9]*$");
264    private const string DefaultVariableName = "enter_name";
265
266    private void variableListView_AfterLabelEdit(object sender, LabelEditEventArgs e) {
267      string name = e.Label;
268      if (!string.IsNullOrEmpty(name)) {
269        var variable = (KeyValuePair<string, object>)variableListView.Items[e.Item].Tag;
270        if (!Content.ContainsKey(name)) {
271          Content.Remove(variable.Key);
272          Content.Add(name, variable.Value);
273        }
274      }
275      e.CancelEdit = true;
276    }
277    #endregion
278
279    #region Button Events
280    protected virtual void addButton_Click(object sender, EventArgs e) {
281      object newVar = CreateItem();
282      if (newVar != null) {
283        string name = GenerateNewVariableName();
284        Content.Add(name, newVar);
285        var item = variableListView.FindItemWithText(name);
286        item.BeginEdit();
287      }
288    }
289    protected virtual void sortAscendingButton_Click(object sender, EventArgs e) {
290      SortItemsListView(SortOrder.Ascending);
291    }
292    protected virtual void sortDescendingButton_Click(object sender, EventArgs e) {
293      SortItemsListView(SortOrder.Descending);
294    }
295    protected virtual void removeButton_Click(object sender, EventArgs e) {
296      if (variableListView.SelectedItems.Count > 0) {
297        foreach (ListViewItem item in variableListView.SelectedItems)
298          Content.Remove(item.Text);
299        variableListView.SelectedItems.Clear();
300      }
301    }
302    #endregion
303
304    #region Content Events
305    protected virtual void Content_ItemsAdded(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
306      if (InvokeRequired)
307        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_ItemsAdded), sender, e);
308      else {
309        foreach (var item in e.Items)
310          AddVariable(item);
311        AdjustListViewColumnSizes();
312      }
313    }
314
315    protected virtual void Content_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
316      if (InvokeRequired)
317        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_ItemsReplaced), sender, e);
318      else {
319        foreach (var item in e.Items)
320          UpdateVariable(item);
321        AdjustListViewColumnSizes();
322      }
323    }
324
325    protected virtual void Content_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
326      if (InvokeRequired)
327        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_ItemsRemoved), sender, e);
328      else {
329        foreach (var item in e.Items)
330          RemoveVariable(item);
331        AdjustListViewColumnSizes();
332      }
333    }
334    protected virtual void Content_CollectionReset(object sender, CollectionItemsChangedEventArgs<KeyValuePair<string, object>> e) {
335      if (InvokeRequired)
336        Invoke(new CollectionItemsChangedEventHandler<KeyValuePair<string, object>>(Content_CollectionReset), sender, e);
337      else {
338        foreach (var item in e.OldItems)
339          RemoveVariable(item);
340        foreach (var item in e.Items)
341          AddVariable(item);
342        AdjustListViewColumnSizes();
343      }
344    }
345
346    private void item_ToStringChanged(object sender, EventArgs e) {
347      foreach (ListViewItem item in variableListView.Items) {
348        var variable = item.Tag as KeyValuePair<string, object>?;
349        if (variable != null && variable.Value.Value == sender) {
350          string value = (variable.Value.Value ?? "null").ToString();
351          item.SubItems[1].Text = value;
352          item.SubItems[2].Text = variable.Value.Value.GetType().ToString();
353          item.ToolTipText = GetToolTipText(variable.Value, item.ImageIndex != 0);
354        }
355      }
356    }
357    #endregion
358
359    #region Helpers
360    protected virtual void SortItemsListView(SortOrder sortOrder) {
361      variableListView.Sorting = SortOrder.None;
362      variableListView.Sorting = sortOrder;
363      variableListView.Sorting = SortOrder.None;
364    }
365    protected virtual void AdjustListViewColumnSizes() {
366      foreach (ColumnHeader ch in variableListView.Columns)
367        ch.Width = -2;
368    }
369
370    private string GetToolTipText(KeyValuePair<string, object> variable, bool serializable) {
371      if (string.IsNullOrEmpty(variable.Key)) throw new ArgumentException("The variable must have a name.", "variable");
372      string value = (variable.Value ?? "null").ToString();
373      string type = variable.Value == null ? "null" : variable.Value.GetType().ToString();
374      string[] lines = {
375        "Name: " + variable.Key,
376        "Value: " + value,
377        "Type: " + type
378      };
379      string toolTipText = string.Join(Environment.NewLine, lines);
380      if (!SafeVariableNameRegex.IsMatch(variable.Key))
381        toolTipText = "Caution: Identifier is no valid C# identifier!" + Environment.NewLine + toolTipText;
382      if (!serializable)
383        toolTipText = "Caution: Type is not serializable!" + Environment.NewLine + toolTipText;
384      return toolTipText;
385    }
386
387    private string GenerateNewVariableName(string defaultName = DefaultVariableName, bool generateValidIdentifier = true) {
388      if (generateValidIdentifier && !SafeVariableNameRegex.IsMatch(defaultName))
389        defaultName = DefaultVariableName;
390      if (Content.ContainsKey(defaultName)) {
391        int i = 1;
392        string formatString = generateValidIdentifier ? "{0}{1}" : "{0} ({1})";
393        string newName;
394        do {
395          newName = string.Format(formatString, defaultName, i++);
396        } while (Content.ContainsKey(newName));
397        return newName;
398      }
399      return defaultName;
400    }
401
402    private bool IsSerializable(KeyValuePair<string, object> variable) {
403      var type = variable.Value.GetType();
404      bool serializable;
405      if (serializableLookup.TryGetValue(type, out serializable))
406        return serializable;
407      var ser = new Serializer(variable, ConfigurationService.Instance.GetDefaultConfig(new XmlFormat()), "ROOT", true);
408      try {
409        return serializableLookup[type] = ser.Count() > 0;
410      } catch (PersistenceException) {
411        return serializableLookup[type] = false;
412      }
413    }
414    #endregion
415  }
416}
Note: See TracBrowser for help on using the repository browser.