Planet Cocoa

May 15, 2008

Orestis Markou

Whither PyObjC?

If you like Python and you like Mac OS X, you should really check out PyObjC.

It’s a bridge that allows you to create first-class Cocoa applications using Python. It also has support from Interface Builder and XCode in Leopard and it’s already bundled with Apple’s Developer Tools!

Unfortunately, if you followed the above link, it doesn’t seem there’s a lot of stuff going on. The examples are outdated and there hasn’t been any update since January.

If you look more closely, there have been some recent checkins in the SVN trunk - the last one was on 5 May, which isn’t that far away. So the project isn’t dead/dying. It just needs some pushing and exposure.

The owner (or at least the person that’s done most of the latest work), Ronald Oussoren hasn’t written anything in his weblog for months, and indeed the only post there describes how he was using a private repository for the Leopard version, probably for NDA purposes. It’s also weird that PyObjC is not hosted in Mac OS forge, Apple’s open source repository.

Anyway, this post isn’t a whine - it’s more like a call to arms. PyObjC needs more visibility and more people using it. More activity in the mailing list, more tweets and more blog posts. I’m working on a small QTKit app to explore the API, and I plan to release it as a tutorial. If I have the time I’ll make a screencast too. There’s also work been done on bringing other examples up to date.

If you have any experience using the latest version of PyObjC, please, jump on the mailing list and contribute!

I’m also planning on tracking the SVN repository with git - look for an announcement of the mirror in the coming days.

Comments

May 15, 2008 03:20 PM

May 14, 2008

Kupuk

Parmanoir

Projected coordinates of a 3D CALayer

There's no explicit method to get projected coordinates of a 3D CALayer, but you can use convertRect:fromLayer to almost get them. Thanks to Simon Fraser on the Quartz ML for that.

Suppose you have layers setup like that :

rootLayer
  layerHavingA3DTransform
    layer1
    layer2
    layer3

layer1, layer2, layer3 will be transformed by layerHavingA3DTransform and will appear 3D. To get their projected coordinates, use

CGRect projectedRect = [layer1 convertRect:localRect toLayer:[self layer]];

This will transform localRect, a rect in layer1 coordinates to root layer coordinates. If you want the layer extent, that's -bounds.width/2, -bounds.height/2, bounds.width, bounds.height for the default anchorPoint of (0.5, 0.5). Note that projectedRect is in layer coordinates, not in NSView coordinates. For this case I had manually centered layerHavingA3DTransform in the view and got coordinates reflecting that, going from -size to +size.

I only tested axis aligned layers. If you want coordinates from a rotated layer, you might try convertPoint:fromLayer to convert each layer point.

by Patrick Geiller at May 14, 2008 02:20 PM

Cocoa Is My Girlfriend

From Hacker to microISV: Custom File Formats

In this continuing series on my own transition from a Mac application hacker to microISV (Independent Software Vendor), I am going to demonstrate how to create your own file format for your application. You’ve probably seen these types of files in popular applications such as iMovie HD (06′) or GarageBand or even xcode in which the actual files used are, behind the scenes, folders that the operating system treats as regular files. These folders/files have a special bit set on them that tell OS X how to deal with them. The goal of this post is to demonstrate how you can do the following:

  • Create your own file format with your own file extension
  • Register your file format with the operating system
  • Provide application loading of your file that’s been double clicked in the Finder
  • Write data and preferences back out to your application file
  • Add resources such as media files to your application file

You can download the demo application here: PDF Keeper Demo Application

A Brief Discussion On Files and Folders

If you’ve ever come across a file in your file system that when control-clicked gives you an option in the pop-up menu to Show Package Contents then you are familiar with the type of file we are looking to create. What you may not know, however, is that this file is actually a directory. The choice to show the package contents is the OS allowing you to treat the file as a folder, which it actually is behind the scenes. When you create a custom file format in this manner, you are simply storing your project data and files under a sub directory.

You can verify that a special bit is set on these types of files by opening Terminal.app, navigating to the directory where such a file resides and running ls -al to get a detailed file list. When I do that on my directory where some of my custom files types are stored, I get a listing that looks like this.

PDF Keeper Project Files in Terminal

Notice the at symbol (@) in the file listing. This is the visual indicator that this directory has extended information–which in our case means that the directory is being treated as a package. If you want to set this bit through the command line, just run the following command:

SetFile -a B <directory_name>

Where <directory_name> is the name of the directory on which you would like to set this bit.

PDF Keeper Demo Application

For this post, I’ve included a demo application I’ve named PDF Keeper. PDF Keeper isn’t terribly practical as an application, but it effectively demonstrates the principles I’m wanting to convey. It simply opens our custom file type and loads in properties and data specified in the Info.plist file that each custom file contains. When you first run the application, you will be prompted to create a new PDF Keeper file. You will then see a list view that displays a list of PDF files stored within our custom file type. When you first create the file, the list veiw will be blank, so you will need to add some PDF files by selecting File | Add PDF File…. It then allows you to double-click any of the files in the list to view them in a PDFView.

If you were to look at one of the PDF Keeper file contents by selecting Show Package Contents from the context menu in the finder, you would see file hierarchy similar to the one below.

PDF Keeper Hierarchy

Note: Keep in mind that you will have different PDF files in the Resources directory than I have.

The Info.plist file inside the Contents directory is a property list file in XML format that keeps track of the PDF files we are storing. The Resources directory is where all of the PDF files are actually stored when we add one in PDF Keeper. Download, build and run the demo application to see it in action.

The path to your Info.plist file is important. It should be located inside of the Contents directory and be named Info.plist. That way, when we call, -[NSBundle bundleWithPath:], we will receive back an NSBundle whose infoDictionary member variable is an NSDictionary populated with the contents of the Info.plist file.

You can see how we do this in the call to -setupProject.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (void)setupProject:(NSString*)projectPath;
{
  currentProjectFilepath = projectPath;
 
  // Load the file bundle to obtain the bundle dictionary
  NSBundle *contextBundle = [NSBundle bundleWithPath:projectPath];
  if (![contextBundle infoDictionary]) return;
 
  // The infoDictionary represents the Info.plist file. We load it
  // and grab the array of files using the key "Files"
  NSArray *files = [[contextBundle infoDictionary] objectForKey:@"Files"];
 
  // If we have a list of files, we initialize our array model with them.
  // Otherwise we initialize the array with a capacity of 10
  if( files != nil )
  {
      pdfFiles = [[NSMutableArray alloc] initWithArray:files];
  }
  else
  {
      pdfFiles = [[NSMutableArray alloc] initWithCapacity:10];
  }
 
  // Reload the table view
  [tableView reloadData];
}

Create Your Own File Format

This is probably the most tedious aspect of the custom file format task as you have to decide what data you want your file to contain. I suggest that you code up a sample property list file in xcode and manually edit it to represent what you want. Then you can tweak it as you go. You won’t think of everything you want to save at first, so you do this loosely, however, you do need to start somewhere. This property list file is what will be stored inside the Contents folder of your custom file. Here is what a Info.plist file looks like in a PDF Keeper custom file.

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Files</key>
  <array>
    <string>About Stacks.pdf</string>
    <string>8051 Microcontroller.pdf</string>
  </array>
</dict>
</plist>

Register Your Custom File Format

This part is actually quite simple. You will, however, want to have your own custom icon ready to use for your custom format file. To register your file format, which is what enables you to double click your file in the Finder and have it properly load your application, with your project loaded in xcode, select Project | Edit Active Target “<Project Name>” where <Project Name> is the name of your project. In the ensuing window, select the Properties tab. You will see at the bottom a list view labeled Document Types:. This is where you will specify your document name, extension, and icon file. Below is a screenshot of the Document Type specified in PDF Keeper.

PDF Keeper Doc Types

Notice that the Package checkbox is checked. This is what tells Finder that your file, though a directory, should look like a single file to the end user. You should also make note that the file extension field does not have a dot (.) in it. I learned that one the hard way.

Hiding/Showing File Extensions

