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

Last change on this file since 12397 was 12397, checked in by pfleck, 7 years ago

#2387

  • Adapted behavior of the matching in the TypeSelector that it behave the same as the NewItemDialog. The search string is tokenized by space and matches if all tokens are contained, (eg. "Sym Reg" matches "SymbolicRegression...").
  • Enabled navigation with up- and down-arrow in the tree while the search-box is focused.
  • Improved response time of NewItemDialog and TypeSelector when they contain a large amount of nodes.
  • Property svn:mergeinfo set to (toggle deleted branches)
    /stable/HeuristicLab.Core.Views/3.3/TypeSelector.csmergedeligible
    /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/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
File size: 17.2 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.Reflection;
26using System.Text;
27using System.Windows.Forms;
28using HeuristicLab.Common;
29using HeuristicLab.PluginInfrastructure;
30
31namespace HeuristicLab.Core.Views {
32  public partial class TypeSelector : UserControl {
33    protected List<TreeNode> treeNodes;
34    protected string currentSearchString;
35    protected TypeSelectorDialog typeSelectorDialog;
36
37    protected IEnumerable<Type> baseTypes;
38    public IEnumerable<Type> BaseTypes {
39      get { return baseTypes; }
40    }
41    protected bool showNotInstantiableTypes;
42    public bool ShowNotInstantiableTypes {
43      get { return showNotInstantiableTypes; }
44    }
45    protected bool showGenericTypes;
46    public bool ShowGenericTypes {
47      get { return showGenericTypes; }
48    }
49    public string Caption {
50      get { return typesGroupBox.Text; }
51      set {
52        if (InvokeRequired)
53          Invoke(new Action<string>(delegate(string s) { Caption = s; }), value);
54        else
55          typesGroupBox.Text = value;
56      }
57    }
58    public TreeView TypesTreeView {
59      get { return typesTreeView; }
60    }
61    protected Type selectedType;
62    public Type SelectedType {
63      get { return selectedType; }
64      private set {
65        if (value != selectedType) {
66          selectedType = value;
67          OnSelectedTypeChanged();
68        }
69      }
70    }
71
72    public TypeSelector() {
73      InitializeComponent();
74      treeNodes = new List<TreeNode>();
75      currentSearchString = string.Empty;
76      selectedType = null;
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    public virtual void Configure(Type baseType, bool showNotInstantiableTypes, bool showGenericTypes) {
88      Configure(baseType, showNotInstantiableTypes, showGenericTypes, (t) => true);
89    }
90
91    public virtual void Configure(Type baseType, bool showNotInstantiableTypes, bool showGenericTypes, Func<Type, bool> typeCondition) {
92      Configure(new List<Type>() { baseType }, showNotInstantiableTypes, showGenericTypes, true, typeCondition);
93    }
94
95    public virtual void Configure(IEnumerable<Type> baseTypes, bool showNotInstantiableTypes, bool showGenericTypes, bool assignableToAllTypes) {
96      Configure(baseTypes, showNotInstantiableTypes, showGenericTypes, assignableToAllTypes, (t) => { return true; });
97    }
98
99    public virtual void Configure(IEnumerable<Type> baseTypes, bool showNotInstantiableTypes, bool showGenericTypes, bool assignableToAllTypes, Func<Type, bool> typeCondition) {
100      if (baseTypes == null) throw new ArgumentNullException();
101      if (InvokeRequired)
102        Invoke(new Action<IEnumerable<Type>, bool, bool, bool, Func<Type, bool>>(Configure), baseTypes, showNotInstantiableTypes, showGenericTypes, assignableToAllTypes, typeCondition);
103      else {
104        this.baseTypes = baseTypes;
105        this.showNotInstantiableTypes = showNotInstantiableTypes;
106        this.showGenericTypes = showGenericTypes;
107        bool selectedTypeFound = false;
108
109        typeParametersSplitContainer.Panel2Collapsed = !showGenericTypes;
110
111        TreeNode selectedNode = typesTreeView.SelectedNode;
112        typesTreeView.Nodes.Clear();
113        treeNodes.Clear();
114
115        imageList.Images.Clear();
116        imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Class);      // default icon
117        imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Namespace);  // plugins
118        imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Interface);  // interfaces
119        imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Template);   // generic types
120        // additional dictionary for image indexes as imageList.ContainsKey and imageList.IndexOfKey are very slow!
121        var imageNames = new Dictionary<string, int>();
122
123        var plugins = from p in ApplicationManager.Manager.Plugins
124                      orderby p.Name, p.Version ascending
125                      select p;
126        foreach (IPluginDescription plugin in plugins) {
127          TreeNode pluginNode = new TreeNode();
128          pluginNode.Text = string.Format("{0} {1}.{2}", plugin.Name, plugin.Version.Major, plugin.Version.Minor);
129          pluginNode.ImageIndex = 1;
130          pluginNode.SelectedImageIndex = pluginNode.ImageIndex;
131          pluginNode.Tag = plugin;
132
133          var types = from t in ApplicationManager.Manager.GetTypes(BaseTypes, plugin, !ShowNotInstantiableTypes, ShowGenericTypes, assignableToAllTypes)
134                      where typeCondition(t)
135                      orderby t.Name ascending
136                      select t;
137          foreach (Type type in types) {
138            bool valid = (ShowNotInstantiableTypes || type.GetConstructor(Type.EmptyTypes) != null); //check for public default ctor
139            if (valid) {
140              TreeNode typeNode = new TreeNode();
141              string name = ItemAttribute.GetName(type);
142              typeNode.Text = name != null ? name : type.GetPrettyName();
143              typeNode.ImageIndex = 0;
144              if (type.IsInterface) typeNode.ImageIndex = 2;
145              else if (type.ContainsGenericParameters) typeNode.ImageIndex = 3;
146              else if (imageNames.ContainsKey(type.FullName)) typeNode.ImageIndex = imageNames[type.FullName];
147              else {
148                var image = ItemAttribute.GetImage(type);
149                if (image != null) {
150                  imageList.Images.Add(image);
151                  typeNode.ImageIndex = imageList.Images.Count - 1;
152                  imageNames.Add(type.FullName, imageList.Images.Count - 1);
153                }
154              }
155              typeNode.SelectedImageIndex = typeNode.ImageIndex;
156              typeNode.Tag = type;
157              pluginNode.Nodes.Add(typeNode);
158              if (type.Equals(selectedType)) selectedTypeFound = true;
159            }
160          }
161          if (pluginNode.Nodes.Count > 0)
162            treeNodes.Add(pluginNode);
163        }
164        if (!selectedTypeFound) SelectedType = null;
165        foreach (TreeNode node in treeNodes)
166          typesTreeView.Nodes.Add((TreeNode)node.Clone());
167        RestoreSelectedNode(selectedNode);
168        Filter(searchTextBox.Text);
169
170        UpdateTypeParameters();
171      }
172    }
173
174    public virtual void Filter(string searchString) {
175      if (InvokeRequired)
176        Invoke(new Action<string>(Filter), searchString);
177      else {
178        searchString = searchString.ToLower();
179
180        if (!searchString.Contains(currentSearchString)) {
181          typesTreeView.BeginUpdate();
182          // expand search -> restore all tree nodes
183          TreeNode selectedNode = typesTreeView.SelectedNode;
184          typesTreeView.Nodes.Clear();
185          foreach (TreeNode node in treeNodes)
186            typesTreeView.Nodes.Add((TreeNode)node.Clone());
187          RestoreSelectedNode(selectedNode);
188          typesTreeView.EndUpdate();
189        }
190
191
192        // remove nodes
193        typesTreeView.BeginUpdate();
194        var searchTokens = searchString.Split(' ');
195
196        int i = 0;
197        while (i < typesTreeView.Nodes.Count) {
198          var node = typesTreeView.Nodes[i];
199          bool remove = FilterNode(node, searchTokens);
200          if (remove)
201            typesTreeView.Nodes.RemoveAt(i);
202          else i++;
203        }
204        typesTreeView.EndUpdate();
205        currentSearchString = searchString;
206
207        // select first item
208        typesTreeView.BeginUpdate();
209        var firstNode = typesTreeView.Nodes.Count > 0 ? typesTreeView.Nodes[0] : null;
210        while (firstNode != null && !(firstNode.Tag is Type))
211          firstNode = firstNode.NextVisibleNode;
212        if (firstNode != null)
213          typesTreeView.SelectedNode = firstNode;
214
215        if (typesTreeView.Nodes.Count == 0) {
216          SelectedType = null;
217          typesTreeView.Enabled = false;
218        } else {
219          SetTreeNodeVisibility();
220          typesTreeView.Enabled = true;
221        }
222        typesTreeView.EndUpdate();
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      } if (node.Tag is Type) { // Type node
238        var text = node.Text;
239        if (searchTokens.Any(searchToken => !text.ToLower().Contains(searchToken))) {
240          var typeTag = (Type)node.Tag;
241          if (typeTag == SelectedType) {
242            SelectedType = null;
243            typesTreeView.SelectedNode = null;
244          }
245          return true;
246        }
247        return false;
248      }
249      throw new InvalidOperationException("Encountered neither a plugin nor a type node during tree traversal.");
250    }
251
252    public virtual object CreateInstanceOfSelectedType(params object[] args) {
253      if (SelectedType == null)
254        throw new InvalidOperationException("No type selected.");
255      else
256        return Activator.CreateInstance(SelectedType, args);
257    }
258
259    protected virtual void UpdateTypeParameters() {
260      typeParametersListView.Items.Clear();
261      if ((SelectedType == null) || !SelectedType.ContainsGenericParameters) {
262        typeParametersGroupBox.Enabled = false;
263        typeParametersSplitContainer.Panel2Collapsed = true;
264      } else {
265        typeParametersGroupBox.Enabled = true;
266        typeParametersSplitContainer.Panel2Collapsed = false;
267        setTypeParameterButton.Enabled = false;
268
269        foreach (Type param in SelectedType.GetGenericArguments()) {
270          if (param.IsGenericParameter) {
271            ListViewItem item = new ListViewItem();
272            item.Text = param.Name;
273
274            item.ToolTipText = "Constraints:";
275            Type[] constraints = param.GetGenericParameterConstraints();
276            if (constraints.Length == 0) {
277              item.ToolTipText += " none";
278            } else {
279              foreach (Type constraint in constraints)
280                item.ToolTipText += " " + constraint.GetPrettyName();
281            }
282
283            item.Tag = param;
284            typeParametersListView.Items.Add(item);
285          }
286        }
287        typeParametersListView.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
288      }
289    }
290
291    protected virtual void SetTypeParameter() {
292      if (typeSelectorDialog == null) {
293        typeSelectorDialog = new TypeSelectorDialog();
294        typeSelectorDialog.Caption = "Select Type of Generic Type Parameter";
295      }
296      Type param = typeParametersListView.SelectedItems[0].Tag as Type;
297      Type[] constraints = param.GetGenericParameterConstraints();
298      bool showNotInstantiableTypes = !param.GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint);
299      typeSelectorDialog.TypeSelector.Configure(constraints, showNotInstantiableTypes, true, true);
300
301      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
302        Type selected = typeSelectorDialog.TypeSelector.SelectedType;
303        Type[] parameters = SelectedType.GetGenericArguments();
304        parameters[param.GenericParameterPosition] = selected;
305        SelectedType = SelectedType.GetGenericTypeDefinition().MakeGenericType(parameters);
306
307        typeParametersListView.SelectedItems[0].Text = param.Name + ": " + selected.GetPrettyName();
308        typeParametersListView.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
309      }
310    }
311
312    protected virtual void UpdateDescription() {
313      descriptionTextBox.Text = string.Empty;
314
315      if (typesTreeView.SelectedNode != null) {
316        IPluginDescription plugin = typesTreeView.SelectedNode.Tag as IPluginDescription;
317        if (plugin != null) {
318          StringBuilder sb = new StringBuilder();
319          sb.Append("Plugin: ").Append(plugin.Name).Append(Environment.NewLine);
320          sb.Append("Version: ").Append(plugin.Version.ToString()).Append(Environment.NewLine);
321          descriptionTextBox.Text = sb.ToString();
322        }
323        Type type = typesTreeView.SelectedNode.Tag as Type;
324        if (type != null) {
325          string description = ItemAttribute.GetDescription(type);
326          if (description != null)
327            descriptionTextBox.Text = description;
328        }
329      } else if (typesTreeView.Nodes.Count == 0) {
330        descriptionTextBox.Text = "No types found";
331      }
332    }
333
334    #region Events
335    public event EventHandler SelectedTypeChanged;
336    protected virtual void OnSelectedTypeChanged() {
337      if (SelectedTypeChanged != null)
338        SelectedTypeChanged(this, EventArgs.Empty);
339    }
340    #endregion
341
342    #region Control Events
343    protected virtual void searchTextBox_TextChanged(object sender, System.EventArgs e) {
344      Filter(searchTextBox.Text);
345    }
346
347    protected virtual void typesTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
348      if (typesTreeView.SelectedNode == null) SelectedType = null;
349      else SelectedType = typesTreeView.SelectedNode.Tag as Type;
350      UpdateTypeParameters();
351      UpdateDescription();
352    }
353    protected virtual void typesTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
354      TreeNode node = (TreeNode)e.Item;
355      Type type = node.Tag as Type;
356      if ((type != null) && (!type.IsInterface) && (!type.IsAbstract) && (!type.HasElementType) && (!type.ContainsGenericParameters)) {
357        object o = Activator.CreateInstance(type);
358        DataObject data = new DataObject();
359        data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, o);
360        DoDragDrop(data, DragDropEffects.Copy);
361      }
362    }
363    protected virtual void typesTreeView_VisibleChanged(object sender, EventArgs e) {
364      if (Visible) SetTreeNodeVisibility();
365    }
366
367    protected virtual void typeParametersListView_SelectedIndexChanged(object sender, EventArgs e) {
368      setTypeParameterButton.Enabled = typeParametersListView.SelectedItems.Count == 1;
369    }
370    protected virtual void typeParametersListView_DoubleClick(object sender, EventArgs e) {
371      if (typeParametersListView.SelectedItems.Count == 1)
372        SetTypeParameter();
373    }
374
375    protected virtual void setTypeParameterButton_Click(object sender, EventArgs e) {
376      SetTypeParameter();
377    }
378    private void searchTextBox_KeyDown(object sender, KeyEventArgs e) {
379      if (typesTreeView.Nodes.Count == 0)
380        return;
381
382      if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) {
383        var selectedNode = typesTreeView.SelectedNode;
384
385        if (selectedNode == null) { // nothing selected => select first
386          if (e.KeyCode == Keys.Down) typesTreeView.SelectedNode = typesTreeView.Nodes.Count > 0 ? typesTreeView.Nodes[0] : null;
387        } else {
388          if (e.KeyCode == Keys.Down && selectedNode.NextVisibleNode != null)
389            typesTreeView.SelectedNode = selectedNode.NextVisibleNode;
390          if (e.KeyCode == Keys.Up && selectedNode.PrevVisibleNode != null)
391            typesTreeView.SelectedNode = selectedNode.PrevVisibleNode;
392        }
393        e.Handled = true;
394      }
395    }
396    #endregion
397
398    #region Helpers
399    private void RestoreSelectedNode(TreeNode selectedNode) {
400      if (selectedNode != null) {
401        foreach (TreeNode node in typesTreeView.Nodes) {
402          if (node.Text.Equals(selectedNode.Text)) typesTreeView.SelectedNode = node;
403          foreach (TreeNode child in node.Nodes) {
404            if ((child.Text.Equals(selectedNode.Text)) && (child.Tag == selectedNode.Tag))
405              typesTreeView.SelectedNode = child;
406          }
407        }
408        if (typesTreeView.SelectedNode == null) SelectedType = null;
409      }
410    }
411    private void SetTreeNodeVisibility() {
412      TreeNode selectedNode = typesTreeView.SelectedNode;
413      if (string.IsNullOrEmpty(currentSearchString) && (typesTreeView.Nodes.Count > 1)) {
414        typesTreeView.CollapseAll();
415        if (selectedNode != null) typesTreeView.SelectedNode = selectedNode;
416      } else {
417        typesTreeView.ExpandAll();
418      }
419      if (selectedNode != null) selectedNode.EnsureVisible();
420    }
421    #endregion
422  }
423}
Note: See TracBrowser for help on using the repository browser.