Earlier this evening I put together a convenient little class, ViewOptions, that allows you to toggle the columns on a table view. Here’s what it looks like:


Requires Mac OS X 10.5.
Download!

Usage is simple:

[[ViewOptions viewOptions] showViewOptionsForTableView:oYourTableView];

And thats really all there is to it.

Update 9/3/08: I just fixed a fatal design flaw that prevent this class from working properly: Because selection was disabled it was impossible to toggle the checkboxes.

You are free to use, modify, and distribute the above code in binary or source without restrictions, provided that you understand the creator is not liable in any size, shape, or form.

by Peter on 08/19/2008, in Cocoa, Random, Software, 0 comments

The following is a convenience method I wrote quite some time ago. Although I don’t have a use for it (now), I thought I’d post it here in the event someone else finds it useful.

CGContextRef CGBitmapContextCreateEmptyContext(CGSize size)

{

CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);

CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, (size.width * 4), colorSpace, kCGImageAlphaPremultipliedLast);

CGColorSpaceRelease(colorSpace);

return context;

}

CGImageRef CGImageCreateWithMaskAndFillColor(CFStringRef name, CGColorRef color)

{

/*

* Create the root image to be used as the mask

* Create the mask image

* Create the context to draw in

* Clip the context so that whatever we draw == inside the mask

* Fill the mask with a color

* Create the image to be returned

*/

NSData *imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:(NSString *)name ofType:@”png”]];

//Create the root image to be used as the mask

CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);

CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);

CFRelease(imageSource);

//Create the mask image

CGRect drawingRect = CGRectMake(0.0, 0.0, CGImageGetWidth(image), CGImageGetHeight(image));

CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(image), CGImageGetHeight(image),CGImageGetBitsPerComponent(image), CGImageGetBitsPerPixel(image), CGImageGetBytesPerRow(image), CGImageGetDataProvider(image), NULL, YES);

CGImageRelease(image);

//Create the context to draw in

CGContextRef imageContext = CGBitmapContextCreateEmptyContext(CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image)));

//Clip the context so that whatever we draw == inside the mask

CGContextClipToMask(imageContext, drawingRect, mask);

CGImageRelease(mask);

//Fill the mask with a color

CGContextSetFillColorWithColor(imageContext, color);

CGContextFillRect(imageContext, drawingRect);

//Create the image to be returned

CGImageRef finalizedImage = CGBitmapContextCreateImage(imageContext);

CGContextRelease(imageContext);

return finalizedImage;

}

You are free to use, modify, and distribute the above code in binary or source without restrictions, provided that you understand the creator is not liable in any size, shape, or form.

by Peter on 08/18/2008, in Software, 0 comments

Due to a recent email, it came to my attention that the zip containing the IKImageFlowView sample I wrote on my sample code page was completely botched and unexpandable. So I set out to rebuild the project and produce a (some-what) fully documented header for IKImageFlowView. The header is included in the project, and for the sake of prosperity I am posting the contents here:

/*

* IKImageFlowView.h

* Documentation Copyright (c) 2008, Peter MacWhinnie. All Rights Reserved.

* (Some documentation directly stolen from IKImageBrowserView, what, I am lazy)

*

* Generated by class-dump 3.1.2.

* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2007 by Steve Nygard.

*/

#import <Cocoa/Cocoa.h>

#import <ImageKit/ImageKit.h>

typedef NSUInteger IKImageFlowAnimationsMask;

/*!

@class IKImageFlowView

@abstract The IKImageFlowView class is used to represent a set of images in a cover flow.

@discussion The documentation in this file is far from complete, but should give you

enough information to have a basic understanding of how to use IKImageFlowView.<br />

This view requires Mac OS X 10.5+ and that you link Quartz or ImageKit.

*/

@interface IKImageFlowView : NSOpenGLView

{

id _dataSource;

id _dragDestinationDelegate;

id _delegate;

void *_reserved;

}

