Monitor image changes (history state changes)

Photoshop Script Snippets - Note: Full Scripts go in the Photoshop Scripts Forum

Moderators: Tom, Kukurykus

dbosst

Monitor image changes (history state changes)

Post by dbosst »

"DEAR PEOPLE FROM THE FUTURE: Here's what we've figured out so far ..."

The below has information relevant to the CS 5 SDK's CSAW library and the Photoshop CS5 SDK's CSXS library.

1) More about CSAW internals:
When you retrieve an object using CSAW library (ie Photoshop.app.activeHistoryState), it will not be the same object reference if it appears anywhere else (or if you retrieve it a second time for instance).
This is because CSAW creates a new object of the corresponding class, and assigns it the hostObjectDelegate property -- it is this property (object) that appears to give its parent object all of its other properties.
When you retrieve a certain object's property, the object itself does not have that property stored, and instead you are retrieving that property via a get function that uses the hostObjectDelegate as its identifier.
In this sense, for each object, its hostObjectDelegate property is a unique reference, and so when you cannot compare objects using their properties (and since you cannot compare using their references), you can compare them using their HostObjectDelegate property.

(Unfortunately, it took decompiling the csaw photoshop library and examining the source code to figure out this all out)

2) About the example:

The example below monitors (using a timer) changes to an object's (activeHistoryState) hostObjectDelegate to identify whenever the user makes a change to the image state. This cannot be done using CSXS (events & ExternalInterface) since there is no event for when using paint/brush/etc on the canvas. In using a timer you must use hostObjectDelegate object since a repeated historyState with the same tool name will make you unable to find their location in the historyStates collection.

The example also monitors changes made to when the user switches the active document -- doing this with CSXS (events and the External interface) doesn't require a timer, but its much more complicated handling all multiple cases, i.e. the events close, select, make(new document).

3) The example code:
Code: Select all      //// Example: Using a timer to monitor changes to image state (history state), and the active photoshop document
      //// Important point: When using Photoshop objects, compare by reference using the HostObjectDelegate property object,
      ////   i.e., use == operator on their HostObjectDelegate properties to determine if they are the same object.

      // create a monitoring timer, since there is no event for monitoring all image state changes
      public var docmonitoringTimer:Timer = new Timer(250);
   
      // add this to your creationComplete handler
      protected function creationComplete(event:FlexEvent):void {
      
         docmonitoringTimer.addEventListener(TimerEvent.TIMER, docmonitoringTimerHandler);
         docmonitoringTimer.start();
      }


      // document and history state have property hostObjectDelegate, which are unique objects (HostObject type)
      internal var dlastHOD:HostObject;
      internal var hlastHOD:HostObject;
      
      public function docmonitoringTimerHandler(event:TimerEvent):void {
         if (Photoshop.app.activeDocument == null) {
            // no active document
            if (hlastHOD != null) {
               // closed last open document
               
               hlastHOD = null;
            } else {
               // started photoshop
            }
         } else {
            var doc:Document = Photoshop.app.activeDocument;
   
            if (dlastHOD != null && dlastHOD != doc.hostObjectDelegate) {
               // active document has changed ( a different document was selected and is now active)
               // multiple built in events cover this action: make new document, open existing document, close document (and switch to another document)
            } else {
               if (hlastHOD != null && hlastHOD != doc.activeHistoryState.hostObjectDelegate) {
                  // active history state has changed ( implying the image state has changed )
               }
            }
            dlastHOD = doc.hostObjectDelegate;
            hlastHOD = doc.activeHistoryState.hostObjectDelegate;
         }
      }
      
      
      // some extra (not used) example helper functions
      // useful if you want to find the index of a document or history state in their associated collection (Documents or HistoryStates)
      
      protected function getDocIndex(d:Document):Number {
         var index:Number;
         var hasdoc:Boolean = false;
         var docs:Documents = Photoshop.app.documents;
         try {
            for (index = 0; index < docs.length; index++) {
               if (docs.index(index).hostObjectDelegate == d.hostObjectDelegate) {
                  hasdoc = true;
                  break;
               }
            }
         } catch(err:Error) {}
         if (hasdoc) {
            return index;
         } else {
            return -1;
         }
      }

      protected function getHStateIndex(d:Document):Number {
         var index:Number;
         var hasstate:Boolean = false;
         var activehstate:HistoryState = d.activeHistoryState;
         var hstates:HistoryStates = d.historyStates;
         var len:Number = hstates.length;
         try {
            
            for (index = 0; index < len; index++) {
               if (hstates.index(index).hostObjectDelegate == activehstate.hostObjectDelegate) {
                  hasstate = true;
                  break;
               }
            }
         } catch(err:Error) {}
         if (hasstate) {
            return index;
         } else {
            return -1;
         }
      }