using PaintDotNet.SystemLayer;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace PaintDotNet.Annotation.Other
{
///
/// 没用到,先放着
///
public class OtherTool : IDisposable
{
private ImageResource toolBarImage;
private Cursor cursor;
private int mouseDown = 0; // incremented for every MouseDown, decremented for every MouseUp
private int ignoreMouseMove = 0; // when >0, MouseMove is ignored and then this is decremented
protected Cursor handCursor;
protected Cursor handCursorMouseDown;
protected Cursor handCursorInvalid;
private Cursor panOldCursor;
private Point lastMouseXY;
private Point lastPanMouseXY;
private bool panMode = false; // 'true' when the user is holding down the spacebar
private bool panTracking = false; // 'true' when panMode is true, and when the mouse is down (which is when MouseMove should do panning)
private ISurfaceBox documentWorkspace;
private bool active = false;
protected bool autoScroll = true;
private Hashtable keysThatAreDown = new Hashtable();
private MouseButtons lastButton = MouseButtons.None;
private int mouseEnter; // increments on MouseEnter, decrements on MouseLeave. The MouseLeave event is ONLY raised when this value decrements to 0, and MouseEnter is ONLY raised when this value increments to 1
public ISurfaceBox DocumentWorkspace
{
get
{
return this.documentWorkspace;
}
}
private sealed class KeyTimeInfo
{
public DateTime KeyDownTime;
public DateTime LastKeyPressPulse;
private int repeats = 0;
public int Repeats
{
get
{
return repeats;
}
set
{
repeats = value;
}
}
public KeyTimeInfo()
{
KeyDownTime = DateTime.Now;
LastKeyPressPulse = KeyDownTime;
}
}
///
/// Tells you whether the tool is "active" or not. If the tool is not active
/// it is not safe to call any other method besides PerformActivate. All
/// properties are safe to get values from.
///
public bool Active
{
get
{
return this.active;
}
}
public bool IsMouseDown
{
get
{
return this.mouseDown > 0;
}
}
///
/// Gets a flag that determines whether the Tool is deactivated while the current
/// layer is changing, and then reactivated afterwards.
///
///
/// This property is queried every time the ActiveLayer property of DocumentWorkspace
/// is changed. If false is returned, then the tool is not deactivated during the
/// layer change and must manually maintain coherency.
///
public virtual bool DeactivateOnLayerChange
{
get
{
return true;
}
}
///
/// Tells you which keys are pressed
///
public Keys ModifierKeys
{
get
{
return Control.ModifierKeys;
}
}
///
/// Represents the Image that is displayed in the toolbar.
///
public ImageResource Image
{
get
{
return this.toolBarImage;
}
}
public event EventHandler CursorChanging;
protected virtual void OnCursorChanging()
{
if (CursorChanging != null)
{
CursorChanging(this, EventArgs.Empty);
}
}
public event EventHandler CursorChanged;
protected virtual void OnCursorChanged()
{
if (CursorChanged != null)
{
CursorChanged(this, EventArgs.Empty);
}
}
///
/// The Cursor that is displayed when this Tool is active and the
/// mouse cursor is inside the document view.
///
public Cursor Cursor
{
get
{
return this.cursor;
}
set
{
OnCursorChanging();
this.cursor = value;
OnCursorChanged();
}
}
///
/// Specifies whether or not an inherited tool should take Ink commands
///
protected virtual bool SupportsInk
{
get
{
return false;
}
}
// Methods to send messages to this class
public void PerformActivate()
{
Activate();
}
public void PerformDeactivate()
{
Deactivate();
}
private bool IsOverflow(MouseEventArgs e)
{
PointF clientPt = DocumentWorkspace.DocumentToClient(new PointF(e.X, e.Y));
return clientPt.X < -16384 || clientPt.Y < -16384;
}
public bool IsMouseEntered
{
get
{
return this.mouseEnter > 0;
}
}
public void PerformMouseEnter()
{
MouseEnter();
}
private void MouseEnter()
{
++this.mouseEnter;
if (this.mouseEnter == 1)
{
OnMouseEnter();
}
}
protected virtual void OnMouseEnter()
{
}
public void PerformMouseLeave()
{
MouseLeave();
}
private void MouseLeave()
{
if (this.mouseEnter == 1)
{
this.mouseEnter = 0;
OnMouseLeave();
}
else
{
this.mouseEnter = Math.Max(0, this.mouseEnter - 1);
}
}
protected virtual void OnMouseLeave()
{
}
public void PerformMouseMove(MouseEventArgs e)
{
if (IsOverflow(e))
{
return;
}
MouseMove(e);
}
public void PerformMouseDown(MouseEventArgs e)
{
if (IsOverflow(e))
{
return;
}
MouseDown(e);
}
public void PerformMouseUp(MouseEventArgs e)
{
if (IsOverflow(e))
{
return;
}
MouseUp(e);
}
public void PerformKeyUp(KeyEventArgs e)
{
KeyUp(e);
}
public void PerformKeyDown(KeyEventArgs e)
{
KeyDown(e);
}
public void PerformClick()
{
Click();
}
public void PerformPulse()
{
Pulse();
}
public void PerformPaste(IDataObject data, out bool handled)
{
Paste(data, out handled);
}
public void PerformPasteQuery(IDataObject data, out bool canHandle)
{
PasteQuery(data, out canHandle);
}
private void Activate()
{
Debug.Assert(this.active != true, "already active!");
this.active = true;
this.handCursor = new Cursor(PdnResources.GetResourceStream("Cursors.PanToolCursor.cur"));
this.handCursorMouseDown = new Cursor(PdnResources.GetResourceStream("Cursors.PanToolCursorMouseDown.cur"));
this.handCursorInvalid = new Cursor(PdnResources.GetResourceStream("Cursors.PanToolCursorInvalid.cur"));
this.panTracking = false;
this.panMode = false;
this.mouseDown = 0;
OnActivate();
}
void FinishedHistoryStepGroup(object sender, EventArgs e)
{
OnFinishedHistoryStepGroup();
}
protected virtual void OnFinishedHistoryStepGroup()
{
}
///
/// This method is called when the tool is being activated; that is, when the
/// user has chosen to use this tool by clicking on it on a toolbar.
///
protected virtual void OnActivate()
{
}
private void Deactivate()
{
this.active = false;
OnDeactivate();
if (this.handCursor != null)
{
this.handCursor.Dispose();
this.handCursor = null;
}
if (this.handCursorMouseDown != null)
{
this.handCursorMouseDown.Dispose();
this.handCursorMouseDown = null;
}
if (this.handCursorInvalid != null)
{
this.handCursorInvalid.Dispose();
this.handCursorInvalid = null;
}
}
///
/// This method is called when the tool is being deactivated; that is, when the
/// user has chosen to use another tool by clicking on another tool on a
/// toolbar.
///
protected virtual void OnDeactivate()
{
}
private void MouseMove(MouseEventArgs e)
{
if (this.ignoreMouseMove > 0)
{
--this.ignoreMouseMove;
}
else if (this.panTracking && e.Button == MouseButtons.Left)
{
// Pan the document, using Stylus coordinates. This is done in
// MouseMove instead of StylusMove because StylusMove is
// asynchronous, and would not 'feel' right (pan motions would
// stack up)
Point position = new Point(e.X, e.Y);
RectangleF visibleRect = DocumentWorkspace.GetVisibleDocumentRectangleF();
PointF visibleCenterPt = new PointF((visibleRect.Left + visibleRect.Right) / 2, (visibleRect.Top + visibleRect.Bottom) / 2);
PointF delta = new PointF(e.X - lastPanMouseXY.X, e.Y - lastPanMouseXY.Y);
PointF newScroll = DocumentWorkspace.GetDocumentScrollPositionF();
if (delta.X != 0 || delta.Y != 0)
{
newScroll.X -= delta.X;
newScroll.Y -= delta.Y;
lastPanMouseXY = new Point(e.X, e.Y);
lastPanMouseXY.X -= (int)Math.Truncate(delta.X);
lastPanMouseXY.Y -= (int)Math.Truncate(delta.Y);
++this.ignoreMouseMove; // setting DocumentScrollPosition incurs a MouseMove event. ignore it prevents 'jittering' at non-integral zoom levels (like, say, 743%)
DocumentWorkspace.SetDocumentScrollPositionF(newScroll);
Update();
}
}
else if (!this.panMode)
{
OnMouseMove(e);
}
this.lastMouseXY = new Point(e.X, e.Y);
this.lastButton = e.Button;
}
///
/// This method is called when the Tool is active and the mouse is moving within
/// the document canvas area.
///
/// Contains information about where the mouse cursor is, in document coordinates.
public virtual void OnMouseMove(MouseEventArgs e)
{
if (this.panMode || this.mouseDown > 0)
{
ScrollIfNecessary(new PointF(e.X, e.Y));
}
}
private void MouseDown(MouseEventArgs e)
{
++this.mouseDown;
if (this.panMode)
{
this.panTracking = true;
this.lastPanMouseXY = new Point(e.X, e.Y);
if (this.CanPan())
{
this.Cursor = this.handCursorMouseDown;
}
}
else
{
OnMouseDown(e);
}
this.lastMouseXY = new Point(e.X, e.Y);
}
///
/// This method is called when the Tool is active and a mouse button has been
/// pressed within the document area.
///
/// Contains information about where the mouse cursor is, in document coordinates, and which mouse buttons were pressed.
public virtual void OnMouseDown(MouseEventArgs e)
{
this.lastButton = e.Button;
}
public void MouseUp(MouseEventArgs e)
{
--this.mouseDown;
if (!this.panMode)
{
OnMouseUp(e);
}
this.lastMouseXY = new Point(e.X, e.Y);
}
///
/// This method is called when the Tool is active and a mouse button has been
/// released within the document area.
///
/// Contains information about where the mouse cursor is, in document coordinates, and which mouse buttons were released.
public virtual void OnMouseUp(MouseEventArgs e)
{
this.lastButton = e.Button;
}
private void Click()
{
OnClick();
}
///
/// This method is called when the Tool is active and a mouse button has been
/// clicked within the document area. If you need more specific information,
/// such as where the mouse was clicked and which button was used, respond to
/// the MouseDown/MouseUp events.
///
protected virtual void OnClick()
{
}
private static DateTime lastToolSwitch = DateTime.MinValue;
// if we are pressing 'S' to switch to the selection tools, then consecutive
// presses of 'S' should switch to the next selection tol in the list. however,
// if we wait awhile then pressing 'S' should go to the *first* selection
// tool. 'awhile' is defined by this variable.
private static readonly TimeSpan toolSwitchReset = new TimeSpan(0, 0, 0, 2, 0);
private const char decPenSizeShortcut = '[';
private const char decPenSizeBy5Shortcut = (char)27; // Ctrl [ but must also test that Ctrl is down
private const char incPenSizeShortcut = ']';
private const char incPenSizeBy5Shortcut = (char)29; // Ctrl ] but must also test that Ctrl is down
private const char swapColorsShortcut = 'x';
private const char swapPrimarySecondaryChoice = 'c';
private char[] wildShortcuts = new char[] { ',', '.', '/' };
// Return true if the key is handled, false if not.
protected virtual bool OnWildShortcutKey(int ordinal)
{
return false;
}
private DateTime lastKeyboardMove = DateTime.MinValue;
private void KeyPress(Keys key)
{
OnKeyPress(key);
}
///
/// This method is called when the tool is active and a keyboard key is pressed
/// and released that is not representable with a regular Unicode chararacter.
/// An example would be the arrow keys.
///
protected virtual void OnKeyPress(Keys key)
{
/**
Point dir = Point.Empty;
if (key != lastKey)
{
lastKeyboardMove = DateTime.MinValue;
}
lastKey = key;
switch (key)
{
case Keys.Left:
--dir.X;
break;
case Keys.Right:
++dir.X;
break;
case Keys.Up:
--dir.Y;
break;
case Keys.Down:
++dir.Y;
break;
}
if (!dir.Equals(Point.Empty))
{
long span = DateTime.Now.Ticks - lastKeyboardMove.Ticks;
if ((span * 4) > TimeSpan.TicksPerSecond)
{
keyboardMoveRepeats = 0;
keyboardMoveSpeed = 1;
}
else
{
keyboardMoveRepeats++;
if (keyboardMoveRepeats > 15 && (keyboardMoveRepeats % 4) == 0)
{
keyboardMoveSpeed++;
}
}
lastKeyboardMove = DateTime.Now;
int offset = (int)(Math.Ceiling(DocumentWorkspace.GetRatio()) * (double)keyboardMoveSpeed);//ScaleFactor.
Cursor.Position = new Point(Cursor.Position.X + offset * dir.X, Cursor.Position.Y + offset * dir.Y);
Point location = DocumentWorkspace.PointToScreenFromTool(Point.Truncate(DocumentWorkspace.DocumentToClient(PointF.Empty)));
PointF stylusLocF = new PointF((float)Cursor.Position.X - (float)location.X, (float)Cursor.Position.Y - (float)location.Y);
Point stylusLoc = new Point(Cursor.Position.X - location.X, Cursor.Position.Y - location.Y);
stylusLoc = DocumentWorkspace.UnscalePoint(stylusLoc);
stylusLocF = DocumentWorkspace.UnscalePoint(stylusLocF);
DocumentWorkspace.PerformDocumentMouseMove(new MouseEventArgs(lastButton, 1, stylusLoc.X, stylusLoc.Y, 0));
}**/
}
public static Rectangle RoundRectangle(RectangleF rectF)
{
float left = (float)Math.Floor(rectF.Left);
float top = (float)Math.Floor(rectF.Top);
float right = (float)Math.Ceiling(rectF.Right);
float bottom = (float)Math.Ceiling(rectF.Bottom);
return Rectangle.Truncate(RectangleF.FromLTRB(left, top, right, bottom));
}
private bool CanPan()
{
//Rectangle vis = RoundRectangle(DocumentWorkspace.GetVisibleDocumentRectangleF());
//vis.Intersect(Document.Bounds);
//if (vis == Document.Bounds)
//{
// return false;
//}
//else
{
return true;
}
}
private void KeyUp(KeyEventArgs e)
{
if (this.panMode)
{
this.panMode = false;
this.panTracking = false;
this.Cursor = this.panOldCursor;
this.panOldCursor = null;
e.Handled = true;
}
OnKeyUp(e);
}
///
/// This method is called when the tool is active and a keyboard key is pressed.
/// If you respond to the keyboard key, set e.Handled to true.
///
protected virtual void OnKeyUp(KeyEventArgs e)
{
keysThatAreDown.Clear();
}
private void KeyDown(KeyEventArgs e)
{
OnKeyDown(e);
}
///
/// This method is called when the tool is active and a keyboard key is released
/// Before responding, check that e.Handled is false, and if you then respond to
/// the keyboard key, set e.Handled to true.
///
protected virtual void OnKeyDown(KeyEventArgs e)
{
if (!e.Handled)
{
if (!keysThatAreDown.Contains(e.KeyData))
{
keysThatAreDown.Add(e.KeyData, new KeyTimeInfo());
}
if (!this.IsMouseDown &&
!this.panMode &&
e.KeyCode == Keys.Space)
{
this.panMode = true;
this.panOldCursor = this.Cursor;
if (CanPan())
{
this.Cursor = this.handCursor;
}
else
{
this.Cursor = this.handCursorInvalid;
}
}
// arrow keys are processed in another way
// we get their KeyDown but no KeyUp, so they can not be handled
// by our normal methods
OnKeyPress(e.KeyData);
}
}
private void SelectionChanging()
{
OnSelectionChanging();
}
///
/// This method is called when the Tool is active and the selection area is
/// about to be changed.
///
protected virtual void OnSelectionChanging()
{
}
private void SelectionChanged()
{
OnSelectionChanged();
}
///
/// This method is called when the Tool is active and the selection area has
/// been changed.
///
protected virtual void OnSelectionChanged()
{
}
private void PasteQuery(IDataObject data, out bool canHandle)
{
OnPasteQuery(data, out canHandle);
}
///
/// This method is called when the system is querying a tool as to whether
/// it can handle a pasted object.
///
///
/// The clipboard data that was pasted by the user that should be inspected.
///
///
/// true if the data can be handled by the tool, false if not.
///
///
/// If you do not set canHandle to true then the tool will not be
/// able to respond to the Edit menu's Paste item.
///
protected virtual void OnPasteQuery(IDataObject data, out bool canHandle)
{
canHandle = false;
}
private void Paste(IDataObject data, out bool handled)
{
OnPaste(data, out handled);
}
///
/// This method is called when the user invokes a paste operation. Tools get
/// the first chance to handle this data.
///
///
/// The data that was pasted by the user.
///
///
/// true if the data was handled and pasted, false if not.
///
///
/// If you do not set handled to true the event will be passed to the
/// global paste handler.
///
protected virtual void OnPaste(IDataObject data, out bool handled)
{
handled = false;
}
private void Pulse()
{
OnPulse();
}
///
/// This method is called many times per second, called by the DocumentWorkspace.
///
protected virtual void OnPulse()
{
if (this.panTracking && this.lastButton == MouseButtons.Right)
{
/**
Point position = this.lastMouseXY;
RectangleF visibleRect = DocumentWorkspace.GetVisibleDocumentRectangleF();
PointF visibleCenterPt = new PointF((visibleRect.Left + visibleRect.Right) / 2, (visibleRect.Top + visibleRect.Bottom) / 2);
PointF delta = new PointF(position.X - visibleCenterPt.X, position.Y - visibleCenterPt.Y);
PointF newScroll = DocumentWorkspace.GetDocumentScrollPositionF();
if (delta.X != 0 || delta.Y != 0)
{
newScroll.X += delta.X;
newScroll.Y += delta.Y;
++this.ignoreMouseMove; // setting DocumentScrollPosition incurs a MouseMove event. ignore it prevents 'jittering' at non-integral zoom levels (like, say, 743%)
UI.SuspendControlPainting(DocumentWorkspace.GetThis());
DocumentWorkspace.SetDocumentScrollPositionF(newScroll);
this.trackingNub.Location = Utility.GetRectangleCenter(DocumentWorkspace.GetVisibleDocumentRectangleF());
UI.ResumeControlPainting(DocumentWorkspace.GetThis());
DocumentWorkspace.InvalidateFromTool(true);
Update();
}**/
}
}
protected bool ScrollIfNecessary(PointF position)
{
if (!autoScroll || !CanPan())
{
return false;
}
RectangleF visible = DocumentWorkspace.GetVisibleDocumentRectangleF();
PointF lastScrollPosition = DocumentWorkspace.GetDocumentScrollPositionF();
PointF delta = PointF.Empty;
PointF zoomedPoint = PointF.Empty;
zoomedPoint.X = ((visible.Left + visible.Right) / 2.0f + 1.02f * (position.X - (visible.Left + visible.Right) / 2.0f));
zoomedPoint.Y = ((visible.Top + visible.Bottom) / 2.0f + 1.02f * (position.Y - (visible.Top + visible.Bottom) / 2.0f));
if (zoomedPoint.X < visible.Left)
{
delta.X = zoomedPoint.X - visible.Left;
}
else if (zoomedPoint.X > visible.Right)
{
delta.X = zoomedPoint.X - visible.Right;
}
if (zoomedPoint.Y < visible.Top)
{
delta.Y = zoomedPoint.Y - visible.Top;
}
else if (zoomedPoint.Y > visible.Bottom)
{
delta.Y = zoomedPoint.Y - visible.Bottom;
}
if (!delta.IsEmpty)
{
PointF newScrollPosition = new PointF(lastScrollPosition.X + delta.X, lastScrollPosition.Y + delta.Y);
DocumentWorkspace.SetDocumentScrollPositionF(newScrollPosition);
Update();
return true;
}
else
{
return false;
}
}
private void SelectionChangingHandler(object sender, EventArgs e)
{
OnSelectionChanging();
}
private void SelectionChangedHandler(object sender, EventArgs e)
{
OnSelectionChanged();
}
protected void Update()
{
DocumentWorkspace.Update();
}
// NOTE: Your constructor must be able to run successfully with a documentWorkspace
// of null. This is sent in while the DocumentControl static constructor
// class is building a list of ToolInfo instances, so that it may construct
// the list without having to also construct a DocumentControl.
public OtherTool(ISurfaceBox documentWorkspace,
ImageResource toolBarImage,
string name,
string helpText,
char hotKey,
bool skipIfActiveOnHotKey)
{
this.documentWorkspace = documentWorkspace;
this.toolBarImage = toolBarImage;
}
public event EventHandler Disposed;
private void OnDisposed()
{
if (Disposed != null)
{
Disposed(this, EventArgs.Empty);
}
}
public void Dispose()
{
}
}
}