Simple action button installer

Photoshop Script Snippets - Note: Full Scripts go in the Photoshop Scripts Forum

Moderators: Tom, Kukurykus

kpt

Simple action button installer

Post by kpt »

Here's some code that may be useful to programmers who needs to send various scripts to other Photoshop users.

The script automatically creates and loads an .atn-file with a single action that calls the script next time, thus relieving the user from doing File > Scripts > Browse... when they want to invoke it.

As a programmer, you would have the below code at the beginning of your script, which must be named install-NAME.jsx (where NAME is something you choose). By calling BUTTON.installcheck() two things happen:

The file is renamed to NAME.jsx
An action file is created and loaded, which contains a button that calls NAME.jsxThere are three parameters that can be changed: the name and color of the button, and whether the script should continue with execution after installation.

Let me know if you find it useful, or if you can suggest improvements.

Code: Select all#target "photoshop"

BUTTON = function BUTTON() { };

/**
 *
 * Simple button shortcut installer for Photoshop scripts (c) Kjell Post, kjell@irstafoto.se
 *
 * Creates and loads an action file which calls this script.
 *
 * Usage:
 *   1) This file must have the name "install-NAME.jsx" where NAME can be whatever you want (within bounds).
 *   2) When executed, via a double click or via File > Scripts > Browse... the script does the following:
 *     2a) renames this file to NAME.jsx
 *     2b) creates an action file NAME.atn
 *     2c) loads the action file, which has an action set with an action BUTTON.NAME (see below)
 *     2d) when the button is pressed, NAME.jsx (i.e., this file) is called.
 *     2e) you can replace "your code here" at the bottom with your code.
 */

BUTTON.NAME = "Test";      /* replace this with your name!! */
BUTTON.COLOR = 1;      /* 0=NONE, 1=RED, 2=ORANGE, 3=YELLOW, 4=GREEN, 5=BLUE, 6=PURPLE, 7=GRAY */
BUTTON.CONTINUE = true;      /* continue with executing the script after installing button? */

/**
 *
 *  Installation code
 *
 */


/* --- BinaryFile class --- */

BUTTON.BinaryFile = function(fname)  {
  this.fname = fname;
  this.contents = "";
};

BUTTON.BinaryFile.prototype.writeFile = function() {
  if (this.contents.length == 0) {
    alert("Error\nNo content for action file. Contact us for support.");
    return;
  }
  var file = new File(this.fname);
  if (!file.open("w")) {
    return false;
  } else {
      file.encoding = "BINARY";
      file.write(this.contents);
      file.close();
      this.contents = "";
      return true;
  }
};

BUTTON.BinaryFile.prototype.putString = function(s) { this.contents += s; };
BUTTON.BinaryFile.prototype.putByte = function(b) { this.contents += String.fromCharCode(b); };
BUTTON.BinaryFile.prototype.putBoolean = function (b) { this.putByte(b ? 1 : 0); };

BUTTON.BinaryFile.prototype.putInt16 = function(i) {
  this.putByte((i >> 8) & 0xff);
  this.putByte(i & 0xff);
};


BUTTON.BinaryFile.prototype.putWord = function(w) {
  this.putByte((w >> 24) & 0xff);
  this.putByte((w >> 16) & 0xff);
  this.putByte((w >>  8) & 0xff);
  this.putByte((w >>  0) & 0xff);
};

BUTTON.BinaryFile.prototype.putUnicode = function(s) {
  this.putWord(1 + s.length);
  for (var i = 0; i < s.length; i++)
    this.putInt16(s.charCodeAt(i));
  this.putInt16(0);
};

BUTTON.BinaryFile.prototype.putAscii = function(s) { this.putWord(s.length); this.putString(s); };

BUTTON.BinaryFile.prototype.putRAW = function(s) {
  for (var i = 0; i < s.length; i++)
    this.putByte(s.charCodeAt(i));
};

/* --- end of BinaryFile class --- */

BUTTON.getScriptFile = function() {  /* courtesy of xbytor */
  var dbLevel = $.level;
  $.level = 0;
  var path = undefined;

  try {
    some_undefined_variable;
  } catch (e) {
    path = e.fileName;
  }

  $.level = dbLevel;
  return new File(path);
};

