Skip to main content
Participant
March 10, 2011
Question

Get selected layers [2011]

  • March 10, 2011
  • 4 replies
  • 7587 views

Hi there.

 

I've been trying to work with multiple selected layers, but I don't know how to do it. If I select multiple layers, app.activeDocument.activeLayer only returns  the topmost one.

 

By the way, I'm looking for a way of doing it without using all that low level ActionReference stuff. It really clutters up the code and it's very hard to understand.

 

Thanks.

4 replies

Flowgun
Inspiring
August 22, 2025

Are there any updates on this? does grouping selected layers, getting the layers in the group and then undoing still the best option available?

Participant
December 17, 2018

Hi there, I have been struggling with this problem and after a lot of trying and failing, I found a very simple solution that works great as long as you don't use layersets.

hope this helps somebody.

function SelectedLayers() {
	try{
		var ActLay = app.activeDocument.activeLayer;
		ActLay.allLocked = true; //lock all selected layers
		var LayerStack = app.activeDocument.artLayers.length;

		var selLayers = new Array();
		for(var i = 0; i <= LayerStack - 1; i++) {
			ActLay = app.activeDocument.layers[i]
			if (ActLay.allLocked == true) {selLayers.push(app.activeDocument.layers[i]);} // push all locked layers into an array
		}

		for (i = 0; i <= LayerStack - 1; i++) {
			var LAY = app.activeDocument.layers[i];
			LAY.allLocked = false; // unlock all locked Layers
		}

		return selLayers;
	}
	catch(e){/*alert(e);*/}
}

SelectedLayers()
Kukurykus
Legend
December 18, 2018

Nice workaround!

Inspiring
March 10, 2011

I think Xbytor has some code for working with multiple selected layers in his xtools that only uses DOM methods. He wrote it before the ''targetLayers" key was added to the document descriptor.

But the 'low level' action manager code is faster and, once you understand it, easier to use for something like this.

Paul Riggott
Inspiring
March 10, 2011

I missed targetLayers Mike, I will have a look, many thanks.

Inspiring
March 10, 2011

After seeing Paul's post I could be wrong about xtools having a way that only uses DOM methods. Xbytor may have had to use some action mangaer code as well.

Paul, here is an example of working with multiple selected layers using action manager. It will remove 'copy' from the selected layers.

if( app.documents.length > 0 ){
app.activeDocument.suspendHistory('Rename selected layers','removeCopyFromSelectedLayersNames()');
}
function removeCopyFromLayerName(){
     if( getSelectedLayersIdx().length > 1 ){
          var selectedLayers = getSelectedLayersIdx();
          makeActiveByIndex( selectedLayers[0], false );
     }
   var startLoop = Number( !hasBackground() );
   var endLoop = getNumberOfLayer() + 1;
   for( var l = startLoop;l < endLoop; l++){
        while( !isValidActiveLayer( l ) ) {
            l++;
        }
          var oldName =  getLayerNameByIndex( l );
          var newName = oldName.replace(/\scopy\s?\d*$/i,'');
          putLayerNameByIndex( l, newName )
     }
     if( selectedLayers != undefined ) makeActiveByIndex( selectedLayers, false );
}
function removeCopyFromSelectedLayersNames(){
     var selectedLayers = getSelectedLayersIdx();
     for( var l = 0;l < selectedLayers.length; l++){
          var oldName =  getLayerNameByIndex( selectedLayers[ l ] );
          var newName = oldName.replace(/\scopy.*$/i,'');
          makeActiveByIndex( selectedLayers[ l ], false );
          putLayerNameByIndex( selectedLayers[ l ], newName )
     }
     makeActiveByIndex( selectedLayers, false );
}
function getNumberOfLayer(){
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
var desc = executeActionGet(ref);
var numberOfLayer = desc.getInteger(charIDToTypeID('NmbL'));
return numberOfLayer;
}
function getLayerNameByIndex( idx ) {
    var ref = new ActionReference();
    ref.putProperty( charIDToTypeID('Prpr') , charIDToTypeID( 'Nm  ' ));
    ref.putIndex( charIDToTypeID( 'Lyr ' ), idx );
    return executeActionGet(ref).getString(charIDToTypeID( 'Nm  ' ));;
}
function putLayerNameByIndex( idx, name ) {
     if( idx == 0 ) return;
    var desc = new ActionDescriptor();
        var ref = new ActionReference();
        ref.putIndex( charIDToTypeID( 'Lyr ' ), idx );
    desc.putReference( charIDToTypeID('null'), ref );
        var nameDesc = new ActionDescriptor();
        nameDesc.putString( charIDToTypeID('Nm  '), name );
    desc.putObject( charIDToTypeID('T   '), charIDToTypeID('Lyr '), nameDesc );
    executeAction( charIDToTypeID('setd'), desc, DialogModes.NO );
}
function getActiveLayerIndex() {
     var ref = new ActionReference();
     ref.putProperty( 1349677170 , 1232366921 );
     ref.putEnumerated( 1283027488, 1332896878, 1416783732 );
     var res = executeActionGet(ref).getInteger( 1232366921 )
                                                       - Number( hasBackground() );
     return res;  
}
function isValidActiveLayer( idx ) {
     var propName = stringIDToTypeID( 'layerSection' );
     var ref = new ActionReference();
     ref.putProperty( 1349677170 , propName);
     ref.putIndex( 1283027488, idx );
     var desc =  executeActionGet( ref );
     var type = desc.getEnumerationValue( propName );
     var res = typeIDToStringID( type );
     return res == 'layerSectionEnd' ? false:true;
}
function hasBackground(){
    var res = undefined;
    try{
        var ref = new ActionReference();
        ref.putProperty( 1349677170 , 1315774496);
        ref.putIndex( 1283027488, 0 );
        executeActionGet(ref).getString(1315774496 );;
        res = true;
    }catch(e){ res = false}
    return res;
}
function getSelectedLayersIdx(){
     var selectedLayers = new Array;
     var ref = new ActionReference();
     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());
          }
     }else{
          var ref = new ActionReference();
          ref.putProperty( charIDToTypeID('Prpr') , charIDToTypeID( 'ItmI' ));
          ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
          selectedLayers.push( executeActionGet(ref).getInteger(charIDToTypeID( 'ItmI' )));
     }
     return selectedLayers;
}
function makeActiveByIndex( idx, visible ){
     if( idx.constructor != Array ) idx = [ idx ];
     for( var i = 0; i < idx.length; i++ ){
          var desc = new ActionDescriptor();
          var ref = new ActionReference();
          ref.putIndex(charIDToTypeID( 'Lyr ' ), idx[i])
          desc.putReference( charIDToTypeID( 'null' ), ref );
          if( i > 0 ) {
               var idselectionModifier = stringIDToTypeID( 'selectionModifier' );
               var idselectionModifierType = stringIDToTypeID( 'selectionModifierType' );
               var idaddToSelection = stringIDToTypeID( 'addToSelection' );
               desc.putEnumerated( idselectionModifier, idselectionModifierType, idaddToSelection );
          }
          desc.putBoolean( charIDToTypeID( 'MkVs' ), visible );
          executeAction( charIDToTypeID( 'slct' ), desc, DialogModes.NO );
     }    
}

