Make Cocoa application respond to simple AppleScript command - cocoa

I am trying to add a trivial AppleScript support to a Cocoa application. The application performs a check periodically and I just want to be able to tell it to perform it on demand.
I am trying to follow the SimpleScriptingVerbs Apple example.
I have subclassed NSScriptCommand as follows:
Header:
#import <Cocoa/Cocoa.h>
#interface rdrNotifierUpdateCommand : NSScriptCommand {
}
-(id)performDefaultImplementation;
#end
Implementation:
#import "rdrNotifierUpdateCommand.h"
#import "rdrNotifierAppDelegate.h"
#implementation rdrNotifierUpdateCommand
-(id)performDefaultImplementation {
NSLog(#"Works at last");
[((rdrNotifierAppDelegate *)[[NSApplication sharedApplication] delegate])
checkForNewItems]; // This just fires the timer
return nil;
}
#end
My .sdef file goes as follows (and the problem seems to be there, but I cannot find it):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="Dictionary" xmlns:xi="http://www.w3.org/2003/XInclude">
<xi:include href="file:///System/Library/ScriptingDefinitions/CocoaStandard.sdef" xpointer="xpointer(/dictionary/suite)"/>
<suite name="rdrNotifier Suite" code="rdrN" description="rdrNotifier application specific scripting facilities.">
<command name="do update" code="rdrNUpdt" description="Check for new items">
<cocoa class="rdrNotifierUpdateCommand"/>
</command>
</suite>
</dictionary>
The Info.plist is set up appropriately.
But, when I try to run the following script in AppleScript editor:
tell application "rdrNotifier"
do update
end tell
I receive an error about variable "update" not being defined.
I can open the dictionary for my application from AppleScript Editor (i.e. it is successfully registered).
Edit: Found a solution
The problem was indeed in the sdef file: I was not specifying that the application can reply to the command. My final definition goes as follows (Obj-C code unchanged):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="Dictionary" xmlns:xi="http://www.w3.org/2003/XInclude">
<!-- I have removed the standard suite as the application does not open, print... -->
<suite name="rdrNotifier Suite" code="rdrN" description="rdrNotifier application specific scripting facilities.">
<command name="do update" code="rdrNUpdt" description="Check for new items">
<cocoa class="rdrNotifierUpdateCommand"/>
</command>
<class name="application" code="Capp">
<cocoa class="NSApplication"/>
<responds-to name="do update">
<!-- Note you need to specify a method here,
although it is blank -->
<cocoa method=""/>
</responds-to>
</class>
</suite>
</dictionary>
Any improvements/tips/criticisms are still welcome.

Thanks for making the effort of adding applescript support to your app! Just a quick observation / criticism: When constructing terminology, by all means include spaces, but if that terminology takes the form of 'verb noun' (like 'do update') appleScripters will be irritated if the noun 'update' is not a properly modelled object, and if 'do' is not a proper command.
I would argue that 'do' is a poor choice for a command name, because it is very vague. What's wrong with just using 'update' as the command? (i.e. treat it as a verb).
This issue is treated in some detail by Apple's own technote 2106 (currently here), which you definitely should read and digest if you hope to please your AppleScripting user community.
Apple themselves are not beyond stupid terminology choices such as updatePodcast and updateAllPodcasts (in iTunes). These are stupid choices because (according to tn2106) they contain no spaces, and because the better choice would be to have a proper podcast class and then have an update command which could be used on an individual podcast, all podcasts, or a particular chosen set of podcasts.

Related

Cocoa Scripting: Integrating the Text Suite

I am attempting to use the Text Suite in my scriptable Mac app.
The little documentation I found in the Cocoa Scripting Guide suggests to use NSTextStorage. But it does not explain how the rest needs to be set up, both in the Sdef and on the coding side.
In particular, I wonder how I tell my scripting definition when to use the Text Suite with its richer commands and when not. The problem I see is that the Text Suite declares its own type named text. But that's already a predefined type used for plain text as well.
So I end up with two choices of "text" in the Sdef editor's text selector for my elements' properties, and to the Sdef that gets written it's the same type. Meaning I cannot make a distinction in the Sdef between properties that handle text that supports the Text Suite and those that don't, even if my code doesn't always use NSTextStorage for them.
Does that mean that I need to store all my scriptable properties in objects of class NSTextStorage? Otherwise, a user may expect to use the extended Text Suite commands on any textual property but will get runtime errors if I use NSString instead of NSTextStorage for them, right?
But for simple properties that only give simple plain text strings back, such as the app's name, it would be overkill to support the Text Suite, doesn't it?
So, how shall I go about this? Shall I simply let the user figure it out when he can use the Text Suite and where not because I use NSTextStorage only for properties that I deem worthy of the rich text support? How do others solve this?
Update
Turns out that when I simply add the Text Suite to my Sdef (I'm using the one the Sdef editor offers as "NSTextSuite" under its File submenu), all properties that return text stop working (causing error -10000). I've compared the Text Suite entries to those from other apps and cannot see any obvious differences.
Why would adding the Text Suite break all properties that have a property of type "text"?
What follows is an abbreviated Sdef so you can see what I'm doing:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="Demo Terminology">
<suite name="Demo Suite" code="Demo" description="Demo suite">
<class name="application" code="capp" description="The application&apos;s top-level scripting object.">
<cocoa class="NSApplication"/>
<property name="demo text" code="DeTx" description="A text prop for testing" type="text">
<cocoa key="testText"/>
</property>
</class>
</suite>
<suite name="Text Suite" code="????" description="A set of basic classes for text processing.">
<cocoa name="NSTextSuite"/>
<class name="text" code="ctxt" description="Rich (styled) text" plural="text">
<cocoa class="NSTextStorage"/>
<element type="paragraph">
<cocoa key="paragraphs"/>
</element>
<element type="word">
<cocoa key="words"/>
</element>
<element type="character">
<cocoa key="characters"/>
</element>
<element type="attribute run">
<cocoa key="attributeRuns"/>
</element>
<element type="attachment">
<cocoa key="attachments"/>
</element>
<property name="color" code="colr" description="The color of the first character." type="color">
<cocoa key="foregroundColor"/>
</property>
<property name="font" code="font" description="The name of the font of the first character." type="text">
<cocoa key="fontName"/>
</property>
<property name="size" code="ptsz" description="The size in points of the first character." type="number">
<cocoa key="fontSize"/>
</property>
</class>
<class name="attachment" code="atts" description="Represents an inline text attachment. This class is used mainly for make commands." inherits="text">
<cocoa class="NSAttachmentTextStorage"/>
<!-- This property should be deprecated like all the other path-centric properties, and replaced with a type="file" property. -->
<property name="file name" code="atfn" description="The path to the file for the attachment" type="text">
<cocoa key="filename"/>
</property>
</class>
<class name="paragraph" code="cpar" description="This subdivides the text into paragraphs." inherits="text">
<cocoa class="NSTextStorage"/>
</class>
<class name="word" code="cwor" description="This subdivides the text into words." inherits="text">
<cocoa class="NSTextStorage"/>
</class>
<class name="character" code="cha " description="This subdivides the text into characters." inherits="text">
<cocoa class="NSTextStorage"/>
</class>
<class name="attribute run" code="catr" description="This subdivides the text into chunks that all have the same attributes." inherits="text">
<cocoa class="NSTextStorage"/>
</class>
</suite>
<suite name="Standard Suite" code="????" description="Common classes and commands for most applications.">
<cocoa name="NSCoreSuite"/>
<class name="color" code="colr" description="A color.">
<cocoa class="NSColor"/>
</class>
</suite>
</dictionary>
Running this script will then result in error -10000:
tell application "Demo"
demo text
end tell
If I remove the "Text Suite" from the Sdef, the code runs without error.
Looks like the Text Suite that the Sdef Editor offers is not usable for apps using the Cocoa Scripting framework. Nor can one adapt the technique that "TextEdit.app" uses, where the text class gets an id assigned ("text.ctxt"), which is then used with property types that shall use the Text Suite's text class.
Instead, the one from the "Sketch" sample code should be used. That one renames the type from "text" to "rich text", thereby solving the issue of conflicting type names I have described in my question.
I find it curious that that "classic" apps use the type "text" for both plain and rich text properties, whereas that appears not possible any more with modern apps that rely on the new Cocoa Scripting framework.
I believe there is an error in the generated SDEF in the line:
<cocoa name="NSTextSuite" />
The tag is used to reference classes or variables in your code. There is no class named NSTextSuite in the framework, afaik. The text suite definition should start like this:
<suite name="Text Suite" code="????" description="...">
<class name="rich text" plural="rich text" code="ctxt" description="Rich (styled) text." inherits="item" hidden="yes" >
<cocoa class="NSTextStorage"/>
<type type="text"/>
...

QuickLink in UML Profile not displaying in Sparx enterprise Architect

I am trying to add a new item to the 'quick link' menu in Sparx Enterprise Architect.
I have followed the instructions on the EA website:
http://www.sparxsystems.com/enterprise_architect_user_guide/10/extending_uml_models/add_quick_linker_definition_to.html
and in despiration I have also tried a vanilla copy of the example from http://www.sparxsystems.com/enterprise_architect_user_guide/10/extending_uml_models/quick_linker_example.html
I have a new profile with a new stereotype of 'quick' which extends the metaclass 'Class'. I have added a 'QuickLink' artefact and copied the below entries into it (from the example above):
Class,quick,,,,Component,,Dependency,,to,,Dependency to,TRUE,TRUE,TRUE,TRUE,Component,0,,,,,
Class,quick,,,,Component,,Dependency,,from,,Dependency from,TRUE,TRUE,TRUE,TRUE,Component,0,,,TRUE,,
Class,quick,Component,,,,,Dependency,,to,Dependency to,,TRUE,,TRUE,TRUE,,0,,,,,
Class,quick,Component,,,,,Dependency,,from,Dependency from,,TRUE,,TRUE,TRUE,,0,,,TRUE,,
Class,quick,Port,,,,,Dependency,,to,Dependency to,,TRUE,,TRUE,TRUE,,0,,,,,
Class,quick,Port,,,,,Dependency,,from,Dependency from,,TRUE,,TRUE,TRUE,,0,,,TRUE,,
Class,quick,Component,,,Port,,Dependency,,to,,Dependency to,TRUE,TRUE,TRUE,TRUE,Port,0,,TRUE,,,
Class,quick,Component,,,Port,,Dependency,,from,,Dependency from,TRUE,TRUE,TRUE,TRUE,Port,0,,TRUE,TRUE,,
I have then saved the UML Profile and generated an MDG from it. I have looked in the profile.xml and MDG xml files and in both cases the CSV information appears as I would expect in the QuickLink element:
e.g.
When I import the MDG, I can create a new diagram, the correct toolbox appears with my 'quick' stereotype on it. When I drag it onto the diagram however and try to create new links, the quicklinks menu is not showing any of my customisations.
Is there anythink I am missing here to make this work?
The problem doesn't appear to be the quicklinker definition. I have created a working technology using that definition (below).
It is likely that the problem is with the way the technology has been put together, rather than the quicklinks themselves. Hopefully you can see what is different by comparing this to your technology. If not then post your technology and I will take a look.
<?xml version="1.0" encoding="windows-1252"?>
<UMLProfile profiletype="uml2">
<Documentation id="C5186393-D" name="MyProfile" version="1.0" notes="MyProfile"/>
<Content>
<Stereotypes>
<Stereotype name="quick" notes="">
<AppliesTo>
<Apply type="Class">
<Property name="isActive" value=""/>
</Apply>
</AppliesTo>
</Stereotype>
</Stereotypes>
<TaggedValueTypes/>
<QuickLink data="Class,quick,,,,Component,,Dependency,,to,,Dependency to,TRUE,TRUE,TRUE,TRUE,Component,0,,,,,
Class,quick,,,,Component,,Dependency,,from,,Dependency from,TRUE,TRUE,TRUE,TRUE,Component,0,,,TRUE,,
Class,quick,Component,,,,,Dependency,,to,Dependency to,,TRUE,,TRUE,TRUE,,0,,,,,
Class,quick,Component,,,,,Dependency,,from,Dependency from,,TRUE,,TRUE,TRUE,,0,,,TRUE,,
Class,quick,Port,,,,,Dependency,,to,Dependency to,,TRUE,,TRUE,TRUE,,0,,,,,
Class,quick,Port,,,,,Dependency,,from,Dependency from,,TRUE,,TRUE,TRUE,,0,,,TRUE,,
Class,quick,Component,,,Port,,Dependency,,to,,Dependency to,TRUE,TRUE,TRUE,TRUE,Port,0,,TRUE,,,
Class,quick,Component,,,Port,,Dependency,,from,,Dependency from,TRUE,TRUE,TRUE,TRUE,Port,0,,TRUE,TRUE,,
"/>
</Content>
</UMLProfile>

How can I find (and rehabilitate, if necessary) the Code Snippet I created?

I created a code snippet (the first of many, hopefully) using mainly this article as guidance.
It seemed to work. Here are the steps I took:
First, I created this file with the code snippet (HtmlTableRowWithTwoCells.snippet):
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/CodeSnippet">
<CodeSnippet Format="1.0.0">
<!-- The Title of the snippet, this will be shown in the snippets manager. -->
<Title>Create a 2-Cell HTML Table Row</Title>
<!-- The description of the snippet. -->
<Description>Creates a 2-Cell Row to be added to an HtmlTable</Description>
<!-- The author of the snippet. -->
<Author>Warble P. McGorkle for Platypi R Us (Duckbills Unlimited)</Author>
<!-- The set of characters that must be keyed in to insert the snippet. -->
<Shortcut>row2</Shortcut>
<!-- The set of snippet types we're dealing with - either Expansion or -->
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<!-- Now we have the snippet itself. -->
<Snippet>
<Declarations>
<Literal>
<ID>RowName</ID>
<ToolTip>Enter the Row instance name</ToolTip>
<Default>RowName</Default>
</Literal>
<Literal>
<ID>Cell1Name</ID>
<ToolTip>Enter the name for Cell 1</ToolTip>
<Default>Cell1Name</Default>
</Literal>
<Literal>
<ID>Cell2Name</ID>
<ToolTip>Enter the name for Cell 2</ToolTip>
<Default>Cell2Name</Default>
</Literal>
</Declarations>
<!-- Sepecify the code language and the actual snippet content. -->
<Code Language="CSharp" Kind="any">
<![CDATA[
var $RowName$ = new HtmlTableRow();
var $Cell1Name$ = new HtmlTableCell();
var $Cell2Name$ = new HtmlTableCell();
$RowName$.Cells.Add($Cell1Name$);
$RowName$.Cells.Add($Cell2Name$);
]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
Then, I created this "manifest" file (.vscontent):
<?xml version="1.0" encoding="utf-8" ?>
<VSContent xmlns="http://schemas.microsoft.com/developer/vscontent/2005" >
<Content>
<FileName>HtmlTableRowWithTwoCells.snippet</FileName>
<DisplayName>Inserts a 2-Cell HTML Table Row</DisplayName>
<Description>Inserts a 2-Cell Row to be added to an HtmlTable</Description>
<FileContentType>Code Snippet</FileContentType>
<ContentVersion>2.0</ContentVersion>
<Attributes>
<Attribute name="lang" value="csharp"/>
</Attributes>
</Content>
</VSContent>
I zipped those two files together, renamed the extension from .zip to .vsi, 2-clicked it, and installed it into the "My Code Snippets" folder here with these steps:
And it indicates the snippet was installed in a reasonable location:
Yet, when I attempt to add a Code Snippet in VS, the only categories I see are these (no "My Code Snippets"):
When I select Tools > Code Snippets Manager..., I can navigate to Visual C# > My Code Snippets, but it is empty.
When I use the Code Snippets Manager's "Import" button and navigate to the location of the snippet and attempt to add the snippet file, I get, "The snippet files chosen were not valid."
Why does it tell me it installed successfully, when it apparently didn't (or where is it hiding)? What flaming hoop did I neglect to catapult myself through?
Is the "weird" name of the "manifest" file possibly the problem? ".vscontent" seems odd to me, but that's what the article referenced above says to name it. Perhaps that was just on oversight, and it should really be [snippetName].vscontent?
UPDATE
Apparently, naming the "manifest" file *.vscontent" is not the problem. Maybe it's a problem, but it's not the problem, because I named it the same as the .snippets file (except for the extension), went through the installation process again, and got the same exact results: seeming success, actual demoralization.
BTW, by default, when choosing a category into which to place the snippet, the Code Snipeets Manager puts a checkbox in "Microsoft Visual Studio Tools for Applications 2005 > My Code Snippets". I had previously unticked that and ticked the topmost "My Code Snippets"; this time I retained the default selection, PLUS my preferred location/category PLUS "Visual C#"
But alas and anon, the only category of those that seems to dispaly via Ctrl+K, X in VS is C#, and the expected shortcut ("row2") does not appear in the snippet dropdown.
Here's an easier way (and, as a bonus, it works):
Download the Snippets Designer:
Write the code directly in Visual Studio:
var RowName = new HtmlTableRow();
var Cell1Name = new HtmlTableCell();
var Cell2Name = new HtmlTableCell();
RowName.Cells.Add(Cell1Name);
RowName.Cells.Add(Cell2Name);
Right-click and select "Export as Snippet"
This puts the code in the Snippet Designer. Here's what it looks like after setting a few properties, and electing which parts of the code would be replaceable:
This is quite easy - sure beats the "olde-fashioned" way I was trying (which would have been okay, had it worked).
All I had to do was right-click the elements of code I wanted to replace on adding a new snippet, set the snippet's shortcut and description properties in Solution Explorer, save it, and voila!
Now mashing Ctrl+K, X in VS shows the snippet in "My Code Snippets" along with its description and shortcut:
Selecting it adds the snippet with the replacement strings highlighted:

Autocorrect / insert textblock by hotkey

I want to insert a default block of text when hitting a hotkey or when entering a special string in Visual Studio 2010 (either way would be fine).
Is there a fast and easy way to achieve this?
(Preferably without the use of third party extensions. )
Background / further explanation:
I want to use a special trigger-word to insert a default doxygen-commentblock, like "///" or "'''" for XML-commentblocks.
Other than the XML-functionality my inserted text does not have to be intelligent, it would suffice to just insert a default textblock.
My suggested trigger-string would be "---" as it would not collide with any program language I know. My suggested hotkey would be Alt+V.
Thanks for the help
Janis
Just write your own Code Snippet:
Code snippets are small blocks of reusable code that can be inserted in a code file using a context menu command or a combination of hotkeys. They typically contain commonly-used code blocks such as try-finally or if-else blocks, but they can be used to insert entire classes or methods.
Snippets are simply XML files that contain instructions to the Code Editor. For instance, here's the one that VS 2012 supplies for the C# if snippet:
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>if</Title>
<Shortcut>if</Shortcut>
<Description>Code snippet for if statement</Description>
<Author>Microsoft Corporation</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>expression</ID>
<ToolTip>Expression to evaluate</ToolTip>
<Default>true</Default>
</Literal>
</Declarations>
<Code Language="csharp"><![CDATA[if ($expression$)
{
$selected$ $end$
}]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
You add new snippets to the IDE using the Code Snippet Manager, available from the Tools menu item from the main menu.

palettes of icons in Google Earth (Mac)

(Cross posted from the GE support groups - now defunct?)
Having trouble using the gs:x extensions to use palettes of icons in an icon
group.
I have loaded the appropriate xmlns:gx="http://www.google.com/kml/ext/2.2"
into the kml header but get the message "Unknown type gs:x
on my Macintosh under GE Google Earth 6.0.3.2197
I suspect this has not been implemented on the Mac version - anybody
with experience on this?
Final code was as follows and it fails on the first gx:s line.
It also fails in the same way if I use the now deprecated x (rather than gx:x)
Also, as shown it follows the kml documentation but I think all terminating terms should be of the form /gx:x rather than gx:x/
as shown in the KML reference. Making that change does not help as it never gets to that point anyway.
The header was copied from a GE placemark copied and pasted into the
editor.
Any help appreciated.
Bob J.
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:kml="http://www.opengis.net/kml/2.2"
xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<StyleMap id="s_Ic_SP">
<Pair><key>normal</key><styleUrl>#sn_Ic_SP</styleUrl></Pair>
<Pair><key>highlight</key><styleUrl>#sh_Ic_SP</styleUrl></Pair>
</StyleMap>
<Style id="sn_Ic_SP">
<IconStyle>
<scale>1.8</scale>
<Icon>
<href>Icons/Traps.png</href>
<gx:x>0<gx:x/><gx:y>128<gx:y/> <gx:w>64<gx:w/><gx:h>64<gx:h/>
</Icon>
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/>
</IconStyle>
<BalloonStyle>
<displayMode>default</displayMode><bgColor>ff00d0ff</bgColor>
<text><![CDATA[<font face="Comic Sans MS" /><table
bgcolor="#ff8000" cellspacing="3" width="160">
<tr bgcolor="#ffff80"><td><b>Sponsor $[name]</b><br/><br/>$
[description]</td></tr></table>]]></text>
</BalloonStyle>
<LabelStyle>
<scale>0.9</scale><color>ff00ffff</color>
</LabelStyle>
<LineStyle><color>ff00ffff</color><width>2.0</width></LineStyle>
<ListStyle>
<ItemIcon>
<href>Icons/Traps.png</href>
<gx:x>0<gx:x/><gx:y>128<gx:y/> <gx:w>64<gx:w/><gx:h>64<gx:h/>
</ItemIcon>
</ListStyle>
</Style>
etc.
Oooops
Seems my problem was a syntax problem
The gx:x set do work correctly. However, they are not operational inside the ItemIcon group as shown in my example. In fact they are ignored in that group.
Thanks for your tolerance of my stupidity
Bob J.

Resources