Script request: Send to group

Anyone, especially newbies, asking for help with Photoshop Scripting and Photoshop Automation - as opposed to those contributing to discussion about an aspect of Photoshop Scripting

Moderators: Tom, Kukurykus

dbox

Script request: Send to group

Post by dbox »

Hello all, I'm new here. Jeffery Tranberry from Adobe sent me here to see if there is anyone that could help with this request.

I would really like a script that would add a "send to group" option when right clicking on a layer.

Here's an image of what I mean:
https://twitter.com/dbox/status/398159893556772864

After clicking it, the user would be presented with a drop down of current layer sets.

When I'm organizing 100+ layer psds, this would be incredibly handy for me.

Doable? Can anyone help? Thanks!
kpt

Script request: Send to group

Post by kpt »

Do you have nested layer groups?
Mike Hale

Script request: Send to group

Post by Mike Hale »

You can not add a context menu to Photoshop. But you can run a script from a keyboard shortcut. This script should be close to what you want.
Code: Select allfunction moveTo(index) {
    var desc = new ActionDescriptor();
        var ref = new ActionReference();
        ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
    desc.putReference( charIDToTypeID('null'), ref );
        var ref = new ActionReference();
        ref.putIndex( charIDToTypeID('Lyr '), index );
    desc.putReference( charIDToTypeID('T   '), ref );
    desc.putBoolean( charIDToTypeID('Adjs'), false );
    desc.putInteger( charIDToTypeID('Vrsn'), 5 );
    executeAction( charIDToTypeID('move'), desc, DialogModes.NO );
};
function getNumberOfLayers(){
   var ref = new ActionReference();
   ref.putProperty( charIDToTypeID( 'Prpr' ), charIDToTypeID( 'NmbL' ) );
   ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID( 'Ordn' ), charIDToTypeID( 'Trgt' ) );
   return executeActionGet( ref ).getInteger( charIDToTypeID( 'NmbL' )) ;
};
function getProperty( psClass, psKey, index ){// integer:Class, integer:key
    var ref = new ActionReference();
    if( psKey != undefined ) ref.putProperty( charIDToTypeID( "Prpr" ), psKey );
    if(index != undefined ){
        ref.putIndex( psClass, index );
    }else{
        ref.putEnumerated( psClass , charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
    }
    try{
        var desc = executeActionGet(ref);
    }catch(e){ return; }// return on error
    if(desc.count == 0) return;// return undefined if property doesn't exists
    var dataType = desc.getType(psKey);
    switch(dataType){// not all types supported - returns undefined if not supported
        case DescValueType.INTEGERTYPE:
            return desc.getInteger(psKey);
            break;
        case DescValueType.ALIASTYPE:
            return desc.getPath(psKey);
            break;
        case DescValueType.BOOLEANTYPE:
            return desc.getBoolean(psKey);
            break;
        case DescValueType.BOOLEANTYPE:
            return desc.getBoolean(psKey);
            break;
        case DescValueType.UNITDOUBLE:
            return desc.getUnitDoubleValue(psKey);
            break;
        case DescValueType.STRINGTYPE:
            return desc.getString(psKey);
            break;
        case  DescValueType.OBJECTTYPE:
            return desc.getObjectValue(psKey);
            break;
        case  DescValueType.LISTTYPE:
            return desc.getList(psKey);
            break;
        case  DescValueType.ENUMERATEDTYPE:
            return desc.getEnumerationValue(psKey);
            break;
    }
};

var doc = app.activeDocument;
var layerCount = getNumberOfLayers();
var layerSets = [];
var setNames = [];
var loopStart = Number(!doc.layers[doc.layers.length-1].isBackgroundLayer);

for(var layerIndex = layerCount;layerIndex>=loopStart;layerIndex--){
    if(typeIDToStringID(getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'layerSection' ), layerIndex ))=='layerSectionStart'){
        layerSets.push(layerIndex);
        setNames.push(getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'name' ), layerIndex ));
    }
}
var win = new Window( 'dialog', 'Send Layer to Group' );
win.dropdown = win.add("dropdownlist", undefined, setNames);
win.dropdown.preferredSize.width = 200;
win.dropdown.items[0].selected = true;
win.ok = win.add('button',undefined,'ok');
var res = win.show();
if(res==1) moveTo(layerSets[win.dropdown.selection.index]);
Note this script assumes you have a doc open with a non locked layer(s) active. It does work with more than one layer selected if you want to send multiple layers to the same group.
christianmagill

