using FastReport.Controls; using FastReport.Data; using FastReport.Forms; using FastReport.Utils; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Reflection; using System.Text; using System.Windows.Forms; namespace FastReport.Design.ToolWindows { /// /// Represents the "Data Dictionary" window. /// public class DictionaryWindow : FilterableToolWindow { #region Fields private ToolbarDropDownButton btnActions; private ToolbarButton btnEdit; private ToolbarButton btnDelete; private ToolbarButton btnView; private ContextMenuItem miNew; private ContextMenuItem miOpen; private ContextMenuItem miMerge; private ContextMenuItem miSave; private ContextMenuItem miChooseData; private ContextMenuItem miSortDataSources; private ContextMenuItem miUploadDataSource; private ContextMenuItem miNewDataSource; private ContextMenuItem miNewRelation; private ContextMenuItem miNewParameter; private ContextMenuItem miNewTotal; private ContextMenuItem miNewCalculatedColumn; private ContextMenuItem miCopyDataSource; private ContextMenuBase mnuContext; private ContextMenuItem miSortDataSources1; private ContextMenuItem miUploadDataSource1; private ContextMenuItem miNewDataSource1; private ContextMenuItem miNewParameter1; private ContextMenuItem miNewTotal1; private ContextMenuItem miNewCalculatedColumn1; private ContextMenuItem miRename; private ContextMenuItem miEdit; private ContextMenuItem miDelete; private ContextMenuItem miDeleteAlias; private ContextMenuItem miView; private ContextMenuItem miViewJson; private ContextMenuItem miSortDataFields; private Panel dragIndicator; private Splitter splitter; private DescriptionControl lblDescription; private Report report; private List expandedNodes; private bool updating; private static DraggedItemCollection draggedItems = new DraggedItemCollection(); #endregion #region Properties private bool IsDataComponent { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is DataComponentBase; } } private bool IsVariable { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is Parameter && !(tree.SelectedNode.Parent.Tag is SystemVariables); } } private bool IsTotal { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is Total; } } private bool IsConnection { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is DataConnectionBase; } } private bool IsTable { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is DataSourceBase; } } private bool IsJsonTable { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is FastReport.Data.JsonConnection.JsonTableDataSource; } } private bool IsRelation { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is Relation; } } private bool IsEditableColumn { get { TreeNode node = tree.SelectedNode; bool result = node != null && node.Tag is Column; if (result) { // check if column belongs to the datasource, not relation. while (node != null) { if (node.Tag is Relation) { result = false; break; } else if (node.Tag is DataSourceBase) break; node = node.Parent; } } return result; } } private bool IsCube { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is CubeSourceBase; } } private bool IsDataSources { get { return tree.SelectedNode != null && tree.SelectedNode.Tag == report.Dictionary.DataSources; } } private bool IsVariables { get { return tree.SelectedNode != null && tree.SelectedNode.Tag == report.Dictionary.Parameters; } } private bool IsSystemVariables { get { return tree.SelectedNode != null && tree.SelectedNode.Tag == report.Dictionary.SystemVariables; } } private bool IsTotals { get { return tree.SelectedNode != null && tree.SelectedNode.Tag == report.Dictionary.Totals; } } private bool IsCubeSources { get { return tree.SelectedNode != null && tree.SelectedNode.Tag == report.Dictionary.CubeSources; } } private bool CanEdit { get { return (IsDataComponent || IsVariable || IsTotal) && !Designer.Restrictions.DontEditData && (tree.SelectedNode.Tag as Base).HasFlag(Flags.CanEdit) && !(tree.SelectedNode.Tag as Base).HasRestriction(Restrictions.DontEdit); } } private bool CanDelete { get { return (IsDataComponent || IsVariable || IsTotal) && !Designer.Restrictions.DontEditData && (tree.SelectedNode.Tag as Base).HasFlag(Flags.CanDelete) && !(tree.SelectedNode.Tag as Base).HasRestriction(Restrictions.DontDelete); } } private bool CanCreateCalculatedColumn { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is DataSourceBase; } } private bool IsAliased { get { return tree.SelectedNode != null && tree.SelectedNode.Tag is DataComponentBase && (tree.SelectedNode.Tag as DataComponentBase).IsAliased; } } #endregion #region Private Methods private TreeNode FindNode(TreeNodeCollection parent, string text) { foreach (TreeNode node in parent) { if (node.Text == text) return node; } return null; } private void NavigateTo(string path) { string[] parts = path.Split('.'); TreeNodeCollection parent = tree.Nodes; TreeNode node = null; foreach (string part in parts) { node = FindNode(parent, part); if (node == null) break; node.Expand(); parent = node.Nodes; } tree.SelectedNode = node; } private void GetExpandedNodes(TreeNodeCollection nodes) { foreach (TreeNode node in nodes) { if (node.IsExpanded) expandedNodes.Add(node.FullPath); GetExpandedNodes(node.Nodes); } } private bool CompareNodes(TreeNodeCollection fromNodes, TreeNodeCollection toNodes) { if (fromNodes.Count != toNodes.Count) return false; for (int i = 0; i < fromNodes.Count; i++) { if (fromNodes[i].Text != toNodes[i].Text || fromNodes[i].ImageIndex != toNodes[i].ImageIndex) return false; toNodes[i].Tag = fromNodes[i].Tag; if (!CompareNodes(fromNodes[i].Nodes, toNodes[i].Nodes)) return false; } return true; } private void CopyNodes(TreeNodeCollection fromNodes, TreeNodeCollection toNodes) { foreach (TreeNode fromNode in fromNodes) { TreeNode toNode = toNodes.Add(fromNode.Text); toNode.Tag = fromNode.Tag; toNode.ImageIndex = fromNode.ImageIndex; toNode.SelectedImageIndex = fromNode.SelectedImageIndex; CopyNodes(fromNode.Nodes, toNode.Nodes); if (expandedNodes.Contains(fromNode.FullPath)) toNode.Expand(); } } protected override void UpdateTree() { expandedNodes.Clear(); GetExpandedNodes(tree.Nodes); TreeView buffer = new TreeView(); if (report != null) { bool canShowData = report.Dictionary.Connections.Count > 0; foreach (DataSourceBase data in report.Dictionary.DataSources) { if (data.Enabled) { canShowData = true; break; } } bool canShowCube = report.Dictionary.CubeSources.Count > 0; TreeNode rootNode = null; if (canShowData) { rootNode = buffer.Nodes.Add(Res.Get("Designer,ToolWindow,Dictionary,DataSources")); rootNode.Tag = report.Dictionary.DataSources; rootNode.ImageIndex = 53; rootNode.SelectedImageIndex = rootNode.ImageIndex; DataTreeHelper.CreateDataTree(report.Dictionary, rootNode.Nodes, true, true, true, true); } // system variables rootNode = buffer.Nodes.Add(Res.Get("Designer,ToolWindow,Dictionary,SystemVariables")); rootNode.Tag = report.Dictionary.SystemVariables; rootNode.ImageIndex = 60; rootNode.SelectedImageIndex = rootNode.ImageIndex; DataTreeHelper.CreateVariablesTree(report.Dictionary.SystemVariables, rootNode.Nodes); // totals rootNode = buffer.Nodes.Add(Res.Get("Designer,ToolWindow,Dictionary,Totals")); rootNode.Tag = report.Dictionary.Totals; rootNode.ImageIndex = 132; rootNode.SelectedImageIndex = rootNode.ImageIndex; DataTreeHelper.CreateTotalsTree(report.Dictionary.Totals, rootNode.Nodes); // parameters rootNode = buffer.Nodes.Add(Res.Get("Designer,ToolWindow,Dictionary,Parameters")); rootNode.Tag = report.Dictionary.Parameters; rootNode.ImageIndex = 234; rootNode.SelectedImageIndex = rootNode.ImageIndex; DataTreeHelper.CreateParametersTree(report.Dictionary.Parameters, rootNode.Nodes); // functions rootNode = buffer.Nodes.Add(Res.Get("Designer,ToolWindow,Dictionary,Functions")); rootNode.ImageIndex = 52; rootNode.SelectedImageIndex = rootNode.ImageIndex; DataTreeHelper.CreateFunctionsTree(report, rootNode.Nodes); if (canShowCube) { rootNode = buffer.Nodes.Add(Res.Get("Designer,ToolWindow,Dictionary,CubeSources")); rootNode.Tag = report.Dictionary.CubeSources; rootNode.ImageIndex = 248; rootNode.SelectedImageIndex = rootNode.ImageIndex; DataTreeHelper.CreateCubeTree(report.Dictionary, rootNode.Nodes, false); } } if (!CompareNodes(buffer.Nodes, tree.Nodes)) { tree.BeginUpdate(); tree.Nodes.Clear(); CopyNodes(buffer.Nodes, tree.Nodes); tree.EndUpdate(); } buffer.Dispose(); UpdateControls(); } private void Change() { Designer.SetModified(this, "EditData"); } private void UpdateControls() { btnEdit.Enabled = CanEdit; btnDelete.Enabled = CanDelete; btnView.Enabled = IsTable && !IsJsonTable; } private void miNew_Click(object sender, EventArgs e) { report.Dictionary.Clear(); report.Dictionary.ReRegisterData(); UpdateTree(); Change(); #if !WPF SearchMatches(); #endif } private void miOpen_Click(object sender, EventArgs e) { using (OpenFileDialog dialog = new OpenFileDialog()) { dialog.Filter = Res.Get("FileFilters,Dictionary"); if (dialog.ShowDialog() == DialogResult.OK) { report.Dictionary.Load(dialog.FileName); UpdateTree(); Change(); #if !WPF SearchMatches(); #endif } } } private void miSave_Click(object sender, EventArgs e) { using (SaveFileDialog dialog = new SaveFileDialog()) { dialog.Filter = Res.Get("FileFilters,Dictionary"); dialog.DefaultExt = "frd"; dialog.FileName = "Dictionary.frd"; if (dialog.ShowDialog() == DialogResult.OK) report.Dictionary.Save(dialog.FileName); } } private void miMerge_Click(object sender, EventArgs e) { using (OpenFileDialog dialog = new OpenFileDialog()) { dialog.Filter = Res.Get("FileFilters,Dictionary"); if (dialog.ShowDialog() == DialogResult.OK) { Dictionary dict = new Dictionary(); dict.SetReport(report); dict.Load(dialog.FileName); foreach (Base obj in dict.AllObjects) { obj.SetReport(report); } report.Dictionary.Merge(dict); UpdateTree(); Change(); #if !WPF SearchMatches(); #endif } } } private void btnActions_DropDownOpening(object sender, EventArgs e) { miNew.Enabled = Designer.cmdChooseData.Enabled; miOpen.Enabled = Designer.cmdChooseData.Enabled; miMerge.Enabled = Designer.cmdChooseData.Enabled; miSave.Enabled = Designer.cmdChooseData.Enabled; miChooseData.Enabled = Designer.cmdChooseData.Enabled; miSortDataSources.Enabled = Designer.cmdSortDataSources.Enabled; miUploadDataSource.Enabled = IsConnection; miNewDataSource.Enabled = Designer.cmdAddData.Enabled; miNewRelation.Enabled = Designer.cmdChooseData.Enabled; miNewParameter.Enabled = Designer.cmdChooseData.Enabled; miNewTotal.Enabled = Designer.cmdChooseData.Enabled; miNewCalculatedColumn.Enabled = Designer.cmdChooseData.Enabled && CanCreateCalculatedColumn; } private void mnuContext_Opening(object sender, CancelEventArgs e) { mnuContext.Clear(); if (!Designer.cmdChooseData.Enabled) { e.Cancel = true; return; } if (IsDataSources || IsConnection) { if (Designer.cmdAddData.Enabled) mnuContext.AddItem(miNewDataSource1); if (Designer.cmdSortDataSources.Enabled) mnuContext.AddItem(miSortDataSources1); if (IsConnection) { mnuContext.AddItem(miCopyDataSource); if (!string.IsNullOrEmpty((tree.SelectedNode.Tag as DataConnectionBase).CloudId)) miUploadDataSource.Text = Res.Get("Designer,ToolWindow,Dictionary,UploadChangesDataSource"); else miUploadDataSource.Text = Res.Get("Designer,ToolWindow,Dictionary,UploadDataSource"); mnuContext.AddItem(miUploadDataSource1); } } else if (IsVariables || IsVariable) mnuContext.AddItem(miNewParameter1); else if (IsTotals || IsTotal) mnuContext.AddItem(miNewTotal1); else if (CanCreateCalculatedColumn) { mnuContext.AddItem(miNewCalculatedColumn1); miNewCalculatedColumn.Enabled = true; } if (CanEdit) mnuContext.AddItem(miEdit); if (IsTable || IsEditableColumn || IsVariable || IsTotal) mnuContext.AddItem(miRename); if (CanDelete) mnuContext.AddItem(miDelete); if (IsAliased) mnuContext.AddItem(miDeleteAlias); if (IsTable || IsJsonTable) { mnuContext.AddItem(IsJsonTable ? miViewJson : miView); DataSourceBase data = tree.SelectedNode.Tag as DataSourceBase; if (data != null && data.Columns.Count > 1) { mnuContext.AddItem(miSortDataFields); } } // .Net: update menu item images mnuContext.ImageList = Designer.GetImages(); e.Cancel = mnuContext.IsEmpty; } private void miNewRelation_Click(object sender, EventArgs e) { Relation relation = new Relation(); report.Dictionary.Relations.Add(relation); using (RelationEditorForm form = new RelationEditorForm(relation)) { if (form.ShowDialog() == DialogResult.OK) { relation.Name = report.Dictionary.CreateUniqueName(relation.ParentDataSource.Name + "_" + relation.ChildDataSource.Name); UpdateTree(); Change(); #if !WPF SearchMatches(); #endif } else relation.Dispose(); } } private void miNewCalculatedColumn_Click(object sender, EventArgs e) { DataSourceBase data = tree.SelectedNode.Tag as DataSourceBase; Column c = new Column(); c.Name = data.Columns.CreateUniqueName("Column"); c.Alias = data.Columns.CreateUniqueAlias(c.Alias); c.Calculated = true; data.Columns.Add(c); UpdateTree(); string navigatePath = Res.Get("Designer,ToolWindow,Dictionary,DataSources"); if (data.Parent is DataConnectionBase) navigatePath += "." + data.Parent.Name + "." + data.Alias; else navigatePath += GetPath(data); navigatePath += "." + c.Alias; NavigateTo(navigatePath); Change(); #if !WPF SearchMatches(); #endif } private string GetPath(Base data) { if (data == null || data.Name == "") return ""; if (data.Parent is DataConnectionBase && data.Parent.Parent == null) return data.Parent.Alias; else return GetPath(data.Parent) + "." + data.Name; } private void miNewParameter_Click(object sender, EventArgs e) { Parameter p = new Parameter(); ParameterCollection parent = null; if (IsVariable) parent = (tree.SelectedNode.Tag as Parameter).Parameters; else parent = report.Dictionary.Parameters; p.Name = parent.CreateUniqueName("Parameter"); parent.Add(p); UpdateTree(); NavigateTo(Res.Get("Designer,ToolWindow,Dictionary,Parameters") + "." + p.FullName); Change(); #if !WPF SearchMatches(); #endif } private void miNewTotal_Click(object sender, EventArgs e) { using (TotalEditorForm form = new TotalEditorForm(Designer)) { if (form.ShowDialog() == DialogResult.OK) { report.Dictionary.Totals.Add(form.Total); UpdateTree(); NavigateTo(Res.Get("Designer,ToolWindow,Dictionary,Totals") + "." + form.Total.Name); Change(); #if !WPF SearchMatches(); #endif } } } private void miRename_Click(object sender, EventArgs e) { if (tree.SelectedNode == null) return; tree.SelectedNode.BeginEdit(); } private void miEdit_Click(object sender, EventArgs e) { if (!CanEdit) return; IHasEditor c = tree.SelectedNode.Tag as IHasEditor; if (c != null && c.InvokeEditor()) { UpdateTree(); Change(); #if !WPF SearchMatches(); #endif } } private void miDelete_Click(object sender, EventArgs e) { TreeNode parentNode = tree.SelectedNode?.Parent; if (parentNode == null) return; int index = parentNode.Nodes.IndexOf(tree.SelectedNode); for (int i = 0; i < tree.SelectedNodes.Count; i++) { if (CanDelete) { (tree.SelectedNode.Tag as Base).Delete(); tree.SelectedNode.Remove(); tree.SelectedNodes.RemoveAt(0); i--; } } index = Math.Min(index, parentNode.Nodes.Count - 1); tree.SelectedNode = index >= 0 ? parentNode.Nodes[index] : null; Change(); } private void miDeleteAlias_Click(object sender, EventArgs e) { if (!IsAliased) return; DataComponentBase c = tree.SelectedNode.Tag as DataComponentBase; c.Alias = c.Name; tree.SelectedNode.Text = c.Name; Change(); } private void miView_Click(object sender, EventArgs e) { if (!IsTable) return; DataSourceBase data = tree.SelectedNode.Tag as DataSourceBase; if (data == null) return; try { data.Init(); } catch (Exception ex) { FRMessageBox.Error(ex.Message); return; } object dataSource = null; if (data is TableDataSource) { dataSource = (data as TableDataSource).Table; } else dataSource = data.Rows; if (dataSource == null) return; using (DataViewForm form = new DataViewForm(data)) { form.ShowDialog(); } } private void miViewJson_Click(object sender, EventArgs e) { if (!IsJsonTable) return; Data.JsonConnection.JsonTableDataSource data = tree.SelectedNode.Tag as Data.JsonConnection.JsonTableDataSource; if (data == null) return; try { data.Init(); } catch (Exception ex) { FRMessageBox.Error(ex.Message); return; } using (JsonEditorForm jsonEditorForm = new JsonEditorForm()) { StringBuilder sb = new StringBuilder(); data.Json.WriteTo(sb, 2); jsonEditorForm.JsonText = sb.ToString(); jsonEditorForm.SetToReadOnly(); jsonEditorForm.ShowDialog(); } } private void miUploadDataSource_Click(object sender, EventArgs e) { if (tree.SelectedNode.Tag is DataConnectionBase data) CloudCommands.UploadDataConnection(data); } private void miCopyDataSource_Click(object sender, EventArgs e) { if (tree.SelectedNode.Tag is DataConnectionBase) { DataConnectionBase data = tree.SelectedNode.Tag as DataConnectionBase; data.Clone(); } Change(); UpdateTree(); Refresh(); //Designer.SetModified(); #if !WPF SearchMatches(); #endif } private void miSortDataFields_Click(object sender, EventArgs e) { if (!IsTable) return; TableDataSource data = tree.SelectedNode.Tag as TableDataSource; if (data == null) return; if (data.Columns.Count > 1) { data.Columns.Sort(); UpdateTree(); Change(); #if !WPF SearchMatches(); #endif } } private void tree_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.F2) miRename_Click(this, null); else if (e.KeyCode == Keys.Delete) miDelete_Click(this, null); } private void tree_SelectionChanged(object sender, EventArgs e) { if (updating) return; Designer.SelectedObjects.Clear(); foreach (var node in tree.SelectedNodes) { if (node.Tag is Base c) Designer.SelectedObjects.Add(c); } Designer.SelectionChanged(this); UpdateControls(); object selected = tree.SelectedNode?.Tag; bool descrVisible = selected is MethodInfo || selected is SystemVariable; splitter.Visible = descrVisible; lblDescription.Visible = descrVisible; if (descrVisible) lblDescription.ShowDescription(report, selected); } private void tree_RightMouseButtonClicked(object sender, MouseEventArgs e) { mnuContext.Show(tree, e.Location); } private void tree_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e) { bool canEdit = (IsTable || IsEditableColumn || IsVariable || IsTotal) && !Designer.Restrictions.DontEditData && !(tree.SelectedNode.Tag as Base).HasRestriction(Restrictions.DontModify); if (!canEdit) e.CancelEdit = true; } private void tree_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) { string newLabel = e.Label == null ? tree.SelectedNode.Text : e.Label; if (newLabel == tree.SelectedNode.Text) return; Base obj = tree.SelectedNode.Tag as Base; bool duplicateName = false; if (obj is DataSourceBase) { if (report.Dictionary.FindByAlias(newLabel) != null) duplicateName = true; else { (obj as DataSourceBase).Alias = newLabel; } } else if (obj is Column) { // get column name, take parent columns into account string columnName = newLabel; TreeNode node = tree.SelectedNode; while (true) { node = node.Parent; if (node.Tag is DataSourceBase) break; columnName = node.Text + "." + columnName; } DataSourceBase data = obj.Parent as DataSourceBase; if (data.Columns.FindByAlias(columnName) != null) duplicateName = true; else (obj as Column).Alias = columnName; } else if (obj is Parameter) { TreeNode parentNode = tree.SelectedNode.Parent; ParameterCollection parent = null; if (parentNode.Tag is Parameter) parent = (parentNode.Tag as Parameter).Parameters; else parent = report.Dictionary.Parameters; if (parent.FindByName(newLabel) != null) duplicateName = true; else obj.Name = newLabel; } else if (obj is Total) { if (report.Dictionary.FindByName(newLabel) != null) duplicateName = true; else obj.Name = newLabel; } if (duplicateName) { e.CancelEdit = true; FRMessageBox.Error(Res.Get("Designer,ToolWindow,Dictionary,DuplicateName")); } else Change(); } private void tree_ItemDrag(object sender, ItemDragEventArgs e) { draggedItems.Clear(); foreach (TreeNode n in tree.SelectedNodes) { string selectedItem = ""; TreeNode node = n; if (node == null) continue; if (node.Tag is Column && !(node.Tag is DataSourceBase)) { while (true) { if (node.Tag is DataSourceBase) { selectedItem = (node.Tag as DataSourceBase).FullName + "." + selectedItem; break; } selectedItem = node.Text + (selectedItem == "" ? "" : ".") + selectedItem; node = node.Parent; } } else if (node.Tag is Parameter || node.Tag is Total) { while (node != null && node.Tag != null) { if (node.Tag is Parameter || node.Tag is Total) selectedItem = node.Text + (selectedItem == "" ? "" : ".") + selectedItem; node = node.Parent; } } else if (node.Tag is MethodInfo) { MethodInfo info = node.Tag as MethodInfo; ParameterInfo[] pars = info.GetParameters(); int parsLength = pars.Length; if (parsLength > 0 && pars[0].Name == "thisReport") parsLength--; selectedItem = info.Name + "(" + (parsLength > 1 ? "".PadRight(parsLength - 1, ',') : "") + ")"; } if (selectedItem != "") draggedItems.Add(new DraggedItem(n.Tag, selectedItem)); } if (draggedItems.Count > 0) tree.DoDragDrop(draggedItems, DragDropEffects.Move); else tree.DoDragDrop(e.Item, DragDropEffects.None); } private void tree_DragOver(object sender, DragEventArgs e) { dragIndicator.Hide(); if (draggedItems.Count == 0) return; var pos = tree.PointToClient(new Point(e.X, e.Y)); TreeNode targetNode = tree.GetNodeAt(pos); if (targetNode == null) return; object target = targetNode.Tag; // the only thing that can be dragged around the DictionaryWindow is a report parameter // allowed moves are: // - target is ParameterCollection: add node to collection or set its z-order to 0 if node is in collection already // - target is Parameter and dragged is not target and is not a root of target: reparent or change z-order (see notes below) // additional check is required against SystemVariable(s) because they are subclasses of Parameter/ParameterCollection if (target is SystemVariable || target is SystemVariables) return; int allow = 0; foreach (DraggedItem draggedItem in draggedItems) { object dragged = draggedItem.obj; if (dragged is Parameter dr && !(dragged is SystemVariable)) { if (target is ParameterCollection || (target is Parameter tr && target != dragged && !tr.HasParent(dr))) { allow++; } } } if (allow != 0 && allow == draggedItems.Count) { e.Effect = e.AllowedEffect; // check cursor position: less than 1/3 of target node width => change z-order, else change parent int _20 = Designer.LogicalToDevice(20); Rectangle dragRect = target is Parameter && pos.X < targetNode.Bounds.X + targetNode.Bounds.Width / 3 ? new Rectangle(_20, targetNode.Bounds.Top, tree.Width - _20 * 2, 0) : new Rectangle(targetNode.Bounds.Left, targetNode.Bounds.Bottom, targetNode.Bounds.Width, 0); dragIndicator.BackColor = tree.ForeColor; dragIndicator.SetBounds(tree.Left + dragRect.Left, tree.Top + dragRect.Top, dragRect.Width, Designer.LogicalToDevice(2)); dragIndicator.Show(); } } private void tree_DragDrop(object sender, DragEventArgs e) { dragIndicator.Hide(); if (draggedItems.Count == 0) return; var pos = tree.PointToClient(new Point(e.X, e.Y)); TreeNode targetNode = tree.GetNodeAt(pos); if (targetNode == null) return; object target = targetNode.Tag; if (target is SystemVariable || target is SystemVariables) return; string draggedName = ""; foreach (DraggedItem draggedItem in draggedItems) { Parameter dragged = draggedItem.obj as Parameter; if (dragged == null || dragged is SystemVariable) continue; if (target is ParameterCollection collection) { if (collection.IndexOf(dragged) == -1) { FixParameterName(dragged, collection); collection.Add(dragged); } } else if (target is Parameter targetPar) { if (pos.X < targetNode.Bounds.X + targetNode.Bounds.Width / 3) { // insert dragged before target dragged.ParentCollection.Remove(dragged); var targetParentCollection = targetPar.ParentCollection; FixParameterName(dragged, targetParentCollection); targetParentCollection.Insert(targetParentCollection.IndexOf(targetPar), dragged); } else { if (targetPar.Parameters.IndexOf(dragged) == -1) { FixParameterName(dragged, targetPar.Parameters); dragged.Parent = targetPar; } } } draggedName = dragged.FullName; } // update all designer plugins (this one too) Designer.SetModified(null, "EditData"); NavigateTo(Res.Get("Designer,ToolWindow,Dictionary,Parameters") + "." + draggedName); } private void tree_DragLeave(object sender, EventArgs e) { dragIndicator.Hide(); } private void FixParameterName(Parameter p, ParameterCollection coll) { p.Name = coll.CreateUniqueName(p.Name); } #endregion #region Public Methods /// public override void SelectionChanged() { base.SelectionChanged(); if (Designer.SelectedObjects.Count == 0 || Designer.SelectedObjects[0] is ComponentBase) { updating = true; tree.SelectedNode = null; updating = false; UpdateControls(); } } /// public override void UpdateContent() { report = Designer.ActiveReport; Throttle.Execute(() => { UpdateTree(); // to save search results in DataTree when changing ReportTree #if !WPF SearchMatches(); #endif }); } /// public override void Localize() { base.Localize(); MyRes res = new MyRes("Designer,ToolWindow,Dictionary"); Text = res.Get(""); btnActions.Text = Res.Get("Buttons,Actions"); btnEdit.ToolTipText = res.Get("Edit"); btnDelete.ToolTipText = res.Get("Delete"); btnView.ToolTipText = res.Get("View"); miNew.Text = res.Get("New"); miOpen.Text = res.Get("Open"); miMerge.Text = res.Get("Merge"); miSave.Text = res.Get("Save"); miChooseData.Text = Res.Get("Designer,Menu,Data,Choose"); miNewDataSource.Text = miNewDataSource1.Text = res.Get("NewDataSource"); miSortDataSources.Text = miSortDataSources1.Text = res.Get("SortDataSources"); miNewRelation.Text = res.Get("NewRelation"); miNewParameter.Text = miNewParameter1.Text = res.Get("NewParameter"); miNewTotal.Text = miNewTotal1.Text = res.Get("NewTotal"); miNewCalculatedColumn.Text = miNewCalculatedColumn1.Text = res.Get("NewCalculatedColumn"); miRename.Text = res.Get("Rename"); miEdit.Text = res.Get("Edit"); miDelete.Text = res.Get("Delete"); miDeleteAlias.Text = res.Get("DeleteAlias"); miView.Text = res.Get("View"); miViewJson.Text = res.Get("ViewJson"); miSortDataFields.Text = res.Get("SortDataFields"); miCopyDataSource.Text = res.Get("CopyDataSource"); miUploadDataSource.Text = miUploadDataSource1.Text = res.Get("UploadDataSource"); UpdateTree(); } /// public override void UpdateUIStyle() { base.UpdateUIStyle(); mnuContext.UpdateUIStyle(); splitter.BackColor = UIStyleUtils.GetControlColor(Designer.UIStyle); } /// public override void UpdateDpiDependencies() { base.UpdateDpiDependencies(); Image = Designer.GetImage(72); tree.ImageList = mnuContext.ImageList = Designer.GetImages(); mnuContext.Font = btnActions.Font = Designer.LogicalToDevice(DrawUtils.DefaultFont); btnActions.UpdateDpiDependencies(Designer); } /// public override void SaveState() { base.SaveState(); Storage.SetDip("DescriptionHeight", lblDescription.Height); } /// public override void RestoreState() { base.RestoreState(); lblDescription.Height = Storage.GetDip("DescriptionHeight", 50, 50, 200); } #endregion /// /// Initializes a new instance of the class with default settings. /// /// The report designer. public DictionaryWindow(Designer designer) : base(designer) { Name = "DictionaryWindow"; btnActions = new ToolbarDropDownButton("", -1, btnActions_DropDownOpening); btnEdit = AddButton(68, miEdit_Click); btnDelete = AddButton(51, miDelete_Click); btnView = AddButton(54, miView_Click); toolbar.Items.Insert(0, btnActions); toolbar.Items.Insert(1, btnEdit); toolbar.Items.Insert(2, btnDelete); toolbar.Items.Insert(3, btnView); miNew = AddMenuItem(0, miNew_Click); miOpen = AddMenuItem(1, miOpen_Click); miMerge = AddMenuItem(miMerge_Click); miSave = AddMenuItem(2, miSave_Click); miChooseData = AddMenuItem(Designer.cmdChooseData.Invoke); miChooseData.BeginGroup = true; miSortDataSources = AddMenuItem(Designer.cmdSortDataSources.Invoke); miUploadDataSource = AddMenuItem(264, miUploadDataSource_Click); miNewDataSource = AddMenuItem(137, Designer.cmdAddData.Invoke); miNewDataSource.BeginGroup = true; miNewRelation = AddMenuItem(139, miNewRelation_Click); miNewCalculatedColumn = AddMenuItem(55, miNewCalculatedColumn_Click); miNewParameter = AddMenuItem(56, miNewParameter_Click); miNewTotal = AddMenuItem(65, miNewTotal_Click); btnActions.AddDropDownItems( miNew, miOpen, miMerge, miSave, miChooseData, miSortDataSources, #if !COMMUNITY miUploadDataSource, #endif miNewDataSource, miNewRelation, miNewCalculatedColumn, miNewParameter, miNewTotal); mnuContext = new ContextMenuBase(designer); miSortDataSources1 = AddMenuItem(Designer.cmdSortDataSources.Invoke); miUploadDataSource1 = AddMenuItem(206, miUploadDataSource_Click); miNewDataSource1 = AddMenuItem(137, Designer.cmdAddData.Invoke); miNewCalculatedColumn1 = AddMenuItem(55, miNewCalculatedColumn_Click); miNewParameter1 = AddMenuItem(56, miNewParameter_Click); miNewTotal1 = AddMenuItem(65, miNewTotal_Click); miRename = AddMenuItem(miRename_Click); //miRename.ShortcutKeys = Keys.F2; miEdit = AddMenuItem(68, miEdit_Click); miCopyDataSource = AddMenuItem(6, miCopyDataSource_Click); miDelete = AddMenuItem(51, miDelete_Click); miDeleteAlias = AddMenuItem(miDeleteAlias_Click); miView = AddMenuItem(54, miView_Click); miViewJson = AddMenuItem(54, miViewJson_Click); miSortDataFields = AddMenuItem(miSortDataFields_Click); mnuContext.Opening += mnuContext_Opening; tree.AllowDrop = true; tree.SelectionChanged += tree_SelectionChanged; tree.RightMouseButtonClicked += tree_RightMouseButtonClicked; tree.BeforeLabelEdit += tree_BeforeLabelEdit; tree.AfterLabelEdit += tree_AfterLabelEdit; tree.KeyDown += tree_KeyDown; tree.DoubleClick += miEdit_Click; tree.ItemDrag += tree_ItemDrag; tree.DragOver += tree_DragOver; tree.DragDrop += tree_DragDrop; tree.DragLeave += tree_DragLeave; dragIndicator = new Panel(); dragIndicator.Visible = false; splitter = new Splitter(); splitter.Dock = DockStyle.Bottom; splitter.Visible = false; lblDescription = new DescriptionControl(); lblDescription.Dock = DockStyle.Bottom; lblDescription.Height = 70; lblDescription.Visible = false; Controls.AddRange(new Control[] { dragIndicator, splitter, lblDescription }); Controls.SetChildIndex(dragIndicator, 0); expandedNodes = new List(); Localize(); UpdateDpiDependencies(); } /// /// Describes an item dragged from the "Data Dictionary" window. /// public class DraggedItem { /// /// The dragged object. /// public Object obj; /// /// The text of dragged object. /// public string text; internal DraggedItem(Object obj, string text) { this.obj = obj; this.text = text; } } /// /// Collection of dragged items. /// public class DraggedItemCollection : List { internal DraggedItemCollection() : base() { } } internal static class DragUtils { public static DraggedItemCollection GetAll(DragEventArgs e) { // holding dragged objects data in DragEventArgs does not work in Mono. Use simpler way //DraggedItemCollection items = (DraggedItemCollection)e.Data.GetData(typeof(DraggedItemCollection)); DraggedItemCollection items = DictionaryWindow.draggedItems; if (items == null || items.Count == 0) return null; return items; } public static DraggedItem GetOne(DragEventArgs e) { DraggedItemCollection items = DictionaryWindow.draggedItems; if (items == null || items.Count == 0) return null; return items[items.Count - 1]; } } } }