Index: trunk/Cocoa/AntiRSI/AUTHORS
===================================================================
--- trunk/Cocoa/AntiRSI/AUTHORS	(revision 331)
+++ trunk/Cocoa/AntiRSI/AUTHORS	(revision 332)
@@ -1,1 +1,2 @@
-Onne Gorter <o.gorter@student.utwente.nl>
+Onne Gorter <o.gorter@gmail.com>
+Nicholas Riley <antirsi@sabi.net>
Index: trunk/Cocoa/AntiRSI/AntiRSI.h
===================================================================
--- trunk/Cocoa/AntiRSI/AntiRSI.h	(revision 331)
+++ trunk/Cocoa/AntiRSI/AntiRSI.h	(revision 332)
@@ -21,8 +21,9 @@
 #import <Cocoa/Cocoa.h>
 #import "AntiRSIView.h"
+#import "CTBadge.h"
 
-#define sLatestVersionURL @"http://tech.inhelsinki.nl/antirsi/antirsi_version.txt"
-#define sURL @"http://tech.inhelsinki.nl/antirsi/"
-#define sVersion @"1.4"
+#define sLatestVersionURL @"http://web.sabi.net/nriley/software/AntiRSI-version.txt"
+#define sURL @"http://web.sabi.net/nriley/software/#antirsi"
+#define sVersion @"1.4njr2"
 
 typedef enum _AntiRSIState {
@@ -43,4 +44,5 @@
     IBOutlet NSTextField *status;
 	IBOutlet NSTextField *version; // XXX unused?
+    IBOutlet NSDatePicker *reset_session_time;
     
     // dock menu
@@ -51,4 +53,5 @@
 	NSImage* dock_image;
 	NSImage* original_dock_image;
+    CTBadge* dock_badge;
 	
 	// window to display the views in
@@ -67,4 +70,5 @@
     double session_t;
 	double date;
+    double reset_session_date;
 		
 	// various timing lengths
@@ -79,5 +83,10 @@
 	bool lock_focus;
 	bool draw_dock_image;
+	bool draw_dock_badge;
 	bool draw_dock_image_q;
+    bool reset_session_timer_daily;
+    bool reset_session_timer_after;
+    NSDate *reset_session_timer_time;
+    int reset_session_timer_after_hours;
 	
 	// various colors
Index: trunk/Cocoa/AntiRSI/AntiRSI.m
===================================================================
--- trunk/Cocoa/AntiRSI/AntiRSI.m	(revision 331)
+++ trunk/Cocoa/AntiRSI/AntiRSI.m	(revision 332)
@@ -102,4 +102,5 @@
 	[elapsed autorelease];
 	elapsed=[c retain];
+    [dock_badge setBadgeColor:elapsed];
 	[self drawDockImage];
 }
@@ -152,4 +153,6 @@
 	original_dock_image = [NSImage imageNamed:@"AntiRSI"];
 	draw_dock_image_q = YES;
+    dock_badge = [[CTBadge systemBadge] retain];
+    [dock_badge setBadgeColor:elapsed];
 	
 	// setup main window that will show either micropause or workbreak
@@ -165,4 +168,6 @@
 	[main_window center];
 	[main_window setContentView:view];
+    NSTimeZone *utcZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
+    [reset_session_time setTimeZone:utcZone];
     [progress setEnabled:NO];
     NSFont *myriad = [NSFont fontWithName: @"Myriad" size: 40];
@@ -181,19 +186,26 @@
 	
 	// create initial values
-	NSMutableDictionary* initial = [NSMutableDictionary dictionaryWithCapacity:10];
-	[initial setObject:[NSNumber numberWithFloat:4] forKey:@"micro_pause_period"];
-	[initial setObject:[NSNumber numberWithFloat:13] forKey:@"micro_pause_duration"];
-	[initial setObject:[NSNumber numberWithFloat:50] forKey:@"work_break_period"];
-	[initial setObject:[NSNumber numberWithFloat:8] forKey:@"work_break_duration"];
-	[initial setObject:@"Smooth" forKey:@"sample_interval"];
-	[initial setObject:[NSNumber numberWithBool:YES] forKey:@"draw_dock_image"];
-	[initial setObject:[NSNumber numberWithBool:NO] forKey:@"lock_focus"];
-	[initial setObject:[NSArchiver archivedDataWithRootObject:elapsed] forKey:@"elapsed"];
-	[initial setObject:[NSArchiver archivedDataWithRootObject:taking] forKey:@"taking"];
-	[initial setObject:[NSArchiver archivedDataWithRootObject:background] forKey:@"background"];
-	[[NSUserDefaultsController sharedUserDefaultsController] setInitialValues:initial];
+	NSUserDefaultsController *dc = [NSUserDefaultsController sharedUserDefaultsController];
+	NSMutableDictionary* initial = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+        [NSNumber numberWithFloat:4], @"micro_pause_period",
+        [NSNumber numberWithFloat:13], @"micro_pause_duration",
+        [NSNumber numberWithFloat:50], @"work_break_period",
+        [NSNumber numberWithFloat:8], @"work_break_duration",
+        @"Smooth", @"sample_interval",
+        [NSNumber numberWithBool:YES], @"draw_dock_image",
+        [NSNumber numberWithBool:YES], @"draw_dock_badge",
+        [NSNumber numberWithBool:NO], @"lock_focus",
+        [NSNumber numberWithBool:NO], @"reset_session_timer_daily",
+        [NSNumber numberWithBool:NO], @"reset_session_timer_after",
+        [NSCalendarDate dateWithYear:2000 month:1 day:1 hour:6 minute:0 second:0 timeZone:utcZone], @"reset_session_timer_time",
+        [NSNumber numberWithInt:8], @"reset_session_timer_after_hours",
+        [NSArchiver archivedDataWithRootObject:elapsed], @"elapsed",
+        [NSArchiver archivedDataWithRootObject:taking], @"taking",
+        [NSArchiver archivedDataWithRootObject:background], @"background",
+        nil];
+    [[NSUserDefaults standardUserDefaults] registerDefaults:initial];
+	[dc setInitialValues:initial];
 
 	// bind to defauls controller
-	id dc = [NSUserDefaultsController sharedUserDefaultsController];
 	[self bind:@"micro_pause_period" toObject:dc withKeyPath:@"values.micro_pause_period" options:nil];
 	[self bind:@"micro_pause_duration" toObject:dc withKeyPath:@"values.micro_pause_duration" options:nil];
@@ -202,12 +214,20 @@
 	[self bind:@"sample_interval" toObject:dc withKeyPath:@"values.sample_interval" options:nil];
 	[self bind:@"draw_dock_image" toObject:dc withKeyPath:@"values.draw_dock_image" options:nil];
+    [self bind:@"draw_dock_badge" toObject:dc withKeyPath:@"values.draw_dock_badge" options:nil];
 	[self bind:@"lock_focus" toObject:dc withKeyPath:@"values.lock_focus" options:nil];
+    [self bind:@"reset_session_timer_daily" toObject:dc withKeyPath:@"values.reset_session_timer_daily" options:nil];
+    [self bind:@"reset_session_timer_after" toObject:dc withKeyPath:@"values.reset_session_timer_after" options:nil];
+    [self bind:@"reset_session_timer_time" toObject:dc withKeyPath:@"values.reset_session_timer_time" options:nil];
+    [self bind:@"reset_session_timer_after_hours" toObject:dc withKeyPath:@"values.reset_session_timer_after_hours" options:nil];
 	NSDictionary* unarchive = [NSDictionary dictionaryWithObject:NSUnarchiveFromDataTransformerName forKey:@"NSValueTransformerName"];
 	[self bind:@"elapsed" toObject:dc withKeyPath:@"values.elapsed" options:unarchive];
 	[self bind:@"taking" toObject:dc withKeyPath:@"values.taking" options:unarchive];
 	[self bind:@"background" toObject:dc withKeyPath:@"values.background" options:unarchive];
-
+    
+    // watch for timer changes
+    [dc addObserver:self forKeyPath:@"values.reset_session_timer_time" options:0 context:NULL];
+    
 	// alert every binding
-	[[NSUserDefaultsController sharedUserDefaultsController] revert:self];
+	[dc revert:self];
 
 	// start the timer
@@ -218,12 +238,42 @@
 }
 