Script request: Send to group

Post by christianmagill »

This is excellent! Thanks so much.
christianmagill

Script request: Send to group

Post by christianmagill »

As of the latest Photoshop CC, the selected contents get moved directly before the selected folder instead of inside. Also, it would be nice to have some indication of folder path in the dropdown menu, whether that is the full path for menu item, or a series of hyphens before each to indicate depth.

Thanks for any help, I love this script and have been using it quite a bit.
christianmagill

Script request: Send to group

Post by christianmagill »

I've got the group level indicators working. I have also realized that the issue may not be new to Photoshop CC. The script works fine as long as their is a background image. However, if there is not a background image, then the selected layers get moved directly above the selected group, instead of inside.

Thanks for any help on this. Here's my updated code with level indicators

Code: Select all
function moveTo(index) {
    var desc = new ActionDescriptor();
        var ref = new ActionReference();
        ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
    desc.putReference( charIDToTypeID('null'), ref );
        var ref = new ActionReference();
        ref.putIndex( charIDToTypeID('Lyr '), index );
    desc.putReference( charIDToTypeID('T   '), ref );
    desc.putBoolean( charIDToTypeID('Adjs'), false );
    desc.putInteger( charIDToTypeID('Vrsn'), 5 );
    executeAction( charIDToTypeID('move'), desc, DialogModes.NO );
};
function getNumberOfLayers(){
   var ref = new ActionReference();
   ref.putProperty( charIDToTypeID( 'Prpr' ), charIDToTypeID( 'NmbL' ) );
   ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID( 'Ordn' ), charIDToTypeID( 'Trgt' ) );
   return executeActionGet( ref ).getInteger( charIDToTypeID( 'NmbL' )) ;
};
function getProperty( psClass, psKey, index ){// integer:Class, integer:key
    var ref = new ActionReference();
    if( psKey != undefined ) ref.putProperty( charIDToTypeID( "Prpr" ), psKey );
    if(index != undefined ){
        ref.putIndex( psClass, index );
    }else{
        ref.putEnumerated( psClass , charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
    }
    try{
        var desc = executeActionGet(ref);
    }catch(e){ return; }// return on error
    if(desc.count == 0) return;// return undefined if property doesn't exists
    var dataType = desc.getType(psKey);
    switch(dataType){// not all types supported - returns undefined if not supported
        case DescValueType.INTEGERTYPE:
            return desc.getInteger(psKey);
            break;
        case DescValueType.ALIASTYPE:
            return desc.getPath(psKey);
            break;
        case DescValueType.BOOLEANTYPE:
            return desc.getBoolean(psKey);
            break;
        case DescValueType.BOOLEANTYPE:
            return desc.getBoolean(psKey);
            break;
        case DescValueType.UNITDOUBLE:
            return desc.getUnitDoubleValue(psKey);
            break;
        case DescValueType.STRINGTYPE:
            return desc.getString(psKey);
            break;
        case  DescValueType.OBJECTTYPE:
            return desc.getObjectValue(psKey);
            break;
        case  DescValueType.LISTTYPE:
            return desc.getList(psKey);
            break;
        case  DescValueType.ENUMERATEDTYPE:
            return desc.getEnumerationValue(psKey);
            break;
    }
};

var doc = app.activeDocument;
var layerCount = getNumberOfLayers();
var layerSets = [];
var setNames = [];
var loopStart = Number(!doc.layers[doc.layers.length-1].isBackgroundLayer);
var prefix = "";

for(var layerIndex = layerCount;layerIndex>=loopStart;layerIndex--){
   
    if(typeIDToStringID(getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'layerSection' ), layerIndex ))=='layerSectionStart'){
        layerSets.push(layerIndex);
        setNames.push(prefix + getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'name' ), layerIndex ));
      prefix += "+ ";
    } else if(typeIDToStringID(getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'layerSection' ), layerIndex ))=='layerSectionEnd'){
      prefix = prefix.substring(0, prefix.length - 2);
   }
}
var win = new Window( 'dialog', 'Send Layer to Group' );
win.dropdown = win.add("dropdownlist", undefined, setNames);
win.dropdown.preferredSize.width = 200;
win.dropdown.items[0].selected = true;
win.ok = win.add('button',undefined,'ok');
var res = win.show();
if(res==1) moveTo(layerSets[win.dropdown.selection.index]);

