// rd_Pre-compose.jsx // Copyright (c) 2007-2013 redefinery (Jeffrey R. Almasol). All rights reserved. // check it: www.redefinery.com // // Name: rd_Pre-compose // Version: 1.0 // // Description: // This script displays a dialog box for pre-composing one or // more layers (like the built-in Pre-compose dialog box), but // adds the ability to trim the pre-comp to the selected layer's // duration, with optional trim handles, and a batch mode // option for performing the same operation on each of the // selected layers. // // Current issue: // -- Using the "Leave all attributes" option on a single selected // negatively stretched layer doesn't trim the layer correctly. // // Note: This version of the script requires After Effects CS5 // or later. // // Originally requested by Gary Jaeger. // Enhancements requested by Jerzy Drozda Jr. (Maltaannon) // and Colin Proctor. // // Legal stuff: // This script is provided "as is," without warranty of any kind, expressed // or implied. In no event shall the author be held liable for any damages // arising in any way from the use of this script. // // In other words, I'm just trying to share knowledge with and help out my // fellow AE script heads, so don't blame me if my code doesn't rate. :-) // rd_Precompose() // // Description: // This function contains the main logic for this script. // // Parameters: // thisObj - "this" object. // // Returns: // Nothing. // (function rd_Precompose(thisObj) { // Globals var rd_PrecomposeData = new Object(); // Store globals in an object rd_PrecomposeData.scriptName = "rd: Pre-compose"; rd_PrecomposeData.scriptTitle = rd_PrecomposeData.scriptName + " v1.0"; rd_PrecomposeData.strNewCompName = {en: "New composition name:"}; rd_PrecomposeData.strLeaveOpt = {en: "Leave all attributes in \\'%s\\'"}; rd_PrecomposeData.strLeaveOpt2 = {en: "Leave all attributes in each selected layer"}; rd_PrecomposeData.strLeaveOptDesc = {en: "Use this option to create a new intermediate composition with only \\'%s\\' in it. The new composition will become the source to the current layer."}; rd_PrecomposeData.strMoveOpt = {en: "Move all attributes into the new composition"}; rd_PrecomposeData.strMoveOptDesc = {en: "Use this option to place the currently selected layers together into a new intermediate composition."}; rd_PrecomposeData.strMoveOptDescMulti = {en: "Use this option to place the currently selected layers together into a new intermediate composition.\n\nThe \"Leave all attributes\" option is not available because more than one layer is selected."}; rd_PrecomposeData.strMoveOptDescNoSrc = {en: "Use this option to place the currently selected layer into a new intermediate composition.\n\nThe \"Leave all attributes\" option is not available because the selected layer does not have source footage."}; rd_PrecomposeData.strBatchMode = {en: "Pre-compose each selected layer"}; rd_PrecomposeData.strBatchModeLayerName = {en: "Use existing layer names for new composition names"}; rd_PrecomposeData.strTrimLayers = {en: "Trim new composition to combined layers\\' duration"}; rd_PrecomposeData.strHeadHandle = {en: "Head handle:"}; rd_PrecomposeData.strTailHandle = {en: "Tail handle:"}; rd_PrecomposeData.strHandleUOM = {en: "secs"}; rd_PrecomposeData.strOK = {en: "OK"}; rd_PrecomposeData.strCancel = {en: "Cancel"}; rd_PrecomposeData.strHelp = {en: "?"}; rd_PrecomposeData.strErrNoCompSel = {en: "Cannot perform operation. Please select or open a single composition in the Project panel, and try again."}; rd_PrecomposeData.strErrNoLayerSel = {en: "Cannot perform operation. Please select at least one layer, and try again."}; rd_PrecomposeData.strMinAE100 = {en: "This script requires Adobe After Effects CS5 or later."}; rd_PrecomposeData.strHelpText = { en: "Copyright (c) 2007-2013 redefinery (Jeffrey R. Almasol). \n" + "All rights reserved.\n" + "\n" + "This script displays a dialog box for pre-composing one or more layers (like the built-in Pre-compose dialog box), but adds the ability to trim the pre-comp to the selected layer's duration, with optional trim handles, and a batch mode option for performing the same operation on each of the selected layers.\n" + "\n" + "Current issue:\n" + "-- Using the \"Leave all attributes\" option on a single selected negatively stretched layer doesn't trim the layer correctly.\n" + "\n" + "Note: This version of the script requires After Effects CS5 or later.\n" + "\n" + "Originally requested by Gary Jaeger.\n" + "Enhancements requested by Jerzy Drozda Jr. (Maltaannon) and Colin Proctor.\n" }; // rd_Precompose_localize() // // Description: // This function localizes the given string variable based on the current locale. // // Parameters: // strVar - The string variable's name. // // Returns: // String. // function rd_Precompose_localize(strVar) { return strVar["en"]; } // rd_Precompose_buildUI() // // Description: // This function builds the user interface. // // Parameters: // thisObj - Panel object (if script is launched from Window menu); null otherwise. // // Returns: // Window or Panel object representing the built user interface. // function rd_Precompose_buildUI(thisObj) { var pal = new Window("dialog", rd_PrecomposeData.scriptName, undefined); if (pal !== null) { var res = "group { \ orientation:'column', alignment:['fill','fill'], alignChildren:['fill','top'], \ compName: Group { \ alignment:['center','top'], \ lbl: StaticText { text:'" + rd_Precompose_localize(rd_PrecomposeData.strNewCompName) + "' }, \ fld: EditText { text:'', characters:31, preferredSize:[-1,20] }, \ }, \ leaveOpt: RadioButton { text:'" + rd_Precompose_localize(rd_PrecomposeData.strLeaveOpt) + "', alignment:['fill','top'], value:true, helpTip:'" + rd_Precompose_localize(rd_PrecomposeData.strLeaveOptDesc) + "' }, \ moveOpt: RadioButton { text:'" + rd_Precompose_localize(rd_PrecomposeData.strMoveOpt) + "', alignment:['fill','top'], helpTip:'" + rd_Precompose_localize(rd_PrecomposeData.strMoveOptDesc) + "' }, \ opts: Group { \ orientation:'column', alignment:['fill','top'], \ batchMode: Checkbox { text:'" + rd_Precompose_localize(rd_PrecomposeData.strBatchMode) + "', alignment:['fill','top'] }, \ batchModeLayerNames: Checkbox { text:'" + rd_Precompose_localize(rd_PrecomposeData.strBatchModeLayerName) + "', alignment:['fill','top'] }, \ trimLayers: Checkbox { text:'" + rd_Precompose_localize(rd_PrecomposeData.strTrimLayers) + "', alignment:['fill','top'] }, \ handles: Group { \ orientation:'row', alignment:['left','fill'], alignChildren:['left','center'], \ headLbl: StaticText { text:'" + rd_Precompose_localize(rd_PrecomposeData.strHeadHandle) + "', enabled:false }, \ headVal: EditText { text:'0', characters:4, enabled:false, preferredSize:[-1,20] }, \ headUOM: StaticText { text:'" + rd_Precompose_localize(rd_PrecomposeData.strHandleUOM) + "', enabled:false }, \ spacer: StaticText { text:' ', alignment:['fill','center'], enabled:false }, \ tailLbl: StaticText { text:'" + rd_Precompose_localize(rd_PrecomposeData.strTailHandle) + "', enabled:false }, \ tailVal: EditText { text:'0', characters:4, enabled:false, preferredSize:[-1,20] }, \ tailUOM: StaticText { text:'" + rd_Precompose_localize(rd_PrecomposeData.strHandleUOM) + "', enabled:false }, \ }, \ }, \ cmds: Group { \ alignment:['fill','top'], \ helpBtn: Button { text:'" + rd_Precompose_localize(rd_PrecomposeData.strHelp) + "', alignment:['left','top'], preferredSize:[-1,20] }, \ okBtn: Button { text:'" + rd_Precompose_localize(rd_PrecomposeData.strOK) + "', alignment:['right','top'], preferredSize:[-1,20] }, \ cancelBtn: Button { text:'" + rd_Precompose_localize(rd_PrecomposeData.strCancel) + "', alignment:['right','top'], preferredSize:[-1,20] }, \ }, \ }"; pal.grp = pal.add(res); pal.grp.opts.margins.top = pal.grp.cmds.margins.top = 10; pal.grp.opts.handles.indent = 20; pal.grp.compName.fld.onChanging = function () { if (this.text.length > 31) this.text = this.text.substr(0, 31); } pal.grp.opts.batchMode.onClick = function () { var enableState = this.value; this.parent.batchModeLayerNames.enabled = enableState; } pal.grp.opts.batchModeLayerNames.onClick = function () { var enableState = this.value; this.parent.parent.compName.lbl.enabled = this.parent.parent.compName.fld.enabled = !enableState; } pal.grp.opts.trimLayers.onClick = function () { var enableState = this.value; //this.parent.handles.enabled = enableState; // doesn't work in AE CS6 this.parent.handles.headLbl.enabled = enableState; this.parent.handles.headVal.enabled = enableState; this.parent.handles.headUOM.enabled = enableState; this.parent.handles.spacer.enabled = enableState; this.parent.handles.tailLbl.enabled = enableState; this.parent.handles.tailVal.enabled = enableState; this.parent.handles.tailUOM.enabled = enableState; } pal.grp.opts.handles.headVal.onChange = pal.grp.opts.handles.tailVal.onChange = function () { var value = parseFloat(this.text); if (isNaN(value) || (value < 0.0)) value = 0; else if (value > 9999) value = 9999; this.text = value.toString(); } pal.grp.cmds.helpBtn.preferredSize.width = 25; pal.grp.cmds.helpBtn.onClick = function () {alert(rd_PrecomposeData.scriptTitle + "\n" + rd_Precompose_localize(rd_PrecomposeData.strHelpText), rd_PrecomposeData.scriptName);} pal.grp.cmds.okBtn.onClick = rd_Precompose_precomp; var comp = app.project.activeItem; pal.grp.leaveOpt.text = rd_Precompose_localize(rd_PrecomposeData.strLeaveOpt).replace("\\'%s\\'", "'"+comp.name+"'"); pal.grp.leaveOpt.helpTip = rd_Precompose_localize(rd_PrecomposeData.strLeaveOptDesc).replace("\\'%s\\'", "'"+comp.selectedLayers[0].name+"'"); if (comp.selectedLayers.length === 1) { // The "Leave all attributes" option is available only if the selected layer has source if (comp.selectedLayers[0].source === null) { pal.grp.moveOpt.value = true; pal.grp.moveOpt.helpTip = rd_Precompose_localize(rd_PrecomposeData.strMoveOptDescNoSrc); pal.grp.leaveOpt.enabled = false; } pal.grp.compName.fld.text = comp.selectedLayers[0].name.substr(0, 31-(" Comp 1".length)) + " Comp 1"; // No batch mode for a single selected layer pal.grp.opts.batchMode.enabled = pal.grp.opts.batchMode.value = pal.grp.opts.batchModeLayerNames.enabled = pal.grp.opts.batchModeLayerNames.value = false; } else { pal.grp.compName.fld.text = "Pre-comp 1"; pal.grp.moveOpt.value = true; pal.grp.moveOpt.helpTip = rd_Precompose_localize(rd_PrecomposeData.strMoveOptDescMulti); pal.grp.leaveOpt.text = rd_Precompose_localize(rd_PrecomposeData.strLeaveOpt2); pal.grp.leaveOpt.enabled = false; // Batch mode is available for multiple selected layer, but off by default pal.grp.opts.batchMode.enabled = true; pal.grp.opts.batchMode.value = pal.grp.opts.batchModeLayerNames.value = false; pal.grp.opts.batchModeLayerNames.enabled = false; } pal.layout.layout(true); } return pal; } // rd_Precompose_precomp() // // Description: // This function performs the actual pre-comp operation. // // Parameters: // None. // // Returns: // Nothing. // function rd_Precompose_precomp() { function processSelected(win) { var layerIndices = new Array(), layer, layerIn, layerOut; var earliest=comp.duration, latest=0; for (var i=0; i latest) latest = layerOut; } if (earliest < 0.0) earliest = 0.0; if (latest > comp.duration) latest = comp.duration; var precomp = comp.layers.precompose(layerIndices, compName, moveAttrs); var precompLayer = comp.selectedLayers[0]; // created precomp should be the single selected layer var totalDur = latest - earliest; // Check if need to trim the precomp var headHandle = 0.0; // default amount of shifting of layers from start of pre-comp if (win.opts.trimLayers.value) { headHandle = parseFloat(win.opts.handles.headVal.text); var tailHandle = parseFloat(win.opts.handles.tailVal.text); totalDur = totalDur + headHandle + tailHandle; } // Shift pre-comped layers to start of comp for (var i=1; i<=precomp.numLayers; i++) { precomp.layer(i).startTime -= earliest - headHandle; } if (moveAttrs) { // Trim precomp to appropriate duration precomp.duration = totalDur; } // Shift pre-comp layer if (precompLayer.stretch < 0) { precompLayer.inPoint = latest; precompLayer.outPoint = earliest; } else { precompLayer.startTime = earliest - headHandle; precompLayer.inPoint = earliest; precompLayer.outPoint = latest; } } var compName = this.parent.parent.compName.fld.text.substr(0, 31); var moveAttrs = this.parent.parent.moveOpt.value; var batchMode = this.parent.parent.opts.batchMode.value; var batchModeLayerNames = this.parent.parent.opts.batchModeLayerNames.value; this.parent.parent.parent.close(); app.beginUndoGroup(rd_PrecomposeData.scriptName); var comp = app.project.activeItem; if (batchMode) { // Save the originally selected layers, in case we are using batch mode var savedLayerIndices = new Array(); for (var i=0; i