Skip to main content
jonathankemp
Inspiring
May 30, 2020
Answered

Get previous and next layer names through script.

  • May 30, 2020
  • 3 replies
  • 4465 views

Hi everyone,

 

I am looking for the most efficient way to get both the next and previous layer names in the layer stack. I need it to work a little differently then the usual Photoshop behaviour. Photoshop usually skips invisible layers, and will not get "inside" a folded layerset. I need a function that would get both visible and invisible layers, and would treat all layerset as if it was unfolded.

 

I did manage to make things work using a recursive function. I basically build myself an array with all the layers, then loop through it until I find the current active layer. I can then use "myArray[i-1]" and "myArray[i+1]" to get what I am looking for. It works, but it's clearly not an efficient way to do it. Gets pretty slow with documents with large number of layers...

 

I was hoping someone might be able to help me with some action manager magic... 🙂

Basically, all I have is "app.activeDocument.activeLayer", and I want a function (or functions) that would return the name of the layer directly above and directly under it, be it visible or not, be it part of a layerset or not...


Any thoughts ?


Thanks !

 

J.

Correct answer jonathankemp

Thanks mate.  This was of great help.

 

It was having trouble with both the first and last layers in the stack, though.  Made a few little tweaks and everything seems fine now.  

 

function getSurroundingLayersNames() {
	// get names of next layer above and below;

	// Get layer index
	var ref1 = new ActionReference();
		ref1.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("itemIndex"));
		ref1.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
	var layerDesc = executeActionGet(ref1);
	var theIndex = layerDesc.getInteger(stringIDToTypeID ("itemIndex"));

	// check background;
	var ref4 = new ActionReference();
		ref4.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
	var docDesc = executeActionGet(ref4);
	var hasBackground = docDesc.getBoolean(stringIDToTypeID("hasBackgroundLayer"));
	var theNumberOfLayers = docDesc.getInteger(stringIDToTypeID("numberOfLayers"));

	// determine the indices;
	var indexPrev;
	var indexNext;
	if (hasBackground == false) {
		indexPrev = theIndex-1;
		indexNext = theIndex+1;
	}
	else {
		indexPrev = theIndex-2;
		indexNext = theIndex;
	}
	
	while (isLayerSectionEnd(indexPrev) == true && indexPrev > 0) {indexPrev--;}
	while (isLayerSectionEnd(indexNext) == true && indexNext < theNumberOfLayers) {indexNext++;}

	if ((indexPrev < 0 && hasBackground) || (indexPrev == 0 && !hasBackground)) {
		// Bottom layer of the stack
		var theNameBelow = "";}
	else {
		var theNameBelow = getLayerName (indexPrev);}

	if (indexNext > theNumberOfLayers) {
		// Top layer of the stack
		var theNameAbove = "";}
	else {
		var theNameAbove = getLayerName (indexNext);}

	returnArray = new Array(theNameAbove,theNameBelow);
	return returnArray;
}

////// get name //////
function getLayerName (theIndex) {
	try{
		var ref = new ActionReference();
			ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
		var layerDesc = executeActionGet(ref);
		var theName = layerDesc.getString(stringIDToTypeID ("name"));
		return theName;
	}
	catch (e) {return e;}
}
////// is layer section end //////
function isLayerSectionEnd (theIndex) {
	try {
		var ref = new ActionReference();
			ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
		var layerDesc = executeActionGet(ref);

			var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
			// if group end;
			if (layerSet == "layerSectionEnd") {return true;}
	} catch (e) {return false;}
}

 

Thanks again.

 

J.

3 replies

Community Expert
May 31, 2020

Here is my stab at the implementation. It will prompt the name of the content layer before and after the currently selected layer. This is my first ever Photoshop scripting code and that too Action Manager code, so do test it at your own risk. Anything can happen, it might fry your computer 🙂

 

 

var s2t = stringIDToTypeID;
var t2s = typeIDToStringID;
var i = activeDocument.activeLayer.itemIndex