christianmagill

Script request: Send to group

Post by christianmagill »

Here's an update with full group paths. Lack of background layer is still an issue.

Code: Select all
function moveTo(index) {
    var desc = new ActionDescriptor();
        var ref = new ActionReference();
        ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
    desc.putReference( charIDToTypeID('null'), ref );
        var ref = new ActionReference();
        ref.putIndex( charIDToTypeID('Lyr '), index );
    desc.putReference( charIDToTypeID('T   '), ref );
    desc.putBoolean( charIDToTypeID('Adjs'), false );
    desc.putInteger( charIDToTypeID('Vrsn'), 5 );
    executeAction( charIDToTypeID('move'), desc, DialogModes.NO );
};
function getNumberOfLayers(){
   var ref = new ActionReference();
   ref.putProperty( charIDToTypeID( 'Prpr' ), charIDToTypeID( 'NmbL' ) );
   ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID( 'Ordn' ), charIDToTypeID( 'Trgt' ) );
   return executeActionGet( ref ).getInteger( charIDToTypeID( 'NmbL' )) ;
};
function getProperty( psClass, psKey, index ){// integer:Class, integer:key
    var ref = new ActionReference();
    if( psKey != undefined ) ref.putProperty( charIDToTypeID( "Prpr" ), psKey );
    if(index != undefined ){
        ref.putIndex( psClass, index );
    }else{
        ref.putEnumerated( psClass , charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
    }
    try{
        var desc = executeActionGet(ref);
    }catch(e){ return; }// return on error
    if(desc.count == 0) return;// return undefined if property doesn't exists
    var dataType = desc.getType(psKey);
    switch(dataType){// not all types supported - returns undefined if not supported
        case DescValueType.INTEGERTYPE:
            return desc.getInteger(psKey);
            break;
        case DescValueType.ALIASTYPE:
            return desc.getPath(psKey);
            break;
        case DescValueType.BOOLEANTYPE:
            return desc.getBoolean(psKey);
            break;
        case DescValueType.BOOLEANTYPE:
            return desc.getBoolean(psKey);
            break;
        case DescValueType.UNITDOUBLE:
            return desc.getUnitDoubleValue(psKey);
            break;
        case DescValueType.STRINGTYPE:
            return desc.getString(psKey);
            break;
        case  DescValueType.OBJECTTYPE:
            return desc.getObjectValue(psKey);
            break;
        case  DescValueType.LISTTYPE:
            return desc.getList(psKey);
            break;
        case  DescValueType.ENUMERATEDTYPE:
            return desc.getEnumerationValue(psKey);
            break;
    }
};

var doc = app.activeDocument;
var layerCount = getNumberOfLayers();
var layerSets = [];
var setNames = [];
var loopStart = Number(!doc.layers[doc.layers.length-1].isBackgroundLayer);
var layerPath = [];
var layerPathString = "";
var layerName = "";

for(var layerIndex = layerCount;layerIndex>=loopStart;layerIndex--){
   
    if(typeIDToStringID(getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'layerSection' ), layerIndex ))=='layerSectionStart'){
        layerSets.push(layerIndex);
      layerName = getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'name' ), layerIndex );
      layerPath[layerPath.length] = layerName;
      layerPathString = layerPath.join(' / ');
        setNames.push(layerPathString);
      
    } else if(typeIDToStringID(getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'layerSection' ), layerIndex ))=='layerSectionEnd'){
      layerPath.pop();
   }
}
var win = new Window( 'dialog', 'Send Layer to Group' );
win.dropdown = win.add("dropdownlist", undefined, setNames);
win.dropdown.preferredSize.width = 200;
win.dropdown.items[0].selected = true;
win.ok = win.add('button',undefined,'ok');
var res = win.show();
if(res==1) moveTo(layerSets[win.dropdown.selection.index]);

Mike Hale

Script request: Send to group

Post by Mike Hale »