Some applications that use this technique for custom file formats hide the file extension while others show it. Garageband, the entry level music recording application from Apple, for example, hides its file extension which is .band while our beloved xcode shows its file extension which is .xcodeproj.

Hiding and showing file extensions is handled in the Finder through the Get Info… window. If you select a Garageband file in the finder and press Command-I you will notice under the Name & Extension section the Hide extension checkbox is checked.

Here is a screenshot of a Garageband file’s Get Info… information.

Garageband File Info

Note: If you don’t have any Garageband files to look at, just run the demo app, PDF Keeper and create a new file. You can then Get Info… by pressing Command-I with that file selected in the finder to see the same thing.

You can do the same with an xcode project and you will notice that its Hide extension checkbox is not checked.

So the question becomes, how do we handle this in the code. When you create a new file of your custom format, you’ll want to control this, so how do we do so? It is a simple attribute you specify in an NSDictionary before calling NSFileManager’s createDirectoryAtPath called NSFileExtensionHidden. The code looks like this in PDF Keeper

1
2
3
4
5
6
NSNumber *num = [NSNumber numberWithBool:YES];
NSDictionary *attribs = [NSDictionary dictionaryWithObjectsAndKeys:num, NSFileExtensionHidden, nil];
if( ![[NSFileManager defaultManager] fileExistsAtPath:filepath] )
{
  [[NSFileManager defaultManager] createDirectoryAtPath:filepath attributes:attribs];
}

Keep in mind that you will also want to control whether or not file extensions can be seen in your NSSavePanel and whether or not the OS should treat file packages as directories. The following code is how we do this in PDF Keeper

1
2
[savePanel setCanSelectHiddenExtension:NO];
[savePanel setTreatsFilePackagesAsDirectories:NO];

If canSelectHiddenExtension is set to YES, the user would have the option to select/deselect a checkbox in the save file panel to show or hide the extension. We simply hide it for PDF Keeper as it reduces confusion and makes it easier to manage everything behind the scenes.

If treatsFilePackagesAsDirectories is set to YES, the file system would allow the user to drill down into the custom file as if it were a regular directory. We don’t want the user to be able to do that as we are treating the custom file as a normal file rather than a directory.

Loading Your Custom File From the Finder

You will likely want to enable your users to double click a file in the Finder that will then automatically load into your application. If you added a document type to your project as specified in the Register Your Custom File Format section above, then you are already part of the way there as on the first run of your application, the file/document type is automatically registered with the OS so that double clicks of your file type will be handed to your application without any further effort on your part.

Now you just need your application to be able to respond when it gets notified that the Finder wants you to load your files type. Fortunately, there is a very simple way to handle this scenario. All you need to do is implement the delegate

- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename

in your app delegate. The filename parameter contains the path to the file that was double-clicked in the Finder. You simply do all of your file loading/initialization when this method gets called. You may also implement

- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames

if you want to handle multiple files being passed to your application. The following code snippet shows how these two delegate methods are implemented in PDF Keeper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
  // This gets called when the user double-clicks a project file in the Finder.
  [self openProject:filename];
  [self setupProject:filename];
  return YES;
}
 
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
{
  // This gets called when the user opens multiple files from the Finder.
 
  // We only take the first file.
  if( [filenames count] > 0 )
  {
      [self application:sender openFile:[filenames objectAtIndex:0]];
  }
}

Importing Resource Files

In the PDF Keeper demo application, we are only concerned with PDF files. When users select File | Add PDF File… they are prompted to select a PDF file that is then imported into the custom file type. This importing procedure is really just a simple file copy. Remember that our custom file is really just a directory that is being treated as a regular file by the OS. The file import just copies the file from the place where it was selected by the user into the Resources directory of our custom file. Here is the code to add a PDF file to our project file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- (IBAction)addPDFFile:(id)sender;
{
  NSOpenPanel *openPanel;
 
  openPanel = [NSOpenPanel openPanel];
  [openPanel setCanChooseDirectories:NO];
  [openPanel setAllowsMultipleSelection:NO];
  [openPanel setResolvesAliases:YES];
  [openPanel setCanChooseFiles:YES];
 
  NSArray *fileTypes = [NSArray arrayWithObjects: @"pdf", nil];
 
  if ([openPanel runModalForTypes:fileTypes] == NSOKButton)
  {
      // Build our destination filename based upon the current project
      // file path and the Contents/Resources path below it.
      NSString *filename = [openPanel filename];
      NSString *displayName = [[NSFileManager defaultManager] displayNameAtPath:filename];
      NSString *destFile = [currentProjectFilepath stringByAppendingPathComponent:@"Contents"];
      destFile = [destFile stringByAppendingPathComponent:@"Resources"];
      destFile = [destFile stringByAppendingPathComponent:displayName];
 
      // Use the NSFileManager to copy the file
      [[NSFileManager defaultManager] copyPath:filename toPath:destFile handler:nil];
 
      if( pdfFiles != nil )
      {
          // Add the filename to our list of files.
          [pdfFiles addObject:displayName];
          // Save the project.
          [self saveProject:currentProjectFilepath];
          // Reload the table view
          [tableView reloadData];
      }
  }
}

Note: When you import the file you are simply copying it. If the file is large, though, you will want to provide a progress indicator so the user isn’t wondering if the app is hung or has crashed. This is non-trivial and will require you to use Carbon APIs to accomplish. In our demo app, I am simply calling NSFileManager’s copyTo: method which will copy the file, but will block until the copy is completed. If you want to implement a file copy using a progress indicator, see my previous post called Cocoa Tutorial: File Copy With Progress Indicator

Updating Info.plist Property List File

Whenever your user selects File | Save you want to update your Info.plist file with the contents of the pdfFiles NSDictionary that is used as the model for the table view control. To do so, we simply get the full path to the Info.plist file, delete the current file, and then save a new file to the same path. Here is the code we use to do so in PDF Keeper

1
2
3
4
5
6
7
8
9
10
11
12
13
// Set the path for our property list file.
NSString *plistFile = [contentsPath stringByAppendingPathComponent:@"Info.plist"];
 
// If it already exists in the project, delete it.
if( [[NSFileManager defaultManager] fileExistsAtPath:plistFile] )
  [[NSFileManager defaultManager] removeFileAtPath:plistFile handler:nil];
 
// Create a dictionary to use to write out our property list file.
NSDictionary *output;
output = [NSDictionary dictionaryWithObjectsAndKeys:pdfFiles, @"Files", nil];
 
// Write the file.
[output writeToFile:plistFile atomically:YES];

Conclusion

Your custom file format can be as simple or complex as you would like to make it. You simply decide on all of the information you would like to store in your Info.plist file and then create sub-directories under your Contents directory where you will store your data. The concepts are simple. You are storing data and information about the data inside of a directory that the filesystem treats as a single file. It’s a very elegant way to create and manage your own custom file formats. Until next time.

by Matt Long at May 14, 2008 03:30 AM

May 13, 2008

Red Sweater Blog

Safari Link Exposer

I think that for any business with an internet presence, an important part of running and growing that business is being astutely aware of your surroundings. In particular, that means recognizing when people on the internet are talking about you, and responding to or engaging them when it’s appropriate.

Typically when a person on the web is kind enough to link to one of my sites, I learn about it quickly. Those of you who are not familiar with the way the web works might be surprised to know that whenever you click a link in a browser, the browser is typically kind enough to also tell the link’s target server where the link was clicked from. This is called the “referrer” and helps a great deal in tabulating statistics about web site visitors.

What’s really interesting about this referral reporting is that statistics software such as Mint can make it exceedingly easy to keep tabs on who is linking to you. Additionally, services such as Technorati and Google Blog Search attempt to keep tabs on where particular pages on the web are linking to, and offer RSS feeds so that you can keep tabs on any new links that might be pointing your way.

Suffice to say, if you are interested in doing so, it’s possible to keep a broad, open eye on what people on the internet are saying about you, provided they include a link to you among their thoughts.

Cutting To The Chase

