/* i-net software provides programming examples for illustration only, without warranty either expressed or implied, including, but not limited to, the implied warranties of merchantability and/or fitness for a particular purpose. This programming example assumes that you are familiar with the programming language being demonstrated and the tools used to create and debug procedures. i-net software support professionals can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific needs. © i-net software 1998-2013 */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using Inet.Viewer.Data; using Inet.Viewer.Resources; namespace Inet.Viewer.WinForms { /// /// This class implements all the drawing of the image of the report. including the scrolling and zooming /// [ToolboxItem(false)] internal partial class ReportContentView : ScrollableControl, IPageReceiver { private const int PageFlipOverScroll = 360; private const int GroupSelectionDuration = 2000; private ReportDataCache rptDataCache; private ReportView reportView; private int currentPage; // Pan private bool autoPan; private bool isPanning; private bool invertMouse; private Point startScrollPosition; private Point startMousePosition; // Zoom private int minZoom = 10; private int maxZoom = 600; private const int SpaceBetweenPages = 8; private const int InnerPadding = 12; /// /// For the Property AutomaticZoomType /// private ZoomMode zoomMode; /// /// For the Property ZoomFactor /// private float zoomFactor; private bool isScrolling; private Rectangle selectionRectangle = new Rectangle(new Point(0, 0), new Size(0, 0)); private Point selectionStartPoint; private ToolStripMenuItem contextmenuCopy; private Viewer.PageViewMode pageViewMode = PageViewMode.SinglePage; private PageContent[] pageContent; private Data.PageInfo lastPageInfo; private bool initialPageLoading = true; private Timer repaintTimer = new Timer(); private int overScrollDelta; private Font stateFont = new Font(FontFamily.GenericSansSerif, 12f, FontStyle.Regular); private ToolTip toolTip = new ToolTip(); private Timer toolTipTimer = new Timer(); private PageClip lastToolTipClip; private Point lastMouseLocation; private bool isSelecting; private GroupTreeNode selectedGroup; /// /// When the Panning is triggered /// public event EventHandler PanEnd; /// /// When the panning is finished /// public event EventHandler PanStart; /// /// When Property InvertMouse Changed /// public event EventHandler InvertMouseChanged; /// /// When Property AutoPan Changed /// public event EventHandler AutoPanChanged; /// /// when the zoom level was changed /// [Category("Property Changed")] public event EventHandler ZoomChanged; /// /// When the was changed for this view /// [Category("Property Changed")] public event EventHandler ZoomModeChanged; /// /// Invoked after the current page was changed. /// public event PageChanged PageChanged; private long selectedGroupStartTicks; /// /// Default Constructor setting default values /// public ReportContentView() { this.BackColor = Color.FromArgb(230, 230, 230); this.Name = "ReportContentView"; this.ResumeLayout(false); CreateContextMenu(); this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true); currentPage = 1; this.AutoScroll = true; this.AutoPan = true; isScrolling = false; this.ZoomMin = 10; this.ZoomMax = 400; this.ZoomMode = Viewer.ZoomMode.Manual; this.ZoomFactor = 1.0f; // 100% repaintTimer.Tick += new EventHandler((e, a) => { Invalidate(); }); toolTipTimer.Tick += toolTipTimer_Tick; toolTipTimer.Interval = 500; Dock = System.Windows.Forms.DockStyle.Fill; } /// protected override void Dispose(bool disposing) { if (pageContent != null) { foreach (PageContent page in pageContent) { page.ClearRendering(); } } base.Dispose(disposing); } /// /// Creates the context menu. /// private void CreateContextMenu() { ContextMenuStrip cm = new ContextMenuStrip(); contextmenuCopy = new ToolStripMenuItem(); contextmenuCopy.Text = strings.Copy; contextmenuCopy.ShortcutKeys = Keys.Control | Keys.C; contextmenuCopy.Click += new EventHandler(ContextmenuCopy); cm.Items.Add(contextmenuCopy); cm.Opening += ContextmenuOpening; this.ContextMenuStrip = cm; } /// /// Returns a Page Clip if fo the x and y coordinates a page clip exists. Includes the AutoCenterPoint and AutoScrollPosition /// for calculating. /// /// the x coordinate /// the y coordinate /// private PageClip GetPageClip(int x, int y) { return GetPageClip(x, y, LinkType.Hyperlink, true); } /// /// Returns a Page Clip if fo the x and y coordinates a page clip exists. Includes the AutoCenterPoint and AutoScrollPosition /// for calculating. /// /// the x coordinate /// the y coordinate /// the wanted link type /// if set to true, link types will be ignored /// private PageClip GetPageClip(int x, int y, LinkType type, bool ignoreType) { int last = LastVisiblePage; for (int p = FirstVisiblePage; p <= last; p++) { PageContent page = GetContent(p); if (page == null) { continue; } IList links = page.Links; if (links == null) { continue; } Point center = CenterPoint(); Point pagepos = CalcPagePosition(p); int px = (int)((x - InnerPadding - center.X - AutoScrollPosition.X - pagepos.X) * Graphics2DPainter.TwipsToPixel / zoomFactor); int py = (int)((y - InnerPadding - center.Y - AutoScrollPosition.Y - pagepos.Y) * Graphics2DPainter.TwipsToPixel / zoomFactor); foreach (PageClip link in links) { if (ignoreType || link.LinkType == type) { if (link.Contains(px, py)) { return link; } } } } // No PageClip was found return null; } /// /// /// [DefaultValue(false), Category("Behavior")] public override bool AutoSize { get { return base.AutoSize; } set { if (base.AutoSize != value) { base.AutoSize = value; UpdatePageContent(false); } } } /// /// Sets the representing IReportView for this PageView /// public IReportView ReportView { get { return reportView; } set { if (reportView != null) { reportView.ZoomChanged -= new EventHandler(ReportViewZoomChanged); } reportView = value as ReportView; if (reportView != null) { reportView.ZoomChanged += new EventHandler(ReportViewZoomChanged); } } } /// /// This is a mandatory Property, as this panel needs the ReportData to be able /// to render this view /// internal ReportDataCache ReportDataCache { get { return rptDataCache; } set { rptDataCache = value; } } /// /// Defines the Page View Mode /// public PageViewMode PageViewMode { get { return pageViewMode; } set { int page = currentPage; Point relPagePos = CalcRelativeScrollPosition(page); pageViewMode = value; AdjustViewPort(); ScrollTo(page, relPagePos); } } /// /// To redraw the image with the new size and adjust the Layout /// /// /// private void ReportViewZoomChanged(object sender, EventArgs e) { if (this.reportView != null) { // silent setting of the zoom, so the ZoomChanged ist not triggered again this.UpdatePageContent(false); } } /// /// To redraw the image with the new size and update the Layout /// /// /// private void ReportViewZoomModeChanged(object sender, EventArgs e) { if (this.reportView != null) { // to calculate the new size of the image this.UpdatePageContent(false); } } /// /// /// protected override void OnDockChanged(EventArgs e) { base.OnDockChanged(e); if (this.Dock != DockStyle.None) this.AutoSize = false; } /// /// /// public override Size GetPreferredSize(Size proposedSize) { if (this.pageContent == null) { return base.GetPreferredSize(proposedSize); } return CalcContentSize(); } /// /// Painting method that paints the border rectangle and draws the image /// according to the current zooming and paning /// /// protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); var g = e.Graphics; bool repaintRequired = false; if (pageContent == null || lastPageInfo == null) { if (LoadFailed) { return; } PageContent.PaintLoadingSpinner(g, new Point(ClientSize.Width / 2, ClientSize.Height / 2)); repaintRequired = true; LoadProgress loadProgress = LoadProgress; if (loadProgress != null && loadProgress.ReportState != null) { ReportState state = loadProgress.ReportState; string s = state.Text; int w = (int)g.MeasureString(s, stateFont).Width; g.DrawString(s, stateFont, Brushes.White, new Point((ClientSize.Width - w) / 2, ClientSize.Height / 2 + 80)); if (state.Progress != 0) { s = state.Progress + "%"; SizeF strSize = g.MeasureString(s, stateFont); g.DrawString(s, stateFont, Brushes.White, new Point((ClientSize.Width - (int)strSize.Width) / 2, (ClientSize.Height - (int)strSize.Height) / 2)); } } } else { int newPage = CalcCenterPage(); if (newPage != currentPage) { currentPage = newPage; if (PageChanged != null) { PageChanged(this, newPage); } } GroupTreeNode selectedGroup = this.selectedGroup; Pen selectedGroupPen = null; if (selectedGroup != null) { int elapsed = (int)(DateTime.Now.Ticks - selectedGroupStartTicks) / 10000; if (elapsed < GroupSelectionDuration) { int alpha = 230 - Math.Max(0, elapsed - 1000) / 5; selectedGroupPen = new Pen(Color.FromArgb(alpha, 255, 244, 0), 100f); selectedGroupPen.LineJoin = LineJoin.Round; repaintRequired = true; } else { selectedGroup = null; } } Point autoCenterOffset = CenterPoint(); float zoom = CalcZoomFactor(); Point autoScrollPosition = AutoScrollPosition; Size imageSize = ImageSize; Matrix tx = g.Transform; g.TranslateTransform(autoCenterOffset.X + autoScrollPosition.X, autoCenterOffset.Y + autoScrollPosition.Y); int last = LastVisiblePage; for (int p = FirstVisiblePage; p <= last; p++) { PageContent page = GetContent(p); if (page == null) { continue; } Matrix origTransform2 = g.Transform; Point relPos = CalcPagePosition(p); g.TranslateTransform(relPos.X + InnerPadding, relPos.Y + InnerPadding); if (page.Paint(g, imageSize.Width, imageSize.Height, !LoadFailed)) { repaintRequired = true; page.Update(false, zoom); } else if (selectedGroup != null) { g.ScaleTransform(zoom / 15f, zoom / 15f); if (selectedGroup.PageFrom == p) { if (selectedGroup.PageFrom == selectedGroup.PageTo) { g.DrawRectangle(selectedGroupPen, 0, selectedGroup.YFrom, PageInfo.Width, selectedGroup.YTo - selectedGroup.YFrom); } else { g.DrawRectangle(selectedGroupPen, 0, selectedGroup.YFrom, PageInfo.Width, PageInfo.Height - selectedGroup.YFrom); } } else if (selectedGroup.PageTo == p) { g.DrawRectangle(selectedGroupPen, 0, 0, PageInfo.Width, selectedGroup.YTo); } else if (selectedGroup.PageFrom < p && p < selectedGroup.PageTo) { g.DrawRectangle(selectedGroupPen, 0, 0, PageInfo.Width, PageInfo.Height); } } g.Transform = origTransform2; } Rectangle selrect = selectionRectangle; if (selrect.Width != 0) { // draw the current mouse selection rectangle Pen pen = new Pen(Color.Black); pen.DashStyle = DashStyle.Dash; g.DrawRectangle(pen, selrect.X, selrect.Y, selrect.Width, selrect.Height); } g.Transform = tx; } repaintTimer.Stop(); if (repaintRequired) { repaintTimer.Interval = 100; repaintTimer.Start(); } } /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public new Size AutoScrollMinSize { get { return base.AutoScrollMinSize; } set { base.AutoScrollMinSize = value; } } /// /// Trigger OnResize event /// /// protected override void OnResize(EventArgs e) { UpdatePageContent(false); base.OnResize(e); } /// /// /// /// protected override void OnScroll(ScrollEventArgs se) { UpdatePageContent(false); base.OnScroll(se); } /// /// Computes the page number from the current scroll position. /// /// the page number private int CalcCenterPage() { switch (pageViewMode) { case Viewer.PageViewMode.SingleContinuous: return Math.Max(1, Math.Min(PageCount, (-AutoScrollPosition.Y - InnerPadding + ClientSize.Height / 2) / (ImageSize.Height + SpaceBetweenPages) + 1)); case Viewer.PageViewMode.DoubleContinuous: int newPage = (-AutoScrollPosition.Y - InnerPadding + ClientSize.Height / 2) / (ImageSize.Height + SpaceBetweenPages) * 2 + 1; if (HScroll && -AutoScrollPosition.X - InnerPadding + ClientSize.Width / 2 > ImageSize.Width + InnerPadding) { ++newPage; } return Math.Max(1, Math.Min(PageCount, newPage)); default: return currentPage; } } /// /// This Property defines if the Pan function is activated or not /// [DefaultValue(true), Category("Behavior")] public bool AutoPan { get { return autoPan; } set { if (autoPan != value) { autoPan = value; this.OnAutoPanChanged(EventArgs.Empty); } } } /// /// /// /// protected virtual void OnAutoPanChanged(EventArgs e) { if (this.AutoPanChanged != null) { this.AutoPanChanged(this, e); } } /// /// Defines how the mouse move is handled during the panning. /// FALSE means the page moves with the mouse move /// TRUE means the page moves opposite to the mouse move (goes with the scrollbars). /// [DefaultValue(false), Category("Behavior")] public bool InvertMouse { get { return invertMouse; } set { if (invertMouse != value) { invertMouse = value; this.OnInvertMouseChanged(EventArgs.Empty); } } } /// /// /// /// protected virtual void OnInvertMouseChanged(EventArgs e) { if (this.InvertMouseChanged != null) this.InvertMouseChanged(this, e); } /// /// Called from the tooltip timer to show the tooltip delayed. /// /// /// void toolTipTimer_Tick(object sender, EventArgs e) { toolTip.Hide(this); if (lastToolTipClip != null) { toolTip.Show(lastToolTipClip.ToolTip, this, new Point(lastMouseLocation.X + 16, lastMouseLocation.Y + 16)); } toolTipTimer.Enabled = false; } /// protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); toolTipTimer.Enabled = false; toolTip.Hide(this); } /// /// Override to implement the panning with the mouse /// /// the event arguments protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (e.Button == MouseButtons.None) { PageClip clip = GetPageClip(e.X, e.Y, LinkType.ToolTip, false); if (clip == null) { clip = GetPageClip(e.X, e.Y); } if (clip != null) { this.Cursor = Cursors.Hand; lastMouseLocation = e.Location; if (lastToolTipClip != clip) { lastToolTipClip = clip; toolTipTimer.Enabled = true; } } else { this.Cursor = Cursors.Arrow; lastMouseLocation = e.Location; toolTip.Hide(this); lastToolTipClip = null; toolTipTimer.Enabled = false; } } else if (e.Button == MouseButtons.Left) { if (ReportView.MouseActionMode == MouseMode.Panning && this.AutoPan) { if (!this.IsPanning) { // Panning starts startMousePosition = e.Location; this.IsPanning = true; } else { // Panning continues int x; int y; Point position; if (!this.InvertMouse) { x = -startScrollPosition.X + (startMousePosition.X - e.Location.X); y = -startScrollPosition.Y + (startMousePosition.Y - e.Location.Y); } else { x = -(startScrollPosition.X + (startMousePosition.X - e.Location.X)); y = -(startScrollPosition.Y + (startMousePosition.Y - e.Location.Y)); } position = new Point(x, y); this.UpdateScrollPosition(position); } } else if (ReportView.MouseActionMode == MouseMode.SelectText) { isSelecting = true; Point selPoint = new Point(e.X, e.Y); Point autoCenterOffset = CenterPoint(); selPoint.X -= autoCenterOffset.X + AutoScrollPosition.X; selPoint.Y -= autoCenterOffset.Y + AutoScrollPosition.Y; selectionRectangle = Normalize(new Rectangle(selectionStartPoint.X, selectionStartPoint.Y, selPoint.X - selectionStartPoint.X, selPoint.Y - selectionStartPoint.Y)); SelectArea(selectionRectangle); Invalidate(); } } } /// /// To set the focus on the ReportPageView /// protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); this.Focus(); selectionStartPoint = new Point(e.X, e.Y); Point autoCenterOffset = CenterPoint(); selectionStartPoint.X -= autoCenterOffset.X + AutoScrollPosition.X; selectionStartPoint.Y -= autoCenterOffset.Y + AutoScrollPosition.Y; } /// /// For panning /// /// protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (this.IsPanning) this.IsPanning = false; if (ReportView.MouseActionMode == MouseMode.SelectText) { selectionRectangle = new Rectangle(new Point(0, 0), new Size(0, 0)); Refresh(); isSelecting = false; } } /// /// Called when the context menu is shown. Enables/disables its entries in respect /// to the current state. /// /// /// private void ContextmenuOpening(object sender, CancelEventArgs e) { if (reportView.ReportInfo != null && reportView.ReportInfo.IsClipboardEnabled) { string text = SelectedText; contextmenuCopy.Enabled = text.Length != 0; } else { contextmenuCopy.Enabled = false; } } /// /// Copies the currently selected text to the clipboard. /// /// the sender /// event args private void ContextmenuCopy(object sender, EventArgs e) { if (reportView.ReportInfo != null && reportView.ReportInfo.IsClipboardEnabled) { string text = SelectedText; if (text.Length != 0) { Clipboard.SetText(text); } } } /// /// The currently selected text or an empty string if no selection is done. /// public string SelectedText { get { StringBuilder sb = new StringBuilder(); foreach (PageContent page in pageContent) { page.AppendSelectedText(sb); } return Regex.Replace(sb.ToString(), @"[ ]+", " ").Trim(); } } /// /// For panning /// /// protected virtual void UpdateScrollPosition(Point position) { this.AutoScrollPosition = position; this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, 0)); this.Update(); toolTip.Hide(this); } /// /// To check if this control is currently in Panning Mode /// [DefaultValue(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)] public bool IsPanning { get { return isPanning; } protected set { if (isPanning != value) { isPanning = value; startScrollPosition = this.AutoScrollPosition; if (value) { this.Cursor = Cursors.SizeAll; this.OnPanStart(EventArgs.Empty); } else { this.Cursor = Cursors.Default; this.OnPanEnd(EventArgs.Empty); } } } } /// /// For Panning /// /// /// protected override bool IsInputKey(Keys keyData) { bool result; if ((keyData & Keys.Right) == Keys.Right | (keyData & Keys.Left) == Keys.Left | (keyData & Keys.Up) == Keys.Up | (keyData & Keys.Down) == Keys.Down) result = true; else result = base.IsInputKey(keyData); return result; } /// /// Panning /// /// protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); switch (e.KeyCode) { case Keys.Left: this.AdjustScroll(-(e.Modifiers == Keys.None ? this.HorizontalScroll.SmallChange : this.HorizontalScroll.LargeChange), 0); break; case Keys.Right: this.AdjustScroll(e.Modifiers == Keys.None ? this.HorizontalScroll.SmallChange : this.HorizontalScroll.LargeChange, 0); break; case Keys.Up: this.AdjustScroll(0, -(e.Modifiers == Keys.None ? this.VerticalScroll.SmallChange : this.VerticalScroll.LargeChange)); break; case Keys.Down: this.AdjustScroll(0, e.Modifiers == Keys.None ? this.VerticalScroll.SmallChange : this.VerticalScroll.LargeChange); break; case Keys.PageDown: this.ReportView.NextPage(); break; case Keys.PageUp: this.ReportView.PreviousPage(); break; case Keys.Home: this.ReportView.FirstPage(); break; case Keys.End: this.ReportView.LastPage(); break; case Keys.ControlKey: isScrolling = true; break; default: // ignore other keys break; } } /// /// For scrolling /// /// protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); switch (e.KeyCode) { case Keys.ControlKey: isScrolling = false; break; default: // ignore other keys break; } } /// /// /// /// /// protected virtual void AdjustScroll(int x, int y) { Point scrollPosition; scrollPosition = new Point(this.HorizontalScroll.Value + x, this.VerticalScroll.Value + y); this.UpdateScrollPosition(scrollPosition); } /// /// For panning /// /// protected virtual void OnPanEnd(EventArgs e) { if (this.PanEnd != null) { this.PanEnd(this, e); } } /// /// For panning /// /// protected virtual void OnPanStart(EventArgs e) { if (this.PanStart != null) { this.PanStart(this, e); } } /// /// Calculate new AutoScrollMinSize /// protected virtual void AdjustViewPort() { Size autoScrollSize = GetPreferredSize(new Size()); if (!autoScrollSize.Equals(AutoScrollMinSize)) { // invalidate before setting the new size to avoid additional refresh Invalidate(); this.AutoScrollMinSize = autoScrollSize; } } /// /// Defines the minium zoom in % /// public int MinZoom { get { return minZoom; } set { minZoom = value; } } /// /// Defines the maximum zoom in % /// public int MaxZoom { get { return maxZoom; } set { maxZoom = value; } } /// /// To trigger the zoom changed event /// protected virtual void OnZoomChanged() { AdjustViewPort(); UpdatePageContent(false); if (this.ZoomChanged != null) this.ZoomChanged(this, new EventArgs()); } /// /// /// /// protected virtual void OnZoomModeChanged(EventArgs e) { UpdatePageContent(false); if (this.ZoomModeChanged != null) this.ZoomModeChanged(this, e); } /// /// The zooming with CTLR + MouseWheel /// /// protected override void OnMouseWheel(MouseEventArgs e) { if (isScrolling) { if (e.Delta > 0) { this.ZoomFactor = ViewerToolbar.NextHigherZoomLevel(ZoomFactor); } else if (e.Delta < 0) { this.ZoomFactor = ViewerToolbar.NextLowerZoomLevel(ZoomFactor); } } else { Point prevAutoScrollPosition = AutoScrollPosition; // only invoking if is not scrolling base.OnMouseWheel(e); if ((pageViewMode == Viewer.PageViewMode.SinglePage || pageViewMode == Viewer.PageViewMode.DoublePage) && prevAutoScrollPosition == AutoScrollPosition) { overScrollDelta += e.Delta; if (overScrollDelta >= PageFlipOverScroll && currentPage > 1) { ScrollTo(currentPage - (IsDoublePageMode() ? 2 : 1), new Point(CalcRelativeScrollPosition(currentPage).X, 100000)); } else if (overScrollDelta <= -PageFlipOverScroll && currentPage < PageCount) { ScrollTo(currentPage + (IsDoublePageMode() ? 2 : 1), new Point(CalcRelativeScrollPosition(currentPage).X, 0)); } } else { overScrollDelta = 0; } this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, 0)); } } /// protected override void OnMouseClick(MouseEventArgs e) { if (IsPanning || isSelecting) { return; } // For Subreport on Demand 2 PageClips are transmitted PageClip hyperLinkClip = GetPageClip(e.X, e.Y, LinkType.Hyperlink, false); PageClip interactiveSortingClip = GetPageClip(e.X, e.Y, LinkType.InteractiveSorting, false); PageClip subReportClip = GetPageClip(e.X, e.Y, LinkType.SubreportOnDemand, false); // is SubreportOnDemand if (subReportClip != null && subReportClip.LinkType == LinkType.SubreportOnDemand) { try { URLRenderData data = (URLRenderData)reportView.ReportData.Clone(); data[URLRenderData.ParameterSubReport] = null; data[URLRenderData.ParameterSubreportOnDemand] = subReportClip.SubReportURL; ReportView newReportView = (ReportView)reportView.ReportViewer.AddNewReportView(data); newReportView.TabLabel = subReportClip.ToolTip; } catch (Exception ex) { ViewerException ve = new ViewerException("Could not create sub-report", ex); this.ShowError(ve); } return; } // Hyperlink or Interactive sorting if (hyperLinkClip != null) { try { Process.Start(hyperLinkClip.Url.ToString()); } catch (Exception ex) { ViewerException ve = new ViewerException("A problem with the hyperlink: " + hyperLinkClip.Url.OriginalString, ex); this.ShowError(ve); } } if (interactiveSortingClip != null) { try { // the linked "URL" consists of a single parameter only int i = interactiveSortingClip.SubReportURL.IndexOf('='); if (i != -1) { reportView.ReportData[interactiveSortingClip.SubReportURL.Substring(0, i)] = interactiveSortingClip.SubReportURL.Substring(i + 1); reportView.DataRefresh(); } } catch (Exception ex) { ViewerException ve = new ViewerException("Could not perform interactive sorting", ex); this.ShowError(ve); } } base.OnMouseClick(e); } /// /// Calculates the top left position of the content. /// /// the top left position of the content private Point CenterPoint() { if (HScroll && VScroll) { return new Point(0, 0); } Size fullSize = CalcContentSize(); int x = this.HScroll ? 0 : (ClientSize.Width - fullSize.Width) / 2; int y = this.VScroll ? 0 : (ClientSize.Height - fullSize.Height) / 2; return new Point(x, y); } /// /// Computes the zoom factor with the last seen page info. /// /// the zoom factor private float CalcZoomFactor() { return lastPageInfo == null ? 1f : CalcZoomFactor(lastPageInfo.Width, lastPageInfo.Height); } /// /// This function zooms the page to fit into the current window. /// According to the current /// /// the zoom factor public float CalcZoomFactor(int pageWidth, int pageHeight) { if (reportView == null) { return 1; } float width = this.Size.Width - (InnerPadding * 2); float height = this.Size.Height - (InnerPadding * 2); float imgWidth = (float)pageWidth / Graphics2DPainter.TwipsToPixel + 16; if (pageViewMode == Viewer.PageViewMode.DoublePage || pageViewMode == Viewer.PageViewMode.DoubleContinuous) { imgWidth += imgWidth + SpaceBetweenPages; } float imgHeight = (float)pageHeight / Graphics2DPainter.TwipsToPixel + 16; float zoom; switch (ZoomMode) { case Viewer.ZoomMode.FullPage: zoom = Math.Min(height / imgHeight, width / imgWidth); break; case Viewer.ZoomMode.PageHeight: zoom = height / imgHeight; if (zoom * imgWidth > width) { zoom = (height - SystemInformation.VerticalScrollBarWidth) / imgHeight; } break; case Viewer.ZoomMode.PageWidth: zoom = width / imgWidth; if (zoom * imgHeight > height) { zoom = (width - SystemInformation.HorizontalScrollBarHeight) / imgWidth; } break; case Viewer.ZoomMode.Manual: default: // this method does not do the manual zooming zoom = zoomFactor; break; } if (zoom < MinZoom / 100.0f) { zoom = MinZoom / 100.0f; } else if (zoom > MaxZoom / 100.0f) { zoom = MaxZoom / 100.0f; } return zoom; } /// /// /// public virtual void ShowError(Exception ex) { this.reportView.ShowError(ex); } /// /// Gets the page info instance of the last rendered page. /// public PageInfo PageInfo { get { return lastPageInfo; } } /// /// /// public bool WriteReportInfo(ReportInfo info, PageLoader loader) { reportView.ReportInfo = info; // Always return true return true; } /// /// /// public bool WritePageInfo(PageInfo info, PageLoader loader) { lastPageInfo = info; loader.Painter.ZoomFactor = CalcZoomFactor(info.PageWidth, info.PageHeight); reportView.CheckDataTimestamp(info.Timestamp); return true; // continue rendering image } /// /// /// public Font GetEmbeddedFont(int fontID, int fontRevision) { return rptDataCache.GetEmbeddedFont(fontID, fontRevision); } /// /// /// public void PageLoadFailure(Exception exception) { ShowError(exception); } /// /// Clears any hightlighted texts. /// public void ClearSelection() { if (pageContent == null) { return; } foreach (PageContent page in pageContent) { page.ClearSelection(); } Refresh(); } /// /// Normalizes the specified rectangle. A normalized rectangle has non-negative width and height. /// /// the rectangle to normalize /// private static Rectangle Normalize(Rectangle r) { return new Rectangle(Math.Min(r.Left, r.Right), Math.Min(r.Top, r.Bottom), Math.Abs(r.Width), Math.Abs(r.Height)); } /// /// Selects any text under the specified rectangle. /// /// the rectangle in pixels private void SelectArea(Rectangle rectangle) { if (pageContent == null) { return; } ClearSelection(); foreach (PageContent page in pageContent) { Point pagePos = CalcPagePosition(page.PageNumber); page.SelectArea(new Rectangle(rectangle.X - pagePos.X, rectangle.Y - pagePos.Y, rectangle.Width, rectangle.Height)); } } /// /// Sets the hightlighted search chunks. Any previously selection will be cleared. /// public SearchChunk[] HighlightedSearchChunks { set { if (pageContent == null) { return; } foreach (PageContent page in pageContent) { page.HighlightedSearchChunks = value; } if (value != null && value.Length > 0) { SearchChunk firstChunk = value[0]; if (!IsVisible(firstChunk.Page, new Point(firstChunk.X, firstChunk.Y)) || !IsVisible(firstChunk.Page, new Point(firstChunk.X + 900, firstChunk.Y)) || !IsVisible(firstChunk.Page, new Point(firstChunk.X, firstChunk.Y + 900)) || !IsVisible(firstChunk.Page, new Point(firstChunk.X + 900, firstChunk.Y + 900))) { ScrollTo(firstChunk.Page, new Point(firstChunk.X, firstChunk.Y - 150)); } } Refresh(); } } /// /// Scrolls to the specified document location. /// /// the page /// the point on the page, in twips internal void ScrollTo(int page, Point p) { ScrollTo(page, p, true); } /// /// Scrolls to the specified document location. /// /// the page /// the point on the page, in twips /// indicating whether the specified point should be centered vertically internal void ScrollTo(int page, Point p, bool centerVertical) { CurrentPage = page; Point pagePos = CalcPagePosition(page); Invalidate(); if (IsHandleCreated) { toolTip.Hide(this); } AutoScrollPosition = new Point( pagePos.X + (int)(p.X * zoomFactor / 15) - ClientSize.Width / 2, pagePos.Y + (int)(p.Y * zoomFactor / 15) - (centerVertical ? ClientSize.Height / 2 : 0)); UpdatePageContent(false); } /// /// Checks if a specified point on a page is in the visible area. /// /// the page /// the point /// true if the point is visible internal bool IsVisible(int page, Point p) { if (pageViewMode == Viewer.PageViewMode.SinglePage && page != currentPage || pageViewMode == Viewer.PageViewMode.DoublePage && page / 2 != currentPage / 2) { return false; } Point pagePos = CalcPagePosition(page); Point asp = AutoScrollPosition; int x = pagePos.X + (int)(p.X * zoomFactor / 15) + asp.X; int y = pagePos.Y + (int)(p.Y * zoomFactor / 15) + asp.Y; return x > 0 && y > 0 && x < ClientSize.Width && y < ClientSize.Height; } /// /// Gets or sets the number of pages in this report. /// public int PageCount { get { return pageContent == null ? 0 : pageContent.Length; } set { initialPageLoading = false; ExpandPageArray(value); UpdatePageContent(false); if (currentPage > value) { CurrentPage = value; } } } /// /// Expands the array of pages (PageContent instances) to the specified value. /// /// the new length private void ExpandPageArray(int value) { PageContent[] newPageData = new PageContent[value]; int prevCount = 0; if (pageContent != null) { Array.Copy(pageContent, newPageData, Math.Min(value, pageContent.Length)); prevCount = pageContent.Length; } pageContent = newPageData; for (int i = prevCount; i < value; i++) { pageContent[i] = new PageContent(i + 1, this, rptDataCache); pageContent[i].PageRendered += HandlePageRendered; } } /// /// Called after a page was rendered. Updates this UI element. /// /// /// protected virtual void HandlePageRendered(object sender, EventArgs e) { var invoker = (MethodInvoker)(() => { AdjustViewPort(); Invalidate(); }); if (InvokeRequired) { Invoke(invoker); } else { invoker(); } } /// /// Updates the page content instances. If required this will load the page data from the server and renders /// the page image in a background thread. /// /// public void UpdatePageContent(bool forceReload) { if (pageContent == null) { return; } int firstVisiblePage = FirstVisiblePage; int lastVisiblePage = LastVisiblePage; float zoomFactor = CalcZoomFactor(); if (initialPageLoading && pageContent.Length < lastVisiblePage) { ExpandPageArray(lastVisiblePage); } foreach (PageContent page in pageContent) { if (page.PageNumber >= firstVisiblePage && page.PageNumber <= lastVisiblePage) { page.Update(forceReload, zoomFactor); // only load the first page with reload flag set forceReload = false; } else { page.ClearRendering(); } } } /// /// Gets the first visible page /// public int FirstVisiblePage { get { switch (pageViewMode) { case Viewer.PageViewMode.SinglePage: return currentPage; case Viewer.PageViewMode.DoublePage: return ((currentPage - 1) & 0xFFFE) + 1; case Viewer.PageViewMode.SingleContinuous: return Math.Max(1, (-AutoScrollPosition.Y - PageContent.BorderTotal) / (ImageSize.Height + SpaceBetweenPages) + 1); case Viewer.PageViewMode.DoubleContinuous: return Math.Max(1, (-AutoScrollPosition.Y - PageContent.BorderTotal) / (ImageSize.Height + SpaceBetweenPages) * 2 + 1); default: return 1; } } } /// /// Gets the last visible page /// public int LastVisiblePage { get { int max = initialPageLoading ? int.MaxValue : PageCount; switch (pageViewMode) { case Viewer.PageViewMode.SinglePage: return Math.Min(max, currentPage); case Viewer.PageViewMode.DoublePage: return Math.Min(max, ((currentPage - 1) & 0xFFFE) + 2); case Viewer.PageViewMode.SingleContinuous: return Math.Min(max, (ClientSize.Height - AutoScrollPosition.Y) / (ImageSize.Height + SpaceBetweenPages) + 1); case Viewer.PageViewMode.DoubleContinuous: return Math.Min(max, (ClientSize.Height - AutoScrollPosition.Y) / (ImageSize.Height + SpaceBetweenPages) * 2 + 2); default: return PageCount; } } } /// /// Calculates the size of the full content area with all pages in respect to the current settings (view mode, zoom factor). /// /// the size of the full content area with all pages, in pixels private Size CalcContentSize() { int width; int height; Size imageSize = ImageSize; switch (PageViewMode) { case Viewer.PageViewMode.SinglePage: width = imageSize.Width; height = imageSize.Height; break; case Viewer.PageViewMode.DoublePage: width = 2 * imageSize.Width + SpaceBetweenPages; height = imageSize.Height; break; case Viewer.PageViewMode.SingleContinuous: width = imageSize.Width; height = PageCount * imageSize.Height + (PageCount - 1) * SpaceBetweenPages; break; case Viewer.PageViewMode.DoubleContinuous: width = 2 * imageSize.Width + SpaceBetweenPages; height = (PageCount + 1) / 2 * imageSize.Height + ((PageCount + 1) / 2 - 1) * SpaceBetweenPages; break; default: throw new NotImplementedException(); } width += InnerPadding * 2; height += InnerPadding * 2; return new Size(width, height); } /// /// Calculates the relative position of the specified page. /// /// the page number /// the relative position of the specified page, in pixels private Point CalcPagePosition(int page) { // make page index zero-relative --page; Size s = ImageSize; switch (pageViewMode) { case Viewer.PageViewMode.SinglePage: return new Point(0, 0); case Viewer.PageViewMode.SingleContinuous: return new Point(0, page * (s.Height + SpaceBetweenPages)); case Viewer.PageViewMode.DoublePage: return new Point((page % 2) * (s.Width + SpaceBetweenPages), 0); case Viewer.PageViewMode.DoubleContinuous: return new Point((page % 2) * (s.Width + SpaceBetweenPages), page / 2 * (s.Height + SpaceBetweenPages)); default: throw new NotImplementedException(); } } /// /// Gets the image size of a page in respect to the current zoom level. /// public Size ImageSize { get { if (lastPageInfo == null) { return new Size(100, 100); } float zoom = CalcZoomFactor(); int width = (int)(lastPageInfo.Width / Graphics2DPainter.TwipsToPixel * zoom); int height = (int)(lastPageInfo.Height / Graphics2DPainter.TwipsToPixel * zoom); return new Size(width, height); } } /// /// set to ZoomMin if is smaller than ZoomMin /// set to Zoom Max if it is bigger than ZoomMax /// /// public float ZoomFactor { get { return zoomFactor; } set { if (value < (ZoomMin / 100.0f)) { value = ZoomMin / 100.0f; } else if (value > (ZoomMax / 100.0f)) { value = ZoomMax / 100.0f; } if (zoomFactor != value) { int page = currentPage; Point relPagePos = CalcRelativeScrollPosition(page); this.zoomFactor = value; OnZoomChanged(); AdjustViewPort(); ScrollTo(page, relPagePos); } } } /// /// Computes the the scroll position (in twips) relative to the specified page. /// /// the relative scroll position private Point CalcRelativeScrollPosition(int page) { if (lastPageInfo == null) { return new Point(0, 0); } Point p = CalcPagePosition(page); return new Point( HScroll ? (int)((-p.X - AutoScrollPosition.X + ClientSize.Width / 2) / zoomFactor * 15) : lastPageInfo.Width / (IsDoublePageMode() ? 1 : 2), VScroll ? (int)((-p.Y - AutoScrollPosition.Y + ClientSize.Height / 2) / zoomFactor * 15) : lastPageInfo.Height / (IsDoublePageMode() ? 1 : 2)); } /// /// /// public ZoomMode ZoomMode { get { return zoomMode; } set { if (zoomMode != value) { int page = currentPage; Point relPagePos = CalcRelativeScrollPosition(page); zoomMode = value; OnZoomChanged(); if (value == Viewer.ZoomMode.FullPage || value == Viewer.ZoomMode.PageHeight || value == Viewer.ZoomMode.PageWidth) { // only AutoPan in manual mode this.AutoPan = false; } ScrollTo(page, relPagePos); } } } /// /// /// public float ZoomMin { get; set; } /// /// /// public float ZoomMax { get; set; } /// /// Gets or sets the current page. /// public int CurrentPage { get { return currentPage; } set { if (value < 1) { value = 1; } else if (value > PageCount && !initialPageLoading) { value = PageCount; } if (value != currentPage) { currentPage = value; overScrollDelta = 0; Point pagePos = CalcPagePosition(currentPage); pagePos.Y -= Math.Max(0, (ClientSize.Height - ImageSize.Height) / 2); AutoScrollPosition = pagePos; UpdatePageContent(false); Invalidate(); if (PageChanged != null) { PageChanged(this, value); } } } } /// /// Gets or sets the failure flag. If the failure flag is set, no loading animation will be shown. /// public bool LoadFailed { get; set; } /// /// Returns the content of the specified page. /// /// page number /// content instance or null if not available protected PageContent GetContent(int pageNum) { PageContent[] pc = pageContent; return pc == null || pc.Length < pageNum ? null : pc[pageNum - 1]; } /// /// Loads an initial page. /// internal void InitialPageLoad() { ExpandPageArray(1); pageContent[0].Update(false, 1f); } /// /// Returns a boolean value indicating whether the current view mode shows two pages side by side. /// /// treu when the current view mode shows two pages side by side public bool IsDoublePageMode() { return pageViewMode == Viewer.PageViewMode.DoublePage || pageViewMode == Viewer.PageViewMode.DoubleContinuous; } /// /// Returns a boolean value indicating whether the horizontal scrollbar is visible. /// /// boolean value indicating whether the horizontal scrollbar is visible public bool IsHScroll() { return HScroll; } /// /// Gets or sets the load progress of this content view. /// public Data.LoadProgress LoadProgress { get; set; } /// /// Selects a group. Any previous selection will be discarded. The group will be shown /// selected for a limited time only. /// /// the group to select public void SelectGroup(GroupTreeNode group) { selectedGroup = group; selectedGroupStartTicks = DateTime.Now.Ticks; } } }