• Script Debugger 4.0.8

    I’m pleased to announce the release of Script Debugger 4.0.8. This free maintenance release provides Leopard (Mac OS X 10.5) compatibility and addresses a series of issues that came to light following the release of Script Debugger 4.0. Please review the Release Notes for details of changes in this release.

    Download

    Release Notes

    Script Debugger 4 Page

  • Sigh – MPAA Bans This Poster

    I saw this post and it just makes you shake your head. Which of these two images is more frightening, and which one gets banned?

    censored.jpg

    I think the “TAXI To The Dark Side” poster is a wonderful example of a good design.

  • Script Debugger 4.0.8b5

    Here is a new beta build of Script Debugger 4.0.8. This build addresses all of the Leopard (Mac OS X 10.5) issues that I’m aware of. Please give it a try and let me know if there are any regressions. I intend to go final with this build on December 15th, so please get those bug reports to me ASAP.

    You can download the Beta software here:

    Universal build requires Mac OS X 10.4 or later (13.1MB)

    If you are running 10.3 (Panther), you need to use this version instead (10.1MB)

    Changes in 4.0.8b5:

    13966 - Addressed a problem that prevented the customization of script window toolbars when running on Leopard.

    13973 - Addressed a series of problems that caused NSLock and NSConditionLock related diagnostic messages to appear in the Console log on Leopard systems.

    13972 - Revised the descriptions of AppleScript’s built-in data types appearing in Dictionary windows.

    13978 - Addressed a problem that caused Script Debugger to fail when saving scripts as bundled applications on Leopard.

    13731 - The Parent Script toolbar item’s popup menu now properly updates as other script documents are opened and closed.

    13734 - The AppleScript Studio entry in the Applications inspector now shows the same icon as the AppleScript Studio entry in the Default Target submenu within the Script menu.


    Changes in 4.0.8b3, Changes in 4.0.8b2

  • OmniInspector with Resizable Tabs

    In my recent OmniInspector Sample Code blog entry I present a sample application illustrating how to use the OmniInspector framework.

    One limitation of the OmniInspector framework is that tabbed inspector windows cannot be resized. If you want a resizable inspector, you have to use a “standard” inspector.

    Most of the inspectors in my applications need to be resizable, so I hacked the OmniInspector sources to allow resizable inspectors in tabbed OmniInspector windows (you have to love Open Source code). Here are my modifications in case you need this capability in your application:

    OmniInspector with Resizable Tabs 164Kb

    All of my modifications to the sources are preceded by a //MALL comment.

  • Script Debugger 4.0.8b4

    I am preparing yet another Script Debugger 4 maintenance release. This release addresses some issues that have surfaced since the 4.0.7 release. This build extends the expiry date in the build to December 1, 2007 to give me some time to address a few more issues.

    You can download the Beta software here:

    Universal build requires Mac OS X 10.4 or later (13.1MB)

    If you are running 10.3 (Panther), you need to use this version instead (10.1MB)


    Changes in 4.0.8b3, Changes in 4.0.8b2

  • QuickTime Skimmer View

    Download LNSQTSkimmerView

    After receiving my copy of iLife ‘08 I began playing with iMovie ‘08. Despite the controversy surrounding iMovie 08 vs iMovie HD, I find the skimming UI Apple has introduced quite good.

    I have an internal project that could use this style of movie selection and previewing so I decided to create my own implementation using Cocoa and QTKit. Here’s what I came up with:

    lnsqtskimmer.jpg (Click Image To Play Demo Movie)

    I’ve been able to implement these features:

    1. skimming
    2. selection
    3. playback
    4. dragging
    5. thumbnail scaling
    6. timeline scaling

    IMPORTANT: this code only seems to perform well with movies encoded using the Apple Intermediate codec. LNSQTSkimmerView relies on QTMovie’s frameImageAtTime: method to display thumbnail frames. This method can become very slow for movies encoded in other formats (i.e. H.264).

    iMovie ‘08 uses the Apple Intermediate codec and also pre-generates thumbnails for its skimming view. Pre-generating thumbnails may allow my code to operate well with any QuickTime movie.

    ####Missing Bits

    There are a couple of things in iMovie 08’s skimming view that I’ve not yet implemented:

    1. embossed frames around thumbnails.
    2. audio scrubbing as you skim over a movie
    3. displaying multiple movie clips on one skimmer view

    ####Acknowledgments

    This code uses the NSBezierPath+PXRoundedRectangleAdditions Objective-C category from Andy Matuschak’s Pixen project.

    ####LICENSE

    MIT License for LNSQTSkimmerView Copyright (c) 2007 Mark Alldritt All Rights Reserved

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

  • Displaying Lists - Part 2

    In my first Displaying Lists post, I described how to use an HTML view to display a list of values formatted using a CSS stylesheet. This time I’ll use an XSL stylesheet to convert the XML data into HTML for display.

    The advantage of using XSL over CSS is that you can restructure the incoming XML data in any way you like. For instance, with CSS, you are stuck with the order of XML elements, but with XSL you can re-order them. Additionally, the XSL stylesheet can incorporate additional HTML markup, such as column headings.

    Here’s a fairly simple XSL stylesheet that converts the XML into an HTML table, complete with headings.

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="[www.w3.org/1999/XSL/...](http://www.w3.org/1999/XSL/Transform)" version="1.0">
      <xsl:output method="html"/>
    
      <xsl:template match="doc">
    		<html>
    		<head>
    			<title><xsl:value-of select="@name"/></title>
    			<link rel="stylesheet" type="text/css2" href="foo.css"/>
    		</head>
    		<body>
    		  <table width="100%" border="0" cellpadding="0" cellspacing="0">
    			<tr>
    			  <td>
    				<table width="100%" border="0" cellspacing="1" cellpadding="0">
    				  <tr>
    					<td height="15" width="140" nowrap="yes">
    					  <table width="100%" height="15" border="0" cellpadding="0" cellspacing="0">
    						<tr>
    						  <td class="tablehead">Name</td>
    						</tr>
    					  </table>
    					</td>
    					<td height="15" width="140" nowrap="yes">
    					  <table width="100%" height="15" border="0" cellpadding="0" cellspacing="0">
    						<tr>
    						  <td class="tablehead">Modification Date</td>
    						</tr>
    					  </table>
    					</td>
    					<td height="15" nowrap="yes">
    					  <table width="100%" height="15" border="0" cellpadding="0" cellspacing="0">
    						<tr>
    						  <td class="tablehead">Path</td>
    						</tr>
    					  </table>
    					</td>
    				  </tr>
    
    				  <xsl:apply-templates/>
    
    				</table>
    			  </td>
    			</tr>
    		  </table>
    		</body>
    		</html>
    
      </xsl:template>
    
      <xsl:template match="row">
    
    		<xsl:variable name="rowStyle">
    			<xsl:choose>
    				<xsl:when test='(position() mod 2) = 1'>oddRow</xsl:when>
    				<xsl:otherwise>evenRow</xsl:otherwise>
    			</xsl:choose>
    		</xsl:variable>
    
    	    <tr valign="top"><xsl:attribute name='class'><xsl:value-of select='$rowStyle'/></xsl:attribute>
    		  <td height="14" width="140" class="tabletext">
    			<xsl:value-of select="./name"/>
    		  </td>
    		  <td height="14" width="140" class="tabletext">
    			<xsl:value-of select="./date"/>
    		  </td>
    		  <td height="14" width="100%" class="tabletext">
    			<xsl:value-of select="./path"/>
    		  </td>
    	    </tr>
      </xsl:template>
    </xsl:stylesheet>
    

    And here’s the AppleScript code that generates the XML data, and displays it in an HTML view. Its very similar to the CSS example. The key difference is the inclusion of the <?xml-stylesheet type=“text/xsl” href=“foo.xsl”?> directive in the XML data which directs the HTML view to apply the foo.xsl stylesheet to the data before displaying it.

    on didInvokeListFinderFiles(theObject)
    	local rsrcsFolder, theNames, theURLs, theDates, theXML
    
    	set rsrcsFolder to POSIX path of (get my application's resources folder)
    
    	try
    		--	Generate XML listing the files in the top Finder window
    		tell application "Finder" to ¬
    			set {theNames, theURLs, theDates} to {name, URL, modification date} of items of first window
    
    		set theXML to {"<?xml version="1.0"?>"}
    		set end of theXML to "<?xml-stylesheet type="text/xsl" href="foo.xsl"?>" -- associated a XSL stylesheet with this XML document
    		set end of theXML to "<doc>"
    		repeat with i from 1 to length of theNames
    			set end of theXML to "  <row>"
    			set end of theXML to "    <name>" & (item i of theNames) & "</name>"
    			set end of theXML to "    <path>" & (item i of theURLs) & "</path>"
    			set end of theXML to "    <date>" & (item i of theDates) & "</date>"
    			set end of theXML to "  </row>"
    		end repeat
    		set end of theXML to "</doc>"
    
    		--	Convert the list of strings we have accumulated into one long string that we can display
    		set AppleScript's text item delimiters to {return}
    		set theXML to theXML as string
    
    		--	A this point we end up with an XML document looking something like this:
    		--
    		--	<?xml version="1.0"?>
    		--	<?xml-stylesheet type="text/xsl" href="foo.xsl"?>
    		--	<doc>
    		--	  <row>
    		--	    <name>AbstractConnection.h</name>
    		--	    <path>file://localhost/Users/mall/Desktop/connection/trunk/AbstractConnection.h</path>
    		--	    <date>Friday, July 13, 2007 4:59:01 PM</date>
    		--	  </row>
    		--	  <row>
    		--	    <name>AbstractConnection.m</name>
    		--	    <path>file://localhost/Users/mall/Desktop/connection/trunk/AbstractConnection.m</path>
    		--	    <date>Friday, July 13, 2007 4:59:01 PM</date>
    		--	  </row>
    		--	  ...
    		--	</doc>
    
    		--	Display the XML in an HTML view using an XSL stylesheet to make it look like a nice pretty table.
    		tell my htmlView
    			set value MIME type to "text/xml" -- makes the HTML view treat the string as XML rather then HTML
    			set value base URL to (path to resource "foo.xsl") -- tells the HTML view where the resources are (stylesheets, CSS & gifs)
    			set value to theXML -- apply the XSL stylesheet and display the resulting HTML
    		end tell
    	on error errMsg
    		display alert "No Finder Window" message "Please open a Finder window to list." buttons "OK" over my window
    	end try
    end didInvokeListFinderFiles
    

    When you put all this together in FaceSpan, here’s what it looks like:

    xsl.jpg

    Display XML (XSL) Example Project

  • Cocoa Scripting: Properties with Multiple Data Types

    The SDEF (Scripting Definition) XML dictionary format allows you to define properties that have multiple data types. This allows applications to support the as parameter to the AppleScript Get command. However, Cocoa Scripting does not fully support the notion of properties with multiple data types. This article describes how to implement multiple data type support using Cocoa Scripting.

    ####Why Do This?

    Before I get into the code, let me briefly describe why you might want to do this in your application. Simply put, the value of certain properties may be expressed in difference data types. For example, you might have a selection property. Lets say that by default, the selection property returns the selected text in your application. However, you may also want to allow the user to ask for a reference to the location of the selected text:

    get selection --> "Hello World"
    get selection as string --> "Hello World"
    get selection as reference --> word 2 thru 3 of document 1
    

    When the scripter tries to alter the selection, they may do it in two ways:

    set selection to "Goodbye" --> Replace the selected text with "Goodbye"
    set selection to word 1 of document 1 --> select the first word
    

    CAUTION: please use multiple data types correctly. The value returned for each data type should represent the same value, but in different ways. Correct use of multiple types avoids the need to have multiple versions of a given property in your dictionary, each returning the same value expressed in a different way.

    ####The SDEF

    Here’s a snippet from an SDEF defining a selection property that supports two data types: text and specifier.

      
    	
    	
    	
      
    

    ####Supporting Set Operations (Easy)

    Cocoa Scripting facilitates Set operations for properties with multiple types. It will convert the incoming AppleEvent data to an instance of NSString for a text value or an instance of NSScriptObjectSpecifier if the value is an object reference (specifier). Here’s how you might implement the setAESelection: accessor:

    - (void) setAESelection:(id) value
    {
      if ([value isKindOfClass:[NSString class]])
      {
    	// replace the selected text with the incoming value
      }
      else if ([value isKindOfClass:[NSScriptObjectSpecifier class]])
      {
      	// change the selection to the range of text specified in the
      	// incoming object specifier
      }
    }
    

    ####Supporting Get-As Operations (A Little Harder)

    Here is where Cocoa Scripting leaves you to your own devices. The problem is that the desired data type is not passed to the accessor function. You have to get this from the current AppleEvent yourself. I use this function:

    DescType FSRequestedTypeForCurrentEvent()
    {
      NSAppleEventDescriptor* event = [[NSAppleEventManager sharedAppleEventManager] currentAppleEvent];
      NSAppleEventDescriptor* requestedType = [event descriptorForKeyword:keyAERequestedType];
    
      if (requestedType)
    	return [requestedType typeCodeValue];
      else
    	return typeBest;
    }
    

    Then, the Accessor can be written like this:

    - (id) AESelection
    {
    	switch (FSRequestedTypeForCurrentEvent())
    	{
    	case typeBest:	// the default if no 'as' parameter specified
    	case typeText:
    	case typeUnicodeText:
    		// return an NSString instance containing the selected string
    		break;
    
    	case typeObjectSpecifier:
    		// return an NSScriptObjectSpecifier instance describing the
    		// location of the selection
    		break;
    
    	default: // unsupported type
    		[[NSScriptCommand currentCommand] setScriptErrorNumber: errAEWrongDataType];
    		return nil;
    	}
    }
    

    NOTE: I generally have AppleEvent specific accessors for use with Cocoa Scripting that in turn use my bindings compatible accessors. This is because Cocoa Scripting needs to support things like multiple data types, and reports errors in a different way.

  • SourceListView

    UPDATE: I have released an updated version of this sample that takes advantage of improvements Apple made in Leopard (Mac OS X 10.5).

    Download SourceListView

    I’ve been looking for a SourceList outline view for FaceSpan that I can use under Mac OS X 10.4. A bunch of Googling revealed parts of the puzzle, but nothing that pulled all the pieces together. So I decided to produce my own SourceList view using the pieces I found.

    I used iPhoto ‘08, iTunes 7, Numbers ‘08 and Mail as models. Here are the elements of the SourceList views in these applications that seem to be different from the stock NSTableView/NSOutlineView:

    1. Light blue background color
    2. Darker blue or blue gradient selection background
    3. The selection background color does not honor the system’s selection color preference
    4. Source Groups are not selectable
    5. Source Groups are drawn All Caps, Bold, Gray with a white shadow (appear embossed into the background)
    6. Source Group rows are a little taller than the others
    7. When inactive, the selection background turns gray, and the text switches from white to black
    8. The disclosure triangles are smaller than the ones provided by NSOutlineView (I’ve still not figured out how to accomplish this).

    Here’s what I came up with:

    Gradient Selection background (thank’s to Matt Gemmell’s iTableView):

    itunessourcelist.jpg

    Flat Selection background:

    numberssourcelist.jpg

    Matt Gemmell’s license seems reasonable, so I’m releasing the SourceList code under his license.

  • Coming from FaceSpan 3

    A few messages on the FaceSpan 5 Alpha mailing list have described the difficulties FaceSpan 3 users face when trying to get up and running with FaceSpan 5.

    I’m going to use this blog post to collect a summary of the differences between FaceSpan 5 and FaceSpan 3. This list will no doubt grow over time as we learn more about how best to program FaceSpan 5.

    So here we go:

    1. The names of many properties have changed. When the FaceSpan 5 dictionary settles down, I’ll prepare a table that maps the old FaceSpan 3 names to the new FaceSpan 5 names. Until then, you’ll have to use the FaceSpanKit dictionary to find the new names.

    2. You can safely ignore FaceSpan 5’s delegates when starting out with FaceSpan 5. FaceSpan 3 message dispatching is identical to FaceSpan 5’s default way of dispatching events (i.e. upwards the containment hierarchy: view -> window -> application).

    3. FaceSpan 3 allowed you to reference any view from any other view. This allowed you to say things like text view “myViewName” in any other view’s script and FaceSpan would search the window for you.

    In FaceSpan 5, you have to explicitly reference the window using my window’s text view “myViewName” (or more directly: my window’s myViewName).

    1. There are no storage items. However, you can simulate storage items using Bag objects. Just create Bag instances at the application level in the IDE. You can then use the Bag’s persistent data property to store information that should be retained between application runs.

    …more later

  • iPhone Price Drop

    I find the reaction to the $200 drop in the price of the Apple iPhone fascinating. Lots has been written on the subject, so I won’t rehash the details.

    I think there are two key issues that made this such a big deal:

    1. The press is itching to find bad iPhone news to report, especially when they were criticized for giving Apple so much free press leading up to the iPhone’s release. It all sounds like a tempest in a teapot.

    2. The simple price drop is very much unlike Apple’s MO. Apple normally discounts its products by removing lower end products, and adding higher end/speed bumped products. The originally high end product simply becomes the low end product with a lower price.

    In this case, Apple simply dropped the low-end iPhone and dropped the price. Apple did this, rather than introducing a 16GB iPhone at the same price as the 8GB iPhone, in an attempt to change the public’s perception of the iPhone as an expensive product.

    So I guess the old saw in product pricing that “you can always lower your price, but you can never raise it” isn’t always true.

  • Affrus 1.0.6 Released

    Affrus 1.0.6 has just been released. This free maintenance release addresses a problem where Perl crashes when scripts finish executing on Intel Macintoshes.

    You can download the software here.

  • LAMEncoder, Spawn of FaceSpan!

    FaceSpan 5 is under development. That means it isn't finished. But it also means that to some degree — indeed, to a rather impressive degree — it is finished. It isn’t all there, but there is certainly something there. And that something is remarkably viable. To illustrate this, I took advantage of FaceSpan 5 to do the very thing FaceSpan 5 is intended to do — to write a complete, useful, “shippable” application. In fact, I’ve actually “shipped” this application, which is called LAMEncoder, and you can download it (here) and try it out.

    The very fact that I was able to do this should serve as an indication of just how rich and how powerful FaceSpan 5 is already, even in its current state.

    Glass half empty, glass half full

    There are actually two main areas in which FaceSpan 5 isn’t finished yet:

    • The IDE. By this I mean the environment in which you work as you develop your application. For example, you can drag a button into a window to tell FaceSpan that there should be a button at this location in this window. But, so far in FaceSpan’s growth, no guidelines appear to show you when the button is at the correct position (a certain number of pixels from the edge of the window, or aligned with other widgets in the window), and you can’t yet double-click on the button to change its title (you have to work in FaceSpan’s “inspector” palettes). Also, when you’re debugging, certain powerful Script Debugger features, such as breakpoints, aren’t yet implemented in FaceSpan.

    • The “widget” repertoire. Certain standard interface elements that lots of people would like to use just aren’t there yet, or are present only in rudimentary form.

    Despite these current limitations, the actual process of developing LAMEncoder was wonderfully fast and easy. In fact, I’d have to say (not without a certain amount of prejudice, to be sure) that FaceSpan 5 is already shaping up to be the best application development environment I’ve ever used. The IDE has enough functionality, and we have enough interface widgets in place, that we’ve passed a certain tipping point of usability and usefulness.

    Indeed, I can specify for you quite precisely what tipping point it is. Very early on, when FaceSpan 5 was just a gleam in Mark’s eye, he posed me a challenge: At some point, he hoped, there would come a day when I would tell him that I’d rather develop using FaceSpan than any other development environment. That day is here. Not since HyperCard has an IDE felt so satisfying and easy to me. In a future article, I’d like to write more about that, using the organization and development of the LAMEncoder code to illustrate.

    In this article, though, I’ll just describe the finished product, LAMEncoder itself, particularly with regard to its interface. This (and using LAMEncoder itself, of course) should give you an idea of just how powerful FaceSpan is, even now.

    The LAME game

    So. What does LAMEncoder do? It encodes WAV and AIFF files to MP3 using the freeware LAME encoding engine. I prefer LAME to iTunes’ MP3 encoding engine. But LAME is a command-line tool, and I can never remember the commands. LAMEncoder’s purpose is to remember and construct those commands for you. You hand it some WAV or AIFF files, and set the parameters the way you want them. Then you press the Convert button, and LAMEncoder uses the LAME engine to encode the files to MP3.

    If this sounds familiar, it should. The idea comes straight from the LAME Encode Automator action developed starting on p. 452 of my AppleScript book. That Automator action wraps an AppleScript script in “just enough interface” to make it convenient and powerful. LAMEncoder wraps the same functionality in even more interface — enough interface to constitute a full-fledged, stand-alone application.

    Here’s the main window:

    shot1

    In the screen shot above, notice the tooltip. All the interface widgets have tooltips, and, like this one, many of them are dynamic - they change depending on the situation. Here, the tooltip is explaining why the file list area is blank. Once we hand LAMEncoder some files to convert, the tooltip will automatically read differently (as we shall see later).

    There are three ways to hand LAMEncoder some files to convert. One way is to press the Add File button in the toolbar at the top of the window. When you do, a standard Open File dialog appears:

    shot2

    In the above screen shot, I’ve selected an AIFF file. Now I’ll press Add. The Open File dialog closes and the selected file appears in the list:

    shot3

    Notice that the Clear and Convert buttons have become enabled as well. They were disabled before, because, with nothing in the file list, there was nothing useful they could do.

    Another way to add files to the list is to press the Add Folder button in the toolbar. When you do this, a standard Choose Folder dialog appears:

    shot4

    When I choose the “wavs” folder and click Add, any AIFF or WAV files in that folder are added to the list:

    shot5

    Naturally, the toolbar isn’t the only place to access the Add File and Add Folder functionality; after all, the user could hide the toolbar, or even customize it to remove those buttons. So the same functionality is also present in the menu, complete with keyboard shortcuts:

    shot10

    The third way of adding files, not illustrated here, is to drag them from the Finder into the list.

    HTML View benefits

    At this point we should pause to answer a burning question that I’m sure you’re racking your brains over right now: what the heck is this interface widget in which the file list appears? It clearly is not a standard Table View. And that makes sense, because the Table View is one of the important interface widgets that hasn’t been written into FaceSpan 5 yet.

    This was probably the biggest limitation of the FaceSpan 5's current state that I faced when writing LAMEncoder. It meant that I had to find a workaround means of displaying a scrolling list of the files the user wanted to encode. The workaround was to use FaceSpan’s HTML View widget. Yes, that area in the middle of the window is actually a Web browser! LAMEncoder structures the list of files as HTML and hands that HTML to the HTML View widget.

    An HTML View widget is a powerful animal in FaceSpan. To see how powerful, take a look at this next screen shot:

    shot6

    The above screen shot displays the tooltip for the HTML View when the list actually contains some files. Notice that it has changed from the tooltip we saw earlier. Notice also that it claims we can click on a file name to play that file. Let’s try it! We click on the last file in the list, and a dialog appears:

    shot7

    You can’t hear it, of course, but in that screen shot the dialog really is playing the WAV file. That part is easy, because FaceSpan has a Movie View widget; the dialog’s interface is nothing but a movie controller, and QuickTime knows how to play AIFF and WAV files. But how is it possible that you can click in the list of files in order to play a file? Well, as I said, an HTML View widget is a powerful thing in FaceSpan. One of the powerful things it can do is intercept the user’s request to navigate to a link. The names of the files are links. So when the user clicks on the name of a file, that’s a link navigation request; the HTML View hears about this, and instead of actually navigating to anything, it hands the information about the link over to the file-playing dialog, which opens and plays the file.

    Incidentally, the power of an HTML View is also why we can drag and drop a file from the Finder in order to add it to the list. This is quite remarkable, because in general, drag-and-drop is one of the things that isn’t implemented yet in FaceSpan! But an HTML View is its own little world, and it already knows how to respond to drag-and-drop. When it does, this counts as navigation (basically, it counts as an attempt to open a file). In LAMEncoder, the HTML View intercepts this attempt, and instead of navigating anywhere, it effectively hands the file over to the Add File button.

    Encoding in action

    So now that we’ve created a list of files to convert, let’s actually convert the files in the list. First, we use the “Conversion parameters” area of the window to set up how the conversion will be performed. In this next screen shot, I’ve decided to use CBR (constant bitrate) encoding, and I’ve entered a bitrate value of 320 for very high quality:

    shot8

    Notice that the text above the popup menu has changed from lame --preset standard to lame --preset cbr 320. That text represents the command-line parameters that will actually be sent to LAME to perform the encoding. The change in the text happens automatically as you play with the “Conversion parameters” interface.

    Furthermore, however you set the “Conversion parameters” interface is automatically remembered by LAMEncoder when you quit, by being written into the application’s user defaults (its Preferences .plist file); when you start up LAMEncoder again, that information is fetched from the user defaults and the “Conversion parameters” interface appears just the way you left it when you quit. That sort of thing, which adds a nice professional touch to our application, is extremely easy in FaceSpan.

    Anyway, now we press the Convert button. The Terminal opens and LAME is started, with the files and conversion parameters that we specified in LAMEncoder’s window:

    shot9

    That may seem a little nerdy, but for that very reason I like it. LAME provides nice progress feedback as it runs in the Terminal, so it makes sense to me to hand over the actual conversion process to the Terminal and let LAME do its thing. Meanwhile, LAMEncoder itself is free for further use; having handed off the correct command to the Terminal, it returns instantly and sits idle. The user can even quit LAMEncoder at this point, while the Terminal chugs merrily along, encoding the files.

    Finishing touches

    Now I’d like to point out a few of the finishing touches in the LAMEncoder interface that give it a full-fledged, “professional” application quality. First, there’s a standard About dialog:

    shot11

    All the underlined blue terms are links that the user can click to go to the relevant Web pages.

    LAMEncoder also has a second About dialog, which properly attributes the LAME engine and provides the text of the LGPL agreement under which it can be used in an application. That’s required, because LAMEncoder actually includes a copy of the LAME engine inside itself:

    shot12

    (Incidentally, the above window is the only part of LAMEncoder containing an interface widget that isn’t really built into FaceSpan. The scrolling Text View containing the text of the LGPL is implemented through a “plug-in”, which is essentially a bundle of Objective-C code. Eventually, the Text View will be a native FaceSpan widget.)

    Next, LAMEncoder has some preferences that the user can set. When you choose Preferences from the LAMEncoder menu, the preferences dialog appears:

    shot13

    As you can see, we have two preferences here.

    • First, using radio buttons, the user can decide whether to use the LAME engine built into LAMEncoder, or to use a copy of the LAME engine that may already be installed on the user’s computer. This is because it’s possible to compile LAME yourself, possibly using settings optimized for your processor, and we wouldn’t want to prevent a user who has done so from using that specially compiled LAME.

    • Second, having chosen the second radio button, it is also necessary to press the Set button and tell LAMEncoder where that on-disk copy of the LAME engine is. In this case, I’ve specified a bogus location; there is no copy of LAME on my desktop. If I press the second radio button and try to dismiss the preferences dialog, I’ll get an error alert:

      shot14

      The preferences dialog then opens again, forcing me either to use LAMEncoder’s internally provided LAME engine or to specify a location where there really is a copy of the LAME engine on my hard disk.

    Finally, like any good application, LAMEncoder provides online help:

    shot15

    The user chooses from the Help menu, and the online help appears (in this case, it’s a page of HTML, and opens in the user’s default browser).

    Future directions

    So much for LAMEncoder itself. There are two kinds of future directions I’d like you to be thinking about.

    First, there are future directions for LAMEncoder. Now that this version of LAMEncoder is finished, it’s easy to add further features. For example, right now LAMEncoder implements only the --preset subset of LAME commands (because those are the only ones I really use). It would be easy to add even more LAME commands, making LAMEncoder an even more powerful, complete front-end interface to LAME.

    Second, there are future directions for FaceSpan 5 — and for you. If LAMEncoder is the kind of application FaceSpan 5 can create right now, think what kinds of application it will be able to generate in the future. Clearly, this is an application development tool worth watching. Stay tuned!

  • Script Debugger 4.0.8b3

    I am preparing yet another Script Debugger 4 maintenance release. This release addresses some issues that have surfaced since the 4.0.7 release.

    You can download the Beta software here:

    Universal build requires Mac OS X 10.4 or later (13.1MB)

    If you are running 10.3 (Panther), you need to use this version instead (10.1MB)

    Changes in 4.0.8b3:

    13740 - Addressed a problem with external debugging support where parameters passed to event handlers were not handled properly on Intel machines.

    See Also

  • GraphX Plugin

    The recent FaceSpan 5.0d42 build includes a graphing plugin based on Chad Weider’s GraphX framework. Chad’s framework provides three graphing views: curve, histogram and scatter plot.

    As always when creating FaceSpan plugins from Cocoa frameworks, the tough part is converting the Cocoa APIs into a simple to use AppleScript syntax. In the case of these graphing views, I have substituted the ‘Data Source’ based APIs with a simple event for the curve view, and a value property that accepts a list of values to be graphed (a list of reals for the histogram and a list of x-y points for the scatter plot).

    Here’s how it all looks running in a FaceSpan-built application:

    graphx.jpg

    The code for the curve view looks like this:

    --
    --	CT curve uses this event handler to get the data to be graphed
    --
    on CT get y value theCTCurve for theXValue
    	return theXValue * theXValue * theXValue + 1.0
    end CT get y value
    

    The event is called repeatedly by the curve view to generate the data to be graphed.

    The code for the histogram looks like this:

    on initialize theResponder
    	--	CT histogram needs a list of reals to graph
    	set theData to {}
    
    	repeat with i from (x axis minimim) to (x axis maximum)
    		set end of theData to random number from 1 to 10
    	end repeat
    	set value to theData
    
    end initialize
    

    Here I simply generate a random series of values and assign them to the view’s value property. The plugin does the rest.

    And finally, the code for the scatter plot looks like this:

    on initialize theResponder
    	--	CT scatter plot requires a list of points (x, y pairs) to graph...
    
    	set yValue to 1.0
    	set theData to {}
    
    	repeat with i from (x axis minimim) to (x axis maximum)
    		set end of theData to {i, yValue}
    		set yValue to yValue + (random number from 0 to 2.0)
    	end repeat
    	set value to theData
    
    end initialize
    

    Again, I simply generate a random series of point values and assign them to the view’s value property. The plugin does the rest.

    The sources for the plugin are included in the FaceSpan 5.0d42 SDK.

  • Forking Scripts

    One of the precepts of FaceSpan programming is that your event handlers should run as quickly as possible to avoid blocking the FaceSpan user interface.

    However, this is at odds with how AppleScript is commonly used to orchestrate the actions of other applications. One generally begins a long process that involves directing one or more applications to complete a task. Even if the AppleScript code is short, it may have to wait for another application to complete a lengthy operation.

    The question becomes how best to accomplish this kind of AppleScript automation in FaceSpan. One answer is forking. This involves breaking the long-running portion of your project’s AppleScript code out into a separate script, and then running that script in its own Unix task. However, you want to do this in a way that keeps all the benefits of running AppleScript code within the FaceSpan environment (delegation, access to the UI, etc.).

    The answer is to use AppleScript’s parent property to link the forked script back to the FaceSpan responder (e.g. a button) that forked it. Here’s a simple example of a long running AppleScript that uses this technique:

    using terms from application "Forking" -- the name of the FaceSpan project
    	property parent : application "Forking"'s pushMe
    	--	From this point onward, all commands and property access is directed
    	--  at the "pushMe" button.  Note that this includes scripting addition
    	--	commands!
    
    	on doSomethingTimeConsuming()
    		delay 1
    	end doSomethingTimeConsuming
    
    	try
    		--	Simulate a long-running AppleScript task
    		repeat with i from 1 to 10
    			reportProgress(i) -- tell the main app how far we have gotten
    			doSomethingTimeConsuming()
    		end repeat
    
    		--	To send commands to the process running this script rather than
    		--	the main FaceSpan application, you must do the following:
    		tell current application to current date
    
    		--	Report back to the main application that we are finished
    		reportAllDone()
    
    		--	Prove that this code can be written as if its running in FaceSpan:
    		display alert "Done" ¬
    			message "I've finished wasting some time" ¬
    			buttons "OK" ¬
    			over my window
    	on error errMsg
    		set responderName to get my name
    		display alert "Runtime Error" ¬
    			message "Error in " & responderName & "'s forked script: " & errMsg ¬
    			buttons "OK" ¬
    			over my window
    	end try
    end using terms from
    

    This code is saved as a standard AppleScript compiled script using Script Editor/Script Debugger in a file within the FaceSpan project’s bundle.

    Now you are ready to fork the script from FaceSpan:

    on action theObject
    	set enabled to false
    
    	--	Fork off a process running an AppleScript that thinks its running in
    	--	the context of this object (button)...
    	set theCommand to "osascript " & ¬
    		quoted form of (POSIX path of (path to resource "fork.scpt"))
    	tell (make new task ¬
    		with properties {command:theCommand, auto delete:true})
    		start
    	end tell
    end action
    
    --	Subroutines that the forked script can call to tell the main application
    --	what's going on:
    on reportProgress(theProgress)
    	set title to "Progress: " & theProgress
    end reportProgress
    
    on reportAllDone()
    	set title to "Push Me"
    	set enabled to true
    end reportAllDone
    

    Notes:

    • since the forked script runs in a FaceSpan task, you could use the task’s ‘task did end’ event to discover when the forked script completes instead of the reportAllDone() subroutine.
    • if you need to abort the forked script for some reason (e.g. the user pressing a Cancel button), you can tell the FaceSpan task to stop.

    This seems like such an important thing to be able to do that I’m working on integrating this capability directly into the FaceSpan runtime for a future build.

    UPDATE: The FaceSpan 5.0d42 build provides this capability with the new fork command. The fork command accepts an AppleScript script object that is executed in a seperate process, thus allowing for long-running operations that don’t block the UI.

  • Dock Icon Changer

    One of my FaceSpan 4 examples demonstrated how to manipulate an application’s Dock icon. In this example, I rewrite that example using FaceSpan 5. Here’s the original code:

    property pAppIcon : missing value
    property pSmileImage : missing value
    property pCoolImage : missing value
    
    on awake from nib theObject
    	-- cache the images we'll be using
    	set pAppIcon to load image "NSApplicationIcon"
    	set pSmileImage to load image "smile"
    	set pCoolImage to load image "cool"
    end awake from nib
    
    on clicked theObject
    	local theName
    
    	-- For radio buttons, theObject is the cell that was clicked
    	-- In our case, we have named the cells 'none', 'smile' and 'cool'
    
    	set theName to name of theObject
    	if theName is "smile" then
    		set image of image view "preview" of window "main" to pSmileImage
    		set icon image to pSmileImage
    	else if theName is "cool" then
    		set image of image view "preview" of window "main" to pCoolImage
    		set icon image to pCoolImage
    	else
    		-- otherwise, reset the dock icon to the application's icon
    		set image of image view "preview" of window "main" to my pAppIcon
    		set icon image to my pAppIcon
    	end if
    end clicked
    

    Much of this code is concerned with managing the images that appear in the example. The balance of the code responds to radio button selections by altering the images in the ‘preview’ image view and the application’s dock icon.

    Here’s the code rewritten to take advantage of FaceSpan 5:

    on action theObject
    	if my id is theObject's id then
    		local theImageName
    
    		--	Read the dockImageName property of the selected radio button
    		set theImageName to my selection's dockImageName
    
    		--	Display the image in the preview view and the dock icon
    		set my application's dock image to theImageName
    		set image of my window's preview to my application's dock image
    	end if
    end action
    

    You’ll notice the following differences (beyond its being considerably shorter):

    • the code deals in the names of images (located within the application bundle) rather than having to explicitly load images, and retain references to the loaded images.
    • references to the “preview” image view are made using the “preview” property of the window object rather than having to give full object specifiers (though that is possible).
    • the code reads the name of the dock image from “dockImageName” property defined in each radio button object.
    • the implicit scope in FaceSpan 5 is the object to which the code belongs (a box object in this case) rather than the application as is the case in FaceSpan 4 (and AppleScript Studio).

    Here’s how the project appears in the FaceSpan IDE:

    dockiconchangeproject.jpg

    Its not enough to simply recreate the FaceSpan 4 example in FaceSpan 5. Lets take advantage of some additional dock functionality: FaceSpan 5 allows you to add progress and count badges.

    Here’s the code that adds a Safari-style progress bar to the Dock icon:

    	set my application's dock progress value to 0.8 -- 80%
    	set my application's dock progress style to Safari progress
    

    And here’s the code that adds a Mail style count to the Dock icon:

    	set my application's dock count value to 2007
    	set my application's dock count style to Mail count
    

    When its all put together, here’s how it appears in the running application:

    And here is the finished project: Dock Icon Change (FS5).

  • Script Debugger 4.0.8b2

    I am preparing yet another Script Debugger 4 maintenance release. This release addresses some issues that have surfaced since the 4.0.7 release.

    You can download the Beta software here:

    Universal build requires Mac OS X 10.4 or later (13.1MB)

    If you are running 10.3 (Panther), you need to use this version instead (10.1MB)

    Changes in 4.0.8b2:

    13761 - Addressed a problem where dragging text ahead of a compile/run-time eror to a position below the error would cause a Cocoa exception to be reported.

    Changes in 4.0.8b1:

    13723 - Corrected a regression introduced in Script Debugger 4.0.6 that caused the relevance ranking of dictionary search results to be incorrect.

  • Unplugged Service

    After Thursday’s post (Unplugged) where I described how to use System Configuration notifications to detect when a laptop’s AC Power Adapter had been unplugged, it occurred to me that this functionality could be packaged into a reusable FaceSpan object.

    The broadcast system that FaceSpan uses to notify code when a System Configuration has been changed can also be used by AppleScript code to send custom notifications.

    This feature makes it possible to broadcast a “ACPowerUnplugged” or a “ACPowerPluggedIn” message when the state of the AC Power Adapter changes that other code in the application can listen for. The code that generates these broadcast messages can be packaged as a stand alone object that can be dragged into any FaceSpan project that needs this capability.

    Here’s how this is done:

    • Create a new FaceSpan project
    • Drag a Bag object from the FaceSpanKit/Services group in the Library inspector into the new project (Other group)
    • Rename the new Bag to “UnpluggedService”
    • Paste the following code into the new Bag’s script:
    property pCurrentPowerSource : missing value
    
    on initialize theResponder
    	if my id is (get id of theResponder) then
    		listen for "State:/IOKit/PowerSources/InternalBattery-0"
    
    		set pCurrentPowerSource to |Power Source State| of ¬
    			(get value of my application's ¬
    				system configuration "State:/IOKit/PowerSources/InternalBattery-0")
    	end if
    end initialize
    
    on did broadcast theObject message theMessage user data theUserData
    	local newPowerSource
    
    	set newPowerSource to |Power Source State| of theUserData
    	if newPowerSource is not pCurrentPowerSource then
    		set pCurrentPowerSource to newPowerSource
    
    		if newPowerSource is "AC Power" then
    			broadcast message "ACPowerPluggedIn"
    		else
    			broadcast message "ACPowerUnplugged"
    		end if
    	end if
    end did broadcast
    
    • Compile

    Like the code from my previous Unplugged post, this code listens for changes in the state of the machine’s AC Power Adapter. The difference is that instead of acting on the change by presenting an alert, it broadcasts messages.

    • Select the “UnpluggedService” Bag and drag it out of the FaceSpan project outline to the Finder desktop (you can also use the File>Export>Object… menu item). This exports the object, and its code, into a file named “UnpluggedService” that can be dragged into any other FaceSpan project that needs this capability.

    Now, we can rewrite the example that appeared in the Unplugged blog entry to use the new UnpluggedService object.

    • Close the UnpluggedService service FaceSpan project
    • Create a new FaceSpan project
    • Drag the UnpluggedService file from the Finder desktop to the Other group
    • Select the application object
    • Paste the following code into application object’s script:
    on initialize theResponder
    	if my id is (get id of theResponder) then
    		listen for "ACPowerUnplugged"
    	end if
    end initialize
    
    on didBroadcastACPowerUnplugged(theObject, theUserData)
    	display alert "Unplugged" ¬
    		message "The AC Adapter has been unplugged and you are now running on battery power" buttons "OK"
    end didBroadcastACPowerUnplugged
    

    When you are done, the project should look something like this:

    result.png

    And we are done. Compare this code to the previous example and you’ll see that the application code now is only talking about the AC Adapter. The details of how this is done using the System Configuration objects and notifications are hidden in the UnpluggedService object.

  • Displaying Lists - Part 1

    As has been discussed at length on the FaceSpan 5 Beta mailing list, the current Alpha builds lack table and outline views. However, FaceSpan does contain an HTML view which can be used to display lists of information to the user. This may be enough to keep you going until real tables/outlines/browsers are introduced into the product.

    There are several ways to approach the problem of displaying tables in HTML. In this first installment, I’ll display XML data in an HTML view using a CSS stylesheet to transform the XML into HTML. In the next installment, I’ll use XSLT to perform the conversion from XML into HTML.

    The first thing you have to do is convert the data you want to display into XML. You can using my XML Tools scripting addition or simply construct a string in AppleScript. In this example, I am generating a list of the files in the top Finder window. The XML that I’m creating looks like this:

    <?xml version="1.0"?>
    <?xml-stylesheet type="text/css" href="foo.css"?>
    <doc>
      <row>
        <name>AbstractConnection.h</name>
        <path>file://localhost/Users/mall/Desktop/connection/trunk/AbstractConnection.h</path>
        <date>Friday, July 13, 2007 4:59:01 PM</date>
      </row>
      <row>
        <name>AbstractConnection.m</name>
        <path>file://localhost/Users/mall/Desktop/connection/trunk/AbstractConnection.m</path>
        <date>Friday, July 13, 2007 4:59:01 PM</date>
      </row>
      ...
    </doc>
    

    A critical part of this XML fragment is the <?xml-stylesheet type=“text/css” href=“foo.css”> line which links the XML to a CSS stylesheet (foo.css). When the HTML View is asked to display this XML data, it will apply the CSS stylesheet for us.

    The stylesheet looks like this:

    doc {
    	display: table;
    	font-family: Lucida, Verdana, Arial, sans-serif;
    	font-size: 11px;
    	background: white;
    	border-collapse: collapse;
    	width: 100%;
    }
    row {
    	display: table-row-group;
    }
    name {
    	display: table-cell;
    	border: 1px solid #c8c8c8;
    	padding: 3px;
    	width: 140px;
    	min-width: 140px;
    	max-width: 140px;
    }
    date {
    	display: table-cell;
    	border: 1px solid #c8c8c8;
    	padding: 3px;
    	width: 140px;
    	min-width: 140px;
    	max-width: 140px;
    }
    path {
    	display: table-cell;
    	border: 1px solid #c8c8c8;
    	padding: 3px;
    }
    

    It contains styles that convert the various XML elements (doc, row, name, date, path) into HTML tables, rows and columns.

    When you put it all togehter in FaceSpan, it looks like this:

    Display XML (CSS)

    Here’s the FaceSpan code that makes this happen. First, it gets the names, dates and URLs of the files in the top Finder window. Next, it generates an XML string from this infromation. Finally, it displays the XML in an HTML view.

    on didInvokeListFinderFiles(theObject)
    	local rsrcsFolder, theNames, theURLs, theDates, theXML
    
    	set rsrcsFolder to POSIX path of (get my application's resources folder)
    
    	try
    		--	Generate XML listing the files in the top Finder window
    		tell application "Finder" to ¬
    			set {theNames, theURLs, theDates} to {name, URL, modification date} of items of first window
    
    		set theXML to {"<?xml version="1.0"?>"}
    		set end of theXML to "<?xml-stylesheet type="text/css" href="foo.css"?>" -- associated a XSL stylesheet with this XML document
    		set end of theXML to "<doc>"
    		repeat with i from 1 to length of theNames
    			set end of theXML to "  <row>"
    			set end of theXML to "    <name>" & (item i of theNames) & "</name>"
    			set end of theXML to "    <date>" & (item i of theDates) & "</date>"
    			set end of theXML to "    <path>" & (item i of theURLs) & "</path>"
    			set end of theXML to "  </row>"
    		end repeat
    		set end of theXML to "</doc>"
    
    		--	Convert the list of strings we have accumulated into one long string that we can display
    		set AppleScript's text item delimiters to {return}
    		set theXML to theXML as string
    
    		--	A this point we end up with an XML document looking something like this:
    		--
    		--	<?xml version="1.0"?>
    		--	<?xml-stylesheet type="text/css" href="foo.css"?>
    		--	<doc>
    		--	  <row>
    		--	    <name>AbstractConnection.h</name>
    		--	    <path>file://localhost/Users/mall/Desktop/connection/trunk/AbstractConnection.h</path>
    		--	    <date>Friday, July 13, 2007 4:59:01 PM</date>
    		--	  </row>
    		--	  <row>
    		--	    <name>AbstractConnection.m</name>
    		--	    <path>file://localhost/Users/mall/Desktop/connection/trunk/AbstractConnection.m</path>
    		--	    <date>Friday, July 13, 2007 4:59:01 PM</date>
    		--	  </row>
    		--	  ...
    		--	</doc>
    
    		--	Display the XML in an HTML view using an XSL stylesheet to make it look like a nice pretty table.
    		tell my htmlView
    			set value MIME type to "text/xml" -- makes the HTML view treat the string as XML rather then HTML
    			set value base url to "file://" & rsrcsFolder & "foo.css" -- tells the HTML view where the resources are (CSS & gifs)
    			set value to theXML -- display the XML data as HTML
    		end tell
    	on error errMsg
    		display alert "No Finder Window" message "Please open a Finder window to list." buttons "OK" over my window
    	end try
    end didInvokeListFinderFiles
    

    Display XML (CSS) Example Project

  • Speedometer View

    Apple recently released sample Cocoa code for a view called Speedometer View. I thought I would turn this into a FaceSpan plugin. The process took about an hour.

    Here’s Speedometer View as it appears in Apple’s test application:

    Apple Speedometer View

    Here’s the same test application recreated in FaceSpan using the plugin:

    FaceSpan Speedometer View

    Speedometer Plugin and Example Project

    The source code for this plugin is included in the FaceSpan 5.0d40 SDK.

  • Unplugged

    FaceSpan can notify your application when the system configuration of your Macintosh changes. Here’s an example inspired by Michele Balistreri’s Unplugged utility that uses the system configuration notificaitons to detect when the AC Power Adapter is unplugged on a laptop.

    The state of the Power Manager is reported in the “State:/IOKit/PowerSources/InternalBattery-0” system configuration entry. In FaceSpan you can read this entry at any time like this:

    get my application’s system configuration ¬ “State:/IOKit/PowerSources/InternalBattery-0”

    The state of this system configuration entry is returned as an AppleScript record (for the time being - this will change to a FaceSpan dictionary when collections are introduced) that looks like this:

    { ¬
        |Current Capacity|:97.0, ¬
        |Time to Full Charge|:0, ¬
        |Max Capacity|:100.0, ¬
        |Transport Type|:"Internal", ¬
        |HealthConfidence|:"Good", ¬
        |Name|:"InternalBattery-0", ¬
        |BatteryHealth|:"Good", ¬
        |Power Source State|:"AC Power", ¬
        |Time to Empty|:0, ¬
        |Is Present|:true, ¬
        |Is Charging|:false ¬
    }
    

    Detecting when the laptop is unplugged is simply a matter of watching for when |Power Source State| is not equal to “AC Power”.

    Here’s the code to do all this in FaceSpan:

    property pCurrentPowerSource : missing value
    
    on initialize theResponder
      if my id is (get id of theResponder) then
        listen for "State:/IOKit/PowerSources/InternalBattery-0"
    
        set pCurrentPowerSource to |Power Source State| of ¬
          (get value of system configuration "State:/IOKit/PowerSources/InternalBattery-0")
      end if
    end initialize
    
    on did broadcast theObject message theMessage user data theUserData
      local newPowerSource
    
      -- When FaceSpan delivers a broadcast notification for a particular
      -- system configuration, theUserData contains the new state of the
      -- configuration.
      log theUserData -- so we can see it in the FaceSpan Event Log
    
      set newPowerSource to |Power Source State| of theUserData
      if newPowerSource is not pCurrentPowerSource and ¬
         newPowerSource is not "AC Power" then
        set pCurrentPowerSource to newPowerSource
        display alert "Unplugged" ¬
          message "The AC Adapter has been unplugged and you are now running on battery power" ¬
          buttons "OK"
      end if
    end did broadcast
    

    The on initialize theResponder handler begins listening for changes to the “State:/IOKit/PowerSources/InternalBattery-0” system configuration entry, and also saves the current state of the power source (in case the machine is already unplugged).

    The on did broadcast theObject message theMessage user data theUserData handler is called by FaceSpan whenever anything changes in the “State:/IOKit/PowerSources/InternalBattery-0” system configuration entry. Here we look to see of the state if the power source has changed and, if it has, we present an alert to the user.

    Unplugged Example Project

    This same technique can be used to detect other types of system configuration change, such as the appearance of new USB devices, changes in the state of the machine’s internet connection, or system preferences changes.

  • Quartz Composer Desktop

    Here’s a FaceSpan 5 example project that turns Quartz Composer animations into desktop backgrounds.

    This project demonstrates the following things:

    1. How to place a FaceSpan window behind icons in the desktop
    2. How to use the new (as of FaceSpan 5.0d40) menu architecture
    3. How to create a Dock menu that shares code with the main menu
    4. Run-time discovery of resources embedded within the project
    5. Cute usage of the Quartz Composer view widget

    Quartz Composer Desktop Project

    I hope your graphics card is up to the task :)

  • Full Screen Image Viewer

    Here’s an example of a full fcreen image viewer application written in FaceSpan. The application displays random images from your iPhoto library.

    ImageViewer

    The button at the bottom of the display sides into view when the mouse is moved to the bottom of the screen.

    This example illustrates how to do a full-screen FaceSpan application. It also illustrates window animation.

    Image Viewer Project

  • Using Sparkle with FaceSpan 5

    I’ve produced a FaceSpan plugin that allows you to use the Sparkle Software Update framework in your FaceSpan 5 applications.

    You can download the Sparkle FaceSpan plugin here:

    sparkle.fsplugin.zip

    Installing:

    Follow these steps to install the Sparkle FaceSpan plugin:

    1. expand the Sparkle.fsplugin.zip archive
    2. move the resulting Sparkle.fsplugin to the ~/Library/Application Support/FaceSpan 5/Plug-ins folder
    3. Quit and relaunch FaceSpan

    To use Sparkle:

    1. drag an instance of the Sparkle object from the Plugins/Sparkle group in the Library inspector to your project (Others group)
    2. edit your project’s Info.plist as directed in the documentation on the Sparkle web site (you’ll also need to create an appcast RSS feed for Sparkle to read)

    At this point, when your application launches, Sparkle will begin checking for software updates as directed by the Sparkle entries in the application’s Info.plist file.

    If you want to have a “Check For Updates” menu item that allows the user to manually check for updates, do this:

    1. Drag a menu item object from the FaceSpanKit/Menus group in the Library inspector to the place in the Main menu where you want the menu item to appear (e.g. Under “About” in the appMenu).
    2. In the new menu item’s Attributes inspector:
    • change the menu item’s title to “Check For Updates”
    • change the menu item’s operation to SparkleCheckForUpdates
    • change the menu item’s delegate to the instance of the Sparkle object your created earlier

    If you want to have a toolbar item that allows a user to manually check for updates, you can create it like this in the window’s ‘window will open’ event handler:

    make new toolbar item at end with properties { ¬ name:“checkForUpdates”, ¬ title:“Check For Updates”, ¬ image"Sparkle.icns", ¬ operation:“SparkleCheckForUpdates”, ¬ delegate:my application’s |Sparkle| }

    If you want to have a button in a window that checks for updates, you can do the following in the button’s action event:

    on action theButton tell my application’s |Sparkle| to ¬ invoke operation “SparkleCheckForUpdates” end tell

    The source code for this plugin is included in the FaceSpan 5.0d41 SDK.

subscribe via RSS