I think this fixes several issues with the original script.
Code: Select all function moveTo(index) {
        var desc = new ActionDescriptor();
            var ref = new ActionReference();
            ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') );
        desc.putReference( charIDToTypeID('null'), ref );
            var ref = new ActionReference();
            ref.putIndex( charIDToTypeID('Lyr '), index );
        desc.putReference( charIDToTypeID('T   '), ref );
        desc.putBoolean( charIDToTypeID('Adjs'), false );
        desc.putInteger( charIDToTypeID('Vrsn'), 5 );
        executeAction( charIDToTypeID('move'), desc, DialogModes.NO );
    };
    function getNumberOfLayers(){
       var ref = new ActionReference();
       ref.putProperty( charIDToTypeID( 'Prpr' ), charIDToTypeID( 'NmbL' ) );
       ref.putEnumerated( charIDToTypeID('Dcmn'), charIDToTypeID( 'Ordn' ), charIDToTypeID( 'Trgt' ) );
       return executeActionGet( ref ).getInteger( charIDToTypeID( 'NmbL' )) ;
    };
    function getProperty( psClass, psKey, index ){// integer:Class, integer:key
        var ref = new ActionReference();
        if( psKey != undefined ) ref.putProperty( charIDToTypeID( "Prpr" ), psKey );
        if(index != undefined ){
            ref.putIndex( psClass, index );
        }else{
            ref.putEnumerated( psClass , charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) );
        }
        try{
            var desc = executeActionGet(ref);
        }catch(e){ return; }// return on error
        if(desc.count == 0) return;// return undefined if property doesn't exists
        var dataType = desc.getType(psKey);
        switch(dataType){// not all types supported - returns undefined if not supported
            case DescValueType.INTEGERTYPE:
                return desc.getInteger(psKey);
                break;
            case DescValueType.ALIASTYPE:
                return desc.getPath(psKey);
                break;
            case DescValueType.BOOLEANTYPE:
                return desc.getBoolean(psKey);
                break;
            case DescValueType.BOOLEANTYPE:
                return desc.getBoolean(psKey);
                break;
            case DescValueType.UNITDOUBLE:
                return desc.getUnitDoubleValue(psKey);
                break;
            case DescValueType.STRINGTYPE:
                return desc.getString(psKey);
                break;
            case  DescValueType.OBJECTTYPE:
                return desc.getObjectValue(psKey);
                break;
            case  DescValueType.LISTTYPE:
                return desc.getList(psKey);
                break;
            case  DescValueType.ENUMERATEDTYPE:
                return desc.getEnumerationValue(psKey);
                break;
        }
    };

    var doc = app.activeDocument;
    if(!doc.activeLayer.isBackgroundLayer){
        if(doc.layerSets.length >0 ){
        var layerCount = getNumberOfLayers();
        var layerSets = [];
        var setNames = [];
        var loopStart = Number(!doc.layers[doc.layers.length-1].isBackgroundLayer);
        var layerPath = [];
        var layerPathString = "";
        var layerName = "";

        for(var layerIndex = layerCount;layerIndex>=loopStart;layerIndex--){
           
            if(typeIDToStringID(getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'layerSection' ), layerIndex ))=='layerSectionStart'){
                layerSets.push(layerIndex);
              layerName = getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'name' ), layerIndex );
              layerPath[layerPath.length] = layerName;
              layerPathString = layerPath.join(' / ');
                setNames.push(layerPathString);
             
            } else if(typeIDToStringID(getProperty( charIDToTypeID('Lyr '), stringIDToTypeID( 'layerSection' ), layerIndex ))=='layerSectionEnd'){
              layerPath.pop();
           }
        }
       
        var win = new Window( 'dialog', 'Send Layer to Group' );
        win.dropdown = win.add("dropdownlist", undefined, setNames);
        win.dropdown.preferredSize.width = 200;
        win.dropdown.items[0].selected = true;
        win.ok = win.add('button',undefined,'ok');
        var res = win.show();
        if(res==1) moveTo(loopStart==0 ?  layerSets[win.dropdown.selection.index]:layerSets[win.dropdown.selection.index]-1);
        }else{
            alert('Document does not contain layerSets');
         }
     }else{
         alert('You can not move Background');
     }
christianmagill

Script request: Send to group

Post by christianmagill »

Much Thanks! Works like a charm.

