123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- using FastReport.Utils;
- using System;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Windows.Forms;
- namespace FastReport
- {
- partial class ComponentBase
- {
- #region Public Methods
- /// <summary>
- /// Corrects the object's size and sizing point if the size becomes negative.
- /// </summary>
- /// <param name="e">Current mouse state.</param>
- /// <para>Typically you don't need to use or override this method.</para>
- /// <para>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.</para>
- 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);
- }
- /// <summary>
- /// Checks if the object is inside its parent.
- /// </summary>
- /// <param name="immediately">if <b>true</b>, check now independent of any conditions.</param>
- /// <remarks>
- /// <para>Typically you don't need to use or override this method.</para>
- /// <para>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 <b>Left</b>, <b>Top</b> and <b>Parent</b>
- /// properties. If <b>immediately</b> parameter is <b>false</b>, you can optimize the method
- /// to search for new parent only if the object's bounds are outside parent. If this parameter is
- /// <b>true</b>, you must skip any optimizations and search for a parent immediately.</para>
- /// </remarks>
- public virtual void CheckParent(bool immediately)
- {
- }
- /// <summary>
- /// Draws the object.
- /// </summary>
- /// <param name="e">Paint event args.</param>
- /// <remarks>
- /// <para>This method is widely used in the FastReport. It is called each time when the object needs to draw
- /// or print itself.</para>
- /// <para>In order to draw the object correctly, you should multiply the object's bounds by the <b>scale</b>
- /// parameter.</para>
- /// <para><b>cache</b> 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).</para>
- /// </remarks>
- 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));
- }
- }
- /// <summary>
- /// Draw the frame around the object to indicate that it accepts the drag&drop operation.
- /// </summary>
- /// <param name="e">Paint event args.</param>
- /// <param name="color">The color of frame.</param>
- 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);
- }
- }
- /// <summary>
- /// Draw the selection points.
- /// </summary>
- /// <param name="e">Paint event args.</param>
- /// <remarks>
- /// This method draws a set of selection points returned by the <see cref="GetSelectionPoints"/> method.
- /// </remarks>
- 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);
- }
- }
- /// <inheritdoc/>
- public override ContextMenuBase GetContextMenu()
- {
- return new ComponentBaseMenu(Report.Designer);
- }
- /// <summary>
- /// Gets the preferred size of an object.
- /// </summary>
- /// <returns>Preferred size.</returns>
- /// <remarks>
- /// This method is called by the FastReport designer when you insert a new object.
- /// </remarks>
- public virtual SizeF GetPreferredSize()
- {
- return new SizeF(Units.Millimeters * 25, Units.Millimeters * 5);
- }
- /// <summary>
- /// Returns a "smart tag" menu.
- /// </summary>
- /// <remarks>
- /// "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).
- /// </remarks>
- public virtual SmartTagBase GetSmartTag()
- {
- return null;
- }
- /// <summary>
- /// Handles double click event in the designer.
- /// </summary>
- /// <remarks>
- /// This method is called when the user doubleclicks the object in the designer. Typical implementation
- /// invokes the object's editor (calls the <b>InvokeEditor</b> method) and sets the designer's
- /// <b>Modified</b> flag.
- /// </remarks>
- public virtual void HandleDoubleClick()
- {
- if (HasFlag(Flags.CanEdit) && !HasRestriction(Restrictions.DontEdit) &&
- this is IHasEditor && (this as IHasEditor).InvokeEditor())
- Report.Designer.SetModified(this, "Change");
- }
- /// <summary>
- /// Handles the DragDrop event in the designer.
- /// </summary>
- /// <param name="e">Current mouse state.</param>
- /// <remarks>
- /// 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 <b>e.DraggedObject</b> object and set the
- /// <b>e.Handled</b> flag to <b>true</b> to complete the drag operation.
- /// </remarks>
- public virtual void HandleDragDrop(FRMouseEventArgs e)
- {
- }
- /// <summary>
- /// Handles the DragOver event in the designer.
- /// </summary>
- /// <param name="e">Current mouse state.</param>
- /// <remarks>
- /// This method is called when the user drags an item from the Data Tree window. This method should
- /// check that the mouse (<b>e.X, e.Y</b>) is inside the object, then set the <b>e.Handled</b> flag
- /// to <b>true</b> if an item can be dragged into this object.
- /// </remarks>
- public virtual void HandleDragOver(FRMouseEventArgs e)
- {
- }
- /// <summary>
- /// Handles KeyDown event in the designer.
- /// </summary>
- /// <param name="sender">The designer's workspace.</param>
- /// <param name="e">Keyboard event parameters.</param>
- /// <remarks>
- /// This method is called when the user presses any key in the designer. Typical implementation
- /// does nothing.
- /// </remarks>
- public virtual void HandleKeyDown(Control sender, KeyEventArgs e)
- {
- }
- /// <summary>
- /// Handles MouseDown event that occurs when the user clicks the mouse in the designer.
- /// </summary>
- /// <remarks>
- /// <para>This method is called when the user press the mouse button in the designer.
- /// The standard implementation does the following:</para>
- /// <list type="bullet">
- /// <item>checks if the mouse pointer is inside the object;</item>
- /// <item>add an object to the selected objects list of the designer;</item>
- /// <item>sets the <b>e.Handled</b> flag to <b>true</b>.</item>
- /// </list>
- /// </remarks>
- /// <param name="e">Current mouse state.</param>
- 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;
- }
- }
- /// <summary>
- /// Handles MouseMove event that occurs when the user moves the mouse in the designer.
- /// </summary>
- /// <remarks>
- /// <para>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 <b>SizeAll</b> when it is over
- /// an object. The standard implementation does the following:</para>
- /// <list type="bullet">
- /// <item>checks if the mouse pointer is inside the object;</item>
- /// <item>changes the cursor shape (<b>e.Cursor</b> property);</item>
- /// <item>sets the <b>e.Handled</b> flag to <b>true</b>.</item>
- /// </list>
- /// </remarks>
- /// <param name="e">Current mouse state.</param>
- public virtual void HandleMouseHover(FRMouseEventArgs e)
- {
- if (PointInObject(new PointF(e.x, e.y)))
- {
- e.handled = true;
- e.cursor = Cursors.SizeAll;
- }
- }
- /// <summary>
- /// Handles MouseMove event that occurs when the user moves the mouse in the designer.
- /// </summary>
- /// <remarks>
- /// <para>This method is called when the user moves the mouse in the designer. The
- /// standard implementation does the following:</para>
- /// <list type="bullet">
- /// <item>
- /// if mouse button is not pressed, check that mouse pointer is inside one of
- /// the selection points returned by the <see cref="GetSelectionPoints"/>
- /// method and set the <b>e.SizingPoint</b> member to the corresponding sizing
- /// point;
- /// </item>
- /// <item>if mouse button is pressed, and <b>e.SizingPoint</b> member is not
- /// <b>SizingPoint.None</b>, resize the object.</item>
- /// </list>
- /// </remarks>
- /// <param name="e">Current mouse state.</param>
- 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);
- }
- }
- /// <summary>
- /// Handles MouseUp event that occurs when the user releases the mouse button in the designer.
- /// </summary>
- /// <remarks>
- /// <para>This method is called when the user releases the mouse button in the
- /// designer. The standard implementation does the following:</para>
- /// <list type="bullet">
- /// <item>if <b>e.Mode</b> is <b>WorkspaceMode2.SelectionRect</b>, checks if object
- /// is inside the selection rectangle and sets <b>e.Handled</b> flag if so;</item>
- /// <item>
- /// checks that object is inside its parent (calls the
- /// <see cref="CheckParent"/> method).
- /// </item>
- /// </list>
- /// </remarks>
- /// <param name="e">Current mouse state.</param>
- 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);
- }
- }
- /// <summary>
- /// Handles mouse wheel event.
- /// </summary>
- /// <param name="e">Current mouse state.</param>
- public virtual void HandleMouseWheel(FRMouseEventArgs e)
- {
- }
- /// <summary>
- /// Checks if given point is inside the object's bounds.
- /// </summary>
- /// <param name="point">point to check.</param>
- /// <returns><b>true</b> if <b>point</b> is inside the object's bounds.</returns>
- /// <remarks>
- /// You can override this method if your objectis not of rectangular form.
- /// </remarks>
- public virtual bool PointInObject(PointF point)
- {
- return AbsBounds.Contains(point);
- }
- #endregion Public Methods
- #region Protected Methods
- /// <summary>
- /// Draws the selection point.
- /// </summary>
- /// <param name="e">Paint event args.</param>
- /// <param name="p"><see cref="Pen"/> object.</param>
- /// <param name="b"><see cref="Brush"/> object.</param>
- /// <param name="x">Left coordinate.</param>
- /// <param name="y">Top coordinate.</param>
- 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);
- }
- /// <summary>
- /// Gets the object's selection points.
- /// </summary>
- /// <returns>Array of <see cref="SelectionPoint"/> objects.</returns>
- /// <remarks>
- /// <para>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 <b>TextObject</b> has eight selection points to change its width and height by the mouse.</para>
- /// <para>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 <b>true</b>,
- /// to disable resizing of the object by the mouse.</para>
- /// </remarks>
- 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) };
- }
- /// <summary>
- /// Gets a value indicating that given point is inside selection point.
- /// </summary>
- /// <param name="x">point's x coordinate.</param>
- /// <param name="y">point's y coordinate.</param>
- /// <param name="point">selection point.</param>
- /// <returns><b>true</b> if <b>(x,y)</b> is inside the <b>point</b></returns>
- 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
- }
- }
|