+- (void)computeResetSessionDate;
+{
+    NSTimeZone *systemZone = [NSTimeZone systemTimeZone];
+    NSCalendarDate *today = [NSCalendarDate calendarDate];
+    NSCalendarDate *reset = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:[reset_session_timer_time timeIntervalSinceReferenceDate]];
+    [reset setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
+    reset = [NSCalendarDate dateWithYear:[today yearOfCommonEra]
+                                   month:[today monthOfYear]
+                                     day:[today dayOfMonth]
+                                    hour:[reset hourOfDay]
+                                  minute:[reset minuteOfHour]
+                                  second:0
+                                timeZone:systemZone];
+    if ([today compare:reset] != NSOrderedAscending)
+        reset = [reset dateByAddingYears:0 months:0 days:1 hours:0 minutes:0 seconds:0];
+    reset_session_date = [reset timeIntervalSinceReferenceDate];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
+{
+    if ([keyPath isEqualToString:@"values.reset_session_timer_time"])
+        [self computeResetSessionDate];
+}
+
 // tick every second and update status
 - (void)tick:(NSTimer *)timer
 {
 	// calculate time since last tick
-	double new_date = [NSDate timeIntervalSinceReferenceDate];
-	double tick_time = new_date - date;
+	NSTimeInterval new_date = [NSDate timeIntervalSinceReferenceDate];
+	NSTimeInterval tick_time = new_date - date;
 	date = new_date;
-	
+
+    if (reset_session_timer_daily && date >= reset_session_date) {
+        [self resetSession:nil];
+        [self computeResetSessionDate];
+        return;
+    }
+    
 	// check if we are still on track of normal time, otherwise we might have slept or something
 	if (tick_time > work_break_duration) {
@@ -268,4 +318,9 @@
     }
     // NSLog(@"CGEventSource %.2f, CGS %.2f", idle_time, cgs_idle_time);
+    
+    if (reset_session_timer_after && idle_time > reset_session_timer_after_hours * 3600) {
+        [self resetSession:nil];
+        return;
+    }
     
 	// calculate slack, this gives a sort of 3 history filtered idea.
@@ -476,4 +531,17 @@
 	[p stroke];
 	
+    // draw session time
+    if (draw_dock_badge) {
+        static NSImage *badge = nil;
+        static int badge_session_minutes = -1;
+        int session_minutes = (int)session_t / 60;
+        if (badge_session_minutes != session_minutes) {
+            if (badge != nil) [badge release];
+            badge = [[dock_badge badgeOverlayImageForString: [NSString stringWithFormat:@"%d:%02d", session_minutes / 60, session_minutes % 60] insetX: 3 y: 3] retain];
+            badge_session_minutes = session_minutes;
+        }
+        [badge compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
+    }
+    
 	[dock_image unlockFocus];
 
Index: trunk/Cocoa/AntiRSI/AntiRSI.xcodeproj/project.pbxproj
===================================================================
--- trunk/Cocoa/AntiRSI/AntiRSI.xcodeproj/project.pbxproj	(revision 331)
+++ trunk/Cocoa/AntiRSI/AntiRSI.xcodeproj/project.pbxproj	(revision 332)
@@ -8,4 +8,12 @@
 
 /* Begin PBXBuildFile section */
+		464D9ADE0C6A622B005A0582 /* CTBadge.h in Headers */ = {isa = PBXBuildFile; fileRef = 464D9ADA0C6A622B005A0582 /* CTBadge.h */; };
+		464D9ADF0C6A622B005A0582 /* CTBadge.m in Sources */ = {isa = PBXBuildFile; fileRef = 464D9ADB0C6A622B005A0582 /* CTBadge.m */; };
+		464D9AE00C6A622B005A0582 /* CTGradient.h in Headers */ = {isa = PBXBuildFile; fileRef = 464D9ADC0C6A622B005A0582 /* CTGradient.h */; };
+		464D9AE10C6A622B005A0582 /* CTGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 464D9ADD0C6A622B005A0582 /* CTGradient.m */; };
+		464D9B790C6A760B005A0582 /* CTBadge_1.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 464D9B750C6A760B005A0582 /* CTBadge_1.pdf */; };
+		464D9B7A0C6A760B005A0582 /* CTBadge_3.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 464D9B760C6A760B005A0582 /* CTBadge_3.pdf */; };
+		464D9B7B0C6A760B005A0582 /* CTBadge_4.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 464D9B770C6A760B005A0582 /* CTBadge_4.pdf */; };
+		464D9B7C0C6A760B005A0582 /* CTBadge_5.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 464D9B780C6A760B005A0582 /* CTBadge_5.pdf */; };
 		4DB6164E061B59EA002EF355 /* AntiRSI.h in Headers */ = {isa = PBXBuildFile; fileRef = 4DB6164C061B59EA002EF355 /* AntiRSI.h */; };
 		4DB6164F061B59EA002EF355 /* AntiRSI.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DB6164D061B59EA002EF355 /* AntiRSI.m */; };
@@ -31,4 +39,12 @@
 		29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
 		32CA4F630368D1EE00C91783 /* AntiRSI_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AntiRSI_Prefix.pch; sourceTree = "<group>"; };
+		464D9ADA0C6A622B005A0582 /* CTBadge.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CTBadge.h; path = CTBadge/CTBadge.h; sourceTree = "<group>"; };
+		464D9ADB0C6A622B005A0582 /* CTBadge.m */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; name = CTBadge.m; path = CTBadge/CTBadge.m; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
+		464D9ADC0C6A622B005A0582 /* CTGradient.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CTGradient.h; path = CTBadge/CTGradient.h; sourceTree = "<group>"; };
+		464D9ADD0C6A622B005A0582 /* CTGradient.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = CTGradient.m; path = CTBadge/CTGradient.m; sourceTree = "<group>"; };
+		464D9B750C6A760B005A0582 /* CTBadge_1.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = CTBadge_1.pdf; sourceTree = "<group>"; };
+		464D9B760C6A760B005A0582 /* CTBadge_3.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = CTBadge_3.pdf; sourceTree = "<group>"; };
+		464D9B770C6A760B005A0582 /* CTBadge_4.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = CTBadge_4.pdf; sourceTree = "<group>"; };
+		464D9B780C6A760B005A0582 /* CTBadge_5.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = CTBadge_5.pdf; sourceTree = "<group>"; };
 		4DB6164C061B59EA002EF355 /* AntiRSI.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AntiRSI.h; sourceTree = "<group>"; };
 		4DB6164D061B59EA002EF355 /* AntiRSI.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AntiRSI.m; sourceTree = "<group>"; wrapsLines = 1; };
@@ -99,4 +115,5 @@
 			children = (
 				080E96DDFE201D6D7F000001 /* Classes */,
+				464D9AE40C6A622F005A0582 /* CTBadge */,
 				29B97315FDCFA39411CA2CEA /* Other Sources */,
 				29B97317FDCFA39411CA2CEA /* Resources */,
@@ -134,4 +151,28 @@
 			);
 			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		464D9AE40C6A622F005A0582 /* CTBadge */ = {
+			isa = PBXGroup;
+			children = (
+				464D9B740C6A760B005A0582 /* badges */,
+				464D9ADA0C6A622B005A0582 /* CTBadge.h */,
+				464D9ADB0C6A622B005A0582 /* CTBadge.m */,
+				464D9ADC0C6A622B005A0582 /* CTGradient.h */,
+				464D9ADD0C6A622B005A0582 /* CTGradient.m */,
+			);
+			name = CTBadge;
+			sourceTree = "<group>";
+		};
+		464D9B740C6A760B005A0582 /* badges */ = {
+			isa = PBXGroup;
+			children = (
+				464D9B750C6A760B005A0582 /* CTBadge_1.pdf */,
+				464D9B760C6A760B005A0582 /* CTBadge_3.pdf */,
+				464D9B770C6A760B005A0582 /* CTBadge_4.pdf */,
+				464D9B780C6A760B005A0582 /* CTBadge_5.pdf */,
+			);
+			name = badges;
+			path = CTBadge/badges;
 			sourceTree = "<group>";
 		};
@@ -147,4 +188,6 @@
 				4DF73F68061E136200B0D9C9 /* AntiRSIView.h in Headers */,
 				E1F60362098592D800806C3B /* AntiRSILevelIndicator.h in Headers */,
+				464D9ADE0C6A622B005A0582 /* CTBadge.h in Headers */,
+				464D9AE00C6A622B005A0582 /* CTGradient.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -202,4 +245,8 @@
 				8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
 				4DF73F86061E155C00B0D9C9 /* AntiRSI.icns in Resources */,
+				464D9B790C6A760B005A0582 /* CTBadge_1.pdf in Resources */,
+				464D9B7A0C6A760B005A0582 /* CTBadge_3.pdf in Resources */,
+				464D9B7B0C6A760B005A0582 /* CTBadge_4.pdf in Resources */,
+				464D9B7C0C6A760B005A0582 /* CTBadge_5.pdf in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -216,4 +263,6 @@
 				4DF73F67061E136200B0D9C9 /* AntiRSIView.m in Sources */,
 				E1F60361098592D800806C3B /* AntiRSILevelIndicator.m in Sources */,
