<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="wordpress/2.3.3" -->
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>Vacuous Virtuoso &#187; Cocoa</title>
	<link>http://lipidity.com</link>
	<description>Despotic Development</description>
	<pubDate>Wed, 02 Apr 2008 14:18:15 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.3.3</generator>
	<language>en</language>
			<item>
		<title>More CoreGraphics Hackery</title>
		<link>http://lipidity.com/apple/cocoa/cgsinternal/</link>
		<comments>http://lipidity.com/apple/cocoa/cgsinternal/#comments</comments>
		<pubDate>Sat, 12 Jan 2008 04:21:03 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Apple]]></category>

		<category><![CDATA[CoreGraphics]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Review]]></category>

		<guid isPermaLink="false">http://lipidity.com/apple/cocoa/cgsinternal/</guid>
		<description><![CDATA[Alacatia Labs, Inc. bring you the most thorough investigation of undocumented CoreGraphics functions to date, with CGSInternal.

There&#8217;s a subversion repository as well if you&#8217;re keen to stay up to date.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://alacatialabs.com/">Alacatia Labs, Inc.</a> bring you the most thorough investigation of undocumented <a href="http://lipidity.com/tag/core-graphics+animation">CoreGraphics</a> functions to date, with <a href="http://alacatialabs.com/toys/cgsinternal/">CGSInternal</a>.</p>

<p>There&#8217;s a <a href="http://svn.alacatialabs.com/CGSInternal/trunk/">subversion repository</a> as well if you&#8217;re keen to stay up to date.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/cocoa/cgsinternal/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Modifier flags during app launch</title>
		<link>http://lipidity.com/apple/modifier-flags-launch/</link>
		<comments>http://lipidity.com/apple/modifier-flags-launch/#comments</comments>
		<pubDate>Tue, 27 Nov 2007 10:45:57 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Source Code]]></category>

		<category><![CDATA[Tutorial]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://lipidity.com/apple/modifier-flags-launch/</guid>
		<description><![CDATA[Unfortunately, [[NSApp currentEvent] modiferFlags] can&#8217;t be used to determine if any modifier keys were held down during launch. To accomplish this, it seems one must delve into the world of Carbon.



The GetCurrentEventKeyModifiers() call works on Mac OS X v10.2 and later through which one can check for shiftKey, cmdKey, optionKey and controlKey.

I use the something [...]]]></description>
			<content:encoded><![CDATA[<p>Unfortunately, <code>[[NSApp currentEvent] modiferFlags]</code> can&#8217;t be used to determine if any modifier keys were held down during launch. To accomplish this, it seems one must delve into the world of Carbon.</p>

<!--more-->

<p>The <code>GetCurrentEventKeyModifiers()</code> call works on Mac OS X v10.2 and later through which one can check for <code>shiftKey</code>, <code>cmdKey</code>, <code>optionKey</code> and <code>controlKey</code>.</p>

<p>I use the something like the following to check if the shift key is pressed during launch for <a href="http://lipidity.com/software/zippit/">my compression app</a>.</p>

<pre><code>#import &lt;Cocoa/Cocoa.h&gt;
#import &lt;Carbon/Carbon.h&gt;

int main(int argc, char *argv[]) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    if((GetCurrentEventKeyModifiers() &amp; shiftKey)!=0) {
        // Change something useful
        NSLog(@"Shift key pressed during launch");
    }
    [[NSApplication sharedApplication] run];
    [pool release];
    return 0;
}
</code></pre>

<p>In the highly unlikely event that you&#8217;ve just got a call to <code>NSApplicationMain</code> in your <code>main</code> function (joking), you can just test <code>(GetCurrentEventKeyModifiers() &amp; shiftKey)!=0</code> in the <code>-applicationWillFinishLaunching:</code> method in your NSApplication delegate.</p>

<p>Any new APIs in Leopard to achieve this or any easier way?</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/modifier-flags-launch/feed/</wfw:commentRss>
		</item>
		<item>
		<title>CTGradient code bloat</title>
		<link>http://lipidity.com/apple/ctgradient-code-bloat/</link>
		<comments>http://lipidity.com/apple/ctgradient-code-bloat/#comments</comments>
		<pubDate>Tue, 20 Nov 2007 21:05:12 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[CoreGraphics]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Review]]></category>

		<category><![CDATA[Source Code]]></category>

		<category><![CDATA[Tutorial]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://lipidity.com/apple/cgradient-code-bloat/</guid>
		<description><![CDATA[While getting rid of extraneous junk in an application package is easy using Trimmit, the only way to prevent &#34;code bloat&#34; (and accompanying excess RAM and CPU usage) is through good programming practices. Where most developers fall short is in poor optimization of borrowed code. Let&#8217;s take CTGradient as an example as it&#8217;s well known [...]]]></description>
			<content:encoded><![CDATA[<p>While getting rid of extraneous junk in an application package is easy using <a href="http://lipidity.com/software/trimmit/">Trimmit</a>, the only way to prevent &quot;code bloat&quot; (and accompanying excess RAM and CPU usage) is through good programming practices. Where most developers fall short is in poor optimization of borrowed code. Let&#8217;s take CTGradient as an example as it&#8217;s well known and used (or more accurately, abused) in dozens of applications.</p>

<!--more-->

<p>CTGradient contains an incredible diversity of built-in gradients, gradient styles, and methods for creating fancy rainbows, radial gradients, linear gradients, aqua gradients, and a number of other interesting class and instance methods. It allows you to dynamically alter gradients by adding or removing colors, changing the level of transparency, filling NSRects or NSBezierPaths, rotating the gradient, etc. For demonstration purposes, all these features are excellent. For production, this is a nightmare.</p>

<p><strong><a href="http://pub.lipidity.com/CTGradient/CTGradient.m">CTGradient.m</a> weighs in at over 1300 lines of code.</strong></p>

<p>Ignoring Mr. Weider&#8217;s unique style of formatting, take a look through the code. If you&#8217;re putting this class in an application, you can immediately remove over a thousand lines. Just like that. But if you take some time to understand what&#8217;s going on, you can optimize this thing till it runs like a Ferrari.</p>

<p>You&#8217;ll notice <a href="http://lipidity.com/software/trimmit/">Trimmit</a> uses a gradient background for it&#8217;s window. Let&#8217;s cut down CTGradient until it matches the level of optimization of Trimmit&#8217;s gradient code.</p>

<h2>Clear the junk</h2>

<p>Firstly, let&#8217;s remove the methods we know for sure we won&#8217;t need. Remove the following methods completely from both the interface and the implementation (scroll down for more):</p>

<pre style='height:340px;overflow:scroll'><code>// We don't need the preset gradients

+ (id)dividerGradient;
+ (id)statusBarGradient;
+ (id)aquaSelectedGradient;
+ (id)aquaNormalGradient;
+ (id)aquaPressedGradient;
+ (id)unifiedSelectedGradient;
+ (id)unifiedNormalGradient;
+ (id)unifiedPressedGradient;
+ (id)unifiedDarkGradient;
+ (id)sourceListSelectedGradient;
+ (id)sourceListUnselectedGradient;
+ (id)rainbowGradient;
+ (id)hydrogenSpectrumGradient;

// We won't make any drastic modifications to the gradient once created

- (CTGradient *)gradientWithAlphaComponent:(float)alpha;
- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position;
- (CTGradient *)removeColorStopAtIndex:(unsigned)index;
- (CTGradient *)removeColorStopAtPosition:(float)position;
- (CTGradientBlendingMode)blendingMode;
- (NSColor *)colorStopAtIndex:(unsigned)index;
- (NSColor *)colorAtPosition:(float)position;

// Now we don't need to conform to the NSCopying and NSCoding protocols

- (id)copyWithZone:(NSZone *)zone;
- (void)encodeWithCoder:(NSCoder *)coder;
- (id)initWithCoder:(NSCoder *)coder;

// We only need to fill a simple NSRect

- (void)drawSwatchInRect:(NSRect)rect;
- (void)radialFillRect:(NSRect)rect;
- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle;
- (void)radialFillBezierPath:(NSBezierPath *)path;

// Remove the entire (Private) category;
// but leave -addElement:

- (void)_commonInit;
// move setBlendingMode code into init
- (void)setBlendingMode:(CTGradientBlendingMode)mode;
- (CTGradientElement *)elementAtIndex:(unsigned)index;
- (CTGradientElement)removeElementAtIndex:(unsigned)index;
- (CTGradientElement)removeElementAtPosition:(float)position;
</code></pre>

<p>The following C functions are now unused:</p>

<pre><code>static void chromaticEvaluation(void *info, const float *in, float *out);
static void inverseChromaticEvaluation(void *info, const float *in, float *out);
static void transformRGB_HSV(float *components);
static void transformHSV_RGB(float *components);
static void resolveHSV(float *color1, float *color2);
</code></pre>

<p>And also remove the following from the header:</p>

<pre><code>typedef enum  _CTBlendingMode
    {
    CTLinearBlendingMode,
    CTChromaticBlendingMode,
    CTInverseChromaticBlendingMode
    } CTGradientBlendingMode;
</code></pre>

<p>Remove the <code>&lt;protocols&gt;</code> and unnecessary instance variables so the interface looks like this:</p>

<pre><code>@interface CTGradient : NSObject {
    CTGradientElement* elementList;
    CGFunctionRef gradientFunction;
}
+ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end;
- (void)fillRect:(NSRect)rect angle:(float)angle;
- (void)addElement:(CTGradientElement *)newElement;
@end
</code></pre>

<p>Before we go further, we&#8217;ll need to take a detour and fix the remaining code so that it compiles. That&#8217;s easy enough - just remove any references to code we&#8217;ve removed.</p>

<p>If you&#8217;ve been following along, your CTGradient should now look like <a href="http://pub.lipidity.com/CTGradient/CTGradient2.m">this</a> (also made it readable!).</p>

<p>In a few short minutes, we&#8217;re down from more than <strong>1300</strong> to a little over <strong>200 lines</strong>.</p>

<p><big>It gets better.</big></p>

<h2>Optimize it</h2>

<p>First stop, <code>fillRect:angle:</code>. Since the gradient for Trimmit&#8217;s window only runs vertically (angle 90), we can straight away take out the <var>angle</var> argument, and also cut the entire if / else structure with the angle down to the two lines that are under the <code>if(angle == 90)</code>. Ah, much better.</p>

<p>Now things start getting a teeny bit more complex - and fun.</p>

<hr />

<p>The CTGradient code is written so that one can have many different color stops. The CTGradientElement struct has a nextElement which points to the next CTGradientElement and the position of the current element is stored under the float <var>position</var>. However, we need just two - the starting and ending shades.</p>

<p>Let&#8217;s take a look at cutting the multiple elements down to just two.</p>

<p>The <code>addElement:</code> method has a lot of looping to insert the element at the correct place. However, since we know that we only ever need two, we can cut it down to this:</p>

<pre><code>- (void)addElement:(CTGradientElement *)newElement {
    if(elementList) {
        // elementList exists, add second element
        elementList-&gt;nextElement = malloc(sizeof(CTGradientElement));
        *(elementList-&gt;nextElement) = *newElement;
        elementList-&gt;nextElement-&gt;nextElement = 0;
    } else {
        // no elements - add first element
        elementList = malloc(sizeof(CTGradientElement));
        *elementList = *newElement;
        elementList-&gt;nextElement = 0;
    }
}
</code></pre>

<p>Similarly with <code>dealloc</code>,</p>

<pre><code>- (void)dealloc {
    CGFunctionRelease(gradientFunction);
    free(elementList-&gt;nextElement);
    free(elementList);
    [super dealloc];
}
</code></pre>

<p>While we&#8217;re at it, let&#8217;s clean up <code>init</code> as well:</p>

<pre><code>- (id)init {
    if(self = [super init]) {
        CGFunctionCallbacks evaluationCallbackInfo = {0 , &amp;linearEvaluation, 0};
        static const float input_value_range[2] = { 0, 1 };
        static const float output_value_ranges[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
        gradientFunction = CGFunctionCreate(&amp;elementList, 1, input_value_range, 4, output_value_ranges, &amp;evaluationCallbackInfo);
    }
    return self;
}
</code></pre>

<p>Now we head to the <code>linearEvaluation</code> function. <var>info</var> passed in refers to <var>elementList</var> (see the CGFunctionCreate call). Again, since we know that we&#8217;ll only ever have two elements we can reduce it to:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    float position = *in;

    CTGradientElement *color1 = *(CTGradientElement **)info;
    CTGradientElement *color2 = color1-&gt;nextElement;

    out[0] = (color2-&gt;red - color1-&gt;red)*position + color1-&gt;red; 
    out[1] = (color2-&gt;green - color1-&gt;green)*position + color1-&gt;green;
    out[2] = (color2-&gt;blue - color1-&gt;blue)*position + color1-&gt;blue;
    out[3] = (color2-&gt;alpha - color1-&gt;alpha)*position + color1-&gt;alpha;
}
</code></pre>

<p>And now we can finally remove the lines from <code>+gradientWithBeginningColor:endingColor:</code> where the positions are set, and also remove the <var>position</var> float from the CTGradientElement struct.</p>

<hr />

<p>We&#8217;re now down to a little over <strong>100 lines</strong>. Your CTGradientElement.m should now be looking something like <a href="http://pub.lipidity.com/CTGradient/CTGradient3.m">this</a>. Going well, but let&#8217;s take things up a notch.</p>

<hr />

<p>For Trimmit&#8217;s background, we&#8217;re not interested in the red, green, blue and alpha components - we just need a grayscale shading. This is the most fun part.</p>

<p>Instead of <code>float red, green, blue, alpha;</code> in the CTGradientElement struct, we can have just <code>float shade;</code>.</p>

<p>Now, during <code>init</code> we have:</p>

<pre><code>CGFunctionCallbacks evaluationCallbackInfo = {0 , &amp;linearEvaluation, 0};
static const float input_value_range[2] = { 0, 1 };
static const float output_value_ranges[8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
gradientFunction = CGFunctionCreate(&amp;elementList, 1, input_value_range, 4, output_value_ranges, &amp;evaluationCallbackInfo);
</code></pre>

<p>The third argument, <var>input_value_range</var> is the domain. This gets passed into linearEvalution through <var>*in</var>.This is the independent variable. When we&#8217;re drawing the gradient, linearEvaluation is called with <var>*in</var> (the domain) starting at the first value of <var>input_value_range</var> and ending up at the second. So, something like 0.000, 0.001, 0.002 &#8230; 0.998, 0.999, 1.000. The function&#8217;s job is to set the the color components for each given value of the domain.</p>

<p>The first optimization we can make here is to return only one channel instead of four. This changes it to:</p>

<pre><code>- (id)init {
    if (self = [super init]) {
        CGFunctionCallbacks evaluationCallbackInfo = {0, &amp;linearEvaluation, 0};
        static const float range[2] = {0, 1};
        static const float domain[2] = {0, 1};
        gradientFunction = CGFunctionCreate(&amp;elementList, 1, domain, 1, range, &amp;evaluationCallbackInfo);
    }
    return self;
}
</code></pre>

<p>This change impacts back on the linearEvaluation function - now it only needs to return one channel:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    CTGradientElement *color1 = *(CTGradientElement **)info;
    out[0] = (color1-&gt;nextElement-&gt;shade - color1-&gt;shade)*(*in) + color1-&gt;shade;
}
</code></pre>

<p>And don&#8217;t forget <code>+gradientWithBeginningColor:endingColor:</code>. We improve performance here, as we only need the shade - not a color. We can rename it to something appropriate.</p>

<pre><code>+ (id)gradientWithBeginningShade:(float)begin endingShade:(float)end {
    id newInstance = [[[self class] alloc] init];
    CTGradientElement color1, color2;

    color1.shade = begin; color2.shade = end;

    [newInstance addElement:&amp;color1];
    [newInstance addElement:&amp;color2];
    return [newInstance autorelease];
}
</code></pre>

<p>Now we&#8217;re only returning one grayscale channel. But hold on, we&#8217;re still in the RGB colorspace! That&#8217;s easily fixed. In <code>fillRect:</code>, replace:</p>

<pre><code>#if MAC_OS_X_VERSION_MAX_ALLOWED &gt;= MAC_OS_X_VERSION_10_4
    CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
#else
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
#endif
</code></pre>

<p>with:</p>

<pre><code>CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
</code></pre>

<p class="note">CGColorSpaceCreateDeviceGray is device-dependent on Mac OS X 10.3 and below, but starting from Tiger, it&#8217;s now device-independent which is good in terms of appearance.</p>

<p>While we&#8217;re in <code>fillRect:</code>, we can also make a few more improvements:</p>

<pre><code>- (void)fillRect:(NSRect)rect {
    CGContextRef currentContext = [[NSGraphicsContext currentContext] graphicsPort];
    CGContextSaveGState(currentContext);
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
    CGContextClipToRect(currentContext, *(CGRect *)&amp;rect);
    CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, CGPointMake(0, 0), CGPointMake(0, NSMaxY(rect)), gradientFunction, 0, 0);
    CGContextDrawShading(currentContext, myCGShading);

    CGShadingRelease(myCGShading);
    CGColorSpaceRelease(colorspace);
    CGContextRestoreGState(currentContext);
}
</code></pre>

<p>I moved the startPoint and endPoint CGPoints straight into the CGShadingCreateAxial call. Also, we only ever fill the whole view with the gradient, so just put in <code>0</code> instead of using NSMinX and NSMinY. We could replace the <code>CGPointMake(0, 0)</code> with <code>(CGPoint){0,0}</code>, but I&#8217;m not sure if that&#8217;d be noticeably faster.</p>

<hr />

<p>Our code is at a lean <strong>80 lines</strong>. Now for the biggest change yet.</p>

<hr />

<p>Heading back to dear old linearEvaluation. Look at this line:</p>

<pre><code>out[0] = (color1-&gt;nextElement-&gt;shade - color1-&gt;shade)*(*in) + color1-&gt;shade;
</code></pre>

<p>What&#8217;s happening here? Think about it like this:</p>

<p>Essentially, we&#8217;re just &quot;graphing&quot; a straight line:</p>

<pre><code>y = mx + c
</code></pre>

<p>where <var>x</var> is the position, <var>*in</var>, and <var>y</var> is the shade we return (<var>out[0]</var>), and <var>c</var> is the initial shade.</p>

<p>Since our <var>x</var> value only goes from 0 to 1 (see domain in CGFunctionCreate), at <code>x = 0</code>, we will have <code>y = c</code>. At <code>x = 1</code>, we&#8217;ll have <code>y = m + c</code>.</p>

<p><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/gradient-graph.png' alt='Graph of Gradient Function' class='feature' /></p>

<p><strong>So</strong> we only need the start shade as our <var>*info</var> value, since we can have <var>m</var> as the difference between the final and initial shades. Let&#8217;s effect this in our code.</p>

<p>We can get rid of the CTGradientElement struct. Remove from the header:</p>

<pre><code>typedef struct _CTGradientElement {
    float shade;
    struct _CTGradientElement *nextElement;
} CTGradientElement;
</code></pre>

<p>Instead of the struct, we&#8217;ll use a single <var>shade</var> instance variable. We don&#8217;t need the <code>+gradientWithBeginningShade:endingShade:</code> anymore, nor do we need <code>-addElement:</code>. So our interface now looks like this:</p>

<pre><code>@interface CTGradient : NSObject {
    float shade;
    CGFunctionRef gradientFunction;
}
- (void)fillRect:(NSRect)rect;
@end
</code></pre>

<p>From the implementation, we can remove the <code>free</code> calls in <code>dealloc</code> as well as the whole of <code>+gradientWithBeginningShade:endingShade:</code> and <code>addElement:</code>. We can set the <code>shade</code> in the init method (and replace <code>&amp;elementList</code> with <code>&amp;shade</code>), and change the linearEvaluation function to:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    out[0] = *(float*)info + (*in)*0.1;
}
</code></pre>

<p>The <code>0.1</code> is what determines how much brighter the top of the gradient is (it&#8217;s the <var>m</var> value of our straight line) compared to the bottom.</p>

<p><strong>But</strong> since the <code>(*in)*0.1</code> will go from <code>0</code> to <code>0.1</code> (as <var>*in</var> goes from 0 to 1), we can actually change the <code>domain</code> we declare for CGFunctionCreate to {0, 0.1} and have our function as:</p>

<pre><code>void linearEvaluation (void *info, const float *in, float *out) {
    out[0] = *(float*)info + *in;
}
</code></pre>

<p>You can now add accessors for <code>shade</code> if you need to change the shade of the gradient or you could even have an <code>initWithShade:</code> method. In fact, here&#8217;s an Xcode project with the gradient.</p>

<p class="download"><a href="http://pub.lipidity.com/CTGradient/GradientSample.tar.bz2">Lean Gradient Xcode Project</a></p>

<p>We now have our gradient code down <big>from <strong>1300 lines</strong> to just over <strong>30 lines</strong></big>.</p>

<hr />

<p>We&#8217;ve almost reached a similar level of optimization to Trimmit&#8217;s gradient. There will be further optimizations you can make, but it depends on what you&#8217;re using the gradient for. For example, if the gradient you&#8217;re drawing is always the same, you can optimize further by removing the <code>shade</code> instance variable and all references to it, and hardcode the value into the linearEvaluation function.</p>

<p>Getting to the end of this article, you&#8217;re probably thinking that it would be a bit of a waste to use CTGradient if by the time you optimize it for your application, it doesn&#8217;t resemble the original at all. And you&#8217;re quite right. The documentation already shows you <a href="http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_shadings/chapter_9_section_6.html">how to draw gradients</a>, yet the number of applications using CTGradient - the whole 1300 lines of it - is astonishing.</p>

<p>Please: When you use other people&#8217;s code, don&#8217;t put it in without a thought. Go through it, understand it, and optimize it for your specific need. For the better performance and reduced RAM usage, computers will thank you.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/ctgradient-code-bloat/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Eliminate Bloatware</title>
		<link>http://lipidity.com/apple/eliminate-bloatware/</link>
		<comments>http://lipidity.com/apple/eliminate-bloatware/#comments</comments>
		<pubDate>Fri, 02 Nov 2007 02:00:45 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Application]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Download]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Review]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://lipidity.com/apple/eliminate-bloatware/</guid>
		<description><![CDATA[The Vikings left their trash on the ground - Mac developers are stuffing theirs into their apps.]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve heard the excuses and listened to the complaints; the amount of junk shipped in third-party applications just doesn&#8217;t seem to change. If anything, it&#8217;s getting worse. Just because users may not realise how much bloat they&#8217;re getting along with the app, doesn&#8217;t change the fact that it&#8217;s bloat. Unnecessary junk.</p>

<p>Finder info, .DS_Store files, resource forks, debug symbols&#8230; They do nothing for the user short of wasting disk space. For the developer, extra bandwidth costs. Do people not realise this is a problem, or are they just too lazy to act? Either way, the solution is simple.</p>

<p>You can clean apps (sometimes to less than half their original size) with <a href="http://lipidity.com/apple/cleaning-apps-mac-os-x/">some basic commands</a> in the Terminal. But this takes time, and what&#8217;s more, Mac users in general have an aversion to the command line. Enter <a href="http://lipidity.com/trimmit/">Trimmit</a>.</p>

<p class='centre'><a href='http://lipidity.com/trimmit/' title='Trimmit'><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/flow2.jpg' alt='[Trimmit]' title="Trimming Flow.app" /></a></p>

<blockquote><p>Trimmit is a svelte utility which takes the pain out of keeping your applications as tight as possible. I use it on Flow each time I seed a build, and I seriously doubt the process could any easier.</p> <cite><a href="http://extendmac.com/" title="ExtendMac Flow">Brian Amerige</a></cite></blockquote>

<p>Trimmit uses superfast UNIX APIs, Apple&#8217;s own developer tools, and the rock-solid Cocoa framework to automate your cleaning process. Just drop your built app onto it, maybe configure a few settings, and you&#8217;re away. <big>It doesn&#8217;t get any easier - or faster.</big></p>

<p>Other applications claim to save disk space by stripping universal binaries, or removing languages. <big>Trimmit does all of that - and more.</big> But it works. Look at this screenshot from Xslimmer regarding Xcode:</p>

<p class='centre'><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/xcode-xslim.jpg' alt='Xslimmer on Xcode' title='Shows only 2 architectures' /></p>

<p>Xslimmer naively believes that only two architectures can exist in any application, but the truth is revealed by Trimmit:</p>

<p class='centre'><img src='http://lipidity.com/wordpress/wp-content/uploads/2007/11/xcodetrim-2.png' alt='i386, ppc, ppc64, x86_64' title='Architectures in Xcode' /></p>

<p><br />Because Trimmit interacts with the shell (<a href="http://zsh.sourceforge.net/FAQ/zshfaq01.html#l3">zsh</a>, in fact), no similar application can even come close to it&#8217;s level of power, accuracy and performance.</p>

<p><big><a href="http://rixstep.com/1/20071101,01.shtml">The results</a> speak for themselves.</big></p>

<p>Use Trimmit to recover disk space that third-party applications so rudely waste.</p>

<p class='centre' style='margin:2em 0'><big><a href="http://lipidity.com/trimmit/">Go get Trimmit.</a></big></p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/eliminate-bloatware/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Javascript does Cocoa too</title>
		<link>http://lipidity.com/apple/javascript-cocoa-webkit/</link>
		<comments>http://lipidity.com/apple/javascript-cocoa-webkit/#comments</comments>
		<pubDate>Sat, 28 Jul 2007 08:07:49 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[javascript]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Tutorial]]></category>

		<category><![CDATA[Web]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/dev/cocoa/javascript-cocoa-webkit</guid>
		<description><![CDATA[Running Objective-C code from Javascript

Java, Python and Ruby can access Cocoa APIs and Objective-C classes. What about the super-extensible yet severely under-appreciated Javascript? With the WebKit framework, you can access Objective-C from any script present in a HTML document.



A `WebScriptObject` is an Objective-C wrapper passed to your application from the scripting environment. You can&#8217;t create [...]]]></description>
			<content:encoded><![CDATA[<p><big>Running Objective-C code from Javascript</big></p>

<p>Java, Python and Ruby can access Cocoa APIs and Objective-C classes. What about the super-extensible yet severely under-appreciated Javascript? With the WebKit framework, you can access Objective-C from any script present in a HTML document.</p>

<!--more-->

<blockquote><p>A `WebScriptObject` is an Objective-C wrapper passed to your application from the scripting environment. You can&#8217;t create an instance directly, but you need to get a `WebScriptObject` by sending `webScriptObject` to a WebView.</p><cite>Paraphrase of <a href="http://developer.apple.com/documentation/Cocoa/Reference/WebKit/Classes/WebScriptObject_Class/Reference/Reference.html#//apple_ref/doc/c_ref/WebScriptObject" title="WebScriptObject Class Reference" target="_blank">WebScriptObject Overview</a></cite></blockquote>

<p>This class, <code>WebScriptObject</code>, lets you &#8220;talk&#8221; and &#8220;listen&#8221; to Javascript (or any other code allowable inside <acronym title="Hyper Text Markup Language">HTML</acronym> <code>&lt;script&gt;</code> tags). That is, you can run Javascript code or functions in the WebView, but you can also let the Javascript run Objective-C methods.</p>

<p>This is the same way Dashboard widgets interact with Cocoa plugins. You know how you access <code>window.widget</code> in your Javascript? That&#8217;s an Objective-C class you&#8217;re referencing. The WebKit framework provides all the necessary classes and functionality for this interaction, making this seemingly difficult task ridiculously easy.</p>

<h4>Exposing an Objective-C class to the scripting environment</h4>

<p>Exposing a class to the scripting environment is very easy. You can use key-value coding methods (for example, <code>setValue:forKey:</code> and <code>valueForKey:</code>) to get and set the properties of a <code>WebScriptObject</code>. These will be accessible as children of the window object in Javascript. Use the <code>removeWebScriptKey:</code> method to remove a scripting object property.</p>

<p>As an example, let&#8217;s say we have a class <code>NSFoo</code>. We&#8217;ve linked to the WebKit framework and <code>webView</code> is a <code>WebView</code>.</p>

<p>We use the delegate method <code>webView:windowScriptObjectAvailable:</code> to ensure that the <code>WebScriptObject</code> is available before we start using it. Then we expose an instance of <code>NSFoo</code> to the scripting environment:</p>

<pre><code>- (void)webView:(WebView *)sender windowScriptObjectAvailable: (WebScriptObject *)windowScriptObject {
    NSFoo *myFoo = [[NSFoo alloc] init];

    scriptObject = windowScriptObject;
    [scriptObject setValue:myFoo forKey:@"foo"];
}
</code></pre>

<p>Now you can access <code>foo</code> through javascript;</p>

<pre><code>&lt;script language='text/javascript'&gt;
var myFoo = foo;
&lt;/script&gt;
</code></pre>

<h4>Allow Javascript access</h4>

<p>For obvious security reasons, no methods or KVC keys are exposed to the Javascript environment by default. This means at this stage while <code>window.foo</code> exists, you can&#8217;t do much with it. To allow Javascript to send messages or get / set properties, the following two class methods need to be utilized:</p>

<pre><code>+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;
</code></pre>

<p>So for example, if we want to allow Javascript to send a <code>bar:</code> message to a class, all we have to do is add the following class method to the implementation:</p>

<pre><code>+ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector {
    if ( selector == @selector(bar:) ) {
        return NO;
    }
    return YES;
}
</code></pre>

<p>Now running <code>foo.bar_(0)</code> in Javascript will be equivalent to running <code>[myFoo bar:0]</code> in Objective-C. Why the underscore? Selectors accessed from Javascript are renamed using the following rules:</p>

<ul>
<li>Any colon (&quot;:&quot;) in the Objective-C selector is replaced by an underscore (&quot;_&quot;).</li>
<li>Any underscore in the Objective-C selector is prefixed with a dollar sign (&quot;$&quot;).</li>
<li>Any dollar sign in the Objective-C selector is prefixed with another dollar sign.</li>
</ul>

<p>So a method called <code>doAction:(id)action withThing:(id)thing</code> will be turned into <code>doAction_withThing_(action,thing)</code>, while <code>do$This:</code> will become <code>do$$This_</code>. This is often pretty ugly and inconvenient. Fortunately, the Javascript name for the selectors can be changed.</p>

<h4>Change Javascript selector names</h4>

<p>Using <code>+ (NSString *)webScriptNameForSelector:(SEL)sel</code>, we can change the name of the selector in Javascript:</p>

<pre><code>+ (NSString *)webScriptNameForSelector:(SEL)sel
{
    if (sel == @selector(bar:))
        return @"bar";
    return nil;
}
</code></pre>

<p>Now we can run <code>foo.bar(0)</code> in Javascript to equal <code>[myFoo bar:0]</code>. Here are a few more examples:</p>

<pre><code>+ (NSString *)webScriptNameForSelector:(SEL)sel
{
    if (sel == @selector(performTransition))
        return @"performTransition";    // widget.performTransition()
    else if (sel == @selector(prepareForTransition:))
        return @"prepareForTransition"; // widget.prepareForTransition(arg)
    else if (sel == @selector(setPreference:forKey:))
        return @"setPreferenceForKey";  // widget.setPreferenceForKey(pref,key)
    else if (sel == @selector(preferenceForKey:))
        return @"preferenceForKey";     // widget.preferenceForKey(key)
    return nil;
}
</code></pre>

<p>If you want to expose all the instance methods of the class to the scripting environment, just return <code>NO</code> without doing any checks:</p>

<pre><code>+ (BOOL)isSelectorExcludedFromWebScript:(SEL)selector { return NO; }
</code></pre>

<p>Remember: selectors are converted into Javascript functions, so even if the method doesn&#8217;t need any arguments, you still need to add the parenthesis at the end for the Javascript; eg. <code>[object doMethod]</code> becomes <code>object.doMethod()</code>.</p>

<h4>Key-Value-Coding from Javascript</h4>

<p>Exposing KVC keys is a similar procedure. Here we&#8217;re giving access to the &#8220;name&#8221; property:</p>

<pre><code>+ (BOOL)isKeyExcludedFromWebScript:(const char *)property {
    if (strcmp(property, "name") == 0) {
        return NO;
    }
    return YES;
}
</code></pre>

<p>Now in Javascript, we can read and write <code>foo.name</code>. Reading <code>foo.name</code> will be equivalent to <code>[myFoo name]</code> and writing to it (<code>foo.name = "A big foo"</code>) will be the same as running <code>[myFoo setName:@"A big foo"]</code>.</p>

<p>As before, just <code>return NO</code> without any checks to allow all KVC keys to be accessed from the scripting environment.</p>

<h3>Summary</h3>

<ul>
<li>Load WebKit.framework which contains the classes <code>WebView</code> and <code>WebScriptObject</code>.</li>
<li>Send <code>setValue:forKey:</code> to a <code>WebScriptObject</code>; makes an object available to the scripting environment.</li>
<li>Implement <code>+isSelectorExcludedFromWebScript:</code> and <code>+isKeyExcludedFromWebScript:</code> and return NO to allow the scripting environment to run a method or access a key.</li>
<li>Use <code>webScriptNameForSelector:</code> / <code>webScriptNameForKey:</code> to specify the names of your methods / keys as seen by Javascript.</li>
</ul>

<h3>Further reading</h3>

<ul>
<li><a href="http://developer.apple.com/documentation/Cocoa/Reference/WebKit/ObjC_classic/index.html">WebKit Documentation</a></li>
<li><a href="http://developer.apple.com/documentation/Cocoa/Reference/WebKit/Protocols/WebScripting_Protocol/index.html#//apple_ref/doc/uid/TP40003837">WebScripting Documentation</a></li>
<li><a href="http://developer.apple.com/documentation/AppleApplications/Conceptual/SafariJSProgTopics/Tasks/ObjCFromJavaScript.html">Obj-C From JavaScript</a></li>
<li><a href="http://will.thimbleby.net/script/">TurtleScript</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/javascript-cocoa-webkit/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Cocoa Animation Effects</title>
		<link>http://lipidity.com/apple/cocoa-transition-animation-effects-video/</link>
		<comments>http://lipidity.com/apple/cocoa-transition-animation-effects-video/#comments</comments>
		<pubDate>Mon, 16 Jul 2007 06:15:19 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Animation]]></category>

		<category><![CDATA[core-image]]></category>

		<category><![CDATA[CoreGraphics]]></category>

		<category><![CDATA[GUI]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Screencast]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/apple/cocoa-transition-animation-effects-video</guid>
		<description><![CDATA[I wrote this app about 8 months ago, and it&#8217;s managed to find itself on Youtube.







It uses Core Graphics and Core Image APIs for the transition effects to animate the window / view, which probably needs a minimum of Mac OS X Tiger. The full source code is available.
]]></description>
			<content:encoded><![CDATA[<p>I wrote this app about 8 months ago, and it&#8217;s managed to find itself on Youtube.</p>

<!--more-->

<div class='centre'>
<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/FSUu_a_f88c&#038;rel=0"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/FSUu_a_f88c&#038;rel=0" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object>
</div>

<p>It uses Core Graphics and Core Image APIs for the transition effects to animate the window / view, which probably needs a minimum of Mac OS X Tiger. The <a href='http://dev.lipidity.com/apple/core-graphics-meet-core-image-demo-app' title='Core Graphics Image Transition Animation Effects'>full source code</a> is available.</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/cocoa-transition-animation-effects-video/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Exposing those private frameworks</title>
		<link>http://lipidity.com/apple/exposing-those-private-frameworks/</link>
		<comments>http://lipidity.com/apple/exposing-those-private-frameworks/#comments</comments>
		<pubDate>Fri, 16 Mar 2007 12:57:06 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[Mac]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/apple/exposing-those-private-frameworks</guid>
		<description><![CDATA[Don't we all just love <a href="http://dev.lipidity.com/feature/tutorial/ebook-xcode-animations-core-graphics">undocumented goodness</a>? Some people spend a lot of time exploring and experimenting with APIs and functions to see what they can accomplish. I get a fair amount of email of people asking how exactly these private, undocumented goodies are discovered. You only need two things. The appropriate tools, and <em>lots</em> of spare time.]]></description>
			<content:encoded><![CDATA[<h3>MagicHat</h3>

<p><a href="http://homepage.mac.com/petite_abeille/MagicHat/" title="MagicHat" rel="external"><img src="http://dev.lipidity.com/wp-content/uploads/2007/03/magichat.png" alt="MagicHat" height="100" width="100" /></a></p>

<blockquote><p><a href="http://homepage.mac.com/petite_abeille/MagicHat/">MagicHat</a> is a programmer&#8217;s research and reference tool. With MagicHat, you navigate through Cocoa&#8217;s application programming interface (API); review the declarations of language elements such as methods, functions, and constants; and retrieve relevant passages from the Cocoa developer documentation.  MagicHat helps you unravel unfamiliar code, whether building blocks from the Cocoa software kits, programming examples, or programs written by your own development team.</p></blockquote>

<p>MagicHat lets you look at the declarations of public, private and local (installed) frameworks. This is a good graphical way to look at the structure of a framework, but is limited to Objective-C; which shouldn&#8217;t matter much for general use.
<a href="http://homepage.mac.com/petite_abeille/MagicHat/" title="Go to MagicHat's homepage"><img src="http://dev.lipidity.com/wp-content/uploads/2007/03/smokehat.png" alt="MagicHat: observing Smoke.framework" /></a></p>

<p class="caption">Above: MagicHat in action, displaying Disco.app&#8217;s <a href="http://dev.lipidity.com/apple/passive-smokin">Smoke.framework</a></p>

<p><a href="http://www.codethecode.com/Projects/class-dump/">
</a></p>

<h3>Class-Dump</h3>

<p><a href="http://www.codethecode.com/Projects/class-dump/" rel="external">Class-Dump</a> is one of the easiest to use command line utilities to look at the Objective-C portions of Mach-O files. The command is immediately available after moving the downloaded file to /usr/bin/ (you may need to use <code>sudo mv</code>). Executing it involves a running a simple &#8220;class-dump nameoffile&#8221; command in Terminal and the output is a nicely formatted Objective-C header. Objective-C output is the feature that makes this tool unique. It is especially useful if you need header files for frameworks which don&#8217;t include them, but it also gives you an idea as to the classes and methods present in the framework.</p>

<h3>otool</h3>

<p><a href="http://www.hmug.org/man/1/otool.php" rel="external">otool</a> provides one of the most comprehensive feature-sets - its primary role being to display parts of object files or libraries. You&#8217;ll generally find that using otool gives you access to frameworks such as CoreGraphics, which don&#8217;t contain Objective-C classes. The <code>-v</code> command will give you verbose output; <code>-o</code> displays the Objective-C and <code>-t</code> the text sections.</p>

<h3>Take &#8216;em apart</h3>

<p>Using these tools, you are now equipped to examine in detail most frameworks and libraries. Be aware that searching though and discovering new functions takes a lot of experimentation and trial and error - which the primary reason I leave it to people like <a href="http://www.cocoadev.com/index.pl?WildWindows" rel="external">Wade Tregaskis</a> (who, like me, lives in Melbourne. Never met him, though).</p>

<p>Obligatory disclaimer: The private frameworks are probably private for a reason. Meddle with them at your own risk.</p>

<p>(For those adventurous souls, the Core Graphics framework is located at <samp>/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework</samp> but there are plenty of other private frameworks besides this one; Core Graphics just happens to have the <a href="http://dev.lipidity.com/feature/tutorial/xcode-transitions-core-graphics-image-2">window transition effects</a>.</p>

<p>Ok, did this help, and what did I miss?</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/exposing-those-private-frameworks/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Warp, bend, squeeze and transform windows with CGSSetWindowWarp</title>
		<link>http://lipidity.com/apple/warp-bend-squeeze-and-transform-windows-with-cgssetwindowwarp/</link>
		<comments>http://lipidity.com/apple/warp-bend-squeeze-and-transform-windows-with-cgssetwindowwarp/#comments</comments>
		<pubDate>Wed, 28 Feb 2007 08:40:38 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Animation]]></category>

		<category><![CDATA[CoreGraphics]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[Source Code]]></category>

		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/apple/warp-bend-squeeze-and-transform-windows-with-cgssetwindowwarp</guid>
		<description><![CDATA[How would you like to have the power to squish, warp and transform windows to your liking? With the private Core Graphics function CGSSetWindowWarp, you can!]]></description>
			<content:encoded><![CDATA[<p><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/Warped.png" style="width: 100%" title="Warped clocks" alt="Warped clocks" /></p>

<p>The function CGSSetWindowWarp is part of the private portion of <a href="http://dev.lipidity.com/feature/tutorial/ebook-xcode-animations-core-graphics" title="Core Graphics tutorial ebook">Core Graphics</a>. Although its just as obscure and undocumented as the rest of the <ins>private parts of the</ins> framework, there has been some discovery and progress into its <a href="http://kevin.sb.org/articles/2006/07/23/cgssetwindowwarp-explained" rel="external">usage</a>.  Erling Ellingsen has written (a while ago) about CGSSetWindowWarp on <a href="http://blog.medallia.com/2006/05/windowwarp.html" rel="external">his blog</a>, and he also shows some very interesting screenshots of what&#8217;s possible.</p>

<p><img src="http://blog.medallia.com/images/warp/full-3d-fancy-thumb.jpg" alt="transformed windows" /><img src="http://blog.medallia.com/images/warp/full-3d-slash-thumb.jpg" alt="transformed windows" /></p>

<p>So, how do you use this incredible function in your Cocoa (<a href="http://paste.lisp.org/display/2717">or Carbon</a>) application?
<!--more--></p>

<h2>CGSSetWindowWarp Usage</h2>

<h3>Including the header</h3>

<p>Whenever you use private Core Graphics functions, you generally need to <code>#import </code> either one of CoreGraphicsServices.h or CGSPrivate.h (available for download with my <a href="http://dev.lipidity.com/feature/tutorial/ebook-xcode-animations-core-graphics" title="Core Graphics tutorial ebook">Core Graphics ebook</a>). However, if you&#8217;re not interested in using the rest of the Core Graphics API, you can get away with simply adding the following to your main class&#8217; header file:</p>

<pre class="code"><code>
typedef struct CGPointWarp {
        CGPoint local;
        CGPoint global;
}CGPointWarp;

typedef int CGSConnectionID;
typedef int CGSWindowID;

extern CGSConnectionID _CGSDefaultConnection();

extern CGError CGSSetWindowWarp(CGSConnectionID, CGSWindowID, int w, int h, CGPointWarp mesh[w][h]);
extern OSStatus CGSGetScreenRectForWindow(CGSConnectionID cid, CGSWindowID wid, CGRect *outRect);
extern OSStatus CGSGetWindowBounds(CGSConnectionID cid, CGSWindowID wid, CGRect *bounds);</code></pre>

<p>As you&#8217;ve probably guessed, the function that actually applies the mesh transform is <code>CGSSetWindowWarp</code>. You can create a mesh in two different ways.</p>

<h3>Creating the mesh</h3>

<p>To create a mesh, we need to specify how many points we&#8217;re going to take for our grid length-wise and width-wise. Something like this:</p>

<pre class="code"><code>#define H 64
#define W 5</code></pre>

<p>We&#8217;ll also need to refer to the position and size of the frame so the following code in the method you&#8217;re writing this will give us access to the frame of the NSWindow:</p>

<pre class="code"><code>CGRect frame;
CGSGetWindowBounds(cid, [window windowNumber], &amp;frame);</code></pre>

<p>The use of CGSGetWindowBounds ensures we get the apparent frame rather than just the frame computed by our code as we&#8217;ll be applying various transformations.</p>

<p>The first way to create a mesh requires more labor (yes, its tiring to code) but gives you greater control over the mesh and the transformations applied to the NSWindow.</p>

<h4>Manual mesh</h4>

<pre class="code"><code>CGPointWarp meshes[H][W] =
{
{ {{0, 0},{10, 10}}, {{200,0},{180,12}} },
{ {{0, 50},{0,50}}, {{200,50},{200,50}} },
{ {{0,100},{0,100}}, {{200,100},{200,100}} },
{ {{0,150},{0,150}}, {{200,150},{200,150}} },
{ {{0,200},{0,200}}, {{200,200},{200,200}} },
};</code></pre>

<p>Here, you specify firstly the coordinates of the point which you&#8217;re taking, and where you want to move that point. The first line in the above code takes (0,0) and moves it to (10,10) and moves (200,0) to (180,12). However, the first set of coordinates are local coordinates, and the second set are global. (ie., the first set are on the plane of the window, while the second set use the screen as the plane. This is to allow the window to be stretched out beyond the scope of the current frame.) We could solve this issue by using:</p>

<pre class="code"><code>
int x = frame.origin.x;
int y = frame.origin.y;
CGPointWarp meshes2[H][W] =
{
{ {{0, 0},{x, y}}, {{200,0},{x+200,y}} },
{ {{0, 50},{x,y+50}}, {{200,50},{x+200,y+50}} },
{ {{0,100},{x,y+100}}, {{200,100},{x+200,y+100}} },
{ {{0,150},{x,y+150}}, {{200,150},{x+200,y+150}} },
{ {{0,200},{x,y+200}}, {{200,200},{x+200,y+200}} },
};</code></pre>

<p>The preceding code just warps the window of size 200&#215;200 to its current position so no change is observable. If we wanted to stretch out the middle of the window horizontally, we could use a mesh in the form:</p>

<pre class="code"><code>
int x = frame.origin.x;
int y = frame.origin.y;
CGPointWarp stretchThroughMiddleMesh[H][W] =
{
{ {{0, 0},{x, y}}, {{200,0},{x+200,y}} },
{ {{0, 50},{x-10,y+50}}, {{200,50},{x+200+10,y+50}} },
{ {{0,100},{x-20,y+100}}, {{200,100},{x+200+20,y+100}} },
{ {{0,150},{x-10,y+150}}, {{200,150},{x+200+10,y+150}} },
{ {{0,200},{x,y+200}}, {{200,200},{x+200+10,y+200}} },
};</code></pre>

<p>We&#8217;ve taken the side where x=0 and moved x further back along the middle, and done the opposite on the opposite side (where x=200). This results in the NSWindow being warped to have a bulge in the middle.
<a href="http://dev.lipidity.com/wp-content/uploads/2007/02/warpbulge.png" title="warp bulge" rel="lightbox"><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warpbulge.thumbnail.png" alt="warp bulge" /></a></p>

<p>Again, this would only look like the screenshot on a window with a height and width of 200. For a window that&#8217;s a different size, you&#8217;ll need to change the values to match the height and width of the window. For the y-values in the mesh (x, x+50, x+100, etc. in the different rows), the numbers are determined by how many points you&#8217;ve taken (your value for <var>H</var>) and the width of the window. The general formula you would use would be <code>x + ( frame.size.height / (W-1) )</code>. You&#8217;ll see this more clearly in the second method.</p>

<h4>Mesh through iterative formulae</h4>

<p>The second method to create a mesh is to run a loop to configure your individual points. Here&#8217;s a basic example that creates a mesh that would be identical to the current state of the window:</p>

<pre class="code"><code>
int h;
int w;
CGPointWarp meshes[H][W];
for (h = 0; h &lt; H; h++) {
    for (w = 0; w &lt; W; w++) {
        CGPointWarp point;
        point.local.x = w * (frame.size.width / (W - 1));
        point.local.y = h * (frame.size.height / (H - 1));
        point.global.x = point.local.x + frame.origin.x;
        point.global.y = point.local.y + frame.origin.y;

        meshes[h][w] = point;
    }
}</code></pre>

<p>Obviously we want to transform the window somehow. We need to apply some sort of mathematical function to the points to make them deviate from their original positions or coordinates. (This is the part where you start to wish you&#8217;d paid more attention in math class).</p>

<p>Lets say we want to skew the window using the x-axis. Each successive x-value needs to be greater than the previous one. Since we&#8217;re looping through each point, at any time in the inner for loop, the values of the current point are (w,h). (In <a href="http://www.google.com/search?q=Cartesian+coordinates">Cartesian coordinates</a> the x value is first, followed by the y value) In order to increase each successive value for x, we need to add something. But, we need to add more than what we did the previous time. The easiest way to do this without adding any new variables is to use the value that we&#8217;re incrementing each time - the x value itself. So, for a skewed window, we have the following mesh:</p>

<pre class="code"><code>
int h;
int w;
CGPointWarp meshes[H][W];
for (h = 0; h &lt; H; h++) {
    for (w = 0; w &lt; W; w++) {
        CGPointWarp point;
        point.local.x = w * (frame.size.width / (W - 1));
        point.local.y = h * (frame.size.height / (H - 1));
        point.global.x = point.local.x + frame.origin.x + <span style="color: black">15*h</span>;
        point.global.y = point.local.y + frame.origin.y;

        meshes[h][w] = point;
    }
}</code></pre>

<p>The skewed window would like this when the mesh was applied:
<a href="http://dev.lipidity.com/wp-content/uploads/2007/02/skew.png" title="Skews CGSWarp" rel="lightbox"><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/skew.thumbnail.png" alt="Skews CGSWarp" /></a></p>

<h3>Applying the mesh</h3>

<p>The transform mesh was really the hard part. Applying the transformation mesh (or grid) to a window is as easy as:</p>

<pre class="code"><code>
CGSSetWindowWarp(_CGSDefaultConnection(), [window windowNumber], W, H, meshes);</code></pre>

<p>The possibilities of this are really amazing. I&#8217;ve always wanted to write an NSWindow subclass that bent and reduced in opacity as it was dragged, imitating <a href="http://static.flickr.com/72/221355329_a7dc022e74.jpg" rel="external">XGL</a>. With CGSSetWindowWarp and Core Graphics, I&#8217;m sure it&#8217;ll be a breeze. Or what about an virtual universe-style Finder cross Desktop, sort of like <a href="http://www.acm.uiuc.edu/macwarriors/projects/oldprojects/3dosx/screenshots.html" rel="external">3DOSX</a>? You could even imitate other effects with this, such as the cube, but still get live updating in your windows - a limitation present in the Core Graphics transition effects.</p>

<p>For a source code, and a further explanation, see Kevin Ballard&#8217;s post on the subject of <a href="http://kevin.sb.org/articles/2006/07/23/cgssetwindowwarp-explained" rel="external">CGSSetWindowWarp</a>.</p>

<p><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/right.png" title="Warped clocks" alt="Warped using CGSSetWindowWarp" /><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/squish.png" title="Warped clocks" alt="Warped clocks" /><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/left.png" title="Warped clocks" alt="Warped clocks" /></p>

<p><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/control.png" title="Warped clocks" alt="Warped clocks" /><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/warps/blur.png" title="Warped clocks" alt="Warped clocks" /></p>

<p>Did you find this useful? Or do you have any other method that you use for eye candy?</p>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/warp-bend-squeeze-and-transform-windows-with-cgssetwindowwarp/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Writing Quicksilver Plugins: Actions</title>
		<link>http://lipidity.com/apple/anatomy-of-a-plugin-part-2/</link>
		<comments>http://lipidity.com/apple/anatomy-of-a-plugin-part-2/#comments</comments>
		<pubDate>Wed, 21 Feb 2007 03:35:40 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Quicksilver]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[plugin]]></category>

		<category><![CDATA[Tutorial]]></category>

		<category><![CDATA[Xcode]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/feature/tutorial/anatomy-of-a-plugin-part-2</guid>
		<description><![CDATA[Writing an action

Previously

We set up Xcode for Quicksilver plugin development, created a Quicksilver plugin project and got familiar with the QSPlugIn portion of the Info.plist file.

We&#8217;ve introduced ourselves to Quicksilver, and told it some fundamental information about the plugin we&#8217;re making in the QSPlugIn section of the Info.plist file associated with the Quicksilver project.

In this [...]]]></description>
			<content:encoded><![CDATA[<h2>Writing an action</h2>

<h3>Previously</h3>

<p>We <a href="http://lipidity.com/apple/quicksilver-plugins-in-objective-c" title="Quicksilver plugins in Cocoa and Xcode">set up Xcode</a> for Quicksilver plugin development, created a Quicksilver plugin project and got familiar with the QSPlugIn portion of the Info.plist file.</p>

<p>We&#8217;ve <a href="http://lipidity.com/apple/anatomy-of-a-plugin-infoplist-part-1" title="The initial set up of the Quicksilver Xcode project.">introduced ourselves to Quicksilver</a>, and told it some fundamental information about the plugin we&#8217;re making in the QSPlugIn section of the Info.plist file associated with the Quicksilver project.</p>

<p>In this tutorial, we look at the procedures involved in specifying and writing an action for Quicksilver.</p>

<!--more-->

<h3>QSActions</h3>

<h4>Communicating our intentions</h4>

<p>Writing an action for Quicksilver couldn&#8217;t be easier. In fact, there are only two steps in this &#8220;How to&#8221; guide. Firstly, it&#8217;s off to the all-important Info.plist; then we&#8217;ll finally begin writing our code.</p>

<p>You wont find a <var>QSActions</var> key in the Info.plist file. It&#8217;s named to <var>QSActionsTemplate</var> by default. This is to stop Quicksilver parsing the default data which is automatically added by creating a new project. If we&#8217;re writing an action, though, we&#8217;ll definitely want to make use of it.</p>

<p>Find the <var>QSActionsTemplate</var> key in the Info.plist file. It&#8217;ll look similar to the following, with the instances of &#8220;test&#8221; replaced with whatever you named your project:</p>

<pre class="code"><code>
    &lt;key&gt;QSActionsTemplate&lt;/key&gt;
    &lt;dict&gt;
        &lt;key&gt;testAction&lt;/key&gt;
        &lt;dict&gt;
            &lt;key&gt;actionClass&lt;/key&gt;
            &lt;string&gt;testAction&lt;/string&gt;
            &lt;key&gt;actionSelector&lt;/key&gt;
            &lt;string&gt;performActionOnObject:&lt;/string&gt;
            &lt;key&gt;directTypes&lt;/key&gt;
            &lt;array&gt;
                &lt;string&gt;testType&lt;/string&gt;
            &lt;/array&gt;
            &lt;key&gt;name&lt;/key&gt;
            &lt;string&gt;Action: test&lt;/string&gt;
            &lt;key&gt;validatesObjects&lt;/key&gt;
            &lt;false/&gt;
        &lt;/dict&gt;
    &lt;/dict&gt;</code></pre>

<p><em>Make sure to rename the <code>QSActionsTemplate</code> key to <code>QSActions</code></em>. Configure the QSActions key as follows (remember &#8220;test&#8221; from now on is replaced with your project name):</p>

<dl> <dt>testAction</dt> <dd>Rename the <var>testAction</var> key to whatever you want to call your action. This is only internal - the user wont see this, but think of something that&#8217;ll tell you what this action does when you&#8217;re looking at it. We&#8217;ll call this the real name of the action. Descriptive names are especially important when working with multiple actions in a single plugin.</dd> <dt>actionClass</dt> <dd>You have the testAction class already created for you (in the testAction.h and testAction.m files). Double check that they exist and are identical to the value under <var>actionClass</var>.</dd> <dt>actionSelector</dt> <dd>If you&#8217;re going to have multiple actions or multiple methods, decide now what you&#8217;re going to call your method for this particular action and stick the value under the <var>actionSelector</var> key. If you&#8217;re only planning on having one action, the default <code>performActionOnObject:</code> should be fine. (If you want to have an indirect object passed to you as well, you&#8217;ll need a method in the form <code>performActionOnObject:withIndirectType:</code>. More on this later.)</dd> <dt>alternateAction</dt> <dd>If you wish to specify an alternate action, make sure you actually have another action in your plugin. Simply add a string with the name of the action to this key. Not the value for the <var>name</var> key, but the real name of the action.</dd> <dt>directTypes</dt> <dd>Add the types of inputs you want to be working with under the <var>directTypes</var> key. It&#8217;s an array of strings that can take values such as <var>NSFilenamesPboardType</var>, <var>NSURLPboardType</var>, <var>NSStringPboardType</var> or <var>Apple URL pasteboard type</var>. A list of allowed types includes:
<ul>
    <li>*</li>
    <li>Apple URL pasteboard type</li>
    <li>NSFilenamesPboardType</li>
    <li>NSStringPboardType</li>
    <li>qs.action</li>
    <li>qs.catalogentry</li>
    <li>qs.command</li>
    <li>qs.process</li>
</ul>
</dd> <dt>directFileTypes</dt> <dd>If you&#8217;re using <var>NSFilenamesPboardType</var> as a <var>directType</var> and you only want files of a specific type, specify <var>directFileTypes</var> as an array containing strings with the extensions you want to allow (eg. <code>png</code>, <code>pdf</code>, <code>app</code>, etc.)</dd> <dt>indirectTypes</dt> <dd>Similar to <var>directTypes</var>, but only applies if you actually want an indirect object passed to your plugin.</dd> <dt>indirectOptional</dt> <dd>A boolean value specifying whether the selection of an indirect is required for the action to be called.</dd> <dt>name</dt> <dd>The name that the user will see when using your action from Quicksilver.</dd> <dt>description</dt> <dd>An (optional) brief description of what the action does.</dd> <dt>enabled</dt> <dd>A boolean value determining whether your plugin is enabled by default.</dd> <dt>icon</dt> <dd>An icon for your action. Can be in the form com.developer.app.</dd> <dt>precedence</dt> <dd>If you&#8217;d like your plugin to have more importance than other actions, enter an integer here. Normal settings are from 0 to 4. The higher the number, the more importance is given to your action. This is especially useful if your plugin deals with a source that no other plugin deals with. In that instance, you&#8217;d want your action to have higher authority than the standard &#8216;Open&#8217; and &#8216;Run&#8217; actions for that particular source.</dd> <dt>reverseArguments</dt> <dd>If set to true, Quicksilver will reverse the direct and indirect selectors before passing you the data. This comes in handy if you want to provide two actions that are the reverse of each other so you only need to write one method. For example, <code>[Text] Find with... [Search Engine]</code> is the reversed version of <code>[Search Engine] Search For... [Text]</code>. They both use the same selector, but the first one gets the direct and indirect objects swapped around.</dd> <dt>displaysResult</dt> <dd>If true, Quicksilver will take the value you return in your method, and set it as the direct object.</dd> <dt>runInMainThread</dt> <dd>Whether the action needs to be forced to run in the main thread. Recommended for timers or actions that calculate a delay.</dd> <dt>hidden</dt> <dd>Boolean specifying whether the action is visible (hence directly usable) by the user. This field is generally best left out unless you&#8217;re writing an internal action.</dd> <dt>disabled</dt> <dd>Whether the action is disabled by default when the plugin is activated. Boolean.</dd> <dt>validatesObjects</dt> <dd>True or false. True if your plugin validates the objects passed to it, false otherwise.</dd> </dl>

<h3>Now for some Cocoa</h3>

<h4>Writing the code</h4>

<p>Yes, after having our head buried in the Info.plist, we finally get to write some code.</p>

<p>The class we declared as our <var>actionClass</var> in the Info.plist is the one that needs to house the method for the action.</p>

<p>Open up testAction.m (you still need to keep replacing &#8216;test&#8217; with your project name). The file currently looks something like:</p>

<pre class="code"><code>
//
//  testAction.m
//  test
//
//  Created by Ankur Kothari on 15/02/07.
//  Copyright Vacuous Virtuoso 2007. All rights reserved.
//

#import "testAction.h"

@implementation testAction

- (QSObject *)performActionOnObject:(QSObject *)dObject{
    return nil;
}
@end</code></pre>

<p>All you have to do, is take the QSObject which is passed to you in the form of the pointer <var>dObject</var>, do something with it, and return something! In fact, you don&#8217;t necessarily even have to return anything. This is the part of the tutorial that you put on your Cocoa hat and let your imagination, creativity and logic run wild. Write that amazing action!</p>

<p>If you don&#8217;t like to mess around until you&#8217;ve figured out how to do something, here&#8217;s an example of a Quicksilver action that takes a string object, then returns the same string with &#8221; hello&#8221; appended to it. (Note that if you want to build the following code as a trial or test, you&#8217;ll need to configure the Info.plist using the information provided above.)</p>

<pre class="code"><code>
//
//  testAction.m
//  test
//
//  Created by Ankur Kothari on 15/02/07.
//  Copyright Vacuous Virtuoso 2007. All rights reserved.
//

#import "testAction.h"

@implementation testAction

- (QSObject *)performActionOnObject:(QSObject *)dObject{
    QSObject *result;
    NSString *dWithHello;

    dWithHello = [NSString stringWithFormat:@"%@ hello",[dObject stringValue]];
    result = [QSObject objectWithString:dWithHello];

    return result;
}
@end</code></pre>

<h4>Build and run</h4>

<p>Double-click on the built plugin (test.qsplugin) to have Quicksilver install it. Passing a string such as &#8220;is this plugin working?&#8221; should get an output of &#8220;<samp>is this plugin working? hello</samp>&#8220;. Try passing a file or folder. The string you&#8217;ll get will be the name of the file / folder with &#8221; hello&#8221; appended to it.</p>

<p>Take careful note of the following:</p>

<ul>
    <li>The string is extracted from <var>dObject</var> using the <code>stringValue</code> method.</li>
    <li>An object of any type is extracted from <var>dObject</var> using &#8230; methods.</li>
    <li>An instance of a QSObject is created using the method objectWithType: where Type is the type of object you wish to add to the QSObject. Eg. <code>[QSObject objectWithString:@"A string"];</code></li>
    <li>If the method returns a non-null value, it must be as a QSObject.</li>
</ul>

<p>That&#8217;s it! You are now fully qualified to develop Quicksilver actions using Cocoa, in your familiar Xcode development environment.</p>

<p>If you&#8217;re very adventurous, look through the source code for other plugins you can find, and browse through the Info.plist files for some of the plugins you have (they&#8217;ll be a bundle with the extension of .qsplugin).</p>

<p>Have fun, and stay tuned for further tutorials on Quicksilver plugin development.</p>

<h3>Also in this series</h3>

<h6><a href="http://lipidity.com/apple/anatomy-of-a-plugin-infoplist-part-1">Info.plist - QSPlugIn</a></h6>

<blockquote><p>A introduction to the Info.plist and our first encounter with the QSPlugIn key. Teaches the initial steps required to ensure communication between Quicksilver and your plugin occurs correctly.</p></blockquote>

<h6><a href="http://lipidity.com/apple/quicksilver-plugins-in-objective-c" title="Step by step guide to writing plugins for Quicksilver in Cocoa using Xcode.">Quicksilver plugins in Objective-C</a></h6>

<blockquote><p>A guide to obtaining the required resources for Quicksilver plugin development in Xcode. Also runs through setting up Xcode to make creating plugins easier.</p></blockquote>

<h6><a href="http://lipidity.com/apple/quicksilver-interface-tutorial" title="How to write a Quicksilver interface using Xcode and Cocoa.">Quicksilver Interface Creation</a></h6>

<blockquote><p>A thorough guide on the techniques to create a working Quicksilver interface. Includes an Xcode project template to minimize the effort required.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/anatomy-of-a-plugin-part-2/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Anatomy of a plugin - Info.plist Part 1</title>
		<link>http://lipidity.com/apple/anatomy-of-a-plugin-infoplist-part-1/</link>
		<comments>http://lipidity.com/apple/anatomy-of-a-plugin-infoplist-part-1/#comments</comments>
		<pubDate>Fri, 16 Feb 2007 09:05:36 +0000</pubDate>
		<dc:creator>Ankur</dc:creator>
		
		<category><![CDATA[Apple]]></category>

		<category><![CDATA[Cocoa]]></category>

		<category><![CDATA[Programming]]></category>

		<category><![CDATA[Quicksilver]]></category>

		<category><![CDATA[Mac]]></category>

		<category><![CDATA[plugin]]></category>

		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">http://dev.lipidity.com/feature/tutorial/anatomy-of-a-plugin-infoplist-part-1</guid>
		<description><![CDATA[Previously

We discussed the basic setup required to begin writing Quicksilver plugins in Quicksilver plugins in Objective-C. Anatomy of a plugin is a multi-section guide which explains the various sections of Info.plist, the different components of a plugin and the basic groundwork required in order to write a usable plugin.

To go through this guide, you need [...]]]></description>
			<content:encoded><![CDATA[<h2>Previously</h2>

<p>We discussed the basic setup required to begin writing Quicksilver plugins in <a href="http://lipidity.com/apple/quicksilver-plugins-in-objective-c">Quicksilver plugins in Objective-C</a>. <q>Anatomy of a plugin</q> is a multi-section guide which explains the various sections of Info.plist, the different components of a plugin and the basic groundwork required in order to write a usable plugin.</p>

<p><strong>To go through this guide, you need to have followed the steps in <a href="http://lipidity.com/apple/quicksilver-plugins-in-objective-c">Quicksilver plugins in Objective-C</a>.</strong> Once you&#8217;ve got Xcode set up properly for Quicksilver development, create a new Quicksilver plugin project in Xcode, and get ready to start planning your plugin.
<!--more--></p>

<h2>Understanding Quicksilver&#8217;s plugin system</h2>

<h3>Info.plist</h3>

<p><img src="http://dev.lipidity.com/wp-content/uploads/2007/02/5_info_plist.png" alt="Info.plist" style="float: right" />If you&#8217;ve worked with Cocoa or <dfn title="A Macintosh programming language loosely based on C">Objective-C</dfn>, you may be familiar with the Info.plist file. As with any standard Cocoa application, the Info.plist file is an XML file specifying the various settings or options for the project in question. Quicksilver plugins use the Info.plist file to also communicate their features, actions and information to Quicksilver. It is in the Info.plist file that you need to begin editing, and realistically, you may well need to keep updating it as you add, remove, or delete features.</p>

<h3>QSPlugIn</h3>

<h4>Tell me about you</h4>

<p>The first thing you should probably fix up in the Info.plist file is the details that are unlikely to change. Find <var>QSPlugIn</var>, and take a look at it&#8217;s <dfn title="The keys inside the QSPlugin.">children</dfn>. At the moment, it probably looks something like:</p>

<pre class="code">  &lt;key&gt;QSPlugIn&lt;/key&gt;
 &lt;dict&gt;
    &lt;key&gt;author&lt;/key&gt;
    &lt;string&gt;&lt;/string&gt;
    &lt;key&gt;categories&lt;/key&gt;
    &lt;array/&gt;
    &lt;key&gt;description&lt;/key&gt;
    &lt;string&gt;&lt;/string&gt;
    &lt;key&gt;icon&lt;/key&gt;
    &lt;string&gt;&lt;/string&gt;
    &lt;key&gt;qsversion&lt;/key&gt;
    &lt;string&gt;29CC&lt;/string&gt;
    &lt;key&gt;relatedBundles &lt;/key&gt;
    &lt;array/&gt;
 &lt;/dict&gt;</pre>

<p>The <var>QSPlugIn</var> section needs to be filled out accurately to inform potential users of its function. Lets go through the various keys that can be written inside <var>QSPlugIn</var>.</p>

<dl> <dt>Author</dt> <dd>The <var>author</var> is obviously you, so fill in your name.
<pre class="code">&lt;key&gt;author&lt;/key&gt;
&lt;string&gt;Ankur Kothari&lt;/string&gt;</pre>
</dd> <dt>Categories</dt> <dd><var>Categories</var> is an array of strings corresponding to the appropriate categories as found in Quicksilver&#8217;s plugin preferences. You can have zero or more of the following:
<ul>
    <li>Applications</li>
    <li>Calendar</li>
    <li>Contacts</li>
    <li>Development</li>
    <li>Files</li>
    <li>Images</li>
    <li>Interfaces</li>
    <li>Mail &amp; Chat</li>
    <li>Miscellaneous</li>
    <li>Music</li>
    <li>Quicksilver</li>
    <li>Search</li>
    <li>System</li>
    <li>Text</li>
    <li>Web</li>
</ul>
If you leave out the categories, then your plugin will still be under the &#8220;All plugins&#8221; section of Quicksilver, but wont show up under any category specific sections.
For a plugin integrating MAMP support, the categories could be
<pre class="code">&lt;key&gt;categories&lt;/key&gt;
&lt;array&gt;
 &lt;string&gt;Development&lt;/string&gt;
 &lt;string&gt;Applications&lt;/string&gt;
 &lt;string&gt;Web&lt;/string&gt;
&lt;/array&gt;</pre>
<strong>Note:</strong> If you use the <var>Mail &amp; Chat</var> category, make sure to escape the ampersand. It should be written as <var>Mail &amp; Chat</var>

</dd><dt>Description</dt> <dd>The <var>description</var> is the text that is displayed in the info panel under the plugin preferences (unless an extendedDescription is set). Make sure you include the usage and function of your plugin in enough detail so potential users are aware of what it does. An example:
<pre class="code">&lt;key&gt;description&lt;/key&gt;
&lt;string&gt;Actions to start / stop the MAMP servers. Requires MAMP.app&lt;/string&gt;</pre>
</dd> <dt>Extended Description</dt> <dd>An <var>extendedDescription</var> is, as you would expect, a lengthier and more in-depth explanation of the plugin&#8217;s function. HTML is allowed in the extended description, but most characters must be encoded (For example, &#8216;&lt;&#8217; becomes &#8216;&lt;&#8217; and &#8216;&gt;&#8217; becomes &#8216;&gt;&#8217;). If an extended description is set, it is shown in the info panel under Quicksilver&#8217;s plugin preferences. This field is optional. An example of an extended description (taken from the Apple Mail plugin):
<pre class="code">&lt;key&gt;extendedDescription&lt;/key&gt;
&lt;string&gt;Enables email actions for &lt;a href="http://www.apple.com/macosx/features/mail/"&gt;Apple Mail&lt;/a&gt;.
Allows browsing mailboxes as children of Mail (beta)&lt;/string&gt;</pre>
</dd> <dt>Icon</dt> <dd>The <var>icon</var> can be in the form <code>com.developer.app</code> (such as <code>com.apple.Mail</code> or <code>com.apple.iTunes</code>) or &lt;speculation&gt; you can write the name of an image that you&#8217;ve imported into your Xcode project &lt;/speculation&gt;. This field is not required. If left blank, Quicksilver will assign your plugin an icon. The icon for the Mail plugin reads:
<pre class="code">&lt;key&gt;icon&lt;/key&gt;
&lt;string&gt;com.apple.Mail&lt;/string&gt;</pre>
</dd> <dt>Hidden</dt> <dd>A boolean value specifying whether the user can see this plugin in the plugin list or not. Generally only used by internal Quicksilver plugins. The Core Support plugin (bundled in Quicksilver.app/Contents/PlugIns/) has hidden set as:
<pre class="code">&lt;key&gt;hidden&lt;/key&gt;
&lt;true/&gt;</pre>
There is also a boolean key called <var>secret</var>, but it&#8217;s obviously, well, secret.

</dd><dt>Related Bundles</dt> <dd><var>Related bundles</var> refers to resources or packages that are similar to or related to this plugin. The Mail plugin&#8217;s related bundle key reads:
<pre classs="code">&lt;key&gt;relatedBundles&lt;/key&gt;
&lt;array&gt;
    &lt;string&gt;com.apple.Mail&lt;/string&gt;
&lt;/array&gt;</pre>
</dd> <dt>Required Bundles</dt> <dd><var>requiresBundle</var> is optional and if set, doesn&#8217;t allow the plugin to be used unless the specified package is currently also installed. A human-readable version of this value will automatically be shown under the heading &#8220;prerequisites&#8221; in the plugin info panel. The key is in the form <code>com.developer.app</code> and may look something like
<pre classs="code">&lt;key&gt;requiresBundle&lt;/key&gt;
&lt;array&gt;
    &lt;string&gt;com.flyingmeat.VoodooPad&lt;/string&gt;
&lt;/array&gt;</pre>
</dd> <dt>webIcon</dt> <dd><var>webIcon</var> is again optional, and may be set to the URL of an image. For example, the Cyberduck module sets the <var>webIcon</var> to a png using the following code:
<pre classs="code">&lt;key&gt;webIcon&lt;/key&gt;
    &lt;string&gt;http://quicksilver.blacktree.com/public/ytrewq1/Cyberduck.png&lt;/string&gt;</pre>
</dd> <dt>Info File</dt> <dd>If you&#8217;d like a readme file associated with your plugin, you can create a simple text or rich text file (.txt or .rtf) and import it into your Xcode project. Then it&#8217;s a simple matter of specifying this as file as the <var>infoFile</var>. An example:
<pre class="code">&lt;key&gt;infoFile&lt;/key&gt;
&lt;string&gt;Info.rtf&lt;/string&gt;</pre>
Currently, I don&#8217;t think the <var>infoFile</var> is actually accessible through Quicksilver&#8217;s interface, but it seems likely that the &#8220;?&#8221; button in the plugin info panel would be used to display it in future versions.

</dd></dl>

<p>Overwhelmed? Don&#8217;t be. There&#8217;s really no need to specify <em>all</em> of the above keys in the QSPlugIns section of the Info.plist. Author, categories, and description are the basic necessities, and anything beyond that is a bonus.</p>

<p>So if you haven&#8217;t already done so, decide on what your Incredible Plugin™ is going to do, and fill out the QSPlugIn portion of the Info.plist. Then take a break and relax while you wait for the next section in this series!</p>

<h3>Also in this series</h3>

<h6><a href="http://lipidity.com/apple/anatomy-of-a-plugin-part-2" title="How to write a Quicksilver action using Xcode and Cocoa.">Quicksilver Actions</a></h6>

<blockquote><p>A quick look at QSActions in the Info.plist, followed by our first look at the code needed to get a plugin up and running.</p></blockquote>

<h6><a href="http://lipidity.com/apple/quicksilver-plugins-in-objective-c">Quicksilver plugins in Objective-C</a></h6>

<blockquote><p>A guide to obtaining the required resources for Quicksilver plugin development in Xcode. Also runs through setting up Xcode to make creating plugins easier.</p></blockquote>

<h6><a href="http://lipidity.com/apple/quicksilver-interface-tutorial" title="How to write a Quicksilver interface using Xcode and Cocoa.">Quicksilver Interface Creation</a></h6>

<blockquote><p>A thorough guide on the techniques to create a working Quicksilver interface. Includes an Xcode project template to minimize the effort required.</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://lipidity.com/apple/anatomy-of-a-plugin-infoplist-part-1/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
