#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Windows.Forms; using HeuristicLab.Common; using HeuristicLab.Core.Views; using HeuristicLab.MainForm; using HeuristicLab.PluginInfrastructure; namespace HeuristicLab.Encodings.SymbolicExpressionTreeEncoding.Views { [View("Symbolic Expression Grammar Editor")] [Content(typeof(ISymbolicExpressionGrammar), true)] public partial class SymbolicExpressionGrammarEditorView : NamedItemView { public SymbolicExpressionGrammarEditorView() { InitializeComponent(); } public override bool ReadOnly { get { if ((Content != null) && Content.ReadOnly) return true; return base.ReadOnly; } set { if ((Content != null) && Content.ReadOnly) base.ReadOnly = true; else base.ReadOnly = value; } } public new ISymbolicExpressionGrammar Content { get { return (ISymbolicExpressionGrammar)base.Content; } set { base.Content = value; } } private Color treeViewBackColor = Color.Empty; protected override void SetEnabledStateOfControls() { base.SetEnabledStateOfControls(); if (Content == null || Content.ReadOnly || ReadOnly || Locked) { addButton.Enabled = false; removeButton.Enabled = false; copyButton.Enabled = false; treeViewBackColor = symbolsTreeView.BackColor; symbolsTreeView.BackColor = Color.FromArgb(255, 240, 240, 240); } else { addButton.Enabled = true; if (symbolsTreeView.SelectedNode != null && !(symbolsTreeView.SelectedNode.Tag is IReadOnlySymbol)) { removeButton.Enabled = true; copyButton.Enabled = true; } treeViewBackColor = Color.Empty; symbolsTreeView.BackColor = treeViewBackColor; } } protected override void OnContentChanged() { base.OnContentChanged(); if (Content != null) { symbolsTreeView.Nodes.Clear(); UpdateSymbolsTreeView(); symbolsTreeView.CollapseAll(); foreach (var node in IterateTreeNodes()) if (node.Checked) node.Expand(); //mkommend: scrolls to the top node symbolsTreeView.Nodes[0].EnsureVisible(); allowedChildSymbolsControl.Grammar = Content; allowedChildSymbolsControl.Symbol = null; symbolDetailsViewHost.Content = null; } else { symbolsTreeView.Nodes.Clear(); allowedChildSymbolsControl.Grammar = null; symbolDetailsViewHost.Content = null; } } #region events protected override void RegisterContentEvents() { base.RegisterContentEvents(); Content.ReadOnlyChanged += new System.EventHandler(Content_ReadOnlyChanged); Content.Changed += new System.EventHandler(Content_Changed); } protected override void DeregisterContentEvents() { Content.ReadOnlyChanged -= new System.EventHandler(Content_ReadOnlyChanged); Content.Changed -= new System.EventHandler(Content_Changed); base.DeregisterContentEvents(); } private void Content_ReadOnlyChanged(object sender, EventArgs e) { ReadOnly = Content.ReadOnly; } private void Content_Changed(object sender, EventArgs e) { ISymbol symbol = null; if (symbolsTreeView.SelectedNode != null) symbol = (ISymbol)symbolsTreeView.SelectedNode.Tag; allowedChildSymbolsControl.Grammar = Content; UpdateSymbolsTreeView(); if (symbol != null && Content.ContainsSymbol(symbol)) { symbolsTreeView.SelectedNode = IterateTreeNodes().Where(n => n.Tag == symbol).ToList().FirstOrDefault(); UpdateSymbolDetailsViews(); } } #endregion private void UpdateSymbolsTreeView() { var symbols = Content.Symbols.ToList(); foreach (var treeNode in IterateTreeNodes().ToList()) { var symbol = treeNode.Tag as ISymbol; if (!symbols.Contains(symbol)) treeNode.Remove(); } var groupSymbols = symbols.OfType().ToList(); var topLevelSymbols = Content.Symbols.Where(s => !groupSymbols.Any(g => g.Symbols.Contains(s))); UpdateChildTreeNodes(symbolsTreeView.Nodes, topLevelSymbols); RebuildImageList(); } private void UpdateChildTreeNodes(TreeNodeCollection collection, IEnumerable symbols) { foreach (ISymbol symbol in symbols) { if (symbol is ProgramRootSymbol) continue; if (symbol is Defun) continue; TreeNode node = collection.Cast().Where(n => n.Tag == symbol).FirstOrDefault(); if (node == null) { node = new TreeNode(); node.Tag = symbol; collection.Add(node); } node.Checked = symbol.Enabled; node.Text = symbol.Name; var groupSymbol = symbol as GroupSymbol; if (groupSymbol != null) UpdateChildTreeNodes(node.Nodes, groupSymbol.Symbols); } } private void symbolsTreeView_AfterSelect(object sender, TreeViewEventArgs e) { if (e.Action != TreeViewAction.Unknown) UpdateSymbolDetailsViews(); SetEnabledStateOfControls(); } private void symbolsTreeView_BeforeCheck(object sender, TreeViewCancelEventArgs e) { if (e.Action == TreeViewAction.Unknown) return; e.Cancel = Content == null || Content.ReadOnly || ReadOnly || Locked; } private void symbolsTreeView_AfterCheck(object sender, TreeViewEventArgs e) { if (e.Action == TreeViewAction.Unknown) return; Content.StartGrammarManipulation(); allowedChildSymbolsControl.Symbol = null; var symbol = (ISymbol)e.Node.Tag; symbol.Enabled = e.Node.Checked; foreach (var node in IterateTreeNodes()) node.Checked = ((ISymbol)node.Tag).Enabled; Content.FinishedGrammarManipulation(); } #region drag & drop operations private void symbolsTreeView_ItemDrag(object sender, ItemDragEventArgs e) { if (!Locked) { var treeNode = e.Item as TreeNode; var data = new DataObject(); data.SetData(HeuristicLab.Common.Constants.DragDropDataFormat, treeNode.Tag); validDragOperation = true; DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Move); } } private bool validDragOperation; private void symbolsTreeView_DragEnter(object sender, DragEventArgs e) { validDragOperation = false; if (Content == null || Content.ReadOnly || ReadOnly || Locked) return; var data = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat); var symbol = data as ISymbol; if (symbol != null && !(symbol is IReadOnlySymbol)) validDragOperation = true; } private void symbolsTreeView_DragOver(object sender, DragEventArgs e) { e.Effect = DragDropEffects.None; if (validDragOperation) { GroupSymbol groupSymbol = null; Point mouse = symbolsTreeView.PointToClient(new Point(e.X, e.Y)); TreeNode node = symbolsTreeView.GetNodeAt(mouse); if (node == null) return; groupSymbol = node.Tag as GroupSymbol; if (groupSymbol == null) return; var symbol = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat); if (symbol == groupSymbol) return; if (e.AllowedEffect.HasFlag(DragDropEffects.Copy)) e.Effect = DragDropEffects.Copy; } } private void symbolsTreeView_DragDrop(object sender, DragEventArgs e) { var symbol = e.Data.GetData(HeuristicLab.Common.Constants.DragDropDataFormat) as ISymbol; GroupSymbol groupSymbol = null; Point mouse = symbolsTreeView.PointToClient(new Point(e.X, e.Y)); TreeNode node = symbolsTreeView.GetNodeAt(mouse); if (node != null) groupSymbol = node.Tag as GroupSymbol; if (node != null && groupSymbol == null) groupSymbol = node.Parent.Tag as GroupSymbol; Content.StartGrammarManipulation(); Cloner cloner = new Cloner(); var clonedSymbol = cloner.Clone(symbol); ChangeDuplicateSymbolNames(clonedSymbol); if (groupSymbol != null) groupSymbol.SymbolsCollection.Add(clonedSymbol); else Content.AddSymbol(clonedSymbol); UpdateGrammerConstraintsForClonedSymbol(symbol, cloner); Content.FinishedGrammarManipulation(); } #endregion private void symbolsTreeView_MouseDown(object sender, MouseEventArgs e) { // enables deselection of treeNodes Point coordinates = new Point(e.X, e.Y); TreeNode node = symbolsTreeView.GetNodeAt(coordinates); if (e.Button == MouseButtons.Left && node == null) { symbolsTreeView.SelectedNode = null; symbolDetailsViewHost.Content = null; SetEnabledStateOfControls(); } } private void symbolsTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) { var symbol = e.Node.Tag as ISymbol; if (symbol == null) return; if (e.Button != MouseButtons.Left) return; if (e.X < e.Node.Bounds.Left - symbolsTreeView.ImageList.Images[e.Node.ImageIndex].Width || e.X > e.Node.Bounds.Right) return; MainFormManager.MainForm.ShowContent(symbol); e.Node.Toggle(); } private void symbolsTreeView_KeyDown(object sender, KeyEventArgs e) { if (Content == null || Content.ReadOnly || ReadOnly || Locked) return; if (symbolsTreeView.SelectedNode == null) return; if (e.KeyCode != Keys.Delete) return; var symbol = (ISymbol)symbolsTreeView.SelectedNode.Tag; if (!(symbol is IReadOnlySymbol)) Content.RemoveSymbol(symbol); SetEnabledStateOfControls(); UpdateSymbolDetailsViews(); RebuildImageList(); } #region button events private TypeSelectorDialog typeSelectorDialog; private void addButton_Click(object sender, EventArgs e) { if (typeSelectorDialog == null) { typeSelectorDialog = new TypeSelectorDialog(); typeSelectorDialog.Caption = "Select Symbol"; typeSelectorDialog.TypeSelector.Caption = "Available Symbols"; typeSelectorDialog.TypeSelector.Configure(typeof(ISymbol), false, false, (t) => { return !typeof(IReadOnlySymbol).IsAssignableFrom(t); }); } if (typeSelectorDialog.ShowDialog(this) == DialogResult.OK) { try { ISymbol symbol = (ISymbol)typeSelectorDialog.TypeSelector.CreateInstanceOfSelectedType(); ChangeDuplicateSymbolNames(symbol); GroupSymbol groupSymbol = null; TreeNode selectedNode = symbolsTreeView.SelectedNode; if (selectedNode != null) { groupSymbol = selectedNode.Tag as GroupSymbol; if (groupSymbol == null && selectedNode.Parent != null) groupSymbol = selectedNode.Parent.Tag as GroupSymbol; } if (groupSymbol != null) groupSymbol.SymbolsCollection.Add(symbol); else Content.AddSymbol(symbol); } catch (Exception ex) { ErrorHandling.ShowErrorDialog(this, ex); } } } private void copyButton_Click(object sender, EventArgs e) { var symbol = symbolsTreeView.SelectedNode.Tag as ISymbol; if (symbol != null && !(symbol is IReadOnlySymbol)) { Content.StartGrammarManipulation(); Cloner cloner = new Cloner(); var clonedSymbol = cloner.Clone(symbol); ChangeDuplicateSymbolNames(clonedSymbol); GroupSymbol groupSymbol = null; if (symbolsTreeView.SelectedNode.Parent != null) groupSymbol = symbolsTreeView.SelectedNode.Parent.Tag as GroupSymbol; if (groupSymbol != null) groupSymbol.SymbolsCollection.Add(clonedSymbol); else Content.AddSymbol(clonedSymbol); UpdateGrammerConstraintsForClonedSymbol(symbol, cloner); Content.FinishedGrammarManipulation(); } } private void removeButton_Click(object sender, EventArgs e) { var symbol = symbolsTreeView.SelectedNode.Tag as ISymbol; if (symbol != null && !(symbol is IReadOnlySymbol)) { Content.RemoveSymbol(symbol); } } private void showDetailsCheckBox_CheckedChanged(object sender, EventArgs e) { splitContainer1.Panel2Collapsed = !showDetailsCheckBox.Checked; } private void showSampleTreeButton_Click(object sender, EventArgs e) { SymbolicExpressionGrammarSampleExpressionTreeView view = new SymbolicExpressionGrammarSampleExpressionTreeView(); view.Content = Content; view.Show(); } #endregion #region helpers private void UpdateGrammerConstraintsForClonedSymbol(ISymbol symbol, Cloner cloner) { foreach (var s in symbol.Flatten().Where(x => !(x is GroupSymbol))) { if (!cloner.ClonedObjectRegistered(s)) throw new InvalidOperationException(); var clone = cloner.Clone(s); Content.SetSubtreeCount(clone, Content.GetMinimumSubtreeCount(s), Content.GetMaximumSubtreeCount(s)); foreach (var childSymbol in Content.GetAllowedChildSymbols(s)) { var newChildSymbol = childSymbol; if (cloner.ClonedObjectRegistered(childSymbol)) newChildSymbol = cloner.Clone(childSymbol); Content.AddAllowedChildSymbol(clone, newChildSymbol); } for (int i = 0; i < Content.GetMaximumSubtreeCount(s); i++) { foreach (var childSymbol in Content.GetAllowedChildSymbols(s, i)) { var newChildSymbol = childSymbol; if (cloner.ClonedObjectRegistered(childSymbol)) newChildSymbol = cloner.Clone(childSymbol); Content.AddAllowedChildSymbol(clone, newChildSymbol, i); } } } } private void ChangeDuplicateSymbolNames(ISymbol symbol) { foreach (var s in symbol.Flatten()) { var originalSymbolName = s.Name; int i = 1; while (Content.ContainsSymbol(s)) { s.Name = originalSymbolName + i; i++; } } } private void UpdateSymbolDetailsViews() { if (symbolsTreeView.SelectedNode != null) { symbolDetailsViewHost.Content = (ISymbol)symbolsTreeView.SelectedNode.Tag; allowedChildSymbolsControl.Symbol = (ISymbol)symbolsTreeView.SelectedNode.Tag; } else { symbolDetailsViewHost.Content = null; allowedChildSymbolsControl.Symbol = null; } } private IEnumerable IterateTreeNodes(TreeNode node = null) { TreeNodeCollection nodes; if (node == null) nodes = symbolsTreeView.Nodes; else { nodes = node.Nodes; yield return node; } foreach (var childNode in nodes.OfType()) foreach (var n in IterateTreeNodes(childNode)) yield return n; } protected virtual void RebuildImageList() { symbolsTreeView.ImageList.Images.Clear(); foreach (TreeNode treeNode in IterateTreeNodes()) { var symbol = (ISymbol)treeNode.Tag; symbolsTreeView.ImageList.Images.Add(symbol == null ? HeuristicLab.Common.Resources.VSImageLibrary.Nothing : symbol.ItemImage); treeNode.ImageIndex = symbolsTreeView.ImageList.Images.Count - 1; } } //necessary code to handle dock correctly regarding the expanded nodes bool[] expandendedState; protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); if (expandendedState == null) return; var nodes = IterateTreeNodes().ToList(); for (int i = 0; i < nodes.Count; i++) if (expandendedState[i]) nodes[i].Expand(); } protected override void OnHandleDestroyed(EventArgs e) { base.OnHandleDestroyed(e); var nodes = IterateTreeNodes().ToList(); expandendedState = new bool[nodes.Count]; for (int i = 0; i < nodes.Count; i++) expandendedState[i] = nodes[i].IsExpanded; } #endregion } //this class is necessary to prevent double clicks which do not fire the checkbox checked event //workaround taken from http://connect.microsoft.com/VisualStudio/feedback/details/374516/treeview-control-does-not-fire-events-reliably-when-double-clicking-on-checkbox internal class CheckBoxTreeView : TreeView { protected override void WndProc(ref Message m) { // Suppress WM_LBUTTONDBLCLK if (m.Msg == 0x203 && IsOnCheckBox(m)) { m.Result = IntPtr.Zero; } else base.WndProc(ref m); } private int GetXLParam(IntPtr lParam) { return lParam.ToInt32() & 0xffff; } private int GetYLParam(IntPtr lParam) { return lParam.ToInt32() >> 16; } private bool IsOnCheckBox(Message m) { int x = GetXLParam(m.LParam); int y = GetYLParam(m.LParam); TreeNode node = this.GetNodeAt(x, y); return ((x <= node.Bounds.Left - 20) && (x >= node.Bounds.Left - 32)); } } }