Two things to note. The action manager layer index changes depending on if there is a background layer. And in action manager layersets have two indexes one of which you can't do much with. That is the reason for hasBackground() and isValidActiveLayer() ie you can't make the layerSelectionEnds index active.

Note 2. I replaced some of the charIDToTypeID calls with the returned numbers in some of the functions. It makes harder to read but does speed the script up if there are a lot of layers.

 

Message was edited by: Michael L Hale

Paul Riggott
Inspiring
March 10, 2011

Some things are just not possible with just using the DOM, this is one these.

What you need to do is group the selected layers into a layerset, then you can get the actual layers into an array, then undo the layerset creation.

IE:-

var GroupedLayers = [];
GroupedLayers = GetSelectedLayers();
for(var z in GroupedLayers) {
	alert(GroupedLayers[z].name);
}

function GetSelectedLayers() {
	var A = [];
	var desc11 = new ActionDescriptor();
		var ref9 = new ActionReference();
		ref9.putClass( stringIDToTypeID('layerSection') );
	desc11.putReference( charIDToTypeID('null'), ref9 );
		var ref10 = new ActionReference();
		ref10.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
	desc11.putReference( charIDToTypeID('From'), ref10 );
	executeAction( charIDToTypeID('Mk  '), desc11, DialogModes.NO );
	var gL = activeDocument.activeLayer.layers;
	for(var i = 0; i < gL.length; i++) { 
		A.push(gL[i]); 
	} 
	executeAction( charIDToTypeID('undo'), undefined, DialogModes.NO );
	return A;
};
Known Participant
April 23, 2022

Is there a simple way to use low-level ActionReference stuff to get a list of only the top-level selected layer sets? I've only found examples of these that generate a list of EVERY nested layer -- not the top-level layer sets.

Kukurykus
Legend
April 23, 2022

 

sTT = stringIDToTypeID; (ref = new ActionReference())
.putProperty(sTT('property'), hBL = sTT('hasBackgroundLayer'))
ref.putEnumerated(sTT('document'), sTT('ordinal'), sTT('targetEnum'))
bL = executeActionGet(ref).getBoolean(hBL);

(ref = new ActionReference())
.putProperty(sTT('property'), tLs = sTT('targetLayers'))
ref.putEnumerated(sTT('document'), sTT('ordinal'), sTT('targetEnum'))
cnt = (slctd = executeActionGet(ref).getList(tLs)).count; arr = []

for(i = 0; i < cnt;) (ref = new ActionReference())
.putIndex(charIDToTypeID('Lyr '), indx = slctd
.getReference(i++).getIndex() + !bL), typeIDToStringID
((dsc = executeActionGet(ref)).getEnumerationValue
(sTT('layerSection'))).indexOf('Content') < 0
&& dsc.getInteger(sTT('parentLayerID')) < 0
&& arr.push(indx); alert(arr)