Making Your Plug-In Scriptable
Transcription
Making Your Plug-In Scriptable
Making Your Plug-In Scriptable Technical Note #10082 Version InDesign CS/InCopy CS 11 Sep 2003 ADOBE SYSTEMS INCORPORATED Corporate Headquarters 345 Park Avenue San Jose, CA 95110-2704 (408) 536-6000 Copyright 2003 Adobe Systems Incorporated. All rights reserved. The information in this document is furnished for informational use only, is subject to change without notice, and should not be construed as a commitment by Adobe Systems Incorporated. Adobe Systems Incorporated assumes no responsibility or liability for any errors or inaccuracies that may appear in this document. The software described in this document is furnished under license and may only be used or copied in accordance with the terms of such license. Adobe, Adobe After Effects, Adobe InDesign, Adobe PhotoDeluxe, Adobe Premiere, Adobe Photoshop, Adobe Illustrator, Adobe Type Manager, ATM and PostScript are trademarks of Adobe Systems Incorporated that may be registered in certain jurisdictions. Macintosh and Apple are registered trademarks, and Mac OS is a trademark of Apple Computer, Inc. Microsoft, Windows, Windows 95, Windows 98, and Windows NT are registered trademarks of Microsoft Corporation. All other products or name brands are trademarks of their respective holders.. Rev # Date Author Comments 0.1 09 Apr 2003 Ken Sadahiro First draft for InDesign/InCopy 3.0 Beta SDK. (Most of the 3.0 specific material was written by Jonathan Brown.) 0.2 11 Apr 2003 Ken Sadahiro Second draft. 0.3 14 Apr 2003 Ken Sadahiro Third draft, corrected images, code indentation, and spelling. Inserted new image for Visual Basic Object Browser. 0.4a 23 May 2003 Ken Sadahiro Fourth draft. Included review notes/corrections/further comments from Jonathan Brown and Steve Pellegrin. 1.0 14 Jul 2003 Ken Sadahiro Updated the “InDesign CS” and “InCopy CS” designations, reimported screen shots. 1.1 03 Sep 2003 Ken Sadahiro Further updates based on review by Jonathan Brown. Reflected changes between last prerelease version and latest builds. 1.2 11 Sep 2003 Ken Sadahiro Added registry cleanup instructions for users of Windows pre-release builds. Contents Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Terminology and Definitions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 What is Scripting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 The Benefits in Making a Plug-in Scriptable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 What is Involved in Making a Plug-in Scriptable? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Necessary Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 Architectural Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 General Description of the Scripting Process. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Interactions in the Scripting Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Example Plug-in: Custom Prefs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Recipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 How to Add a New Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Exposing a New Property or Event on an Existing Object . . . . . . . . . . . . . . . . . . . . . . . 18 Defining IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Reviewing the New Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 Supporting The InDesign Interchange File Format: Additional Info . . . . . . . . . . . . . . . . . 21 Verifying That Your New Script Objects Are Exported In The New InDesign Interchange File Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Tips for Debugging the Scripting Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Converting Existing Scriptable Plug-ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS . . . . 25 Converting Script Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 API Changes Between InDesign 2.0.x And InDesign CS . . . . . . . . . . . . . . . . . . . . . . . . . 31 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Resource Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 APIs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Appendix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 How did the Scripting Architecture Change from InDesign/InCopy 2.x to InDesign CS/InCopy CS? 52 Using The TypeInfoConverter Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Additional Resources For Further Understanding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 iii Contents iv 11 Sep 2003 Making Your Plug-In Scriptable 1 #10082 Making Your Plug-in Scriptable Introduction This technical note describes the benefits and the process of adding scripting support to an InDesign CS/InCopy CS plug-in. This document is the programming counterpart to the InDesign CS/InCopy CS Scripting Guide, which provides details on script programming approaches and a script object reference. Both the Scripting Guide and this technical note are recommended reading if you, as a plug-in developer, are considering a script-based solution. This technical note also describes the relationship between scripting support and the new InDesign Interchange file format in InDesign CS. Terminology and Definitions This section defines terms used within this document. • Apple Events; High-level, semantic messages designed to allow for collaboration between programs. • Apple Event Terminology extension (AETE); A resource that defines Apple Events and objects which your application understands, both for scripts and for applications that send you events. Developed by Apple. • Component Object Model (COM); A distributed, object-oriented system for creating binary software components that can interact with each other. Developed by Microsoft. • Object Description Language (ODL); A Windows-specific description about COM objects. This is described in an .ODL file, and is compiled by the Microsoft Interface Definition Language (MIDL) compiler. The output of the MIDL compiler is an COM type library that is linked in with the plug-in as a resource. • Type Library; Binary files (.tlb files) that include information about types and objects exposed by a COM application. A type library can contain any of the following: (1) Information about data types, such as aliases, enumerations, structures, or unions. (2) Descriptions of one or more objects, such as a module, interface, IDispatch interface (dispinterface), or component object class (coclass). Each of these descriptions is commonly referred to as a typeinfo. (3) References to type descriptions from other type libraries. • JavaScript; A scripting language to automate the document object model within an HTML browser, such as Netscape Navigator or Microsoft Internet Explorer. • Script; A series of commands that tell InDesign/InCopy to perform a series of actions. These actions may only involve an item in an InDesign/InCopy document or may involve #10082 Making Your Plug-in Scriptable 5 #10082 Making Your Plug-in Scriptable What is Scripting many documents and additional applications such as word processors, spreadsheets, and databases. • Script object; Target of an event (method call), or property request issued by execution of a user script. • Property; An attribute of the script object. • Event; A method call on a script object. • Host Application; The application that provides the script-based automation support. In this technical note, this primarily refers to InDesign/InCopy. • Script provider; A boss that provides an implementation to find a script object, get and set its properties, or execute its methods (called Events). • Script manager; Application-supplied managers represent supported languages, AppleScriptMgr for AppleScript or OLEAutomationMgr for Visual Basic. • Script language; The language in which you write your scripts. For InDesign/InCopy, three scripting languages are supported: AppleScript, Visual Basic and JavaScript. • InDesign/InCopy Document Object Model (DOM); Set of objects, properties, and events that make up a document, including page items on the document hierarchy, and various preferences. • InDesign/InCopy Object Model; Implementation view of the document—bosses, interfaces, implementations. What is Scripting InDesign/InCopy allows users of the applications to automate layout and text editing tasks, by means of executing instructions in a script, written in AppleScript, Visual Basic/COM, or JavaScript. By playing back the script in the appropriate environment, such as AppleScript or Visual Basic, you can perform tasks in an automated manner or with a customized interaction. For the application user, scripting is useful when the task at hand is very repetitive or cumbersome to perform. In some cases, scripting provides a convenient way to prototype an automated and/or customized user interaction and in some cases may be considered a viable alternative to plug-in development, when the task you want to automate can be achieved using the functionality provided by the application (most of the application already comes with scripting support built-in). This technical note will not discuss how to write such scripts for InDesign/InCopy, but will focus instead on how to extend your own plug-in so that its features can be automated by a script. If you are looking for details on how to write scripts for InDesign/InCopy, please instead refer to the Scripting Guide, which is available on the application's CD-ROM, and on the internet at http://www.adobe.com/products/indesign/scripting.html. NOTE: The rest of this technical note is targeted for the plug-in developer. 6 #10082 Making Your Plug-in Scriptable What is Scripting The Benefits in Making a Plug-in Scriptable For the plug-in developer, the benefits are of making a plug-in scriptable are as follows: • InDesign/InCopy users can automate the features added by your plug-in by writing and executing scripts. • You can seamlessly provide support for custom DOM objects and properties added by your plug-in, when a user saves a document in the InDesign Interchange file format. The potential for new solutions using automation with InDesign means added productivity for you and your customers, as explained in the previous section. In InDesign CS, if your plug-ins implement custom DOM objects, such as custom page items, and additional persistent data, you can seamlessly augment the InDesign Interchange file format simply by adding scripting support to your objects. What is Involved in Making a Plug-in Scriptable? The principal costs of adding scripting support for your plug-in are in: • Designing and implementing the addition of scripting objects, methods and properties. This depends on how many scripting objects, methods and properties you need to implement. By reading the Recipes section of this guide, this should take about a day or two. • Rearchitecting your code to fit into the scripting architecture. The time required for rearchitecting depends on the complexity of your plug-in and how well separated your user interface was from your model. This technical note does not make any suggestions on how to refactor your own plug-in code, however, a common refactoring task may be to decouple the user interface and model related source code in your plug-ins. A good way to think of the scripting architecture is that it is another user interface. • Testing. Testing your scripting solution involves writing separate scripts on two platforms: Macintosh and Windows. With the refactoring of the scripting architecture (especially in the resource definitions) in InDesign CS/InCopy CS, the necessity for complete platformspecific testing has been reduced. While this technical note does not go into detail about testing your scriptable plug-ins, you can refer to Reviewing the New Resources in the Recipes section to learn how to verify that your scriptable plug-in does indeed expose the scripting objects you intend. • Documenting your solution. Documenting your script provider depends on the complexity of your plug-in. Also, the scripting language syntax differs by platform, so naturally, so will your documentation. This is an exercise left to the reader of this technical note. Necessary Tools To make your plug-in scriptable, you need the following tools: #10082 Making Your Plug-in Scriptable 7 #10082 Making Your Plug-in Scriptable Architectural Overview • The current integrated development environment (IDE) that you use for developing plug-ins. Refer to the Readme file in the InDesign CS/InCopy CS SDK for the latest supported IDEs and their versions. • An environment to develop and run scripts (also to verify that your scriptable plug-in is exposing the right script objects) — Macintosh: AppleScript Editor — Windows: Microsoft Visual Basic, VBA/VBScript environment, or any COM development environment (including Microsoft Visual C++) — Both platforms: JavaScript editor and environment (Refer to Reviewing the New Resources in the Recipes section for steps on how to verify your script objects.) Architectural Overview This section provides a general architectural description of scripting for InDesign/InCopy. Details on how to create the necessary resource files and use the relevant interfaces are contained in the section titled “Recipes”. General Description of the Scripting Process 8 1. Once the host application launches, the scripting system architecture support is available to the user through scripts. However, nothing is activated until a scripting event is received. 2. When a scripting event is received, the script manager (either AppleScriptMgr for Macintosh or OLEAutomationMgr for Windows) is responsible for figuring out what to do with that event. Basically, after doing setup, checks, and so on, the script manager populates the IScriptEventData interface with the data that has been passed in. 3. The script manager invokes the IScriptRequestHandler via one of the basic entry points: DoAccessProperty(), DoSetProperty(), DoHandleEvent(), or DoGetObject(), which in turn invokes the appropriate script provider. • If a property comes in, it is the script service provider's responsibility to either get or set the property based on the parameters passed in through the IScriptEventData interface. If it is a get operation, the script provider simply returns the requested data from the model. If it is a set operation, the script provider extracts the input and either executes its code successfully (i.e. modifying the model) or returns an error. • Events work in a similar fashion. Some events don't require any data at all, but most events require some set of parameters. The script provider uses the IScriptEventData interface to get that input, executes any necessary model modification via commands, and either returns data or an error code. #10082 Making Your Plug-in Scriptable Architectural Overview 4. Then, control goes back to the script manager, which does cleanup and conversion. The script manager is responsible for taking the data in the IScriptEventData interface and converting it back into something the scripting language understands. 5. Finally, the script manager returns, and the control is passed back to the script. Interactions in the Scripting Architecture Generally speaking, the scripting architecture, scripting languages and the object model all interact to provide support for scripting. (For implementation details, refer to the Recipes section.) • Interaction with Scripting Languages InDesign supplies managers and interfaces to handle the interaction between your plugin and the scripting language. This section describes the pieces necessary to enable a plugin to receive a scripting event from the scripting manager and to report data back to the scripting manager (step 3 in the ordered sequence above). 1. Scripting Language Resources One portion of the language interaction every scripting plug-in needs to supply is a ScriptElementInfo resource (new in InDesign CS/InCopy CS). (NOTE: In InDesign/InCopy 2.x, this used to be implemented in platform specific resources, namely the AETE Resource for Macinotsh and ODL for Windows.) In this resource, you identify details about objects, relationships with other objects, and map them to unique four-character script IDs. (To discover the unique IDs reserved by the application, see source/public/includes/ScriptingDefs.h).The contents of these resources will be discussed in detail later in this document. 2. IScript Interface The IScript interface is used as a marker in a boss to identify a scriptable object. The script object is the target of an event, method call, or property request issued by execution of a user script. For example, the application-supplied Document object is the target of the Save() method and of the Selection property. The need to implement an IScript interface depends on whether you want to add functionality to an application-supplied object, or are implementing one of your own. If you want to add functionality to an application-supplied object, such as the Document, you do not implement the IScript interface. If you are creating a new script object, you must define a boss class that implements the IScript interface, which maps your boss to the scripting object identified by its four-character ID. The details will be discussed later in the document. • Interaction with the model As with other user interfaces you would give a plug-in, scripting is intended to query and change the model. To accomplish this, we must be able to identify objects in the hierarchy DOM via IScript and get and/or set values. Furthermore, we must carefully design our own additions to the model in the form of objects and properties. This section describes #10082 Making Your Plug-in Scriptable 9 #10082 Making Your Plug-in Scriptable Architectural Overview the parts of the plug-in that enable the scripting manager to access and/or modify the model (namely step3 in the ordered sequence above). 1. Script provider boss base classes When you implement a script provider boss, you extend kBaseScriptProviderBoss. (Refer to the HTML-based Reference in the SDK to examine what interfaces and implementation are aggregated on these bosses.) 2. IScriptProvider Supply the IScriptProvider interface implementation to help the script manager find an object, get and set its properties, and execute its events. It is in the methods of this interface where you change the model. Example Plug-in: Custom Prefs Let's examine one of the SDK sample plug-ins, CustomPrefs (a plug-in that adds a custom prefs object of bool16 type to the workspace and document workspace bosses), to see how scripting support is added to a plug-in that only provides the model, a user interface (in this case, the preference panel) and a command to modify the model. A prefs object is a special kind of script object. • Defining the Script Object In defining your script object, you must first determine whether you need to create your own script object, or use an existing script object, provided that a meaningful one exists. (To see a list of the existing objects, look at ScriptClasses in ScriptingDefs.h and at ScriptingWindowDefs.h in the SDK’s API directory.) In the CustomPrefs plug-in, we had added a custom preference which requires a custom object, known as a Preferences Object. When you create a custom object, you must decide where you want to place it within the DOM hierarchy. The CustomPrefs plug-in adds a preference to the kWorkspaceBoss and kDocWorkspaceBoss bosses, which corresponds to the Application and Document objects. 10 #10082 Making Your Plug-in Scriptable Architectural Overview Below is a screen shot of the Visual C++ OLE Type Library Browser. You can see the Document object and its properties and events. • Properties and Events Your script object may have methods and/or properties. The CustomPrefs plug-in adds a single property called "pref value", which is a readable/writable bool16 value. The plug-in adds no events (akin to methods in a C++ class), however, if we were to add one, say "FlipValue( )", we would define it on the same object. • Summary of the New Script Object You can prepare a table (like the one below) that shows for the methods and properties of each object your plug-in will support. In it, you define method and property names, decide on data types, read-write access, and finally provide a description of the method or property. TABLE 1.1 Defining your new objects, properties, and events DOM Object Custom Preference New object? Yes Parent object(s) Application, Document Collection? No Properties pref value: Boolean (read/write) Events n/a Description The custom preference property value added in by the CustomPrefs plug-in. #10082 Making Your Plug-in Scriptable 11 #10082 Making Your Plug-in Scriptable Architectural Overview • Defining the ScriptElementInfo Resource The next step is to define the ScriptElementInfo resource as an ODFRC resource, so that the script manager can add our object to the DOM where we want it. We would write this resource info in a .fr file. The necessary parts to this resource for a Preferences Object are as shown below. — Object: Defines our custom object. — Property: One for the custom object itself, as it will be considered a property of the Application and Document objects, and one for the boolean value. — Provider: Defines where the custom object will be located in the DOM, and specifies the script provider boss. The following ScriptElementInfo resource can be found in {SDK}/source/samplecode/customprefs/CstPrf.fr. In the ScriptElementInfo resource below, the IDs we need to define are denoted by italicized text. (Comments have been removed from the example below for brevity. Descriptions of each of the fields can be found in the "References" section, later in this technical note.) resource ScriptElementInfo(1) { kCoreScriptManagerBoss, kWildFS, k_Wild, { Object { kCstPrfObjectScriptElement, c_CustomPref, "Custom preference", "The Custom preferences object....", kCPrefs_CLSID, NoPluralInfo, kPreferencesObjectScriptElement, kPreferencesSuiteScriptElement, NoSampleScripts, } Property { kCstPrfPropertyScriptElement, p_CustomPref, "Custom preferences", "The custom preference property...", ObjectType( kCstPrfObjectScriptElement ), {} kNoAttributeClass, } Property 12 #10082 Making Your Plug-in Scriptable Architectural Overview { kCstPrfPrefValuePropertyScriptElement, p_PrefVal, "pref value", "The custom preference property value...", BoolType, {} kNoAttributeClass, } Provider { kCstPrfScriptProviderBoss, { Parent { kApplicationObjectScriptElement }, Parent { kDocumentObjectScriptElement }, RepresentObject{ kCstPrfObjectScriptElement }, ParentProperty{ kCstPrfPropertyScriptElement, kReadOnly }, Property{ kCstPrfPrefValuePropertyScriptElement, kReadWrite }, } } } }; • Defining IDs In the ScriptElementInfo resource above, we needed to define several IDs. The IDs can be divided into the following categories: 1. 2. IDs in the kScriptInfoIDSpace: This includes script architecture related IDs for suites, objects, properties, and events. (These can be found in {SDK}/source/samplecode/customprefs/CstPrfID.h.) These IDs fall into this category: — kCstPrfObjectScriptElement — kCstPrfPropertyScriptElement — kCstPrfPrefValuePropertyScriptElement IDs in the kClassIDSpace and kImplementationIDSpace: These are necessary since we are creating bosses and providing implementations. (These can be found in {SDK}/source/samplecode/customprefs/CstPrfID.h.) These IDs fall into this category: — kCstPrfScriptProviderBoss — kCstPrfScriptProviderImpl #10082 Making Your Plug-in Scriptable 13 #10082 Making Your Plug-in Scriptable Architectural Overview 3. 4. Script IDs: These symbols are macro definitions for 4-letter IDs (used by the integer value they represent) that are used by the script manager to identify objects (prefixed with a "c_"), properties ("p_"), events ("e_"), suites ("s_"), and enumeration constants ("en_"). The assigned values must be unique. (These can be found in {SDK}/source/samplecode/customprefs/CstPrfScriptingDefs.h.) These IDs fall into this category: — #define c_CustomPref 'cprf' — #define p_CustomPref 'sprf' — #define p_PrefVal 'pVal' GUIDs: These are Windows-specific IDs (GUID = Globally Unique IDs) that allow COM to interact with the script manager. These were generated using the Microsoft GUID generator GUIDGEN.EXE, then placed into a DECLARE_GUID macro defined in {SDK}/source/public/includes/ScriptingWindowDefs.h. (The IDs below can be found in {SDK}/source/samplecode/customprefs/CstPrfScriptingDefs.h.) This ID falls into this category: — • kCPrefs_CLSID Creating a Script Provider Boss Note that in the Provider part of the ScriptElementInfo resource above, we specified the ID of our script provider boss as kCstPrfScriptProviderBoss. Now we need to define this boss class. Our boss class inherits from kBaseScriptProviderBoss, as shown below. (This can also be found in {SDK}/source/samplecode/customprefs/CstPrf.fr.) Class { kCstPrfScriptProviderBoss, kBaseScriptProviderBoss, { IID_ISCRIPTPROVIDER, kCstPrfScriptProviderImpl, } }, • Writing an implementation for IScriptProvider The last step in adding our Preferences Object is to provide an implementation of the IScriptProvider interface. As shown in the Class declaration above, we shall call our implementation class CstPrfScriptProvider. (This can be found in {SDK}/source/samplecode/customprefs/CstPrfScriptProvider.cpp.) In this implementation, we do the following: 14 — Since our object is a Preference Object, the CstPrfScriptProvider inherits from the PrefsScriptProvider base class, provided by the API (PrefsScriptProvider.h) — The constructor calls DefinePreference() to declare the new Custom Preferences object to the PrefsScriptProvider base class. This base class will handle the ScriptID for our the actual Custom Preferences object, so we don't need to worry about it. #10082 Making Your Plug-in Scriptable Recipes — • We implement the AccessProperty() method that responds with the appropriate action when the passed-in ScriptID is p_PrefVal. This ID corresponds to the bool16 value in our Preference Object. Building and testing the scriptable plug-in Once we have defined the ScriptElementInfo with IDs, created the script provider boss, and written the IScriptProvider implementation, we are ready to build and test the plugin. When you add a new script provider into the DOM, it is recommended that you clean out your Saved Data files in the application defaults folder so that the necessary platformspecific resources will be generated at application startup. The platform-specific resources are generated when the Saved Data files are regenerated. (Refer to Reviewing the New Resources in the Recipes section for the path to your application defaults folder.) You can then write a script to test the entire interaction between the script and your scriptable plug-in. Recipes How to Add a New Object If it is a UID-based object 1. Create an IScript implementation for the object that subclasses class CScript and aggregate to the object's boss. 2. Aggregate the IScript implementation to the object's boss along with the default IAutomationRefCount implementation kAutomationRefCountImpl (defined in VisualBasicID.h, wrap with #ifdef WINDOWS . . . #endif) (in .fr file) Class { kYourBoss, kInvalidClass, { ... //Other interfaces on your boss IID_ISCRIPT, kYourScriptImpl, #ifdef WINDOWS IID_IAUTOMATIONREFCOUNT, kAutomationRefCountImpl, #endif } } 3. Create a script provider boss that subclasses kBaseScriptProviderBoss. 4. Create an IScriptProvider implementation that subclasses class RepresentScriptProvider and aggregate to the script provider boss. #10082 Making Your Plug-in Scriptable 15 #10082 Making Your Plug-in Scriptable Recipes Class { kYourScriptProviderBoss, kBaseScriptProviderBoss, { IID_ISCRIPTPROVIDER, kYourScriptProviderImpl, } } 5. Create necessary ScriptElementInfo resources for the object, events, properties, and provider defined above. If it's NOT a UID-Based Object and if the object has a boss 1. Make your boss a subclass of kBaseProxyScriptObjectBoss (if you can't do this, proceed as "If the Object does NOT have a Boss" below). 2. Create an IScript implementation for the object that subclasses class CProxyScript and aggregate to the object's boss. Class { kYourBoss, kBaseProxyScriptObjectBoss, { ... //Other interfaces on your boss IID_ISCRIPT, kYourScriptImpl, } } 3. Create a script provider boss that subclasses kBaseScriptProviderBoss. 4. Create an IScriptProvider implementation that subclasses class RepresentScriptProvider and aggregate to the script provider boss. Class { kYourScriptProviderBoss, kBaseScriptProviderBoss, { IID_ISCRIPTPROVIDER, kYourScriptProviderImpl, } } 5. By default proxy objects are specified by index. If you want to specify by another property (e.g., name): a. 6. 16 In your implementation of IScript, override IScript::GetScriptObject and substitute code to build your custom ScriptObject. Create ScriptElementInfo resources for the object, events, properties, and provider. #10082 Making Your Plug-in Scriptable Recipes If it's NOT a UID-Based Object and if the Object does NOT have a Boss 1. Create a proxy script object boss that subclasses kBaseProxyScriptObjectBoss. 2. Create an IScript implementation for the object that subclasses class CProxyScript and aggregate to the proxy script object boss. Class { kYourScriptObjectBoss, kBaseProxyScriptObjectBoss, { IID_ISCRIPT, kYourScriptImpl, } } 3. Create a script provider boss that subclasses kBaseScriptProviderBoss. Then create an IScriptProvider implementation that subclasses class RepresentScriptProvider and aggregate to the script provider boss Class { kYourScriptProviderBoss, kBaseScriptProviderBoss, { IID_ISCRIPTPROVIDER, kYourScriptProviderImpl, } } 4. Create a script provider boss that subclasses kBaseScriptProviderBoss. 5. Create an IScriptProvider implementation that subclasses class RepresentScriptProvider and aggregate to the script provider boss. Class { kYourScriptProviderBoss, kBaseScriptProviderBoss, { IID_ISCRIPTPROVIDER, kYourScriptProviderImpl, } } 6. In your implementation of IScriptProvider, call IScriptUtils::CreateProxyScriptObject() to create one of your objects. 7. By default proxy objects are specified by index. If you want to specify by another property (e.g., name): a. Aggregate an appropriate data interface to your proxy script object's boss (e.g. IID_ISTRINGDATA). #10082 Making Your Plug-in Scriptable 17 #10082 Making Your Plug-in Scriptable Recipes 8. b. In your implementation of IScriptProvider, set the specifier data manually after the call to IScriptUtils::CreateProxyScriptObject(). c. In your implementation of IScript, override IScript::GetScriptObject() and substitute code to build your custom ScriptObject. Create ScriptElementInfo resources for the object, events, properties, and provider. If it is a Preference object A preference object is an object that exists as a property on either the Application object or a Document object. In the Object Model, this corresponds to preference data interfaces on kWorkspaceBoss and kDocWorkspaceBoss, respectively. 1. You generally don't need to define a boss or IScript implementation for a preferences object, as it can use kBasePrefsScriptObjectBoss instead. 2. Create a script provider boss that subclasses kBaseScriptProviderBoss. Then write an IScriptProvider implementation that subclasses class PrefsScriptProvider and aggregate to the script provider boss. In the constructor, call DefinePreference to link the object ID, property ID, and prefs script object boss ID (generally kBasePrefsScriptObjectBoss) Class { kYourPrefsScriptProviderBoss, kBaseScriptProviderBoss, { IID_ISCRIPTPROVIDER, kYourPrefsScriptProviderImpl, } } 3. Create necessary ScriptElementInfo resources for the objects, parent property, object properties, and providers. Exposing a New Property or Event on an Existing Object This is similar to adding scripting support for a preference object. 1. 18 Create/modify a script provider. • Add support to an existing script provider, or • Make a new provider. a. Write an IScriptProvider implemenation that subclasses class CScriptProvider. b. Create a script provider boss that subclasses kBaseScriptProviderBoss and aggregate the script provider implementation. #10082 Making Your Plug-in Scriptable Recipes 2. Create ScriptElementInfo resources. a. For the new property or event b. For the new script provider (if adding a new script provider boss), or add to an existing Provider resource. Defining IDs To keep the ID definitions easy to find in our plug-in project, you can define the various IDs using the following organization. • Define your 4-letter ScriptIDs in (YourPlug-in)ScriptingDefs.h. #define c_CustomPref 'cprf' In the CustomPrefs sample plug-in, this is done in {SDK}/source/sdksamples/customprefs/CstPrfScriptingDefs.h for convenience. • Define ScriptElementIDs in (YourPlug-in)ID.h file. See {SDK}/source/sdksamples/customprefs/CstPrfID.h for a sample. • Define Windows CLSIDs in (YourPlug-in)ScriptingWindowDefs.h. See {SDK}/source/sdksamples/customprefs/CstPrfScriptingDefs.h for a sample. Reviewing the New Resources InDesign CS/InCopy CS generates a new AppleScript dictionary (Mac only) or COM type library (Win only) at run-time only when the plug-in list (or the any of the modification dates on the plug-ins) changes, or when InDesign has to create a new InDesign Saved Data file. (One way to force a new AppleScript dictionary or COM type library to be regenerated is to delete the InDesign Saved Data file.) The result is written to the user's application defaults folder, in the following locations: • Mac: ${home}/Library/Preferences/Adobe {InDesign or InCopy}/Version 3.0(J))/Scripting Support/. The AppleScript dictionary is stored as "Resources for AppleScript.AETE". • Windows: %USERPROFILE%\Application Data\Adobe\{InDesign or InCopy)\Version 3.0(J)\Scripting Support\. The COM type library is stored as "Resources for Visual Basic.tlb". Reviewing the AppleScript Dictionary Follow these steps to verify that your new script objects have been incorporated into the AppleScript dictionary. 1. Launch the AppleScript Editor on your system. 2. File > Open Dictionary. . . #10082 Making Your Plug-in Scriptable 19 #10082 Making Your Plug-in Scriptable Recipes 3. Select InDesign CS or InDesign CS_J, and browse the dictionary. Reviewing the COM Type Library Choose one of the following approaches to verify that your new script objects have been incorporated into the COM type library. Using Microsoft Visual C++ 1. Launch Visual C++. 2. Select the Tools > OLE/COM Object Viewer menu. 3. Select the File > View TypeLib... menu. 4. Open "Resources for Visual Basic.tlb" from your application defaults folder and browse the type library. Using Microsoft Visual Basic 1. Launch Visual Basic and create a new project. 2. Select the Project > References… menu. 3. From the Available References list, check "Adobe InDesign CS(J) Type Library" or "Adobe InCopy CS Type Library" and click OK. If it doesn't show up in the list, click on the Browse… button to find the "Resources for Visual Basic.tlb" in your application defaults folder. Click OK on the References dialog. 4. Select the View > Object Browser menu (or hit the F2 key). Pick "InDesign" or “InCopy” from the first dropdown list to view the type library. Using Microsoft Word or Excel's Macro Editor 20 1. Launch Microsoft Word or Excel. 2. Select the Tools > Macro > Visual Basic Editor menu. 3. Select the Tools > References menu. 4. From the Available References list, check "Adobe InDesign CS(J) Type Library" or "Adobe InCopy CS Type Library" and click OK If it doesn't show up in the list, click Browse and open "Adobe InDesign CS(J) Type Library" or "Adobe InCopy CS Type Library" from your defaults folder. #10082 Making Your Plug-in Scriptable Recipes 5. Select the View > Object Browser menu (or hit the F2 key). Pick "InDesign" or “InCopy” from the first dropdown list to view the type library. If you previously used a pre-release version of "InDesign 3.0" or "InCopy 3.0" (Windows only). If you started developing your scriptable plug-in with a pre-release build of "InDesign 3.0" or "InCopy 3.0" for Windows, you may need to edit your registry to make sure that references to the GUID for the main COM Type Library doesn't point to "Adobe InDesign 3.0 Type Library" or "Adobe InCopy 3.0 Type Library". Leaving these in may not result in a successful binding between your Visual Basic script and the COM Type Library. Follow these steps to remove such references: 1. Make sure that you are not running any build of InDesign or InCopy, and that you are not running your Visual Basic script that calls either application's COM Type Library. 2. Run the Windows Registry Editor by typing "regedit" from the Start >> Run menu. 3. If you were using InDesign 3.0, look for the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{6926C15F-E97E-4B13-8658A97D8C616945} and delete it. If you were using InCopy 3.0, look for the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{B8C5B92E-C373-401A-973F9DD03FEF0490} and delete it. 4. Exit the Windows Registry Editor. This will save your changes automatically. 5. Clean up the Saved Data files as described earlier. (NOTE: Replace the word "InDesign" with "InCopy" for the InCopy saved data path.) 6. Restart InDesign CS or InCopy CS. Doing so will regenerate the necessary registry keys. 7. Restart your Visual Basic script environment, and remove references to the "Adobe InDesign 3.0 Type Library" or "Adobe InCopy 3.0 Type Library" from your script or project. In the Visual Basic IDE, go to the References dialog (Project >> References menu) and uncheck the references, then click OK for the dialog. 8. Then re-add references to the "Adobe InDesign CS Type Library" or "Adobe InCopy CS Type Library" to your script or project. 9. Save your script or project. Supporting The InDesign Interchange File Format: Additional Info For the most part, you are providing support for the InDesign Interchange file format simply by providing a scripting provider for all of your custom objects. However, if you are implementing a scripting provider on a custom page item object, there are a few extra precautions you may want to consider. 1. Make sure that your don’t rely on "with data" (parameters) during the Create event (e_Create). This is because the application component that generates the InDesign Interchange file format may not have all of the data to create the InDesign Interchange tag attributes on your custom objects. Also, it may not set properties on your custom object in any specific order. So in your script provider where you support the Create event #10082 Making Your Plug-in Scriptable 21 #10082 Making Your Plug-in Scriptable Recipes (e_Create), you should be able to support instantiation without properties while using reasonable defaults, and be able to tolerate setting properties in random order. 2. You must remember to aggregate two additional interfaces: IXMLFragment (IID_IXMLFRAGMENT) and IDOMElement (IID_IDOMELEMENT). Both interfaces are located in the {SDK}/source/public/interfaces/xmedia folder, and the IIDs and implementation IDs are defined in {SDK}/source/public/interfaces/xmedia/XMLDocumentID.h. For example, kDocBoss has a few AddIns: AddIn { kDocBoss, kInvalidClass, { IID_IXMLFRAGMENT, kXMLDocumentContentFragmentImpl, IID_IDOMELEMENT, kDOMElementImpl // from API } Many other hierarchy objects, such as kFrameItemBoss and kMultiColumnItemBoss, just aggregate the implementation kXMLNoContentFragmentImpl for the IXMLFragment interface. (This implementation ID is also defined in {SDK}/source/public/interfaces/xmedia/XMLDocumentID.h.) AddIn { kFrameItemBoss, kInvalidClass, { IID_IXMLFRAGMENT, kXMLNoContentFragmentImpl, IID_IDOMELEMENT, kDOMElementImpl // from API } }; Also, if you have read-only properties that are set automatically by the application or by your plug-in (e.g. file modification date/time), you may need a separate read/write version of the property for use by the kXMLDocumentScriptManagerBoss so that the InDesign interchange subsystem can update it while writing. For example, kPageCountPropertyScriptElement is a read-only element that needs to be updated at the time of writing: resource ScriptElementInfo(31) { kXMLDocumentScriptManagerBoss, kInDesignAllLanguagesFS, k_Wild, { 22 #10082 Making Your Plug-in Scriptable Recipes Provider { kSpreadScriptProviderBoss, { Object { kMasterSpreadObjectScriptElement }, Property { kPageCountPropertyScriptElement, kReadWrite }, } } } } ; Refer to Client-Specific Script Element Resources for more information. Verifying That Your New Script Objects Are Exported In The New InDesign Interchange File Format Follow these steps to make sure that your new script objects are exported in the new InDesign Interchange file format (.inx). 1. Load your scriptable plug-in into InDesign CS. 2. Create a new document, and create necessary page items on the layout. 3. Select the File > Export menu, and select the “InDesign Interchange” format. Save your file with a .inx extension. 4. Open the .inx file you just exported in a text editor or an XML viewer. Search for the 4 letter script IDs specified in your plug-in’s ScriptingDefs.h file. For example, the CustomPrefs plug-ins prefval property (which is simply added onto an existing object) will be saved in this file as such: When the pref value is kFalse: <cprf pVal="b_f" Self="rc_d.cprf[1]"/> When the pref value is kTrue: <cprf pVal="b_t" Self="rc_d.cprf[1]"/> #10082 Making Your Plug-in Scriptable 23 #10082 Making Your Plug-in Scriptable Recipes Tips for Debugging the Scripting Architecture You can use the following Debug-build only menu items to help in debugging your scriptable plug-in. • Turn on Test > TRACE > ScriptInfoManager > Registration to trace the loading of script info resources. • Turn on Test > TRACE > ScriptInfoManager > Resource Dependencies to enable asserts when a dependent resource is missing. • Turn on Test > TRACE > ScriptRequestHandler to trace requests and see which script providers are invoked. • Turn on Test > TRACE > CoreObjectSpecifier to trace the specification and resolution of objects used by a JavaScript. • Turn on Test > TRACE > GenericCOMObject to trace operations on InDesign COM objects used by a VisualBasic script. • From the Test > Scripting menu, you can dump the contents of the ScriptInfoManager for various clients to a text file in the QA Logs folder. This makes it easy to diff changes you are making or between builds. Converting Existing Scriptable Plug-ins To convert existing scriptable plug-ins made for previous versions of InDesign/InCopy, you can follow these steps. (NOTE: The following numbered list assumes you are able to use the TypeInfoConverter tool, which is only available for Windows. You can find more information about converting scriptable plug-ins in the section "Detailed information about porting a scriptable plug-in from InDesign/InCopy 2.x to InDesign CS/InCopy". 1. Find which plug-in you're working on, and make sure you have a compiled/built version of that plug-in. (There is no need to load this in InDesign/InCopy at this point.) Make a copy of the entire project for that plug-in, so you can make changes to the copy. Within the project, find a script provider implementation (.cpp) and scripting resources (.r and .odl). Open the source files in your IDE so you can view and edit them. 2. Launch the the TypeInfoConverter tool ({SDK}/tools/typeinfoconverter/TypeInfoConverter.exe). 3. Use the TypeInfoConverter tool to generate sections of ScriptElementInfo resources for objects in the 2.x version of your scriptable plug-in. (The results of your conversions will be placed in a Notepad text file, which you can save as a temporary .fr file. Further documentation on how to use this tool is available in the "Appendix" section.) 4. While looking at the AETE and ODL resources in your scriptable plug-in’s source files: • 24 Change the name to be all lower case with spaces between words. (The script manager will automatically capitalize the first letter in each word for Visual Basic.) #10082 Making Your Plug-in Scriptable Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS • Create one description. The format is that the first word starts with an uppercase. Only add a period if another sentence is added and if so the next word should be capitalized. All other words should be lower case. • Update the enumeration id, since the TypeInfoConverter tool can only provide a guess. • Check if the data type is a unit (UnitType) since neither the ODL nor AETE contain this information. • Make sure the data type is the same in the AETE and ODL. 5. Using the TypeInfoConverter tool, create a Provider resource. Refer at the constructor in your IScriptProvider implementation, and add the Object, Parent, Property, and Event to the Provider resource. 6. Save the file as [MyPlug-in]ScriptInfo.fr in your plug-in's folder and either add the contents of the file to the plug-in's master .fr file, or simply #include it from the plug-in’s master .fr file. 7. Remove IID_IK2SERVICEPROVIDER from the script provider's boss resource, as it is no longer needed. 8. Add necessary element IDs to the plug-in's ID.h file. 9. Change the code in the constructor of your IScriptProvider implementation. • Remove all the calls to DefineProperty(), DefineMethod, etc. However, do NOT remove any DefinePreference() calls. 10. If you find any differences between the way your AETE and ODL resources were defined, keep in mind that that changes to the resources might break your end users’ scripts, so you may want to document these changes somewhere. 11. Be sure to compile and load your scriptable plug-in on both platforms using the debug build of InDesign/InCopy. This would allow you to check for asserts during loading of the new resources and creation of the AppleScript dictionary and COM Type Library. Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS Converting Script Providers There are two interconnected parts to creating a ScriptElementInfo resource. The first involves extracting information on individual script elements (objects, properties, events, etc.) from the existing AETE resource and ODL resource (compiled into a type library). The second involves extracting information on how all these elements relate to each other from a script provider. Generally it is easiest to work backwards, beginning with a script provider. In the old paradigm, the constructor for a script provider contained all the information about which #10082 Making Your Plug-in Scriptable 25 #10082 Making Your Plug-in Scriptable Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS elements (objects, properties, events, etc.) that script provider supports. In the new paradigm, this information is stored in a script Provider resource. There are three kinds of script providers: 1. Those that inherit from RepresentScriptProvider represent one or more objects, and usually support related events and properties. 2. Those that inherit from CScriptProvider add in support for one or more events and/or properties to one or more existing objects. 3. Those that inherit from PrefsScriptProvider represent a preferences object, and often support one or more related properties. Converting A Represent Script Provider Represent script providers are recognizable by the fact that their implementation typically inherits from the RepresentScriptProvider base class. The constructor will typically include one or more DefineParent statements, one or more DefineObject and DefineCollection pairs of statements, and a variety of DefineEvent and DefineProperty statements. 1. Remove all of the code within the constructor of the script provider. Take note of which objects appear within the DefineObject statements. 2. Use the TypeInfoConverter tool to generate a ScriptElementInfo resource for the object(s), and any events and properties that appear in the script provider's constructor. You do not need to generate resources for common events and properties that already have resources defined elsewhere (e.g. the Count and Delete events, the Name property, etc.). 3. Note that Object resources include a reference to a suite script element, which is used by AppleScript to group related objects and events together. Since suites are not used by VB, Suite resources are not automatically generated by the TypeInfoConverter tool. You can copy the information you need to create one by hand from the relevant suite definition in the existing AETE resource file. The resulting resource should look something like this: resource ScriptElementInfo(1) { kCoreScriptManagerBoss, kWildFS, k_Wild, { Object { kDocumentObjectScriptElement, c_Document, "document", "A document", kDocument_CLSID, c_Documents, "documents", 26 #10082 Making Your Plug-in Scriptable Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS "All open documents", kDocuments_CLSID, kNonIDBasedObjectScriptElement, kBasicSuiteScriptElement, } Event { kCreateDocumentEventScriptElement, e_Create, "add", "Create a new document", ObjectType( kDocumentObjectScriptElement ), "The new document", { p_ShowInWindow, "show in window", "Whether to open the document in a window", BoolType, kOptional, } } Enum { kSaveOptionsEnumScriptElement, en_SaveOptions, "save options", "Options for saving before closing a document", { en_No, "no", "Don't save changes", en_AskUserSaveFile, "ask", "Ask user whether to save changes", en_Yes, "yes", "Save changes", } } Event { kCloseDocumentEventScriptElement, e_Close, "close", "Close the document", VoidType, { #10082 Making Your Plug-in Scriptable 27 #10082 Making Your Plug-in Scriptable Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS p_SaveOptions, "save changes", "Whether to save changes ...", EnumType( kSaveOptionsEnumScriptElement ), kOptional, keyAEFile, "saving in", "The file in which to save the document", FileType, kOptional, } } ... // Other events Property { kModifiedPropertyScriptElement, p_Modified, "modified", "Whether the object has been modified", BoolType, {} } ... // Other properties Provider { kDocumentScriptProviderBoss, { Parent{ kApplicationObjectScriptElement }, RepresentObject{ kDocumentObjectScriptElement }, //kCountEventScriptElement is a common event // defined elsewhere CollectionEvent{kCountEventScriptElement}, CollectionEvent{kCreateDocumentEventScriptElement}, Event{ kCloseDocumentEventScriptElement }, ... // other events //kNamePropertyScriptElement is a common property // defined elsewhere Property{ kNamePropertyScriptElement, kReadOnly }, Property{ kModifiedPropertyScriptElement, kReadOnly }, ... // other properties } } } } ; 28 #10082 Making Your Plug-in Scriptable Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS Converting An Add-in Script Provider Add-in script providers are recognizable by the fact that their implementation typically inherits from the CScriptProvider base class. The constructor will typically include one or more DefineObject statements, and a variety of DefineEvent and/or DefineProperty statements. 1. Remove all of the code within the constructor of the script provider. Take note of which objects appear within the DefineObject statements. 2. Use the TypeInfoConverter tool to generate a ScriptElementInfo resource for any events and properties that appear in the script provider's constructor. Converting A Preferences Script Provider Preferences script providers are recognizable by the fact that their implementation typically inherits from the PrefsScriptProvider base class. The constructor will typically include one or more DefineParent statements, a DefinePreference statement, and sometimes some DefineProperty statements. Preferences objects are a bit unusual in that they are added as a singleton child to their parent object (via the ParentProperty mechanism). Thus a preferences script provider's constructor (in the old way) and Provider resource (in the new way) contains information about the preferences object itself, the properties of the preferences object, the parent(s) of the preferences object, and the property on the parent object(s) that allows access to the preferences object. 1. Do not remove the DefinePreference statement(s), but do remove any other code (generally DefineProperty statements) within the constructor of the script provider. Take note of which object appears within the DefinePreference statement - - also note one or more of the parent objects. 2. Use the TypeInfoConverter tool to generate an ScriptElementInfo resource for the preference object, and any properties that appear in the script provider's constructor. 3. You will also need to generate a property resource to assign to the parent objects that have access to this preferences object. The easiest way to do this in the TypeInfoConverter tool is to look in the properties list of one of the parent objects (e.g., find the "DocumentPreferences" property on the "Document" object). Double-click on this to generate a preferences property resource for your preferences object. Reference this property in the Provider resource for your preferences object, but using the ParentProperty label. 4. Recommended style is for the name of preferences objects to have the word "preference" (singular), whereas the preferences property has the word "preferences" (plural). The resulting resource should look something like this: resource ScriptElementInfo(1) { kCoreScriptManagerBoss, kWildFS, k_Wild, { Object { #10082 Making Your Plug-in Scriptable 29 #10082 Making Your Plug-in Scriptable Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS kDocPrefsObjectScriptElement, c_DocPref, //"preference" appears here as singular "document preference", "Document preferences", kDocPref_CLSID, //Preferences objects don't have plural forms NoPluralInfo, //Preferences objects are always based on this type kPreferencesObjectScriptElement, //Most preferences objects are in this suite kPreferencesSuiteScriptElement, } Property { kDocPrefsPropertyScriptElement, p_DocPref, //"preferences" appears here as plural "document preferences", "Document preferences", ObjectType( kDocPrefsObjectScriptElement ), {} } Property { kNumPagesPropertyScriptElement, p_DPNumPages, "pages per document", "The number of pages in a document", Int32Type, {} } ... // other Property resources Provider { kDocPrefsScriptProviderBoss, { Parent{ kApplicationObjectScriptElement }, Parent{ kDocumentObjectScriptElement }, RepresentObject{ kDocPrefsObjectScriptElement }, //This will be added to the parent(s) ParentProperty{ kDocPrefsPropertyScriptElement }, //This will be added to the preferences object Property{ kNumPagesPropertyScriptElement, kReadWrite }, ...// other properties } 30 #10082 Making Your Plug-in Scriptable References } } } ; API Changes Between InDesign 2.0.x And InDesign CS For details on API changes, including changes to IScriptProvider, refer to the API Advisor report between InDesign/InCopy 2.0.1 and InDesign CS. This HTML file is located in {SDK}/docs/references/APIAdvisorID2_vs_ID3.html. References Resource Definitions ScriptElementInfo Resource Defined in: {SDK}/source/public/includes/ScriptInfoTypes.fh ScriptElementInfo is the name for a core scripting resource. Note that there may be more than one ScriptElementInfo resource in a ScriptInfo .fr file. Like every resource, the first thing included in a ScriptElementInfo resource is the resource's id, which must be unique to the plug-in. Next is the client specifier (the ClassID of a script manager boss). Typically this is kCoreScriptManagerBoss, which indicates that the resources are applicable to all clients. If you want to make a script element visible to only one client, you can put the boss class ID of the client's script manager (e.g. kMyScriptManagerBoss) instead. Refer to Client-Specific Script Element Resources for more details on this field. The next thing in a ScriptElementInfo resource is a feature set ID and a locale ID. In most cases, these will be kWildFS, k_Wild, which means this resource applies to all feature sets and all UI locales. The most absolutely basic (and also totally useless) ScriptElementInfo resource would look something like: resource ScriptElementInfo(1) { kCoreScriptManagerBoss, kWildFS, k_Wild, { } } ; To make it useful, include one or more Suite, Object, Event, Property, Enum, and Provider resources inside. #10082 Making Your Plug-in Scriptable 31 #10082 Making Your Plug-in Scriptable References Strings In Resources The name of an element should be all lowercase (e.g., "delete", "text frame"), unless you want to force uppercase for an acronym (e.g., "URL") or product name (e.g., "InDesign basics"). The name of an element is never translated. The description of an element should be sentence case (e.g., initial word capitalized) with no closing punctuation, unless the description is more than one sentence long, in which case all sentences except the last should have closing punctuation. Sample scripts are not translated, but we use a string key to look up the full script in the nontranslate string table. Object-Sensitive Descriptions It often makes sense for the description field(s) of an event or property element to contain the name of the relevant object. For example: • Description of the name property: "The name of the swatch" • Description of the count event: "The number of swatches" • Description of the return value for the create event: "The new swatch" By using ^Object for the singular form and ^Objects for the plural form, the name of the relevant object will be substituted at runtime. For example: • Description of the name property: "The name of the ^Object" • Description of the count event: "The number of ^Objects" • Description of the return value for the create event: "The new ^Object" This is particularly useful for events and properties that appear on multiple objects. Suite Resources Object resources include a reference to a suite script element, which is used by AppleScript to group related objects and events together. A typical Suite resource looks like: Suite { //The suite's ScriptElementID 32 #10082 Making Your Plug-in Scriptable References // (defined in the plug-in's ID.h file) kBasicSuiteScriptElement, //The suite's ScriptID (defined in // {SDK}/source/public/includes/ScriptingDefs.h) s_InDesignBasicSuite, //The name of the suite "InDesign basics", //A description of the suite "Terms applicable to many InDesign operations", } ScriptElementIDs for common suites are defined in {SDK}/source/public/interfaces/Architecture/ScriptingID.h. Object Resources A typical Object resource looks like: Object { //The object's ScriptElementID // (defined in the plug-in's ID.h file) kSpreadObjectScriptElement, //The object's ScriptID (defined in // {SDK}/source/public/Includes/ScriptingDefs.h) c_Spread, //The name of the object "spread", //A description of the object "A spread", //The Windows CLSID (see below) of the object kSpread_CLSID, //The plural form of the object's ScriptID c_Spreads, //The name of the plural form "spreads", //A description of the plural form "Every spread", //The Windows CLSID of the plural form kSpreads_CLSID, //The based-on type of the object kUniqueIDBasedObjectScriptElement, //The suite to which the object belongs kLayoutSuiteScriptElement, //Indicates there are no sample scripts NoSampleScripts, } #10082 Making Your Plug-in Scriptable 33 #10082 Making Your Plug-in Scriptable References Some objects are singletons and don't have a plural form. The Application object is one example, as are preferences objects. These look like: Object { kMarginPrefsObjectScriptElement, c_MarginPref, "margin preference", "Margin preferences", kMarginPref_CLSID, //No plural form NoPluralInfo, //The based-on type of a preferences object kPreferencesObjectScriptElement, kPreferencesSuiteScriptElement, NoSampleScripts, } A Note On Scripting Object Inheritance Objects are generally non-ID-based (i.e., proxy objects in scripting; e.g., Text objects, TabStops), non-unique-ID-based (i.e., those that have an id, but not one that is unique across the document; e.g., Cells, XMLElements), or unique-ID-based (i.e., all UID-based objects). This is defined in the object's core scripting resource by using the appropriate id: kNonIDBasedObjectScriptElement, kNonUniqueIDBasedObjectScriptElement, or kUniqueIDBasedObjectScriptElement respectively. One way to tell whether an object is ID-based or non-ID-based is to look at the IScript implementation for the object. If it inherits from CProxyScript, then it is probably non-IDbased (or possibly non-unique-ID-based). If it inherits from CScript, then it is probably IDbased. An object may be based on another by specifying the base object in the object's core scripting resource. For example, Oval objects are based on PageItem objects: Object { kPageItemObjectScriptElement, c_PageItem, "page item", "A generic page item. Any page item can be ...", kPageItem_CLSID, c_PageItems, "page items", "All page items", kPageItems_CLSID, kUniqueIDBasedObjectScriptElement, kLayoutSuiteScriptElement, } 34 #10082 Making Your Plug-in Scriptable References Object { kOvalObjectScriptElement, c_Oval, "oval", "An oval", kOval_CLSID, c_Ovals, "ovals", "All ovals", kOvals_CLSID, kPageItemObjectScriptElement, /* inheritance */ kLayoutSuiteScriptElement, } If an object is based on another, it inherits the base object's events and properties (but not its parents, children/collections, or collection events). It also implies that a collection of the base objects (e.g., a spread's PageItems collection) will automatically include any existing subclass objects (e.g., the spread's Ovals). This is a requirement for the InDesign Interchange file format and is programatically dependent upon the implementation of the IScriptProvider::GetObject methods for the base object's collection. This approach to object inheritance replaces the concept of a generic type that was previously implemented via IScript::GetGenericType in InDesign 2.x and earlier. Windows CLSIDs The CLSID must be defined using the new DECLARE_GUID macro (which is defined in {SDK}/public/Includes/ScriptingWindowDefs.h) to be usable in a scripting resource. For example: #define kDocument_CLSID { 0x1a5e8db4, 0x3443, 0x11d1, { 0x80, 0x3c, 0x0, 0x60, 0xb0, 0x3c, 0x2, 0xe4 } } DECLARE_GUID( Document_CLSID, kDocument_CLSID ) ; Event Resources Typical Event resources look like these: Event { //The event's ScriptElementID // (defined in the plug-in's ID.h file) kDeleteEventScriptElement, //The event's ScriptID (defined in // {SDK}/source/public/Includes/ScriptingDefs.h) e_Delete, //The name of the event #10082 Making Your Plug-in Scriptable 35 #10082 Making Your Plug-in Scriptable References "delete", //A description of the event "Delete the ^Object", //The return type--this event has no return value VoidType, //Parameters--this event has none {} } Event { kOpenEventScriptElement, e_Open, "open", "Open a document", //This event returns a Document object VariableType { ObjectType(kDocumentObjectScriptElement), ObjectType(kBookObjectScriptElement), ObjectType(kLibraryObjectScriptElement) }, //A description of the return value "The opened document, book, or library", { //Parameters // (1) ScriptID, // (2) name, // (3) description, // (4) type, // (5) optional/required // (6) default value (if optional) keyDirectObject, "from", "One or more file paths", VariableType { FileType, FileArrayType( kVariableLength ) }, kRequired, p_ShowInWindow, "showing window", "Whether to show the document in a window", BoolType, kOptional, BoolDefault(kTrue), 36 #10082 Making Your Plug-in Scriptable References } } Parameter Defaults You must specify a default value for all optional parameters using one of the following declarations: • NoDefault • BoolDefault( true or false ) • Int16Default( short integer ) • Int32Default( long integer ) • StringDefault( string ) • RealDefault( real number ) • EnumDefault( enumerator ) You will notice that there are no declarations for unit, data, file, object, record, or variable or array types. For some of these types, you may use one of the permitted default value types. For example, a Unit can typically be specified as a real or a string; a variable type that includes object and string types, could be specified using a string. However, there's currently no way to specify an array of default values for an array type. Parameters that don't have a default value or for which none can be indicated using the existing specifiers, must be declared as NoDefault. Some Special Events • • Create/Add/Make: 1. Are known collectively as the "Create" event but is called "Add" in Visual Basic and "make" in AppleScript; use e_Create as the ScriptID and "add" as the name in the Event resource. 2. Most objects can use the default kCreateEventScriptElement, unless they have required parameters for the create. 3. Is always a CollectionEvent in a Provider resource. e.g.: CollectionEvent{ kCreateEventScriptElement }, Count: 1. Is considered a property in VB and appears on collections (the plural form of an object, e.g. "Books") in the type library. 2. Is always a CollectionEvent in a Provider resource: e.g.: CollectionEvent{ kCountEventScriptElement }, Property Resources Typical Property resources look like these: Property { #10082 Making Your Plug-in Scriptable 37 #10082 Making Your Plug-in Scriptable References //The property's ScriptElementID // (defined in the plug-in's ID.h file) kBoundsPropertyScriptElement, //The property's ScriptID (defined in // {SDK}/source/public/Includes/ScriptingDefs.h) p_Bounds, //The name of the property "bounds", //A description of the property "The bounds of the window", //Types that the property could return on a "Get" UnitArrayType(4), //Any additional types that the property will accept // on a "Set" {} //The boss ID of the equivalent page item or // text attribute boss kNoAttributeClass, } Property { kLogFilePropertyScriptElement, p_LogFile, "log file", "The file to use for a log", //The property's inherent type is "File" FileType, { //You could also set the property by using a "String" StringType } kNoAttributeClass, } //This will be referenced as a ParentProperty // in the Provider resource but Property { // is still defined by a Property resource kMarginPrefsPropertyScriptElement, p_MarginPref, "margin preferences", "Margin preferences", //This property points to another object through // that object's ScriptElementID ObjectType( kMarginPrefsObjectScriptElement ), {} 38 #10082 Making Your Plug-in Scriptable References kNoAttributeClass, } Some Special Properties • • Name property — Is predefined in {SDK}/source/components/script/resources/ScriptingScriptInfo.fr as kNamePropertyScriptElement. — Does need to be added into a Provider resource that supports the Name property. e.g.: Property{ kNamePropertyScriptElement, kReadWrite }, Parent property — • ID property — • Is automatically generated and so does not need to be added into any Provider resource. Is automatically generated and so does not need to be added into any Provider resource. Class property — Is automatically generated and so does not need to be added into any Provider resource. Enum Resources Enum resources look like this: Enum { //The enum's ScriptElementID // (defined in the plug-in's ID.h file) kSaveOptionsEnumScriptElement, //The enum's ScriptID (defined in // {SDK}/source/public/Includes/ScriptingDefs.h) en_SaveOptions, //The name of the enum "save options", //A description of the enum "Options for saving before closing a document", //A list of enumerators for the enum { // An enumerator's ScriptID, name, and description en_No, "no", "Don't save changes", en_AskUserSaveFile, #10082 Making Your Plug-in Scriptable 39 #10082 Making Your Plug-in Scriptable References "ask", "Ask user whether to save changes", en_Yes, "yes", "Save changes", } } Provider Resources The Provider resource ties together the other resources: • It indicates which events and properties are available on which objects. • It defines the object hierarchy. • And it specifies which script provider boss knows how to handle the referenced object(s), event(s) and property(s). A Provider resource looks like this: Provider { //The class id of the script provider boss that handles // the referenced object(s), event(s) and property(s) kDocumentScriptProviderBoss, { //The parent object of the referenced object(s) // in the object hierarchy Parent{ kApplicationObjectScriptElement }, //The object which has the referenced parent(s), // event(s) and property(s) RepresentObject{ kDocumentObjectScriptElement }, //An event on a collection of the referenced object(s) //(typically Count/Create events are collection events) CollectionEvent{ kCountEventScriptElement }, //An event on the referenced object(s) Event{ kCloseDocumentEventScriptElement }, //A property on the referenced object(s), and whether // the property is read-write/read-only Property{ kNamePropertyScriptElement, kReadOnly }, ... //Other references to parents, objects, events, // and/or properties handled by this script provider } } Types Types are found in Property and Event resources. 40 #10082 Making Your Plug-in Scriptable References TABLE 1.2 Basic Types Description ODFRC Type ODL Type AETE Type Void VoidType void typeNull Short Integer Int16Type short typeShortInteger Long Integer Int32Type long typeLongInteger Boolean BoolType VARIANT_BOOL typeBoolean String StringType BSTR typeChar Measurement unit UnitType double cFixed Real number RealType double cFixed Date or Time DateType DATE typeLongDateTime File name or path FileType BSTR or VARIANT typeAlias Enum EnumType ( enum_id ) enum's name enum's ScriptID Object ObjectType ( object_id ) IDispatch* cObjectSpecifier Array of short integers Int16ArrayType ( length ) VARIANT typeShortInteger (listOfItems) Array of long integers Int32ArrayType ( length ) VARIANT typeLongInteger (listOfItems) Array of booleans BoolArrayType ( length ) VARIANT typeBoolean (listOfItems) Array of strings StringArrayType ( length ) VARIANT typeChar (listOfItems) Array of measurement units UnitArrayType ( length ) VARIANT cFixed (listOfItems) Array of real numbers RealArrayType ( length ) VARIANT cFixed (listOfItems) Array of dates/times DateArrayType ( length ) VARIANT typeLongDateTime (listOfItems) Array of file names/paths FileArrayType ( length ) VARIANT typeAlias (listOfItems) Array of enum values EnumArrayType ( enum_id , length ) VARIANT enum's ScriptID (listOfItems) Array of objects ObjectArrayType ( object_id , length ) IDispatch* cObjectSpecifier (listOfItems) #10082 Making Your Plug-in Scriptable 41 #10082 Making Your Plug-in Scriptable References Description ODFRC Type ODL Type AETE Type List of key/value pairs RecordType VARIANT typeAERecord TABLE 1.3 Special Types Description ODFRC Type ODL Type AETE Type Variable VariableType { type_list } VARIANT typeWildCard Array of variable contents VariableArrayType ( length ) { type_list } VARIANT typeWildCard (listOfItems) Key: • length: the number of items in the array; if it varies, use kVariableLength. • enum_id: a valid ScriptElementID of an enum. • object_id: a valid ScriptElementID of an object. • type_list: a list of one or more basic types separated by commas. Types are actually defined as ODFRC macros (in {SDK}/source/public/Includes/ScriptInfoTypes.fh). Some Type Examples TABLE 1.4 Special Types 42 VariableType{ FileType, FileArrayType( kVariableLength ) } A single file or an array of files VariableType{ ObjectType( kAnyObjectScriptElement ), ObjectArrayType( kAnyObjectScriptElement, kVariableLength ) } A single object of any kind, or an array of any kind of objects VariableType{ StringType, EnumType( kNothingEnumScriptElement ) } A string or the enumeration 'none' VariableType{ EnumType( kAnchorPointEnumScriptElement ), UnitArrayType(2) } An anchor point enumeration or an array of two unit values (i.e., a measurement point) VariableType{ ObjectType( kSwatchObjectScriptElement ), StringType } A swatch object or a string (i.e., the name of the swatch) VariableType{ EnumType( kUIColorsEnumScriptElement ), RealArrayType(3) } One of the enumerated UI colors or an array of three reals (i.e., a list of RGB values) #10082 Making Your Plug-in Scriptable References VariableArrayType( kVariableLength ) { ObjectType( kPageItemObjectScriptElement ),ObjectType( kImageObjectScriptElement ), ObjectType( kEPSObjectScriptElement ), ObjectType( kPDFObjectScriptElement ) } An array of one or more page items, images, EPSes, and/or PDFs Object-Sensitive Types It occasionally makes sense for the type of a property or in an event to be the type of the relevant object. For example: • Type of the AppleScript object reference property • Type of the object returned by a create or duplicate event • Type of a parameter to the move event For these cases, use ObjectType( kContainerObjectScriptElement ) as the type. This is particularly useful for events and properties that appear on multiple objects. Even more rarely a property or event parameter may have the type of the relevant object's parents. For example: • Type of the Parent property • Type of the reference parameter to a move, duplicate, or create event For these cases, use ObjectType( kContainerParentScriptElement ) as the type. These options are particularly useful for events and properties that appear on multiple objects. Record Type A "record" is a list of key/value pairs, where the key is the ScriptID of a property and the value is any appropriate one for that property. A RecordType declaration takes an object_id, which is the ScriptElementID of the object to which the properties in the record apply. For example: • RecordType( kDocumentEventScriptElement ) indicates that the keys in the record correspond to properties of the Document object. • Furthermore, you can use an object-sensitive type such as RecordType( kContainerObjectScriptElement ): in a property resource this would indicate that the keys correspond to properties on the object that contains the property; in an event's parameter resource this would indicate that they correspond to properties on the object that contains the event. Defining Script Element IDs You can use the following text as a template by copying it into your plug-in's ID.h file. Recommended style is to group the ids by element type (objects, events, properties, etc.). //Script Element IDs //Suites #10082 Making Your Plug-in Scriptable 43 #10082 Making Your Plug-in Scriptable References DECLARE_PMID(kScriptInfoIDSpace, k???SuiteScriptElement, k???Prefix + 1) //Objects DECLARE_PMID(kScriptInfoIDSpace, k???ObjectScriptElement, k???Prefix + 40) //Events DECLARE_PMID(kScriptInfoIDSpace, k???EventScriptElement, k???Prefix + 80) //Properties DECLARE_PMID(kScriptInfoIDSpace, k???PropertyScriptElement,k???Prefix + 140) //Enums DECLARE_PMID(kScriptInfoIDSpace, k???EnumScriptElement, k???Prefix + 220) Overloading an Existing Event or Property Two objects may have the same property or event, but with a slightly different definition. For example, the description may be different, or the type of the property, or the parameters of the event. In this case, you will need to define two different resources. • Define a unique ScriptElementID for each resource • The name and ScriptID must be identical in both resources • Other information (description, type, parameters, etc.) may be identical or different as desired Removing a Core Scripting Resource To remove a scripting resource, simply delete it. You will also need to delete any other references to its Script Element ID (in any Provider resources and in the plug-in's ID.h file). Client-Specific Script Element Resources Certain script elements are only applicable to one client of the scripting DOM. These script elements are declared the same way as core resources, but using the ClassID of the client's script manager boss. These script elements are managed automatically by the same IScriptManagerInfo interface, and are visible or not depending on the client that has requested the info. Elements That Are Only Visible to One Client. All ScriptElementInfo resources specify a client--the ClassID of a script manager boss. For core resources, this is kCoreScriptManagerBoss. For client-specific script elements, this is the ClassID of the client's script manager boss. For example: resource ScriptElementInfo(10) { //ClassID of the script manager for the client for which // these resources are exposed kMyScriptManagerBoss, kWildFS, k_Wild, { 44 #10082 Making Your Plug-in Scriptable References //Client-specific Object, Event, Property, and // Provider resources ... } } ; On first launch, all script elements are loaded into a single "database", which is contained in the implementation of IScriptInfoManagerFactory and stored in SavedData. Each element includes a member datum of the ElementContext class (defined in ScriptInfo.h), which specifies the element's client (core or specific), feature set, locale, and version (first and last). When a client wants access to the script info database, it must specify a RequestContext (defined in ScriptInfo.h), which indicates what client, feature set, locale, and version the client is interested in. The client calls IScriptUtils::QueryScriptInfoManager or IScriptUtils::QueryScriptRequestHandler. If this is the first request using a particular RequestContext, the ScriptInfoManagerFactory will create a new ScriptInfoManager boss containing only the relevant elements. If this RequestContext has previously been received, the ScriptInfoManagerFactory will return the appropriate interface on the existing boss. The elements contained in a ScriptInfoManager boss for a particular RequestContext will include ALL core script elements PLUS all the elements that are specific to the specified client (if any). Elements That Are Defined Differently By Different Clients. Attributes of a script element that are intrinsic to its definition (i.e., that appear in fields of the script element's resource), can be changed on a per-client basis. For example, a property might have a different type or an event might have different parameters. One way to do this is to define a new script element with its own ScriptElementID, name, and ScriptID. In this case, both versions of the element would be available. Alternatively, you can use the same ScriptElementID, but change some or all of the other attributes. In this case, only the client-specific version of the element would be available for that client. In either case, the alternative script element could be handled by the same or a different script provider as the original one, since that is determined by the Provider resource. For example, here the Delete event for the Swatch object has one parameter by default, but no parameters for my client (which also uses a different script provider to handle the event): resource ScriptElementInfo(20) { kCoreScriptManagerBoss, kWildFS, k_Wild, { Event { kDeleteSwatchEventScriptElement, e_Delete, "delete", "Delete swatch", VoidType, #10082 Making Your Plug-in Scriptable 45 #10082 Making Your Plug-in Scriptable References { p_Replace, "replacing with", "The swatch to apply in place of this one", VariableType { ObjectType(kSwatchObjectScriptElement), StringType }, kRequired, } } Provider { kSwatchScriptProviderBoss, { Object { kSwatchObjectScriptElement }, Property { kDeleteSwatchEventScriptElement }, } } } } ; resource ScriptElementInfo(21) { kMyScriptManagerBoss, kWildFS, k_Wild, { Event { kDeleteSwatchEventScriptElement, e_Delete, "delete", "Delete swatch", VoidType, { } } Provider { kMySwatchScriptProviderBoss, { 46 #10082 Making Your Plug-in Scriptable References Object { kSwatchObjectScriptElement }, Event { kDeleteSwatchEventScriptElement }, } } } } ; Relationships Between Elements That Are Different For Different Clients. In some cases, attributes of a script element that are related to its behavior for a specific object are set in the Provider resources. For example, whether an object has a particular parent or whether a property is read-only or readwrite. For a particular client to obtain behavior that is different from the core attributes set in the core resources, define a separate Provider resource in a context-sensitive ScriptElementInfo resource for that client. For example, here the Name property of the Hyphenation Exception object is read-only by default, but read-write for my client: resource ScriptElementInfo(30) { kCoreScriptManagerBoss, kWildFS, k_Wild, { Provider { kHyphExceptionScriptProviderBoss, { Object { kHyphenationExceptionObjectScriptElement }, Property { kNamePropertyScriptElement, kReadOnly }, } } } } ; resource ScriptElementInfo(31) { kMyScriptManagerBoss, #10082 Making Your Plug-in Scriptable 47 #10082 Making Your Plug-in Scriptable References kWildFS, k_Wild, { Provider { kHyphExceptionScriptProviderBoss, { Object { kHyphenationExceptionObjectScriptElement }, Property { kNamePropertyScriptElement, kReadWrite }, } } } } ; Relationships Between Elements That Are Not Applicable To A Particular Client. By default, elements that are specified in a core resource will be available in all clients, although sometimes these elements will have an alternative, client-specific definition (see above). However, in most cases, whether or not these elements are actually accessible to a user of the client is determined by whether they appear in a Provider resource. That is because it's the Provider resources that determine the parent/child relationships of the DOM hierarchy, as well as which events and properties are supported by which objects. This makes it possible for a client to hide a particular element by hiding relationships between elements. (Note: This isn't true of core Suite or Enum elements, which therefore can not be hidden.) To remove a relationship, define a Provider resource that uses kNotSupported as the id of the script provider. Doing so means that any relationships in that Provider resource will specifically NOT be supported for the client in question. In the following example, a client-specific Provider resource is used to add additional parents for the Link object and to remove a property that these parents use to access links in the default case. resource ScriptElementInfo(40) { kCoreScriptManagerBoss, kInDesignAllLanguagesFS, k_Wild, { //For core clients, only the Document // has a links collection Provider { kLinkScriptProviderBoss, 48 #10082 Making Your Plug-in Scriptable References { SurrogateParent { kDocumentObjectScriptElement }, RepresentObject { kLinkObjectScriptElement }, CollectionEvent { kCountEventScriptElement }, } } Property { kItemLinkPropertyScriptElement, p_Link, "item link", "Link to a placed file", ObjectType( kLinkObjectScriptElement ), {} kNoAttributeClass, } //For core clients, there is a item link // property on linkable objects Provider { kLinkScriptProviderBoss, { Object { kStoryObjectScriptElement }, Object { kGraphicObjectScriptElement }, Property { kItemLinkPropertyScriptElement, kReadOnly }, } } } #10082 Making Your Plug-in Scriptable 49 #10082 Making Your Plug-in Scriptable References } ; resource ScriptElementInfo(41) { kMyScriptManagerBoss, kInDesignAllLanguagesFS, k_Wild, { //For my client, there is a links // collection on linkable objects Provider { kLinkScriptProviderBoss, { Parent { kStoryObjectScriptElement }, Parent { kImageObjectScriptElement }, Parent { kEPSObjectScriptElement }, Parent { kWMFObjectScriptElement }, Parent { kPICTObjectScriptElement }, Parent { kPDFObjectScriptElement }, RepresentObject { kLinkObjectScriptElement }, CollectionEvent { kCountEventScriptElement }, } } //For my client, there is no item link 50 #10082 Making Your Plug-in Scriptable References // property on linkable objects Provider { kNotSupported, { Object { kStoryObjectScriptElement }, Object { kGraphicObjectScriptElement }, Property { kItemLinkPropertyScriptElement, kReadOnly }, } } } } ; APIs Interfaces The following is a listing of commonly used scripting related interfaces. For reference documentation, you can either search in {SDK}/docs/references/index.chm for the specified interface by name, or if you have uncompressed the HTML-based API documentation (sdkdocs.tar.gz), you can view {SDK}/docs/references/api/class{InterfaceName}.html with your HTML browser. • IScript: {SDK}/public/interfaces/architecture/IScript.h • IScriptEventData: {SDK}/public/interfaces/architecture/IScriptEventData.h • IScriptProvider: {SDK}/public/interfaces/architecture/IScriptProvider.h • IScriptRequestHandler: {SDK}/public/interfaces/architecture/IScriptRequestHandler.h • IScriptUtils: {SDK}/public/interfaces/architecture/IScriptUtils.h Classes The following is a listing of commonly used scripting related partial implementation classes, which you can inherit from in your implementations.. For reference documentation, you can either search in {SDK}/docs/references/index.chm for the specified class by name, or if you have uncompressed the HTML-based API documentation (sdkdocs.tar.gz), you can view {SDK}/docs/references/api/class{ClassName}.html with your HTML browser. #10082 Making Your Plug-in Scriptable 51 #10082 Making Your Plug-in Scriptable Appendix • CScript: {SDK}/public/includes/CScript.h • CScriptEventData: {SDK}/public/includes/ CScriptEventData.h • CScriptProvider: {SDK}/public/includes/CScriptProvider.h Other Headers • ScriptingID.h: {SDK}/public/interfaces/architecture/ScriptingID.h. This header is needed in .fr files for base boss class IDs declarations. • ScriptInfoTypes.(f)h: {SDK}/public/includes/ScriptInfoTypes.(f)h • ScriptingDefs.h: {SDK}/public/includes/ScriptingDefs.h • ScriptingWindowDefs.h: {SDK}/public/includes/ScriptingWindowDefs.h Appendix How did the Scripting Architecture Change from InDesign/InCopy 2.x to InDesign CS/InCopy CS? The InDesign/InCopy 2.x Scriptable Plug-in Architecture In InDesign/InCopy 2.x, a scriptable plug-in contained all of the pieces necessary to support a script based operation. • All of the script providers • IScript implementations for every object • The core scripting architecture Information provided to the external (scripting) clients about what objects, properties, and events are available were provided by edge resources, which were platform-specific (AETE resource for AppleScript and ODL resource for Visual Basic). The Problems The 2.x architecture did allow for internal and external plug-ins to provide scripting support, but it wasn't very flexible. In particular: 52 #10082 Making Your Plug-in Scriptable Appendix • The script providers were language neutral in theory, but since only two languages were supported (AppleScript and Visual Basic), a lot of assumptions were made in the code. • Metadata about objects, properties, and events (names, types, descriptions, etc.) were trapped in script providers' code and edge resources. • It was difficult to add or remove additional elements. • It was difficult to provide a localized scripting experience, as it required building separate versions of plug-in for InDesign's Japanese feature set and for InCopy. • The feature coverage was incomplete. The Refactored InDesign CS/InCopy CS Scripting Architecture To solve the above problems, the InDesign CS/InCopy CS Scripting Architecture was refactored in the following ways: • The core scripting architecture was consolidated in a required plug-in (Scripting). • Scripting language support was moved into an optional edge plug-in (Support for AppleScript, Support for Visual Basic). • The scripting feature support was made easier to implement within the feature's (model) plug-in. • Metadata about objects, properties, and events are now stored in a single ODFRC resource. • APIs to access to metadata about objects, properties, events have been improved. The benefits provided by the improved architecture are: • Consistent information about the DOM is published to all clients of the scripting architecture. • Edge code when extending to other languages, such as JavaScript has been reduced. • The new architecture is easily reconfigurable (i.e. If the underlying feature is removed, so is the DOM support). The refactoring of the scripting architecture was originally conceived with the primary motivation to make the scripting architecture easier to extend, but as a side benefit, it enabled other technologies and features to be built on top. #10082 Making Your Plug-in Scriptable 53 #10082 Making Your Plug-in Scriptable Appendix • Saving a document in the InDesign Interchange file format for end users, the Package for GoLive feature, third party integrators. • JavaScript support. Using The TypeInfoConverter Tool The TypeInfoConverter tool, located at {SDK}/tools/typeinfoconverter/TypeInfoConverter.exe, is a convenient tool that facilitates the process of converting scriptable plug-ins developed for InDesign/InCopy 2.x to the new and improved InDesign CS/InCopy CS architecture. Specifically, this tool generates the Object, Property, Event, and Enum sections to the ScriptElementInfo resource, and also creates entries for the Provider section of the ScriptElementInfo resource. All scriptable plug-ins in the application have been converted using this tool. This section gives a brief user guide for this tool, and discusses some caveats. NOTE: Since this tool was written in Visual Basic, this tool is supported only on the Windows platform. Using The Tool For The First Time 1. Run {SDK}/tools/typeinfoconverter/TypeInfoConverter.exe. 2. The first time you run this tool, you will see this dialog that asks for a Type Library File: Specify the path to a scriptable plug-in from InDesign/InCopy 2.x, such as CustomPrefsScript.pln, as shown in the screen shot. 54 #10082 Making Your Plug-in Scriptable Appendix 3. This is followed by another dialog that asks for a Scripting Definitions File: Specify the path to the .h file for your InDesign/InCopy 2.x scriptable plug-in project where the script related IDs were defined. 4. Once you have specified the type library and definitions file paths, you will see the main dialog: Note that the various objects, properties, and events that were exposed from the scriptable plug-in have all been detected. Click on the different objects, properties and #10082 Making Your Plug-in Scriptable 55 #10082 Making Your Plug-in Scriptable Appendix events to verify that the definitions are as you originally defined them in your scriptable plug-in project. 5. Click on the "Open ODFRC File". This will open another file dialog box. Specify the path to the new .fr file you will be writing for the InDesign CS/InCopy CS version of your plug-in. (If you don't have one yet, just cancel out of the file dialog box.) After you select your .fr file (or cancel), you will see another window to the right titled "ODFRC File Browser", which looks like this: 6. To generate the Object section of a ScriptElementInfo resource, select one of the items in the Object frame, and click on the "Write One Object" button. You will see something like this be added into the ODFRC File Browser: Object { kCPrefsObjectScriptElement, c_CPrefs, "CPrefs", "Adobe InDesign 2.0 CustomPrefs Object", kCPrefs_CLSID, NoCollectionInfo, k/*?*/IDBasedObjectScriptElement, k/*?*/SuiteScriptElement, } RepresentObject{ kCPrefsObjectScriptElement }, The part from the word "Object" to the closing curly bracket ("}") is the part you will add within your ScriptElementInfo resource for this object. The last line, that starts with RepresentObject, will be added to the Provider section of your ScriptElementInfo resource. 56 7. To generate the Property, Event, or Enum sections of a ScriptElementInfo resource, select one of the items in the Properties, Events, or Enums frames, and click on the "Write One Property", "Write One Event", or "Write One Enum" button. You will see ODFRC File Browser being augmented with sections of the ScriptElementInfo resource, similar to what is shown above. 8. When you have generated ScriptElementInfo sections for all the necessary parts of your script objects, you can save the ODFRC sections in the ODFRC File Browser to a text file. It is recommended that you save this to a temporary file using the File > Save As menu item, so you can place the contents where you want to. #10082 Making Your Plug-in Scriptable Appendix 9. When you are done, click the "Quit" button on the main dialog. Running The Tool The Second Time And Onwards The TypeInfoConverter tool remembers your selections for the Type Library and the Scripting Definitions files. • If you want to change the Type Library file to examine, click on the "Open Type Library..." button. • If you want to change the Scripting Definitions file to examine, click on the "Open Definitions File..." button. (The selections are stored in the Windows Registry under the key HKEY_CURRENT_USER\Software\VB and VBA Program Settings\TypeLibBrowser\Files as string keys.) Caveats Since this tool was written, the ScriptElementInfo format has changed slightly. You can either hand-edit the generated ScriptElementInfo sections while pasting them into your .fr file. Please refer to the References section in this technical note for the latest resource definitions and field descriptions. If you find any other problems with this tool, please contact the InDesign/InCopy Developer Support Team at the Adobe Solutions Network. Additional Resources For Further Understanding • Microsoft Developers Network (MSDN) page on COM: http://msdn.microsoft.com/com/ • Apple Developer Connection (ADC) page on AppleScripting: http://developer.apple.com/techpubs/macosx/Carbon/interapplicationcomm/AppleScrip t/applescript.html • Adobe page on “Scripting with InDesign”: http://www.adobe.com/products/indesign/scripting.html #10082 Making Your Plug-in Scriptable 57 #10082 Making Your Plug-in Scriptable Appendix 58