Free cookie consent management tool by TermsFeed Policy Generator

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

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

#2025

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