I tend to skim every referral that looks like it might be from a legitimate source (not link spam). For instance, sometimes I’ll find a blog post where somebody reveals a problem they’re having with MarsEdit or another of my applications. If it’s possible for me to chime in with proactive customer support, I figure the user will be more overjoyed than creeped out by my “stalking” their blog post.

If you’re lucky enough to start getting linked a great deal, it can start to become a burden to evaluate all those links and decide whether any of them require (or would benefit from) your attention. Typically for me this has involved opening every such link in Safari, and then proceeding to glance at it to see where they are linking to me, and what the context is. This can be difficult on a long page, or when the person has attached the link to an unlikely phrase, such as “this guy says.”

What we need here is a computer. Something that can perform the painstaking task of looking at a web page and deciding where the important parts are. Once these important parts are brought to my attention, I can quickly evaluate and decide whether to quietly take in the referral, or whether to engage in some way with the author.

A Scripted Solution

Safari Link Exposer is a small script I wrote to facilitate this task. Let’s take a look at a sample web page I might encounter in my referral following. See how it’s possible to scan for the links to me, but they don’t exactly jump out from the page:

Now keep in mind this is an arbitrarily simple test case. Normally the web page might be pages long or a lot more complex. Even still, a significant amount of time is spent parsing the “unimportant” information to get to the all-important references to me. Now let’s look what happens when I run my keyboard-activated script in Safari:

Ouch! My eyes, they bleed! Help! But see, that’s the point. There’s no missing the links to me (in red with white text). The first-level container of said links are brightly lit in yellow to accommodate easily tracking to them, and the second-level container is lit in a more subdued yellow to broadly attract your eye’s attention.

Free Download: Safari Link Exposer

I used my shortcuts utility, FastScripts, to attach a Safari-specific keyboard shortcut of Ctrl-Cmd-F to the script. Now when I’m browsing my referrals I can zoom in on the nitty gritty with a single keystroke.

Hope this is helpful to somebody. The basic script needs to be edited to be useful to you, but it’s set up so that you can easily replace the “red-sweater” search term with a term of your choice. You could also use this script as the basis for other types of “smart scanning” scripts that expose elements based on other criteria.

It’s also worth noting that the bulk of the script is JavaScript and should be easily adaptable to other web browsers. I use Safari and I trust its AppleScript-based “do javascript” command, so naturally that is the approach I took in writing this.

Please let me know if you have any improvements for the script or other ideas for how to streamline this process.

by Daniel Jalkut at May 13, 2008 05:41 PM

May 12, 2008

Matt Legend Gemmell

ImageCrop updated

Just a quick note that my ImageCrop source code has been updated, thanks to a contribution from Matt Tonkin at NovaMind.

In Matt’s own words:

I added:

- (void)drawInRect:(NSRect)dstRect operation:(NSCompositingOperation)op
fraction:(CGFloat)delta method:(MGImageResizingMethod)resizeMethod;

This has a couple of advantages when drawing directly rather than producing images:

  1. Image Quality - If you’re drawing in a scaled view (or with some funky scaling transformation matrix), the images will draw with their full original quality, rather than reduced to the new size (should help for Res Independence).
  2. Speed - as producing a second resized image means drawing twice.

The existing methods still work as they did originally.

Many thanks to Matt for the improvements!

by Matt Legend Gemmell at May 12, 2008 11:06 AM

Not pillow talk

Another chatlog with Neil. I think he makes a valid point, albeit not the one I was trying to discuss.


Matt: ah the old perceived complexity curve
Matt: I encounter it a lot during design of a new API for something
Matt: the task starts off seeming simple, then upon further thought and exploration, it becomes considerably more complex with several unanswered questions. Then, upon refining the idea still further, it becomes clear that the level of refinement has lead to discovering once again that it’s simpler than it seemed previously.
Neil: yes, I’m familiar with it.
Matt: the cycle can continue that way, of course, but eventually it narrows in on an average level of complexity which pretty much all non-trivial APIs share
Matt: I find that fascinating
Matt: it creates the proposition that expertise isn’t a superior ability to grasp complex concepts, but rather a high enough level of familiarity with the subject that the simplicity underlying the apparent complexity can be seen, and dealt with on that level.
Matt: I guess nature/biology is the ultimate example.
Neil: I bet you’re a barrel of laughs in the pub.
Matt: and IN THE BEDROOM
Neil: it’s more tragedy than comedy from what I hear
Matt: I like to think there’s an intermediate genre
Neil: romance

by Matt Legend Gemmell at May 12, 2008 10:10 AM

May 11, 2008

Gus Mueller

Acorn 1.2

Acorn 1.2 is out, and the best way to get it is probably the auto-update if you're already running it, or you can download it from the website if you haven't already.

New features, it's faster, and bug fixes: release notes for Acorn 1.2

May 11, 2008 09:49 PM

Tony Arnold

How late I am&#8230;

How late I am...

This just demonstrates how much your planning ends up meaning when you have a full-time job in addition to developing an application. I started earnestly developing Hyperspaces just under 6 months ago now (17th December, 2007 is the date of the first subversion commit). I’d been toying with the idea of a version of VirtueDesktops that wasn’t an insane hack, but instead worked with Apple’s implementation of virtual desktops for a few months prior. Until that week, I wasn’t sure it would be possible to gather enough information or interact with Spaces in a way that would even let me achieve what I wanted to.

Is the development vs. full-time job dilemma common among other small developers? Long term, I’d love to be able to develop great mac applications full-time, but it seems to be a catch-22 situation.

May 11, 2008 03:20 AM

May 10, 2008

Parmanoir

Oval radial gradients

Radial gradients are filled by moving a circle along a line, while changing its color. They look nice but a bit too 'circly' for my tastes, and I couldn't figure out how to squash them. Turns out it's really simple !, as the points you use to define them obey the same rules as any other primitive : they're transformed by the view matrix. Use CGContextScaleCTM(context, 1, someSquashValue) then draw as usual with CGContextDrawRadialGradient.

Image:Radial gradients.png

This will squash your gradient's circle and its height, so you'll need to compensate CGContextDrawRadialGradient's startCenter and endCenter. Once there, you can draw a really nice radial background.

by Patrick Geiller at May 10, 2008 11:24 AM

Kupuk

Vidnik

Handy for those quick videos you want to put on YouTube, like, right now! Vidnik lets you record video from your ISight and immediately upload it to YouTube. David Phillip Oster:

“To use Vidnik, run the app, then click the record button to start recording. Click it again to stop. Trim to just the golden moments you want to keep, as in the screen shot above. Fill in the required title, description, and so on. Click the upload button. That’s it.”

91BD6D79-4CBF-4651-89B7-0AA19A67B6FB.jpg

by Paul Robinson at May 10, 2008 06:49 AM

Tony Arnold

These are the voyages of the USS Hyperspaces&#8230;

Double-click to play.

Hyperspaces is feature-complete, I’m just working on the user interface controls to configure the desktop elements you can see in the video. I have a few more bugs to fix, a web site to construct and a tiny bit of documentation to write, and then (finally) I’ll be releasing a public beta. Yes, I know what I said - I’m taking it back. I want Hyperspaces to be as polished as CoverStream, and right now it’s not. But it will be - and it will be soon, but whereas 24 hours ago soon meant “Monday”, now it means “before WWDC08”. Flame on.

Oh, and if I haven’t said it enough alread - Core Animation is seriously the poo. If you’re not using it in your apps, you should be. Watch the video and tell me you don’t want implicit animation.

May 10, 2008 06:20 AM

May 09, 2008

Gus Mueller

TapeDeck 1.0

My friends Chris Liscio and Danial Sandler have just released TapeDeck 1.0.

'''TapeDeck is a new audio recorder exclusively for Mac OS X 10.5, designed with a quick-capture workflow in mind. You’re never more than a single mouse click (or keystroke) away from making a new recording, which are called, unsurprisingly, “tapes.” TapeDeck records directly to compressed MP4-AAC audio, making it equally useful for quick high-fidelity samples or hours and hours of lecture.'''

I've been testing TapeDeck for a couple of weeks, and it's pretty neat.

Congrats on shipping guys!

May 09, 2008 11:01 PM

Kupuk

