Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
07/10/15 12:11:55 (9 years ago)
Author:
mkommend
Message:

#2025:Merged all changes regarding the new item dialog into stable.
#2387: Merged all changes regarding the type selector into stable.

Location:
stable
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • stable

  • stable/HeuristicLab.Optimizer

    • Property svn:mergeinfo set to (toggle deleted branches)
      /branches/HLScript/HeuristicLab.Optimizermergedeligible
      /trunk/sources/HeuristicLab.Optimizermergedeligible
      /branches/1721-RandomForestPersistence/HeuristicLab.Optimizer10321-10322
      /branches/Algorithms.GradientDescent/HeuristicLab.Optimizer5516-5520
      /branches/Benchmarking/sources/HeuristicLab.Optimizer6917-7005
      /branches/CloningRefactoring/HeuristicLab.Optimizer4656-4721
      /branches/CodeEditor/HeuristicLab.Optimizer11700-11806
      /branches/DataAnalysis Refactoring/HeuristicLab.Optimizer5471-5808
      /branches/DataAnalysis SolutionEnsembles/HeuristicLab.Optimizer5815-6180
      /branches/DataAnalysis/HeuristicLab.Optimizer4458-4459,​4462,​4464
      /branches/DataPreprocessing/HeuristicLab.Optimizer10085-11101
      /branches/GP.Grammar.Editor/HeuristicLab.Optimizer6284-6795
      /branches/GP.Symbols (TimeLag, Diff, Integral)/HeuristicLab.Optimizer5060
      /branches/HeuristicLab.DatasetRefactor/sources/HeuristicLab.Optimizer11570-12508
      /branches/HeuristicLab.Problems.DataAnalysis.Trading/HeuristicLab.Optimizer6123-9799
      /branches/LogResidualEvaluator/HeuristicLab.Optimizer10202-10483
      /branches/NET40/sources/HeuristicLab.Optimizer5138-5162
      /branches/NSGA-II Changes/HeuristicLab.Optimizer12033-12122
      /branches/NewItemDialog/HeuristicLab.Optimizer12184-12284
      /branches/ParallelEngine/HeuristicLab.Optimizer5175-5192
      /branches/ProblemInstancesRegressionAndClassification/HeuristicLab.Optimizer7568-7810
      /branches/QAPAlgorithms/HeuristicLab.Optimizer6350-6627
      /branches/Restructure trunk solution/HeuristicLab.Optimizer6828
      /branches/RuntimeOptimizer/HeuristicLab.Optimizer8943-9078
      /branches/ScatterSearch (trunk integration)/HeuristicLab.Optimizer7787-8333
      /branches/SlaveShutdown/HeuristicLab.Optimizer8944-8956
      /branches/SpectralKernelForGaussianProcesses/HeuristicLab.Optimizer10204-10479
      /branches/SuccessProgressAnalysis/HeuristicLab.Optimizer5370-5682
      /branches/Trunk/HeuristicLab.Optimizer6829-6865
      /branches/UnloadJobs/HeuristicLab.Optimizer9168-9215
      /branches/VNS/HeuristicLab.Optimizer5594-5752
      /branches/histogram/HeuristicLab.Optimizer5959-6341
  • stable/HeuristicLab.Optimizer/3.3/NewItemDialog.cs

    r12009 r12708  
    2121
    2222using System;
     23using System.Collections;
     24using System.Collections.Generic;
    2325using System.Drawing;
    2426using System.Linq;
    2527using System.Windows.Forms;
     28using HeuristicLab.Common;
     29using HeuristicLab.Common.Resources;
    2630using HeuristicLab.Core;
    2731using HeuristicLab.PluginInfrastructure;
     
    2933namespace HeuristicLab.Optimizer {
    3034  internal partial class NewItemDialog : Form {
    31     private bool initialized;
     35    private bool isInitialized;
     36
     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    }
    3250
    3351    private IItem item;
     
    3755
    3856    public NewItemDialog() {
    39       initialized = false;
     57      InitializeComponent();
     58      treeNodes = new List<TreeNode>();
     59      currentSearchString = string.Empty;
    4060      item = null;
    41       InitializeComponent();
     61      SelectedTypeChanged += this_SelectedTypeChanged;
    4262    }
    4363
    4464    private void NewItemDialog_Load(object sender, EventArgs e) {
    45       if (!initialized) {
    46         var categories = from t in ApplicationManager.Manager.GetTypes(typeof(IItem))
    47                          where CreatableAttribute.IsCreatable(t)
    48                          orderby CreatableAttribute.GetCategory(t), ItemAttribute.GetName(t), ItemAttribute.GetVersion(t) ascending
    49                          group t by CreatableAttribute.GetCategory(t) into c
    50                          select c;
    51 
    52         itemsListView.SmallImageList = new ImageList();
    53         itemsListView.SmallImageList.Images.Add(HeuristicLab.Common.Resources.VSImageLibrary.Class);  // default icon
    54         foreach (var category in categories) {
    55           ListViewGroup group = new ListViewGroup(category.Key);
    56           itemsListView.Groups.Add(group);
    57           foreach (var creatable in category) {
    58             string name = ItemAttribute.GetName(creatable);
    59             string version = ItemAttribute.GetVersion(creatable).ToString();
    60             string description = ItemAttribute.GetDescription(creatable);
    61             ListViewItem item = new ListViewItem(new string[] { name, version, description }, group);
    62             item.ImageIndex = 0;
    63             Image image = ItemAttribute.GetImage(creatable);
    64             if (image != null) {
    65               itemsListView.SmallImageList.Images.Add(image);
    66               item.ImageIndex = itemsListView.SmallImageList.Images.Count - 1;
    67             }
    68             item.Tag = creatable;
    69             itemsListView.Items.Add(item);
     65      if (isInitialized) return;
     66
     67      // Sorted by hasOrdering to create category nodes first with concrete ordering.
     68      // Items with categoryname without ordering are inserted afterwards correctly
     69      var categories =
     70        from type in ApplicationManager.Manager.GetTypes(typeof(IItem))
     71        where CreatableAttribute.IsCreatable(type)
     72        let category = CreatableAttribute.GetCategory(type)
     73        let hasOrdering = category.Contains(CreatableAttribute.Categories.OrderToken)
     74        let name = ItemAttribute.GetName(type)
     75        let priority = CreatableAttribute.GetPriority(type)
     76        let version = ItemAttribute.GetVersion(type)
     77        orderby category, hasOrdering descending, priority, name, version ascending
     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) {
     96      imageList.Images.Add(VSImageLibrary.Class);      // default icon
     97      imageList.Images.Add(VSImageLibrary.Namespace);  // plugins
     98
     99      var rootNode = new TreeNode();
     100
     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
     106      foreach (var category in categories) {
     107        var rawName = category.Key;
     108        string fullName = CreatableAttribute.Categories.GetFullName(rawName);
     109        string name = CreatableAttribute.Categories.GetName(rawName);
     110
     111        // Skip categories with same full name because the raw name can still be different (missing order)
     112        if (rootNode.Nodes.Find(fullName, true).Length > 0)
     113          continue;
     114
     115        var categoryNode = new TreeNode(name, 1, 1) {
     116          Name = fullName,
     117          Tag = rawName
     118        };
     119
     120        var parents = CreatableAttribute.Categories.GetParentRawNames(rawName);
     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    }
     130    private TreeNode FindOrCreateParentNode(TreeNode node, IEnumerable<string> rawParentNames) {
     131      TreeNode parentNode = null;
     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);
     136        parentNode = node.Nodes.Find(fullName, false).SingleOrDefault();
     137        if (parentNode == null) {
     138          var name = CreatableAttribute.Categories.GetName(rawName);
     139          parentNode = new TreeNode(name, 1, 1) {
     140            Name = fullName,
     141            Tag = rawName
     142          };
     143          node.Nodes.Add(parentNode);
     144        }
     145        node = parentNode;
     146      }
     147      return parentNode;
     148    }
     149    private void CreateItemNodes(TreeNode node, IEnumerable<IGrouping<string, Type>> categories) {
     150      foreach (var category in categories) {
     151        var fullName = CreatableAttribute.Categories.GetFullName(category.Key);
     152        var categoryNode = node.Nodes.Find(fullName, true).Single();
     153        foreach (var creatable in category) {
     154          var itemNode = CreateItemNode(creatable);
     155          itemNode.Name = itemNode.Name + ":" + fullName;
     156          categoryNode.Nodes.Add(itemNode);
     157        }
     158      }
     159    }
     160    private TreeNode CreateItemNode(Type creatable) {
     161      string name = ItemAttribute.GetName(creatable);
     162
     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
     179    private void NewItemDialog_Shown(object sender, EventArgs e) {
     180      searchTextBox.Text = string.Empty;
     181      searchTextBox.Focus();
     182      SelectedType = null;
     183      typesTreeView.SelectedNode = null;
     184      UpdateDescription();
     185
     186      foreach (TreeNode node in typesTreeView.Nodes)
     187        node.Expand();
     188      typesTreeView.Nodes[0].EnsureVisible();
     189    }
     190
     191    public virtual void Filter(string searchString) {
     192      if (InvokeRequired) {
     193        Invoke(new Action<string>(Filter), searchString);
     194      } else {
     195        searchString = searchString.ToLower();
     196        var selectedNode = typesTreeView.SelectedNode;
     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
     222        typesTreeView.BeginUpdate();
     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        }
     236        typesTreeView.EndUpdate();
     237
     238        RestoreSelectedNode(selectedNode);
     239        UpdateDescription();
     240      }
     241    }
     242
     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;
    70260          }
    71         }
    72         for (int i = 0; i < itemsListView.Columns.Count; i++)
    73           itemsListView.Columns[i].AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
    74         initialized = true;
    75       }
    76     }
    77 
    78     private void NewItemDialog_Shown(object sender, EventArgs e) {
    79       item = null;
    80     }
    81 
    82     private void itemTypesListView_SelectedIndexChanged(object sender, EventArgs e) {
    83       okButton.Enabled = itemsListView.SelectedItems.Count == 1;
    84     }
     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) {
     275        var node = typesTreeView.SelectedNode;
     276        string category = node.Tag as string;
     277        if (category != null) {
     278          itemDescriptionTextBox.Text = string.Join(" - ", node.Name.Split(new[] { CreatableAttribute.Categories.SplitToken }, StringSplitOptions.RemoveEmptyEntries));
     279        }
     280        Type type = node.Tag as Type;
     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) {
     326        var node = typesTreeView.Nodes.Find(selectedNode.Name, true).SingleOrDefault();
     327        if (node != null)
     328          typesTreeView.SelectedNode = node;
     329        if (typesTreeView.SelectedNode == null)
     330          SelectedType = null;
     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
    85344
    86345    private void okButton_Click(object sender, EventArgs e) {
    87       if (itemsListView.SelectedItems.Count == 1) {
    88         item = (IItem)Activator.CreateInstance((Type)itemsListView.SelectedItems[0].Tag);
     346      if (SelectedType != null) {
     347        item = (IItem)Activator.CreateInstance(SelectedType);
    89348        DialogResult = DialogResult.OK;
    90349        Close();
    91350      }
    92351    }
    93     private void itemTypesListView_DoubleClick(object sender, EventArgs e) {
    94       if (itemsListView.SelectedItems.Count == 1) {
    95         item = (IItem)Activator.CreateInstance((Type)itemsListView.SelectedItems[0].Tag);
     352    private void itemTreeView_DoubleClick(object sender, EventArgs e) {
     353      if (SelectedType != null) {
     354        item = (IItem)Activator.CreateInstance(SelectedType);
    96355        DialogResult = DialogResult.OK;
    97356        Close();
    98357      }
    99358    }
     359    private void this_SelectedTypeChanged(object sender, EventArgs e) {
     360      okButton.Enabled = SelectedType != null;
     361    }
     362
     363    private TreeNode toolStripMenuNode;
     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) {
     394      typesTreeView.BeginUpdate();
     395      if (toolStripMenuNode != null) toolStripMenuNode.ExpandAll();
     396      typesTreeView.EndUpdate();
     397    }
     398    private void expandAllToolStripMenuItem_Click(object sender, EventArgs e) {
     399      typesTreeView.BeginUpdate();
     400      typesTreeView.ExpandAll();
     401      typesTreeView.EndUpdate();
     402    }
     403    private void collapseToolStripMenuItem_Click(object sender, EventArgs e) {
     404      typesTreeView.BeginUpdate();
     405      if (toolStripMenuNode != null) toolStripMenuNode.Collapse();
     406      typesTreeView.EndUpdate();
     407    }
     408    private void collapseAllToolStripMenuItem_Click(object sender, EventArgs e) {
     409      typesTreeView.BeginUpdate();
     410      typesTreeView.CollapseAll();
     411      typesTreeView.EndUpdate();
     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
     426        if (selectedNode == null) { // nothing selected => select first
     427          if (e.KeyCode == Keys.Down) typesTreeView.SelectedNode = FirstVisibleNode;
     428        } else {
     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;
     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 {
     445      private static readonly IComparer<string> Comparer = new NaturalStringComparer();
     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) {
     451          return Comparer.Compare((string)lhs.Tag, (string)rhs.Tag);
     452        }
     453        if (lhs.Tag is string) {
     454          return -1;
     455        }
     456        return 1;
     457      }
     458    }
    100459  }
    101460}
Note: See TracChangeset for help on using the changeset viewer.