protected virtual void HandleInkAdded(object sender, StrokesEventArgs e) { Rectangle invalid = Rectangle.Empty; // HACK: It would be much simpler and more efficient to just use // Ink.CreateStrokes(e.StrokeIds).GetBoundingBox(), but CreateStrokes has // a tendency to throw "ComException HRESULT 0x80040222" (see Bug 726). // This hack manually iterates through the strokes looking for the added ones; // there doesn't seem to be any way to make this more efficient with a table // lookup, at least not with the APIs exposed by Microsoft.Ink.Ink. using (Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { foreach (Stroke stroke in this.m_Sheet.Ink.Strokes) { if (Array.IndexOf <int>(e.StrokeIds, stroke.Id) >= 0) { invalid = (invalid == Rectangle.Empty) ? stroke.GetBoundingBox() : Rectangle.Union(invalid, stroke.GetBoundingBox()); } } } this.InvalidateInkSpaceRectangle(invalid); }
private IEnumerable <IEnumerable <Point> > GetSignatureStrokes() { var args = new StrokesEventArgs(); StrokesRequested?.Invoke(this, args); return(args.Strokes); }
protected override bool UpdateTarget(ReceiveContext context) { bool update = base.UpdateTarget(context); InkSheetModel sheet = this.Target as InkSheetModel; if (sheet == null) { return(update); } ArrayList deleting = new ArrayList(this.StrokeIds.Length); foreach (Stroke existing in sheet.Ink.Strokes) { if (existing.ExtendedProperties.DoesPropertyExist(StrokeIdExtendedProperty)) { if (Array.IndexOf(this.StrokeIds, existing.ExtendedProperties[StrokeIdExtendedProperty].Data) >= 0) { deleting.Add(existing.Id); } } } int[] ids = ((int[])deleting.ToArray(typeof(int))); using (Strokes strokes = sheet.Ink.CreateStrokes(ids)) { StrokesEventArgs args = new StrokesEventArgs(ids); sheet.OnInkDeleting(args); sheet.Ink.DeleteStrokes(strokes); sheet.OnInkDeleted(args); } return(update); }
private void HandleInkDeleting(object sender, StrokesEventArgs e) { // We must extract the deleted ink immediately, or else it won't be around // by the time we process the event on another thread. // Compile a list of all of the Guids which identify the strokes to be removed. // The Guids are created by HandleInkAdded above. // If a stroke does not have a Guid, then it was not broadcast and therefore // we don't need to worry about telling the remote clients to remove it. string[] ids; using (Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { int[] strokeIds = e.StrokeIds; ArrayList guids = new ArrayList(strokeIds.Length); using (Strokes strokes = this.m_Sheet.Ink.CreateStrokes(strokeIds)) { foreach (Stroke stroke in strokes) { if (stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) { guids.Add(stroke.ExtendedProperties[InkSheetMessage.StrokeIdExtendedProperty].Data); } } } ids = ((string[])guids.ToArray(typeof(string))); } if (ids.Length > 0) { this.Sender.Post(delegate() { this.HandleInkDeletingHelper(ids); }); } }
private void HandleInkAdded(object sender, StrokesEventArgs e) { // We must extract the added ink immediately so it can't be deleted before the // message is sent on another thread. Ink extracted; using (Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { using (Strokes strokes = this.m_Sheet.Ink.CreateStrokes(e.StrokeIds)) { // Ensure that each stroke has a Guid which will uniquely identify it on the remote side. foreach (Stroke stroke in strokes) { if (!stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) { stroke.ExtendedProperties.Add(InkSheetMessage.StrokeIdExtendedProperty, Guid.NewGuid().ToString()); } } extracted = this.m_Sheet.Ink.ExtractStrokes(strokes, ExtractFlags.CopyFromOriginal); } } this.Sender.Post(delegate() { this.HandleInkAddedHelper(extracted, Group.AllParticipant); }); }
private int[] neweststrokeids; // Note: Ink.AddStrokesAtRectangle doesn't return // the new stroke ids, so we must get them ourselves, from the InkAdded event. private void inkoverlay_Ink_InkAdded(object sender, StrokesEventArgs e) { // This event fires on a background thread, which creates a race condition // when you access neweststrokeids. // Return to the UI thread before continuing. if (this.InvokeRequired) { this.Invoke( new StrokesEventHandler(inkoverlay_Ink_InkAdded), new object[] { sender, e }); return; } dbg.WriteLine("----- inkoverlay_Ink_InkAdded -----"); try // To prevent exceptions from propagating back to ink runtime. { neweststrokeids = e.StrokeIds; } catch (Exception ex) { // Log the error. Global.HandleThreadException(this, new System.Threading.ThreadExceptionEventArgs(ex)); } }
private void LoadInkIntoTarget(InkSheetModel sheet, Ink extracted, out int[] ids) { Ink restored = extracted; using (Synchronizer.Lock(sheet.Ink.Strokes.SyncRoot)) { ids = new int[restored.Strokes.Count]; for (int i = 0; i < ids.Length; i++) { Stroke stroke = restored.Strokes[i]; // Remove any strokes that have the same remote Id as the new one. // Unfortunately, because the InkSheetUndoService cannot preserve stroke referential identity, // we must do a full search each time and cannot simply keep a table. if (stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) { object id = stroke.ExtendedProperties[InkSheetMessage.StrokeIdExtendedProperty].Data; foreach (Stroke existing in sheet.Ink.Strokes) { if (existing.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) { if (id.Equals(existing.ExtendedProperties[InkSheetMessage.StrokeIdExtendedProperty].Data)) { StrokesEventArgs args = new StrokesEventArgs(new int[] { existing.Id }); sheet.OnInkDeleting(args); sheet.Ink.DeleteStroke(existing); sheet.OnInkDeleted(args); } } } } // The stroke has no association with the current Ink object. // Therefore, we have to recreate it by copying the raw packet data. // This first requires recreating the TabletPropertyDescriptionCollection, // which, for some stupid reason, must be done manually for lack of a better API. TabletPropertyDescriptionCollection properties = new TabletPropertyDescriptionCollection(); foreach (Guid property in stroke.PacketDescription) { properties.Add(new TabletPropertyDescription(property, stroke.GetPacketDescriptionPropertyMetrics(property))); } // Create a new stroke from the raw packet data. Stroke created = sheet.Ink.CreateStroke(stroke.GetPacketData(), properties); // Copy the DrawingAttributes and all application data // (especially the StrokesIdExtendedProperty) to the new stroke. created.DrawingAttributes = stroke.DrawingAttributes; foreach (ExtendedProperty prop in stroke.ExtendedProperties) { created.ExtendedProperties.Add(prop.Id, prop.Data); } ids[i] = created.Id; } } }
/// <summary> /// Handle the button being clicked /// </summary> /// <param name="args">The event args</param> protected override void OnClick(EventArgs args) { if (this.m_Slide == null) { return; } using (Synchronizer.Lock(this.m_Slide.SyncRoot)) { InkSheetModel inks = null; // TODO: This code is duplicated in InkSheetAdapter. Extract to a "ActiveInkAnnotationSheet" property of the SlideModel. // Find the *top-most* *local* InkSheetModel in the annotation layer. for (int i = m_Slide.AnnotationSheets.Count - 1; i >= 0; i--) { SheetModel sheet = m_Slide.AnnotationSheets[i]; if ((sheet.Disposition & SheetDisposition.Remote) != 0) { continue; } if (sheet is InkSheetModel) { inks = ((InkSheetModel)sheet); } //else if (sheet is EditableSheetModel ) { // ///only erase the sheet model if the user made it // ///(i.e. it is editable. If this is sent from another // ///person, don't erase it). // EditableSheetModel edit_sheet = (EditableSheetModel)sheet; // if (edit_sheet.IsEditable) { // m_Slide.AnnotationSheets.Remove(sheet); // } //} } if (inks != null) { using (Synchronizer.Lock(inks.SyncRoot)) { Strokes strokes = inks.Ink.Strokes; using (Synchronizer.Lock(strokes.SyncRoot)) { int[] ids = new int[inks.Ink.Strokes.Count]; for (int j = 0; j < strokes.Count; j++) { ids[j] = strokes[j].Id; } StrokesEventArgs sea = new StrokesEventArgs(ids); inks.OnInkDeleting(sea); inks.Ink.DeleteStrokes(); inks.OnInkDeleted(sea); } } } } base.OnClick(args); }
private void AddInk() { using (Synchronizer.Lock(this)) { // Create an array of stroke Ids in order to fire the InkAdded event later. int[] ids = new int[this.m_StrokesToAdd.Count]; using (Synchronizer.Lock(this.m_Watcher.m_InkSheet.Ink.Strokes.SyncRoot)) { for (int i = 0; i < ids.Length; i++) { Stroke stroke = this.m_StrokesToAdd[i]; // The stroke probably has no association with the current Ink object. // Therefore, we have to recreate it by copying the raw packet data. // This first requires recreating the TabletPropertyDescriptionCollection, // which, for some stupid reason, must be done manually for lack of a better API. TabletPropertyDescriptionCollection properties = new TabletPropertyDescriptionCollection(); foreach (Guid property in stroke.PacketDescription) { properties.Add(new TabletPropertyDescription(property, stroke.GetPacketDescriptionPropertyMetrics(property))); } // Create a new stroke from the raw packet data. Stroke created = this.m_Watcher.m_InkSheet.Ink.CreateStroke(stroke.GetPacketData(), properties); // Copy the DrawingAttributes and all application data // (especially the StrokesIdExtendedProperty) to the new stroke. created.DrawingAttributes = stroke.DrawingAttributes; foreach (ExtendedProperty prop in stroke.ExtendedProperties) { created.ExtendedProperties.Add(prop.Id, prop.Data); } // Get the new stroke's Id so we can fire the InkAdded event. ids[i] = created.Id; } // If the added strokes don't yet have StrokeIdExtendedProperty properties, // create new Guids for them. Regardless, set this.m_StrokesToRemove // to the list of stroke Guids. this.UpdateAndSetStrokesToRemoveIds(this.m_StrokesToAdd); // Then, unset this.m_StrokesToAdd since they're already added. this.m_StrokesToAdd = null; } // Create the event arguments and add them to the ignore list so the // InkSheetUndoService won't create an InkUndoer for this change. StrokesEventArgs args = new StrokesEventArgs(ids); using (Synchronizer.Lock(this.m_Watcher.m_Ignore.SyncRoot)) { this.m_Watcher.m_Ignore.Add(args); } // Finally fire the appropriate InkAdded event from the InkSheetModel. this.m_Watcher.m_InkSheet.OnInkAdded(args); } }
public void OnInkDeleted(StrokesEventArgs args) { // The default event add/remove methods lock(this), so we need to do the same to make sure the delegates don't change. using (Synchronizer.Lock(this)) { if (this.m_InkDeletedDelegate != null) { this.m_InkDeletedDelegate(this, args); } } }
private void RemoveInk() { using (Synchronizer.Lock(this)) { // Collect all of the strokes we're supposed to delete. using (Synchronizer.Lock(this.m_Watcher.m_InkSheet.Ink.Strokes.SyncRoot)) { Strokes deleting = this.m_Watcher.m_InkSheet.Ink.CreateStrokes(); foreach (Stroke stroke in this.m_Watcher.m_InkSheet.Ink.Strokes) { if (stroke.ExtendedProperties.DoesPropertyExist(StrokeIdExtendedProperty)) { if (Array.IndexOf(this.m_StrokesToRemove, stroke.ExtendedProperties[StrokeIdExtendedProperty].Data) >= 0) { deleting.Add(stroke); } } } // It's possible that some of the strokes have been deleted elsewhere. // But this shouldn't happen because doing so should have caused an InkUndoer to be // pushed onto the Undo stack. So, for now, this check is "merely" a Debug.Assert. // TODO: Decide whether this should be an error, or at least whether it should // invalidate the rest of the undo stack. Debug.Assert(deleting.Count == this.m_StrokesToRemove.Length); // Get the stroke Ids so we can make a copy of the ink and fire the OnDeleting and OnDeleted events. int[] ids = new int[deleting.Count]; for (int i = 0; i < ids.Length; i++) // Is there a better way to get the array of Ids? { ids[i] = deleting[i].Id; } // Make a copy of the strokes, because if the original ink is later deleted // from the Ink object then it will become unusable. Ink ink = this.m_Watcher.m_InkSheet.Ink.ExtractStrokes(deleting, ExtractFlags.CopyFromOriginal); this.m_StrokesToAdd = ink.Strokes; this.m_StrokesToRemove = null; // Create the event arguments and add them to the ignore list so the // InkSheetUndoService won't create an InkUndoer for this change. StrokesEventArgs args = new StrokesEventArgs(ids); using (Synchronizer.Lock(this.m_Watcher.m_Ignore.SyncRoot)) { this.m_Watcher.m_Ignore.Add(args); } // Actually delete the ink, firing the appropriate events on the InkSheetModel. this.m_Watcher.m_InkSheet.OnInkDeleting(args); this.m_Watcher.m_InkSheet.Ink.DeleteStrokes(deleting); this.m_Watcher.m_InkSheet.OnInkDeleted(args); } } }
private void HandleInkAdded(object sender, StrokesEventArgs e) { using(Synchronizer.Lock(this.m_Ignore.SyncRoot)) { if(this.m_Ignore.Contains(e)) { this.m_Ignore.Remove(e); return; } } // Make a copy of the strokes that were added. This must be done immediately // or the strokes might not exist by the time we need them. Strokes strokes = this.m_InkSheet.Ink.CreateStrokes(e.StrokeIds); SetStrokeIds(strokes); Ink ink = this.m_InkSheet.Ink.ExtractStrokes(strokes, ExtractFlags.CopyFromOriginal); this.m_EventQueue.Post(delegate() { this.m_HandleInkChangedDelegate(sender, ink.Strokes, true); }); }
private void HandleInkDeleting(object sender, StrokesEventArgs e) { using (Synchronizer.Lock(this.m_Ignore.SyncRoot)) { if (this.m_Ignore.Contains(e)) { this.m_Ignore.Remove(e); return; } } // Make a copy of the strokes that were removed. This must be done immediately // or the strokes _will_not_ exist by the time we need them. Strokes strokes = this.m_InkSheet.Ink.CreateStrokes(e.StrokeIds); SetStrokeIds(strokes); Ink ink = this.m_InkSheet.Ink.ExtractStrokes(strokes, ExtractFlags.CopyFromOriginal); this.m_EventQueue.Post(delegate() { this.m_HandleInkChangedDelegate(sender, ink.Strokes, false); }); }
private void HandleInkAdded(object sender, StrokesEventArgs e) { // We must extract the added ink immediately so it can't be deleted before the // message is sent on another thread. Ink extracted; using( Synchronizer.Lock(this.m_SourceSheet.Ink.Strokes.SyncRoot) ) { using(Strokes strokes = this.m_SourceSheet.Ink.CreateStrokes(e.StrokeIds)) { // Ensure that each stroke has a Guid which will uniquely identify it on the remote side. foreach(Stroke stroke in strokes) { if(!stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) stroke.ExtendedProperties.Add(InkSheetMessage.StrokeIdExtendedProperty, Guid.NewGuid().ToString()); } extracted = this.m_SourceSheet.Ink.ExtractStrokes(strokes, ExtractFlags.CopyFromOriginal); } } this.m_Sender.Post(delegate() { this.HandleInkAddedHelper(extracted); }); }
/* * private void SendExistingInk(Group receivers) { * Ink extracted; * using(Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { * // Ensure that each stroke has a Guid which will uniquely identify it on the remote side. * foreach(Stroke stroke in this.m_Sheet.Ink.Strokes) { * if(!stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) * stroke.ExtendedProperties.Add(InkSheetMessage.StrokeIdExtendedProperty, Guid.NewGuid().ToString()); * } * * // Extract all of the strokes. * extracted = this.m_Sheet.Ink.ExtractStrokes(this.m_Sheet.Ink.Strokes, ExtractFlags.CopyFromOriginal); * } * * // Send a message as if the already-existing ink was just added to the sheet. * this.Sender.Post(delegate() { * this.HandleInkAddedHelper(extracted, receivers); * }); * } */ /// <summary> /// Handle when ink is added to the slide /// </summary> /// <param name="sender">The sender of this event</param> /// <param name="e">The parameters for this event</param> private void HandleInkAdded(object sender, StrokesEventArgs e) { // Always copy all strokes for now... Ink extracted; using (Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { // Ensure that each stroke has a Guid which will uniquely identify it on the remote side. foreach (Stroke stroke in this.m_Sheet.Ink.Strokes) { if (!stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) { stroke.ExtendedProperties.Add(InkSheetMessage.StrokeIdExtendedProperty, Guid.NewGuid().ToString()); } } // Extract all of the strokes. extracted = this.m_Sheet.Ink.ExtractStrokes(this.m_Sheet.Ink.Strokes, ExtractFlags.CopyFromOriginal); } // Send a message as if the already-existing ink was just added to the sheet. this.Sender.Post(delegate() { this.HandleInkAddedHelper(extracted); }); }
protected virtual void HandleInkDeleted(object sender, StrokesEventArgs e) { using (Synchronizer.Lock(this.m_Sheet.SyncRoot)) { this.SlideDisplay.Invalidate(this.m_Sheet.Bounds); } }
/// <summary> /// Handle the button being clicked /// </summary> /// <param name="args">The event args</param> protected override void OnClick( EventArgs args ) { if( this.m_Slide == null ) return; using( Synchronizer.Lock( this.m_Slide.SyncRoot ) ) { InkSheetModel inks = null; // TODO: This code is duplicated in InkSheetAdapter. Extract to a "ActiveInkAnnotationSheet" property of the SlideModel. // Find the *top-most* *local* InkSheetModel in the annotation layer. for (int i = m_Slide.AnnotationSheets.Count - 1; i >= 0; i--) { SheetModel sheet = m_Slide.AnnotationSheets[i]; if ((sheet.Disposition & SheetDisposition.Remote) != 0) continue; if (sheet is InkSheetModel) { inks = ((InkSheetModel)sheet); } //else if (sheet is EditableSheetModel ) { // ///only erase the sheet model if the user made it // ///(i.e. it is editable. If this is sent from another // ///person, don't erase it). // EditableSheetModel edit_sheet = (EditableSheetModel)sheet; // if (edit_sheet.IsEditable) { // m_Slide.AnnotationSheets.Remove(sheet); // } //} } if (inks != null) { using (Synchronizer.Lock(inks.SyncRoot)) { Strokes strokes = inks.Ink.Strokes; using (Synchronizer.Lock(strokes.SyncRoot)) { int[] ids = new int[inks.Ink.Strokes.Count]; for (int j = 0; j < strokes.Count; j++) ids[j] = strokes[j].Id; StrokesEventArgs sea = new StrokesEventArgs(ids); inks.OnInkDeleting(sea); inks.Ink.DeleteStrokes(); inks.OnInkDeleted(sea); } } } } base.OnClick( args ); }
public void OnInkDeleting(StrokesEventArgs args) { // The default event add/remove methods lock(this), so we need to do the same to make sure the delegates don't change. using(Synchronizer.Lock(this)) { if(this.m_InkDeletingDelegate != null) { this.m_InkDeletingDelegate(this, args); } } }
private void inkoverlay_Ink_InkAdded(object sender, StrokesEventArgs e) { // This event fires on a background thread, which creates a race condition // when you access neweststrokeids. // Return to the UI thread before continuing. if (this.InvokeRequired) { this.Invoke( new StrokesEventHandler(inkoverlay_Ink_InkAdded), new object[] { sender, e }); return; } dbg.WriteLine("----- inkoverlay_Ink_InkAdded -----"); try // To prevent exceptions from propagating back to ink runtime. { neweststrokeids = e.StrokeIds; } catch (Exception ex) { // Log the error. Global.HandleThreadException(this, new System.Threading.ThreadExceptionEventArgs(ex)); } }
private void AddInk() { using(Synchronizer.Lock(this)) { // Create an array of stroke Ids in order to fire the InkAdded event later. int[] ids = new int[this.m_StrokesToAdd.Count]; using (Synchronizer.Lock(this.m_Watcher.m_InkSheet.Ink.Strokes.SyncRoot)) { for (int i = 0; i < ids.Length; i++) { Stroke stroke = this.m_StrokesToAdd[i]; // The stroke probably has no association with the current Ink object. // Therefore, we have to recreate it by copying the raw packet data. // This first requires recreating the TabletPropertyDescriptionCollection, // which, for some stupid reason, must be done manually for lack of a better API. TabletPropertyDescriptionCollection properties = new TabletPropertyDescriptionCollection(); foreach (Guid property in stroke.PacketDescription) properties.Add(new TabletPropertyDescription(property, stroke.GetPacketDescriptionPropertyMetrics(property))); // Create a new stroke from the raw packet data. Stroke created = this.m_Watcher.m_InkSheet.Ink.CreateStroke(stroke.GetPacketData(), properties); // Copy the DrawingAttributes and all application data // (especially the StrokesIdExtendedProperty) to the new stroke. created.DrawingAttributes = stroke.DrawingAttributes; foreach (ExtendedProperty prop in stroke.ExtendedProperties) created.ExtendedProperties.Add(prop.Id, prop.Data); // Get the new stroke's Id so we can fire the InkAdded event. ids[i] = created.Id; } // If the added strokes don't yet have StrokeIdExtendedProperty properties, // create new Guids for them. Regardless, set this.m_StrokesToRemove // to the list of stroke Guids. this.UpdateAndSetStrokesToRemoveIds(this.m_StrokesToAdd); // Then, unset this.m_StrokesToAdd since they're already added. this.m_StrokesToAdd = null; } // Create the event arguments and add them to the ignore list so the // InkSheetUndoService won't create an InkUndoer for this change. StrokesEventArgs args = new StrokesEventArgs(ids); using(Synchronizer.Lock(this.m_Watcher.m_Ignore.SyncRoot)) { this.m_Watcher.m_Ignore.Add(args); } // Finally fire the appropriate InkAdded event from the InkSheetModel. this.m_Watcher.m_InkSheet.OnInkAdded(args); } }
protected override bool UpdateTarget(ReceiveContext context) { bool update = base.UpdateTarget(context); InkSheetModel sheet = this.Target as InkSheetModel; if(sheet == null) return update; ArrayList deleting = new ArrayList(this.StrokeIds.Length); foreach(Stroke existing in sheet.Ink.Strokes) { if(existing.ExtendedProperties.DoesPropertyExist(StrokeIdExtendedProperty)) { if(Array.IndexOf(this.StrokeIds, existing.ExtendedProperties[StrokeIdExtendedProperty].Data) >= 0) { deleting.Add(existing.Id); } } } int[] ids = ((int[]) deleting.ToArray(typeof(int))); using(Strokes strokes = sheet.Ink.CreateStrokes(ids)) { StrokesEventArgs args = new StrokesEventArgs(ids); sheet.OnInkDeleting(args); sheet.Ink.DeleteStrokes(strokes); sheet.OnInkDeleted(args); } return update; }
private void LoadInkIntoTarget(InkSheetModel sheet, byte[] saved, out int[] ids) { Ink restored = new Ink(); restored.Load(saved); using(Synchronizer.Lock(sheet.Ink.Strokes.SyncRoot)) { ids = new int[restored.Strokes.Count]; for(int i = 0; i < ids.Length; i++) { Stroke stroke = restored.Strokes[i]; // Remove any strokes that have the same remote Id as the new one. // Unfortunately, because the InkSheetUndoService cannot preserve stroke referential identity, // we must do a full search each time and cannot simply keep a table. if(stroke.ExtendedProperties.DoesPropertyExist(StrokeIdExtendedProperty)) { object id = stroke.ExtendedProperties[StrokeIdExtendedProperty].Data; foreach(Stroke existing in sheet.Ink.Strokes) { if(existing.ExtendedProperties.DoesPropertyExist(StrokeIdExtendedProperty)) { if(id.Equals(existing.ExtendedProperties[StrokeIdExtendedProperty].Data)) { StrokesEventArgs args = new StrokesEventArgs(new int[] { existing.Id }); sheet.OnInkDeleting(args); sheet.Ink.DeleteStroke(existing); sheet.OnInkDeleted(args); } } } } // The stroke has no association with the current Ink object. // Therefore, we have to recreate it by copying the raw packet data. // This first requires recreating the TabletPropertyDescriptionCollection, // which, for some stupid reason, must be done manually for lack of a better API. TabletPropertyDescriptionCollection properties = new TabletPropertyDescriptionCollection(); foreach(Guid property in stroke.PacketDescription) properties.Add(new TabletPropertyDescription(property, stroke.GetPacketDescriptionPropertyMetrics(property))); // Create a new stroke from the raw packet data. Stroke created = sheet.Ink.CreateStroke(stroke.GetPacketData(), properties); // Copy the DrawingAttributes and all application data // (especially the StrokesIdExtendedProperty) to the new stroke. created.DrawingAttributes = stroke.DrawingAttributes; foreach(ExtendedProperty prop in stroke.ExtendedProperties) created.ExtendedProperties.Add(prop.Id, prop.Data); ids[i] = created.Id; if (ViewerStateModel.NonStandardDpi) created.Transform(ViewerStateModel.DpiNormalizationReceiveMatrix); } } }
protected virtual void HandleInkDeleted(object sender, StrokesEventArgs e) { using(Synchronizer.Lock(this.m_Sheet.SyncRoot)) { this.SlideDisplay.Invalidate(this.m_Sheet.Bounds); } }
protected virtual void HandleInkAdded(object sender, StrokesEventArgs e) { Rectangle invalid = Rectangle.Empty; // HACK: It would be much simpler and more efficient to just use // Ink.CreateStrokes(e.StrokeIds).GetBoundingBox(), but CreateStrokes has // a tendency to throw "ComException HRESULT 0x80040222" (see Bug 726). // This hack manually iterates through the strokes looking for the added ones; // there doesn't seem to be any way to make this more efficient with a table // lookup, at least not with the APIs exposed by Microsoft.Ink.Ink. using (Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { foreach (Stroke stroke in this.m_Sheet.Ink.Strokes) { if (Array.IndexOf<int>(e.StrokeIds, stroke.Id) >= 0) { invalid = (invalid == Rectangle.Empty) ? stroke.GetBoundingBox() : Rectangle.Union(invalid, stroke.GetBoundingBox()); } } } this.InvalidateInkSpaceRectangle(invalid); }
private void RemoveInk() { using (Synchronizer.Lock(this)) { // Collect all of the strokes we're supposed to delete. using (Synchronizer.Lock(this.m_Watcher.m_InkSheet.Ink.Strokes.SyncRoot)) { Strokes deleting = this.m_Watcher.m_InkSheet.Ink.CreateStrokes(); foreach (Stroke stroke in this.m_Watcher.m_InkSheet.Ink.Strokes) if (stroke.ExtendedProperties.DoesPropertyExist(StrokeIdExtendedProperty)) if (Array.IndexOf(this.m_StrokesToRemove, stroke.ExtendedProperties[StrokeIdExtendedProperty].Data) >= 0) deleting.Add(stroke); // It's possible that some of the strokes have been deleted elsewhere. // But this shouldn't happen because doing so should have caused an InkUndoer to be // pushed onto the Undo stack. So, for now, this check is "merely" a Debug.Assert. // TODO: Decide whether this should be an error, or at least whether it should // invalidate the rest of the undo stack. Debug.Assert(deleting.Count == this.m_StrokesToRemove.Length); // Get the stroke Ids so we can make a copy of the ink and fire the OnDeleting and OnDeleted events. int[] ids = new int[deleting.Count]; for (int i = 0; i < ids.Length; i++) // Is there a better way to get the array of Ids? ids[i] = deleting[i].Id; // Make a copy of the strokes, because if the original ink is later deleted // from the Ink object then it will become unusable. Ink ink = this.m_Watcher.m_InkSheet.Ink.ExtractStrokes(deleting, ExtractFlags.CopyFromOriginal); this.m_StrokesToAdd = ink.Strokes; this.m_StrokesToRemove = null; // Create the event arguments and add them to the ignore list so the // InkSheetUndoService won't create an InkUndoer for this change. StrokesEventArgs args = new StrokesEventArgs(ids); using (Synchronizer.Lock(this.m_Watcher.m_Ignore.SyncRoot)) { this.m_Watcher.m_Ignore.Add(args); } // Actually delete the ink, firing the appropriate events on the InkSheetModel. this.m_Watcher.m_InkSheet.OnInkDeleting(args); this.m_Watcher.m_InkSheet.Ink.DeleteStrokes(deleting); this.m_Watcher.m_InkSheet.OnInkDeleted(args); } } }
private void HandleInkDeleting(object sender, StrokesEventArgs e) { // We must extract the deleted ink immediately, or else it won't be around // by the time we process the event on another thread. // Compile a list of all of the Guids which identify the strokes to be removed. // The Guids are created by HandleInkAdded above. // If a stroke does not have a Guid, then it was not broadcast and therefore // we don't need to worry about telling the remote clients to remove it. string[] ids; using(Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { int[] strokeIds = e.StrokeIds; ArrayList guids = new ArrayList(strokeIds.Length); using(Strokes strokes = this.m_Sheet.Ink.CreateStrokes(strokeIds)) { foreach(Stroke stroke in strokes) { if(stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) guids.Add(stroke.ExtendedProperties[InkSheetMessage.StrokeIdExtendedProperty].Data); } } ids = ((string[]) guids.ToArray(typeof(string))); } if (ids.Length > 0) this.Sender.Post(delegate() { this.HandleInkDeletingHelper(ids); }); }
/* private void SendExistingInk(Group receivers) { Ink extracted; using(Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { // Ensure that each stroke has a Guid which will uniquely identify it on the remote side. foreach(Stroke stroke in this.m_Sheet.Ink.Strokes) { if(!stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) stroke.ExtendedProperties.Add(InkSheetMessage.StrokeIdExtendedProperty, Guid.NewGuid().ToString()); } // Extract all of the strokes. extracted = this.m_Sheet.Ink.ExtractStrokes(this.m_Sheet.Ink.Strokes, ExtractFlags.CopyFromOriginal); } // Send a message as if the already-existing ink was just added to the sheet. this.Sender.Post(delegate() { this.HandleInkAddedHelper(extracted, receivers); }); } */ /// <summary> /// Handle when ink is added to the slide /// </summary> /// <param name="sender">The sender of this event</param> /// <param name="e">The parameters for this event</param> private void HandleInkAdded(object sender, StrokesEventArgs e) { // Always copy all strokes for now... Ink extracted; using (Synchronizer.Lock(this.m_Sheet.Ink.Strokes.SyncRoot)) { // Ensure that each stroke has a Guid which will uniquely identify it on the remote side. foreach (Stroke stroke in this.m_Sheet.Ink.Strokes) { if (!stroke.ExtendedProperties.DoesPropertyExist(InkSheetMessage.StrokeIdExtendedProperty)) stroke.ExtendedProperties.Add(InkSheetMessage.StrokeIdExtendedProperty, Guid.NewGuid().ToString()); } // Extract all of the strokes. extracted = this.m_Sheet.Ink.ExtractStrokes(this.m_Sheet.Ink.Strokes, ExtractFlags.CopyFromOriginal); } // Send a message as if the already-existing ink was just added to the sheet. this.Sender.Post(delegate() { this.HandleInkAddedHelper(extracted); }); }