Cocoa is My Girlfriend T-Shirts

Now THIS is going to make an impression when I go out clubbing.

Get your Cocoa Is My Girlfriend T-Shirt now.

D439F2E4-E899-478A-AB32-3225B3D7CC57.jpg

by Paul Robinson at May 09, 2008 10:49 PM

Fun Script

F-Script for Experimenting with Cocoa


As a follow-up to the Mac Developer Roundtable Episode 007, Colin Wheeler explains on Cocoa Samurai how he uses F-Script:

I'm a huge fan of creating experimental projects and playing around with API's and that's one reason I'm a fan of F-Script because then I don't have 30 small projects cluttering up my desktop or a temp directory and so now I do much less tiny projects in Xcode and use F-Script to see how the API's work.
You can start F-Script up in a console and create an app from scratch or just create temp objects and see how they work and play around with Cocoa in a way that's not possible in Xcode without creating loads of small projects.
You can download F-Script from http://www.fscript.org though if you are on Leopard you will need to download and install a special version of FScript Anywhere from http://osiris.laya.com/blog/?p=24

You can learn more on this in "Exploring Cocoa with F-Script", an article that shows the graphical F-Script object browser and some other cool exploration tools in action.

by Philippe Mougin at May 09, 2008 09:36 PM

Gus Mueller

Gus Mueller on MacVoices

