Free cookie consent management tool by TermsFeed Policy Generator

source: branches/NewItemDialog/HeuristicLab.Optimizer/3.3/NewItemDialog.cs @ 12238

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

#2025 Fixed bug when filtering nodes.

File size: 15.5 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;
24using System.Collections.Generic;
25using System.Drawing;
26using System.Linq;
27using System.Windows.Forms;
28using HeuristicLab.Core;
29using HeuristicLab.PluginInfrastructure;
30
31namespace HeuristicLab.Optimizer {
32  internal partial class NewItemDialog : Form {
33    private bool isInitialized;
34
35    private List<TreeNode> treeNodes;
36    private string currentSearchString;
37
38    private Type selectedType;
39    public Type SelectedType {
40      get { return selectedType; }
41      private set {
42        if (value != selectedType) {
43          selectedType = value;
44          OnSelectedTypeChanged();
45        }
46      }
47    }
48
49    private IItem item;
50    public IItem Item {
51      get { return item; }
52    }
53
54    public NewItemDialog() {
55      InitializeComponent();
56      treeNodes = new List<TreeNode>();
57      currentSearchString = string.Empty;
58      item = null;
59      SelectedTypeChanged += this_SelectedTypeChanged;
60    }
61
62    private void NewItemDialog_Load(object sender, EventArgs e) {
63      if (isInitialized) return;
64
65      var categories =
66        from type in ApplicationManager.Manager.GetTypes(typeof(IItem))
67        let category = CreatableAttribute.GetCategory(type)
68        let name = ItemAttribute.GetName(type)
69        let version = ItemAttribute.GetVersion(type)
70        where CreatableAttribute.IsCreatable(type)
71        orderby category, name, version ascending
72        group type by category into categoryGroup
73        select categoryGroup;
74
75      var rootNode = CreateCategoryTree(categories);
76      CreateItemNodes(rootNode, categories);
77
78      foreach (TreeNode topNode in rootNode.Nodes)
79        treeNodes.Add(topNode);
80      foreach (var node in treeNodes)
81        typesTreeView.Nodes.Add((TreeNode)node.Clone());
82
83      typesTreeView.TreeViewNodeSorter = new ItemTreeNodeComparer();
84      typesTreeView.Sort();
85
86      isInitialized = true;
87    }
88
89    private TreeNode CreateCategoryTree(IEnumerable<IGrouping<string, Type>> categories) {
90      imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Class);      // default icon
91      imageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Namespace);  // plugins
92
93      var rootNode = new TreeNode();
94
95      foreach (var category in categories) {
96        var fullName = category.Key;
97        var tokens = fullName.Split('#');
98        var name = tokens.Last();
99        var parents = tokens.Take(tokens.Length - 1);
100
101        var categoryNode = new TreeNode(name, 1, 1) {
102          Name = fullName,
103          Tag = fullName
104        };
105
106        var parentNode = FindOrCreateParentNode(rootNode, parents);
107        if (parentNode != null)
108          parentNode.Nodes.Add(categoryNode);
109        else
110          rootNode.Nodes.Add(categoryNode);
111      }
112
113      return rootNode;
114    }
115    private TreeNode FindOrCreateParentNode(TreeNode node, IEnumerable<string> parentCategories) {
116      TreeNode parentNode = null;
117      string fullName = null;
118      foreach (string parentCategory in parentCategories) {
119        fullName = fullName == null ? parentCategory : fullName + "#" + parentCategory;
120        parentNode = node.Nodes.Find(fullName, searchAllChildren: false).SingleOrDefault();
121        if (parentNode == null) {
122          parentNode = new TreeNode(parentCategory, 1, 1) {
123            Name = fullName,
124            Tag = fullName
125          };
126          node.Nodes.Add(parentNode);
127        }
128        node = parentNode;
129      }
130      return parentNode;
131    }
132    private void CreateItemNodes(TreeNode node, IEnumerable<IGrouping<string, Type>> categories) {
133      foreach (var category in categories) {
134        var categoryNode = node.Nodes.Find(category.Key, searchAllChildren: true).Single();
135        foreach (var creatable in category) {
136          var itemNode = CreateItemNode(creatable);
137          itemNode.Name = itemNode.Name + ":" + category.Key;
138          categoryNode.Nodes.Add(itemNode);
139        }
140      }
141    }
142    private TreeNode CreateItemNode(Type creatable) {
143      string name = ItemAttribute.GetName(creatable);
144
145      var itemNode = new TreeNode(name) {
146        ImageIndex = 0,
147        Tag = creatable,
148        Name = name
149      };
150
151      var image = ItemAttribute.GetImage(creatable);
152      if (image != null) {
153        imageList.Images.Add(image);
154        itemNode.ImageIndex = imageList.Images.Count - 1;
155      }
156      itemNode.SelectedImageIndex = itemNode.ImageIndex;
157
158      return itemNode;
159    }
160
161    private void NewItemDialog_Shown(object sender, EventArgs e) {
162      searchTextBox.Text = string.Empty;
163      searchTextBox.Focus();
164    }
165
166    public virtual void Filter(string searchString) {
167      if (InvokeRequired) {
168        Invoke(new Action<string>(Filter), searchString);
169      } else {
170        searchString = searchString.ToLower();
171
172        if (!searchString.Contains(currentSearchString)) {
173          typesTreeView.BeginUpdate();
174          // expand saerch -> restore all tree nodes
175          var selectedNode = typesTreeView.SelectedNode;
176          typesTreeView.Nodes.Clear();
177          foreach (TreeNode node in treeNodes)
178            typesTreeView.Nodes.Add((TreeNode)node.Clone());
179          RestoreSelectedNode(selectedNode);
180          typesTreeView.EndUpdate();
181        }
182
183        // remove nodes
184        typesTreeView.BeginUpdate();
185        var searchTokens = searchString.Split(' ');
186
187        int i = 0;
188        while (i < typesTreeView.Nodes.Count) {
189          var categoryNode = typesTreeView.Nodes[i];
190          bool remove = FilterNode(categoryNode, searchTokens);
191          if (remove)
192            typesTreeView.Nodes.RemoveAt(i);
193          else i++;
194        }
195        typesTreeView.EndUpdate();
196        currentSearchString = searchString;
197
198        // select first item
199        if (SelectedType == null) {
200          var firstNode = FirstVisibleNode;
201          while (firstNode != null && !(firstNode.Tag is Type))
202            firstNode = firstNode.NextVisibleNode;
203          if (firstNode != null)
204            typesTreeView.SelectedNode = firstNode;
205        }
206
207        if (typesTreeView.Nodes.Count == 0) {
208          SelectedType = null;
209          typesTreeView.Enabled = false;
210        } else {
211          SetTreeNodeVisibility();
212          typesTreeView.Enabled = true;
213        }
214        UpdateDescription();
215      }
216    }
217
218    private bool FilterNode(TreeNode node, string[] searchTokens) {
219      if (node.Tag is string) { // Category node
220        int i = 0;
221        while (i < node.Nodes.Count) {
222          bool remove = FilterNode(node.Nodes[i], searchTokens);
223          if (remove)
224            node.Nodes.RemoveAt(i);
225          else i++;
226        }
227        return node.Nodes.Count == 0;
228      }
229      if (node.Tag is Type) { // Type node
230        var text = node.Text;
231        if (searchTokens.Any(searchToken => !text.ToLower().Contains(searchToken))) {
232          var typeTag = (Type)node.Tag;
233          if (typeTag.Equals(SelectedType)) {
234            SelectedType = null;
235            typesTreeView.SelectedNode = null;
236          }
237          return true;
238        }
239        return false;
240      }
241      return true;
242    }
243
244    protected virtual void UpdateDescription() {
245      descriptionTextBox.Text = string.Empty;
246
247      if (typesTreeView.SelectedNode != null) {
248        string category = typesTreeView.SelectedNode.Tag as string;
249        if (category != null) {
250          descriptionTextBox.Text = category;
251          pluginTextBox.Text = string.Empty;
252          versionTextBox.Text = string.Empty;
253        }
254        Type type = typesTreeView.SelectedNode.Tag as Type;
255        if (type != null) {
256          string description = ItemAttribute.GetDescription(type);
257          var version = ItemAttribute.GetVersion(type);
258          var plugin = ApplicationManager.Manager.GetDeclaringPlugin(type);
259          if (description != null)
260            descriptionTextBox.Text = description;
261          if (plugin != null)
262            pluginTextBox.Text = plugin.Name;
263          if (version != null)
264            versionTextBox.Text = version.ToString();
265        }
266      } else if (typesTreeView.Nodes.Count == 0) {
267        descriptionTextBox.Text = "No types found";
268        pluginTextBox.Text = string.Empty;
269        versionTextBox.Text = string.Empty;
270      }
271    }
272
273    #region Events
274    public event EventHandler SelectedTypeChanged;
275    protected virtual void OnSelectedTypeChanged() {
276      if (SelectedTypeChanged != null)
277        SelectedTypeChanged(this, EventArgs.Empty);
278    }
279    #endregion
280
281    #region Control Events
282    protected virtual void searchTextBox_TextChanged(object sender, EventArgs e) {
283      Filter(searchTextBox.Text);
284    }
285
286    protected virtual void itemsTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
287      if (typesTreeView.SelectedNode == null) SelectedType = null;
288      else SelectedType = typesTreeView.SelectedNode.Tag as Type;
289      UpdateDescription();
290    }
291
292    protected virtual void itemsTreeView_VisibleChanged(object sender, EventArgs e) {
293      if (Visible) SetTreeNodeVisibility();
294    }
295    #endregion
296
297    #region Helpers
298    private void RestoreSelectedNode(TreeNode selectedNode) {
299      if (selectedNode != null) {
300        var node = typesTreeView.Nodes.Find(selectedNode.Name, searchAllChildren: true).SingleOrDefault();
301        typesTreeView.SelectedNode = node;
302        if (typesTreeView.SelectedNode == null) SelectedType = null;
303      }
304    }
305    private void SetTreeNodeVisibility() {
306      TreeNode selectedNode = typesTreeView.SelectedNode;
307      if (string.IsNullOrEmpty(currentSearchString) && (typesTreeView.Nodes.Count > 1)) {
308        typesTreeView.CollapseAll();
309        if (selectedNode != null) typesTreeView.SelectedNode = selectedNode;
310      } else {
311        typesTreeView.ExpandAll();
312      }
313      if (selectedNode != null) selectedNode.EnsureVisible();
314    }
315    #endregion
316
317    private void okButton_Click(object sender, EventArgs e) {
318      if (SelectedType != null) {
319        item = (IItem)Activator.CreateInstance(SelectedType);
320        DialogResult = DialogResult.OK;
321        Close();
322      }
323    }
324    private void itemTreeView_DoubleClick(object sender, EventArgs e) {
325      if (SelectedType != null) {
326        item = (IItem)Activator.CreateInstance(SelectedType);
327        DialogResult = DialogResult.OK;
328        Close();
329      }
330    }
331    private void this_SelectedTypeChanged(object sender, EventArgs e) {
332      okButton.Enabled = SelectedType != null;
333    }
334
335    private void expandAllButton_Click(object sender, EventArgs e) {
336      typesTreeView.ExpandAll();
337    }
338    private void collapseAllButton_Click(object sender, EventArgs e) {
339      typesTreeView.CollapseAll();
340    }
341
342    private TreeNode toolStripMenuNode = null;
343    private void typesTreeView_MouseDown(object sender, MouseEventArgs e) {
344      if (e.Button == MouseButtons.Right) {
345        Point coordinates = typesTreeView.PointToClient(Cursor.Position);
346        toolStripMenuNode = typesTreeView.GetNodeAt(coordinates);
347
348        if (toolStripMenuNode != null && coordinates.X >= toolStripMenuNode.Bounds.Left &&
349            coordinates.X <= toolStripMenuNode.Bounds.Right) {
350          typesTreeView.SelectedNode = toolStripMenuNode;
351
352          expandToolStripMenuItem.Enabled =
353            expandToolStripMenuItem.Visible = !toolStripMenuNode.IsExpanded && toolStripMenuNode.Nodes.Count > 0;
354          collapseToolStripMenuItem.Enabled = collapseToolStripMenuItem.Visible = toolStripMenuNode.IsExpanded;
355        } else {
356          expandToolStripMenuItem.Enabled = expandToolStripMenuItem.Visible = false;
357          collapseToolStripMenuItem.Enabled = collapseToolStripMenuItem.Visible = false;
358        }
359        expandAllToolStripMenuItem.Enabled =
360          expandAllToolStripMenuItem.Visible =
361            !typesTreeView.Nodes.OfType<TreeNode>().All(x => TreeNodeIsFullyExpanded(x));
362        collapseAllToolStripMenuItem.Enabled =
363          collapseAllToolStripMenuItem.Visible = typesTreeView.Nodes.OfType<TreeNode>().Any(x => x.IsExpanded);
364        if (contextMenuStrip.Items.Cast<ToolStripMenuItem>().Any(item => item.Enabled))
365          contextMenuStrip.Show(Cursor.Position);
366      }
367    }
368    private bool TreeNodeIsFullyExpanded(TreeNode node) {
369      return (node.Nodes.Count == 0) || (node.IsExpanded && node.Nodes.OfType<TreeNode>().All(x => TreeNodeIsFullyExpanded(x)));
370    }
371
372    private void expandToolStripMenuItem_Click(object sender, EventArgs e) {
373      if (toolStripMenuNode != null) toolStripMenuNode.ExpandAll();
374    }
375    private void expandAllToolStripMenuItem_Click(object sender, EventArgs e) {
376      typesTreeView.ExpandAll();
377    }
378    private void collapseToolStripMenuItem_Click(object sender, EventArgs e) {
379      if (toolStripMenuNode != null) toolStripMenuNode.Collapse();
380    }
381    private void collapseAllToolStripMenuItem_Click(object sender, EventArgs e) {
382      typesTreeView.CollapseAll();
383    }
384
385    private void clearSearchButton_Click(object sender, EventArgs e) {
386      searchTextBox.Text = string.Empty;
387      searchTextBox.Focus();
388    }
389
390    private void searchTextBox_KeyDown(object sender, KeyEventArgs e) {
391      if (typesTreeView.Nodes.Count == 0)
392        return;
393
394      if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) {
395        var selectedNode = typesTreeView.SelectedNode;
396
397        if (selectedNode == null) { // nothing selected => select first or last
398          if (e.KeyCode == Keys.Down) typesTreeView.SelectedNode = FirstVisibleNode;
399          if (e.KeyCode == Keys.Up) typesTreeView.SelectedNode = LastVisibleNode;
400        } else {
401          if (e.KeyCode == Keys.Down)
402            typesTreeView.SelectedNode = selectedNode.NextVisibleNode ?? FirstVisibleNode; // select next or cycle to first
403          if (e.KeyCode == Keys.Up)
404            typesTreeView.SelectedNode = selectedNode.PrevVisibleNode ?? LastVisibleNode; // select prev or cycle to last
405        }
406        e.Handled = true;
407      }
408    }
409
410    private TreeNode FirstVisibleNode {
411      get {
412        return typesTreeView.Nodes.Count > 0 ? typesTreeView.Nodes[0] : null;
413      }
414    }
415    private TreeNode LastVisibleNode {
416      get {
417        var node = FirstVisibleNode;
418        while (node != null && node.NextVisibleNode != null) node = node.NextVisibleNode;
419        return node;
420      }
421    }
422
423    private class ItemTreeNodeComparer : IComparer {
424      public int Compare(object x, object y) {
425        var lhs = (TreeNode)x;
426        var rhs = (TreeNode)y;
427
428        if (lhs.Tag is string && rhs.Tag is string) {
429          return lhs.Name.CompareTo(rhs.Name);
430        } else if (lhs.Tag is string) {
431          return -1;
432        } else
433          return 1;
434      }
435    }
436  }
437}
Note: See TracBrowser for help on using the repository browser.