using FastReport.Utils;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace FastReport
{
partial class ComponentBase
{
#region Public Methods
///
/// Corrects the object's size and sizing point if the size becomes negative.
///
/// Current mouse state.
/// Typically you don't need to use or override this method.
/// This method is called by the FastReport designer to check if the object's size becomes negative
/// when resizing the object by the mouse. Method must correct the object's size and/or position to
/// make it positive, also change the sizing point if needed.
public virtual void CheckNegativeSize(FRMouseEventArgs e)
{
if (Width < 0 && Height < 0)
{
e.sizingPoint = SizingPointHelper.SwapDiagonally(e.sizingPoint);
Bounds = new RectangleF(Right, Bottom, -Width, -Height);
}
else if (Width < 0)
{
e.sizingPoint = SizingPointHelper.SwapHorizontally(e.sizingPoint);
Bounds = new RectangleF(Right, Top, -Width, Height);
}
else if (Height < 0)
{
e.sizingPoint = SizingPointHelper.SwapVertically(e.sizingPoint);
Bounds = new RectangleF(Left, Bottom, Width, -Height);
}
else
return;
e.cursor = SizingPointHelper.ToCursor(e.sizingPoint);
}
///
/// Checks if the object is inside its parent.
///
/// if true, check now independent of any conditions.
///
/// Typically you don't need to use or override this method.
/// When you move an object with the mouse, it may be moved outside its parent. If so, this method
/// must find a new parent for the object and correct it's Left, Top and Parent
/// properties. If immediately parameter is false, you can optimize the method
/// to search for new parent only if the object's bounds are outside parent. If this parameter is
/// true, you must skip any optimizations and search for a parent immediately.
///
public virtual void CheckParent(bool immediately)
{
}
///
/// Draws the object.
///
/// Paint event args.
///
/// This method is widely used in the FastReport. It is called each time when the object needs to draw
/// or print itself.
/// In order to draw the object correctly, you should multiply the object's bounds by the scale
/// parameter.
/// cache parameter is used to optimize the drawing speed. It holds all items such as
/// pens, fonts, brushes, string formats that was used before. If the item with requested parameters
/// exists in the cache, it will be returned (instead of create new item and then dispose it).
///
public virtual void Draw(FRPaintEventArgs e)
{
if (IsDesigning)
{
if (IsAncestor)
e.Graphics.DrawImage(Report.Designer.GetImage(99), (int)(AbsRight * e.ScaleX - Report.Designer.LogicalToDevice(14)), (int)(AbsTop * e.ScaleY));
}
}
///
/// Draw the frame around the object to indicate that it accepts the drag&drop operation.
///
/// Paint event args.
/// The color of frame.
public virtual void DrawDragAcceptFrame(FRPaintEventArgs e, Color color)
{
RectangleF rect = new RectangleF(AbsLeft * e.ScaleX, AbsTop * e.ScaleY, Width * e.ScaleX, Height * e.ScaleY);
Pen p = e.Cache.GetPen(color, 1, DashStyle.Dot);
for (int i = 0; i < 3; i++)
{
e.Graphics.DrawRectangle(p, rect.Left, rect.Top, rect.Width, rect.Height);
rect.Inflate(-1, -1);
}
}
///
/// Draw the selection points.
///
/// Paint event args.
///
/// This method draws a set of selection points returned by the method.
///
public virtual void DrawSelection(FRPaintEventArgs e)
{
if (Page == null)
return;
bool firstSelected = Report.Designer.SelectedObjects.IndexOf(this) == 0;
Pen p = firstSelected ? Pens.Black : Pens.White;
Brush b = firstSelected ? Brushes.White : Brushes.Black;
SelectionPoint[] selectionPoints = GetSelectionPoints();
foreach (SelectionPoint pt in selectionPoints)
{
DrawSelectionPoint(e, p, b, pt.x, pt.y);
}
}
///
public override ContextMenuBase GetContextMenu()
{
return new ComponentBaseMenu(Report.Designer);
}
///
/// Gets the preferred size of an object.
///
/// Preferred size.
///
/// This method is called by the FastReport designer when you insert a new object.
///
public virtual SizeF GetPreferredSize()
{
return new SizeF(Units.Millimeters * 25, Units.Millimeters * 5);
}
///
/// Returns a "smart tag" menu.
///
///
/// "Smart tag" is a little button that appears near the object's top-right corner when we are in the
/// designer and move the mouse over the object. When you click that button you will see a popup window
/// where you can set up some properties of the object. FastReport uses smart tags to quickly choose
/// the datasource (for a band) or data column (for objects).
///
public virtual SmartTagBase GetSmartTag()
{
return null;
}
///
/// Handles double click event in the designer.
///
///
/// This method is called when the user doubleclicks the object in the designer. Typical implementation
/// invokes the object's editor (calls the InvokeEditor method) and sets the designer's
/// Modified flag.
///
public virtual void HandleDoubleClick()
{
if (HasFlag(Flags.CanEdit) && !HasRestriction(Restrictions.DontEdit) &&
this is IHasEditor && (this as IHasEditor).InvokeEditor())
Report.Designer.SetModified(this, "Change");
}
///
/// Handles the DragDrop event in the designer.
///
/// Current mouse state.
///
/// This method is called when the user drops an item from the Data Tree window into this object.
/// This method should copy the information from the e.DraggedObject object and set the
/// e.Handled flag to true to complete the drag operation.
///
public virtual void HandleDragDrop(FRMouseEventArgs e)
{
}
///
/// Handles the DragOver event in the designer.
///
/// Current mouse state.
///
/// This method is called when the user drags an item from the Data Tree window. This method should
/// check that the mouse (e.X, e.Y) is inside the object, then set the e.Handled flag
/// to true if an item can be dragged into this object.
///
public virtual void HandleDragOver(FRMouseEventArgs e)
{
}
///
/// Handles KeyDown event in the designer.
///
/// The designer's workspace.
/// Keyboard event parameters.
///
/// This method is called when the user presses any key in the designer. Typical implementation
/// does nothing.
///
public virtual void HandleKeyDown(Control sender, KeyEventArgs e)
{
}
///
/// Handles MouseDown event that occurs when the user clicks the mouse in the designer.
///
///
/// This method is called when the user press the mouse button in the designer.
/// The standard implementation does the following:
///
/// - checks if the mouse pointer is inside the object;
/// - add an object to the selected objects list of the designer;
/// - sets the e.Handled flag to true.
///
///
/// Current mouse state.
public virtual void HandleMouseDown(FRMouseEventArgs e)
{
if (PointInObject(new PointF(e.x, e.y)))
{
SelectedObjectCollection selection = Report.Designer.SelectedObjects;
if (e.modifierKeys == Keys.Shift)
{
// toggle selection
if (selection.IndexOf(this) != -1)
{
if (selection.Count > 1)
selection.Remove(this);
}
else
selection.Add(this);
}
else
{
// select the object if not selected yet
if (selection.IndexOf(this) == -1)
{
selection.Clear();
selection.Add(this);
}
}
e.handled = true;
e.mode = WorkspaceMode2.Move;
}
}
///
/// Handles MouseMove event that occurs when the user moves the mouse in the designer.
///
///
/// This method is called when the user moves the mouse in the designer. Typical
/// use of this method is to change the mouse cursor to SizeAll when it is over
/// an object. The standard implementation does the following:
///
/// - checks if the mouse pointer is inside the object;
/// - changes the cursor shape (e.Cursor property);
/// - sets the e.Handled flag to true.
///
///
/// Current mouse state.
public virtual void HandleMouseHover(FRMouseEventArgs e)
{
if (PointInObject(new PointF(e.x, e.y)))
{
e.handled = true;
e.cursor = Cursors.SizeAll;
}
}
///
/// Handles MouseMove event that occurs when the user moves the mouse in the designer.
///
///
/// This method is called when the user moves the mouse in the designer. The
/// standard implementation does the following:
///
/// -
/// if mouse button is not pressed, check that mouse pointer is inside one of
/// the selection points returned by the
/// method and set the e.SizingPoint member to the corresponding sizing
/// point;
///
/// - if mouse button is pressed, and e.SizingPoint member is not
/// SizingPoint.None, resize the object.
///
///
/// Current mouse state.
public virtual void HandleMouseMove(FRMouseEventArgs e)
{
if (!IsSelected)
return;
if (e.button == MouseButtons.None)
{
PointF point = new PointF(e.x, e.y);
e.sizingPoint = SizingPoint.None;
SelectionPoint[] selectionPoints = GetSelectionPoints();
foreach (SelectionPoint pt in selectionPoints)
{
if (PointInSelectionPoint(pt.x, pt.y, point))
{
e.sizingPoint = pt.sizingPoint;
break;
}
}
if (e.sizingPoint != SizingPoint.None)
{
e.handled = true;
e.mode = WorkspaceMode2.Size;
e.cursor = SizingPointHelper.ToCursor(e.sizingPoint);
}
}
else if (!IsParentSelected)
{
if (e.mode == WorkspaceMode2.Move)
{
Left += e.delta.X;
Top += e.delta.Y;
}
else if (e.mode == WorkspaceMode2.Size)
{
if ((e.modifierKeys & Keys.Shift) > 0)
{
bool wider = Math.Abs(e.delta.X) > Math.Abs(e.delta.Y);
float width = Width;
float height = Height;
switch (e.sizingPoint)
{
case SizingPoint.LeftTop:
if (wider)
{
Left += e.delta.Y;
Width -= e.delta.Y;
if (width != 0)
{
Top += Height - (Height * Width / width);
Height = Height * Width / width;
}
}
else
{
Top += e.delta.X;
Height -= e.delta.X;
if (height != 0)
{
Left += Width - (Width * Height / height);
Width = Width * Height / height;
}
}
break;
case SizingPoint.LeftBottom:
if (wider)
{
Left -= e.delta.Y;
Width += e.delta.Y;
if (width != 0)
Height = Height * Width / width;
}
else
{
Height -= e.delta.X;
if (height != 0)
{
Left += Width - (Width * Height / height);
Width = Width * Height / height;
}
}
break;
case SizingPoint.RightTop:
if (wider)
{
Width -= e.delta.Y;
if (width != 0)
{
Top += Height - (Height * Width / width);
Height = Height * Width / width;
}
}
else
{
Height += e.delta.X;
Top -= e.delta.X;
if (height != 0)
Width = Width * Height / height;
}
break;
case SizingPoint.RightBottom:
if (wider)
{
Width += e.delta.Y;
if (width != 0)
Height = Height * Width / width;
}
else
{
Height += e.delta.X;
if (height != 0)
Width = Width * Height / height;
}
break;
case SizingPoint.TopCenter:
Top += e.delta.Y;
Height -= e.delta.Y;
if (height != 0)
Width = Width * Height / height;
break;
case SizingPoint.BottomCenter:
Height += e.delta.Y;
if (height != 0)
Width = Width * Height / height;
break;
case SizingPoint.LeftCenter:
Left += e.delta.X;
Width -= e.delta.X;
if (width != 0)
Height = Height * Width / width;
break;
case SizingPoint.RightCenter:
Width += e.delta.X;
if (width != 0)
Height = Height * Width / width;
break;
}
}
else
{
switch (e.sizingPoint)
{
case SizingPoint.LeftTop:
Left += e.delta.X;
Width -= e.delta.X;
Top += e.delta.Y;
Height -= e.delta.Y;
break;
case SizingPoint.LeftBottom:
Left += e.delta.X;
Width -= e.delta.X;
Height += e.delta.Y;
break;
case SizingPoint.RightTop:
Width += e.delta.X;
Top += e.delta.Y;
Height -= e.delta.Y;
break;
case SizingPoint.RightBottom:
Width += e.delta.X;
Height += e.delta.Y;
break;
case SizingPoint.TopCenter:
Top += e.delta.Y;
Height -= e.delta.Y;
break;
case SizingPoint.BottomCenter:
Height += e.delta.Y;
break;
case SizingPoint.LeftCenter:
Left += e.delta.X;
Width -= e.delta.X;
break;
case SizingPoint.RightCenter:
Width += e.delta.X;
break;
}
}
CheckNegativeSize(e);
}
CheckParent(false);
}
}
///
/// Handles MouseUp event that occurs when the user releases the mouse button in the designer.
///
///
/// This method is called when the user releases the mouse button in the
/// designer. The standard implementation does the following:
///
/// - if e.Mode is WorkspaceMode2.SelectionRect, checks if object
/// is inside the selection rectangle and sets e.Handled flag if so;
/// -
/// checks that object is inside its parent (calls the
/// method).
///
///
///
/// Current mouse state.
public virtual void HandleMouseUp(FRMouseEventArgs e)
{
if (e.mode == WorkspaceMode2.SelectionRect)
{
if (e.selectionRect.IntersectsWith(new RectangleF(AbsLeft, AbsTop, Width, Height)))
e.handled = true;
}
else if (e.mode == WorkspaceMode2.Move || e.mode == WorkspaceMode2.Size)
{
if (IsSelected)
CheckParent(true);
}
}
///
/// Handles mouse wheel event.
///
/// Current mouse state.
public virtual void HandleMouseWheel(FRMouseEventArgs e)
{
}
///
/// Checks if given point is inside the object's bounds.
///
/// point to check.
/// true if point is inside the object's bounds.
///
/// You can override this method if your objectis not of rectangular form.
///
public virtual bool PointInObject(PointF point)
{
return AbsBounds.Contains(point);
}
#endregion Public Methods
#region Protected Methods
///
/// Draws the selection point.
///
/// Paint event args.
/// object.
/// object.
/// Left coordinate.
/// Top coordinate.
protected virtual void DrawSelectionPoint(FRPaintEventArgs e, Pen p, Brush b, float x, float y)
{
IGraphics g = e.Graphics;
x = (float)Math.Round(x * e.ScaleX);
y = (float)Math.Round(y * e.ScaleY);
float m = Report?.Designer?.DpiMultiplier() ?? 1;
Rectangle rect = Rectangle.Round(new RectangleF(x - 2 * m, y - 2 * m, 4 * m, 4 * m));
g.FillRectangle(b, rect);
g.DrawRectangle(p, rect);
}
///
/// Gets the object's selection points.
///
/// Array of objects.
///
/// Selection point is a small square displayed at the object's sides when object is selected
/// in the designer. You can drag this square by the mouse to change the object's size. For example,
/// the TextObject has eight selection points to change its width and height by the mouse.
/// If you are developing a new component for FastReport, you may override this method
/// if your object has non-standard set of selection points. For example, if an object has something like
/// "AutoSize" property, it would be good to disable all selection points if that property is true,
/// to disable resizing of the object by the mouse.
///
protected virtual SelectionPoint[] GetSelectionPoints()
{
return new SelectionPoint[] {
new SelectionPoint(AbsLeft, AbsTop, SizingPoint.LeftTop),
new SelectionPoint(AbsLeft + Width, AbsTop, SizingPoint.RightTop),
new SelectionPoint(AbsLeft, AbsTop + Height, SizingPoint.LeftBottom),
new SelectionPoint(AbsLeft + Width, AbsTop + Height, SizingPoint.RightBottom),
new SelectionPoint(AbsLeft + Width / 2, AbsTop, SizingPoint.TopCenter),
new SelectionPoint(AbsLeft + Width / 2, AbsTop + Height, SizingPoint.BottomCenter),
new SelectionPoint(AbsLeft, AbsTop + Height / 2, SizingPoint.LeftCenter),
new SelectionPoint(AbsLeft + Width, AbsTop + Height / 2, SizingPoint.RightCenter) };
}
///
/// Gets a value indicating that given point is inside selection point.
///
/// point's x coordinate.
/// point's y coordinate.
/// selection point.
/// true if (x,y) is inside the point
protected bool PointInSelectionPoint(float x, float y, PointF point)
{
return x >= point.X - 3 && x <= point.X + 3 && y >= point.Y - 3 && y <= point.Y + 3;
}
#endregion Protected Methods
}
}