Gus Mueller on MacVoices. (Hey, that's me!)

Chuck Joiner interviewed me earlier this week about the various products that Flying Meat has. I rambled on about Acorn and VoodooPad and FlySketch, and even FlyGesture. It was a fun time, and I hope to be back on one of his shows again.

Thanks Chuck!

May 09, 2008 07:37 PM

Official Google Mac Blog

Vidnik



Vidnik is our newest application in the Google Mac playground. It's a simple program for using the built-in camera on your Mac to create movies and upload them to YouTube.


You can use Vidnik to create a video diary, or just to quickly record a video comment to attach to an existing YouTube video. Vidnik works with the built-in video cameras on recent Macs, with Firewire video cameras, and with many USB video cameras.

To use Vidnik, run the app, then click the record button to start recording. Click it again to stop. Trim to just the golden moments you want to keep, as in the screen shot above. Fill in the required title, description, and so on. Click the upload button. That's it.

Or you can drag movies made in other programs onto Vidnik's column of movies, then click the upload button. And to use another program to do a little post-production, use the Gear menu to show Vidnik's movie file in the Finder. Edit the movie in the other program, then upload it.

by Scott Knaster (noreply@blogger.com) at May 09, 2008 06:44 PM

Red Sweater Blog

Bokeh 1.0

It’s 1.0 Friday among my indie developer friends. I know some of it has to do with the impending May 12 deadline for the Apple Design Awards.

Geoff Pado released his new application, Bokeh, today. Bokeh lets users “fast track” a particular application so that it gets the majority of the CPU on a system. By stopping other applications in their tracks, a CPU-intensive operation in Photoshop, for instance, might finish more quickly.

Congrats, Geoff. Getting to 1.0 is a beautiful thing.

by Daniel Jalkut at May 09, 2008 05:18 PM

TapeDeck 1.0

My friends Chris Liscio and Daniel Sandler have joined forces for a very cool new product called TapeDeck.

What is TapeDeck? Well, as those of you old enough to remember tapes can guess, it’s essentially a very simple audio recorder which embraces with gusto the tape cassette metaphor. It’s pretty fun to watch the wheels of a cute little “virtual tape” turn as you record your mental notes, a riff on the guitar, whatever.

Back in the OS 9 days, Apple used to ship a default audio recorder in the system preferences, so you could easily record your own system beeps, etc. Since then, it’s been remarkably hard to “just grab some audio” in a quick and painless manner. TapeDeck might be the new solution for this problem in my day to day workflow.

Congrats, Chris and Daniel, on a very well done 1.0.

by Daniel Jalkut at May 09, 2008 03:07 PM

RemObjects Software

A First Look at RO for Mac OS X and iPhone OS

They say a picture says more then a thousand words, so here we go:

What are we seeing here?

In the background, there's the .NET version of our standard "MegaDemo" sample server that we've been shipping for years. In this case, it's running using Mono/WinForms right on the Mac, but it might as well be running on a Linux or Windows server far away.

In the front (and not out to win the Apple 2008 Design Award, mind you) is a native Mac OS X application based on what will eventually become "RemObjects SDK for Mac OS X" (or something like that). You're looking at a 100% Objective-C Cocoa application build in Xcode 3, making use of our pure Objective-C implementation of the RO client stack.

Oh, and: a similar app might of course be found running on an iPhone or iPod touch nearby, but i do not want to tempt the NDA gods by posting pictures of it, you'll understand ;).

Want to know more? let me know. we're still looking for a few dedicated testers for when this goes into beta, hopefully later this month.

by marc hoffman at May 09, 2008 02:44 PM

Cocoa Samurai

I am on the Mac Developer Roundtable 007 Source Code Management

I was an invited guest on the Mac Developer Roundtable and came on to talk Source Code Management and to advocate for GIT. It was my first time ever on a podcast and I was a bit nervous, still listening to the first part of it right now, but it sounds pretty good so far. Please forgive all the "uhs" I do, Im normally a lot more confident speaking publicly, but I didn't know what to expect on the

by Colin Wheeler (noreply@blogger.com) at May 09, 2008 10:05 AM

May 08, 2008

Uli's Web Site

Not doing your best can hurt...

I'm creating an installer right now. Trouble is, Apple's PackageMaker is kinda buggy, insisting on updating existing copies it finds on another partition instead of installing it in the absolute path I need it to be in. Workarounds suggested by various people fail for various other reasons, like PackageMaker simply forgetting I ticked off a particular setting and checking it again... So I went looking for third-party package makers. One of them seemed OK, then I ran the installer, which told me it had to restart the Mac after installation. (...)

May 08, 2008 09:19 PM

Surfin' Safari

Reporting WebKit Security Bugs

Security is a top priority for the WebKit project. As contributors to the project have grown, it has become apparent that we need a process for safely reporting security vulnerabilities to the WebKit project in addition to the process for reporting vulnerabilities to Apple. Today, we are announcing a new mailing list for this purpose, security@webkit.org. In addition, you can now safely report vulnerabilities over https to our bug tracker, https://bugs.webkit.org, by placing the bug in the Security component. The people privy to the mailing list and the Security component are members of the Security group. It currently includes vendors shipping products that include WebKit, active port owners and trusted security experts. If you are shipping a product that includes WebKit and would like to be notified of security issues, please email security@webkit.org.

by Sam Weinig at May 08, 2008 02:22 AM

May 07, 2008

Matt Legend Gemmell

Mac devs Manchester meet-up

Hot on the heels of our upcoming Edinburgh mac devs meet-up, there’s one in Manchester next week too: you can read about it here.

It’s on Thursday, May 15 2008 beginning at 6:30 pm, and it’s at the Manchester Digital Development Agency, 117-119 Portland Street, Manchester M1 6ED. If you’re going to be in the area on Thursday, be sure to drop by!

by Matt Legend Gemmell at May 07, 2008 08:35 AM

Square Signals

User Interaction 101

I have a lot of trouble explaining my interest in user interaction to others.

A typical response to my reasoning goes something along the lines of: “Yeah, but with Beryl, Linux looks even better!”

The computer scientists I talk to just don’t understand that user interface is about how the program acts, not how it looks. And really, I think that’s why there’s so much terrible software out there: these are the people who are building it!

The Five Minutes Left Test

In five minutes, you’ve got to leave for a doctor’s appointment, but there’s this thing you’ve really got to get done first, and you need iFoo to do it.

You’re stressed and in a hurry. Is iFoo going to make you even more stressed, or is it going to sit you down, give you a slap, and tell you everything’s going to be okay?

The sad truth is that unless I’m very familiar with it, almost every piece of software is going to do the former. I’m in a hurry, but now I’ve got to figure out how to use this thing. More specifically, I’ve got to figure out how to express what I want to do in terms it can understand.

Doing It Right

The difference between mediocre software and superlative software is the language it speaks. Stressful software makes users express what they want to do in its terms; excellent software works on the user’s terms. It doesn’t make the user know what to ask or how to ask it, and most importantly, it doesn’t get in their way.

A number of principles are key to getting this right.

First and foremost is the principle of least astonishment. Essentially, actions shouldn’t surprise the user. They should work consistently across applications, they should be undoable, and what works elsewhere should work everywhere.

The next level is the principle of the four-star waiter (no Wikipedia article on that, I’m afraid). When you’re at the Ritz, and you’ve put all the marshmallows into your hot chocolate, you don’t have to ask for more. When you turn to grab another, the waiter has already silently refilled the bowl. Clippy and his wizard friends are an attempt to imitate this dapper young man, but they do a terrible job because they’ve forgotten about the silent part.

Your application should anticipate the user’s needs and—without confusing or obtruding—act appropriately. Things like remembering a window’s position or the previous input to a form are only the first step. iChat manages your away status for you, asking as few questions as possible. iTunes keeps your iPhone continuously in sync, and if you run out of space, it’ll leave off things like photos before more critical data like contacts or addresses. Twitterrific focuses tweets directed at you because it knows you want to read them.

The next important step is minimization of interaction. You can start by reducing the number of actions required to perform tasks, but that’s not good enough. When I want to broadcast a thought, I use Twitterrific more often than MarsEdit not so much because of number of steps but rather as a result of how many things I have to deal with. Every control, label, and window makes your application more stressful to use; every time I have to change focus (especially window focus), you’re breaking my flow a little. Safari is good because its interface is tiny. We like Twitter because it has one text field. iMovie ‘08 is a poster child for reduction to a tiny number of core actions.

Get Out of My Way!

This is really a matter of flow—flow is the most important thing. Flow is how we move, and it’s why people sometimes prefer a notepad over an electronic document. When you want to note something, you write it on the pad, and put it in your pocket. You don’t have to make up a filename or where it should go. You don’t have to worry about remembering where to find it again later. You don’t have to think about quitting the app when you’re done.

Flow happens when you perform a task, thinking only about what to do, not how to do it. This is why people like things like emacs and vi so much: after the (considerable) learning curve is over, they can move like lightning, slowed by the speed of their ideas rather than by the speed of their tool. When focus isn’t in the right place, when a modal dialog pops up, or when a button isn’t placed near the data it affects, it breaks flow. You have to stop thinking about the grand unified theory you’re writing long enough to explain to the computer what you mean.

That’s the real price of bad software.

When a personal finance program sucks, that’s annoying. But my blogging software gets in my way, so I write fewer posts. And when I can’t figure out how to change the time signature midway through my piece in a music composition tool, I stop composing. The music stops playing in my head. I look around. I get out the manual. All this because the icon for the tool in question wasn’t good enough!

As software becomes more powerful, it becomes more essential to creation—to making things. And if your software is doing anything to distract your users from their magnum opus, the world is never going to hear it. They’ll hear something just slightly inferior; you will have been responsible for confusing the creator at some pivotal moment.

If you’ve written something fantastic, though, it’ll become an extension of the artist’s hand, just like a paintbrush. It’s an extra tool that enables him to solve new problems and produce even better results. It’s something to strive for—it’s why I write software.

Learning

Fortunately, it’s not impossible to get a sense of your users’ flow.

Step One: You don’t count. You’re not confused by anything (one hopes) because you wrote the app. Your experiences will only catch blatantly obvious flaws. Use them as an initial filter.

Step Two: Your imagination, however, does. Write user stories. Write them for every conceivable use case. Throw out the ones that you don’t want to support. Declare that the ones which remain are the only cases you care about, and stick to that.

Step Three: Watch real users. Some of the most useful experiences I had during Pixen’s development were when I sat down an entirely new user in front of it and watched them go. You’ll see exactly works and exactly what’s confusing. You’ll discover new user stories or (with enough samples) gain the confidence to trash others.

Iterate, iterate, iterate, and never allow anything to grate or confuse. Never tell a user “well, I guess you can do that, but you have to go do these other two things with the whozit first, then click refresh.”

And whatever you do, don’t need a Knowledge Base.

by Andy Matuschak at May 07, 2008 08:30 AM

Kupuk

Television is the New Gin

Clay Shirky:

“If you take Wikipedia as a kind of unit, all of Wikipedia, the whole project–every page, every edit, every talk page, every line of code, in every language that Wikipedia exists in–that represents something like the cumulation of 100 million hours of human thought. I worked this out with Martin Wattenberg at IBM; it’s a back-of-the-envelope calculation, but it’s the right order of magnitude, about 100 million hours of thought.

And television watching? Two hundred billion hours, in the U.S. alone, every year.”

by Paul Robinson at May 07, 2008 04:32 AM

May 06, 2008

Matt Legend Gemmell

Mac devs Edinburgh meet-up

Details here.

We’re meeting at Baroque, 39-41 Broughton Street, Edinburgh, EH1 3JU, on Tuesday 13 May 2008, from 19:00. Email David (as requested in the post I’ve linked to above) if you’re coming along, and feel free to let me know here too.

by Matt Legend Gemmell at May 06, 2008 10:32 PM

Red Sweater Blog

MacGourmet Loves MarsEdit

Advenio software today released MacGourmet 2.3, an update to its recipe management and generally impressive kitchen cooking helper.

Among the added goodies is a feature to allow easily sending a recipe from your catalog over to MarsEdit for easy blogging. If you run a food related blog, or just a personal blog which would occasionally benefit from an epicurean boost, this should greatly ease your workflow!

I love the idea of 3rd party applications using MarsEdit to bring the easy blogging to their users. In the future I’ll be looking at ways to offer improved mechanisms to other developers so that the process can be even more streamlined and customized.

by Daniel Jalkut at May 06, 2008 03:03 PM

Parmanoir

Core Animation's convertPoint:toLayer: and sublayerTransform

Quick note : if you have a container CALayer containing layers arranged in a grid, have a sublayerTransform setup on it, and want to get its transformed layer coordinates on a mouseDown, you'll need a helper layer :

// We're in mouseDown
CGPoint point = NSPointToCGPoint([self convertPoint:event.locationInWindow fromView:nil]);

// Build a dummy CALayer and insert it
CALayer* helperLayer = [CALayer layer];
[containerLayer addSublayer:helperLayer];

// Get coordinates
CGPoint layerPoint = [[self layer] convertPoint:point toLayer:helperLayer];

// Remove our helper layer
[helperLayer removeFromSuperlayer];

Now why can't we just ask for containerLayer's coordinates ? Because it has a sublayerTransform, so its coordinates are really the same as its parent. We can't ask for one of containerLayer.sublayers either : being laid out (layouted ? :) ) in a grid, they each have their own coordinate system. We therefore use a dummy layer : it has a default position of [0, 0] and will give the point transformed trough sublayerTransform.

by Patrick Geiller at May 06, 2008 09:31 AM

Kupuk

Cocoa Camp Toronto

On Saturday I had the pleasure of attending the first ever Tacow Cocoa Camp. This was a BarCamp style gathering of Cocoa enthusiasts and professionals from every corner of Toronto.

AB4BDDDF-56AD-46AC-9030-BB7E26AA6408.jpg

The big idea of a BarCamp is that it is highly unstructured. The sessions are not decided on until that day, and all attendees can present, listen or do, well, do whatever they please. The Toronto Cocoa Camp spawned sessions that were all over the map. From DTrace/Instruments to QTKit to NSOperation, concepts were talked, code was hacked, and pizza was consumed.

A big thanks to Rick Innis and Karl Moskowski for organizing this and to Peentoo Patel and the guys at MDialog for providing the space (and paying for the pizza - we owe you guys pizza!)

ccamp_small.jpg

Me in my brand new Panic Tee

Photos by Austin Ziegler

by Paul Robinson at May 06, 2008 12:26 AM

May 05, 2008

Cocoa Is My Girlfriend

WWDC 2008 T-Shirts!

Last year I wore t-shirts for Zarra Studios and got quite a few compliments. These days I am becoming more known for this blog than anything else. Therefore, I am doing different t-shirts this year to celebrate this blog. As a reader of this blog you have an opportunity to purchase one of these shirts. They will only be run for WWDC so they are limited edition! :)

