Applescript Studio Plug In How To

Applescript Studio Plugins How To

-1) I’m not a writer, nor a professional coder… it’s in point form and the code may blow up… you have been warned. (give me cudos, not law suits)

  1. Plugins: What (resources that add functionality and are seperately loaded and developed… as in any extra application related resources… data, functions, nib files… ANYTHING), Why (more developers, better seperation of parts)

.5) Ingredients: property list osax from late nite software, xcode developer tools (probably works with earlier versions and project builder; but I haven’t tried it and the steps may change)

  1. Getting your project ready… (or you could make a new one…)

– Edit application target: Project > Add New Build Phase > Copy Files > Plug-Ins
– In MainMenu.nib add a “Submenu” to the main menu
– – set the title and remove unwanted items
– – give the /menu item/ an awake from nib handler in a new script ( see (A) )
– – – by menu item I mean: The menu item (as seen in the property inspector) /on/ the menu bar
– – – awake from nib
– – – – detects all the plugins
– – – – loads their property list
– – – – makes a menu item based on key in property list
– – – – attaches a script to the menu item
– – – – – has the plugin’s path
– – – – – has the plugin’s plist record
– – – – – and has a handler to run the plugin’s script

– Group > New Group > “PlugIn Resources”
– – This is for holding all the stuff for plugins so it doesn’t mix with the rest of your project

  1. Creating PlugIns

– Project > Add New Target > Legacy > Bundle (don’t add to Application target… we’ll do that after)
– Group > New Group > PlugIn Resources : PlugIn1
– Project > Add New File… > Applescript > Applescript File (add to plugin1 target, not to application)
– – put in group we made for it…


on run argv
    return "plugin one checking in"
end run

– Edit the Target’s Info.plist in Expert View
– – New Sibling > PlugInMenuTitle > String > PlugIn One
– – New Sibling > PlugInEntryPoint > String > plugin1.applescript
– Edit Application’s Target…
– – drag from Project:Products:PlugIn1.bundle
– – drop to Targets:Project:Copy Files Build Phase(PlugIns):

  1. Other Thoughts about plugins

– It may be possible to have a plugin load a nib with a panel and display it modally to the applications main window…
– – (note that the plugin script is in it’s own applescript context so you would need to pass a reference to the window in the args)
– (note that the plugin script is in it’s own applescript context so it is a little resource heavy to build and tear down that context on each run)
– you may even want to have perl script plugins, or data only plugins… differentiate in the bundle’s plist with a PlugInType key or something
– USE THE PLIST LUKE
– – You can put all kind of data in here and it’s easy to edit and access in your script
– Check out a plugin based app’s info panel in the Finder!!! coolness
– for others to develop publish what variables you send to the run handler and the kind of thing you expect in a result, and the expected plist keys

A) The Code – plug in menu’s script


