Wednesday, January 30, 2008

Ignore target folders in eclipse

In Eclipse when you press Command-Shift-R (Open Resource), you end up seeing the same resource a few times -- when it e.g. gets copied into a location in the workspace by your build tool (e.g. maven, ant).

I was looking for a solution for some time now and way playing with working sets for this purpose.
Rob Mayhew obviously has one:
http://robmayhew.com/eclipse-ignore-folder/

Now the "only" thing left to do is to mark those target directories as derived -- after each clean up of those folders.

Luckily there is a project dash in Eclipse that can use javascript to do this.
Someone on the net even wrote the right script

Sunday, January 13, 2008

Mac apps runtime config

Much of an application on a Mac is configured by a file called Info.plist. You can find the file when
you right click on an application and choose "Show packet content" and descend down in the Content folder.

Apple has a page about all the key/value pairs at http://developer.apple.com/documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/PListKeys.html.

I was looking for a way to disable the dock icon and Main menu from a "StatusBarApp" (Apple calls them Agents IIUC). But without knowing the right terminology, this is hard to find :)

Tuesday, January 08, 2008

Cocoa: Preferences - and Binding

Since Mac OS X 10.3 there is a new "Binding" mechanism. The idea behind this is to e.g. have an observer pattern for the enablement of GUI elements or to read and store preferences without coding. To use this, you need to pull the NSUserDefaultsController from the controller palette in InterfaceBuilder into your app. In the inspector for a field, you can then go to the "bindings" pane and bind the values (shown here for a NSTextField, but applies to other GUI elements like checkboxes as well):



Bind to: should be set to the NSUserDefaultsController. For the controller key, you can leave the values. Model key path finally contains the key in the preferences. Depending on the kind of GUI element, the right data type will be choosen automatically.

The technique of binding basically allows to provide a preferences panel without any (Objective-C) coding to set and store the values -- very nice.
It is still a good idea to provide defaults for the settings as described in the previous post.


Update: I just found a nice article from Apple describing all this as well.

Friday, January 04, 2008

Cocoa: Preferences

Sometimes you want to save some user preferences for your app. Luckily there is good support for this in Mac OS X.
Perferences are key-value pairs that can be stored in the filesystem. The system works in multiple layers which means that if a key is not found in an upper layer, it will be searched in the layer below. Layers are: command line, application, global, language and default. This is means also that you don't need any additional code to see if a property is given on the command line or in the preferences etc - that is all transparent.
A good idea is to always provide the fallback values, so your code never needs to check if a preferences key is actually present or not.

Before your preferences show up in the Filesystem as user database under your choosen id, you have to fill the program id in Info.plist at:

<key>CFBundleIdentifier</key>
<string>XXX</string>

With the XXX the prefernces store would be ~/Library/Preferences/XXX.plist

An easy approach to set up the defaults if you only have a few preferences items is:

NSUserDefaults *preferences = [[NSUserDefaults standardUserDefaults] retain];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
  [NSNumber numberWithInt:11] ,@"interval",
  @"127.0.0.2" , @"targetAddr",
  NO, @"logEvents",
  @"osa:@shimoUp.aplescript", @"upScript",
  @"osa:@shimoDown.aplescript", @"downScript",
  nil ]; // terminate the list
[preferences registerDefaults:dict];


If you have more than a few items, it is easier to provide a defaults file in the app bundle and load this instead:

NSUserDefaults *preferences = [[NSUserDefaults standardUserDefaults] retain];
NSString *file = [[NSBundle mainBundle]
  pathForResource:@"Defaults" ofType:@"plist"];

NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:file];
[preferences registerDefaults:dict];


After this is done, reading preference values is easy:


NSString *someString = [preferences objectForKey:@"targetAddr"];
int interv = [preferences integerForKey:@"interval"];

You might want to use the respective xxxForKey message depending on the type of preferences values.

Storing modified values is equally easy:

[preferences setInteger:someInt forKey:@"interval"];
[preferences setObject:someString forKey:@"targetAddr"];


The documentation for NSUserDefaults states that it will store the modifications from time to time to the underlying filesystem object, but I found it better to explicitly call [preferences synchronize] to trigger this.