Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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;
};
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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)
Copy link to clipboard
Copied
This is really cool! However it currently relays the item index. Can it be modified to output the layer name instead?
Copy link to clipboard
Copied
Change last line to:
&& arr.push(dsc.getString(sTT('name')))
alert(arr)
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
I missed targetLayers Mike, I will have a look, many thanks.
Copy link to clipboard
Copied
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
Copy link to clipboard
Copied
Many thanks for that Mike, I will file it away for later use.
Copy link to clipboard
Copied
This is a pretty old thread, but if anyone's still around...
I'm an Applescripter, and I just need to group the already selected layers. I was achieving this fine with user interface scripting and just having it call [command] +
It looks like this functionality isn't available to Applescript, so I was hoping to find a "Do Javascript" call that accomplishes the same thing. I don't know any javascript, but figured I could just run this and then start deleting bits that look promising until only the "group" functionality is still functional. But I keep messing with it and can't get it to run... even just saving the whole thing as a Javascript and running it from the Script menu, I just get General Error 8800 "this functionality may not be available" messages. Any help would be appreciated. Thanks.
Copy link to clipboard
Copied
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()
Copy link to clipboard
Copied
Nice workaround!
Copy link to clipboard
Copied
Hello again, after some thinking I got a solution that works also with layersets... yeah! 🙂
function SelectedLayers() {
try{
//Version V2 works with folders aka layersets
var ActLay = app.activeDocument.activeLayer;
ActLay.allLocked = true;
var L = app.activeDocument.layers.length;
var selLayers = new Array();
for(var i = 0; i < L; i++) {
var LayerType = app.activeDocument.layers[i].typename;
var layerRef = app.activeDocument.layers[i];
if (LayerType == "LayerSet" ) {
var refLength = layerRef.layers.length;
for(var j = 0; j < refLength; j++) {
var refLay = layerRef.layers[j];
if (refLay.allLocked == true){selLayers.push(refLay)}
}
continue;
}
if (layerRef.allLocked == true) {selLayers.push(layerRef)}
}
ActLay.allLocked = false;
return selLayers;
}
catch (e) {/*alert(e)*/}
}
SelectedLayers()
Copy link to clipboard
Copied
Again good job. But try to keep locked layers if they were locked before running script 😉
Copy link to clipboard
Copied
Hi, bjoernu!
Can I have question about the this part:
var layerRef = app.activeDocument.layers[i];
if (LayerType == "LayerSet" ) {
var refLength = layerRef.layers.length;
So
refLength = app.activeDocument.layers[i].layers.length;
Why is this running well? Layer object doesn't even have properties named 'layers' or 'length'.
Copy link to clipboard
Copied
app.activeDocument.layers == collection (array-like) of Layer objects