on make_menu_script(an_item, a_pList, a_path)
	script PlugInMenuItemScript
		property _path : a_path
		property _pList : a_pList
		on choose menu item an_item
			set the_script_path to (_path & ":Contents:Resources:" & _pList's |PlugInEntryPoint|)
			set the_message to run script file the_script_path 
				-- with parameters arg_list 
			log the_message as string 
				-- do something with the result
		end choose menu item
	end script
	set an_item's script to PlugInMenuItemScript
end make_menu_script

on awake from nib theObject
	try
		set plugins_folder to POSIX file ((main bundle's path as string) & "/Contents/PlugIns/")
		repeat with each_plugin in list folder plugins_folder
			set plugin_path to ((plugins_folder as string) & each_plugin)
			set a_pList to read property list file (plugin_path & ":Contents:info.plist")
			set plugin_title to a_pList's |PlugInMenuTitle|
			set a_menu_item to make new menu item at the end of the menu items of the sub menu of theObject with properties {title:plugin_title, enabled:true}
			make_menu_script(a_menu_item, a_pList, plugin_path)
		end repeat
	on error message
		log message
	end try
end awake from nib

B) development issues

– make sure you clean all targets between builds… some changes may not be applied with multiple targets
– when making a plugin as a seperate xcode project start with an empty project and add the bundle target as before in (2)
– – (don’t use one of the built in bundles–if you are desperate make your own template ( that’s for another time ) )

C) Inspiration Credit: Joe Zobkiw “Mac OS X – Advanced Development Techniques”, Cocoa/Carbon Plugin chapters

This looks helpful. Do you think you could post a shell project? I’ve got an idea for a plug-in that I think would be very helpful to scripter.

Thanks,
Jon

What kind of plug-in were you thinking of supplying people? A bit of code that runs on the program’s start up, a data set, maybe a menu of items that could be usefull in any program (say a debug menu plug in… hmmm that sounds like a good idea!)

I’m not quite sure what kind of plug in you want to make…Note that plugins can be anything. I chose bundles because they allow you to put resources and scripts in there, but if you put a normal plist, text file, image, nib, clipping, alias, or any other file into the applications PlugIns folder it’s a plugin… the point is without an agreement between user program and supplying plug in (say like a “contract” but without trademark issues) specifying what kinds of inputs the plugin expects and needs, and the kinds of outputs the application expects from the plugin, you can’t really do much. It would be possible to build a system where the info.plist files of each declared their agreements and the program could detect plugins that met their criteria, but that’s beyond the casual (and normal) level of ASStudio.

The project I am working on right now is a larger one that I have componentized into a plug in architecture for simpler development. I use some plug ins that provide image files and textual data–other plugins provide manipulations of that data. Both types have menus that have awake on nib handlers (In seperate scripts) that go through all the plugins as shown in the awake on nib handler shown in (A) and use an info.plist key to detect the type of plugin and create a menu item based on the type of plugin. So in a sense that declaration in the plugin of what type of plugin it is is the contract. I have a seperate text file in my project that’s not part of any target that defines what other keys are expected to be in a certain type of plugin and what information the application will send to a type of plugin, as well as what it expects to recieve. (to remind me)

Here’s a sample of that “plugin api.text” file slightly edited(note that it’s not parsed by anything but me…yet):

Anyway all that to say… tell me about your plugin and I can give you a project shell that will load it. (or vice versa)… There needs to be a per application plugin protocol. This is true even with Cocoa or other plugin systems, as the point is to teach your apps new tricks, but different apps only can adapt in certain ways… It reminds me of a joke my friend used to tell: Q:“What do you do if you’re driving your canoe through the desert and a wheel falls off?” A:“Nothing. Snakes don’t have armpits!” :wink: okay maybe that has nothing to do with it…sorry

I’ve got a bunch of scripts for Xcode (they will only really work when running in single window mode) that help scripters. For instance, take a look at this script:

http://homepage.mac.com/jonn8/as/dist/MessageCancel.sit

In Xcode, working in single window mode, select a paragraph in your AppleScript code, then run this script. I want to create an plug-in to Xcode that allows me to place scripts into a folder and have the plug-in dynamically create menu items for every item in the folder (or submenus for subfolders) and simply execute the script (I’ve got about 70 of these scripts so far). So it needs to be a plug-in to add a new menu to Xcode itself.

Thanks so much for your generous tutorials.

Jon

PS I finally got the AS Studio status menu project with dynamic menus working without problems. Let me know if you’re interested and I’ll post some more info.

There’s two parts to this answer so I’ll give you the less good part first.

There is a way to make plugins for xcode (notice the plugins suite in the ASKDictionary…see also studioreference section on plungins) that comes with a template for making such a project in XCode. I wanted to make one but the template xcode files crash xcode for me so I haven’t been able to use them…

Here’s the good news. If it does work for you (Try New Project > Standard Apple Plugins > Applescript XCode Plugin) then you should be given a project for creating a menu to add to XCode. (1) in the applescript file create an on plugin loaded handler that (a) creates a new menu on the bar to hold your items (b) uses the bundle argument passed to the handler to find its plugins directory (c) for each file in that directory add a menu item the calls the script. (2) Create a copy files build phase in the main target to add files to the PlugIns directory. (3) add scripts to the project and put them in the copy files build phase we made in (2). (Note that what this is is making an xcode applescript plugin, be plugin-able (is that a word?))

heres sample code for the “on plugin loaded theBundle” handler


on add_item_script(a_path,an_item)

   script ThisItemScript
       property _path : a_path
       on choose menu item theItem
           run script file _path
       end choose menu item
   end script

   set an_item's script to ThisItemScript

end add_item_script

on plugin loaded theBundle -- Make a new script script pluginScript property 

    set plugins_path to POSIX file ((theBundle's path as string) & "/Contents/PlugIns/")
    set plugin_menu to make new menu at end of menus of main menu with properties {title:"My Helpers"}
    repeat with each_plugin in list folder (plugins_path)
      set my_path to (plugins_path as string & each_plugin)
      set my_item to make new menu item at the end of the menu items of plugin_menu with properties{title:each_plugin}
      add_item_script(my_path,my_item)
   end repeat

end plugin loaded


That’ll probably do what you want… but as I said I can’t test it… /-_-,… you could probably also get the selected text in ThisItemScript’s on choose menu item script and send it as an argument to your other script’s (The plugin’s plugin’s) on run handler so you can leave that code out of each plugin…

If like me you’re having troubles getting the XCode Plugin template to work, just go in and manually adjust both the .h file and .m file that come with the project… you should see where, what, and how to change even without objc knowledge…

This only seems to work on 10.3+. If you get the properties of a menu item in 10.3, then it returns a script property (that you can also set) but in 10.2.x, there is no script property for menu items and trying to set it results in an “NSCannotCreateScriptCommandError”.

I know that this thread is for Xcode plug-ins and Xcode necessarily limits the code to running on a 10.3+ machine but this limitation precludes similar techniques from working for building dynamic menus in apps that need to run on 10.2.x machines. Does anyone have a workaround for this?

Jon

You have the answer to this… check your MenuApp example for how to write the actions in obj-c use call method to create the menu with attached action… :smiley:

That’s how to set a menu item’s action with an Obj-C bridge, but I want to do it all in AppleScript and set its script. I actually came up with a viable solution that works both 10.2 & 10.3 all in AppleScript that I’ve employed in my AppleScript Studio app Finder Window Manager. What I’ve done is create a dummy menu that is not visible that has menu items with the handler “choose menu item” already attached to them and are already named. Then when I need to create new menu items, I just duplicate these prototype menu items to the end of the dynamic menu and change their titles:

Then, in my “choose menu item” handler, I check for the object name and then, on a match, I act on the item based on the name.

Jon


[This script was automatically tagged for color coded syntax by Convert Script to Markup Code]

Actually, my solution above DID NOT WORK. So, it is not a solution. While the duplicate command did indeed create a menu item that allowed me to change the title and it had the script already associated with it, when selecting any of the duplicate menu items, the application just registered a “choose menu item” call for the original item, regardless of the title or location of the duplicate. Silly testing of FWM on my part didn’t allow me to see this until after I had released the app. I have released a fix that goes back to my solution of a circular call to a custom Obj-C method that in turn calls a script. Not as elegant as I’d like but it does work.

Jon

So what you’re trying to do is basically allow Xcode to have the same type of contextual menu scripts that are available in Script Editor?

If so, that’d definitely rock.