Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 12547 was 12512, checked in by pfleck, 10 years ago

#2025

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