/*!

@method flowViewIsSupportedByCurrentHardware

@abstract Returns YES if cover flow is supported by the current hardware; NO otherwise.

*/

+ (BOOL)flowViewIsSupportedByCurrentHardware;

/*!

@method reloadCellDataAtIndex:

@abstract Marks a specified row as needing reloading.

*/

- (void)reloadCellDataAtIndex:(NSInteger)index;

/*!

@method reloadData

@abstract Marks the receiver as needing its data reloaded.

*/

- (void)reloadData;

/*!

@method itemAtIndexIsLoaded:

@abstract Returns YES if the item at the specified index is loaded; NO otherwise.

*/

- (BOOL)itemAtIndexIsLoaded:(NSUInteger)index;

/*!

@property animationsMask

@abstract The animations mask used by the receiver.

@discussion No further information available for this property,

if you have any an email would be appreciated.

*/

@property IKImageFlowAnimationsMask animationsMask;

/*!

@property selectedIndex

@abstract The index of the selected item in the receiver.

*/

@property NSUInteger selectedIndex;

/*!

@property focusedIndex

@abstract The index of the focused item in the receiver.

@discussion If you have more information about this method, an email would be appreciated.

*/

- (NSUInteger)focusedIndex;

/*!

@method cellIndexAtLocation:

@abstract Returns the index of the cell at the specified point.

*/

- (NSUInteger)cellIndexAtLocation:(NSPoint)point;

/*!

@method cellIndexAtPosition:

@abstract Returns the index of the cell at the specified position.

*/

- (NSInteger)cellIndexAtPosition:(CGFloat)position;

/*!

@method countOfVisibleCellsOnEachSide

@abstract The number of cells visible on the left and right side of the selected item.

*/

- (NSInteger)countOfVisibleCellsOnEachSide;

/*!

@method flipCellsWithOldSelectedIndex:newSelectedIndex:

@abstract No information available for this method.

*/

- (void)flipCellsWithOldSelectedIndex:(NSUInteger)oldSelectedIndex newSelectedIndex:(NSUInteger)newSelectedIndex;

/*!

@method updateLayoutInRange:

@abstract No information available for this method.

*/

- (void)updateLayoutInRange:(NSRange)range;

/*!

@method updateLayout

@abstract No information available for this method.

*/

- (void)updateLayout;

/*!

@method selectedImageFrame

@abstract Returns the frame of the selected item.

*/

- (NSRect)selectedImageFrame;

/*!

@property showSplitter

@abstract Whether or not to show the splitter in the image flow view.

*/

@property BOOL showSplitter;

/*!

@property delegate

@abstract The delegate of the receiver.

*/

@property (assign) id delegate;

/*!

@property dataSource

@abstract The data source of the receiver.

*/

@property (retain) id dataSource;

/*!

@property zoomOnSelectedLayer

@abstract Whether or not the receiver zooms on the selected item.

*/

@property BOOL zoomOnSelectedLayer;

/*!

@method backgroundIsLight

@abstract Returns YES if the receivers background is light; NO otherwise.

*/

- (BOOL)backgroundIsLight;

/*!

@method backgroundIsBlack

@abstract Returns YES if the receivers background is black; NO otherwise.

*/

- (BOOL)backgroundIsBlack;

/*!

@property backgroundColor

@abstract The background color of the image flow view.

*/

@property (retain) NSColor *backgroundColor;

/*!

@property cellBackgroundColor

@abstract The cell background color of the image flow view.

*/

@property (retain) id cellBackgroundColor;

/*!

@property cellBorderColor

@abstract The cell border color of the image flow view.

*/

@property (retain) NSColor *cellBorderColor;

/*!

@property imageAspectRatio

@abstract The image aspect ratio used by the receiver.

*/

@property CGFloat imageAspectRatio;

/*!

@property cellsAlignOnBaseline

@abstract Whether or not the receiver aligns its cells on its baseline.

*/

@property BOOL cellsAlignOnBaseline;

