/*
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;
}
}
}