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() { } } }