While the design is not complete yet, here is the first public draft of the T-Shirts:

Public_Draft_1.png

Normal (Male) T-Shirt

WWDC2008F_Proof_1.png

V-Neck (Female) T-Shirt

Currently, the plan is to take them with me to WWDC and hand them out to those who have purchased one right after the keynote on Monday. If you are not going to WWDC but would still like to order a T-Shirt, that is also doable but naturally shipping costs will be involved.

The price per shirt will be $20.00 US. If you are interested in purchasing one or more, please contact me at marcus at cimgf dot com and I will work out the details. Right now I am processing orders manually but if they get too crazy then I may set up an actual shopping cart. Quantities are limited so act now! ;-)

Lastly, to follow Justin’s great idea. I am interested in trading T-shirts with other WWDC attendees. If you have a NEW shirt that you would like to trade for a CIMGF shirt, please contact me at marcus at cimgf dot com to plan a swap.

All Orders must be received and paid for by the 19th of May, 2008. Any orders received after that date can be shipped but cannot be delivered to WWDC.

Legal Junk

It is not my intention to make a profit on these t-shirts. I am offering them to readers at the approximate production cost. It is not my intent to mass produce these t-shirts with the goal of making a profit. If the production run comes close to or exceeds 500,000 t-shirts (wouldn’t that be amazing!) then the artwork on the back of the t-shirts may change due to restrictions in the license agreement.

by Marcus Zarra at May 05, 2008 11:42 PM

Gus Mueller

Acorn 1.2b1

Acorn 1.2b1 is available for folks to test out. You can download it from the "latest builds" page.

Lots of bug fixes and improvements, and some new features as well. Here are some highlights:

New:
New "Last Filter" item under the Filter menu.
New Layer With Selection / New Layer With Selection via Cut.
Holding down the option key when choosing a core image filter from the Filter menu, now applies it right away without bringing up the filter panel.
Acorn can now open and save JPEG 2000 images.
New Overlay blend mode.
New "Offset" filter under Filter->Tile Effect.
Smarter trim command, now crops to the edge color and not just alpha pixels.
Fancy Crop (tm). The crop tool now tries to figure out a nice default if you just click on the canvas without dragging. (Ok, not really (tm)).
Holding down the option key when using Trim to Edges works just like Fancy Crop (tm!) does.

Fixes/Changes:
Tweaked the way selections work, so they more closely resemble "industry standards".
Much better looking cursor for the draw tool.
Plugged some memory leaks.
Various performance improvements when opening large files.
The canvas no longer accepts "first clicks" when it's not part of the main window.
QuickLook fixes, and more.

The full release notes are available as well.

May 05, 2008 09:10 PM

Parmanoir

ImageKit's HUD Controls

Frustrated by the absence of HUD controls in Leopard ? ImageKit may alleviate a little, if you dare use undocumented stuff.

Image:ImageKit HUD Controls.png

Add Quartz.framework to your project, open your NIB, drag sliders in and set their class to IKGraySlider (top one in the screenshot) or IKSlider (bottom one). The white spinner is IKSmallProgressIndicator, it has to be setup in code.

Thanks to F-Script for the spying around. There are more classes but I can't get them to work. Also, plenty of images in /System/Library/Frameworks/Quartz.framework/Versions/A/Frameworks/ImageKit.framework/Versions/A/Resources/

Sample code

Image:iconZip.png HUD Controls.zip

by Patrick Geiller at May 05, 2008 12:48 PM

May 04, 2008

Kupuk

May 03, 2008

briksoftware Blog

Darcs and source code management

In the last years we’ve seen many SCM trying to replace SVN (which in turn replaced CVS in many places). Git, Mercurial are two examples of that. I never really looked into them as they mostly sounded like “SVN with this” or “SVN with that”. 

Then I discovered darcs and I never went back. Sure, XCode has no integration with darcs, but one can live without that. What made me switch from SVN to darcs is the “theory of patches” it is based on. It makes redundant most concepts like branches, revision and even tags are different (they are an empty patch depending on all patches that were in the repository already). This way it is really easy to branch (if XCode had any integration, it would make its snapshot feature incredibly powerful) and merge back. I also found it easy to handle conflicts (except on project.pbxproj files, be careful with those).

For Cocoa developers (like us) it is also really welcome that darcs puts only ONE directory (called _darcs) in the main project folder (unlike svn putting a .svn folder everywhere) so you are unlikely to mess things up, for example by copying nibs to localize them or things like that (which was plaguing me as PDFKey Pro is localized in many languages and I kept doing the same mistakes over and over with SVN).

Currently, a Darcs GUI for Mac OS X is in the works, is called Darcheology. It is already quite neat but is in dire need of contributors, so if you got some spare time, it is surely worth to take a look at it!

by Michele at May 03, 2008 12:11 PM

Cocoa Is My Girlfriend

Cocoa Tutorial: File Copy With Progress Indicator

I often find and therefore come to expect common problems in Cocoa to be easily solvable, however, there are times, like this, where I am a bit disappointed that the problem can be so difficult. All I need to do is copy a file. This is a simple task right? All you need is NSFileManager and a call to - (BOOL)copyPath:(NSString *)source toPath:(NSString *)destination handler:(id)handler and you’re good to go. Not so fast, city slicker. This one is going to take a bit more effort cause you want one of them fancy new-fangled progress indicators to show how much of the file has been copied. Well, if you want Cocoa to help you, you’re out of luck cause NSFileManager won’t do that for you, but with a little bit of effort you can get it to work with some… gulp… C APIs.

You can download the demo project for this post here: Demo Project Copy File

It Couldn’t Be Simpler! Well, Maybe A Little Bit

The only example of doing an asynchronous file copy while implementing a callback from Apple is found here in the FSFileOperation example’s main.c file which is some 545 lines of code. I don’t know about you, but I really need small examples to understand what is going on. In fact, that’s one of the highly vaunted goals of our writing here at Cocoa Is My Girlfriend. If it’s not something you can grok in small digestable pieces, then it’s not terribly useful. At least that’s my limitation in learning and my philosophy in writing.

Anyhow, once I started down the path to find the solution, I simply looked at the call to FSCopyObjectAsync which has this signature.

1
2
3
4
5
6
7
8
9
10
OSStatus FSCopyObjectAsync (
   FSFileOperationRef fileOp,
   const FSRef *source,
   const FSRef *destDir,
   CFStringRef destName,
   OptionBits flags,
   FSFileOperationStatusProcPtr callback,
   CFTimeInterval statusChangeInterval,
   FSFileOperationClientContext *clientContext
);

I then just tried to figure out how to get each parameter that call needs and went from there. I got it about 95% of the way there and then had to ask the Cocoa Dev list why my callback wasn’t working. It was copying the file without a problem, but it wasn’t calling my callback at the specified interval in the CFTimeInterval statusChangeInterval parameter. Apparently, I needed to schedule my callback with the run loop using this call before it would work right:

1
OSStatus status = FSFileOperationScheduleWithRunLoop(fileOp, runLoop, kCFRunLoopDefaultMode);

The Gory Details

I will spare you those. Actually, it turns out that doing an asynchronous file copy while updating a progress indicator is not that difficult. It’s just isn’t terribly well documented for Cocoa–at least that I could find. I hope this post will help to remedy that. Take a look at the source code in the demo project and see what you think.

One detail I will provide, though, is that you will need to make your progress indicator available to the callback which is declared outside of the AppDelegate. To do so, I simply create a static pointer to an NSProgressIndicator object and then set it equal to my local NSProgressIndicator in the awakeFromNib of my AppDelegate. This allows me to go ahead and hook up my progress indicator in Interface Builder still, while making the reference to the indicator available to my callback. Here is the code I’m referring to:

1
2
3
4
5
static NSProgressIndicator *progressIndicator;
 