BUTTON.deleteActionSet = function(as) {
  var desc = new ActionDescriptor();
  var ref = new ActionReference();
  ref.putName( charIDToTypeID( "ASet" ), decodeURI(as));
  desc.putReference( charIDToTypeID( "null" ), ref );
  executeAction( charIDToTypeID( "Dlt " ), desc, DialogModes.NO );
};

BUTTON.makeActionFile = function(scriptfile) {
  var atnfname = BUTTON.NAME + ".atn";
  var scriptloc = decodeURI(scriptfile.fsName);
  var atnfile = new BUTTON.BinaryFile(scriptloc.replace(".jsx", ".atn"));

  createDescriptor = function(script) {
    var desc = new ActionDescriptor();
    desc.putPath(CT("jsCt"), script);
    desc.putString(CT("jsMs"), script.name);
    return desc;
  };

  atnfile.putWord(0x10);   /* action set version */
  atnfile.putUnicode(BUTTON.NAME); /* actionSet name */
  atnfile.putBoolean(true);   /* actionSet expanded */
  atnfile.putWord(1);      /* number of actions */

  /* write action */
  atnfile.putInt16(0);
  atnfile.putBoolean(false);
  atnfile.putBoolean(false);
  atnfile.putInt16(BUTTON.COLOR);
  atnfile.putUnicode(BUTTON.NAME);
  atnfile.putBoolean(false);
  atnfile.putWord(1);
 
  /* write action item */
  atnfile.putBoolean(false);
  atnfile.putBoolean(true);
  atnfile.putBoolean(false);
  atnfile.putByte(0);
  atnfile.putString("TEXT");
  atnfile.putAscii("AdobeScriptAutomation Scripts");
  atnfile.putAscii("Scripts");
  var descriptor = createDescriptor(scriptfile);
  atnfile.putWord(descriptor ? -1 : 0);
  if (descriptor) {
    var bytes = descriptor.toStream();
    atnfile.putRAW(bytes.slice(4));
  }

  if (!atnfile.writeFile()) {
    alert("Can't generate the action file!\nIs the folder write-protected?\n" + atnfile.fname);
    return false;
  } else {
    return atnfile.fname;
  }
};

BUTTON.installcheck = function() {
  BUTTON.scriptfile = BUTTON.getScriptFile(); /* ~/Downloads/install-skin.jsx file object */
  BUTTON.scriptname = BUTTON.scriptfile.name;
  BUTTON.scriptfolder = BUTTON.scriptfile.parent;
  BUTTON.newScriptname = BUTTON.scriptname.replace("install-", "");
  BUTTON.newScriptfile = new File(BUTTON.scriptfolder + "/" + BUTTON.newScriptname);

  if (BUTTON.scriptname.match(/install/i)) {
    if (!confirm("Would you like to install an action shortcut for " + BUTTON.NAME + "?"))
      return true;      /* ignore installation, just continue */
    if (BUTTON.newScriptfile.exists) {
      if (!confirm("A previous copy of " + BUTTON.newScriptname + " was found\nOK to replace it?"))
   return true;      /* user cancelled, just continue */
      if (!BUTTON.newScriptfile.remove()) {
   alert("Could not remove old copy of " + BUTTON.newScriptname);
   return true;
      }
    }
    if (!BUTTON.scriptfile.rename(BUTTON.newScriptname)) {
      alert("Couldn't rename " + BUTTON.scriptname + " to " + BUTTON.newScriptname);
      return true;
    }
    var atnfile = BUTTON.makeActionFile(BUTTON.newScriptfile);
    if (atnfile) {
      try {
   BUTTON.deleteActionSet(BUTTON.NAME); /* delete old action set */
      } catch (e) {
   /* no previous action set with this name */
      }
      app.load(File(atnfile));
    }
    return BUTTON.CONTINUE; /* return true if you want your code to run as well */
  }
 
  /* this script was invoked through the button shortcut, or manually - just continue */
  return true;      
};

/**
 *
 *
 *  End of installation code
 *
 *
 */

if (BUTTON.installcheck()) {   
    alert("Your code here!");
    /* your code here */
}