Archive

Archive for the ‘Leopard’ Category

Day Two: iChibi and ImageSoup

April 20th, 2009 Comments off

Six Days of Cocoa: Day Two

Day Two: code-name iChibi

My whole family is very much into all things japanese (including, of course, manga and anime). The other day, they found a little “sound-playing ghost” called Flele (I have no idea what the name means).

As far as I can tell, Flele is a small application that you drop one or more MP3s on, and it starts to “sing” when you click on the character’s hair. I have no idea how it actually works, since it requires Windows and the Japanese language pack. But my daughter and I quickly hashed out that what we could do would be a globally-floating iTunes controller, who would respond to click and animate when music was playing (we’ll figure out the “singing” part later).

iChibi

My daughter drew lots of PNGs this weekend (using Painter, of course), and we assembled them in sequences to make animations. The iChibi can blink, and lights turn on/off on its headphones whenever music is playing.

iChibi can be dragged anywhere on screen, and will float on top of all windows.

Clicking on its left ear starts playing in iTunes, on the right ear stops playing. The left headphone goes to the previous track, the right headphone to the next track.

Clicking on the zipper gives a Settings panel, where you can control the its size, opacity and which playlist the songs are coming from.

The higher the song rating, the happier it will look.

iTunes support is done through the excellent Eyetunes Framework.

iChibi works on MacOSX 10.5 and later.

If you are interested in following the development of iChibi, follow @ichibiapp on Twitter.


ImageSoup

Since iChibi uses lots of images for animations, and I didn’t want to keep multiple copies of the same image in memory (for example, if an image is re-used in multiple animation loops), I figured I should make a small class to hold all the images, loading them as necessary. I named this class ImageSoup, referring to the Newton’s filesystem of course…

ImageSoup is very simple, and does not need to be its own class (after all, it’s a dictionary). But it is nice to be able to abstract that implementation detail out of your code, as well as the image-loading code. What if your source image was an Acorn image or a Painter RIFF? You could hide the image-loading code in ImageSoup, and your calling code need not be aware of this.

To use ImageSoup, just create an ImageSoup* instance and always ask it to load your images (using the full path to the image):

#import "ImageSoup.h"

[...]

ImageSoup* allImages = [[ImageSoup alloc] init];

[...]

NSImage* myImage = [allImages getImageAtPath: @"Full/path/to/image"];

If the image exists and has been loaded previously, ImageSoup will return it right away. If the image does not exist, ImageSoup will load it and store it for future reference.

When you release your ImageSoup*, all its images will be released.

ImageSoup.h

//
//  ImageSoup.h
//  iChibi
//
//  Created by Philippe on 09-04-19.
//  Copyright 2009 Philippe Casgrain. All rights reserved.
//

#import <Cocoa/Cocoa.h>

@interface ImageSoup : NSObject
{
  NSMutableDictionary* _images;
}

- (NSImage*) getImageAtPath: (NSString*) path;

@end

ImageSoup.m

//
//  ImageSoup.m
//  iChibi
//
//  Created by Philippe on 09-04-19.
//  Copyright 2009 Philippe Casgrain. All rights reserved.
//

#import "ImageSoup.h"

@implementation ImageSoup

- (id) init
{
  self = [super init];
  if (self != nil) 
  {
    _images = [[NSMutableDictionary dictionaryWithCapacity: 0] retain];
  }
  return self;
}

- (void) dealloc
{
  [_images removeAllObjects];
  [_images release];
  [super dealloc];
}


- (NSImage*) getImageAtPath: (NSString*) path
{
  NSImage* retrievedImage = [_images valueForKey: path];
  if (retrievedImage == nil)
  {
    NSImage* image = [[NSImage alloc] initWithContentsOfFile: path];
    if (image)
      [_images setObject: image forKey: path];
    [image release];
    retrievedImage = image;
  }
  return retrievedImage;
}

@end
Categories: Graphics, Leopard, Six Days of Cocoa Tags:

AvatarManager is Open Source

February 18th, 2009 Comments off

What is it?

Around the time of c4[2] I was futzing around with making a proper UI for my Twitter Avatar Acorn script.

I also wanted to learn about Core Data and Image Kit.

So I wrote AvatarManager, which uses Core Data to store images in png, tiff or jpg format and displays them in an ImageKit view. People suggested that it should not be limited to Twitter (which is my main Social Network app), so I made a pop-up menu populated by scripts that are run as an NSTask by the app. The scripts provide the interface to the image-upload interface of your on-line service.

Things change

Unfortunately, the Twitter “API” got modified and my script broke, so I lost interest. I was using Ruby’s ‘mechanize’ module to essentially script my way in the web page, which is an interesting way of doing it.

