TheSwamp

Code Red => .NET => Topic started by: Atook on December 02, 2016, 03:52:51 PM

Title: Trap copy/pasteclip event?
Post by: Atook on December 02, 2016, 03:52:51 PM
I want to trap any copied entities of mine created during copy/pasteclip and strip some xdata from the copied objects. I don’t want to change any entities created by my code.

I can use the Database.ObjectAppended ObjectEventHandler, but that would fire even when my code creates new objects, unless I filter for that as well. I’m leery of adding too much to the database eventhandler for performance reasons. Creating object eventhandlers might work, but it seems like a bad way of doing it.

What’s the best way of doing this? Is there a good way to trap commands such as copy/pasteclip and handle the new entities?
Title: Re: Trap copy/pasteclip event?
Post by: Atook on December 05, 2016, 02:27:45 AM
Here's what I've come up with so far, seems like a weird way of doing it. See any glaring mistakes or a better way executing?

Code - C#: [Select]
  1. public class AIDApplication : IExtensionApplication
  2. {
  3.         private bool filterEntities = false;
  4.  
  5.         public void Initialize()
  6.         {
  7.                 // add document handler to every document we open
  8.                 Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.DocumentActivated += AddDocumentHandlers;
  9.         }
  10.  
  11.         private void callback_ObjectAppended(object sender, ObjectEventArgs e)
  12.         {
  13.                 if (filterEntities && objectsForFilter!=null)
  14.                 {       // add the objects to a collection do deal with when the command ends
  15.                         objectsForFilter.Add(e.DBObject.ObjectId);
  16.                 }
  17.         }
  18.  
  19.         private  void AddDocumentHandlers(object sender, DocumentCollectionEventArgs e)
  20.         {
  21.                 // make sure there's an active document to copyt styles to
  22.                 if (Active.Document == null) return;
  23.                 Active.Document.CommandWillStart += doc_CommandWillStart;
  24.                 Active.Document.CommandEnded += doc_CommandEnded;
  25.                 Active.Database.ObjectAppended += callback_ObjectAppended;
  26.         }
  27.         private void doc_CommandWillStart(object sender, CommandEventArgs e)
  28.         {
  29.                 Debug.WriteLine(string.Format("Command {0} will start.", e.GlobalCommandName));
  30.                 if (e.GlobalCommandName.ToUpper()=="COPY")
  31.                 {
  32.                         filterEntities = true;
  33.                         objectsForFilter = new ObjectIdCollection();
  34.                 }
  35.         }
  36.         void doc_CommandEnded(object sender, CommandEventArgs e)
  37.         {
  38.                 // deal with any objects that need it.
  39.                 if (objectsForFilter == null) return;
  40.                 foreach (ObjectId id in objectsForFilter)
  41.                 {
  42.                         int pipeID = XData.ReadInt(id, AID_AppName.PIPE);
  43.                         if (pipeID >= 0)
  44.                         {
  45.                                 // we have a pipe, maybe do something about it
  46.                                 Debug.WriteLine("Pipe added to database not by our code, fix it");
  47.                                 // strip xData
  48.                                 XData.DeleteXData(id, AID_AppName.LABEL_HANDLE);
  49.  
  50.                                 // set color to bylayer
  51.                                 Utililty.SetColor(id, 256);
  52.                         }
  53.                         string arrowID = XData.ReadString(id, AID_AppName.FLOW_ARROW);
  54.                         if (arrowID.Length > 0)
  55.                         {
  56.                                 // erase it.
  57.                                 Utililty.Erase(id);
  58.                         }
  59.  
  60.                 }
  61.                 objectsForFilter.Clear();
  62.                 filterEntities = false;
  63.                 Debug.WriteLine(string.Format("Command {0} Ended.", e.GlobalCommandName));
  64.         }
  65. }
  66.  
Title: Re: Trap copy/pasteclip event?
Post by: MexicanCustard on December 05, 2016, 01:02:57 PM
Don't forget to loop through any open documents in your initialize method.
Code - C#: [Select]
  1. foreach (var document in Application.DocumentManager.Cast<Document>())
  2. {
  3.     document.CommandWillStart += doc_CommandWillStart;
  4.     document.CommandEnded += doc_CommandEnded;
  5.     //etc.
  6. }
Title: Re: Trap copy/pasteclip event?
Post by: Bobby C. Jones on December 05, 2016, 02:59:11 PM
Probably irrelevant as I imagine your filterEntitties flag is preventing any noticeable overhead, but a pattern I typically use in this situation is to subscribe and unsubscribe to the object appended event in the command start and end events.
Title: Re: Trap copy/pasteclip event?
Post by: Atook on December 06, 2016, 02:56:30 PM
Thanks for the comments.
Bobby, you're right, I had that thought as I was thinking about how to accomplish this task. Any overhead is too much when it comes to reactors, I'm essentially re-writing a COM app that had terrible reactor performance, and I don't want to repeat that mistake.

MC, my code is being loaded via bundle, so the doc collection should be empty when the Initialization code runs. Your comment made me realize that users might possibly not load the bundle and netload later creating problems, better safe than sorry.