var layerCount = getProperty("document", "numberOfLayers").getInteger(s2t("numberOfLayers"))
var bgLayer = getProperty("document", "hasBackgroundLayer").getBoolean(s2t("hasBackgroundLayer")) ? 0 : 1
!bgLayer && i--
var retval = getName(i + 1, true)
if(retval != undefined)
	alert("Before Layer name is " + retval)

var retval = getName(i-1, false)
if(retval != undefined)
	alert("After Layer name is " + retval)
function getProperty(className, property, index)
{
	var r = new ActionReference(), p = s2t(property)
	r.putProperty(s2t('property'), p)
	if(index != undefined)
		r.putIndex(s2t(className),index)
	else
		r.putEnumerated(s2t(className), s2t('ordinal'), s2t('targetEnum'))
	var a = executeActionGet(r)
	return a
}
function getName(i, inc)
{
	if(i < bgLayer || i > layerCount)
	{
		return
	}
	var prop = "layerSection"
	var retval = t2s(getProperty("layer", prop, i).getEnumerationValue(s2t(prop)))
	if(retval == 'layerSectionEnd' || retval == 'layerSectionStart')
	{
		inc ? i++ : i--
		return getName(i, inc)
	}
	else
	{
		var prop = "name"
		var retval =  getProperty("layer", prop, i).getString(s2t(prop))
		return retval
	}
}

 

 

 

Question for Action Manager veterans, this works fine but i have noticed that the when the background layer is present then getting the name using index 0 or 1 returns the layer above background layer. The nameof background is not returned, is this so or i missed something?

 

-Manan

-Manan
Community Expert
May 31, 2020

Another question for the experienced folks, is there a way to jump from ActionDescriptor to the JS DOM object and vice versa. For ex i get ActionDescriptor for layer, from that can i get the JS object, i suspect not just want to confirm

 

-Manan

-Manan
c.pfaffenbichler
Community Expert
Community Expert
May 30, 2020

You can give this a try: 

 

 

// get names of next layer above and below;
// 2020, use it at your own risk;
if (app.documents.length > 0) {
var ref1 = new ActionReference();
ref1.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("itemIndex"));
ref1.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref1);
var theIndex = layerDesc.getInteger(stringIDToTypeID ("itemIndex"));
// check background;
var ref4 = new ActionReference();
ref4.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var docDesc = executeActionGet(ref4);
var hasBackground = docDesc.getBoolean(stringIDToTypeID("hasBackgroundLayer"));
var theNumberOfLayers = docDesc.getInteger(stringIDToTypeID("numberOfLayers"));// determine the indices;
if (hasBackground == false) {
var index1 = theIndex-1;
var index2 = theIndex+1;
} else {
var index1 = theIndex-2;
var index2 = theIndex;
theNumberOfLayers++};
// get below name;
if (index1 >= 1) {
while (isLayerSectionEnd(index1) == true && index1 < theNumberOfLayers) {index1--}
var theNameBelow = getLayerName (index1);
} else {var theNameBelow = "nothing"};
// get above name;
if (index2 < theNumberOfLayers) {
while (isLayerSectionEnd(index2) == true && index2 < theNumberOfLayers) {index2++};
var theNameAbove = getLayerName (index2);
} else {var theNameAbove = "nothing"};
alert ("\nbelow "+theNameBelow+"\nabove "+theNameAbove);
};
////// get name //////
function getLayerName (theIndex) {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
var layerDesc = executeActionGet(ref);
var theName = layerDesc.getString(stringIDToTypeID ("name"));
return theName
};
////// is layer section end //////
function isLayerSectionEnd (theIndex) {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
var layerDesc = executeActionGet(ref);
try {
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
// if group end;
if (layerSet == "layerSectionEnd") {return true};
} catch (e) {return false};
};

 

edited

 

jonathankemp
jonathankempAuthorCorrect answer
Inspiring
June 1, 2020

Thanks mate.  This was of great help.

 

It was having trouble with both the first and last layers in the stack, though.  Made a few little tweaks and everything seems fine now.  

 

