source: trunk/HeuristicLab.Core.Views/3.3/TypeSelector.cs @ 16430

Last change on this file since 16430 was 16430, checked in by pfleck, 8 months ago

#2845 merged branch into trunk
Reviewed by mkommenda

  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/2839_HiveProjectManagement/HeuristicLab.Core.Views/3.3/TypeSelector.csmergedeligible
    /branches/2915-AbsoluteSymbol/HeuristicLab.Core.Views/3.3/TypeSelector.csmergedeligible
    /stable/HeuristicLab.Core.Views/3.3/TypeSelector.csmergedeligible
    /trunk/HeuristicLab.Core.Views/3.3/TypeSelector.csmergedeligible
    /branches/2892_LR-prediction-intervals/HeuristicLab.Core.Views/3.3/TypeSelector.cs15743-16388
    /branches/Async/HeuristicLab.Core.Views/3.3/TypeSelector.cs13329-15286
    /branches/CloningRefactoring/HeuristicLab.Core.Views/3.3/TypeSelector.cs4656-4721
    /branches/DataAnalysis Refactoring/HeuristicLab.Core.Views/3.3/TypeSelector.cs5471-5808
    /branches/DataAnalysis SolutionEnsembles/HeuristicLab.Core.Views/3.3/TypeSelector.cs5815-6180
    /branches/DataAnalysis/HeuristicLab.Core.Views/3.3/TypeSelector.cs4458-4459,​4462,​4464
    /branches/DataPreprocessing/HeuristicLab.Core.Views/3.3/TypeSelector.cs10085-11101
    /branches/GP.Grammar.Editor/HeuristicLab.Core.Views/3.3/TypeSelector.cs6335
    /branches/GP.Symbols (TimeLag, Diff, Integral)/HeuristicLab.Core.Views/3.3/TypeSelector.cs5060
    /branches/HeuristicLab.DatasetRefactor/sources/HeuristicLab.Core.Views/3.3/TypeSelector.cs11570-12508
    /branches/HeuristicLab.Problems.Orienteering/HeuristicLab.Core.Views/3.3/TypeSelector.cs11130-12721
    /branches/NET40/sources/HeuristicLab.Core.Views/3.3/TypeSelector.cs5138-5162
    /branches/ParallelEngine/HeuristicLab.Core.Views/3.3/TypeSelector.cs5175-5192
    /branches/QAPAlgorithms/HeuristicLab.Core.Views/3.3/TypeSelector.cs6350-6627
    /branches/Restructure trunk solution/HeuristicLab.Core.Views/3.3/TypeSelector.cs6828
    /branches/SuccessProgressAnalysis/HeuristicLab.Core.Views/3.3/TypeSelector.cs5370-5682
    /branches/Trunk/HeuristicLab.Core.Views/3.3/TypeSelector.cs6829-6865
    /branches/VNS/HeuristicLab.Core.Views/3.3/TypeSelector.cs5594-5752
    /branches/histogram/HeuristicLab.Core.Views/3.3/TypeSelector.cs5959-6341
    /branches/symbreg-factors-2650/HeuristicLab.Core.Views/3.3/TypeSelector.cs14232-14825
    /trunk/sources/HeuristicLab.Core.Views/3.3/TypeSelector.cs15406-15681
