using System; using System.Collections; using System.Windows.Forms; using System.Drawing; using FastReport.Utils; namespace FastReport.Design.PageDesigners { internal abstract class DesignWorkspaceBase : UserControl { #region Fields private PageDesignerBase pageDesigner; protected WorkspaceMode1 mode1; protected WorkspaceMode2 mode2; protected bool mouseDown; protected bool mouseMoved; private PointF lastMousePoint; protected RectangleF selectionRect; protected FRMouseEventArgs eventArgs; protected VirtualGuides virtualGuides; private System.Windows.Forms.ToolTip toolTip; private SmartTagButton smartTag; private InsertFrom insertionSource; private SelectedObjectCollection selectedObjectsAtMouseDown; private static Cursor FFormatToolCursor; #endregion #region Properties public PageDesignerBase PageDesigner { get { return pageDesigner; } } public Designer Designer { get { return pageDesigner.Designer; } } public bool Locked { get { return pageDesigner.Locked; } } public Report Report { get { return pageDesigner.Report; } } public virtual Point Offset { get { return new Point(0, 0); } } protected abstract float GridSnapSize { get; } private GraphicCache GraphicCache { get { return Report.GraphicCache; } } #endregion public abstract float GetScale(); protected abstract bool CheckGridStep(); protected RectangleF NormalizeSelectionRect() { RectangleF result = selectionRect; if (selectionRect.Left > selectionRect.Right) { result.X = selectionRect.Right; result.Width = -selectionRect.Width; } if (selectionRect.Top > selectionRect.Bottom) { result.Y = selectionRect.Bottom; result.Height = -selectionRect.Height; } return result; } protected void CloneSelectedObjects() { ObjectCollection list = new ObjectCollection(); foreach (Base c in Designer.Objects) { if (Designer.SelectedObjects.IndexOf(c) != -1) if (c.HasFlag(Flags.CanCopy)) list.Add(c); } // prepare to create unique name FastNameCreator nameCreator = new FastNameCreator(Report.AllNamedObjects); Designer.SelectedObjects.Clear(); foreach (Base c in list) { Base clone = Activator.CreateInstance(c.GetType()) as Base; clone.AssignAll(c); clone.Name = ""; clone.Parent = c.Parent; nameCreator.CreateUniqueName(clone); foreach (Base c1 in clone.AllObjects) { nameCreator.CreateUniqueName(c1); } Designer.Objects.Add(clone); Designer.SelectedObjects.Add(clone); } } protected void HandleDoubleClick() { if (Designer.SelectedObjects.Count == 1 && Designer.SelectedObjects[0] is ComponentBase) { ComponentBase c = Designer.SelectedObjects[0] as ComponentBase; c.HandleDoubleClick(); } } protected void MoveSelectedObjects(int xDir, int yDir, bool smooth) { foreach (Base obj in Designer.SelectedObjects) { if (obj is ComponentBase && !(obj is PageBase)) { ComponentBase c = obj as ComponentBase; c.Left += smooth ? xDir : xDir * GridSnapSize; c.Top += smooth ? yDir : yDir * GridSnapSize; c.CheckParent(true); } } virtualGuides.Check(); Refresh(); Designer.SetModified(pageDesigner, "Move"); } protected void ResizeSelectedObjects(int xDir, int yDir, bool smooth) { foreach (Base obj in Designer.SelectedObjects) { if (obj is ComponentBase && !(obj is PageBase)) { ComponentBase c = obj as ComponentBase; c.Width += smooth ? xDir : xDir * GridSnapSize; c.Height += smooth ? yDir : yDir * GridSnapSize; c.CheckNegativeSize(eventArgs); } } virtualGuides.Check(); Refresh(); Designer.SetModified(pageDesigner, "Size"); } protected void SelectNextObject(bool forward) { int index = 0; if (Designer.SelectedObjects.Count != 0) index = Designer.Objects.IndexOf(Designer.SelectedObjects[0]); index += forward ? 1 : -1; if (index < 0) index = Designer.Objects.Count - 1; if (index > Designer.Objects.Count - 1) index = 0; Designer.SelectedObjects.Clear(); Designer.SelectedObjects.Add(Designer.Objects[index]); Designer.SelectionChanged(null); } protected void ShowToolTip(string text, int x, int y) { if (toolTip == null) toolTip = new System.Windows.Forms.ToolTip(); if (toolTip != null) toolTip.Show(text, this, x, y); } protected void HideToolTip() { if (toolTip != null) { toolTip.Hide(this); toolTip.Dispose(); toolTip = null; } } protected abstract Base GetParentForPastedObjects(); public virtual void Paste(ObjectCollection list, InsertFrom source) { insertionSource = source; pageDesigner.IsInsertingObject = true; // prepare to create unique name ObjectCollection allObjects = Report.AllNamedObjects; // prepare a list of existing names Hashtable names = new Hashtable(); foreach (Base c in allObjects) { names[c.Name] = 0; } // since we are trying to preserve pasted object's name, add all names to the // allObjects list to guarantee that FastNameCreator will work correctly foreach (Base c in list) { allObjects.Add(c); // there is an existing object with the same name. Clear the name to indicate // that we should create an unique name for this object if (names.ContainsKey(c.Name)) c.Name = ""; if (c is IParent) { foreach (Base c1 in c.AllObjects) { allObjects.Add(c1); if (names.ContainsKey(c1.Name)) c1.Name = ""; } } } FastNameCreator nameCreator = new FastNameCreator(allObjects); // create unique names and add objects to designer's lists Designer.SelectedObjects.Clear(); foreach (Base c in list) { c.Parent = GetParentForPastedObjects(); if (c.Name == "") nameCreator.CreateUniqueName(c); Designer.Objects.Add(c); // reset group index if (c is ComponentBase) (c as ComponentBase).GroupIndex = 0; if (c is IParent) { foreach (Base c1 in c.AllObjects) { if (c1.Name == "") nameCreator.CreateUniqueName(c1); Designer.Objects.Add(c1); } } Designer.SelectedObjects.Add(c); } } public void CancelPaste() { pageDesigner.IsInsertingObject = false; if (mode1 != WorkspaceMode1.Select) { while (Designer.SelectedObjects.Count > 0) { Base c = Designer.SelectedObjects[0]; Designer.SelectedObjects.Remove(c); Designer.Objects.Remove(c); c.Dispose(); } mode1 = WorkspaceMode1.Select; mode2 = WorkspaceMode2.None; OnMouseUp(new MouseEventArgs(MouseButtons.None, 0, 0, 0, 0)); Designer.ResetObjectsToolbar(true); } } protected override void Dispose(bool disposing) { CancelPaste(); base.Dispose(disposing); } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (Locked) return; Designer.SelectedObjects.CopyTo(selectedObjectsAtMouseDown); mouseDown = true; mouseMoved = false; eventArgs.x = e.X / GetScale() - Offset.X; eventArgs.y = e.Y / GetScale() - Offset.Y; eventArgs.button = e.Button; eventArgs.modifierKeys = ModifierKeys; eventArgs.handled = false; if (mode2 == WorkspaceMode2.None) { // find an object under the mouse for (int i = Designer.Objects.Count - 1; i >= 0; i--) { ComponentBase c = Designer.Objects[i] as ComponentBase; if (c != null) { c.HandleMouseDown(eventArgs); if (eventArgs.handled) { mode2 = eventArgs.mode; break; } } } } else if (eventArgs.activeObject != null) { eventArgs.activeObject.HandleMouseDown(eventArgs); } if (!selectedObjectsAtMouseDown.Equals(Designer.SelectedObjects)) { Designer.SelectionChanged(pageDesigner); Designer.SelectedObjects.CopyTo(selectedObjectsAtMouseDown); } lastMousePoint.X = eventArgs.x; lastMousePoint.Y = eventArgs.y; selectionRect = new RectangleF(eventArgs.x, eventArgs.y, 0, 0); virtualGuides.Create(); smartTag.Hide(); Refresh(); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (Locked || Designer.ActiveReport != Report) return; eventArgs.x = e.X / GetScale() - Offset.X; eventArgs.y = e.Y / GetScale() - Offset.Y; eventArgs.button = mode1 == WorkspaceMode1.Insert ? MouseButtons.Left : e.Button; if (mode1 == WorkspaceMode1.Insert && mode2 == WorkspaceMode2.Move) Cursor = Cursors.Default; eventArgs.delta = new PointF(eventArgs.x - lastMousePoint.X, eventArgs.y - lastMousePoint.Y); if (e.Button == MouseButtons.None && mode1 == WorkspaceMode1.Select) { mode2 = WorkspaceMode2.None; eventArgs.cursor = Cursors.Default; eventArgs.mode = mode2; eventArgs.activeObject = null; eventArgs.handled = false; // check object's sizing points for (int i = Designer.Objects.Count - 1; i >= 0; i--) { ComponentBase c = Designer.Objects[i] as ComponentBase; if (c != null) { c.HandleMouseMove(eventArgs); if (eventArgs.handled) { mode2 = eventArgs.mode; eventArgs.activeObject = c; break; } } } // no sizing points found, check hover if (!eventArgs.handled) { for (int i = Designer.Objects.Count - 1; i >= 0; i--) { ComponentBase c = Designer.Objects[i] as ComponentBase; if (c != null) { c.HandleMouseHover(eventArgs); if (eventArgs.handled) { if (Designer.FormatPainter) eventArgs.cursor = FFormatToolCursor; eventArgs.activeObject = c; if (c.HasFlag(Flags.HasSmartTag) && !c.HasRestriction(Restrictions.DontEdit)) smartTag.Show(c); break; } } } } if (eventArgs.activeObject == null || !eventArgs.activeObject.HasFlag(Flags.HasSmartTag)) smartTag.Hide(); Cursor = eventArgs.cursor; } else if (mode2 == WorkspaceMode2.Move || mode2 == WorkspaceMode2.Size) { // handle drag&drop from the data tree bool dragHandled = false; if (mode1 == WorkspaceMode1.DragDrop) { eventArgs.dragTarget = null; eventArgs.handled = false; eventArgs.dragMessage = ""; for (int i = Designer.Objects.Count - 1; i >= 0; i--) { ComponentBase c = Designer.Objects[i] as ComponentBase; if (c != null && (eventArgs.dragSources == null || Array.IndexOf(eventArgs.dragSources, c) == -1) && !c.HasRestriction(Restrictions.DontModify)) { c.HandleDragOver(eventArgs); } if (eventArgs.handled) { eventArgs.dragTarget = c; dragHandled = true; eventArgs.handled = false; // handle remained objects to reset its state. To do this, invert mouse location eventArgs.x = -eventArgs.x; eventArgs.y = -eventArgs.y; } } if (dragHandled) { // revert back the mouse location eventArgs.x = -eventArgs.x; eventArgs.y = -eventArgs.y; } foreach (ComponentBase obj in eventArgs.dragSources) obj.SetFlags(Flags.CanDraw, !dragHandled); } if (!dragHandled && !CheckGridStep()) return; // if insert is on and user press the mouse button and move the mouse, resize selected objects if (mode1 == WorkspaceMode1.Insert && e.Button == MouseButtons.Left && !mouseMoved) { mode2 = WorkspaceMode2.Size; foreach (Base c in Designer.SelectedObjects) { if (c is ComponentBase) { (c as ComponentBase).Width = 0; (c as ComponentBase).Height = 0; } } eventArgs.sizingPoint = SizingPoint.RightBottom; } // ctrl was pressed, clone selected objects if (mode1 == WorkspaceMode1.Select && ModifierKeys == Keys.Control && !mouseMoved) CloneSelectedObjects(); mouseMoved = true; eventArgs.mode = mode2; eventArgs.handled = false; // first serve the active object if (eventArgs.activeObject != null) eventArgs.activeObject.HandleMouseMove(eventArgs); // if active object does not reset the handled flag, serve other objects if (!eventArgs.handled) { foreach (Base c in Designer.Objects) { if (c is ComponentBase) { if (c != eventArgs.activeObject) (c as ComponentBase).HandleMouseMove(eventArgs); } } } lastMousePoint.X += eventArgs.delta.X; lastMousePoint.Y += eventArgs.delta.Y; if (mode1 == WorkspaceMode1.DragDrop && !dragHandled) { // correct the location of the dragged object because we skip the GridCheck float offset = 0f; foreach (ComponentBase obj in eventArgs.dragSources) { if (offset == 0f) { obj.Left = (int)Math.Round(obj.Left / GridSnapSize) * GridSnapSize; obj.Top = (int)Math.Round(obj.Top / GridSnapSize) * GridSnapSize; } else { obj.Left = offset; obj.Top = (int)Math.Round(obj.Top / GridSnapSize) * GridSnapSize; } offset = obj.Right + GridSnapSize; } } if (mode1 != WorkspaceMode1.DragDrop || !dragHandled) { virtualGuides.Check(); ShowLocationSizeToolTip(e.X + 20, e.Y + 20); } else { virtualGuides.Clear(); if (String.IsNullOrEmpty(eventArgs.dragMessage)) HideToolTip(); else ShowToolTip(eventArgs.dragMessage, e.X + 20, e.Y + 20); } Refresh1(); } else if (mode2 == WorkspaceMode2.SelectionRect) { selectionRect = new RectangleF(selectionRect.Left, selectionRect.Top, eventArgs.x - selectionRect.Left, eventArgs.y - selectionRect.Top); Refresh(); } else if (mode2 == WorkspaceMode2.Custom) { if (!CheckGridStep()) return; mouseMoved = true; eventArgs.mode = mode2; if (eventArgs.activeObject != null) eventArgs.activeObject.HandleMouseMove(eventArgs); lastMousePoint.X += eventArgs.delta.X; lastMousePoint.Y += eventArgs.delta.Y; Refresh(); } } protected virtual void ShowLocationSizeToolTip(int x, int y) { } protected virtual void Refresh1() { Refresh(); } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (Locked) return; HideToolTip(); pageDesigner.IsInsertingObject = false; eventArgs.x = e.X / GetScale() - Offset.X; eventArgs.y = e.Y / GetScale() - Offset.Y; eventArgs.button = e.Button; eventArgs.mode = mode2; eventArgs.handled = false; if (mode2 == WorkspaceMode2.Move || mode2 == WorkspaceMode2.Size) { // handle drag&drop from the data tree bool dragHandled = false; if (mode1 == WorkspaceMode1.DragDrop) { if (eventArgs.dragTarget != null) { dragHandled = true; eventArgs.dragTarget.HandleDragDrop(eventArgs); if (eventArgs.dragSources != null) { foreach (ComponentBase dragSource in eventArgs.dragSources) { Designer.Objects.Remove(dragSource); dragSource.Dispose(); } eventArgs.dragSources = null; } Designer.SelectedObjects.Clear(); Designer.SelectedObjects.Add(eventArgs.dragTarget); } } // change selected object when resizing band if (eventArgs.activeObject is BandBase && mode2 == WorkspaceMode2.Size) { Designer.SelectedObjects.Clear(); Designer.SelectedObjects.Add(eventArgs.activeObject); } // serve all objects for (int i = 0; i < Designer.Objects.Count; i++) { ComponentBase c = Designer.Objects[i] as ComponentBase; if (c != null) c.HandleMouseUp(eventArgs); } // remove objects with zero size if (mode1 == WorkspaceMode1.Insert) { int i = 0; while (i < Designer.SelectedObjects.Count) { ComponentBase c = Designer.SelectedObjects[i] as ComponentBase; if (c != null && c.Width == 0 && c.Height == 0) { Designer.Objects.Remove(c); Designer.SelectedObjects.Remove(c); c.Dispose(); i--; } i++; } } if (mode1 != WorkspaceMode1.Select && !dragHandled) { // during OnInsert call current context may be changed WorkspaceMode1 saveMode = mode1; mode1 = WorkspaceMode1.Select; ObjectCollection insertedObjects = new ObjectCollection(); Designer.SelectedObjects.CopyTo(insertedObjects); foreach (Base c in insertedObjects) { c.OnAfterInsert(insertionSource); if ((c is Matrix.MatrixObject || c is AdvMatrix.AdvMatrixObject) && (c.Parent is HeaderFooterBandBase || c.Parent is PageHeaderBand || c.Parent is PageFooterBand)) { MyRes res = new MyRes("Messages"); string message = String.Format(res.Get("WarningMatrixOnRepeatedBand"), c.ClassName, c.Parent.ClassName); FRMessageBox.Information(message); } } mode1 = saveMode; } // check if we actually move a mouse after we clicked it string action = mode1 != WorkspaceMode1.Select ? "Insert" : mode2 == WorkspaceMode2.Move ? "Move" : "Size"; if (dragHandled) action = "Change"; if (mouseMoved || mode1 != WorkspaceMode1.Select) Designer.SetModified(pageDesigner, action); } else if (mode2 == WorkspaceMode2.SelectionRect) { eventArgs.selectionRect = NormalizeSelectionRect(); if (eventArgs.activeObject != null) eventArgs.activeObject.HandleMouseUp(eventArgs); } else if (mode2 == WorkspaceMode2.Custom) { if (eventArgs.activeObject != null) eventArgs.activeObject.HandleMouseUp(eventArgs); } bool needReset = mode1 != WorkspaceMode1.Select; if (!selectedObjectsAtMouseDown.Equals(Designer.SelectedObjects) || needReset) Designer.SelectionChanged(pageDesigner); mouseDown = false; mode1 = WorkspaceMode1.Select; mode2 = WorkspaceMode2.None; virtualGuides.Clear(); Refresh2(); if (needReset) Designer.ResetObjectsToolbar(false); if (e.Button == MouseButtons.Right && Designer.SelectedObjects.Count > 0) { ContextMenuBase menu = Designer.SelectedObjects[0].GetContextMenu(); if (menu != null) { menu.Show(this, e.Location); } } } protected virtual void Refresh2() { Refresh(); } protected override void OnMouseDoubleClick(MouseEventArgs e) { base.OnMouseDoubleClick(e); if (e.Button == MouseButtons.Left) HandleDoubleClick(); } protected override bool IsInputKey(Keys keyData) { return (keyData & Keys.Up) != 0 || (keyData & Keys.Down) != 0 || (keyData & Keys.Left) != 0 || (keyData & Keys.Right) != 0 || (keyData & Keys.Tab) != 0; } protected override void OnKeyDown(KeyEventArgs e) { if (Config.DisableHotkeys) return; base.OnKeyDown(e); // serve all objects foreach (Base c in Designer.Objects) { if (c is ComponentBase) { (c as ComponentBase).HandleKeyDown(this, e); if (e.Handled) return; } } int xDir = 0; int yDir = 0; switch (e.KeyCode) { case Keys.Escape: Designer.ResetObjectsToolbar(true); CancelPaste(); Designer.SelectedObjects.Clear(); Designer.SelectedObjects.Add(PageDesigner.Page); Designer.SelectionChanged(null); break; case Keys.Enter: HandleDoubleClick(); break; case Keys.Insert: if ((e.Modifiers & Keys.Control) != 0) Designer.cmdCopy.Invoke(); else if ((e.Modifiers & Keys.Shift) != 0) Designer.cmdPaste.Invoke(); break; case Keys.Delete: CancelPaste(); if ((e.Modifiers & Keys.Shift) == 0) Designer.cmdDelete.Invoke(); else Designer.cmdCut.Invoke(); break; case Keys.Up: yDir = -1; break; case Keys.Down: yDir = 1; break; case Keys.Left: xDir = -1; break; case Keys.Right: xDir = 1; break; case Keys.Tab: SelectNextObject((e.Modifiers & Keys.Shift) == 0); break; } if ((e.Modifiers & Keys.Control) != 0) { switch (e.KeyCode) { case Keys.C: Designer.cmdCopy.Invoke(); break; case Keys.X: Designer.cmdCut.Invoke(); break; case Keys.V: Designer.cmdPaste.Invoke(); break; case Keys.A: Designer.cmdSelectAll.Invoke(); break; } } if (xDir != 0 || yDir != 0) { virtualGuides.Create(); bool smooth = (e.Modifiers & Keys.Control) != 0; if ((e.Modifiers & Keys.Shift) != 0) ResizeSelectedObjects(xDir, yDir, smooth); else MoveSelectedObjects(xDir, yDir, smooth); virtualGuides.Clear(); } } internal DesignWorkspaceBase(PageDesignerBase pageDesigner) { this.pageDesigner = pageDesigner; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true); AllowDrop = true; eventArgs = new FRMouseEventArgs(); selectedObjectsAtMouseDown = new SelectedObjectCollection(); smartTag = new SmartTagButton(this); Controls.Add(smartTag); virtualGuides = new VirtualGuides(this); } static DesignWorkspaceBase() { FFormatToolCursor = ResourceLoader.GetCursor("Format.cur"); } private class SmartTagButton : Button { private DesignWorkspaceBase workspace; private ComponentBase component; protected override void OnClick(EventArgs e) { base.OnClick(e); Point pt = PointToScreen(this.LogicalToDevice(new Point(10, 0))); SmartTagBase smartTag = component.GetSmartTag(); if (smartTag != null) smartTag.Show(pt); } protected override bool IsInputKey(Keys keyData) { return (keyData & Keys.Up) != 0 || (keyData & Keys.Down) != 0 || (keyData & Keys.Left) != 0 || (keyData & Keys.Right) != 0 || (keyData & Keys.Tab) != 0; } protected override void OnKeyDown(KeyEventArgs kevent) { workspace.OnKeyDown(kevent); } public void Show(ComponentBase c) { BackgroundImage = this.GetImage(77); Point loc = new Point((int)Math.Round(c.AbsRight * workspace.GetScale()) - this.LogicalToDevice(14), (int)Math.Round(c.AbsTop * workspace.GetScale()) - this.LogicalToDevice(5)); if (loc.Y < 0) loc.Y = 0; Location = loc; Size = this.LogicalToDevice(new Size(10, 10)); Visible = true; component = c; } public SmartTagButton(DesignWorkspaceBase workspace) { this.workspace = workspace; Visible = false; Cursor = Cursors.Hand; BackgroundImageLayout = ImageLayout.Center; BackColor = Color.White; FlatStyle = FlatStyle.Flat; FlatAppearance.BorderSize = 0; } } } }