@interface AppDelegate : NSObject {
    IBOutlet NSProgressIndicator *localProgressIndicator;
snip...

And then in my -awakeFromNib:

1
2
3
4
- (void)awakeFromNib;
{
    progressIndicator = localProgressIndicator;
}

My callback is then able to access and update it like this:

1
2
3
4
5
CGFloat floatBytesCompleted;
CFNumberGetValue (bytesCompleted, kCFNumberCGFloatType, &floatBytesCompleted);
 
[progressIndicator setDoubleValue:(double)floatBytesCompleted];
[progressIndicator displayIfNeeded];

Note: Keep in mind that when you run the demo application, you should copy a file that will take longer than one second to copy if you want to see progress. The minimum callback interval in the FSCopyObjectAsync call is 1 second so anything smaller than several hundred MB will probably not be big enough to see the progress indicator advance progressively. This is also why you see a delay when the file copy first starts.

Conclusion

Copying files seems like such a common task so I’m pretty surprised how little documentation there is out there to demonstrate how to do it with a progress indicator in Cocoa. So much so, that I’m going to concede right now that I’ve probably missed something much simpler. If you can share that with me, I would love to know. Give me your feedback in the comments section or shoot me an email at matt at cimgf dot com. Until we meet again.

by Matt Long at May 03, 2008 05:52 AM

May 02, 2008

Gus Mueller

Newspaper Blackout Poems

Austin Kleon's Newspaper Blackout Poems are very cool. He creates poetry from newspaper clippings by blacking out all but the few words he needs.

(Found via kottke.)

May 02, 2008 07:14 PM

Red Sweater Blog

Blogger Supports Future Posting

Blogger announced today that they now officially support scheduled posting.

What does this mean for MarsEdit users? It means the nifty future posting technique that I described in a previous blog post will now work with Blogger.

In short, all you have to do is set the date (from the Post menu -> Edit Date) before you send your post to the server, and if it’s in the future, Blogger will automatically delay publishing of the post until that time. This brings Blogger’s behavior in this regard into line with WordPress, which also supports implied future posting by date. You’re next, Movable Type :)

I haven’t thoroughly tested this because it just became available, but I sent a post to my Blogger blog with a date of two minutes into the future, and sure enough it didn’t show up until the 2 minute time had elapsed. As always, I recommend testing this feature on your own blog before posting anything of a time-sensitive nature.

by Daniel Jalkut at May 02, 2008 12:33 AM

May 01, 2008

Parmanoir

Changing the background color of an NSButton

If you want to color or visually change one of your controls, the easiest way to do it is with Core Image. In Interface Builder, click wants Core Animation Layer, and add content filters.

Image:Changing background color of NSButton.png

This one uses Color Monochrome to color the button. In Dynamic overloading : sample code, I used Color Matrix but strangely it's not available in my version of IB. Well, you can always set it up in code. It's better than overloading all NSButtons' drawRect — that's quite overkill to color just one button. :)

by Patrick Geiller at May 01, 2008 11:55 PM

Kupuk

Iphone Wireframes

For mocking up your IPhone app to be in OmniGraffle, some very nice IPhone stencils:

69BFE904-6C3D-42FE-BF92-B8B3E463C0C8.jpg

by Paul Robinson at May 01, 2008 09:44 PM

RemObjects Software

Three Years of Chrome

Today, May 1, marks the third anniversary of Chrome, which was officially launched on May 1, 2005.

