Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 12398 was 12398, checked in by pfleck, 9 years ago

#2387

  • Added clearSearch-button in TypeSelector.
  • Adapted behavior of TypeSelector and NewItemDialog that a selected node stays selected as long as it matches the search criteria.
  • 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.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.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        TreeNode selectedNode = typesTreeView.SelectedNode;
180
181        if (!searchString.Contains(currentSearchString)) {
182          typesTreeView.BeginUpdate();
183          // expand search -> restore all tree nodes
184          typesTreeView.Nodes.Clear();
185          foreach (TreeNode node in treeNodes)
186            typesTreeView.Nodes.Add((TreeNode)node.Clone());
187          typesTreeView.EndUpdate();
188        }
189
190        // remove nodes
191        typesTreeView.BeginUpdate();
192        var searchTokens = searchString.Split(' ');
193        int i = 0;
194        while (i < typesTreeView.Nodes.Count) {
195          var node = typesTreeView.Nodes[i];
196          bool remove = FilterNode(node, searchTokens);
197          if (remove)
198            typesTreeView.Nodes.RemoveAt(i);
199          else i++;
200        }
201        typesTreeView.EndUpdate();
202        currentSearchString = searchString;
203
204        // select first item
205        typesTreeView.BeginUpdate();
206        var firstNode = typesTreeView.Nodes.Count > 0 ? typesTreeView.Nodes[0] : null;
207        while (firstNode != null && !(firstNode.Tag is Type))
208          firstNode = firstNode.NextVisibleNode;
209        if (firstNode != null)
210          typesTreeView.SelectedNode = firstNode;
211
212        if (typesTreeView.Nodes.Count == 0) {
213          SelectedType = null;
214          typesTreeView.Enabled = false;
215        } else {
216          SetTreeNodeVisibility();
217          typesTreeView.Enabled = true;
218        }
219        typesTreeView.EndUpdate();
220
221        RestoreSelectedNode(selectedNode);
222        UpdateDescription();
223      }
224    }
225
226    private bool FilterNode(TreeNode node, string[] searchTokens) {
227      if (node.Tag is IPluginDescription) { // Plugin node
228        int i = 0;
229        while (i < node.Nodes.Count) {
230          bool remove = FilterNode(node.Nodes[i], searchTokens);
231          if (remove)
232            node.Nodes.RemoveAt(i);
233          else i++;
234        }
235        return node.Nodes.Count == 0;
236      } if (node.Tag is Type) { // Type node
237        var text = node.Text;
238        if (searchTokens.Any(searchToken => !text.ToLower().Contains(searchToken))) {
239          var typeTag = (Type)node.Tag;
240          if (typeTag == SelectedType) {
241            SelectedType = null;
242            typesTreeView.SelectedNode = null;
243          }
244          return true;
245        }
246        return false;
247      }
248      throw new InvalidOperationException("Encountered neither a plugin nor a type node during tree traversal.");
249    }
250
251    public virtual object CreateInstanceOfSelectedType(params object[] args) {
252      if (SelectedType == null)
253        throw new InvalidOperationException("No type selected.");
254      else
255        return Activator.CreateInstance(SelectedType, args);
256    }
257
258    protected virtual void UpdateTypeParameters() {
259      typeParametersListView.Items.Clear();
260      if ((SelectedType == null) || !SelectedType.ContainsGenericParameters) {
261        typeParametersGroupBox.Enabled = false;
262        typeParametersSplitContainer.Panel2Collapsed = true;
263      } else {
264        typeParametersGroupBox.Enabled = true;
265        typeParametersSplitContainer.Panel2Collapsed = false;
266        setTypeParameterButton.Enabled = false;
267
268        foreach (Type param in SelectedType.GetGenericArguments()) {
269          if (param.IsGenericParameter) {
270            ListViewItem item = new ListViewItem();
271            item.Text = param.Name;
272
273            item.ToolTipText = "Constraints:";
274            Type[] constraints = param.GetGenericParameterConstraints();
275            if (constraints.Length == 0) {
276              item.ToolTipText += " none";
277            } else {
278              foreach (Type constraint in constraints)
279                item.ToolTipText += " " + constraint.GetPrettyName();
280            }
281
282            item.Tag = param;
283            typeParametersListView.Items.Add(item);
284          }
285        }
286        typeParametersListView.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
287      }
288    }
289
290    protected virtual void SetTypeParameter() {
291      if (typeSelectorDialog == null) {
292        typeSelectorDialog = new TypeSelectorDialog();
293        typeSelectorDialog.Caption = "Select Type of Generic Type Parameter";
294      }
295      Type param = typeParametersListView.SelectedItems[0].Tag as Type;
296      Type[] constraints = param.GetGenericParameterConstraints();
297      bool showNotInstantiableTypes = !param.GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint);
298      typeSelectorDialog.TypeSelector.Configure(constraints, showNotInstantiableTypes, true, true);
299
300      if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) {
301        Type selected = typeSelectorDialog.TypeSelector.SelectedType;
302        Type[] parameters = SelectedType.GetGenericArguments();
303        parameters[param.GenericParameterPosition] = selected;
304        SelectedType = SelectedType.GetGenericTypeDefinition().MakeGenericType(parameters);
305
306        typeParametersListView.SelectedItems[0].Text = param.Name + ": " + selected.GetPrettyName();
307        typeParametersListView.Columns[0].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
308      }
309    }
310
311    protected virtual void UpdateDescription() {
312      descriptionTextBox.Text = string.Empty;
313
314      if (typesTreeView.SelectedNode != null) {
315        IPluginDescription plugin = typesTreeView.SelectedNode.Tag as IPluginDescription;
316        if (plugin != null) {
317          StringBuilder sb = new StringBuilder();
318          sb.Append("Plugin: ").Append(plugin.Name).Append(Environment.NewLine);
319          sb.Append("Version: ").Append(plugin.Version.ToString()).Append(Environment.NewLine);
320          descriptionTextBox.Text = sb.ToString();
321        }
322        Type type = typesTreeView.SelectedNode.Tag as Type;
323        if (type != null) {
324          string description = ItemAttribute.GetDescription(type);
325          if (description != null)
326            descriptionTextBox.Text = description;
327        }
328      } else if (typesTreeView.Nodes.Count == 0) {
329        descriptionTextBox.Text = "No types found";
330      }
331    }
332
333    #region Events
334    public event EventHandler SelectedTypeChanged;
335    protected virtual void OnSelectedTypeChanged() {
336      if (SelectedTypeChanged != null)
337        SelectedTypeChanged(this, EventArgs.Empty);
338    }
339    #endregion
340
341    #region Control Events
342    protected virtual void searchTextBox_TextChanged(object sender, System.EventArgs e) {
343      Filter(searchTextBox.Text);
344    }
345
346    protected virtual void typesTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
347      if (typesTreeView.SelectedNode == null) SelectedType = null;
348      else SelectedType = typesTreeView.SelectedNode.Tag as Type;
349      UpdateTypeParameters();
350      UpdateDescription();
351    }
352    protected virtual void typesTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
353      TreeNode node = (TreeNode)e.Item;
354      Type type = node.Tag as Type;
355      if ((type != null) && (!type.IsInterface) && (!type.IsAbstract) && (!type.HasElementType) && (!type.ContainsGenericParameters)) {
356        object o = Activator.CreateInstance(type);
357        DataObject data = new DataObject();
358        data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, o);
359        DoDragDrop(data, DragDropEffects.Copy);
360      }
361    }
362    protected virtual void typesTreeView_VisibleChanged(object sender, EventArgs e) {
363      if (Visible) SetTreeNodeVisibility();
364    }
365
366    protected virtual void typeParametersListView_SelectedIndexChanged(object sender, EventArgs e) {
367      setTypeParameterButton.Enabled = typeParametersListView.SelectedItems.Count == 1;
368    }
369    protected virtual void typeParametersListView_DoubleClick(object sender, EventArgs e) {
370      if (typeParametersListView.SelectedItems.Count == 1)
371        SetTypeParameter();
372    }
373
374    protected virtual void setTypeParameterButton_Click(object sender, EventArgs e) {
375      SetTypeParameter();
376    }
377    private void searchTextBox_KeyDown(object sender, KeyEventArgs e) {
378      if (typesTreeView.Nodes.Count == 0)
379        return;
380
381      if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) {
382        var selectedNode = typesTreeView.SelectedNode;
383
384        if (selectedNode == null) { // nothing selected => select first
385          if (e.KeyCode == Keys.Down) typesTreeView.SelectedNode = typesTreeView.Nodes.Count > 0 ? typesTreeView.Nodes[0] : null;
386        } else {
387          if (e.KeyCode == Keys.Down && selectedNode.NextVisibleNode != null)
388            typesTreeView.SelectedNode = selectedNode.NextVisibleNode;
389          if (e.KeyCode == Keys.Up && selectedNode.PrevVisibleNode != null)
390            typesTreeView.SelectedNode = selectedNode.PrevVisibleNode;
391        }
392        e.Handled = true;
393      }
394    }
395    private void clearSearchButton_Click(object sender, EventArgs e) {
396      searchTextBox.Text = string.Empty;
397      searchTextBox.Focus();
398    }
399    #endregion
400
401    #region Helpers
402    private void RestoreSelectedNode(TreeNode selectedNode) {
403      if (selectedNode != null) {
404        foreach (TreeNode node in typesTreeView.Nodes) {
405          if (node.Text.Equals(selectedNode.Text)) typesTreeView.SelectedNode = node;
406          foreach (TreeNode child in node.Nodes) {
407            if ((child.Text.Equals(selectedNode.Text)) && (child.Tag == selectedNode.Tag))
408              typesTreeView.SelectedNode = child;
409          }
410        }
411        if (typesTreeView.SelectedNode == null) SelectedType = null;
412      }
413    }
414    private void SetTreeNodeVisibility() {
415      TreeNode selectedNode = typesTreeView.SelectedNode;
416      if (string.IsNullOrEmpty(currentSearchString) && (typesTreeView.Nodes.Count > 1)) {
417        typesTreeView.CollapseAll();
418        if (selectedNode != null) typesTreeView.SelectedNode = selectedNode;
419      } else {
420        typesTreeView.ExpandAll();
421      }
422      if (selectedNode != null) selectedNode.EnsureVisible();
423    }
424    #endregion
425  }
426}
Note: See TracBrowser for help on using the repository browser.