As it stands, the updated code looks something like this:
Code - C#: [Select]
  1. public class AIDApplication : IExtensionApplication
  2. {
  3.         public ObjectIdCollection objectsForFilter;
  4.  
  5.  
  6.         public void Initialize()
  7.         {
  8.                 docManager.DocumentCreated += AddDocumentHandlers;
  9.                 docManager.DocumentToBeDestroyed += RemoveDocumentHandlers;
  10.                 // add document handlers to all existing documents (shouldn't happen if we load from bundle)
  11.                 foreach (var doc in docManager.Cast<Document>())
  12.                 {
  13.                         doc.CommandWillStart += doc_CommandWillStart;
  14.                         doc.CommandEnded += doc_CommandEnded;
  15.                 }
  16.         }
  17.  
  18.         /// <summary>
  19.         /// When objects are added to the database, check them and see if they need to be handled
  20.         /// </summary>
  21.         /// <param name="sender">The source of the event.</param>
  22.         /// <param name="e">The <see cref="ObjectEventArgs"/> instance containing the event data.</param>
  23.         private void callback_ObjectAppended(object sender, ObjectEventArgs e)
  24.         {
  25.                 if (objectsForFilter!=null)
  26.                 {
  27.                         objectsForFilter.Add(e.DBObject.ObjectId);
  28.                 }
  29.         }
  30.  
  31.         /// <summary>
  32.         /// Add event handlers to the command events and the objectAppended
  33.         /// </summary>
  34.         /// <param name="sender">The sender.</param>
  35.         /// <param name="e">The <see cref="DocumentCollectionEventArgs"/> instance containing the event data.</param>
  36.         private void AddDocumentHandlers(object sender, DocumentCollectionEventArgs e)
  37.         {
  38.                 if (Active.Document == null) return;
  39.                 Active.Document.CommandWillStart += doc_CommandWillStart;
  40.                 Active.Document.CommandEnded += doc_CommandEnded;
  41.         }
  42.  
  43.         /// <summary>
  44.         /// Removes the document handlers.
  45.         /// </summary>
  46.         /// <param name="sender">The sender.</param>
  47.         /// <param name="e">The <see cref="Autodesk.AutoCAD.ApplicationServices.DocumentCollectionEventArgs" /> instance containing the event data.</param>
  48.         private void RemoveDocumentHandlers(object sender, DocumentCollectionEventArgs e)
  49.         {
  50.                 if (Active.Document == null) return;
  51.                 Active.Document.CommandWillStart -= doc_CommandWillStart;
  52.                 Active.Document.CommandEnded -= doc_CommandEnded;
  53.         }
  54.  
  55.         /// <summary>
  56.         /// Handles the CommandWillStart event of the document.
  57.         /// Checks to see if copy command is starting
  58.         /// </summary>
  59.         /// <param name="sender">The source of the event.</param>
  60.         /// <param name="e">The <see cref="CommandEventArgs"/> instance containing the event data.</param>
  61.         private void doc_CommandWillStart(object sender, CommandEventArgs e)
  62.         {
  63.                 Debug.WriteLine(string.Format("Command {0} will start.", e.GlobalCommandName));
  64.                 if (e.GlobalCommandName.ToUpper()=="COPY" || e.GlobalCommandName.ToUpper()=="PASTECLIP")
  65.                 {
  66.                         objectsForFilter = new ObjectIdCollection();
  67.                         Active.Database.ObjectAppended += callback_ObjectAppended;
  68.                 }
  69.         }
  70.  
  71.         /// <summary>
  72.         /// Command Ended
  73.         /// </summary>
  74.         /// <param name="sender">The source of the event.</param>
  75.         /// <param name="e">The <see cref="CommandEventArgs"/> instance containing the event data.</param>
  76.         void doc_CommandEnded(object sender, CommandEventArgs e)
  77.         {
  78.                 Active.Database.ObjectAppended -= callback_ObjectAppended;
  79.                 if (objectsForFilter == null) return;
  80.                 // if we have objects to check for do cool stuff
  81.                 foreach (ObjectId id in objectsForFilter)
  82.                 {
  83.                         // Do cool stuff
  84.                 }
  85.                 objectsForFilter.Clear();
  86.                 Debug.WriteLine(string.Format("Command {0} Ended.", e.GlobalCommandName));
  87.         }
  88.  
  89. }
  90.  
Title: Re: Trap copy/pasteclip event?
Post by: n.yuan on December 06, 2016, 06:11:09 PM
I'd hook up AddDocumentHandler with DocumentCollection.DocumentCreated event, not DocumentActivated event, because DocumentActivated event can occur many times during the life span of a document, such as each time you display a modal dialog/message box, when the dialog box/message box is dismissed, DocuemtActivated event fires So, your code would add doc_CommandWillStart and doc_CommandEnded event handlers to the database multiple times.
Title: Re: Trap copy/pasteclip event?
Post by: Atook on December 06, 2016, 07:44:05 PM
I'd hook up AddDocumentHandler with DocumentCollection.DocumentCreated event...

Nice catch Yuan, this was actually causing the code to crash, I assume from adding the same handler multiple times, I've updated the code above to show the DocumentCreated event.
Title: Re: Trap copy/pasteclip event?
Post by: huiz on December 13, 2016, 09:45:49 AM
The Clipboard Manager was an ADN Plugin of the Month once:

http://through-the-interface.typepad.com/through_the_interface/2009/09/clipboard-manager-octobers-adn-plugin-of-the-month-now-live-on-autodesk-labs.html

Sourcecode is available too somewhere, I might have a copy probably if you need. It's VB.NET unfortunately.