Chrome 1.0 was a landmark release, being the first full-featured third-party language integrated into Visual Studio. It opened up Pascal for the .NET framework, and introduced exciting new features such as Class Contracts (loosely based on Eiffel's Design By Contract, Asynchronous Methods and more.

A mere six months later, on November 7 2005, Chrome 1.5 (code-named 'Floorshow'), followed with what was essentially a major new version, despite it's .5 version number. Floorshow shipped on the same day as the .NET 2.0 framework and Visual Studio 2005, and it formalized support for these new technologies, including integration with the new IDE and language support for the new features introduced in .NET 2.0, such as generics, iterators, and more.

On August 1 2007, enjoying the longest development cycle yet, we released Chrome 'Joyride', version 2.0. Joyride brought along official support for new and emerging technologies that went beyond .NET 2.0, starting with libraries such as Windows Presentation Foundation and Windows Communication Foundation, and a wide range of extensions to the language, from enhanced nullable types support to sequences and queries and related technologies. Joyride was the first .NET language to ship production level support for LINQ, months before even Microsoft wold ship release versions of C# 3.0 and VB 9.0. Later that year, Joyride customers also saw the introduction of a new SKU which included a free copy of the Visual Studio 2008 IDE.

Finally, last month we announced the upcoming release of Oxygene, version 3.0 and with that the fourth major version of the Chrome language. On schedule for release on May 30, 2008, Oxygene revolutionizes the ways developers will be building multi-threaded applications for the next generation of hardware devices.

While a core focus of the first 3 releases has been to follow where C# and Microsoft were leading the platform, Oxygene is taking the language to the next level, beyond the "essential .NET". With the .NET platform stabilizing and the stream of new technologies coming out of Redmond slowing down, this has given our team the chance to put more focus on driving the language itself, and then parallelism features shipping this month will only be the start of that!

Thank you for your continued support and patronage of Chrome over the past 3 years — and here's to the next three years of Oxygene!

by marc hoffman at May 01, 2008 05:41 PM

Parmanoir

Core Animation is right handed

Right handed ? Count to 3 on your left and right hands. Align your fingers so that each finger is perpendicular to the other two. One, two, three gives the axes : X, Y, Z. Line up your hands so that X goes left and Y goes up. Now each hand's Z goes opposite the other's. Your right hand is your Core Animation frame, same as OpenGL.

For some reason I thought CA was left handed — I had my layers set with a positive Z value. The layers showed up where I was expecting them, but visually it was a mess : the far layers were drawn on top of the near layers ! Using a negative Z value fixed the glitch.

Perspective in CA, using 3D CALayers and a perspective transform :

your eye     layers are closer (bigger) here         layers have same size as in 2D        layers are farther (smaller) here

------------------ positive Z value ------------------------- zero Z value ----------------------- negative Z value---------

by Patrick Geiller at May 01, 2008 01:48 PM

waffle

May

Ah. I needed that.

I’m not quite ready to return yet; I said you’d be able to comment again in May, and now it’s May, and soon you’ll be able to comment, but not now. I want to be clear with this: regardless of the current trend, I don’t want my weblog to turn into a closed room.

People may occasionally be rude or misguided, you may be besieged by spamming scum and comments may be a poor way of creating a sustained conversation. But the right way to attack that isn’t to close down comments, it’s to realize what you’re working with and make adjustments accordingly.

Even if closing down comments makes sense to you, it is a resounding fingers-in-ears, “LALALALALA I’M NOT LISTENING” to some of us who have commented in the past. I won’t be making this mistake, and I’ll appreciate the irony in you not being able - due to technical difficulties, as it were - to comment on this post right now.

by Jesper at May 01, 2008 08:33 AM

The Eponymous Weblog

An easy way to manage singleton window controllers

In Sandvox and iMedia Browser, we use a number of utility windows that are singletons; there will be only one of any of them open. Examples include the registration window, email list signup dialog, problem reporter window, and the release notes window.

I had noticed a lot of redundant code in each of these controllers' classes dealing with maintaining the single instance of that window, which we would store in a static variable and access with a lazy accessor, not unlike the technique here.

When something starts to get repetitive, it's time to find a better way. So I created a class KSSingletonWindowController which manages the singleton instances. Instead of storing each instance of a window controller in its own static variable, it maintains a static reference to an NSMutableDictionary holding the window controllers, keyed by an identifier (generally the subclass name).

The class contains four class methods:

@interface KSSingletonWindowController :  NSWindowController

+ (id)sharedController;
+ (id)sharedControllerWithoutLoading;
+ (id)sharedControllerNamed:(NSString *)registrationName;
+ (id)sharedControllerWithoutLoadingNamed:(NSString *)registrationName;

@end

To access a window controller, you only need to invoke sharedController on your subclass of KSSingletonWindowController. The object will be created if it is not yet registered, and then that single object will be returned to you. If you want to get the instance but only if it has already been loaded (useful, for instance, in clean-up code or a response to a method to close a window; there's no point creating it if it hasn't already been created) you can use sharedControllerWithoutLoading.

If you need to have more than instance of a class, you would need to register each one with a different name. For example, you might have one instance of RTFDWindowController which you use to show release notes, and another one for showing credits. You would use the methods sharedControllerNamed: and sharedControllerWithoutLoadingNamed:, passing in arbitrary strings to identify each unique instance.

Here is the implementation. Each method builds upon the previous ones.

static NSMutableDictionary *sControllerRegistry = nil;

@implementation KSSingletonWindowController
sharedControllerWithoutLoadingNamed: lazily instantiates the controller registry and looks up the entry for the given key.
+ (id)sharedControllerWithoutLoadingNamed:(NSString *)registrationName
{
  if (!sControllerRegistry)
  {
    sControllerRegistry = [[NSMutableDictionary alloc] init];
  }
  KSSingletonWindowController *result
    = [sControllerRegistry objectForKey:registrationName];
  return result;
}
sharedControllerNamed: looks up the named item. It creates and stores the object if it wasn't allocated yet.
+ (id)sharedControllerNamed:(NSString *)registrationName
{
  KSSingletonWindowController *result
    = [self sharedControllerWithoutLoadingNamed:registrationName];
  if (!result)
  {
    result = [[[self alloc] init] autorelease];
    [sControllerRegistry setObject:result forKey:registrationName];
  }
  return result;
}
sharedController and sharedControllerWithoutLoading call their "named" counterparts using the name of the class as the key. (Obviously, you would invoke +[YourClass sharedController], not +[KSSingletonWindowController sharedController] for this to work.)
+ (id)sharedController;
{
  NSString *className = NSStringFromClass(self);
  return [self sharedControllerNamed:className];
}

+ (id)sharedControllerWithoutLoading;
{
  NSString *className = NSStringFromClass(self);
  return [self sharedControllerWithoutLoadingNamed:className];
}

That should do it! Please feel free to use and adapt this for your projects. Enjoy!

May 01, 2008 12:00 AM

A Cool Way to Integrate Web and Desktop

Following the example of Panic and others, we've put some code into our applicatons that, upon first launch, will ask people if they want to join our email announcement list.

One thing I don't like is when computers are dumb. And what I thought would seem dumb would be the case where you are already on our email list, and you hear about our new app, so you download it from the link we provide, and upon launching said app, it asks you if you want to join the email list. Well, duh!

So I mulled this over in the back of my head for a long time. Should we have a special version of our application that skips this question? Wow that would be a lot of hassle and be potentially confusing. Or would there be some way we could use AppleScript to set the user defaults for our application so that it wouldn't ask? That would probably be pretty invasive and require the user running a script in ScriptEditor. Or download a helper application that sets the stage in advance, then launches the real application? Geez, that's not going to be obvious. There must be a better way....

Somewhere along the way, it occurred to me: Cookies. Yeah, that's it. We can access the cookie store from Cocoa; it's the same cookie store used by Safari or other Webkit-based browsers. True, some Mac users are using Firefox or Camino, but this might solve it for a good chunk of users.

So what I did was to store a cookie associated with our website onn the page that a person goes to when they have confirmed that they want to sign up for our mailing list. I also set that same cookie when somebody clicks on the download link in our email blast; the link takes them to a page that sets the cookie and starts downloading the desired file.

Our application, upon launching, first checks the user default that will be set to prevent being asked more than once. If that has not been set yet, it checks the cookie store. If the cookie indicating that the they are on our email list is present, then they will be spared the redundant question. Only if the default and the cookie are absent will they be bothered.

The cool thing is that this cookie works across multiple applications. The cookie check is built into iMedia Browser 1.1, which we just released this morning, but we will be building it into Sandvox as well. Once that cookie is set, they won't be asked again from any of our applications. Well, at least until the cookie expires — but by then they will have stored the user default to prevent being asked.

Clearly, this is not a fool-proof technique. The cookie store might not have been written to disk by the time somebody launches the application. The user might have cleared out their cookies, or isn't using a Webkit-based browser. But it's an interesting, light-weight mechanism for "communicating" between Browser and Desktop.

Naturally the next thing to think about is "What else could you accomplish with this technique?" You could set cookies on your company's website that your desktop application might be able to take advantage of, e.g. your most recent login ID (but not password of course), recently visited pages, and so forth. The "communication" could go the other way as well; a desktop application might set some cookies that help give the corresponding website some hints for web content.

This is a very "weak" form of communication. If you want to literally control your Desktop application from a website, you could put in hyperlinks to custom URLs that your application can respond to by implementing this kind of code. (For instance, this link, if you already have been running iMedia Browser, will open up the application's feedback window with some pre-populated fields; we've set this up to help people follow up on tech support issues.) And of course from your desktop application you can open up arbitrary URLs in your browser using NSWorkspace operations.

But when you want non-obtrusive passing of interesting data or settings between desktop and browser, this may be a useful technique.

May 01, 2008 12:00 AM

Self-Registering Transformers

Here is a snippet of code you can put into any subclass of NSValueTransformer that will cause it to automatically register itself when the class is loaded. This is useful for almost all value transformers, except perhaps those that you need to be given a parameter in the initialization process, such as these cool transformer classes.

This code will just cause the transformer to be registered with the name of the class itself. So if you class was, for instance, CondenseTransformer then you would specify "CondenseTransformer" in your nib file.

+ (void)load
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSValueTransformer *theTransformer = [[[self alloc] init] autorelease];
  [NSValueTransformer setValueTransformer:theTransformer
         forName:NSStringFromClass([self class])];
  [pool release];
}

May 01, 2008 12:00 AM

April 30, 2008

Cocoa Is My Girlfriend

Cocoa Tutorial: Wiring Undo Management Into Core Data

Undo support in Cocoa is fantastic but for those who have tried to mix it with Core Data know that it can be a bit frustrating. Generally, undo support can be ignored in most applications and it will “just work”. But when Core Data is added to the recipe then things get a bit confusing and more complicated.

The Conflict With NSUndoManager

For those familiar with the inner workings of Cocoa’s undo support (and if you are not familiar with it, check out the April edition of MacTech ;-) , all of the undo events are registered with an NSUndoManager. In a normal document model application, the window controller (or the window’s delegate if set to something other than the controller) will supply an NSUndoManager for all of the text fields and other editable bits in the window.

This works fine until Core Data comes along. The reason for this is that the NSManagedObjectContext has its own NSUndoManager that is registering those same events and can easily get out of sync.

What I prefer to do is avoid the double NSUndoManager and just use the one included with the NSManagedObjectContext. This can be done by implementing one of the window delegate methods:

1
2
3
4
5
6
7
8
9
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window
{
  return [self undoManager];
}
 
- (NSUndoManager*)undoManager
{
  return [[self managedObjectContext] undoManager];
}

By utilizing the NSUndoManager that is included in the context, some very powerful features are enabled.

Grouping sheet modifications

A common UI design is to allow the editing of an object in a sheet. The user double clicks on the item or otherwise indicates that they want to edit it and a sheet drops down allowing them to change the values. Naturally, that sheet has an NSUndoManager attached to it (can even use the one from Core Data) and each individual field can be undone. However, how to do we handle the cancel button or the ability to undo the entire sheet?

Both of the above can be handled with groupings inside of the NSManagedObjectContext object’s NSUndoManager. The methods would work as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- (void)presentEditSheet:(id)sender
{
  [[self undoManager] beginUndoGrouping];
  [NSApp beginSheet:[self editSheet] 
     modalForWindow:[self window] 
      modalDelegate:nil 
     didEndSelector:NULL 
        contextInfo:nil];
}
 
- (void)acceptChanges:(id)sender
{
  [[self editSheet] orderOut:sender];
  [NSApp endSheet:[self editSheet]];
  [[self undoManager]<