Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Optimizer/3.3/NewItemDialog.cs @ 13229

Last change on this file since 13229 was 12649, checked in by ascheibe, 9 years ago

#2025 applied ReSharper suggestions to NewItemDialog

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