+				464D9ADF0C6A622B005A0582 /* CTBadge.m in Sources */,
+				464D9AE10C6A622B005A0582 /* CTGradient.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
Index: trunk/Cocoa/AntiRSI/CTBadge/CTBadge.h
===================================================================
--- trunk/Cocoa/AntiRSI/CTBadge/CTBadge.h	(revision 332)
+++ trunk/Cocoa/AntiRSI/CTBadge/CTBadge.h	(revision 332)
@@ -0,0 +1,48 @@
+//
+//  CTBadge.h
+//  CTWidgets
+//
+//  Created by Chad Weider on 2/14/07.
+//  Copyright (c) 2007 Chad Weider.
+//  Some rights reserved: <http://creativecommons.org/licenses/by/2.5/>
+//
+//  Version: 1.5
+
+#import <Cocoa/Cocoa.h>
+#import "CTGradient.h"
+
+extern const float CTLargeBadgeSize;
+extern const float CTSmallBadgeSize;
+extern const float CTLargeLabelSize;
+extern const float CTSmallLabelSize;
+
+@interface CTBadge : NSObject
+  {
+  NSColor *badgeColor;
+  NSColor *labelColor;
+  }
+
++ (CTBadge *)systemBadge;																//Classic white on red badge
++ (CTBadge *)badgeWithColor:(NSColor *)badgeColor labelColor:(NSColor *)labelColor;		//Badge of any color scheme
+
+- (NSImage *)smallBadgeForValue:(unsigned)value;				   //Image to use during drag operations
+- (NSImage *)smallBadgeForString:(NSString *)string;
+- (NSImage *)largeBadgeForValue:(unsigned)value;				   //For dock icons, etc
+- (NSImage *)largeBadgeForString:(NSString *)string;
+- (NSImage *)badgeOfSize:(float)size forValue:(unsigned)value;	   //A badge of arbitrary size,
+- (NSImage *)badgeOfSize:(float)size forString:(NSString *)string; //	<size> is the size in pixels of the badge
+																   //	not counting the shadow effect
+																   //	(image returned will be larger than <size>)
+
+- (NSImage *)badgeOverlayImageForValue:(unsigned)value insetX:(float)dx y:(float)dy;		//Returns a transparent 128x128 image
+- (NSImage *)badgeOverlayImageForString:(NSString *)string insetX:(float)dx y:(float)dy;	//  with Large badge inset dx/dy from the upper right
+- (void)badgeApplicationDockIconWithValue:(unsigned)value insetX:(float)dx y:(float)dy;		//Badges the Application's icon with <value>
+- (void)badgeApplicationDockIconWithString:(NSString *)string insetX:(float)dx y:(float)dy; //	and puts it on the dock
+
+- (void)setBadgeColor:(NSColor *)theColor;					//Sets the color used on badge
+- (void)setLabelColor:(NSColor *)theColor;					//Sets the color of the label
+
+- (NSColor *)badgeColor;									//Color currently being used on the badge
+- (NSColor *)labelColor;									//Color currently being used on the label
+
+@end
Index: trunk/Cocoa/AntiRSI/CTBadge/CTBadge.m
===================================================================
--- trunk/Cocoa/AntiRSI/CTBadge/CTBadge.m	(revision 332)
+++ trunk/Cocoa/AntiRSI/CTBadge/CTBadge.m	(revision 332)
@@ -0,0 +1,311 @@
+//
+//  CTBadge.m
+//  CTWidgets
+//
+//  Created by Chad Weider on 2/14/07.
+//  Copyright (c) 2007 Chad Weider.
+//  Some rights reserved: <http://creativecommons.org/licenses/by/2.5/>
+//
+//  Version: 1.5
+
+#import "CTBadge.h"
+
+const float CTLargeBadgeSize = 46.;
+const float CTSmallBadgeSize = 23.;
+const float CTLargeLabelSize = 24.;
+const float CTSmallLabelSize = 11.;
+
+@interface CTBadge (Private)
+- (NSImage *)badgeMaskOfSize:(float)size length:(unsigned)length;			//return a badge with height of <size> to fit <length> characters
+- (NSAttributedString *)labelForString:(NSString *)string size:(unsigned)size;	//returns appropriately attributed label string (not autoreleased)
+- (CTGradient *)badgeGradient;												//gradient used to fill badge mask
+@end
+
+@implementation CTBadge
+
+- (id)init
+  {
+  self = [super init];
+  
+  if (self != nil)
+	{
+	badgeColor = nil;
+	labelColor = nil;
+	
+	[self setBadgeColor:[NSColor redColor]];
+	[self setLabelColor:[NSColor whiteColor]];
+	}
+  return self;
+  }
+
+- (void)dealloc
+  {
+  if(badgeColor != nil)
+	[badgeColor release];
+  if(labelColor != nil)
+    [labelColor release];
+  
+  [super dealloc];
+  }
+
++ (CTBadge *)systemBadge
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  return [newInstance autorelease];
+  }
+
++ (CTBadge *)badgeWithColor:(NSColor *)badgeColor labelColor:(NSColor *)labelColor;
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  [newInstance setBadgeColor:badgeColor];
+  [newInstance setLabelColor:labelColor];
+  
+  return [newInstance autorelease];
+  }
+#pragma mark -
+
+
+#pragma mark Appearance
+- (void)setBadgeColor:(NSColor *)theColor;
+  {
+  if(badgeColor != nil)
+	[badgeColor release];
+  
+  badgeColor = theColor;
+  [badgeColor retain];
+  }
+- (void)setLabelColor:(NSColor *)theColor;
+  {
+  if(labelColor != nil)
+	[labelColor release];
+  
+  labelColor = theColor;
+  [labelColor retain];
+  }
+
+- (NSColor *)badgeColor
+  {
+  return badgeColor;
+  }
+- (NSColor *)labelColor
+  {
+  return labelColor;
+  }
+#pragma mark -
+
+
+#pragma mark Drawing
+- (NSImage *)smallBadgeForValue:(unsigned)value		//does drawing in it's own special way
+  {
+  return [self badgeOfSize:CTSmallBadgeSize forValue:value];
+  }
+
+- (NSImage *)smallBadgeForString:(NSString *)string
+  {
+  return [self badgeOfSize:CTSmallBadgeSize forString:string];
+  }
+
+- (NSImage *)largeBadgeForValue:(unsigned)value
+  {
+  return [self badgeOfSize:CTLargeBadgeSize forValue:value];
+  }
+
+- (NSImage *)largeBadgeForString:(NSString *)string
+  {
+  return [self badgeOfSize:CTLargeBadgeSize forString:string];
+  }
+
+- (NSImage *)badgeOfSize:(float)size forValue:(unsigned)value
+  {
+  return [self badgeOfSize:(float)size forString:[NSString stringWithFormat:@"%u", value]];
+  }
+
+- (NSImage *)badgeOfSize:(float)size forString:(NSString *)string
+  {
+  float scaleFactor = 1;
+  
+  if(size <= 0)
+	[NSException raise:NSInvalidArgumentException format:@"%@ %@: size (%f) must be positive", [self class], NSStringFromSelector(_cmd), size];
+  else if(size <= CTSmallBadgeSize)
+	scaleFactor = size/CTSmallBadgeSize;
+  else
+	scaleFactor = size/CTLargeBadgeSize;
+  
+  //Label stuff  -----------------------------------------------
+  NSAttributedString *label;
+  NSSize labelSize;
+  
+  if(size <= CTSmallBadgeSize)
+	label = [self labelForString:string size:CTSmallLabelSize*scaleFactor];
+  else
+	label = [self labelForString:string size:CTLargeLabelSize*scaleFactor];
+  
+  labelSize = [label size];
+  
+  //Badge stuff  -----------------------------------------------
+  NSImage *badgeImage;	//this the image with the gradient fill
+  NSImage *badgeMask ;	//we nock out this mask from the gradient
+  
+  CTGradient *badgeGradient = [self badgeGradient];
+  
+  float shadowOpacity,
+		shadowOffset,
+        shadowBlurRadius;
+  
+  int angle;
+  
+  if(size <= CTSmallBadgeSize)
+	{
+	shadowOpacity    = .6;
+	shadowOffset     = floorf(1*scaleFactor);
+	shadowBlurRadius = ceilf(1*scaleFactor);
+	}
+  else
+	{
+	shadowOpacity    = .8;
+	shadowOffset     = ceilf(1*scaleFactor);
+	shadowBlurRadius = ceilf(2*scaleFactor);
+	}
+  
+  if ([label length] <= 3)	//Badges have different gradient angles
+	angle = -45;
+  else
+	angle = -30;
+  
+  badgeMask = [self badgeMaskOfSize:size length:[label length]];
+  
+  NSSize badgeSize = [badgeMask size];
+  NSPoint   origin = NSMakePoint(shadowBlurRadius, shadowBlurRadius+shadowOffset);
+  
+  badgeImage = [[NSImage alloc] initWithSize:NSMakeSize(badgeSize.width  + 2*shadowBlurRadius,											//sometimes it needs more
+														badgeSize.height + 2*shadowBlurRadius - shadowOffset + (size <= CTSmallBadgeSize))];	//space when small
+  
+  [badgeImage lockFocus];
+	[badgeGradient fillRect:NSMakeRect(origin.x, origin.y, floorf(badgeSize.width), floorf(badgeSize.height)) angle:angle];				//apply the gradient
+	[badgeMask compositeToPoint:origin operation: NSCompositeDestinationAtop];															//knock out the badge area
+	[label drawInRect:NSMakeRect(origin.x+floorf((badgeSize.width-labelSize.width)/2), origin.y+floorf((badgeSize.height-labelSize.height)/2), badgeSize.width, labelSize.height)];	//draw label in center
+  [badgeImage unlockFocus];
+  
+  
+  //Final stuff   -----------------------------------------------
+  NSImage *image = [[NSImage alloc] initWithSize:[badgeImage size]];
+  
+  [image lockFocus];
+	[NSGraphicsContext saveGraphicsState];
+	  NSShadow *theShadow = [[NSShadow alloc] init];
+	  [theShadow setShadowOffset: NSMakeSize(0,-shadowOffset)];
+	  [theShadow setShadowBlurRadius:shadowBlurRadius];
+	  [theShadow setShadowColor:[[NSColor blackColor] colorWithAlphaComponent:shadowOpacity]];
+	  [theShadow set];
+	  [theShadow release];
+	  [badgeImage compositeToPoint:NSZeroPoint operation:NSCompositeSourceOver];
+	[NSGraphicsContext restoreGraphicsState];
+  [image unlockFocus];
+  
+  [label release];
+  [badgeImage release];
+  
+  return [image autorelease];
+  }
+
+
+- (NSImage *)badgeOverlayImageForString:(NSString *)string insetX:(float)dx y:(float)dy;
+  {
+  NSImage *badgeImage = [self largeBadgeForString:string];
+  NSImage *overlayImage = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
+
+  //draw large icon in the upper right corner of the overlay image
+  [overlayImage lockFocus];
+  NSSize badgeSize = [badgeImage size];
+  [badgeImage compositeToPoint:NSMakePoint(128-dx-badgeSize.width,128-dy-badgeSize.height) operation:NSCompositeSourceOver];  
+  [overlayImage unlockFocus];
+
+  return [overlayImage autorelease];
+  }
+
+- (void)badgeApplicationDockIconWithString:(NSString *)string insetX:(float)dx y:(float)dy;
+  {
+  NSImage *appIcon      = [NSImage imageNamed:@"NSApplicationIcon"];
+  NSImage *badgeOverlay = [self badgeOverlayImageForString:string insetX:dx y:dy];
+  
+  //Put the appIcon underneath the badgeOverlay
+  [badgeOverlay lockFocus];
+	[appIcon compositeToPoint:NSZeroPoint operation:NSCompositeDestinationOver];
+  [badgeOverlay unlockFocus];
+  
+  [NSApp setApplicationIconImage:badgeOverlay];
+  }
+
+- (NSImage *)badgeOverlayImageForValue:(unsigned)value insetX:(float)dx y:(float)dy
+  {
+  return [self badgeOverlayImageForString:[NSString stringWithFormat:@"%u", value] insetX:dx y:dy];
+  }
+
+- (void)badgeApplicationDockIconWithValue:(unsigned)value insetX:(float)dx y:(float)dy
+  {
+  [self badgeApplicationDockIconWithString:[NSString stringWithFormat:@"%u", value] insetX:dx y:dy];
+  }
+#pragma mark -
+
+
+#pragma mark Misc.
+- (CTGradient *)badgeGradient
+  {
+  CTGradient *aGradient = [CTGradient gradientWithBeginningColor:[self badgeColor] endingColor:[[self badgeColor] shadowWithLevel:(1./3.)]];
+  
+  aGradient = [aGradient addColorStop:[self badgeColor] atPosition:(1./3.)];
+  
+  return aGradient;
+  }
+
+- (NSAttributedString *)labelForString:(NSString *)label size:(unsigned)size
+  {
+  //set Attributes to use on String  ---------------------------
+  NSFont *labelFont;
+  
+  if(size <= CTSmallLabelSize)
+	labelFont = [NSFont boldSystemFontOfSize:size];
+  else
+	labelFont = [NSFont fontWithName:@"Helvetica-Bold" size:size];
+  
+  NSMutableParagraphStyle *pStyle = [[NSMutableParagraphStyle alloc] init];[pStyle setAlignment:NSCenterTextAlignment];
+  NSDictionary *attributes = [[NSDictionary alloc] initWithObjectsAndKeys:[self labelColor], NSForegroundColorAttributeName,
+																		  labelFont        , NSFontAttributeName           , nil];
+  [pStyle release];
+  
+  //Label stuff
+  if([label length] >= 6)
+    label = [NSString stringWithUTF8String:"\xe2\x88\x9e"];
+  
+  NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:label attributes:attributes];
+  [attributes release];
+  
+  return attributedString;
+  }
+
+- (NSImage *)badgeMaskOfSize:(float)size length:(unsigned)length;
+  {
+  NSImage *badgeMask;
+  
+  if(length <=2)
+	badgeMask = [NSImage imageNamed:@"CTBadge_1.pdf"];
+  else if(length <=3)
+	badgeMask = [NSImage imageNamed:@"CTBadge_3.pdf"];
+  else if(length <=4)
+	badgeMask = [NSImage imageNamed:@"CTBadge_4.pdf"];
+  else
+	badgeMask = [NSImage imageNamed:@"CTBadge_5.pdf"];
+  
+  if(size > 0 && size != [badgeMask size].height)
+	{
+	[badgeMask setName:nil];
+	[badgeMask setScalesWhenResized:YES];
+	[badgeMask setSize:NSMakeSize([badgeMask size].width*(size/[badgeMask size].height), size)];
+	}
+  
+  return badgeMask;
+  }
+
+@end
Index: trunk/Cocoa/AntiRSI/CTBadge/CTGradient.h
===================================================================
--- trunk/Cocoa/AntiRSI/CTBadge/CTGradient.h	(revision 332)
+++ trunk/Cocoa/AntiRSI/CTBadge/CTGradient.h	(revision 332)
@@ -0,0 +1,72 @@
+//
+//  CTGradient.h
+//
+//  Created by Chad Weider on 2/14/07.
+//  Copyright (c) 2007 Chad Weider.
+//  Some rights reserved: <http://creativecommons.org/licenses/by/2.5/>
+//
+//  Version: 1.6
+
+#import <Cocoa/Cocoa.h>
+
+typedef struct _CTGradientElement 
+	{
+	float red, green, blue, alpha;
+	float position;
+	
+	struct _CTGradientElement *nextElement;
+	} CTGradientElement;
+
+typedef enum  _CTBlendingMode
+	{
+	CTLinearBlendingMode,
+	CTChromaticBlendingMode,
+	CTInverseChromaticBlendingMode
+	} CTGradientBlendingMode;
+
+
+@interface CTGradient : NSObject <NSCopying, NSCoding>
+	{
+	CTGradientElement* elementList;
+	CTGradientBlendingMode blendingMode;
+	
+	CGFunctionRef gradientFunction;
+	}
+
++ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end;
+
++ (id)aquaSelectedGradient;
++ (id)aquaNormalGradient;
++ (id)aquaPressedGradient;
+
++ (id)unifiedSelectedGradient;
++ (id)unifiedNormalGradient;
++ (id)unifiedPressedGradient;
++ (id)unifiedDarkGradient;
+
++ (id)sourceListSelectedGradient;
++ (id)sourceListUnselectedGradient;
+
++ (id)rainbowGradient;
++ (id)hydrogenSpectrumGradient;
+
+- (CTGradient *)gradientWithAlphaComponent:(float)alpha;
+
+- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position;	//positions given relative to [0,1]
+- (CTGradient *)removeColorStopAtIndex:(unsigned)index;
+- (CTGradient *)removeColorStopAtPosition:(float)position;
+
+- (CTGradientBlendingMode)blendingMode;
+- (NSColor *)colorStopAtIndex:(unsigned)index;
+- (NSColor *)colorAtPosition:(float)position;
+
+
+- (void)drawSwatchInRect:(NSRect)rect;
+- (void)fillRect:(NSRect)rect angle:(float)angle;					//fills rect with axial gradient
+																	//	angle in degrees
+- (void)radialFillRect:(NSRect)rect;								//fills rect with radial gradient
+																	//  gradient from center outwards
+- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle;
+- (void)radialFillBezierPath:(NSBezierPath *)path;
+
+@end
Index: trunk/Cocoa/AntiRSI/CTBadge/CTGradient.m
===================================================================
--- trunk/Cocoa/AntiRSI/CTBadge/CTGradient.m	(revision 332)
+++ trunk/Cocoa/AntiRSI/CTBadge/CTGradient.m	(revision 332)
@@ -0,0 +1,1253 @@
+//
+//  CTGradient.m
+//
+//  Created by Chad Weider on 2/14/07.
+//  Copyright (c) 2007 Chad Weider.
+//  Some rights reserved: <http://creativecommons.org/licenses/by/2.5/>
+//
+//  Version: 1.6
+
+#import "CTGradient.h"
+
+@interface CTGradient (Private)
+- (void)_commonInit;
+- (void)setBlendingMode:(CTGradientBlendingMode)mode;
+- (void)addElement:(CTGradientElement*)newElement;
+
+- (CTGradientElement *)elementAtIndex:(unsigned)index;
+
+- (CTGradientElement)removeElementAtIndex:(unsigned)index;
+- (CTGradientElement)removeElementAtPosition:(float)position;
+@end
+
+//C Fuctions for color blending
+static void linearEvaluation   (void *info, const float *in, float *out);
+static void chromaticEvaluation(void *info, const float *in, float *out);
+static void inverseChromaticEvaluation(void *info, const float *in, float *out);
+static void transformRGB_HSV(float *components);
+static void transformHSV_RGB(float *components);
+static void resolveHSV(float *color1, float *color2);
+
+
+@implementation CTGradient
+/////////////////////////////////////Initialization Type Stuff
+- (id)init
+  {
+  self = [super init];
+  
+  if (self != nil)
+	{
+	[self _commonInit];
+	[self setBlendingMode:CTLinearBlendingMode];
+	}
+  return self;
+  }
+
+- (void)_commonInit
+  {
+  elementList = nil;
+  }
+
+- (void)dealloc
+  {
+  CGFunctionRelease(gradientFunction);
+  
+  CTGradientElement *elementToRemove = elementList;
+  while(elementList != nil)
+	{
+	elementToRemove = elementList;
+	elementList = elementList->nextElement;
+	free(elementToRemove);
+	}
+  
+  [super dealloc];
+  }
+
+- (id)copyWithZone:(NSZone *)zone
+  {
+  CTGradient *copy = [[[self class] allocWithZone:zone] init];
+  
+  //now just copy my elementlist
+  CTGradientElement *currentElement = elementList;
+  while(currentElement != nil)
+	{
+	[copy addElement:currentElement];
+	currentElement = currentElement->nextElement;
+	}
+  
+  [copy setBlendingMode:blendingMode];
+  
+  return copy;
+  }
+
+- (void)encodeWithCoder:(NSCoder *)coder
+  {
+  if([coder allowsKeyedCoding])
+	{
+	unsigned count = 0;
+	CTGradientElement *currentElement = elementList;
+	while(currentElement != nil)
+		{
+		[coder encodeValueOfObjCType:@encode(float) at:&(currentElement->red)];
+		[coder encodeValueOfObjCType:@encode(float) at:&(currentElement->green)];
+		[coder encodeValueOfObjCType:@encode(float) at:&(currentElement->blue)];
+		[coder encodeValueOfObjCType:@encode(float) at:&(currentElement->alpha)];
+		[coder encodeValueOfObjCType:@encode(float) at:&(currentElement->position)];
+		
+		count++;
+		currentElement = currentElement->nextElement;
+		}
+	[coder encodeInt:count forKey:@"CTGradientElementCount"];
+	[coder encodeInt:blendingMode forKey:@"CTGradientBlendingMode"];
+	}
+  else
+	[NSException raise:NSInvalidArchiveOperationException format:@"Only supports NSKeyedArchiver coders"];
+  }
+
+- (id)initWithCoder:(NSCoder *)coder
+  {
+  [self _commonInit];
+  
+  [self setBlendingMode:[coder decodeIntForKey:@"CTGradientBlendingMode"]];
+  unsigned count = [coder decodeIntForKey:@"CTGradientElementCount"];
+  
+  while(count != 0)
+	{
+    CTGradientElement newElement;
+	
+	[coder decodeValueOfObjCType:@encode(float) at:&(newElement.red)];
+	[coder decodeValueOfObjCType:@encode(float) at:&(newElement.green)];
+	[coder decodeValueOfObjCType:@encode(float) at:&(newElement.blue)];
+	[coder decodeValueOfObjCType:@encode(float) at:&(newElement.alpha)];
+	[coder decodeValueOfObjCType:@encode(float) at:&(newElement.position)];
+	
+	count--;
+	[self addElement:&newElement];
+	}
+  return self;
+  }
+#pragma mark -
+
+
+
+#pragma mark Creation
++ (id)gradientWithBeginningColor:(NSColor *)begin endingColor:(NSColor *)end
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  CTGradientElement color2;
+  
+  [[begin colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color1.red
+															   green:&color1.green
+																blue:&color1.blue
+															   alpha:&color1.alpha];
+  
+  [[end   colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&color2.red
+															   green:&color2.green
+																blue:&color2.blue
+															   alpha:&color2.alpha];  
+  color1.position = 0;
+  color2.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)aquaSelectedGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red   = 0.58;
+  color1.green = 0.86;
+  color1.blue  = 0.98;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red   = 0.42;
+  color2.green = 0.68;
+  color2.blue  = 0.90;
+  color2.alpha = 1.00;
+  color2.position = 11.5/23;
+  
+  CTGradientElement color3;
+  color3.red   = 0.64;
+  color3.green = 0.80;
+  color3.blue  = 0.94;
+  color3.alpha = 1.00;
+  color3.position = 11.5/23;
+  
+  CTGradientElement color4;
+  color4.red   = 0.56;
+  color4.green = 0.70;
+  color4.blue  = 0.90;
+  color4.alpha = 1.00;
+  color4.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  [newInstance addElement:&color3];
+  [newInstance addElement:&color4];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)aquaNormalGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red = color1.green = color1.blue  = 0.95;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red = color2.green = color2.blue  = 0.83;
+  color2.alpha = 1.00;
+  color2.position = 11.5/23;
+  
+  CTGradientElement color3;
+  color3.red = color3.green = color3.blue  = 0.95;
+  color3.alpha = 1.00;
+  color3.position = 11.5/23;
+  
+  CTGradientElement color4;
+  color4.red = color4.green = color4.blue  = 0.92;
+  color4.alpha = 1.00;
+  color4.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  [newInstance addElement:&color3];
+  [newInstance addElement:&color4];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)aquaPressedGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red = color1.green = color1.blue  = 0.80;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red = color2.green = color2.blue  = 0.64;
+  color2.alpha = 1.00;
+  color2.position = 11.5/23;
+  
+  CTGradientElement color3;
+  color3.red = color3.green = color3.blue  = 0.80;
+  color3.alpha = 1.00;
+  color3.position = 11.5/23;
+  
+  CTGradientElement color4;
+  color4.red = color4.green = color4.blue  = 0.77;
+  color4.alpha = 1.00;
+  color4.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  [newInstance addElement:&color3];
+  [newInstance addElement:&color4];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)unifiedSelectedGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red = color1.green = color1.blue  = 0.85;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red = color2.green = color2.blue  = 0.95;
+  color2.alpha = 1.00;
+  color2.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)unifiedNormalGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red = color1.green = color1.blue  = 0.75;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red = color2.green = color2.blue  = 0.90;
+  color2.alpha = 1.00;
+  color2.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)unifiedPressedGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red = color1.green = color1.blue  = 0.60;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red = color2.green = color2.blue  = 0.75;
+  color2.alpha = 1.00;
+  color2.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)unifiedDarkGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red = color1.green = color1.blue  = 0.68;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red = color2.green = color2.blue  = 0.83;
+  color2.alpha = 1.00;
+  color2.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)sourceListSelectedGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red   = 0.06;
+  color1.green = 0.37;
+  color1.blue  = 0.85;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red   = 0.30;
+  color2.green = 0.60;
+  color2.blue  = 0.92;
+  color2.alpha = 1.00;
+  color2.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)sourceListUnselectedGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red   = 0.43;
+  color1.green = 0.43;
+  color1.blue  = 0.43;
+  color1.alpha = 1.00;
+  color1.position = 0;
+  
+  CTGradientElement color2;
+  color2.red   = 0.60;
+  color2.green = 0.60;
+  color2.blue  = 0.60;
+  color2.alpha = 1.00;
+  color2.position = 1;
+  
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)rainbowGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement color1;
+  color1.red   = 1.00;
+  color1.green = 0.00;
+  color1.blue  = 0.00;
+  color1.alpha = 1.00;
+  color1.position = 0.0;
+  
+  CTGradientElement color2;
+  color2.red   = 0.54;
+  color2.green = 0.00;
+  color2.blue  = 1.00;
+  color2.alpha = 1.00;
+  color2.position = 1.0;
+    
+  [newInstance addElement:&color1];
+  [newInstance addElement:&color2];
+  
+  [newInstance setBlendingMode:CTChromaticBlendingMode];
+  
+  return [newInstance autorelease];
+  }
+
++ (id)hydrogenSpectrumGradient
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  struct {float hue; float position; float width;} colorBands[4];
+  
+  colorBands[0].hue = 22;
+  colorBands[0].position = .145;
+  colorBands[0].width = .01;
+  
+  colorBands[1].hue = 200;
+  colorBands[1].position = .71;
+  colorBands[1].width = .008;
+  
+  colorBands[2].hue = 253;
+  colorBands[2].position = .885;
+  colorBands[2].width = .005;
+  
+  colorBands[3].hue = 275;
+  colorBands[3].position = .965;
+  colorBands[3].width = .003;
+  
+  int i;
+  /////////////////////////////
+  for(i = 0; i < 4; i++)
+	{	
+	float color[4];
+	color[0] = colorBands[i].hue - 180*colorBands[i].width;
+	color[1] = 1;
+	color[2] = 0.001;
+	color[3] = 1;
+	transformHSV_RGB(color);
+	CTGradientElement fadeIn;
+	fadeIn.red   = color[0];
+	fadeIn.green = color[1];
+	fadeIn.blue  = color[2];
+	fadeIn.alpha = color[3];
+	fadeIn.position = colorBands[i].position - colorBands[i].width;
+	
+	
+	color[0] = colorBands[i].hue;
+	color[1] = 1;
+	color[2] = 1;
+	color[3] = 1;
+	transformHSV_RGB(color);
+	CTGradientElement band;
+	band.red   = color[0];
+	band.green = color[1];
+	band.blue  = color[2];
+	band.alpha = color[3];
+	band.position = colorBands[i].position;
+	
+	color[0] = colorBands[i].hue + 180*colorBands[i].width;
+	color[1] = 1;
+	color[2] = 0.001;
+	color[3] = 1;
+	transformHSV_RGB(color);
+	CTGradientElement fadeOut;
+	fadeOut.red   = color[0];
+	fadeOut.green = color[1];
+	fadeOut.blue  = color[2];
+	fadeOut.alpha = color[3];
+	fadeOut.position = colorBands[i].position + colorBands[i].width;
+	
+	
+	[newInstance addElement:&fadeIn];
+	[newInstance addElement:&band];
+	[newInstance addElement:&fadeOut];
+	}
+  
+  [newInstance setBlendingMode:CTChromaticBlendingMode];
+  
+  return [newInstance autorelease];
+  }
+
+#pragma mark -
+
+
+
+#pragma mark Modification
+- (CTGradient *)gradientWithAlphaComponent:(float)alpha
+  {
+  id newInstance = [[[self class] alloc] init];
+  
+  CTGradientElement *curElement = elementList;
+  CTGradientElement tempElement;
+
+  while(curElement != nil)
+	{
+	tempElement = *curElement;
+	tempElement.alpha = alpha;
+	[newInstance addElement:&tempElement];
+	
+	curElement = curElement->nextElement;
+	}
+  
+  return [newInstance autorelease];
+  }
+
+- (CTGradient *)gradientWithBlendingMode:(CTGradientBlendingMode)mode
+  {
+  CTGradient *newGradient = [self copy];  
+  
+  [newGradient setBlendingMode:mode];
+  
+  return [newGradient autorelease];
+  }
+
+
+//Adds a color stop with <color> at <position> in elementList
+//(if two elements are at the same position then added imediatly after the one that was there already)
+- (CTGradient *)addColorStop:(NSColor *)color atPosition:(float)position
+  {
+  CTGradient *newGradient = [self copy];
+  CTGradientElement newGradientElement;
+  
+  //put the components of color into the newGradientElement - must make sure it is a RGB color (not Gray or CMYK) 
+  [[color colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&newGradientElement.red
+															   green:&newGradientElement.green
+																blue:&newGradientElement.blue
+															   alpha:&newGradientElement.alpha];
+  newGradientElement.position = position;
+  
+  //Pass it off to addElement to take care of adding it to the elementList
+  [newGradient addElement:&newGradientElement];
+  
+  return [newGradient autorelease];
+  }
+
+
+//Removes the color stop at <position> from elementList
+- (CTGradient *)removeColorStopAtPosition:(float)position
+  {
+  CTGradient *newGradient = [self copy];
+  CTGradientElement removedElement = [newGradient removeElementAtPosition:position];
+  
+  if(isnan(removedElement.position))
+	[NSException raise:NSRangeException format:@"-[%@ removeColorStopAtPosition:]: no such colorStop at position (%f)", [self class], position];
+  
+  return [newGradient autorelease];
+  }
+
+- (CTGradient *)removeColorStopAtIndex:(unsigned)index
+  {
+  CTGradient *newGradient = [self copy];
+  CTGradientElement removedElement = [newGradient removeElementAtIndex:index];
+  
+  if(isnan(removedElement.position))
+	[NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
+  
+  return [newGradient autorelease];
+  }
+#pragma mark -
+
+
+
+#pragma mark Information
+- (CTGradientBlendingMode)blendingMode
+  {
+  return blendingMode;
+  }
+
+//Returns color at <position> in gradient
+- (NSColor *)colorStopAtIndex:(unsigned)index
+  {
+  CTGradientElement *element = [self elementAtIndex:index];
+  
+  if(element != nil)
+	return [NSColor colorWithCalibratedRed:element->red 
+									 green:element->green
+									  blue:element->blue
+									 alpha:element->alpha];
+  
+  [NSException raise:NSRangeException format:@"-[%@ removeColorStopAtIndex:]: index (%i) beyond bounds", [self class], index];
+  
+  return nil;
+  }
+
+- (NSColor *)colorAtPosition:(float)position
+  {
+  float components[4];
+  
+  switch(blendingMode)
+	{
+	case CTLinearBlendingMode:
+		 linearEvaluation(&elementList, &position, components);				break;
+	case CTChromaticBlendingMode:
+		 chromaticEvaluation(&elementList, &position, components);			break;
+	case CTInverseChromaticBlendingMode:
+		 inverseChromaticEvaluation(&elementList, &position, components);	break;
+	}
+  
+  
+  return [NSColor colorWithCalibratedRed:components[0]
+								   green:components[1]
+								    blue:components[2]
+								   alpha:components[3]];
+  }
+#pragma mark -
+
+
+
+#pragma mark Drawing
+- (void)drawSwatchInRect:(NSRect)rect
+  {
+  [self fillRect:rect angle:45];
+  }
+
+- (void)fillRect:(NSRect)rect angle:(float)angle
+  {
+  //First Calculate where the beginning and ending points should be
+  CGPoint startPoint;
+  CGPoint endPoint;
+  
+  if(angle == 0)		//screw the calculations - we know the answer
+  	{
+  	startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));	//right of rect
+  	endPoint   = CGPointMake(NSMaxX(rect), NSMinY(rect));	//left  of rect
+  	}
+  else if(angle == 90)	//same as above
+  	{
+  	startPoint = CGPointMake(NSMinX(rect), NSMinY(rect));	//bottom of rect
+  	endPoint   = CGPointMake(NSMinX(rect), NSMaxY(rect));	//top    of rect
+  	}
+  else						//ok, we'll do the calculations now 
+  	{
+  	float x,y;
+  	float sina, cosa, tana;
+  	
+  	float length;
+  	float deltax,
+  		  deltay;
+	
+  	float rangle = angle * pi/180;	//convert the angle to radians
+	
+  	if(fabsf(tan(rangle))<=1)	//for range [-45,45], [135,225]
+		{
+		x = NSWidth(rect);
+		y = NSHeight(rect);
+		
+		sina = sin(rangle);
+		cosa = cos(rangle);
+		tana = tan(rangle);
+		
+		length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
+		
+		deltax = length*cosa/2;
+		deltay = length*sina/2;
+		}
+	else						//for range [45,135], [225,315]
+		{
+		x = NSHeight(rect);
+		y = NSWidth(rect);
+		
+		sina = sin(rangle - 90*pi/180);
+		cosa = cos(rangle - 90*pi/180);
+		tana = tan(rangle - 90*pi/180);
+		
+		length = x/fabsf(cosa)+(y-x*fabsf(tana))*fabsf(sina);
+		
+		deltax =-length*sina/2;
+		deltay = length*cosa/2;
+		}
+  
+	startPoint = CGPointMake(NSMidX(rect)-deltax, NSMidY(rect)-deltay);
+	endPoint   = CGPointMake(NSMidX(rect)+deltax, NSMidY(rect)+deltay);
+	}
+  
+  //Calls to CoreGraphics
+  CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+  CGContextSaveGState(currentContext);
+	  #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+		CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+	  #else
+		CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
+	  #endif
+	  CGShadingRef myCGShading = CGShadingCreateAxial(colorspace, startPoint, endPoint, gradientFunction, false, false);
+	  
+	  CGContextClipToRect (currentContext, *(CGRect *)&rect);	//This is where the action happens
+	  CGContextDrawShading(currentContext, myCGShading);
+	  
+	  CGShadingRelease(myCGShading);
+	  CGColorSpaceRelease(colorspace );
+  CGContextRestoreGState(currentContext);
+  }
+
+- (void)radialFillRect:(NSRect)rect
+  {
+  CGPoint startPoint , endPoint;
+  float startRadius, endRadius;
+  float scalex, scaley, transx, transy;
+  
+  startPoint = endPoint = CGPointMake(NSMidX(rect), NSMidY(rect));
+  
+  startRadius = -1;
+  if(NSHeight(rect)>NSWidth(rect))
+	{
+	scalex = NSWidth(rect)/NSHeight(rect);
+	transx = (NSHeight(rect)-NSWidth(rect))/2;
+	scaley = 1;
+	transy = 1;
+	endRadius = NSHeight(rect)/2;
+	}
+  else
+	{
+	scalex = 1;
+	transx = 1;
+	scaley = NSHeight(rect)/NSWidth(rect);
+	transy = (NSWidth(rect)-NSHeight(rect))/2;
+	endRadius = NSWidth(rect)/2;
+	}
+  
+  //Calls to CoreGraphics
+  CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
+  CGContextSaveGState(currentContext);
+	  #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+		CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+	  #else
+		CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
+	  #endif
+  	  CGShadingRef myCGShading = CGShadingCreateRadial(colorspace, startPoint, startRadius, endPoint, endRadius, gradientFunction, true, true);
+
+	  CGContextClipToRect  (currentContext, *(CGRect *)&rect);
+	  CGContextScaleCTM    (currentContext, scalex, scaley);
+	  CGContextTranslateCTM(currentContext, transx, transy);
+	  CGContextDrawShading (currentContext, myCGShading);		//This is where the action happens
+	  
+	  CGShadingRelease(myCGShading);
+	  CGColorSpaceRelease(colorspace);
+  CGContextRestoreGState(currentContext);
+  }
+
+- (void)fillBezierPath:(NSBezierPath *)path angle:(float)angle
+  {
+  NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+  [currentContext saveGraphicsState];
+	NSAffineTransform *transform = [[NSAffineTransform alloc] init];
+	
+	[transform rotateByDegrees:-angle];
+	[path transformUsingAffineTransform:transform];
+	[transform invert];
+	[transform concat];
+	
+	[path addClip];
+	[self fillRect:[path bounds] angle:0];
+	[path transformUsingAffineTransform:transform];
+	[transform release];
+  [currentContext restoreGraphicsState];
+  }
+- (void)radialFillBezierPath:(NSBezierPath *)path
+  {
+  NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+  [currentContext saveGraphicsState];
+	[path addClip];
+	[self radialFillRect:[path bounds]];
+  [currentContext restoreGraphicsState];
+  }
+#pragma mark -
+
+
+
+#pragma mark Private Methods
+- (void)setBlendingMode:(CTGradientBlendingMode)mode;
+  {
+  blendingMode = mode;
+  
+  //Choose what blending function to use
+  void *evaluationFunction;
+  switch(blendingMode)
+	{
+	case CTLinearBlendingMode:
+		 evaluationFunction = &linearEvaluation;			break;
+	case CTChromaticBlendingMode:
+		 evaluationFunction = &chromaticEvaluation;			break;
+	case CTInverseChromaticBlendingMode:
+		 evaluationFunction = &inverseChromaticEvaluation;	break;
+	}
+  
+  //replace the current CoreGraphics Function with new one
+  if(gradientFunction != NULL)
+	  CGFunctionRelease(gradientFunction);
+    
+  CGFunctionCallbacks evaluationCallbackInfo = {0 , evaluationFunction, NULL};	//Version, evaluator function, cleanup function
+  
+  static const float input_value_range   [2] = { 0, 1 };						//range  for the evaluator input
+  static const float output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };		//ranges for the evaluator output (4 returned values)
+  
+  gradientFunction = CGFunctionCreate(&elementList,					//the two transition colors
+									  1, input_value_range  ,		//number of inputs (just fraction of progression)
+									  4, output_value_ranges,		//number of outputs (4 - RGBa)
+									  &evaluationCallbackInfo);		//info for using the evaluator function
+  }
+
+- (void)addElement:(CTGradientElement *)newElement
+{
+  if(elementList == nil || newElement->position < elementList->position)	//inserting at beginning of list
+	{
+	CTGradientElement *tmpNext = elementList;
+	elementList = malloc(sizeof(CTGradientElement));
+	*elementList = *newElement;
+	elementList->nextElement = tmpNext;
+	}
+  else																		//inserting somewhere inside list
+	{
+	CTGradientElement *curElement = elementList;
+	
+	while(curElement->nextElement != nil && !((curElement->position <= newElement->position) && (newElement->position < curElement->nextElement->position)))
+		{
+		curElement = curElement->nextElement;
+		}
+	
+	CTGradientElement *tmpNext = curElement->nextElement;
+	curElement->nextElement = malloc(sizeof(CTGradientElement));
+	*(curElement->nextElement) = *newElement;
+	curElement->nextElement->nextElement = tmpNext;
+	}
+  }
+
+- (CTGradientElement)removeElementAtIndex:(unsigned)index
+  {
+  CTGradientElement removedElement;
+  
+  if(elementList != nil)
+	{
+	if(index == 0)
+		{
+		CTGradientElement *tmpNext = elementList;
+		elementList = elementList->nextElement;
+		
+		removedElement = *tmpNext;
+		free(tmpNext);
+		
+		return removedElement;
+		}
+	
+	unsigned count = 1;		//we want to start one ahead
+	CTGradientElement *currentElement = elementList;
+	while(currentElement->nextElement != nil)
+		{
+		if(count == index)
+			{
+			CTGradientElement *tmpNext  = currentElement->nextElement;
+			currentElement->nextElement = currentElement->nextElement->nextElement;
+			
+			removedElement = *tmpNext;
+			free(tmpNext);
+
+			return removedElement;
+			}
+
+		count++;
+		currentElement = currentElement->nextElement;
+		}
+	}
+  
+  //element is not found, return empty element
+  removedElement.red   = 0.0;
+  removedElement.green = 0.0;
+  removedElement.blue  = 0.0;
+  removedElement.alpha = 0.0;
+  removedElement.position = NAN;
+  removedElement.nextElement = nil;
+  
+  return removedElement;
+  }
+
+- (CTGradientElement)removeElementAtPosition:(float)position
+  {
+  CTGradientElement removedElement;
+  
+  if(elementList != nil)
+	{
+	if(elementList->position == position)
+		{
+		CTGradientElement *tmpNext = elementList;
+		elementList = elementList->nextElement;
+		
+		removedElement = *tmpNext;
+		free(tmpNext);
+		
+		return removedElement;
+		}
+	else
+		{
+		CTGradientElement *curElement = elementList;
+		while(curElement->nextElement != nil)
+			{
+			if(curElement->nextElement->position == position)
+				{
+				CTGradientElement *tmpNext = curElement->nextElement;
+				curElement->nextElement = curElement->nextElement->nextElement;
+				
+				removedElement = *tmpNext;
+				free(tmpNext);
+
+				return removedElement;
+				}
+			}
+		}
+	}
+  
+  //element is not found, return empty element
+  removedElement.red   = 0.0;
+  removedElement.green = 0.0;
+  removedElement.blue  = 0.0;
+  removedElement.alpha = 0.0;
+  removedElement.position = NAN;
+  removedElement.nextElement = nil;
+  
+  return removedElement;
+  }
+
+
+- (CTGradientElement *)elementAtIndex:(unsigned)index;			
+  {
+  unsigned count = 0;
+  CTGradientElement *currentElement = elementList;
+  
+  while(currentElement != nil)
+	{
+	if(count == index)
+		return currentElement;
+	
+	count++;
+	currentElement = currentElement->nextElement;
+	}
+  
+  return nil;
+  }
+#pragma mark -
+
+
+
+#pragma mark Core Graphics
+//////////////////////////////////////Blending Functions/////////////////////////////////////
+void linearEvaluation (void *info, const float *in, float *out)
+  {
+  float position = *in;
+  
+  if(*(CTGradientElement **)info == nil)	//if elementList is empty return clear color
+	{
+	out[0] = out[1] = out[2] = out[3] = 1;
+	return;
+	}
+  
+  //This grabs the first two colors in the sequence
+  CTGradientElement *color1 = *(CTGradientElement **)info;
+  CTGradientElement *color2 = color1->nextElement;
+  
+  //make sure first color and second color are on other sides of position
+  while(color2 != nil && color2->position < position)
+  	{
+  	color1 = color2;
+  	color2 = color1->nextElement;
+  	}
+  //if we don't have another color then make next color the same color
+  if(color2 == nil)
+    {
+	color2 = color1;
+    }
+  
+  //----------FailSafe settings----------
+  //color1->red   = 1; color2->red   = 0;
+  //color1->green = 1; color2->green = 0;
+  //color1->blue  = 1; color2->blue  = 0;
+  //color1->alpha = 1; color2->alpha = 1;
+  //color1->position = .5;
+  //color2->position = .5;
+  //-------------------------------------
+  
+  if(position <= color1->position)			//Make all below color color1's position equal to color1
+  	{
+  	out[0] = color1->red; 
+  	out[1] = color1->green;
+  	out[2] = color1->blue;
+  	out[3] = color1->alpha;
+  	}
+  else if (position >= color2->position)	//Make all above color color2's position equal to color2
+  	{
+  	out[0] = color2->red; 
+  	out[1] = color2->green;
+  	out[2] = color2->blue;
+  	out[3] = color2->alpha;
+  	}
+  else										//Interpolate color at postions between color1 and color1
+  	{
+  	//adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position 
+	position = (position-color1->position)/(color2->position - color1->position);
+	
+	out[0] = (color2->red   - color1->red  )*position + color1->red; 
+  	out[1] = (color2->green - color1->green)*position + color1->green;
+  	out[2] = (color2->blue  - color1->blue )*position + color1->blue;
+  	out[3] = (color2->alpha - color1->alpha)*position + color1->alpha;
+  	}
+  }
+
+
+
+
+//Chromatic Evaluation - 
+//	This blends colors by their Hue, Saturation, and Value(Brightness) right now I just 
+//	transform the RGB values stored in the CTGradientElements to HSB, in the future I may
+//	streamline it to avoid transforming in and out of HSB colorspace *for later*
+//
+//	For the chromatic blend we shift the hue of color1 to meet the hue of color2. To do
+//	this we will add to the hue's angle (if we subtract we'll be doing the inverse
+//	chromatic...scroll down more for that). All we need to do is keep adding to the hue
+//  until we wrap around the colorwheel and get to color2.
+void chromaticEvaluation(void *info, const float *in, float *out)
+  {
+  float position = *in;
+  
+  if(*(CTGradientElement **)info == nil)	//if elementList is empty return clear color
+	{
+	out[0] = out[1] = out[2] = out[3] = 1;
+	return;
+	}
+  
+  //This grabs the first two colors in the sequence
+  CTGradientElement *color1 = *(CTGradientElement **)info;
+  CTGradientElement *color2 = color1->nextElement;
+  
+  float c1[4];
+  float c2[4];
+    
+  //make sure first color and second color are on other sides of position
+  while(color2 != nil && color2->position < position)
+  	{
+  	color1 = color2;
+  	color2 = color1->nextElement;
+  	}
+  //if we don't have another color then make next color the same color
+  if(color2 == nil)
+    {
+	color2 = color1;
+    }
+  
+  
+  c1[0] = color1->red; 
+  c1[1] = color1->green;
+  c1[2] = color1->blue;
+  c1[3] = color1->alpha;
+  
+  c2[0] = color2->red; 
+  c2[1] = color2->green;
+  c2[2] = color2->blue;
+  c2[3] = color2->alpha;
+  
+  transformRGB_HSV(c1);
+  transformRGB_HSV(c2);
+  resolveHSV(c1,c2);
+  
+  if(c1[0] > c2[0]) //if color1's hue is higher than color2's hue then 
+	 c2[0] += 360;	//	we need to move c2 one revolution around the wheel
+  
+  
+  if(position <= color1->position)			//Make all below color color1's position equal to color1
+  	{
+  	out[0] = c1[0]; 
+  	out[1] = c1[1];
+  	out[2] = c1[2];
+  	out[3] = c1[3];
+  	}
+  else if (position >= color2->position)	//Make all above color color2's position equal to color2
+  	{
+  	out[0] = c2[0]; 
+  	out[1] = c2[1];
+  	out[2] = c2[2];
+  	out[3] = c2[3];
+  	}
+  else										//Interpolate color at postions between color1 and color1
+  	{
+  	//adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position 
+	position = (position-color1->position)/(color2->position - color1->position);
+	
+	out[0] = (c2[0] - c1[0])*position + c1[0]; 
+  	out[1] = (c2[1] - c1[1])*position + c1[1];
+  	out[2] = (c2[2] - c1[2])*position + c1[2];
+  	out[3] = (c2[3] - c1[3])*position + c1[3];
+  	}
+    
+  transformHSV_RGB(out);
+  }
+
+
+
+//Inverse Chromatic Evaluation - 
+//	Inverse Chromatic is about the same story as Chromatic Blend, but here the Hue
+//	is strictly decreasing, that is we need to get from color1 to color2 by decreasing
+//	the 'angle' (i.e. 90¼ -> 180¼ would be done by subtracting 270¼ and getting -180¼...
+//	which is equivalent to 180¼ mod 360¼
+void inverseChromaticEvaluation(void *info, const float *in, float *out)
+  {
+    float position = *in;
+  
+  if(*(CTGradientElement **)info == nil)	//if elementList is empty return clear color
+	{
+	out[0] = out[1] = out[2] = out[3] = 1;
+	return;
+	}
+  
+  //This grabs the first two colors in the sequence
+  CTGradientElement *color1 = *(CTGradientElement **)info;
+  CTGradientElement *color2 = color1->nextElement;
+  
+  float c1[4];
+  float c2[4];
+      
+  //make sure first color and second color are on other sides of position
+  while(color2 != nil && color2->position < position)
+  	{
+  	color1 = color2;
+  	color2 = color1->nextElement;
+  	}
+  //if we don't have another color then make next color the same color
+  if(color2 == nil)
+    {
+	color2 = color1;
+    }
+
+  c1[0] = color1->red; 
+  c1[1] = color1->green;
+  c1[2] = color1->blue;
+  c1[3] = color1->alpha;
+  
+  c2[0] = color2->red; 
+  c2[1] = color2->green;
+  c2[2] = color2->blue;
+  c2[3] = color2->alpha;
+
+  transformRGB_HSV(c1);
+  transformRGB_HSV(c2);
+  resolveHSV(c1,c2);
+
+  if(c1[0] < c2[0]) //if color1's hue is higher than color2's hue then 
+	 c1[0] += 360;	//	we need to move c2 one revolution back on the wheel
+
+  
+  if(position <= color1->position)			//Make all below color color1's position equal to color1
+  	{
+  	out[0] = c1[0]; 
+  	out[1] = c1[1];
+  	out[2] = c1[2];
+  	out[3] = c1[3];
+  	}
+  else if (position >= color2->position)	//Make all above color color2's position equal to color2
+  	{
+  	out[0] = c2[0]; 
+  	out[1] = c2[1];
+  	out[2] = c2[2];
+  	out[3] = c2[3];
+  	}
+  else										//Interpolate color at postions between color1 and color1
+  	{
+  	//adjust position so that it goes from 0 to 1 in the range from color 1 & 2's position 
+	position = (position-color1->position)/(color2->position - color1->position);
+	
+	out[0] = (c2[0] - c1[0])*position + c1[0]; 
+  	out[1] = (c2[1] - c1[1])*position + c1[1];
+  	out[2] = (c2[2] - c1[2])*position + c1[2];
+  	out[3] = (c2[3] - c1[3])*position + c1[3];
+  	}
+    
+  transformHSV_RGB(out);
+  }
+
+
+
+void transformRGB_HSV(float *components) //H,S,B -> R,G,B
+	{
+	float H, S, V;
+	float R = components[0],
+		  G = components[1],
+		  B = components[2];
+	
+	float MAX = R > G ? (R > B ? R : B) : (G > B ? G : B),
+	      MIN = R < G ? (R < B ? R : B) : (G < B ? G : B);
+	
+	if(MAX == MIN)
+		H = NAN;
+	else if(MAX == R)
+		if(G >= B)
+			H = 60*(G-B)/(MAX-MIN)+0;
+		else
+			H = 60*(G-B)/(MAX-MIN)+360;
+	else if(MAX == G)
+		H = 60*(B-R)/(MAX-MIN)+120;
+	else if(MAX == B)
+		H = 60*(R-G)/(MAX-MIN)+240;
+	
+	S = MAX == 0 ? 0 : 1 - MIN/MAX;
+	V = MAX;
+	
+	components[0] = H;
+	components[1] = S;
+	components[2] = V;
+	}
+
+void transformHSV_RGB(float *components) //H,S,B -> R,G,B
+	{
+	float R, G, B;
+	float H = fmodf(components[0],359),	//map to [0,360)
+		  S = components[1],
+		  V = components[2];
+	
+	int   Hi = (int)floorf(H/60.) % 6;
+	float f  = H/60-Hi,
+		  p  = V*(1-S),
+		  q  = V*(1-f*S),
+		  t  = V*(1-(1-f)*S);
+	
+	switch (Hi)
+		{
+		case 0:	R=V;G=t;B=p;	break;
+		case 1:	R=q;G=V;B=p;	break;
+		case 2:	R=p;G=V;B=t;	break;
+		case 3:	R=p;G=q;B=V;	break;
+		case 4:	R=t;G=p;B=V;	break;
+		case 5:	R=V;G=p;B=q;	break;
+		}
+	
+	components[0] = R;
+	components[1] = G;
+	components[2] = B;
+	}
+
+void resolveHSV(float *color1, float *color2)	//H value may be undefined (i.e. graycale color)
+	{											//	we want to fill it with a sensible value
+	if(isnan(color1[0]) && isnan(color2[0]))
+		color1[0] = color2[0] = 0;
+	else if(isnan(color1[0]))
+		color1[0] = color2[0];
+	else if(isnan(color2[0]))
+		color2[0] = color1[0];
+	}
+
+@end
Index: trunk/Cocoa/AntiRSI/English.lproj/MainMenu.nib/classes.nib
===================================================================
--- trunk/Cocoa/AntiRSI/English.lproj/MainMenu.nib/classes.nib	(revision 331)
+++ trunk/Cocoa/AntiRSI/English.lproj/MainMenu.nib/classes.nib	(revision 332)
@@ -16,4 +16,5 @@
                 postpone = NSButton; 
                 progress = NSLevelIndicator; 
+                "reset_session_time" = NSDatePicker; 
                 "session_time" = NSTextField; 
                 "session_time_item" = NSMenuItem; 
@@ -32,4 +33,5 @@
         {CLASS = AntiRSIView; LANGUAGE = ObjC; SUPERCLASS = NSView; }, 
         {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, 
+        {CLASS = NSDatePicker; LANGUAGE = ObjC; SUPERCLASS = NSControl; }, 
         {CLASS = NSLevelIndicator; LANGUAGE = ObjC; SUPERCLASS = NSControl; }
     ); 
Index: trunk/Cocoa/AntiRSI/English.lproj/MainMenu.nib/info.nib
===================================================================
--- trunk/Cocoa/AntiRSI/English.lproj/MainMenu.nib/info.nib	(revision 331)
+++ trunk/Cocoa/AntiRSI/English.lproj/MainMenu.nib/info.nib	(revision 332)
@@ -8,5 +8,5 @@
 	<dict>
 		<key>259</key>
-		<string>688 524 300 322 0 0 1680 1028 </string>
+		<string>668 524 300 322 0 0 1680 1028 </string>
 		<key>29</key>
 		<string>276 845 249 44 0 0 1680 1028 </string>
@@ -20,5 +20,4 @@
 	<key>IBOpenObjects</key>
 	<array>
-		<integer>21</integer>
 		<integer>259</integer>
 	</array>
Index: trunk/Cocoa/AntiRSI/Info.plist
===================================================================
--- trunk/Cocoa/AntiRSI/Info.plist	(revision 331)
+++ trunk/Cocoa/AntiRSI/Info.plist	(revision 332)
@@ -18,5 +18,5 @@
 	<string>ONNE</string>
 	<key>CFBundleVersion</key>
-	<string>1.4njr1</string>
+	<string>1.4njr2</string>
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
Index: trunk/Cocoa/AntiRSI/version.plist
===================================================================
--- trunk/Cocoa/AntiRSI/version.plist	(revision 331)
+++ 	(revision )
@@ -1,16 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>BuildVersion</key>
-	<string>17</string>
-	<key>CFBundleShortVersionString</key>
-	<string>0.1</string>
-	<key>CFBundleVersion</key>
-	<string>0.1</string>
-	<key>ProjectName</key>
-	<string>NibPBTemplates</string>
-	<key>SourceVersion</key>
-	<string>1150000</string>
-</dict>
-</plist>