The only thing I've added below is a try/catch in case the move command fails, for instance, if the selection includes a parent of the destination... however unlikely.

Code: Select allfunction moveTo(index) {
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putEnumerated(charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
    desc.putReference(charIDToTypeID('null'), ref);
    var ref = new ActionReference();
    ref.putIndex(charIDToTypeID('Lyr '), index);
    desc.putReference(charIDToTypeID('T   '), ref);
    desc.putBoolean(charIDToTypeID('Adjs'), false);
    desc.putInteger(charIDToTypeID('Vrsn'), 5);
    executeAction(charIDToTypeID('move'), desc, DialogModes.NO);
};

function getNumberOfLayers() {
    var ref = new ActionReference();
    ref.putProperty(charIDToTypeID('Prpr'), charIDToTypeID('NmbL'));
    ref.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
    return executeActionGet(ref).getInteger(charIDToTypeID('NmbL'));
};

function getProperty(psClass, psKey, index) { // integer:Class, integer:key
    var ref = new ActionReference();
    if (psKey != undefined) ref.putProperty(charIDToTypeID("Prpr"), psKey);
    if (index != undefined) {
        ref.putIndex(psClass, index);
    } else {
        ref.putEnumerated(psClass, charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
    }
    try {
        var desc = executeActionGet(ref);
    } catch (e) {
        return;
    } // return on error
    if (desc.count == 0) return; // return undefined if property doesn't exists
    var dataType = desc.getType(psKey);
    switch (dataType) { // not all types supported - returns undefined if not supported
        case DescValueType.INTEGERTYPE:
            return desc.getInteger(psKey);
            break;
        case DescValueType.ALIASTYPE:
            return desc.getPath(psKey);
            break;
        case DescValueType.BOOLEANTYPE:
            return desc.getBoolean(psKey);
            break;
        case DescValueType.BOOLEANTYPE:
            return desc.getBoolean(psKey);
            break;
        case DescValueType.UNITDOUBLE:
            return desc.getUnitDoubleValue(psKey);
            break;
        case DescValueType.STRINGTYPE:
            return desc.getString(psKey);
            break;
        case DescValueType.OBJECTTYPE:
            return desc.getObjectValue(psKey);
            break;
        case DescValueType.LISTTYPE:
            return desc.getList(psKey);
            break;
        case DescValueType.ENUMERATEDTYPE:
            return desc.getEnumerationValue(psKey);
            break;
    }
};

var doc = app.activeDocument;
if (!doc.activeLayer.isBackgroundLayer) {
    if (doc.layerSets.length > 0) {
        var layerCount = getNumberOfLayers();
        var layerSets = [];
        var setNames = [];
        var loopStart = Number(!doc.layers[doc.layers.length - 1].isBackgroundLayer);
        var layerPath = [];
        var layerPathString = "";
        var layerName = "";

        for (var layerIndex = layerCount; layerIndex >= loopStart; layerIndex--) {

            if (typeIDToStringID(getProperty(charIDToTypeID('Lyr '), stringIDToTypeID('layerSection'), layerIndex)) == 'layerSectionStart') {
                layerSets.push(layerIndex);
                layerName = getProperty(charIDToTypeID('Lyr '), stringIDToTypeID('name'), layerIndex);
                layerPath[layerPath.length] = layerName;
                layerPathString = layerPath.join(' / ');
                setNames.push(layerPathString);

            } else if (typeIDToStringID(getProperty(charIDToTypeID('Lyr '), stringIDToTypeID('layerSection'), layerIndex)) == 'layerSectionEnd') {
                layerPath.pop();
            }
        }

        var win = new Window('dialog', 'Send Layer to Group');
        win.dropdown = win.add("dropdownlist", undefined, setNames);
        win.dropdown.preferredSize.width = 200;
        win.dropdown.items[0].selected = true;
        win.ok = win.add('button', undefined, 'ok');
        var res = win.show();
        if (res == 1) {
            try {
                moveTo(loopStart == 0 ? layerSets[win.dropdown.selection.index] : layerSets[win.dropdown.selection.index] - 1);
            } catch () {
                alert('Unable to move selection. Please ensure your selection does not contain a parent of the destination.');
            }
        }
    } else {
        alert('Document does not contain layerSets');
    }
} else {
    alert('You can not move Background');
}