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();
}
}
}