//These methods are no-ops, so there is no information available on them.

- (id)thumbnailImageAtIndex:(NSInteger)fp8;

- (id)previewImageAtIndex:(NSInteger)fp8;

@end

/*!

@protocol IKImageFlowViewDataSource

@abstract The IKImageBrowserDataSource formal protocol declares the methods that an instance

of the IKImageFlowView class uses to access the contents of its data source object.

*/

@protocol IKImageFlowViewDataSource

@required

/*!

@method numberOfItemsInImageBrowser:

@abstract Returns the number of records managed by the data source object.

@param sender An image flow view.

@returnValue The number of records managed by the image flow view.

*/

- (NSUInteger)numberOfItemsInImageFlow:(IKImageFlowView *)sender;

/*!

@method imageFlow:itemAtIndex:

@abstract Returns an object for the item in an image flow view that corresponds to the specified index.

@param sender An image flow view.

@param index The index of the item you want to retrieve.

@returnValue An IKImageBrowserItem object.

*/

- (id)imageFlow:(IKImageFlowView *)sender itemAtIndex:(NSInteger)index;

@optional

/*!

@method imageFlow:writeItemsAtIndexes:toPasteboard:

@abstract Signals that a drag should begin.

@param sender An image flow view.

@param indexes The indexes of the items that should be dragged.

@param pasteboard The pasteboard to copy the items to.

@returnValue The number of items written to the pasteboard.

@discussion This method is optional. It is invoked after Image Kit determines

that a drag should begin, but before the drag has been started.

*/

- (NSUInteger)imageFlow:(IKImageFlowView *)browser writeItemsAtIndexes:(NSIndexSet *)indexes toPasteboard:(NSPasteboard *)pasteboard;

/*!

@method imageFlow:removeItemsAtIndexes:

@abstract Signals that a remove operation should be applied to the specified items.

@param sender An image flow view.

@param indexes The indexes of the items that should be removed.

@discussion This method is optional. It is invoked by the image browser after Image Kit

determines that a remove operation should be applied. In response, the

data source should update itself by removing the specified items.

*/

- (void)imageFlow:(IKImageFlowView *)sender removeItemsAtIndexes:(NSIndexSet *)indexes;

@end

/*!

@protocol IKImageFlowViewDelegate

@abstract The IKImageFlowViewDelegate is an informal protocol for the delegate of

an IKImageFlowView object. You can implement these methods to perform custom

tasks when in response to events in the image browser view.

*/

@protocol IKImageFlowViewDelegate

@optional

/*!

@method imageFlow:cellWasDoubleClickedAtIndex:

@abstract Performs custom tasks when the user double-clicks an item in the image flow view.

*/

- (void)imageFlow:(IKImageFlowView *)sender cellWasDoubleClickedAtIndex:(NSInteger)index;

/*!

@method imageFlow:cellWasDoubleClickedAtIndex:

@abstract Performs custom tasks when the user clicks an item in the image flow view.

*/

- (void)imageFlow:(IKImageFlowView *)sender didSelectItemAtIndex:(NSInteger)index;

@end

by Peter on 08/14/2008, in Cocoa, Software, 0 comments

In the process of a chat, the basic rules of cocoa memory management came up. This led me to make a list of the basic rules, which I’ve decided to post here for no particular reason. I don’t claim this list is particularly perfect, but I think its a pretty good attempt.

  • Objects instantiated by nibs (views, controllers, the likes) are automagically released when your application terminates
  • If you use a convenience constructor, like + NSString#stringWithFormat:, the value is autoreleased and doesn’t need to be explicitly released. In fact, if you release an object with a retain count of 1 thats been autoreleased, you’ll get a runtime error.
  • If you’re creating a lot of autoreleased objects, say in a loop, its best to create and drain an autorelease pool in the loop or around the loop to prevent unnecessary memory use.
  • Any time you allocate and initialize something, you need to balance it with a release. If you’re returning a new object in an accessor, you should autorelease it. Note: its really bad practice to autorelease objects that you could easily release a few lines later, it just adds to memory use.
  • Objects returned by accessors are either autoreleased, or owned by the receiver itself unless explicitly marked otherwise.
  • If you don’t start your application with NSApplicationMain, or you’ve spawned a new thread, you need to create an autorelease pool before anything you do, and you need to drain it when you’re done. If you don’t create an autorelease pool, you will receive an error message and will leak memory.