It’s still a good project!

I think the app is a good starting point: it’s simple, robust and extensible. The source code is easy to understand, and it shows how to use the Keychain (to store your passwords) and NSTasks.

You can help!

Grab a copy of the code and hack away! Patches welcome. Especially if you have new and updated upload scripts.

hg clone http://bitbucket.org/philippec/avatarmanager

Requirements

  • MacOSX 10.5 (Leopard)
  • Xcode 3
Categories: Development, Leopard, MacOSX Tags:

Search Paths for QuickLook plugins

May 27th, 2008 Comments off

You can debug your QuickLook plugin easily with the help of qlmanage. But sometimes, you don’t want to dump your plugin in /Library/QuickLook/ every time you build because you run as a regular (non-admin) user.

QuickLook has a search path feature. You can set the search order, and in my case I use my two build folders (Debug and Release) before the main /Library/QuickLook/ folder. It is a global, per-user property (hence the -g), and as far as I know, the array can contain as many values as you want

defaults write -g QLGeneratorSearchPath -array "/Volumes/DevSource/xcodebuild/Debug" \
                                               "/Volumes/DevSource/xcodebuild/Release/" \
                                               "/Library/QuickLook/"

It’s probably a good idea to leave /Library/QuickLook/ in there as the last element of the array.

If you want to delete the search path, simply type:

defaults delete -g QLGeneratorSearchPath

This does not seem to be documented, but it works for me; as usual, YMMV…

Categories: Graphics, Leopard Tags:

Pimp my IKImageBrowserView: Slide Show

March 27th, 2008 7 comments

After listening to Frasier Speirs on Late Night Cocoa, I decided to take a look at ImageKit, a new Leopard-only technology that contains the most interesting parts of iPhoto.

I had a need for an Image Browser, so I tried the (very well-written) Browsing Images Programming Guide. At the tutorial’s end, I had a “Browse Images” application:

If you haven’t done so, you can do this tutorial too. I will wait until you’re done…

All done? Great. I wanted some extras in “Browse Images”, for example a slideshow and a filter. There is a guide to build a slideshow application, but it makes you write a separate application. We already have “Browse Images”, surely we can re-use it?

Adding a slideshow

The IKSlideShow class is very simple. One call makes it all happen:

[[IKSlideshow sharedSlideshow] runSlideshowWithDataSource: (id) self inMode: IKSlideshowModeImages options: nil]

The objects have to implement the IKSlideshowDataSource protocol, which requires at least two methods:

	- (NSUInteger) numberOfSlideshowItems
	- (id) slideshowItemAtIndex: (NSUInteger) index

That looks familiar. For the IKImageBrowserDataSource protocol, we already implemented similar methods:

- (int) numberOfItemsInImageBrowser: (IKImageBrowserView*) view
{
	return [mImages count];
}

- (id) imageBrowser: (IKImageBrowserView*) view itemAtIndex: (int) index
{
	return [mImages objectAtIndex: index];
}

The only difference is the objects returned by (id) imageBrowser: itemAtIndex: had to implement the IKImageBrowserItem protocol, but they still returned an NSString*, a path to the image file.

(Another difference is the objects could be of more image types, but we will leave that for another time.)

Implementing the IKSlideshowDataSource protocol

In your ImageBrowserController implementation, add the missing methods to make the slide show work:

- (NSUInteger) numberOfSlideshowItems
{
	return [mImages count];
}

- (id) slideshowItemAtIndex: (NSUInteger) index
{
	int i = index % [mImages count];

	return [[mImages objectAtIndex: i] path]; // path? Where did that come from?
}

The first method is the same as before, while the second one returns not a MyImageObject (what is stored in mImages), but the path of that object. To get this path, simply add an accessor to MyImageObject:

- (NSString*) path 
{ 
	return mPath;
}

(Also add the prototype to the interface, lest you get a warning…)

Tying it together in Interface Builder

Now that our controller conforms to the IKSlideshowDataSource protocol, we need a way to start the slide show. To this end, create an IBAction in the controller’s interface, and implement it like this:

- (IBAction) startSlideshow: (id) sender
{
	if ([mImages count] > 0)
	{
		[[IKSlideshow sharedSlideshow] runSlideshowWithDataSource: (id) self inMode: IKSlideshowModeImages options: nil];
	}
}

Create a button in Interface Builder, and Control-drag it to your controller to connect it to the startSlideShow: action (IB 3 should have automatically picked up the change in your header file and offer you the new action).

Build and run. You should be able to browse pictures like before, and pressing on the “Slideshow” button should start a full-screen slideshow, complete with index sheet.

Categories: Graphics, Leopard, MacOSX Tags: