source: trunk/sources/HeuristicLab.Core.Views/3.3/OperatorTreeView.cs @ 3729

Last change on this file since 3729 was 3729, checked in by swagner, 12 years ago

Fixed exception thrown in OperatorGraph views when removing one of the operator parameters of a MultiOperator (#1004)

File size: 19.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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.Generic;
24using System.ComponentModel;
25using System.Drawing;
26using System.Linq;
27using System.Windows.Forms;
28using HeuristicLab.Collections;
29using HeuristicLab.MainForm;
30
31namespace HeuristicLab.Core.Views {
32  /// <summary>
33  /// The visual representation of an <see cref="OperatorGraph"/>.
34  /// </summary>
35  [View("Operator View (Successors)")]
36  [Content(typeof(IOperator), false)]
37  public sealed partial class OperatorTreeView : ItemView {
38    private Dictionary<IValueParameter, List<TreeNode>> opParamNodeTable;
39    private Dictionary<IOperator, List<TreeNode>> operatorNodeTable;
40    private Dictionary<IKeyedItemCollection<string, IParameter>, IOperator> parametersOperatorTable;
41
42    /// <summary>
43    /// Gets or sets the operator graph to represent visually.
44    /// </summary>
45    /// <remarks>Uses property <see cref="ViewBase.Item"/> of base class <see cref="ViewBase"/>.
46    /// No own data storage present.</remarks>
47    public new IOperator Content {
48      get { return (IOperator)base.Content; }
49      set { base.Content = value; }
50    }
51
52    private IOperator selectedOperator;
53    public IOperator SelectedOperator {
54      get { return selectedOperator; }
55      private set {
56        if (value != selectedOperator) {
57          selectedOperator = value;
58          OnSelectedOperatorChanged();
59        }
60      }
61    }
62
63    /// <summary>
64    /// Initializes a new instance of <see cref="OperatorGraphView"/> with caption "Operator Graph".
65    /// </summary>
66    public OperatorTreeView() {
67      InitializeComponent();
68      graphTreeView.Sorted = true;
69      opParamNodeTable = new Dictionary<IValueParameter, List<TreeNode>>();
70      operatorNodeTable = new Dictionary<IOperator, List<TreeNode>>();
71      parametersOperatorTable = new Dictionary<IKeyedItemCollection<string, IParameter>, IOperator>();
72      Caption = "Operator";
73    }
74
75    /// <summary>
76    /// Updates all controls with the latest data of the model.
77    /// </summary>
78    /// <remarks>Calls <see cref="ViewBase.UpdateControls"/> of base class <see cref="ViewBase"/>.</remarks>
79    protected override void OnContentChanged() {
80      base.OnContentChanged();
81      if (graphTreeView.Nodes.Count > 0)
82        RemoveTreeNode(graphTreeView.Nodes[0]);
83      Caption = "Operator";
84      if (Content != null) {
85        Caption = Content.Name + " (" + Content.GetType().Name + ")";
86        TreeNode root = new TreeNode();
87        FillTreeNode(root, Content);
88        graphTreeView.Nodes.Add(root);
89      }
90      SetEnabledStateOfControls();
91    }
92
93    protected override void OnReadOnlyChanged() {
94      base.OnReadOnlyChanged();
95      SetEnabledStateOfControls();
96    }
97
98    private void SetEnabledStateOfControls() {
99      graphTreeView.Enabled = Content != null;
100    }
101
102    public event EventHandler SelectedOperatorChanged;
103    private void OnSelectedOperatorChanged() {
104      if (SelectedOperatorChanged != null)
105        SelectedOperatorChanged(this, EventArgs.Empty);
106    }
107
108    #region TreeNode Management
109    private TreeNode CreateTreeNode(IValueParameter opParam) {
110      TreeNode node = new TreeNode();
111      node.Text = opParam.Name + ": ";
112      SetOperatorParameterTag(node, opParam);
113
114      if (!opParamNodeTable.ContainsKey(opParam)) {
115        opParamNodeTable.Add(opParam, new List<TreeNode>());
116        opParam.ValueChanged += new EventHandler(opParam_ValueChanged);
117      }
118      opParamNodeTable[opParam].Add(node);
119
120      FillTreeNode(node, (IOperator)opParam.Value);
121      return node;
122    }
123    private void FillTreeNode(TreeNode node, IOperator op) {
124      if (op == null) {
125        node.Text += "-";
126        node.ToolTipText = "";
127        graphTreeView.ImageList.Images.Add(HeuristicLab.Common.Resources.VS2008ImageLibrary.Nothing);
128        node.ImageIndex = graphTreeView.ImageList.Images.Count - 1;
129        node.SelectedImageIndex = node.ImageIndex;
130        node.ForeColor = graphTreeView.ForeColor;
131      } else {
132        node.Text += op.Name;
133        node.ToolTipText = op.ItemName + ": " + op.ItemDescription;
134        graphTreeView.ImageList.Images.Add(op.ItemImage);
135        node.ImageIndex = graphTreeView.ImageList.Images.Count - 1;
136        node.SelectedImageIndex = node.ImageIndex;
137        SetOperatorTag(node, op);
138
139        if (!operatorNodeTable.ContainsKey(op)) {
140          operatorNodeTable.Add(op, new List<TreeNode>());
141          op.ItemImageChanged += new EventHandler(op_ItemImageChanged);
142          op.NameChanged += new EventHandler(op_NameChanged);
143          op.BreakpointChanged += new EventHandler(op_BreakpointChanged);
144          parametersOperatorTable.Add(op.Parameters, op);
145          op.Parameters.ItemsAdded += new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsAdded);
146          op.Parameters.ItemsRemoved += new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsRemoved);
147          op.Parameters.ItemsReplaced += new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsReplaced);
148          op.Parameters.CollectionReset += new CollectionItemsChangedEventHandler<IParameter>(Parameters_CollectionReset);
149        }
150        operatorNodeTable[op].Add(node);
151
152        if (op.Breakpoint) node.ForeColor = Color.Red;
153        else node.ForeColor = graphTreeView.ForeColor;
154
155        foreach (IValueParameter param in op.Parameters.OfType<IValueParameter>()) {
156          if (typeof(IOperator).IsAssignableFrom(param.DataType))
157            node.Nodes.Add(new TreeNode());
158        }
159        node.Collapse();
160      }
161    }
162    private void ClearTreeNode(TreeNode node) {
163      while (node.Nodes.Count > 0)
164        RemoveTreeNode(node.Nodes[0]);
165
166      if (node.ImageIndex != -1) {
167        int i = node.ImageIndex;
168        CorrectImageIndexes(graphTreeView.Nodes, i);
169        graphTreeView.ImageList.Images.RemoveAt(i);
170      }
171
172      IOperator op = GetOperatorTag(node);
173      if (op != null) {
174        operatorNodeTable[op].Remove(node);
175        if (operatorNodeTable[op].Count == 0) {
176          op.ItemImageChanged -= new EventHandler(op_ItemImageChanged);
177          op.NameChanged -= new EventHandler(op_NameChanged);
178          op.BreakpointChanged -= new EventHandler(op_BreakpointChanged);
179          operatorNodeTable.Remove(op);
180          op.Parameters.ItemsAdded -= new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsAdded);
181          op.Parameters.ItemsRemoved -= new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsRemoved);
182          op.Parameters.ItemsReplaced -= new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsReplaced);
183          op.Parameters.CollectionReset -= new CollectionItemsChangedEventHandler<IParameter>(Parameters_CollectionReset);
184          parametersOperatorTable.Remove(op.Parameters);
185        }
186      }
187      SetOperatorTag(node, null);
188    }
189    private void RemoveTreeNode(TreeNode node) {
190      ClearTreeNode(node);
191
192      IValueParameter opParam = GetOperatorParameterTag(node);
193      if (opParam != null) {
194        opParamNodeTable[opParam].Remove(node);
195        if (opParamNodeTable[opParam].Count == 0) {
196          opParam.ValueChanged -= new EventHandler(opParam_ValueChanged);
197          opParamNodeTable.Remove(opParam);
198        }
199      }
200      SetOperatorParameterTag(node, null);
201      node.Remove();
202    }
203    private void AddParameterNodes(IOperator op, IEnumerable<IParameter> parameters) {
204      foreach (IValueParameter param in parameters.OfType<IValueParameter>()) {
205        if (typeof(IOperator).IsAssignableFrom(param.DataType)) {
206          foreach (TreeNode node in operatorNodeTable[op])
207            node.Nodes.Add(CreateTreeNode(param));
208        }
209      }
210    }
211    private void RemoveParameterNodes(IEnumerable<IParameter> parameters) {
212      foreach (IValueParameter param in parameters.OfType<IValueParameter>()) {
213        if (typeof(IOperator).IsAssignableFrom(param.DataType)) {
214          while (opParamNodeTable.ContainsKey(param))
215            RemoveTreeNode(opParamNodeTable[param][0]);
216        }
217      }
218    }
219    #endregion
220
221    #region Parameter and Operator Events
222    private void opParam_ValueChanged(object sender, EventArgs e) {
223      if (InvokeRequired)
224        Invoke(new EventHandler(opParam_ValueChanged), sender, e);
225      else {
226        IValueParameter opParam = (IValueParameter)sender;
227        if (opParamNodeTable.ContainsKey(opParam)) {
228          foreach (TreeNode node in opParamNodeTable[opParam].ToArray())
229            ClearTreeNode(node);
230          foreach (TreeNode node in opParamNodeTable[opParam]) {
231            node.Text = opParam.Name + ": ";
232            FillTreeNode(node, (IOperator)opParam.Value);
233          }
234        }
235      }
236    }
237    void op_ItemImageChanged(object sender, EventArgs e) {
238      if (InvokeRequired)
239        Invoke(new EventHandler(op_ItemImageChanged), sender, e);
240      else {
241        IOperator op = (IOperator)sender;
242        foreach (TreeNode node in operatorNodeTable[op]) {
243          int i = node.ImageIndex;
244          graphTreeView.ImageList.Images[i] = op.ItemImage;
245          node.ImageIndex = -1;
246          node.SelectedImageIndex = -1;
247          node.ImageIndex = i;
248          node.SelectedImageIndex = i;
249        }
250      }
251    }
252    private void op_NameChanged(object sender, EventArgs e) {
253      if (InvokeRequired)
254        Invoke(new EventHandler(op_NameChanged), sender, e);
255      else {
256        IOperator op = (IOperator)sender;
257        foreach (TreeNode node in operatorNodeTable[op]) {
258          IValueParameter opParam = GetOperatorParameterTag(node);
259          if (opParam == null)
260            node.Text = op.Name + " (" + op.ItemName + ")";
261          else
262            node.Text = opParam.Name + ": " + op.Name;
263        }
264      }
265    }
266    private void op_BreakpointChanged(object sender, EventArgs e) {
267      if (InvokeRequired)
268        Invoke(new EventHandler(op_BreakpointChanged), sender, e);
269      else {
270        IOperator op = (IOperator)sender;
271        foreach (TreeNode node in operatorNodeTable[op]) {
272          if (op.Breakpoint)
273            node.ForeColor = Color.Red;
274          else
275            node.ForeColor = graphTreeView.ForeColor;
276        }
277      }
278    }
279
280    private void Parameters_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IParameter> e) {
281      if (InvokeRequired)
282        Invoke(new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsAdded), sender, e);
283      else {
284        IKeyedItemCollection<string, IParameter> coll = (IKeyedItemCollection<string, IParameter>)sender;
285        IOperator op = parametersOperatorTable[coll];
286        AddParameterNodes(op, e.Items);
287      }
288    }
289    private void Parameters_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IParameter> e) {
290      if (InvokeRequired)
291        Invoke(new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsRemoved), sender, e);
292      else
293        RemoveParameterNodes(e.Items);
294    }
295    private void Parameters_ItemsReplaced(object sender, CollectionItemsChangedEventArgs<IParameter> e) {
296      if (InvokeRequired)
297        Invoke(new CollectionItemsChangedEventHandler<IParameter>(Parameters_ItemsReplaced), sender, e);
298      else {
299        RemoveParameterNodes(e.Items);
300        IKeyedItemCollection<string, IParameter> coll = (IKeyedItemCollection<string, IParameter>)sender;
301        IOperator op = parametersOperatorTable[coll];
302        AddParameterNodes(op, e.Items);
303      }
304    }
305    private void Parameters_CollectionReset(object sender, CollectionItemsChangedEventArgs<IParameter> e) {
306      if (InvokeRequired)
307        Invoke(new CollectionItemsChangedEventHandler<IParameter>(Parameters_CollectionReset), sender, e);
308      else {
309        RemoveParameterNodes(e.Items);
310        IKeyedItemCollection<string, IParameter> coll = (IKeyedItemCollection<string, IParameter>)sender;
311        IOperator op = parametersOperatorTable[coll];
312        AddParameterNodes(op, e.Items);
313      }
314    }
315    #endregion
316
317    #region TreeView Events
318    private void graphTreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e) {
319      TreeNode node = e.Node;
320      if ((node.Nodes.Count > 0) && (node.Nodes[0].Tag == null)) {
321        node.Nodes.Clear();
322        IOperator op = GetOperatorTag(node);
323        foreach (IValueParameter param in op.Parameters.OfType<IValueParameter>()) {
324          if (typeof(IOperator).IsAssignableFrom(param.DataType)) node.Nodes.Add(CreateTreeNode(param));
325        }
326      }
327    }
328    private void graphTreeView_MouseDown(object sender, MouseEventArgs e) {
329      TreeNode node = graphTreeView.GetNodeAt(e.X, e.Y);
330      graphTreeView.SelectedNode = node;
331      graphTreeView.Refresh();
332    }
333    private void graphTreeView_KeyDown(object sender, KeyEventArgs e) {
334      if (!ReadOnly && (e.KeyCode == Keys.Delete) && (graphTreeView.SelectedNode != null)) {
335        IValueParameter opParam = GetOperatorParameterTag(graphTreeView.SelectedNode);
336        if (opParam != null) opParam.Value = null;
337      }
338    }
339    private void graphTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
340      SelectedOperator = graphTreeView.SelectedNode == null ? null : GetOperatorTag(graphTreeView.SelectedNode);
341    }
342    private void graphContextMenuStrip_Opening(object sender, CancelEventArgs e) {
343      viewToolStripMenuItem.Enabled = false;
344      breakpointToolStripMenuItem.Enabled = false;
345      breakpointToolStripMenuItem.Checked = false;
346      if (graphTreeView.SelectedNode != null) {
347        IOperator op = GetOperatorTag(graphTreeView.SelectedNode);
348        if (op != null) {
349          Type viewType = MainFormManager.GetDefaultViewType(op.GetType());
350          if (viewType != null) {
351            viewToolStripMenuItem.Enabled = true;
352            viewToolStripMenuItem.Tag = op;
353          }
354          breakpointToolStripMenuItem.Enabled = true;
355          breakpointToolStripMenuItem.Tag = op;
356          if (op.Breakpoint)
357            breakpointToolStripMenuItem.Checked = true;
358        }
359      }
360    }
361    private void graphTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
362      if (!Locked) {
363        TreeNode node = (TreeNode)e.Item;
364        IValueParameter opParam = GetOperatorParameterTag(node);
365        IOperator op = GetOperatorTag(node);
366        DataObject data = new DataObject();
367        data.SetData("Type", op.GetType());
368        data.SetData("Value", op);
369        if (ReadOnly || (opParam == null)) {
370          DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link);
371        } else {
372          DragDropEffects action = DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Link | DragDropEffects.Move);
373          if ((action & DragDropEffects.Move) == DragDropEffects.Move)
374            opParam.Value = null;
375        }
376      }
377    }
378    private void graphTreeView_DragEnterOver(object sender, DragEventArgs e) {
379      e.Effect = DragDropEffects.None;
380      Type type = e.Data.GetData("Type") as Type;
381      if (!ReadOnly && (type != null) && (typeof(IOperator).IsAssignableFrom(type))) {
382        TreeNode node = graphTreeView.GetNodeAt(graphTreeView.PointToClient(new Point(e.X, e.Y)));
383        if ((node != null) && !node.IsExpanded) node.Expand();
384        if ((node != null) && (GetOperatorParameterTag(node) != null)) {
385          if ((e.KeyState & 8) == 8) e.Effect = DragDropEffects.Copy;  // CTRL key
386          else if ((e.KeyState & 4) == 4) e.Effect = DragDropEffects.Move;  // SHIFT key
387          else if ((e.AllowedEffect & DragDropEffects.Link) == DragDropEffects.Link) e.Effect = DragDropEffects.Link;
388          else if ((e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy) e.Effect = DragDropEffects.Copy;
389          else if ((e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move) e.Effect = DragDropEffects.Move;
390        }
391      }
392    }
393    private void graphTreeView_DragDrop(object sender, DragEventArgs e) {
394      if (e.Effect != DragDropEffects.None) {
395        IOperator op = e.Data.GetData("Value") as IOperator;
396        if ((e.Effect & DragDropEffects.Copy) == DragDropEffects.Copy) op = (IOperator)op.Clone();
397        TreeNode node = graphTreeView.GetNodeAt(graphTreeView.PointToClient(new Point(e.X, e.Y)));
398        IValueParameter opParam = GetOperatorParameterTag(node);
399        opParam.Value = op;
400      }
401    }
402    #endregion
403
404    #region Context Menu Events
405    private void viewToolStripMenuItem_Click(object sender, EventArgs e) {
406      IOperator op = ((ToolStripMenuItem)sender).Tag as IOperator;
407      IContentView view = MainFormManager.MainForm.ShowContent(op);
408      if (view != null) {
409        view.ReadOnly = this.ReadOnly;
410        view.Locked = this.Locked;
411      }
412    }
413    private void breakpointToolStripMenuItem_Click(object sender, EventArgs e) {
414      IOperator op = (IOperator)breakpointToolStripMenuItem.Tag;
415      op.Breakpoint = breakpointToolStripMenuItem.Checked;
416    }
417    #endregion
418
419    #region Helpers
420    private class Tuple<T1, T2> {
421      public T1 Item1 { get; set; }
422      public T2 Item2 { get; set; }
423
424      public Tuple() {
425        Item1 = default(T1);
426        Item2 = default(T2);
427      }
428      public Tuple(T1 item1, T2 item2) {
429        Item1 = item1;
430        Item2 = item2;
431      }
432    }
433
434    private IValueParameter GetOperatorParameterTag(TreeNode node) {
435      if (node.Tag != null)
436        return ((Tuple<IValueParameter, IOperator>)node.Tag).Item1;
437      else
438        return null;
439    }
440    private void SetOperatorParameterTag(TreeNode node, IValueParameter opParam) {
441      if (node.Tag == null)
442        node.Tag = new Tuple<IValueParameter, IOperator>(opParam, null);
443      else
444        ((Tuple<IValueParameter, IOperator>)node.Tag).Item1 = opParam;
445    }
446    private IOperator GetOperatorTag(TreeNode node) {
447      if (node.Tag != null)
448        return ((Tuple<IValueParameter, IOperator>)node.Tag).Item2;
449      else
450        return null;
451    }
452    private void SetOperatorTag(TreeNode node, IOperator op) {
453      if (node.Tag == null)
454        node.Tag = new Tuple<IValueParameter, IOperator>(null, op);
455      else
456        ((Tuple<IValueParameter, IOperator>)node.Tag).Item2 = op;
457    }
458
459    private void CorrectImageIndexes(TreeNodeCollection nodes, int removedIndex) {
460      foreach (TreeNode node in nodes) {
461        if (node.ImageIndex > removedIndex) {
462          node.ImageIndex--;
463          node.SelectedImageIndex--;
464        }
465        CorrectImageIndexes(node.Nodes, removedIndex);
466      }
467    }
468    #endregion
469  }
470}
Note: See TracBrowser for help on using the repository browser.