If you happen to be lazy, and don’t care about leaks beyond your control, you can in fact use garbage collection in 10.5 and up. Cocoa garbage collection is very far from transparent, and only works automatically for cocoa objects and nothing else. Here are a few things to keep in mind when using garbage collection:

  • CoreFoundation objects are not managed by the garbage collector. Even though it would make _a lot_ of sense, CFObjects are not automatically managed by the cocoa garbage collector even though they are internally the same as NSObjects. This means that using any CF* method that returns a new object must be released appropriately.
  • Just because you use garbage collection, doesn’t mean you shouldn’t write regular memory management code, even if its no-op. In the event you need to use some code you’ve written on a system older then leopard, or you just find that you don’t like garbage collection and its ugly overheads, this will in fact come quite in handy.
  • References don’t really just go away. As long as you have a reference to an object, be it an ivar, a regular variable, or in a collection the object will _never_ be destroyed which will result in a leak. Theoretically this should solve itself, but there are times when it doesn’t. If two objects are referencing each other, neither will ever be destroyed.

I wouldn’t say this is a definitive guide, its really just observations from experience. I don’t claim 100% accuracy, I am only human, and only writing this blog post as a passing fancy. But, if this can help someone[s], then thats great.

by Peter on 08/06/2008, in Cocoa, Software, 0 comments

For a little while now I’ve wondered why the label “Open Source” seems to mean that whatever the label has been applied to is fantastic, beautiful, and somehow better than everything else in the world evar. To me this seems a little backward, at best. So far I’ve found very few great open source desktop applications. Its not like there aren’t thousands, it just seems like most of them just aren’t up to par with their commercial counterparts, and, even if they are their interface is so damn awful that no one knows how to use it!

Interface
It seems one of the biggest problems open source software has is lack of good taste when it comes to interface. Often times open source applications are either unbelievably ugly and alien or so complicated that you need a manual to find out how to select something.
Its not that the open applications are inherently low quality or unstable, its just painfully obvious that the people who wrote the code, also designed the interface. Not all people who know how to write code are terrible at interface design, it just seems to be a fairly common trend. A somewhat notable exception to the rule seems to be growl, which is in fact open source and really even the ugliest themes for it are tolerable. ‘Though it does help they’ve had [semi-]professional designers, design things for them.

The backside
Although most open source software (OSS) seems to have absolutely no desire to actually have a nice polished interface, OSS does have a saving grace: Libraries. If there’s one thing the open source community is good at, its producing fairly quality libraries. The reason is fairly simple: The people who are actively involved in the OSS community are ultimately massive geeks and programmers. They know how to write good code and assuming the open project in question doesn’t have an interface, you can bet its going to be of some level of quality.
Another thing thats good about OSS is that it produces open, common standards that tend to carry across platforms, and projects. A good example of this is ogg vorbis which is a lovely open lossy audio format. Ogg Vorbis is now fairly widely supported and is really a rather nice format.

End.
I suppose the point I was trying to get across with this fairly crappy blog post was that open source desktop applications are usually of low quality in comparison to applications that have been at least partially commercially backed. Of course this is not always true, and its always nice to see an exception. In addition to that the OSS community seems to be quite prolific at producing both libraries and standards en-masse. Certainly not all libraries and standards are solid gold, but a notable amount are.

Until my next attempt to write a blog post, byebye.

by Peter on 06/18/2008, in Software, 0 comments
Copyright © 2007-2008, Peter MacWhinnie. All Fancy Rights Reserved. Boring ones too.