Photoshop: Get selected layers with Photoshop CC scripting API

  • 3
  • Idea
  • Updated 11 months ago
  • (Edited)
Finally Photoshop CC scripting API was released, and it was disappointing to see that there is still no way to get list of selected layers. I believe that majority of scripts are related to selected layers, and using Action Reference to get the list of selected layers is hacky, slow and ugly. If you look up forums you would see that this in of the most highly discussed issue of ps api for years. I am sure all scripting community would love to see this coming, and also it would make easier for new authors to script.
Photo of timur IC

timur IC

  • 31 Posts
  • 6 Reply Likes

Posted 5 years ago

  • 3
Photo of christoph pfaffenbichler

christoph pfaffenbichler, Champion

  • 1199 Posts
  • 159 Reply Likes
I admit that Action Manager code is inconvenient to use for me, too, but »slow«?
Action Manager code usually runs faster than DOM code. Quite considerably sometimes.
Photo of John Peterson

John Peterson, Employee

  • 9 Posts
  • 0 Reply Likes
Hi Timur,

Here's some sample code for accessing the selected layers in Photoshop CC. It works across layer groups.

// Load the Photoshop Event Terminology definitions
var g_StackScriptFolderPath = app.path + "/"+ localize("$$$/ScriptingSupport/InstalledScripts=Presets/Scripts") + "/"
+ localize("$$$/private/Exposuremerge/StackScriptOnly=Stack Scripts Only/");

$.evalFile(g_StackScriptFolderPath + "Terminology.jsx");

const keyLayerID = app.charIDToTypeID('LyrI');


getSelectedLayerIndicies = function()
{
// ktargetLayers is missing from Terminology.jsx
const ktargetLayers = app.stringIDToTypeID("targetLayers");

var backgroundIndexOffset = 0;
try {
// This throws an error if there's no background
if (app.activeDocument.backgroundLayer)
backgroundIndexOffset = 1;
}
catch (err)
{}

var resultLayerIndices = [];
var ref = new ActionReference();
var args = new ActionDescriptor();
ref.putProperty( classProperty, ktargetLayers );
ref.putEnumerated( classDocument, typeOrdinal, enumTarget );
args.putReference( keyTarget, ref );
var resultDesc = executeAction( eventGet, args, DialogModes.NO );
if (! resultDesc.hasKey( ktargetLayers ))
{
resultLayerIndices.push( app.activeDocument.activeLayer.itemIndex - backgroundIndexOffset );
return resultLayerIndices;
}
var selIndexList = resultDesc.getList( ktargetLayers );
for (i = 0; i < selIndexList.count; ++i)
resultLayerIndices.push(selIndexList.getReference(i).getIndex( classLayer ) + (1-backgroundIndexOffset) );

return resultLayerIndices; // Reverse again to match layer palette
}


layerIndexToName = function( layerIndex )
{
var ref = new ActionReference();
ref.putProperty( classProperty, stringIDToTypeID("name") );
ref.putIndex( classLayer, layerIndex );
var resultDesc = executeActionGet( ref );
return resultDesc.getString(stringIDToTypeID("name"));
}


var selectedLayers = getSelectedLayerIndicies ();
$.writeln( selectedLayers );

for (var i = 0; i < selectedLayers.length; ++i)
$.writeln("Layer ["+selectedLayers[i]+"] " + layerIndexToName(selectedLayers[i]) + " is selected");













Photo of Paul Riggott

Paul Riggott

  • 343 Posts
  • 130 Reply Likes
The code doesn't check if no layers are selected or even if one layer is selected. In fact it errors if less than two layers selected.
Photo of John Peterson

John Peterson, Employee

  • 9 Posts
  • 0 Reply Likes
That's what the "if (! resultDesc.hasKey( ktargetLayers ))" line is for. The ktargetLayers property is only defined if multiple (> 1) layers are selected, so it should return the DOM's active layer if only one (or none) are selected.
Photo of Paul Riggott

Paul Riggott

  • 343 Posts
  • 130 Reply Likes
There lies the problem, if a layer is not selected the top layer is always returned.
Photo of Max Johnson

Max Johnson, Champion

  • 395 Posts
  • 171 Reply Likes
There lies the problem, if a layer is not selected the top layer is always returned.

This seems to be intrinsic in adobe's api... asking via action manager code or simply looking at app.activeDocument.activeLayer gives you the same thing. No selection == topmost layer returned.

Does anyone know a way to test for NO selected layers?
(Edited)
Photo of Paul Riggott

Paul Riggott

  • 343 Posts
  • 130 Reply Likes
I thought that had been fixed in CC?
I only use versions below CC, this code will work to return no layers, if the problem still exists in CC remove the :-
if(app.version.match(/^\d+/) > 15) return selectedLayers ;

//////////////////////////////
function getSelectedLayersIdx(){ 
      var selectedLayers = new Array(); 
      var backGroundCounter = 1;
            if(activeDocument.artLayers.length > 0){
            backGroundCounter = activeDocument.artLayers[activeDocument.artLayers.length - 1].isBackgroundLayer ? 0 : 1;
            }
      var ref = new ActionReference(); 
	  ref.putProperty(charIDToTypeID("Prpr"), stringIDToTypeID("targetLayers"));
      ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
      var desc = executeActionGet(ref); 
      if( desc.hasKey( stringIDToTypeID( "targetLayers" ) ) ){ 
         desc = desc.getList( stringIDToTypeID( "targetLayers" )); 
          var c = desc.count; 
          var selectedLayers = new Array(); 
          for(var i=0;i<c;i++){ 
               selectedLayers.push(  desc.getReference( i ).getIndex() +backGroundCounter ); 
          } 
      if(app.version.match(/^\d+/) > 15) return selectedLayers ;
       }else{ 
           if(app.version.match(/^\d+/) > 15) return selectedLayers ;
         var ref = new ActionReference(); 
         ref.putProperty( charIDToTypeID("Prpr") , charIDToTypeID( "ItmI" )); 
         ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
         if(!backGroundCounter){
            selectedLayers.push( executeActionGet(ref).getInteger(charIDToTypeID( "ItmI" )) -1);
            }else{
                selectedLayers.push( executeActionGet(ref).getInteger(charIDToTypeID( "ItmI" )));
                }
     var vis = app.activeDocument.activeLayer.visible;
        if(vis == true) app.activeDocument.activeLayer.visible = false;
        var desc9 = new ActionDescriptor();
    var list9 = new ActionList();
    var ref9 = new ActionReference();
    ref9.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
    list9.putReference( ref9 );
    desc9.putList( charIDToTypeID("null"), list9 );
    executeAction( charIDToTypeID("Shw "), desc9, DialogModes.NO );
    if(app.activeDocument.activeLayer.visible == false) selectedLayers.shift();
        app.activeDocument.activeLayer.visible = vis;
      } 
      return selectedLayers; 
};