File size: 19.9 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.Drawing;
25using System.Linq;
26using System.Reflection;
27using System.Text;
28using System.Windows.Forms;
29using HeuristicLab.Common;
30using HeuristicLab.PluginInfrastructure;
31
32namespace HeuristicLab.Core.Views {
33  public partial class TypeSelector : UserControl {
34    protected List<TreeNode> treeNodes;
35    protected string currentSearchString;
36    protected TypeSelectorDialog typeSelectorDialog;
37
38    protected IEnumerable<Type> baseTypes;
39    public IEnumerable<Type> BaseTypes {
40      get { return baseTypes; }
41    }
42    protected bool showNotInstantiableTypes;
43    public bool ShowNotInstantiableTypes {
44      get { return showNotInstantiableTypes; }
45    }
46    protected bool showGenericTypes;
47    public bool ShowGenericTypes {
48      get { return showGenericTypes; }
49    }
50    public string Caption {
51      get { return typesGroupBox.Text; }
52      set {
53        if (InvokeRequired)
54          Invoke(new Action<string>(delegate(string s) { Caption = s; }), value);
55        else
56          typesGroupBox.Text = value;
57      }
58    }
59    public TreeView TypesTreeView {
60      get { return typesTreeView; }
61    }
62    protected Type selectedType;
63    public Type SelectedType {
64      get { return selectedType; }
65      private set {
66        if (value != selectedType) {
67          selectedType = value;
68          OnSelectedTypeChanged();
69        }
70      }
71    }
72
73    public TypeSelector() {
74      InitializeComponent();
75      treeNodes = new List<TreeNode>();
76      currentSearchString = string.Empty;
77      selectedType = null;
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    public virtual void Configure(Type baseType, bool showNotInstantiableTypes, bool showGenericTypes) {
89      Configure(baseType, showNotInstantiableTypes, showGenericTypes, (t) => true);
90    }
91
92    public virtual void Configure(Type baseType, bool showNotInstantiableTypes, bool showGenericTypes, Func<Type, bool> typeCondition) {
93      Configure(new List<Type>() { baseType }, showNotInstantiableTypes, showGenericTypes, true, typeCondition);
94    }
95
96    public virtual void Configure(IEnumerable<Type> baseTypes, bool showNotInstantiableTypes, bool showGenericTypes, bool assignableToAllTypes) {
97      Configure(baseTypes, showNotInstantiableTypes, showGenericTypes, assignableToAllTypes, (t) => { return true; });
98    }
99
100    public virtual void Configure(IEnumerable<Type> baseTypes, bool showNotInstantiableTypes, bool showGenericTypes, bool assignableToAllTypes, Func<Type, bool> typeCondition) {
101      if (baseTypes == null) throw new ArgumentNullException();
102      if (InvokeRequired)
103        Invoke(new Action<IEnumerable<Type>, bool, bool, bool, Func<Type, bool>>(Configure), baseTypes, showNotInstantiableTypes, showGenericTypes, assignableToAllTypes, typeCondition);
104      else {
105        this.baseTypes = baseTypes;
106        this.showNotInstantiableTypes = showNotInstantiableTypes;
107        this.showGenericTypes = showGenericTypes;
108        bool selectedTypeFound = false;
109
110        typeParametersSplitContainer.Panel2Collapsed = !showGenericTypes;
111
112        TreeNode selectedNode = typesTreeView.SelectedNode;
113        typesTreeView.Nodes.Clear();
114        treeNodes.Clear();
115
116        imageList.Images.Clear();
117        imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Class);      // default icon
118        imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Namespace);  // plugins
119        imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Interface);  // interfaces
120        imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Template);   // generic types
121        // additional dictionary for image indexes as imageList.ContainsKey and imageList.IndexOfKey are very slow!
122        var imageNames = new Dictionary<string, int>();
123
124        var plugins = from p in ApplicationManager.Manager.Plugins
125                      orderby p.Name, p.Version ascending
126                      select p;
127        foreach (IPluginDescription plugin in plugins) {
128          TreeNode pluginNode = new TreeNode();
129          pluginNode.Text = string.Format("{0} {1}.{2}", plugin.Name, plugin.Version.Major, plugin.Version.Minor);
130          pluginNode.ImageIndex = 1;
131          pluginNode.SelectedImageIndex = pluginNode.ImageIndex;
132          pluginNode.Tag = plugin;
133
134          var types = from t in ApplicationManager.Manager.GetTypes(BaseTypes, plugin, !ShowNotInstantiableTypes, ShowGenericTypes, assignableToAllTypes)
135                      where typeCondition(t)
136                      orderby t.Name ascending
137                      select t;
138          foreach (Type type in types) {
139            bool valid = (ShowNotInstantiableTypes || type.GetConstructor(Type.EmptyTypes) != null); //check for public default ctor
140            if (valid) {
141              TreeNode typeNode = new TreeNode();
142              string name = ItemAttribute.GetName(type);
143              typeNode.Text = name != null ? name : type.GetPrettyName();
144              typeNode.ImageIndex = 0;
145              if (type.IsInterface) typeNode.ImageIndex = 2;
146              else if (type.ContainsGenericParameters) typeNode.ImageIndex = 3;
147              else if (imageNames.ContainsKey(type.FullName)) typeNode.ImageIndex = imageNames[type.FullName];
148              else {
149                var image = ItemAttribute.GetImage(type);
150                if (image != null) {
151                  imageList.Images.Add(image);
152                  typeNode.ImageIndex = imageList.Images.Count - 1;
153                  imageNames.Add(type.FullName, imageList.Images.Count - 1);
154                }
155              }
156              typeNode.SelectedImageIndex = typeNode.ImageIndex;
157              typeNode.Tag = type;
158              pluginNode.Nodes.Add(typeNode);
159              if (type.Equals(selectedType)) selectedTypeFound = true;
160            }
161          }
162          if (pluginNode.Nodes.Count > 0)
163            treeNodes.Add(pluginNode);
164        }
165        if (!selectedTypeFound) SelectedType = null;
166        foreach (TreeNode node in treeNodes)
167          typesTreeView.Nodes.Add((TreeNode)node.Clone());
168        RestoreSelectedNode(selectedNode);
169        Filter(searchTextBox.Text);
170
171        UpdateTypeParameters();
172      }
173    }
174
175    public virtual void Filter(string searchString) {
176      if (InvokeRequired)
177        Invoke(new Action<string>(Filter), searchString);
178      else {
179        searchString = searchString.ToLower();
180        TreeNode selectedNode = typesTreeView.SelectedNode;
181
182        if (!searchString.Contains(currentSearchString)) {
183          typesTreeView.BeginUpdate();
184          // expand search -> restore all tree nodes
185          typesTreeView.Nodes.Clear();
186          foreach (TreeNode node in treeNodes)
187            typesTreeView.Nodes.Add((TreeNode)node.Clone());
188          typesTreeView.EndUpdate();
189        }
190
191        // remove nodes
192        typesTreeView.BeginUpdate();
193        var searchTokens = searchString.Split(' ');
194        int i = 0;
195        while (i < typesTreeView.Nodes.Count) {
196          var node = typesTreeView.Nodes[i];
197          bool remove = FilterNode(node, searchTokens);
198          if (remove)
199            typesTreeView.Nodes.RemoveAt(i);
200          else i++;
201        }
202        typesTreeView.EndUpdate();
203        currentSearchString = searchString;
204
205        // select first item
206        typesTreeView.BeginUpdate();
207        var firstNode = typesTreeView.Nodes.Count > 0 ? typesTreeView.Nodes[0] : null;
208        while (firstNode != null && !(firstNode.Tag is Type))
209          firstNode = firstNode.NextVisibleNode;
210        if (firstNode != null)
211          typesTreeView.SelectedNode = firstNode;
212
213        if (typesTreeView.Nodes.Count == 0) {
214          SelectedType = null;
215          typesTreeView.Enabled = false;
216        } else {
217          SetTreeNodeVisibility();
218          typesTreeView.Enabled = true;
219        }
220        typesTreeView.EndUpdate();
221
222        RestoreSelectedNode(selectedNode);
223        UpdateDescription();
224      }
225    }
226
227    private bool FilterNode(TreeNode node, string[] searchTokens) {
228      if (node.Tag is IPluginDescription) { // Plugin node
229        int i = 0;
230        while (i < node.Nodes.Count) {
231          bool remove = FilterNode(node.Nodes[i], searchTokens);
232          if (remove)
233            node.Nodes.RemoveAt(i);
234          else i++;
235        }
236        return node.Nodes.Count == 0;
237      }
238      if (node.Tag is Type) { // Type node
239        var text = node.Text;
240        if (searchTokens.Any(searchToken => !text.ToLower().Contains(searchToken))) {
241          var typeTag = (Type)node.Tag;
242          if (typeTag == SelectedType) {
243            SelectedType = null;
244            typesTreeView.SelectedNode = null;
245          }
246          return true;
247        }
248        return false;
249      }
250      throw new InvalidOperationException("Encountered neither a plugin nor a type node during tree traversal.");
251    }
252
253    public virtual object CreateInstanceOfSelectedType(params object[] args) {
254      if (SelectedType == null)
255        throw new InvalidOperationException("No type selected.");
256      else
257        return Activator.CreateInstance(SelectedType, args);
258    }
259
260    protected virtual void UpdateTypeParameters() {
261      typeParametersListView.Items.Clear();
262      if ((SelectedType == null) || !SelectedType.ContainsGenericParameters) {
263        typeParametersGroupBox.Enabled = false;
264        typeParametersSplitContainer.Panel2Collapsed = true;
265      } else {
266        typeParametersGroupBox.Enabled = true;
267        typeParametersSplitContainer.Panel2Collapsed = false;
268        setTypeParameterButton.Enabled = false;
269
270        foreach (Type param in SelectedType.GetGenericArguments()) {
271          if (param.IsGenericParameter) {
272            ListViewItem item = new ListViewItem();
273            item.Text = param.Name;
274
275            item.ToolTipText = "Constraints:";
276            Type[] constraints = param.GetGenericParameterConstraints();
277            if (constraints.Length == 0) {
278              item.ToolTipText += " none";
279            } else {
280              foreach (Type constraint in constraints)
281                item.ToolTipText += " " + constraint.GetPrettyName();
282            }
283
284            item.Tag = param;
285            typeParametersListView.Items.Add(item);
286          }
287        }
288        typeParametersListView.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
289      }
290    }
291
292    protected virtual void SetTypeParameter() {
293      if (typeSelectorDialog == null) {
294        typeSelectorDialog = new TypeSelectorDialog();
295        typeSelectorDialog.Caption = "Select Type of Generic Type Parameter";
296      }
297      Type param = typeParametersListView.SelectedItems[0].Tag as Type;
298      Type[] constraints = param.GetGenericParameterConstraints();
299      bool showNotInstantiableTypes = !param.GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint);
300      typeSelectorDialog.TypeSelector.Configure(constraints, showNotInstantiableTypes, true, true);
301
302      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
303        Type selected = typeSelectorDialog.TypeSelector.SelectedType;
304        Type[] parameters = SelectedType.GetGenericArguments();
305        parameters[param.GenericParameterPosition] = selected;
306        SelectedType = SelectedType.GetGenericTypeDefinition().MakeGenericType(parameters);
307
308        typeParametersListView.SelectedItems[0].Text = param.Name + ": " + selected.GetPrettyName();
309        typeParametersListView.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
310      }
311    }
312
313    protected virtual void UpdateDescription() {
314      descriptionTextBox.Text = string.Empty;
315
316      if (typesTreeView.SelectedNode != null) {
317        IPluginDescription plugin = typesTreeView.SelectedNode.Tag as IPluginDescription;
318        if (plugin != null) {
319          StringBuilder sb = new StringBuilder();
320          sb.Append("Plugin: ").Append(plugin.Name).Append(Environment.NewLine);
321          sb.Append("Version: ").Append(plugin.Version.ToString()).Append(Environment.NewLine);
322          descriptionTextBox.Text = sb.ToString();
323        }
324        Type type = typesTreeView.SelectedNode.Tag as Type;
325        if (type != null) {
326          string description = ItemAttribute.GetDescription(type);
327          if (description != null)
328            descriptionTextBox.Text = description;
329        }
330      } else if (typesTreeView.Nodes.Count == 0) {
331        descriptionTextBox.Text = "No types found";
332      }
333    }
334
335    #region Events
336    public event EventHandler SelectedTypeChanged;
337    protected virtual void OnSelectedTypeChanged() {
338      if (SelectedTypeChanged != null)
339        SelectedTypeChanged(this, EventArgs.Empty);
340    }
341    #endregion
342
343    #region Control Events
344    protected virtual void searchTextBox_TextChanged(object sender, System.EventArgs e) {
345      Filter(searchTextBox.Text);
346    }
347
348    protected virtual void typesTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
349      if (typesTreeView.SelectedNode == null) SelectedType = null;
350      else SelectedType = typesTreeView.SelectedNode.Tag as Type;
351      UpdateTypeParameters();
352      UpdateDescription();
353    }
354    protected virtual void typesTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
355      TreeNode node = (TreeNode)e.Item;
356      Type type = node.Tag as Type;
357      if ((type != null) && (!type.IsInterface) && (!type.IsAbstract) && (!type.HasElementType) && (!type.ContainsGenericParameters)) {
358        object o = Activator.CreateInstance(type);
359        DataObject data = new DataObject();
360        data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, o);
361        DoDragDrop(data, DragDropEffects.Copy);
362      }
363    }
364    protected virtual void typesTreeView_VisibleChanged(object sender, EventArgs e) {
365      if (Visible) SetTreeNodeVisibility();
366    }
367
368    protected virtual void typeParametersListView_SelectedIndexChanged(object sender, EventArgs e) {
369      setTypeParameterButton.Enabled = typeParametersListView.SelectedItems.Count == 1;
370    }
371    protected virtual void typeParametersListView_DoubleClick(object sender, EventArgs e) {
372      if (typeParametersListView.SelectedItems.Count == 1)
373        SetTypeParameter();
374    }
375
376    protected virtual void setTypeParameterButton_Click(object sender, EventArgs e) {
377      SetTypeParameter();
378    }
379    private void searchTextBox_KeyDown(object sender, KeyEventArgs e) {
380      if (typesTreeView.Nodes.Count == 0)
381        return;
382
383      if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) {
384        var selectedNode = typesTreeView.SelectedNode;
385
386        if (selectedNode == null) { // nothing selected => select first
387          if (e.KeyCode == Keys.Down) typesTreeView.SelectedNode = typesTreeView.Nodes.Count > 0 ? typesTreeView.Nodes[0] : null;
388        } else {
389          if (e.KeyCode == Keys.Down && selectedNode.NextVisibleNode != null)
390            typesTreeView.SelectedNode = selectedNode.NextVisibleNode;
391          if (e.KeyCode == Keys.Up && selectedNode.PrevVisibleNode != null)
392            typesTreeView.SelectedNode = selectedNode.PrevVisibleNode;
393        }
394        e.Handled = true;
395      }
396    }
397    private void clearSearchButton_Click(object sender, EventArgs e) {
398      searchTextBox.Text = string.Empty;
399      searchTextBox.Focus();
400    }
401
402    private TreeNode toolStripMenuNode = null;
403    private void typesTreeView_MouseDown(object sender, MouseEventArgs e) {
404      if (e.Button == MouseButtons.Right) {
405        Point coordinates = typesTreeView.PointToClient(Cursor.Position);
406        toolStripMenuNode = typesTreeView.GetNodeAt(coordinates);
407
408        if (toolStripMenuNode != null && coordinates.X >= toolStripMenuNode.Bounds.Left &&
409            coordinates.X <= toolStripMenuNode.Bounds.Right) {
410          typesTreeView.SelectedNode = toolStripMenuNode;
411
412          expandToolStripMenuItem.Enabled =
413            expandToolStripMenuItem.Visible = !toolStripMenuNode.IsExpanded && toolStripMenuNode.Nodes.Count > 0;
414          collapseToolStripMenuItem.Enabled = collapseToolStripMenuItem.Visible = toolStripMenuNode.IsExpanded;
415        } else {
416          expandToolStripMenuItem.Enabled = expandToolStripMenuItem.Visible = false;
417          collapseToolStripMenuItem.Enabled = collapseToolStripMenuItem.Visible = false;
418        }
419        expandAllToolStripMenuItem.Enabled =
420          expandAllToolStripMenuItem.Visible =
421            !typesTreeView.Nodes.OfType<TreeNode>().All(x => TreeNodeIsFullyExpanded(x));
422        collapseAllToolStripMenuItem.Enabled =
423          collapseAllToolStripMenuItem.Visible = typesTreeView.Nodes.OfType<TreeNode>().Any(x => x.IsExpanded);
424        if (contextMenuStrip.Items.Cast<ToolStripMenuItem>().Any(item => item.Enabled))
425          contextMenuStrip.Show(Cursor.Position);
426      }
427    }
428    private bool TreeNodeIsFullyExpanded(TreeNode node) {
429      return (node.Nodes.Count == 0) || (node.IsExpanded && node.Nodes.OfType<TreeNode>().All(x => TreeNodeIsFullyExpanded(x)));
430    }
431    private void expandToolStripMenuItem_Click(object sender, EventArgs e) {
432      typesTreeView.BeginUpdate();
433      if (toolStripMenuNode != null) toolStripMenuNode.ExpandAll();
434      typesTreeView.EndUpdate();
435    }
436    private void expandAllToolStripMenuItem_Click(object sender, EventArgs e) {
437      typesTreeView.BeginUpdate();
438      typesTreeView.ExpandAll();
439      typesTreeView.EndUpdate();
440    }
441    private void collapseToolStripMenuItem_Click(object sender, EventArgs e) {
442      typesTreeView.BeginUpdate();
443      if (toolStripMenuNode != null) toolStripMenuNode.Collapse();
444      typesTreeView.EndUpdate();
445    }
446    private void collapseAllToolStripMenuItem_Click(object sender, EventArgs e) {
447      typesTreeView.BeginUpdate();
448      typesTreeView.CollapseAll();
449      typesTreeView.EndUpdate();
450    }
451    #endregion
452
453    #region Helpers
454    private void RestoreSelectedNode(TreeNode selectedNode) {
455      if (selectedNode != null) {
456        foreach (TreeNode node in typesTreeView.Nodes) {
457          if (node.Text.Equals(selectedNode.Text)) typesTreeView.SelectedNode = node;
458          foreach (TreeNode child in node.Nodes) {
459            if ((child.Text.Equals(selectedNode.Text)) && (child.Tag == selectedNode.Tag))
460              typesTreeView.SelectedNode = child;
461          }
462        }
463        if (typesTreeView.SelectedNode == null) SelectedType = null;
464      }
465    }
466    private void SetTreeNodeVisibility() {
467      TreeNode selectedNode = typesTreeView.SelectedNode;
468      if (string.IsNullOrEmpty(currentSearchString) && (typesTreeView.Nodes.Count > 1)) {
469        typesTreeView.CollapseAll();
470        if (selectedNode != null) typesTreeView.SelectedNode = selectedNode;
471      } else {
472        typesTreeView.ExpandAll();
473      }
474      if (selectedNode != null) selectedNode.EnsureVisible();
475    }
476    #endregion
477  }
478}
Note: See TracBrowser for help on using the repository browser.