using FastReport.Controls; using FastReport.Utils; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace FastReport.Table { internal enum MouseMode { None, SelectColumn, SelectRow, SelectCell, ResizeColumn, ResizeRow } partial class TableBase { #region Private Fields private static Cursor curDownArrow = ResourceLoader.GetCursor("DownArrow.cur"); private static Cursor curRightArrow = ResourceLoader.GetCursor("RightArrow.cur"); private TableClipboard clipboard; private MouseMode mouseMode; private Point startSelectionPoint; #endregion Private Fields #region Public Properties /// /// This property is not relevant to this class. /// [Browsable(false)] public new BreakableComponent BreakTo { get { return base.BreakTo; } set { base.BreakTo = value; } } /// /// This property is not relevant to this class. /// [Browsable(false)] public new bool CanGrow { get { return base.CanGrow; } set { base.CanGrow = value; } } /// /// This property is not relevant to this class. /// [Browsable(false)] public new bool CanShrink { get { return base.CanShrink; } set { base.CanShrink = value; } } /// public override float Height { get { return base.Height; } set { if (IsDesigning && !lockColumnRowChange) { foreach (TableRow r in Rows) { r.Height += (value - base.Height) / Rows.Count; } } base.Height = value; } } /// /// This property is not relevant to this class. /// [Browsable(false)] public new Hyperlink Hyperlink { get { return base.Hyperlink; } } /// public override bool IsSelected { get { if (Report == null) return false; return Report.Designer.SelectedObjects.IndexOf(this) != -1 || IsInternalSelected; } } /// public override float Width { get { return base.Width; } set { if (IsDesigning && !lockColumnRowChange) { foreach (TableColumn c in Columns) { c.Width += (value - base.Width) / Columns.Count; } } base.Width = value; } } #endregion Public Properties #region Internal Properties internal TableClipboard Clipboard { get { if (clipboard == null) clipboard = new TableClipboard(this); return clipboard; } } internal MouseMode MouseMode { get { return mouseMode; } set { mouseMode = value; } } #endregion Internal Properties #region Private Properties private bool IsInternalSelected { get { if (Report == null) return false; SelectedObjectCollection selection = Report.Designer.SelectedObjects; return selection.Count > 0 && ( (selection[0] is TableRow && (selection[0] as TableRow).Parent == this) || (selection[0] is TableColumn && (selection[0] as TableColumn).Parent == this) || (selection[0] is TableCell && (selection[0] as TableCell).Parent != null && (selection[0] as TableCell).Parent.Parent == this)); } } #endregion Private Properties #region Public Methods /// public override ContextMenuBase GetContextMenu() { return new TableObjectMenu(Report.Designer); } /// public override void HandleKeyDown(Control sender, KeyEventArgs e) { SelectedObjectCollection selection = Report.Designer.SelectedObjects; if (!IsSelected || !(selection[0] is TableCell)) return; TableCell topCell = selection[0] as TableCell; int left = topCell.Address.X; int top = topCell.Address.Y; bool selectionChanged = false; switch (e.KeyCode) { case Keys.Enter: topCell.HandleKeyDown(sender, e); break; case Keys.Delete: foreach (Base c in selection) { if (c is TableCell) (c as TableCell).Text = ""; } Report.Designer.SetModified(null, "Change", Name); break; case Keys.Up: top--; selectionChanged = true; break; case Keys.Down: top += topCell.RowSpan; selectionChanged = true; break; case Keys.Left: left--; selectionChanged = true; break; case Keys.Right: left += topCell.ColSpan; selectionChanged = true; break; } if (selectionChanged) { if (left < 0) left = 0; if (left >= Columns.Count) left = Columns.Count - 1; if (top < 0) top = 0; if (top >= Rows.Count) top = Rows.Count - 1; mouseMode = MouseMode.SelectCell; SetSelection(left, top, left, top); Report.Designer.SelectionChanged(null); } e.Handled = true; } /// public override void HandleMouseDown(FRMouseEventArgs e) { if (mouseMode == MouseMode.None) { HandleMouseHover(e); if (e.handled) { e.mode = WorkspaceMode2.Move; if (IsSelected) { SelectedObjectCollection selection = Report.Designer.SelectedObjects; if (!selection.Contains(this)) { selection.Clear(); selection.Add(this); } } } else { if (PointInObject(new PointF(e.x, e.y))) { e.handled = true; e.mode = WorkspaceMode2.Custom; e.activeObject = this; mouseMode = MouseMode.SelectCell; SelectedObjectCollection selection = Report.Designer.SelectedObjects; if (e.button == MouseButtons.Left) { startSelectionPoint = GetAddressAtMousePoint(new PointF(e.x, e.y), true); TableCell cell = this[startSelectionPoint.X, startSelectionPoint.Y]; if (e.modifierKeys == Keys.Shift) { // toggle selection if (selection.Contains(cell)) { if (selection.Count > 1) selection.Remove(cell); } else selection.Add(cell); } else { selection.Clear(); selection.Add(cell); } } else if (e.button == MouseButtons.Right) { Point selectionPoint = GetAddressAtMousePoint(new PointF(e.x, e.y), true); Rectangle selectionRect = GetSelectionRect(); if (!selectionRect.Contains(selectionPoint)) { selection.Clear(); selection.Add(this[selectionPoint.X, selectionPoint.Y]); } } } } } else if (e.button == MouseButtons.Left) { startSelectionPoint = GetAddressAtMousePoint(new PointF(e.x, e.y), false); if (mouseMode == MouseMode.SelectColumn) SetSelection(startSelectionPoint.X, 0, startSelectionPoint.X, Rows.Count - 1); else if (mouseMode == MouseMode.SelectRow) SetSelection(0, startSelectionPoint.Y, Columns.Count - 1, startSelectionPoint.Y); } } /// public override void HandleMouseHover(FRMouseEventArgs e) { if (IsSelected) { float m = 1 / Report.Designer.Zoom; if (new RectangleF(AbsLeft + 8 * m, AbsTop - 8 * m, 16 * m, 16 * m).Contains(new PointF(e.x, e.y))) { e.handled = true; e.cursor = Cursors.SizeAll; } } } /// public override void HandleMouseMove(FRMouseEventArgs e) { base.HandleMouseMove(e); if (!e.handled && e.button == MouseButtons.None) { PointF point = new PointF(e.x, e.y); mouseMode = MouseMode.None; // don't process if mouse is over move area HandleMouseHover(e); if (e.handled) { e.handled = false; return; } // check column resize or select if (IsSelected) { float left = AbsLeft; for (int x = 0; x < Columns.Count; x++) { float width = Columns[x].Width; left += width; if (point.Y > AbsTop && point.Y < AbsBottom && point.X > left - 3 && point.X < left + 3) { // column resize PointF pt = new PointF(e.x + 3, e.y); Point pt1 = GetAddressAtMousePoint(pt, false); Point pt2 = GetAddressAtMousePoint(pt, true); // check if we are in span if (pt1 == pt2) { mouseMode = MouseMode.ResizeColumn; e.cursor = Cursors.VSplit; e.data = Columns[x]; break; } } else if (point.Y > AbsTop - 8 && point.Y < AbsTop + 2 && point.X > left - width && point.X < left) { // column select mouseMode = MouseMode.SelectColumn; e.cursor = curDownArrow; e.data = Columns[x]; break; } } // check row resize or select if (mouseMode == MouseMode.None) { float top = AbsTop; for (int y = 0; y < Rows.Count; y++) { float height = Rows[y].Height; top += height; if (point.X > AbsLeft && point.X < AbsRight && point.Y > top - 3 && point.Y < top + 3) { // row resize PointF pt = new PointF(e.x, e.y + 3); Point pt1 = GetAddressAtMousePoint(pt, false); Point pt2 = GetAddressAtMousePoint(pt, true); // check if we are in span if (pt1 == pt2) { mouseMode = MouseMode.ResizeRow; e.cursor = Cursors.HSplit; e.data = Rows[y]; break; } } else if (point.X > AbsLeft - 8 && point.X < AbsLeft + 2 && point.Y > top - height && point.Y < top) { // row select mouseMode = MouseMode.SelectRow; e.cursor = curRightArrow; e.data = Rows[y]; break; } } } } if (mouseMode != MouseMode.None) { e.handled = true; e.mode = WorkspaceMode2.Custom; } } else if (e.mode == WorkspaceMode2.Custom && e.button == MouseButtons.Left) { switch (mouseMode) { case MouseMode.SelectColumn: SetSelection(startSelectionPoint.X, 0, GetAddressAtMousePoint(new PointF(e.x, e.y), false).X, Rows.Count - 1); break; case MouseMode.SelectRow: SetSelection(0, startSelectionPoint.Y, Columns.Count - 1, GetAddressAtMousePoint(new PointF(e.x, e.y), false).Y); break; case MouseMode.SelectCell: Point pt = GetAddressAtMousePoint(new PointF(e.x, e.y), false); SetSelection(startSelectionPoint.X, startSelectionPoint.Y, pt.X, pt.Y); break; case MouseMode.ResizeColumn: TableColumn col = e.data as TableColumn; col.Width += e.delta.X; if ((e.modifierKeys & Keys.Control) != 0 && col.Index < ColumnCount - 1) { TableColumn nextCol = Columns[col.Index + 1]; nextCol.Width -= e.delta.X; } if (col.Width <= 0) col.Width = 1; break; case MouseMode.ResizeRow: TableRow row = e.data as TableRow; row.Height += e.delta.Y; if ((e.modifierKeys & Keys.Control) != 0 && row.Index < RowCount - 1) { TableRow nextRow = Rows[row.Index + 1]; nextRow.Height -= e.delta.Y; } if (row.Height <= 0) row.Height = 1; break; } } } /// public override void HandleMouseUp(FRMouseEventArgs e) { base.HandleMouseUp(e); if (mouseMode == MouseMode.ResizeRow) { // update band's height if (Parent is BandBase) (Parent as BandBase).FixHeight(); Report.Designer.SetModified(null, "Size", (e.data as TableRow).Name); } if (mouseMode == MouseMode.ResizeColumn) Report.Designer.SetModified(null, "Size", (e.data as TableColumn).Name); mouseMode = MouseMode.None; } /// public override void OnAfterInsert(InsertFrom source) { CreateUniqueNames(); } /// public override void OnBeforeInsert(int flags) { Width = TableColumn.DefaultWidth * 5; Height = TableRow.DefaultHeight * 5; RowCount = 5; ColumnCount = 5; } #endregion Public Methods #region Internal Methods internal virtual ContextMenuBase GetCellContextMenu(TableCell cell) { return null; } internal virtual SmartTagBase GetCellSmartTag(TableCell cell) { return null; } internal virtual ContextMenuBase GetColumnContextMenu(TableColumn column) { return null; } internal virtual ContextMenuBase GetRowContextMenu(TableRow row) { return null; } internal Rectangle GetSelectionRect() { SelectedObjectCollection selection = Report.Designer.SelectedObjects; int minX = 1000; int minY = 1000; int maxX = 0; int maxY = 0; foreach (Base c in selection) { if (c is TableCell) { TableCell cell = c as TableCell; Point a = cell.Address; if (a.X < minX) minX = a.X; if (a.X > maxX) maxX = a.X; if (a.Y < minY) minY = a.Y; if (a.Y > maxY) maxY = a.Y; } } return new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1); } internal virtual void HandleCellDoubleClick(TableCell cell) { // do nothing } internal virtual string GetCellDisplayText(TableCell cell, string defaultText) { return defaultText; } internal virtual void ProvideCustomItems(TableCell cell, DataTreeView tree) { // does nothing } #endregion Internal Methods #region Private Methods private void DrawDesign(FRPaintEventArgs e) { if (IsDesigning && IsSelected) { float m = Report.Designer.DpiMultiplier(); e.Graphics.DrawImage(Report.Designer.GetImage(75), (int)(AbsLeft * e.ScaleX + 8 * m), (int)(AbsTop * e.ScaleY - 8 * m)); } } private void DrawDesign_Borders(FRPaintEventArgs e) { if (IsDesigning) DrawCells(e, DrawDesignBorders); } private void DrawDesign_BordersRtl(FRPaintEventArgs e) { if (IsDesigning) DrawCellsRtl(e, DrawDesignBorders); } private void DrawDesign_SelectedCells(FRPaintEventArgs e) { if (IsDesigning && IsSelected) { DrawCells(e, DrawSelectedCells); DrawSelectedRowsColumns(e); } } private void DrawDesign_SelectedCellsRtl(FRPaintEventArgs e) { if (IsDesigning && IsSelected) { DrawCellsRtl(e, DrawSelectedCells); DrawSelectedRowsColumns(e); } } private void DrawDesignBorders(FRPaintEventArgs e, TableCell cell) { if (cell.Fill is SolidFill && (cell.Fill.IsTransparent || (cell.Fill as SolidFill).Color == Color.White)) { cell.DrawMarkers(e, MarkerStyle.Rectangle); } } private void DrawSelectedCells(FRPaintEventArgs e, TableCell cell) { if (Report.Designer.SelectedObjects.Contains(cell)) { Brush brush = e.Cache.GetBrush(Color.FromArgb(128, SystemColors.Highlight)); e.Graphics.FillRectangle(brush, cell.AbsLeft * e.ScaleX, cell.AbsTop * e.ScaleY, cell.Width * e.ScaleX, cell.Height * e.ScaleY); } } private void DrawSelectedRowsColumns(FRPaintEventArgs e) { IGraphics g = e.Graphics; SelectedObjectCollection selection = Report.Designer.SelectedObjects; Brush brush = e.Cache.GetBrush(Color.FromArgb(128, SystemColors.Highlight)); foreach (Base c in selection) { if (c is TableRow) { TableRow row = c as TableRow; g.FillRectangle(brush, AbsLeft * e.ScaleX, (row.Top + AbsTop) * e.ScaleY, Width * e.ScaleX, row.Height * e.ScaleY); } else if (c is TableColumn) { TableColumn col = c as TableColumn; g.FillRectangle(brush, (col.Left + AbsLeft) * e.ScaleX, AbsTop * e.ScaleY, col.Width * e.ScaleX, Height * e.ScaleY); } } } private Point GetAddressAtMousePoint(PointF pt, bool checkSpan) { Point result = new Point(); float left = AbsLeft; for (int x = 0; x < Columns.Count; x++) { float width = Columns[x].Width; if (pt.X >= left && pt.X < left + width) { result.X = x; break; } left += width; } if (pt.X >= AbsRight) result.X = Columns.Count - 1; float top = AbsTop; for (int y = 0; y < Rows.Count; y++) { float height = Rows[y].Height; if (pt.Y >= top && pt.Y < top + height) { result.Y = y; break; } top += height; } if (pt.Y >= AbsBottom) result.Y = Rows.Count - 1; if (checkSpan) { List spans = GetSpanList(); foreach (Rectangle span in spans) { if (span.Contains(result)) { result = span.Location; break; } } } return result; } private void SetSelection(int x1, int y1, int x2, int y2) { Rectangle rect = new Rectangle(); rect.X = x1 < x2 ? x1 : x2; rect.Y = y1 < y2 ? y1 : y2; rect.Width = (int)Math.Abs(x1 - x2) + 1; rect.Height = (int)Math.Abs(y1 - y2) + 1; SelectedObjectCollection selection = Report.Designer.SelectedObjects; selection.Clear(); if (mouseMode == MouseMode.SelectRow) { for (int y = rect.Top; y < rect.Bottom; y++) { selection.Add(Rows[y]); } } else if (mouseMode == MouseMode.SelectColumn) { for (int x = rect.Left; x < rect.Right; x++) { selection.Add(Columns[x]); } } else if (mouseMode == MouseMode.SelectCell) { List spans = GetSpanList(); // widen selection if spans are inside foreach (Rectangle span in spans) { if (rect.IntersectsWith(span)) { if (span.X < rect.X) { rect.Width += rect.X - span.X; rect.X = span.X; } if (span.Right > rect.Right) rect.Width += span.Right - rect.Right; if (span.Y < rect.Y) { rect.Height += rect.Y - span.Y; rect.Y = span.Y; } if (span.Bottom > rect.Bottom) rect.Height += span.Bottom - rect.Bottom; } } for (int x = rect.Left; x < rect.Right; x++) { for (int y = rect.Top; y < rect.Bottom; y++) { selection.Add(this[x, y]); } } } } #endregion Private Methods } }