Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / Ink / EditingCoordinator.cs / 1 / EditingCoordinator.cs
//----------------------------------------------------------------------------
//
// File: EditingCoordinator.cs
//
// Description:
// Coordinates editing for InkCanvas
// Features:
//
// History:
// 4/29/2003 waynezen: Rewrote for mid-stroke support
// 3/15/2003 samgeo: Created
//
// Copyright (C) 2003 by Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Interop;
using System.Windows.Navigation;
using System.Windows.Media;
using MS.Utility;
namespace MS.Internal.Ink
{
///
/// Internal class that represents the editing stack of InkCanvas
/// Please see the design detain at http://tabletpc/longhorn/Specs/Mid-Stroke%20and%20Pen%20Cursor%20Dev%20Design.mht
///
internal class EditingCoordinator
{
//-------------------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------------------
#region Constructors
///
/// Constructors
///
/// InkCanvas instance
internal EditingCoordinator(InkCanvas inkCanvas)
{
if (inkCanvas == null)
{
throw new ArgumentNullException("inkCanvas");
}
_inkCanvas = inkCanvas;
// Create a stack for tracking the behavior
_activationStack = new Stack(2);
// NTRAID#WINDOWS-1104477-2005/02/26-waynezen,
// listen to in-air move so that we could get notified when Stylus is inverted.
//
_inkCanvas.AddHandler(Stylus.StylusInRangeEvent, new StylusEventHandler(OnInkCanvasStylusInAirOrInRangeMove));
_inkCanvas.AddHandler(Stylus.StylusInAirMoveEvent, new StylusEventHandler(OnInkCanvasStylusInAirOrInRangeMove));
_inkCanvas.AddHandler(Stylus.StylusOutOfRangeEvent, new StylusEventHandler(OnInkCanvasStylusOutOfRange));
}
#endregion Constructors
//--------------------------------------------------------------------------------
//
// Internal Methods
//
//-------------------------------------------------------------------------------
#region Internal Methods
///
/// Activate a dynamic behavior
/// Called from:
/// SelectionEditor.OnCanvasMouseMove
///
///
///
///
/// Critical: Calls critical methods AddStylusPoints and InitializeCapture
///
[SecurityCritical]
internal void ActivateDynamicBehavior(EditingBehavior dynamicBehavior, InputDevice inputDevice)
{
// Only SelectionEditor should be enable to initiate dynamic behavior
Debug.Assert(ActiveEditingBehavior == SelectionEditor,
"Only SelectionEditor should be able to initiate dynamic behavior");
DebugCheckDynamicBehavior(dynamicBehavior);
// Push the dynamic behavior.
PushEditingBehavior(dynamicBehavior);
// If this is LassoSelectionBehavior, we should capture the current stylus and feed the cached
if ( dynamicBehavior == LassoSelectionBehavior )
{
bool fSucceeded = false;
// The below code might call out StrokeErasing or StrokeErased event.
// The out-side code could throw exception.
// We use try/finally block to protect our status.
try
{
//we pass true for userInitiated because we've simply consulted the InputDevice
//(and only StylusDevice or MouseDevice) for the current position of the device
InitializeCapture(inputDevice, (IStylusEditing)dynamicBehavior,
true /*userInitiated*/, false/*Don't reset the RTI*/);
fSucceeded = true;
}
finally
{
if ( !fSucceeded )
{
// Abort the current editing.
ActiveEditingBehavior.Commit(false);
// Release capture and do clean-up.
ReleaseCapture(true);
}
}
}
_inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas));
}
///
/// Deactivate a dynamic behavior
/// Called from:
/// EditingBehavior.Commit
///
internal void DeactivateDynamicBehavior()
{
// Make sure we are under correct state:
// ActiveEditingBehavior has to be either LassoSelectionBehavior or SelectionEditingBehavior.
DebugCheckDynamicBehavior(ActiveEditingBehavior);
// Pop the dynamic behavior.
PopEditingBehavior();
}
///
/// Update the current active EditingMode
/// Called from:
/// EditingCoordinator.UpdateInvertedState
/// InkCanvas.Initialize()
///
internal void UpdateActiveEditingState()
{
UpdateEditingState(_stylusIsInverted);
}
///
/// Update the EditingMode
/// Called from:
/// InkCanvas.RaiseEditingModeChanged
/// InkCanvas.RaiseEditingModeInvertedChanged
///
/// A flage which indicates whether the new mode is for the Inverted state
internal void UpdateEditingState(bool inverted)
{
// If the mode is inactive currently, we should skip the update.
if ( inverted != _stylusIsInverted )
{
return;
}
// Get the current behavior and the new behavior.
EditingBehavior currentBehavior = ActiveEditingBehavior;
EditingBehavior newBehavior = GetBehavior(ActiveEditingMode);
// Check whether the user is editing.
if ( UserIsEditing )
{
// Check if we are in the mid-stroke.
if ( IsInMidStroke )
{
// We are in mid-stroke now.
Debug.Assert(currentBehavior is StylusEditingBehavior,
"The current behavoir has to be one of StylusEditingBehaviors");
( (StylusEditingBehavior)currentBehavior ).SwitchToMode(ActiveEditingMode);
}
else
{
if ( currentBehavior == SelectionEditingBehavior )
{
// Commit the current moving/resizing behavior
currentBehavior.Commit(true);
}
ChangeEditingBehavior(newBehavior);
}
}
else
{
// Check if we are in the mid-stroke.
if ( IsInMidStroke )
{
// We are in mid-stroke now.
Debug.Assert(currentBehavior is StylusEditingBehavior,
"The current behavoir has to be one of StylusEditingBehaviors");
( (StylusEditingBehavior)currentBehavior ).SwitchToMode(ActiveEditingMode);
}
else
{
// Make sure we are under correct state:
// currentBehavior cannot be any of Dynamic Behavior.
DebugCheckNonDynamicBehavior(currentBehavior);
ChangeEditingBehavior(newBehavior);
}
}
_inkCanvas.UpdateCursor();
}
///
/// Update the PointEraerCursor
/// Called from:
/// InkCanvas.set_EraserShape
///
internal void UpdatePointEraserCursor()
{
// We only have to update the point eraser when the active mode is EraseByPoint
// In other case, EraseBehavior will update cursor properly in its OnActivate routine.
if ( ActiveEditingMode == InkCanvasEditingMode.EraseByPoint )
{
InvalidateBehaviorCursor(EraserBehavior);
}
}
///
/// InvalidateTransform
/// Called by: InkCanvas.OnPropertyChanged
///
internal void InvalidateTransform()
{
// We only have to invalidate transform flags for InkCollectionBehavior and EraserBehavior for now.
SetTransformValid(InkCollectionBehavior, false);
SetTransformValid(EraserBehavior, false);
}
///
/// Invalidate the behavior cursor
/// Call from:
/// EditingCoordinator.UpdatePointEraserCursor
/// EraserBehavior.OnActivate
/// InkCollectionBehavior.OnInkCanvasDefaultDrawingAttributesReplaced
/// InkCollectionBehavior.OnInkCanvasDefaultDrawingAttributesChanged
/// SelectionEditingBehavior.OnActivate
/// SelectionEditor.UpdateCursor(InkCanvasSelectionHandle handle)
///
/// the behavior which its cursor needs to be updated.
internal void InvalidateBehaviorCursor(EditingBehavior behavior)
{
// Should never be null
Debug.Assert(behavior != null);
// InvalidateCursor first
SetCursorValid(behavior, false);
if ( !GetTransformValid(behavior) )
{
behavior.UpdateTransform();
SetTransformValid(behavior, true);
}
// If the behavior is active, we have to update cursor right now.
if ( behavior == ActiveEditingBehavior )
{
_inkCanvas.UpdateCursor();
}
}
///
/// Check whether the cursor of the specified behavior is valid
///
/// EditingBehavior
/// True if the cursor doesn't need to be updated
internal bool IsCursorValid(EditingBehavior behavior)
{
return GetCursorValid(behavior);
}
///
/// Check whether the cursor of the specified behavior is valid
///
/// EditingBehavior
/// True if the cursor doesn't need to be updated
internal bool IsTransformValid(EditingBehavior behavior)
{
return GetTransformValid(behavior);
}
///
/// Change to another StylusEditing mode or None mode.
///
///
///
///
internal IStylusEditing ChangeStylusEditingMode(StylusEditingBehavior sourceBehavior, InkCanvasEditingMode newMode)
{
// NOTICE-2006/04/27-WAYNEZEN,
// Before the caller invokes the method, the external event could have been fired.
// The user code may interrupt the Mid-Stroke by releasing capture or switching to another mode.
// So we should check if we still is under mid-stroke and the source behavior still is active.
// If not, we just no-op.
if ( IsInMidStroke &&
( ( // We expect that either InkCollectionBehavior or EraseBehavior is active.
sourceBehavior != LassoSelectionBehavior && sourceBehavior == ActiveEditingBehavior )
// Or We expect SelectionEditor is active here since
// LassoSelectionBehavior will be deactivated once it gets committed.
|| ( sourceBehavior == LassoSelectionBehavior && SelectionEditor == ActiveEditingBehavior ) ) )
{
Debug.Assert(_activationStack.Count == 1, "The behavior stack has to contain one behavior.");
// Pop up the old behavior
PopEditingBehavior();
// Get the new behavior
EditingBehavior newBehavior = GetBehavior(ActiveEditingMode);
if ( newBehavior != null )
{
// Push the new behavior
PushEditingBehavior(newBehavior);
// If the new mode is Select, we should push the LassoSelectionBehavior now.
if ( newMode == InkCanvasEditingMode.Select
// NOTICE-2006/04/27-WAYNEZEN,
// Make sure the newBehavior is SelectionEditor since it could be changed by the user event handling code.
&& newBehavior == SelectionEditor )
{
PushEditingBehavior(LassoSelectionBehavior);
}
}
else
{
// None-mode now. We stop collecting the packets.
ReleaseCapture(true);
}
_inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas));
return ActiveEditingBehavior as IStylusEditing;
}
else
{
// No-op
return null;
}
}
///
/// Debug Checker
///
///
[Conditional("DEBUG")]
internal void DebugCheckActiveBehavior(EditingBehavior behavior)
{
Debug.Assert(behavior == ActiveEditingBehavior);
}
///
/// Debug check for the dynamic behavior
///
///
[Conditional("DEBUG")]
private void DebugCheckDynamicBehavior(EditingBehavior behavior)
{
Debug.Assert(behavior == LassoSelectionBehavior || behavior == SelectionEditingBehavior,
"Only LassoSelectionBehavior or SelectionEditingBehavior is dynamic behavior");
}
///
/// Debug check for the non dynamic behavior
///
///
[Conditional("DEBUG")]
private void DebugCheckNonDynamicBehavior(EditingBehavior behavior)
{
Debug.Assert(behavior != LassoSelectionBehavior && behavior != SelectionEditingBehavior,
"behavior cannot be LassoSelectionBehavior or SelectionEditingBehavior");
}
#endregion Internal Methods
//--------------------------------------------------------------------------------
//
// Internal Properties
//
//--------------------------------------------------------------------------------
#region Internal Properties
///
/// Gets / sets whether move is enabled or note
///
internal bool MoveEnabled
{
get { return _moveEnabled; }
set { _moveEnabled = value; }
}
///
/// The property that indicates if the user is interacting with the current InkCanvas
///
internal bool UserIsEditing
{
get
{
return _userIsEditing;
}
set
{
_userIsEditing = value;
}
}
///
/// Helper flag that tells if we're between a preview down and an up.
///
internal bool StylusOrMouseIsDown
{
get
{
bool stylusDown = false;
StylusDevice stylusDevice = Stylus.CurrentStylusDevice;
if (stylusDevice != null && _inkCanvas.IsStylusOver && !stylusDevice.InAir)
{
stylusDown = true;
}
bool mouseDown = (_inkCanvas.IsMouseOver && Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed);
if (stylusDown || mouseDown)
{
return true;
}
return false;
}
}
///
/// Returns the StylusDevice to call DynamicRenderer.Reset with. Stylus or Mouse
/// must be in a down state. If the down device is a Mouse, null is returned, since
/// that is what DynamicRenderer.Reset expects for the Mouse.
///
///
internal InputDevice GetInputDeviceForReset()
{
Debug.Assert((_capturedStylus != null && _capturedMouse == null)
|| (_capturedStylus == null && _capturedMouse != null),
"There must be one and at most one device being captured.");
if ( _capturedStylus != null && !_capturedStylus.InAir )
{
return _capturedStylus;
}
else if ( _capturedMouse != null && _capturedMouse.LeftButton == MouseButtonState.Pressed )
{
return _capturedMouse;
}
return null;
}
///
/// Gets / sets whether resize is enabled or note
///
internal bool ResizeEnabled
{
get { return _resizeEnabled; }
set { _resizeEnabled = value; }
}
///
/// Lazy init access to the LassoSelectionBehavior
///
///
internal LassoSelectionBehavior LassoSelectionBehavior
{
get
{
if ( _lassoSelectionBehavior == null )
{
_lassoSelectionBehavior = new LassoSelectionBehavior(this, _inkCanvas);
}
return _lassoSelectionBehavior;
}
}
///
/// Lazy init access to the SelectionEditingBehavior
///
///
internal SelectionEditingBehavior SelectionEditingBehavior
{
get
{
if ( _selectionEditingBehavior == null )
{
_selectionEditingBehavior = new SelectionEditingBehavior(this, _inkCanvas);
}
return _selectionEditingBehavior;
}
}
///
/// Internal helper prop to indicate the active editing mode
///
internal InkCanvasEditingMode ActiveEditingMode
{
get
{
if ( _stylusIsInverted )
{
return _inkCanvas.EditingModeInverted;
}
return _inkCanvas.EditingMode;
}
}
///
/// Lazy init access to the SelectionEditor
///
///
internal SelectionEditor SelectionEditor
{
get
{
if ( _selectionEditor == null )
{
_selectionEditor = new SelectionEditor(this, _inkCanvas);
}
return _selectionEditor;
}
}
///
/// Gets the mid-stroke state
///
internal bool IsInMidStroke
{
get
{
return _capturedStylus != null || _capturedMouse != null;
}
}
///
/// Returns Stylus Inverted state
///
internal bool IsStylusInverted
{
get
{
return _stylusIsInverted;
}
}
#endregion Internal Properties
//-------------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------------
#region Private Methods
///
/// Retrieve the behavior instance based on the EditingMode
///
/// EditingMode
///
private EditingBehavior GetBehavior(InkCanvasEditingMode editingMode)
{
EditingBehavior newBehavior;
switch ( editingMode )
{
case InkCanvasEditingMode.Ink:
case InkCanvasEditingMode.GestureOnly:
case InkCanvasEditingMode.InkAndGesture:
newBehavior = InkCollectionBehavior;
break;
case InkCanvasEditingMode.Select:
newBehavior = SelectionEditor;
break;
case InkCanvasEditingMode.EraseByPoint:
case InkCanvasEditingMode.EraseByStroke:
newBehavior = EraserBehavior;
break;
default:
// Treat as InkCanvasEditingMode.None. Return null value
newBehavior = null;
break;
}
return newBehavior;
}
///
/// Pushes an EditingBehavior onto our stack, disables any current ones
///
/// The EditingBehavior to activate
private void PushEditingBehavior(EditingBehavior newEditingBehavior)
{
Debug.Assert(newEditingBehavior != null);
EditingBehavior behavior = ActiveEditingBehavior;
// Deactivate the previous behavior
if ( behavior != null )
{
behavior.Deactivate();
}
// Activate the new behavior.
_activationStack.Push(newEditingBehavior);
newEditingBehavior.Activate();
}
///
/// Pops an EditingBehavior onto our stack, disables any current ones
///
private void PopEditingBehavior()
{
EditingBehavior behavior = ActiveEditingBehavior;
if ( behavior != null )
{
behavior.Deactivate();
_activationStack.Pop();
behavior = ActiveEditingBehavior;
if ( ActiveEditingBehavior != null )
{
behavior.Activate();
}
}
return;
}
///
/// Handles in-air stylus movements
///
private void OnInkCanvasStylusInAirOrInRangeMove(object sender, StylusEventArgs args)
{
// If the current capture is mouse, we should reset the capture.
if ( _capturedMouse != null )
{
if (ActiveEditingBehavior == InkCollectionBehavior && _inkCanvas.InternalDynamicRenderer != null)
{
// NTRAID#WINDOWS-1378904-2005/11/17-WAYNEZEN
// When InkCanvas loses the current capture, we should stop the RTI since the App thread are no longer collecting ink.
// By flipping the Enabled property, the RTI will be reset.
_inkCanvas.InternalDynamicRenderer.Enabled = false;
_inkCanvas.InternalDynamicRenderer.Enabled = true;
}
// Call ActiveEditingBehavior.Commit at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
ActiveEditingBehavior.Commit(true);
// Release capture and do clean-up.
ReleaseCapture(true);
}
UpdateInvertedState(args.StylusDevice, args.Inverted);
}
///
/// Handle StylusOutofRange event
///
///
///
private void OnInkCanvasStylusOutOfRange(object sender, StylusEventArgs args)
{
// Reset the inverted state once OutOfRange has been received.
UpdateInvertedState(args.StylusDevice, false);
}
///
/// InkCanvas.StylusDown handler
///
///
///
///
/// Critical:
/// Calls SecurityCritcal method: InitializeCapture and AddStylusPoints
/// Eventually calls SecurityCritical method InkCanvas.RaiseGestureOrStrokeCollected
///
/// TreatAsSafe: This method is called by the input system, from security transparent
/// code, so it can not be marked critical. We check the eventArgs.UserInitiated
/// to verify that the input was user initiated and pass this flag to
/// InkCanvas.RaiseGestureOrStrokeCollected and use it to decide if we should
/// perform gesture recognition
///
[SecurityCritical, SecurityTreatAsSafe]
internal void OnInkCanvasDeviceDown(object sender, InputEventArgs args)
{
MouseButtonEventArgs mouseButtonEventArgs = args as MouseButtonEventArgs;
bool resetDynamicRenderer = false;
if ( mouseButtonEventArgs != null )
{
// NTRADI:WINDOWSOS#1563896-2006/03/20-WAYNEZEN,
// Note we don't mark Handled for the None EditingMode.
// Set focus to InkCanvas.
if ( _inkCanvas.Focus() && ActiveEditingMode != InkCanvasEditingMode.None )
{
mouseButtonEventArgs.Handled = true;
}
// ISSUE-2005/09/20-WAYNEZEN,
// We will reevaluate whether we should allow the right-button down for the modes other than the Ink mode.
// Skip collecting for the non-left buttons.
if ( mouseButtonEventArgs.ChangedButton != MouseButton.Left )
{
return;
}
// NTRAID-WINDOWS#1607286-2006/04/14-WAYNEZEN,
// If the mouse down event is from a Stylus, make sure we have a correct inverted state.
if ( mouseButtonEventArgs.StylusDevice != null )
{
UpdateInvertedState(mouseButtonEventArgs.StylusDevice, mouseButtonEventArgs.StylusDevice.Inverted);
}
}
else
{
// NTRAID-WINDOWS#1395155-2005/12/15-WAYNEZEN,
// When StylusDown is received, We should check the Invert state again.
// If there is any change, the ActiveEditingMode will be updated.
// The dynamic renderer will be reset in InkCollectionBehavior.OnActivated since the device is under down state.
StylusEventArgs stylusEventArgs = args as StylusEventArgs;
UpdateInvertedState(stylusEventArgs.StylusDevice, stylusEventArgs.Inverted);
}
// If the active behavior is not one of StylusEditingBehavior, don't even bother here.
IStylusEditing stylusEditingBehavior = ActiveEditingBehavior as IStylusEditing;
// We might receive StylusDown from a different device meanwhile we have already captured one.
// Make sure that we are starting from a fresh state.
if ( !IsInMidStroke && stylusEditingBehavior != null )
{
bool fSucceeded = false;
try
{
InputDevice capturedDevice = null;
// Capture the stylus (if mouse event make sure to use stylus if generated by a stylus)
if ( mouseButtonEventArgs != null && mouseButtonEventArgs.StylusDevice != null )
{
capturedDevice = mouseButtonEventArgs.StylusDevice;
resetDynamicRenderer = true;
}
else
{
capturedDevice = args.Device;
}
InitializeCapture(capturedDevice, stylusEditingBehavior, args.UserInitiated, resetDynamicRenderer);
// The below code might call out StrokeErasing or StrokeErased event.
// The out-side code could throw exception.
// We use try/finally block to protect our status.
fSucceeded = true;
}
finally
{
if ( !fSucceeded )
{
// Abort the current editing.
ActiveEditingBehavior.Commit(false);
// Release capture and do clean-up.
ReleaseCapture(IsInMidStroke);
}
}
}
}
///
/// InkCanvas.StylusMove handler
///
///
///
///
/// Critical:
/// Calls SecurityCritcal method:
/// Eventually calls SecurityCritical method InkCanvas.RaiseGestureOrStrokeCollected
///
/// TreatAsSafe: This method is called by the input system, from security transparent
/// code, so it can not be marked critical. We check the eventArgs.UserInitiated
/// to verify that the input was user initiated and pass this flag to
/// InkCanvas.RaiseGestureOrStrokeCollected and use it to decide if we should
/// perform gesture recognition
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnInkCanvasDeviceMove(object sender, TEventArgs args)
where TEventArgs : InputEventArgs
{
// Make sure that the stylus is the one we captured previously.
if ( IsInputDeviceCaptured(args.Device) )
{
IStylusEditing stylusEditingBehavior = ActiveEditingBehavior as IStylusEditing;
Debug.Assert(stylusEditingBehavior != null || ActiveEditingBehavior == null,
"The ActiveEditingBehavior should be either null (The None mode) or type of IStylusEditing.");
if ( stylusEditingBehavior != null )
{
StylusPointCollection stylusPoints;
if ( _capturedStylus != null )
{
stylusPoints = _capturedStylus.GetStylusPoints(_inkCanvas, _commonDescription);
}
else
{
// Make sure we ignore stylus generated mouse events.
MouseEventArgs mouseEventArgs = args as MouseEventArgs;
if ( mouseEventArgs != null && mouseEventArgs.StylusDevice != null )
{
return;
}
stylusPoints = new StylusPointCollection(new Point[] { _capturedMouse.GetPosition(_inkCanvas) });
}
bool fSucceeded = false;
// The below code might call out StrokeErasing or StrokeErased event.
// The out-side code could throw exception.
// We use try/finally block to protect our status.
try
{
stylusEditingBehavior.AddStylusPoints(stylusPoints, args.UserInitiated);
fSucceeded = true;
}
finally
{
if ( !fSucceeded )
{
// Abort the current editing.
ActiveEditingBehavior.Commit(false);
// Release capture and do clean-up.
ReleaseCapture(true);
}
}
}
}
}
///
/// InkCanvas.StylusUp handler
///
///
///
internal void OnInkCanvasDeviceUp(object sender, InputEventArgs args)
{
// Make sure that the stylus is the one we captured previously.
if ( IsInputDeviceCaptured(args.Device) )
{
Debug.Assert(ActiveEditingBehavior == null || ActiveEditingBehavior is IStylusEditing,
"The ActiveEditingBehavior should be either null (The None mode) or type of IStylusEditing.");
// Make sure we only look at mouse left button events if watching mouse events.
if ( _capturedMouse != null )
{
MouseButtonEventArgs mouseButtonEventArgs = args as MouseButtonEventArgs;
if ( mouseButtonEventArgs != null )
{
if ( mouseButtonEventArgs.ChangedButton != MouseButton.Left )
{
return;
}
}
}
try
{
// The follow code raises variety editing events.
// The out-side code could throw exception in the their handlers. We use try/finally block to protect our status.
if ( ActiveEditingBehavior != null )
{
// Commit the current editing.
ActiveEditingBehavior.Commit(true);
}
}
finally
{
// Call ReleaseCapture(true) at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
ReleaseCapture(true);
}
}
}
///
/// InkCanvas.LostStylusCapture handler
///
///
///
private void OnInkCanvasLostDeviceCapture(object sender, TEventArgs args)
where TEventArgs : InputEventArgs
{
// If user is editing, we have to commit the current operation and reset our state.
if ( UserIsEditing )
{
// Note ReleaseCapture(false) won't raise any external events. It only reset the internal states.
ReleaseCapture(false);
if ( ActiveEditingBehavior == InkCollectionBehavior && _inkCanvas.InternalDynamicRenderer != null )
{
// NTRAID#WINDOWS-1378904-2005/11/17-WAYNEZEN
// When InkCanvas loses the current capture, we should stop the RTI since the App thread are no longer collecting ink.
// By flipping the Enabled property, the RTI will be reset.
_inkCanvas.InternalDynamicRenderer.Enabled = false;
_inkCanvas.InternalDynamicRenderer.Enabled = true;
}
// Call ActiveEditingBehavior.Commit at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
ActiveEditingBehavior.Commit(true);
}
}
///
/// Initialize the capture state
///
///
///
///
///
///
/// Critical: Returns critical data from the inputDevice, StylusPointCollection.
///
[SecurityCritical]
private void InitializeCapture(InputDevice inputDevice, IStylusEditing stylusEditingBehavior, bool userInitiated, bool resetDynamicRenderer)
{
Debug.Assert(inputDevice != null, "A null device is passed in.");
Debug.Assert(stylusEditingBehavior != null, "stylusEditingBehavior cannot be null.");
Debug.Assert(!IsInMidStroke, "The previous device hasn't been released yet.");
StylusPointCollection stylusPoints;
_capturedStylus = inputDevice as StylusDevice;
_capturedMouse = inputDevice as MouseDevice;
// NOTICE-2005/09/15-WAYNEZEN,
// We assume that the StylusDown always happens before the MouseDown. So, we could safely add the listeners of
// XXXMove and XXXUp as the below which branchs out the coming mouse or stylus events.
if ( _capturedStylus != null )
{
StylusPointCollection newPoints = _capturedStylus.GetStylusPoints(_inkCanvas);
_commonDescription =
StylusPointDescription.GetCommonDescription(_inkCanvas.DefaultStylusPointDescription,
newPoints.Description);
stylusPoints = newPoints.Reformat(_commonDescription);
if ( resetDynamicRenderer )
{
// Reset the dynamic renderer for InkCollectionBehavior
InkCollectionBehavior inkCollectionBehavior = stylusEditingBehavior as InkCollectionBehavior;
if ( inkCollectionBehavior != null )
{
inkCollectionBehavior.ResetDynamicRenderer();
}
}
stylusEditingBehavior.AddStylusPoints(stylusPoints, userInitiated);
// If the current down device is stylus, we should only listen to StylusMove, StylusUp and LostStylusCapture
// events.
_inkCanvas.CaptureStylus();
if ( _inkCanvas.IsStylusCaptured && ActiveEditingMode != InkCanvasEditingMode.None )
{
_inkCanvas.AddHandler(Stylus.StylusMoveEvent, new StylusEventHandler(OnInkCanvasDeviceMove));
_inkCanvas.AddHandler(UIElement.LostStylusCaptureEvent, new StylusEventHandler(OnInkCanvasLostDeviceCapture));
}
else
{
_capturedStylus = null;
}
}
else
{
Debug.Assert(!resetDynamicRenderer, "The dynamic renderer shouldn't be reset for Mouse");
_commonDescription = null;
Point[] points = new Point[] { _capturedMouse.GetPosition(_inkCanvas) };
stylusPoints = new StylusPointCollection(points);
stylusEditingBehavior.AddStylusPoints(stylusPoints, userInitiated);
// NTRAID:WINDOWSOS#1536974-2006/03/02-WAYNEZEN,
// CaptureMouse triggers MouseDevice.Synchronize which sends a simulated MouseMove event.
// So we have to call CaptureMouse after at the end of the initialization.
// Otherwise, the MouseMove handlers will be executed in the middle of the initialization.
_inkCanvas.CaptureMouse();
// The user code could change mode or call release capture when the simulated Move event is received.
// So we have to check the capture still is on and the mode is correct before hooking up our handlers.
// If the current down device is mouse, we should only listen to MouseMove, MouseUp and LostMouseCapture
// events.
if ( _inkCanvas.IsMouseCaptured && ActiveEditingMode != InkCanvasEditingMode.None )
{
_inkCanvas.AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnInkCanvasDeviceMove));
_inkCanvas.AddHandler(UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnInkCanvasLostDeviceCapture));
}
else
{
_capturedMouse = null;
}
}
}
///
/// Clean up the capture state
///
///
private void ReleaseCapture(bool releaseDevice)
{
Debug.Assert(IsInMidStroke || !releaseDevice, "The captured device has been release unexpectly.");
if ( _capturedStylus != null )
{
// The Stylus was captured. Remove the stylus listeners.
_commonDescription = null;
_inkCanvas.RemoveHandler(Stylus.StylusMoveEvent, new StylusEventHandler(OnInkCanvasDeviceMove));
_inkCanvas.RemoveHandler(UIElement.LostStylusCaptureEvent, new StylusEventHandler(OnInkCanvasLostDeviceCapture));
_capturedStylus = null;
if ( releaseDevice )
{
// Call ReleaseStylusCapture at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
_inkCanvas.ReleaseStylusCapture();
}
}
else if ( _capturedMouse != null )
{
// The Mouse was captured. Remove the mouse listeners.
_inkCanvas.RemoveHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnInkCanvasDeviceMove));
_inkCanvas.RemoveHandler(UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnInkCanvasLostDeviceCapture));
_capturedMouse = null;
if ( releaseDevice )
{
// Call ReleaseMouseCapture at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
_inkCanvas.ReleaseMouseCapture();
}
}
}
///
/// Retrieve whether a specified device is captured by us.
///
///
///
private bool IsInputDeviceCaptured(InputDevice inputDevice)
{
Debug.Assert(_capturedStylus == null || _capturedMouse == null, "InkCanvas cannot capture both stylus and mouse at the same time.");
return (inputDevice == _capturedStylus && ((StylusDevice)inputDevice).Captured == _inkCanvas)
|| (inputDevice == _capturedMouse && ( (MouseDevice)inputDevice).Captured == _inkCanvas);
}
///
/// Return the cursor of the active behavior
///
///
internal Cursor GetActiveBehaviorCursor()
{
EditingBehavior behavior = ActiveEditingBehavior;
if ( behavior == null )
{
// Return the default Arrow cursor for the None mode.
return Cursors.Arrow;
}
// Retrieve the cursor.
Cursor cursor = behavior.Cursor;
// Clean up the dirty flag when it's done.
if ( !GetCursorValid(behavior) )
{
SetCursorValid(behavior, true);
}
return cursor;
}
///
/// A private method which returns the valid flag of behaviors' cursor.
///
///
///
private bool GetCursorValid(EditingBehavior behavior)
{
BehaviorValidFlag flag = GetBehaviorCursorFlag(behavior);
return GetBitFlag(flag);
}
///
/// A private method which sets/resets the valid flag of behaviors' cursor
///
///
///
private void SetCursorValid(EditingBehavior behavior, bool isValid)
{
BehaviorValidFlag flag = GetBehaviorCursorFlag(behavior);
SetBitFlag(flag, isValid);
}
///
/// A private method which returns the valid flag of behaviors' cursor.
///
///
///
private bool GetTransformValid(EditingBehavior behavior)
{
BehaviorValidFlag flag = GetBehaviorTransformFlag(behavior);
return GetBitFlag(flag);
}
///
/// A private method which sets/resets the valid flag of behaviors' cursor
///
///
///
private void SetTransformValid(EditingBehavior behavior, bool isValid)
{
BehaviorValidFlag flag = GetBehaviorTransformFlag(behavior);
SetBitFlag(flag, isValid);
}
///
/// GetBitFlag
///
///
///
private bool GetBitFlag(BehaviorValidFlag flag)
{
return (_behaviorValidFlag & flag) != 0;
}
///
/// SetBitFlag
///
///
///
private void SetBitFlag(BehaviorValidFlag flag, bool value)
{
if ( value )
{
_behaviorValidFlag |= flag;
}
else
{
_behaviorValidFlag &= (~flag);
}
}
///
/// A helper returns behavior cursor flag from a behavior instance
///
///
///
private BehaviorValidFlag GetBehaviorCursorFlag(EditingBehavior behavior)
{
BehaviorValidFlag flag = 0;
if ( behavior == InkCollectionBehavior )
{
flag = BehaviorValidFlag.InkCollectionBehaviorCursorValid;
}
else if ( behavior == EraserBehavior )
{
flag = BehaviorValidFlag.EraserBehaviorCursorValid;
}
else if ( behavior == LassoSelectionBehavior )
{
flag = BehaviorValidFlag.LassoSelectionBehaviorCursorValid;
}
else if ( behavior == SelectionEditingBehavior )
{
flag = BehaviorValidFlag.SelectionEditingBehaviorCursorValid;
}
else if ( behavior == SelectionEditor )
{
flag = BehaviorValidFlag.SelectionEditorCursorValid;
}
else
{
Debug.Assert(false, "Unknown behavior");
}
return flag;
}
///
/// A helper returns behavior transform flag from a behavior instance
///
///
///
private BehaviorValidFlag GetBehaviorTransformFlag(EditingBehavior behavior)
{
BehaviorValidFlag flag = 0;
if ( behavior == InkCollectionBehavior )
{
flag = BehaviorValidFlag.InkCollectionBehaviorTransformValid;
}
else if ( behavior == EraserBehavior )
{
flag = BehaviorValidFlag.EraserBehaviorTransformValid;
}
else if ( behavior == LassoSelectionBehavior )
{
flag = BehaviorValidFlag.LassoSelectionBehaviorTransformValid;
}
else if ( behavior == SelectionEditingBehavior )
{
flag = BehaviorValidFlag.SelectionEditingBehaviorTransformValid;
}
else if ( behavior == SelectionEditor )
{
flag = BehaviorValidFlag.SelectionEditorTransformValid;
}
else
{
Debug.Assert(false, "Unknown behavior");
}
return flag;
}
private void ChangeEditingBehavior(EditingBehavior newBehavior)
{
Debug.Assert(!IsInMidStroke, "ChangeEditingBehavior cannot be called in a mid-stroke");
Debug.Assert(_activationStack.Count <= 1, "The behavior stack has to contain at most one behavior when user is not editing.");
try
{
_inkCanvas.ClearSelection(true);
}
finally
{
if ( ActiveEditingBehavior != null )
{
// Pop the old behavior.
PopEditingBehavior();
}
// Push the new behavior
if ( newBehavior != null )
{
PushEditingBehavior(newBehavior);
}
_inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas));
}
}
///
/// Update the Inverted state
///
///
///
/// true if the behavior is updated
private bool UpdateInvertedState(StylusDevice stylusDevice, bool stylusIsInverted)
{
if ( !IsInMidStroke ||
( IsInMidStroke && IsInputDeviceCaptured(stylusDevice) ))
{
if ( stylusIsInverted != _stylusIsInverted )
{
_stylusIsInverted = stylusIsInverted;
//
// this can change editing state
//
UpdateActiveEditingState();
return true;
}
}
return false;
}
#endregion Private Methods
//-------------------------------------------------------------------------------
//
// Private Properties
//
//-------------------------------------------------------------------------------
#region Private Properties
///
/// Gets the top level active EditingBehavior
///
///
private EditingBehavior ActiveEditingBehavior
{
get
{
EditingBehavior EditingBehavior = null;
if ( _activationStack.Count > 0 )
{
EditingBehavior = _activationStack.Peek();
}
return EditingBehavior;
}
}
///
/// Lazy init access to the InkCollectionBehavior
///
///
internal InkCollectionBehavior InkCollectionBehavior
{
get
{
if ( _inkCollectionBehavior == null )
{
_inkCollectionBehavior = new InkCollectionBehavior(this, _inkCanvas);
}
return _inkCollectionBehavior;
}
}
///
/// Lazy init access to the EraserBehavior
///
///
private EraserBehavior EraserBehavior
{
get
{
if ( _eraserBehavior == null )
{
_eraserBehavior = new EraserBehavior(this, _inkCanvas);
}
return _eraserBehavior;
}
}
#endregion Private Properties
//-------------------------------------------------------------------------------
//
// Private Enum
//
//--------------------------------------------------------------------------------
#region Private Enum
///
/// Enum values which represent the cursor valid flag for each EditingBehavior.
///
[Flags]
private enum BehaviorValidFlag
{
InkCollectionBehaviorCursorValid = 0x00000001,
EraserBehaviorCursorValid = 0x00000002,
LassoSelectionBehaviorCursorValid = 0x00000004,
SelectionEditingBehaviorCursorValid = 0x00000008,
SelectionEditorCursorValid = 0x00000010,
InkCollectionBehaviorTransformValid = 0x00000020,
EraserBehaviorTransformValid = 0x00000040,
LassoSelectionBehaviorTransformValid = 0x00000080,
SelectionEditingBehaviorTransformValid= 0x00000100,
SelectionEditorTransformValid = 0x00000200,
}
#endregion Private Enum
//-------------------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------------------
#region Private Fields
///
/// The InkCanvas this EditingStack is being used in
///
private InkCanvas _inkCanvas;
private Stack _activationStack;
private InkCollectionBehavior _inkCollectionBehavior;
private EraserBehavior _eraserBehavior;
private LassoSelectionBehavior _lassoSelectionBehavior;
private SelectionEditingBehavior _selectionEditingBehavior;
private SelectionEditor _selectionEditor;
private bool _moveEnabled = true;
private bool _resizeEnabled = true;
private bool _userIsEditing = false;
///
/// Flag that indicates if the stylus is inverted
///
private bool _stylusIsInverted = false;
private StylusPointDescription _commonDescription;
private StylusDevice _capturedStylus;
private MouseDevice _capturedMouse;
// Fields related to cursor and transform.
private BehaviorValidFlag _behaviorValidFlag;
#endregion Private Fields
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
// File: EditingCoordinator.cs
//
// Description:
// Coordinates editing for InkCanvas
// Features:
//
// History:
// 4/29/2003 waynezen: Rewrote for mid-stroke support
// 3/15/2003 samgeo: Created
//
// Copyright (C) 2003 by Microsoft Corporation. All rights reserved.
//
//---------------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Interop;
using System.Windows.Navigation;
using System.Windows.Media;
using MS.Utility;
namespace MS.Internal.Ink
{
///
/// Internal class that represents the editing stack of InkCanvas
/// Please see the design detain at http://tabletpc/longhorn/Specs/Mid-Stroke%20and%20Pen%20Cursor%20Dev%20Design.mht
///
internal class EditingCoordinator
{
//-------------------------------------------------------------------------------
//
// Constructors
//
//-------------------------------------------------------------------------------
#region Constructors
///
/// Constructors
///
/// InkCanvas instance
internal EditingCoordinator(InkCanvas inkCanvas)
{
if (inkCanvas == null)
{
throw new ArgumentNullException("inkCanvas");
}
_inkCanvas = inkCanvas;
// Create a stack for tracking the behavior
_activationStack = new Stack(2);
// NTRAID#WINDOWS-1104477-2005/02/26-waynezen,
// listen to in-air move so that we could get notified when Stylus is inverted.
//
_inkCanvas.AddHandler(Stylus.StylusInRangeEvent, new StylusEventHandler(OnInkCanvasStylusInAirOrInRangeMove));
_inkCanvas.AddHandler(Stylus.StylusInAirMoveEvent, new StylusEventHandler(OnInkCanvasStylusInAirOrInRangeMove));
_inkCanvas.AddHandler(Stylus.StylusOutOfRangeEvent, new StylusEventHandler(OnInkCanvasStylusOutOfRange));
}
#endregion Constructors
//--------------------------------------------------------------------------------
//
// Internal Methods
//
//-------------------------------------------------------------------------------
#region Internal Methods
///
/// Activate a dynamic behavior
/// Called from:
/// SelectionEditor.OnCanvasMouseMove
///
///
///
///
/// Critical: Calls critical methods AddStylusPoints and InitializeCapture
///
[SecurityCritical]
internal void ActivateDynamicBehavior(EditingBehavior dynamicBehavior, InputDevice inputDevice)
{
// Only SelectionEditor should be enable to initiate dynamic behavior
Debug.Assert(ActiveEditingBehavior == SelectionEditor,
"Only SelectionEditor should be able to initiate dynamic behavior");
DebugCheckDynamicBehavior(dynamicBehavior);
// Push the dynamic behavior.
PushEditingBehavior(dynamicBehavior);
// If this is LassoSelectionBehavior, we should capture the current stylus and feed the cached
if ( dynamicBehavior == LassoSelectionBehavior )
{
bool fSucceeded = false;
// The below code might call out StrokeErasing or StrokeErased event.
// The out-side code could throw exception.
// We use try/finally block to protect our status.
try
{
//we pass true for userInitiated because we've simply consulted the InputDevice
//(and only StylusDevice or MouseDevice) for the current position of the device
InitializeCapture(inputDevice, (IStylusEditing)dynamicBehavior,
true /*userInitiated*/, false/*Don't reset the RTI*/);
fSucceeded = true;
}
finally
{
if ( !fSucceeded )
{
// Abort the current editing.
ActiveEditingBehavior.Commit(false);
// Release capture and do clean-up.
ReleaseCapture(true);
}
}
}
_inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas));
}
///
/// Deactivate a dynamic behavior
/// Called from:
/// EditingBehavior.Commit
///
internal void DeactivateDynamicBehavior()
{
// Make sure we are under correct state:
// ActiveEditingBehavior has to be either LassoSelectionBehavior or SelectionEditingBehavior.
DebugCheckDynamicBehavior(ActiveEditingBehavior);
// Pop the dynamic behavior.
PopEditingBehavior();
}
///
/// Update the current active EditingMode
/// Called from:
/// EditingCoordinator.UpdateInvertedState
/// InkCanvas.Initialize()
///
internal void UpdateActiveEditingState()
{
UpdateEditingState(_stylusIsInverted);
}
///
/// Update the EditingMode
/// Called from:
/// InkCanvas.RaiseEditingModeChanged
/// InkCanvas.RaiseEditingModeInvertedChanged
///
/// A flage which indicates whether the new mode is for the Inverted state
internal void UpdateEditingState(bool inverted)
{
// If the mode is inactive currently, we should skip the update.
if ( inverted != _stylusIsInverted )
{
return;
}
// Get the current behavior and the new behavior.
EditingBehavior currentBehavior = ActiveEditingBehavior;
EditingBehavior newBehavior = GetBehavior(ActiveEditingMode);
// Check whether the user is editing.
if ( UserIsEditing )
{
// Check if we are in the mid-stroke.
if ( IsInMidStroke )
{
// We are in mid-stroke now.
Debug.Assert(currentBehavior is StylusEditingBehavior,
"The current behavoir has to be one of StylusEditingBehaviors");
( (StylusEditingBehavior)currentBehavior ).SwitchToMode(ActiveEditingMode);
}
else
{
if ( currentBehavior == SelectionEditingBehavior )
{
// Commit the current moving/resizing behavior
currentBehavior.Commit(true);
}
ChangeEditingBehavior(newBehavior);
}
}
else
{
// Check if we are in the mid-stroke.
if ( IsInMidStroke )
{
// We are in mid-stroke now.
Debug.Assert(currentBehavior is StylusEditingBehavior,
"The current behavoir has to be one of StylusEditingBehaviors");
( (StylusEditingBehavior)currentBehavior ).SwitchToMode(ActiveEditingMode);
}
else
{
// Make sure we are under correct state:
// currentBehavior cannot be any of Dynamic Behavior.
DebugCheckNonDynamicBehavior(currentBehavior);
ChangeEditingBehavior(newBehavior);
}
}
_inkCanvas.UpdateCursor();
}
///
/// Update the PointEraerCursor
/// Called from:
/// InkCanvas.set_EraserShape
///
internal void UpdatePointEraserCursor()
{
// We only have to update the point eraser when the active mode is EraseByPoint
// In other case, EraseBehavior will update cursor properly in its OnActivate routine.
if ( ActiveEditingMode == InkCanvasEditingMode.EraseByPoint )
{
InvalidateBehaviorCursor(EraserBehavior);
}
}
///
/// InvalidateTransform
/// Called by: InkCanvas.OnPropertyChanged
///
internal void InvalidateTransform()
{
// We only have to invalidate transform flags for InkCollectionBehavior and EraserBehavior for now.
SetTransformValid(InkCollectionBehavior, false);
SetTransformValid(EraserBehavior, false);
}
///
/// Invalidate the behavior cursor
/// Call from:
/// EditingCoordinator.UpdatePointEraserCursor
/// EraserBehavior.OnActivate
/// InkCollectionBehavior.OnInkCanvasDefaultDrawingAttributesReplaced
/// InkCollectionBehavior.OnInkCanvasDefaultDrawingAttributesChanged
/// SelectionEditingBehavior.OnActivate
/// SelectionEditor.UpdateCursor(InkCanvasSelectionHandle handle)
///
/// the behavior which its cursor needs to be updated.
internal void InvalidateBehaviorCursor(EditingBehavior behavior)
{
// Should never be null
Debug.Assert(behavior != null);
// InvalidateCursor first
SetCursorValid(behavior, false);
if ( !GetTransformValid(behavior) )
{
behavior.UpdateTransform();
SetTransformValid(behavior, true);
}
// If the behavior is active, we have to update cursor right now.
if ( behavior == ActiveEditingBehavior )
{
_inkCanvas.UpdateCursor();
}
}
///
/// Check whether the cursor of the specified behavior is valid
///
/// EditingBehavior
/// True if the cursor doesn't need to be updated
internal bool IsCursorValid(EditingBehavior behavior)
{
return GetCursorValid(behavior);
}
///
/// Check whether the cursor of the specified behavior is valid
///
/// EditingBehavior
/// True if the cursor doesn't need to be updated
internal bool IsTransformValid(EditingBehavior behavior)
{
return GetTransformValid(behavior);
}
///
/// Change to another StylusEditing mode or None mode.
///
///
///
///
internal IStylusEditing ChangeStylusEditingMode(StylusEditingBehavior sourceBehavior, InkCanvasEditingMode newMode)
{
// NOTICE-2006/04/27-WAYNEZEN,
// Before the caller invokes the method, the external event could have been fired.
// The user code may interrupt the Mid-Stroke by releasing capture or switching to another mode.
// So we should check if we still is under mid-stroke and the source behavior still is active.
// If not, we just no-op.
if ( IsInMidStroke &&
( ( // We expect that either InkCollectionBehavior or EraseBehavior is active.
sourceBehavior != LassoSelectionBehavior && sourceBehavior == ActiveEditingBehavior )
// Or We expect SelectionEditor is active here since
// LassoSelectionBehavior will be deactivated once it gets committed.
|| ( sourceBehavior == LassoSelectionBehavior && SelectionEditor == ActiveEditingBehavior ) ) )
{
Debug.Assert(_activationStack.Count == 1, "The behavior stack has to contain one behavior.");
// Pop up the old behavior
PopEditingBehavior();
// Get the new behavior
EditingBehavior newBehavior = GetBehavior(ActiveEditingMode);
if ( newBehavior != null )
{
// Push the new behavior
PushEditingBehavior(newBehavior);
// If the new mode is Select, we should push the LassoSelectionBehavior now.
if ( newMode == InkCanvasEditingMode.Select
// NOTICE-2006/04/27-WAYNEZEN,
// Make sure the newBehavior is SelectionEditor since it could be changed by the user event handling code.
&& newBehavior == SelectionEditor )
{
PushEditingBehavior(LassoSelectionBehavior);
}
}
else
{
// None-mode now. We stop collecting the packets.
ReleaseCapture(true);
}
_inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas));
return ActiveEditingBehavior as IStylusEditing;
}
else
{
// No-op
return null;
}
}
///
/// Debug Checker
///
///
[Conditional("DEBUG")]
internal void DebugCheckActiveBehavior(EditingBehavior behavior)
{
Debug.Assert(behavior == ActiveEditingBehavior);
}
///
/// Debug check for the dynamic behavior
///
///
[Conditional("DEBUG")]
private void DebugCheckDynamicBehavior(EditingBehavior behavior)
{
Debug.Assert(behavior == LassoSelectionBehavior || behavior == SelectionEditingBehavior,
"Only LassoSelectionBehavior or SelectionEditingBehavior is dynamic behavior");
}
///
/// Debug check for the non dynamic behavior
///
///
[Conditional("DEBUG")]
private void DebugCheckNonDynamicBehavior(EditingBehavior behavior)
{
Debug.Assert(behavior != LassoSelectionBehavior && behavior != SelectionEditingBehavior,
"behavior cannot be LassoSelectionBehavior or SelectionEditingBehavior");
}
#endregion Internal Methods
//--------------------------------------------------------------------------------
//
// Internal Properties
//
//--------------------------------------------------------------------------------
#region Internal Properties
///
/// Gets / sets whether move is enabled or note
///
internal bool MoveEnabled
{
get { return _moveEnabled; }
set { _moveEnabled = value; }
}
///
/// The property that indicates if the user is interacting with the current InkCanvas
///
internal bool UserIsEditing
{
get
{
return _userIsEditing;
}
set
{
_userIsEditing = value;
}
}
///
/// Helper flag that tells if we're between a preview down and an up.
///
internal bool StylusOrMouseIsDown
{
get
{
bool stylusDown = false;
StylusDevice stylusDevice = Stylus.CurrentStylusDevice;
if (stylusDevice != null && _inkCanvas.IsStylusOver && !stylusDevice.InAir)
{
stylusDown = true;
}
bool mouseDown = (_inkCanvas.IsMouseOver && Mouse.PrimaryDevice.LeftButton == MouseButtonState.Pressed);
if (stylusDown || mouseDown)
{
return true;
}
return false;
}
}
///
/// Returns the StylusDevice to call DynamicRenderer.Reset with. Stylus or Mouse
/// must be in a down state. If the down device is a Mouse, null is returned, since
/// that is what DynamicRenderer.Reset expects for the Mouse.
///
///
internal InputDevice GetInputDeviceForReset()
{
Debug.Assert((_capturedStylus != null && _capturedMouse == null)
|| (_capturedStylus == null && _capturedMouse != null),
"There must be one and at most one device being captured.");
if ( _capturedStylus != null && !_capturedStylus.InAir )
{
return _capturedStylus;
}
else if ( _capturedMouse != null && _capturedMouse.LeftButton == MouseButtonState.Pressed )
{
return _capturedMouse;
}
return null;
}
///
/// Gets / sets whether resize is enabled or note
///
internal bool ResizeEnabled
{
get { return _resizeEnabled; }
set { _resizeEnabled = value; }
}
///
/// Lazy init access to the LassoSelectionBehavior
///
///
internal LassoSelectionBehavior LassoSelectionBehavior
{
get
{
if ( _lassoSelectionBehavior == null )
{
_lassoSelectionBehavior = new LassoSelectionBehavior(this, _inkCanvas);
}
return _lassoSelectionBehavior;
}
}
///
/// Lazy init access to the SelectionEditingBehavior
///
///
internal SelectionEditingBehavior SelectionEditingBehavior
{
get
{
if ( _selectionEditingBehavior == null )
{
_selectionEditingBehavior = new SelectionEditingBehavior(this, _inkCanvas);
}
return _selectionEditingBehavior;
}
}
///
/// Internal helper prop to indicate the active editing mode
///
internal InkCanvasEditingMode ActiveEditingMode
{
get
{
if ( _stylusIsInverted )
{
return _inkCanvas.EditingModeInverted;
}
return _inkCanvas.EditingMode;
}
}
///
/// Lazy init access to the SelectionEditor
///
///
internal SelectionEditor SelectionEditor
{
get
{
if ( _selectionEditor == null )
{
_selectionEditor = new SelectionEditor(this, _inkCanvas);
}
return _selectionEditor;
}
}
///
/// Gets the mid-stroke state
///
internal bool IsInMidStroke
{
get
{
return _capturedStylus != null || _capturedMouse != null;
}
}
///
/// Returns Stylus Inverted state
///
internal bool IsStylusInverted
{
get
{
return _stylusIsInverted;
}
}
#endregion Internal Properties
//-------------------------------------------------------------------------------
//
// Private Methods
//
//--------------------------------------------------------------------------------
#region Private Methods
///
/// Retrieve the behavior instance based on the EditingMode
///
/// EditingMode
///
private EditingBehavior GetBehavior(InkCanvasEditingMode editingMode)
{
EditingBehavior newBehavior;
switch ( editingMode )
{
case InkCanvasEditingMode.Ink:
case InkCanvasEditingMode.GestureOnly:
case InkCanvasEditingMode.InkAndGesture:
newBehavior = InkCollectionBehavior;
break;
case InkCanvasEditingMode.Select:
newBehavior = SelectionEditor;
break;
case InkCanvasEditingMode.EraseByPoint:
case InkCanvasEditingMode.EraseByStroke:
newBehavior = EraserBehavior;
break;
default:
// Treat as InkCanvasEditingMode.None. Return null value
newBehavior = null;
break;
}
return newBehavior;
}
///
/// Pushes an EditingBehavior onto our stack, disables any current ones
///
/// The EditingBehavior to activate
private void PushEditingBehavior(EditingBehavior newEditingBehavior)
{
Debug.Assert(newEditingBehavior != null);
EditingBehavior behavior = ActiveEditingBehavior;
// Deactivate the previous behavior
if ( behavior != null )
{
behavior.Deactivate();
}
// Activate the new behavior.
_activationStack.Push(newEditingBehavior);
newEditingBehavior.Activate();
}
///
/// Pops an EditingBehavior onto our stack, disables any current ones
///
private void PopEditingBehavior()
{
EditingBehavior behavior = ActiveEditingBehavior;
if ( behavior != null )
{
behavior.Deactivate();
_activationStack.Pop();
behavior = ActiveEditingBehavior;
if ( ActiveEditingBehavior != null )
{
behavior.Activate();
}
}
return;
}
///
/// Handles in-air stylus movements
///
private void OnInkCanvasStylusInAirOrInRangeMove(object sender, StylusEventArgs args)
{
// If the current capture is mouse, we should reset the capture.
if ( _capturedMouse != null )
{
if (ActiveEditingBehavior == InkCollectionBehavior && _inkCanvas.InternalDynamicRenderer != null)
{
// NTRAID#WINDOWS-1378904-2005/11/17-WAYNEZEN
// When InkCanvas loses the current capture, we should stop the RTI since the App thread are no longer collecting ink.
// By flipping the Enabled property, the RTI will be reset.
_inkCanvas.InternalDynamicRenderer.Enabled = false;
_inkCanvas.InternalDynamicRenderer.Enabled = true;
}
// Call ActiveEditingBehavior.Commit at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
ActiveEditingBehavior.Commit(true);
// Release capture and do clean-up.
ReleaseCapture(true);
}
UpdateInvertedState(args.StylusDevice, args.Inverted);
}
///
/// Handle StylusOutofRange event
///
///
///
private void OnInkCanvasStylusOutOfRange(object sender, StylusEventArgs args)
{
// Reset the inverted state once OutOfRange has been received.
UpdateInvertedState(args.StylusDevice, false);
}
///
/// InkCanvas.StylusDown handler
///
///
///
///
/// Critical:
/// Calls SecurityCritcal method: InitializeCapture and AddStylusPoints
/// Eventually calls SecurityCritical method InkCanvas.RaiseGestureOrStrokeCollected
///
/// TreatAsSafe: This method is called by the input system, from security transparent
/// code, so it can not be marked critical. We check the eventArgs.UserInitiated
/// to verify that the input was user initiated and pass this flag to
/// InkCanvas.RaiseGestureOrStrokeCollected and use it to decide if we should
/// perform gesture recognition
///
[SecurityCritical, SecurityTreatAsSafe]
internal void OnInkCanvasDeviceDown(object sender, InputEventArgs args)
{
MouseButtonEventArgs mouseButtonEventArgs = args as MouseButtonEventArgs;
bool resetDynamicRenderer = false;
if ( mouseButtonEventArgs != null )
{
// NTRADI:WINDOWSOS#1563896-2006/03/20-WAYNEZEN,
// Note we don't mark Handled for the None EditingMode.
// Set focus to InkCanvas.
if ( _inkCanvas.Focus() && ActiveEditingMode != InkCanvasEditingMode.None )
{
mouseButtonEventArgs.Handled = true;
}
// ISSUE-2005/09/20-WAYNEZEN,
// We will reevaluate whether we should allow the right-button down for the modes other than the Ink mode.
// Skip collecting for the non-left buttons.
if ( mouseButtonEventArgs.ChangedButton != MouseButton.Left )
{
return;
}
// NTRAID-WINDOWS#1607286-2006/04/14-WAYNEZEN,
// If the mouse down event is from a Stylus, make sure we have a correct inverted state.
if ( mouseButtonEventArgs.StylusDevice != null )
{
UpdateInvertedState(mouseButtonEventArgs.StylusDevice, mouseButtonEventArgs.StylusDevice.Inverted);
}
}
else
{
// NTRAID-WINDOWS#1395155-2005/12/15-WAYNEZEN,
// When StylusDown is received, We should check the Invert state again.
// If there is any change, the ActiveEditingMode will be updated.
// The dynamic renderer will be reset in InkCollectionBehavior.OnActivated since the device is under down state.
StylusEventArgs stylusEventArgs = args as StylusEventArgs;
UpdateInvertedState(stylusEventArgs.StylusDevice, stylusEventArgs.Inverted);
}
// If the active behavior is not one of StylusEditingBehavior, don't even bother here.
IStylusEditing stylusEditingBehavior = ActiveEditingBehavior as IStylusEditing;
// We might receive StylusDown from a different device meanwhile we have already captured one.
// Make sure that we are starting from a fresh state.
if ( !IsInMidStroke && stylusEditingBehavior != null )
{
bool fSucceeded = false;
try
{
InputDevice capturedDevice = null;
// Capture the stylus (if mouse event make sure to use stylus if generated by a stylus)
if ( mouseButtonEventArgs != null && mouseButtonEventArgs.StylusDevice != null )
{
capturedDevice = mouseButtonEventArgs.StylusDevice;
resetDynamicRenderer = true;
}
else
{
capturedDevice = args.Device;
}
InitializeCapture(capturedDevice, stylusEditingBehavior, args.UserInitiated, resetDynamicRenderer);
// The below code might call out StrokeErasing or StrokeErased event.
// The out-side code could throw exception.
// We use try/finally block to protect our status.
fSucceeded = true;
}
finally
{
if ( !fSucceeded )
{
// Abort the current editing.
ActiveEditingBehavior.Commit(false);
// Release capture and do clean-up.
ReleaseCapture(IsInMidStroke);
}
}
}
}
///
/// InkCanvas.StylusMove handler
///
///
///
///
/// Critical:
/// Calls SecurityCritcal method:
/// Eventually calls SecurityCritical method InkCanvas.RaiseGestureOrStrokeCollected
///
/// TreatAsSafe: This method is called by the input system, from security transparent
/// code, so it can not be marked critical. We check the eventArgs.UserInitiated
/// to verify that the input was user initiated and pass this flag to
/// InkCanvas.RaiseGestureOrStrokeCollected and use it to decide if we should
/// perform gesture recognition
///
[SecurityCritical, SecurityTreatAsSafe]
private void OnInkCanvasDeviceMove(object sender, TEventArgs args)
where TEventArgs : InputEventArgs
{
// Make sure that the stylus is the one we captured previously.
if ( IsInputDeviceCaptured(args.Device) )
{
IStylusEditing stylusEditingBehavior = ActiveEditingBehavior as IStylusEditing;
Debug.Assert(stylusEditingBehavior != null || ActiveEditingBehavior == null,
"The ActiveEditingBehavior should be either null (The None mode) or type of IStylusEditing.");
if ( stylusEditingBehavior != null )
{
StylusPointCollection stylusPoints;
if ( _capturedStylus != null )
{
stylusPoints = _capturedStylus.GetStylusPoints(_inkCanvas, _commonDescription);
}
else
{
// Make sure we ignore stylus generated mouse events.
MouseEventArgs mouseEventArgs = args as MouseEventArgs;
if ( mouseEventArgs != null && mouseEventArgs.StylusDevice != null )
{
return;
}
stylusPoints = new StylusPointCollection(new Point[] { _capturedMouse.GetPosition(_inkCanvas) });
}
bool fSucceeded = false;
// The below code might call out StrokeErasing or StrokeErased event.
// The out-side code could throw exception.
// We use try/finally block to protect our status.
try
{
stylusEditingBehavior.AddStylusPoints(stylusPoints, args.UserInitiated);
fSucceeded = true;
}
finally
{
if ( !fSucceeded )
{
// Abort the current editing.
ActiveEditingBehavior.Commit(false);
// Release capture and do clean-up.
ReleaseCapture(true);
}
}
}
}
}
///
/// InkCanvas.StylusUp handler
///
///
///
internal void OnInkCanvasDeviceUp(object sender, InputEventArgs args)
{
// Make sure that the stylus is the one we captured previously.
if ( IsInputDeviceCaptured(args.Device) )
{
Debug.Assert(ActiveEditingBehavior == null || ActiveEditingBehavior is IStylusEditing,
"The ActiveEditingBehavior should be either null (The None mode) or type of IStylusEditing.");
// Make sure we only look at mouse left button events if watching mouse events.
if ( _capturedMouse != null )
{
MouseButtonEventArgs mouseButtonEventArgs = args as MouseButtonEventArgs;
if ( mouseButtonEventArgs != null )
{
if ( mouseButtonEventArgs.ChangedButton != MouseButton.Left )
{
return;
}
}
}
try
{
// The follow code raises variety editing events.
// The out-side code could throw exception in the their handlers. We use try/finally block to protect our status.
if ( ActiveEditingBehavior != null )
{
// Commit the current editing.
ActiveEditingBehavior.Commit(true);
}
}
finally
{
// Call ReleaseCapture(true) at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
ReleaseCapture(true);
}
}
}
///
/// InkCanvas.LostStylusCapture handler
///
///
///
private void OnInkCanvasLostDeviceCapture(object sender, TEventArgs args)
where TEventArgs : InputEventArgs
{
// If user is editing, we have to commit the current operation and reset our state.
if ( UserIsEditing )
{
// Note ReleaseCapture(false) won't raise any external events. It only reset the internal states.
ReleaseCapture(false);
if ( ActiveEditingBehavior == InkCollectionBehavior && _inkCanvas.InternalDynamicRenderer != null )
{
// NTRAID#WINDOWS-1378904-2005/11/17-WAYNEZEN
// When InkCanvas loses the current capture, we should stop the RTI since the App thread are no longer collecting ink.
// By flipping the Enabled property, the RTI will be reset.
_inkCanvas.InternalDynamicRenderer.Enabled = false;
_inkCanvas.InternalDynamicRenderer.Enabled = true;
}
// Call ActiveEditingBehavior.Commit at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
ActiveEditingBehavior.Commit(true);
}
}
///
/// Initialize the capture state
///
///
///
///
///
///
/// Critical: Returns critical data from the inputDevice, StylusPointCollection.
///
[SecurityCritical]
private void InitializeCapture(InputDevice inputDevice, IStylusEditing stylusEditingBehavior, bool userInitiated, bool resetDynamicRenderer)
{
Debug.Assert(inputDevice != null, "A null device is passed in.");
Debug.Assert(stylusEditingBehavior != null, "stylusEditingBehavior cannot be null.");
Debug.Assert(!IsInMidStroke, "The previous device hasn't been released yet.");
StylusPointCollection stylusPoints;
_capturedStylus = inputDevice as StylusDevice;
_capturedMouse = inputDevice as MouseDevice;
// NOTICE-2005/09/15-WAYNEZEN,
// We assume that the StylusDown always happens before the MouseDown. So, we could safely add the listeners of
// XXXMove and XXXUp as the below which branchs out the coming mouse or stylus events.
if ( _capturedStylus != null )
{
StylusPointCollection newPoints = _capturedStylus.GetStylusPoints(_inkCanvas);
_commonDescription =
StylusPointDescription.GetCommonDescription(_inkCanvas.DefaultStylusPointDescription,
newPoints.Description);
stylusPoints = newPoints.Reformat(_commonDescription);
if ( resetDynamicRenderer )
{
// Reset the dynamic renderer for InkCollectionBehavior
InkCollectionBehavior inkCollectionBehavior = stylusEditingBehavior as InkCollectionBehavior;
if ( inkCollectionBehavior != null )
{
inkCollectionBehavior.ResetDynamicRenderer();
}
}
stylusEditingBehavior.AddStylusPoints(stylusPoints, userInitiated);
// If the current down device is stylus, we should only listen to StylusMove, StylusUp and LostStylusCapture
// events.
_inkCanvas.CaptureStylus();
if ( _inkCanvas.IsStylusCaptured && ActiveEditingMode != InkCanvasEditingMode.None )
{
_inkCanvas.AddHandler(Stylus.StylusMoveEvent, new StylusEventHandler(OnInkCanvasDeviceMove));
_inkCanvas.AddHandler(UIElement.LostStylusCaptureEvent, new StylusEventHandler(OnInkCanvasLostDeviceCapture));
}
else
{
_capturedStylus = null;
}
}
else
{
Debug.Assert(!resetDynamicRenderer, "The dynamic renderer shouldn't be reset for Mouse");
_commonDescription = null;
Point[] points = new Point[] { _capturedMouse.GetPosition(_inkCanvas) };
stylusPoints = new StylusPointCollection(points);
stylusEditingBehavior.AddStylusPoints(stylusPoints, userInitiated);
// NTRAID:WINDOWSOS#1536974-2006/03/02-WAYNEZEN,
// CaptureMouse triggers MouseDevice.Synchronize which sends a simulated MouseMove event.
// So we have to call CaptureMouse after at the end of the initialization.
// Otherwise, the MouseMove handlers will be executed in the middle of the initialization.
_inkCanvas.CaptureMouse();
// The user code could change mode or call release capture when the simulated Move event is received.
// So we have to check the capture still is on and the mode is correct before hooking up our handlers.
// If the current down device is mouse, we should only listen to MouseMove, MouseUp and LostMouseCapture
// events.
if ( _inkCanvas.IsMouseCaptured && ActiveEditingMode != InkCanvasEditingMode.None )
{
_inkCanvas.AddHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnInkCanvasDeviceMove));
_inkCanvas.AddHandler(UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnInkCanvasLostDeviceCapture));
}
else
{
_capturedMouse = null;
}
}
}
///
/// Clean up the capture state
///
///
private void ReleaseCapture(bool releaseDevice)
{
Debug.Assert(IsInMidStroke || !releaseDevice, "The captured device has been release unexpectly.");
if ( _capturedStylus != null )
{
// The Stylus was captured. Remove the stylus listeners.
_commonDescription = null;
_inkCanvas.RemoveHandler(Stylus.StylusMoveEvent, new StylusEventHandler(OnInkCanvasDeviceMove));
_inkCanvas.RemoveHandler(UIElement.LostStylusCaptureEvent, new StylusEventHandler(OnInkCanvasLostDeviceCapture));
_capturedStylus = null;
if ( releaseDevice )
{
// Call ReleaseStylusCapture at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
_inkCanvas.ReleaseStylusCapture();
}
}
else if ( _capturedMouse != null )
{
// The Mouse was captured. Remove the mouse listeners.
_inkCanvas.RemoveHandler(Mouse.MouseMoveEvent, new MouseEventHandler(OnInkCanvasDeviceMove));
_inkCanvas.RemoveHandler(UIElement.LostMouseCaptureEvent, new MouseEventHandler(OnInkCanvasLostDeviceCapture));
_capturedMouse = null;
if ( releaseDevice )
{
// Call ReleaseMouseCapture at the end of the routine. The method will cause an external event fired.
// So it should be invoked after we set up our states.
_inkCanvas.ReleaseMouseCapture();
}
}
}
///
/// Retrieve whether a specified device is captured by us.
///
///
///
private bool IsInputDeviceCaptured(InputDevice inputDevice)
{
Debug.Assert(_capturedStylus == null || _capturedMouse == null, "InkCanvas cannot capture both stylus and mouse at the same time.");
return (inputDevice == _capturedStylus && ((StylusDevice)inputDevice).Captured == _inkCanvas)
|| (inputDevice == _capturedMouse && ( (MouseDevice)inputDevice).Captured == _inkCanvas);
}
///
/// Return the cursor of the active behavior
///
///
internal Cursor GetActiveBehaviorCursor()
{
EditingBehavior behavior = ActiveEditingBehavior;
if ( behavior == null )
{
// Return the default Arrow cursor for the None mode.
return Cursors.Arrow;
}
// Retrieve the cursor.
Cursor cursor = behavior.Cursor;
// Clean up the dirty flag when it's done.
if ( !GetCursorValid(behavior) )
{
SetCursorValid(behavior, true);
}
return cursor;
}
///
/// A private method which returns the valid flag of behaviors' cursor.
///
///
///
private bool GetCursorValid(EditingBehavior behavior)
{
BehaviorValidFlag flag = GetBehaviorCursorFlag(behavior);
return GetBitFlag(flag);
}
///
/// A private method which sets/resets the valid flag of behaviors' cursor
///
///
///
private void SetCursorValid(EditingBehavior behavior, bool isValid)
{
BehaviorValidFlag flag = GetBehaviorCursorFlag(behavior);
SetBitFlag(flag, isValid);
}
///
/// A private method which returns the valid flag of behaviors' cursor.
///
///
///
private bool GetTransformValid(EditingBehavior behavior)
{
BehaviorValidFlag flag = GetBehaviorTransformFlag(behavior);
return GetBitFlag(flag);
}
///
/// A private method which sets/resets the valid flag of behaviors' cursor
///
///
///
private void SetTransformValid(EditingBehavior behavior, bool isValid)
{
BehaviorValidFlag flag = GetBehaviorTransformFlag(behavior);
SetBitFlag(flag, isValid);
}
///
/// GetBitFlag
///
///
///
private bool GetBitFlag(BehaviorValidFlag flag)
{
return (_behaviorValidFlag & flag) != 0;
}
///
/// SetBitFlag
///
///
///
private void SetBitFlag(BehaviorValidFlag flag, bool value)
{
if ( value )
{
_behaviorValidFlag |= flag;
}
else
{
_behaviorValidFlag &= (~flag);
}
}
///
/// A helper returns behavior cursor flag from a behavior instance
///
///
///
private BehaviorValidFlag GetBehaviorCursorFlag(EditingBehavior behavior)
{
BehaviorValidFlag flag = 0;
if ( behavior == InkCollectionBehavior )
{
flag = BehaviorValidFlag.InkCollectionBehaviorCursorValid;
}
else if ( behavior == EraserBehavior )
{
flag = BehaviorValidFlag.EraserBehaviorCursorValid;
}
else if ( behavior == LassoSelectionBehavior )
{
flag = BehaviorValidFlag.LassoSelectionBehaviorCursorValid;
}
else if ( behavior == SelectionEditingBehavior )
{
flag = BehaviorValidFlag.SelectionEditingBehaviorCursorValid;
}
else if ( behavior == SelectionEditor )
{
flag = BehaviorValidFlag.SelectionEditorCursorValid;
}
else
{
Debug.Assert(false, "Unknown behavior");
}
return flag;
}
///
/// A helper returns behavior transform flag from a behavior instance
///
///
///
private BehaviorValidFlag GetBehaviorTransformFlag(EditingBehavior behavior)
{
BehaviorValidFlag flag = 0;
if ( behavior == InkCollectionBehavior )
{
flag = BehaviorValidFlag.InkCollectionBehaviorTransformValid;
}
else if ( behavior == EraserBehavior )
{
flag = BehaviorValidFlag.EraserBehaviorTransformValid;
}
else if ( behavior == LassoSelectionBehavior )
{
flag = BehaviorValidFlag.LassoSelectionBehaviorTransformValid;
}
else if ( behavior == SelectionEditingBehavior )
{
flag = BehaviorValidFlag.SelectionEditingBehaviorTransformValid;
}
else if ( behavior == SelectionEditor )
{
flag = BehaviorValidFlag.SelectionEditorTransformValid;
}
else
{
Debug.Assert(false, "Unknown behavior");
}
return flag;
}
private void ChangeEditingBehavior(EditingBehavior newBehavior)
{
Debug.Assert(!IsInMidStroke, "ChangeEditingBehavior cannot be called in a mid-stroke");
Debug.Assert(_activationStack.Count <= 1, "The behavior stack has to contain at most one behavior when user is not editing.");
try
{
_inkCanvas.ClearSelection(true);
}
finally
{
if ( ActiveEditingBehavior != null )
{
// Pop the old behavior.
PopEditingBehavior();
}
// Push the new behavior
if ( newBehavior != null )
{
PushEditingBehavior(newBehavior);
}
_inkCanvas.RaiseActiveEditingModeChanged(new RoutedEventArgs(InkCanvas.ActiveEditingModeChangedEvent, _inkCanvas));
}
}
///
/// Update the Inverted state
///
///
///
/// true if the behavior is updated
private bool UpdateInvertedState(StylusDevice stylusDevice, bool stylusIsInverted)
{
if ( !IsInMidStroke ||
( IsInMidStroke && IsInputDeviceCaptured(stylusDevice) ))
{
if ( stylusIsInverted != _stylusIsInverted )
{
_stylusIsInverted = stylusIsInverted;
//
// this can change editing state
//
UpdateActiveEditingState();
return true;
}
}
return false;
}
#endregion Private Methods
//-------------------------------------------------------------------------------
//
// Private Properties
//
//-------------------------------------------------------------------------------
#region Private Properties
///
/// Gets the top level active EditingBehavior
///
///
private EditingBehavior ActiveEditingBehavior
{
get
{
EditingBehavior EditingBehavior = null;
if ( _activationStack.Count > 0 )
{
EditingBehavior = _activationStack.Peek();
}
return EditingBehavior;
}
}
///
/// Lazy init access to the InkCollectionBehavior
///
///
internal InkCollectionBehavior InkCollectionBehavior
{
get
{
if ( _inkCollectionBehavior == null )
{
_inkCollectionBehavior = new InkCollectionBehavior(this, _inkCanvas);
}
return _inkCollectionBehavior;
}
}
///
/// Lazy init access to the EraserBehavior
///
///
private EraserBehavior EraserBehavior
{
get
{
if ( _eraserBehavior == null )
{
_eraserBehavior = new EraserBehavior(this, _inkCanvas);
}
return _eraserBehavior;
}
}
#endregion Private Properties
//-------------------------------------------------------------------------------
//
// Private Enum
//
//--------------------------------------------------------------------------------
#region Private Enum
///
/// Enum values which represent the cursor valid flag for each EditingBehavior.
///
[Flags]
private enum BehaviorValidFlag
{
InkCollectionBehaviorCursorValid = 0x00000001,
EraserBehaviorCursorValid = 0x00000002,
LassoSelectionBehaviorCursorValid = 0x00000004,
SelectionEditingBehaviorCursorValid = 0x00000008,
SelectionEditorCursorValid = 0x00000010,
InkCollectionBehaviorTransformValid = 0x00000020,
EraserBehaviorTransformValid = 0x00000040,
LassoSelectionBehaviorTransformValid = 0x00000080,
SelectionEditingBehaviorTransformValid= 0x00000100,
SelectionEditorTransformValid = 0x00000200,
}
#endregion Private Enum
//-------------------------------------------------------------------------------
//
// Private Fields
//
//--------------------------------------------------------------------------------
#region Private Fields
///
/// The InkCanvas this EditingStack is being used in
///
private InkCanvas _inkCanvas;
private Stack _activationStack;
private InkCollectionBehavior _inkCollectionBehavior;
private EraserBehavior _eraserBehavior;
private LassoSelectionBehavior _lassoSelectionBehavior;
private SelectionEditingBehavior _selectionEditingBehavior;
private SelectionEditor _selectionEditor;
private bool _moveEnabled = true;
private bool _resizeEnabled = true;
private bool _userIsEditing = false;
///
/// Flag that indicates if the stylus is inverted
///
private bool _stylusIsInverted = false;
private StylusPointDescription _commonDescription;
private StylusDevice _capturedStylus;
private MouseDevice _capturedMouse;
// Fields related to cursor and transform.
private BehaviorValidFlag _behaviorValidFlag;
#endregion Private Fields
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DataControlPagerLinkButton.cs
- CommandHelpers.cs
- _CacheStreams.cs
- EventMappingSettings.cs
- FloatUtil.cs
- TextBoxBase.cs
- DataServiceResponse.cs
- DPCustomTypeDescriptor.cs
- ExpandCollapsePatternIdentifiers.cs
- DataObjectMethodAttribute.cs
- DesignerActionUIService.cs
- Menu.cs
- XmlDeclaration.cs
- SchemaEntity.cs
- RelationshipNavigation.cs
- DataGridViewRowsRemovedEventArgs.cs
- AutomationPatternInfo.cs
- GridPattern.cs
- TreeViewHitTestInfo.cs
- XsltConvert.cs
- LambdaCompiler.Logical.cs
- RetrieveVirtualItemEventArgs.cs
- TaiwanLunisolarCalendar.cs
- SqlRowUpdatingEvent.cs
- SqlDataSourceQueryEditor.cs
- EncoderNLS.cs
- PropertyGrid.cs
- VirtualDirectoryMapping.cs
- MatrixAnimationUsingKeyFrames.cs
- DisplayInformation.cs
- BulletChrome.cs
- WebException.cs
- ConstraintStruct.cs
- DbProviderFactory.cs
- ImmutableDispatchRuntime.cs
- DispatchWrapper.cs
- FixedElement.cs
- ParserContext.cs
- ClientConfigPaths.cs
- ToolStripHighContrastRenderer.cs
- MULTI_QI.cs
- _ListenerResponseStream.cs
- PageThemeCodeDomTreeGenerator.cs
- TimersDescriptionAttribute.cs
- WindowsTokenRoleProvider.cs
- ButtonBaseAutomationPeer.cs
- RootBrowserWindow.cs
- XmlSchemaRedefine.cs
- ActivityTrace.cs
- ActiveXSite.cs
- AttributedMetaModel.cs
- IntSecurity.cs
- precedingquery.cs
- TypeConverterAttribute.cs
- UriTemplateDispatchFormatter.cs
- WeakReferenceKey.cs
- RenderOptions.cs
- BinaryCommonClasses.cs
- NonceToken.cs
- RotationValidation.cs
- EvidenceBase.cs
- MultipleViewProviderWrapper.cs
- Span.cs
- OracleCommandBuilder.cs
- AssemblyBuilder.cs
- GatewayDefinition.cs
- StringComparer.cs
- ActiveXSite.cs
- SiteMap.cs
- ProxyWebPartManager.cs
- NativeMethods.cs
- DockAndAnchorLayout.cs
- WinEventHandler.cs
- XslTransformFileEditor.cs
- ResourceAttributes.cs
- CompiledRegexRunner.cs
- LineServices.cs
- BufferedOutputStream.cs
- PagesChangedEventArgs.cs
- AutoSizeToolBoxItem.cs
- CrossAppDomainChannel.cs
- HtmlString.cs
- MemoryFailPoint.cs
- XmlCodeExporter.cs
- PersonalizationProviderCollection.cs
- ParagraphResult.cs
- StyleSheetRefUrlEditor.cs
- ForceCopyBuildProvider.cs
- rsa.cs
- TreeViewImageKeyConverter.cs
- KerberosRequestorSecurityTokenAuthenticator.cs
- SeekableReadStream.cs
- GlyphInfoList.cs
- BufferedWebEventProvider.cs
- TimeSpan.cs
- tooltip.cs
- ThrowHelper.cs
- StateBag.cs
- Debug.cs
- DomainConstraint.cs