function getSurroundingLayersNames() {
	// get names of next layer above and below;

	// Get layer index
	var ref1 = new ActionReference();
		ref1.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("itemIndex"));
		ref1.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
	var layerDesc = executeActionGet(ref1);
	var theIndex = layerDesc.getInteger(stringIDToTypeID ("itemIndex"));

	// check background;
	var ref4 = new ActionReference();
		ref4.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
	var docDesc = executeActionGet(ref4);
	var hasBackground = docDesc.getBoolean(stringIDToTypeID("hasBackgroundLayer"));
	var theNumberOfLayers = docDesc.getInteger(stringIDToTypeID("numberOfLayers"));

	// determine the indices;
	var indexPrev;
	var indexNext;
	if (hasBackground == false) {
		indexPrev = theIndex-1;
		indexNext = theIndex+1;
	}
	else {
		indexPrev = theIndex-2;
		indexNext = theIndex;
	}
	
	while (isLayerSectionEnd(indexPrev) == true && indexPrev > 0) {indexPrev--;}
	while (isLayerSectionEnd(indexNext) == true && indexNext < theNumberOfLayers) {indexNext++;}

	if ((indexPrev < 0 && hasBackground) || (indexPrev == 0 && !hasBackground)) {
		// Bottom layer of the stack
		var theNameBelow = "";}
	else {
		var theNameBelow = getLayerName (indexPrev);}

	if (indexNext > theNumberOfLayers) {
		// Top layer of the stack
		var theNameAbove = "";}
	else {
		var theNameAbove = getLayerName (indexNext);}

	returnArray = new Array(theNameAbove,theNameBelow);
	return returnArray;
}

////// get name //////
function getLayerName (theIndex) {
	try{
		var ref = new ActionReference();
			ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
		var layerDesc = executeActionGet(ref);
		var theName = layerDesc.getString(stringIDToTypeID ("name"));
		return theName;
	}
	catch (e) {return e;}
}
////// is layer section end //////
function isLayerSectionEnd (theIndex) {
	try {
		var ref = new ActionReference();
			ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
		var layerDesc = executeActionGet(ref);

			var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
			// if group end;
			if (layerSet == "layerSectionEnd") {return true;}
	} catch (e) {return false;}
}

 

Thanks again.

 

J.

Community Expert
June 1, 2020

Hi jonathankemp,

 

I tested your code as well as the once shared by c_pfaffenbichler, i see that it crashes if the selected layer is the on the top and bottom of the stack for both. Did you try my code, i have edited it and it seems to be working fine in these cases as well.

 

-Manan

 

-Manan
c.pfaffenbichler
Community Expert
Community Expert
May 30, 2020

A Group has a »layerSectionEnd« that’s invisible in the Layers Panel and a »layerSectionStart«, so the next layer above a Layer below a Group would not be a Layer in the Group but the »layerSectionEnd« … so what do you mean by »invisible« exactly? 

 

If you had posted a more meaningful description that included screenshots or sketches and the intended results one would not have to ask about such a thing. 

jonathankemp
Inspiring
May 30, 2020

Yeah, I guess "invisible" wasn't the good term.  I simply meant "not visible", or "with the visibility turned off".

 

Consider those 2 figures : 

 

 

 

In Fig1, if the Background is selected, and you move to the "next layer" using the usual kb shortcuts, PS will select Layer 2, ignoring the (not visible) Layer 1. 

 

In Fig2, if the Layer 2 is selected, and again, you move to the "next layer", Group 1 gets selected.

 

I am trying to find a way to avoid this behaviour, and always get the layer that sits immediately above or below the active one. 

 

Example : If Layer 2 is the active layer, previousLayer would be Layer 1, and nextLayer would be Layer 3, no matter if Group 1 is unfolded or not.

 

Is that clearer ?

c.pfaffenbichler
Community Expert
Community Expert
May 30, 2020

For that names seem useless and the index or the identifier would seem to make more sense …