using FastReport.Controls; using FastReport.Utils; using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace FastReport.Design.ToolWindows { /// /// Represents the "Report Tree" window. /// public class ReportTreeWindow : FilterableToolWindow { #region Fields private Dictionary objectNodes; private Panel dragIndicator; private ToolbarButton btnSync; #endregion #region Private Methods private void UpdateSelection() { if (Designer.SelectedObjects == null || Designer.SelectedObjects.Count == 0) return; if (objectNodes.ContainsKey(Designer.SelectedObjects[0])) { ObjectsToSelectedNodes(); } } private void ObjectsToSelectedNodes() { var nodes = new List(); foreach (Base b in Designer.SelectedObjects) nodes.Add(objectNodes[b]); tree.SelectedNodes = nodes; if (btnSync.Checked) tree.TrackSelection(); } private void SelectedNodesToObjects() { Designer.SelectedObjects.Clear(); foreach (var node in tree.SelectedNodes) Designer.SelectedObjects.Add(node.Tag as Base); // activate object's page if (Designer.SelectedObjects.Count > 0) { var c = Designer.SelectedObjects[0]; if (!(c is Report)) Designer.ActiveReportTab.ActivePage = c.Page; } Designer.SelectionChanged(this); } private void EnumComponents(Base rootComponent, TreeNodeCollection rootNode) { string name = rootComponent is Report ? "Report - " + Designer.ActiveReportTab.ReportName : rootComponent.Name; TreeNode node = rootNode.Add(name); node.Tag = rootComponent; objectNodes[rootComponent] = node; int imageIndex = rootComponent.GetImageIndex(); if (imageIndex != -1) { node.ImageIndex = imageIndex; node.SelectedImageIndex = imageIndex; } if (rootComponent.HasFlag(Flags.CanShowChildrenInReportTree)) { foreach (Base component in rootComponent.ChildObjects) EnumComponents(component, node.Nodes); } } private bool CheckChanges(Base rootComponent, TreeNode rootNode) { if (rootNode.Tag != rootComponent) return false; if (!(rootComponent is Report)) { if (rootNode.Text != rootComponent.Name) return false; } if (!rootComponent.HasFlag(Flags.CanShowChildrenInReportTree)) return true; ObjectCollection childObjects = rootComponent.ChildObjects; if (childObjects.Count != rootNode.Nodes.Count) return false; for (int i = 0; i < childObjects.Count; i++) { if (!CheckChanges(childObjects[i], rootNode.Nodes[i])) return false; } return true; } private void tree_SelectionChanged(object sender, EventArgs e) { SelectedNodesToObjects(); } private void tree_RightMouseButtonClicked(object sender, MouseEventArgs e) { var menu = Designer.SelectedObjects[0].GetContextMenu(); menu?.Show(tree, e.Location); } private void tree_ItemDrag(object sender, ItemDragEventArgs e) { // check if every item can be dragged var effect = DragDropEffects.Move; foreach (Base draggedItem in Designer.SelectedObjects) { if (draggedItem is ComponentBase && (draggedItem.IsAncestor || draggedItem.HasFlag(Flags.CanChangeParent))) { // ok } else { effect = DragDropEffects.None; break; } } tree.DoDragDrop(e.Item, effect); } private void tree_DragOver(object sender, DragEventArgs e) { dragIndicator.Hide(); TreeNode targetNode = (tree.GetNodeAt(tree.PointToClient(new Point(e.X, e.Y)))); if (targetNode == null || e.AllowedEffect == DragDropEffects.None) return; Base target = targetNode.Tag as Base; // allowed moves are: // - target is not dragged // - target is not child of dragged // - dragged is not child of parent // - target can contain dragged, or // parent of target can contain dragged int dragPos = 0; int allow = 0; foreach (Base dragged in Designer.SelectedObjects) { if (dragged == target || target.HasParent(dragged) || dragged.Parent == target) continue; bool changeParent = (target as IParent)?.CanContain(dragged) ?? false; bool changeOrder = (target.Parent as IParent)?.CanContain(dragged) ?? false; if (changeParent || changeOrder) { allow++; dragPos = changeParent ? targetNode.Bounds.Bottom : targetNode.Bounds.Top; } } if (allow == Designer.SelectedObjects.Count) { e.Effect = e.AllowedEffect; dragIndicator.BackColor = tree.ForeColor; dragIndicator.SetBounds(tree.Left + targetNode.Bounds.Left, tree.Top + dragPos, targetNode.Bounds.Width, Designer.LogicalToDevice(2)); dragIndicator.Show(); } } private void tree_DragDrop(object sender, DragEventArgs e) { dragIndicator.Hide(); TreeNode targetNode = tree.GetNodeAt(tree.PointToClient(new Point(e.X, e.Y))); Base target = targetNode.Tag as Base; // cases: // - target can contain dragged. Just change parent. // - target cannot contain dragged. Change creation order (Z-order). foreach (Base dragged in Designer.SelectedObjects) { if (target is IParent par && par.CanContain(dragged)) { dragged.Parent = target; } else { Base parent = target.Parent; dragged.Parent = parent; dragged.ZOrder = target.ZOrder; } } // update all designer plugins (this one too) Designer.SetModified(null, "ChangeParent"); } private void tree_DragLeave(object sender, EventArgs e) { dragIndicator.Hide(); } private void tree_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Delete) Designer.cmdDelete.Invoke(); } private void tree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e) { Base c = e.Node.Tag as Base; if (c is Report) e.CancelEdit = true; } private void tree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) { if (e.Label != null) { Base c = e.Node.Tag as Base; try { c.Name = e.Label; Designer.SetModified(this, "Change"); } catch (Exception ex) { FRMessageBox.Error(ex.Message); e.CancelEdit = true; } } } private void btnSync_Click(object sender, EventArgs e) { tree.TrackSelection(); } #endregion #region Protected/Public Methods /// protected override void UpdateTree() { // if there was no changes in the report structure, do nothing if (Designer.ActiveReport != null && tree.Nodes.Count > 0) { if (CheckChanges(Designer.ActiveReport, tree.Nodes[0])) return; } tree.BeginUpdate(); tree.Nodes.Clear(); objectNodes.Clear(); if (Designer.ActiveReport != null) EnumComponents(Designer.ActiveReport, tree.Nodes); tree.ExpandAll(); tree.EndUpdate(); } /// public override void SelectionChanged() { Throttle.Execute(UpdateSelection); } /// public override void UpdateContent() { Throttle.Execute(() => { UpdateTree(); UpdateSelection(); #if !WPF SearchMatches(); #endif }); } /// public override void Localize() { base.Localize(); MyRes res = new MyRes("Designer,ToolWindow,ReportTree"); Text = res.Get(""); btnSync.ToolTipText = res.Get("Sync"); } /// public override void UpdateDpiDependencies() { base.UpdateDpiDependencies(); Image = Designer.GetImage(189); tree.ImageList = Designer.GetImages(); } #endregion /// /// Initializes a new instance of the class with default settings. /// /// The report designer. public ReportTreeWindow(Designer designer) : base(designer) { Name = "ReportTreeWindow"; objectNodes = new Dictionary(); tree.LabelEdit = true; tree.ShowRootLines = false; tree.AllowDrop = true; tree.SelectionChanged += tree_SelectionChanged; tree.RightMouseButtonClicked += tree_RightMouseButtonClicked; tree.ItemDrag += tree_ItemDrag; tree.DragOver += tree_DragOver; tree.DragDrop += tree_DragDrop; tree.DragLeave += tree_DragLeave; tree.KeyDown += tree_KeyDown; tree.BeforeLabelEdit += tree_BeforeLabelEdit; tree.AfterLabelEdit += tree_AfterLabelEdit; btnSync = AddButton(81, btnSync_Click); btnSync.CheckOnClick = true; toolbar.AddItems(btnSync); dragIndicator = new Panel(); dragIndicator.Visible = false; Controls.Add(dragIndicator); Controls.SetChildIndex(dragIndicator, 0); Localize(); UpdateDpiDependencies(); } } }