diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..937a86e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +xcuserdata +*.xcuserdatad +build diff --git a/AnyBar.xcodeproj/project.pbxproj b/AnyBar.xcodeproj/project.pbxproj index 37772a5..0b7950e 100644 --- a/AnyBar.xcodeproj/project.pbxproj +++ b/AnyBar.xcodeproj/project.pbxproj @@ -7,36 +7,35 @@ objects = { /* Begin PBXBuildFile section */ - C511DFF01AA4DEBF00DEE15F /* black@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFE71AA4DEBF00DEE15F /* black@2x.png */; }; - C511DFF11AA4DEBF00DEE15F /* blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFE81AA4DEBF00DEE15F /* blue@2x.png */; }; - C511DFF21AA4DEBF00DEE15F /* cyan@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFE91AA4DEBF00DEE15F /* cyan@2x.png */; }; - C511DFF31AA4DEBF00DEE15F /* green@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFEA1AA4DEBF00DEE15F /* green@2x.png */; }; - C511DFF41AA4DEBF00DEE15F /* orange@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFEB1AA4DEBF00DEE15F /* orange@2x.png */; }; - C511DFF51AA4DEBF00DEE15F /* purple@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFEC1AA4DEBF00DEE15F /* purple@2x.png */; }; - C511DFF61AA4DEBF00DEE15F /* red@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFED1AA4DEBF00DEE15F /* red@2x.png */; }; - C511DFF71AA4DEBF00DEE15F /* white@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFEE1AA4DEBF00DEE15F /* white@2x.png */; }; - C511DFF81AA4DEBF00DEE15F /* yellow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFEF1AA4DEBF00DEE15F /* yellow@2x.png */; }; - C511DFFA1AA4E16600DEE15F /* alternate@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C511DFF91AA4E16600DEE15F /* alternate@2x.png */; }; - C511DFFC1AA4E9CF00DEE15F /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = C511DFFB1AA4E9CF00DEE15F /* README.md */; }; + 7648BAB81AA5A3C0003DA28F /* alternate@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAAD1AA5A3C0003DA28F /* alternate@2x.png */; }; + 7648BAB91AA5A3C0003DA28F /* black@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAAE1AA5A3C0003DA28F /* black@2x.png */; }; + 7648BABA1AA5A3C0003DA28F /* blue@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAAF1AA5A3C0003DA28F /* blue@2x.png */; }; + 7648BABB1AA5A3C0003DA28F /* cyan@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAB01AA5A3C0003DA28F /* cyan@2x.png */; }; + 7648BABC1AA5A3C0003DA28F /* green@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAB11AA5A3C0003DA28F /* green@2x.png */; }; + 7648BABD1AA5A3C0003DA28F /* orange@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAB21AA5A3C0003DA28F /* orange@2x.png */; }; + 7648BABE1AA5A3C0003DA28F /* purple@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAB31AA5A3C0003DA28F /* purple@2x.png */; }; + 7648BABF1AA5A3C0003DA28F /* red@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAB41AA5A3C0003DA28F /* red@2x.png */; }; + 7648BAC11AA5A3C0003DA28F /* white@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAB61AA5A3C0003DA28F /* white@2x.png */; }; + 7648BAC21AA5A3C0003DA28F /* yellow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7648BAB71AA5A3C0003DA28F /* yellow@2x.png */; }; + 7648BAC31AA5A50B003DA28F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C5AB32C41A8F9091002258B6 /* MainMenu.xib */; }; C5AB32BF1A8F9091002258B6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C5AB32BE1A8F9091002258B6 /* AppDelegate.m */; }; C5AB32C11A8F9091002258B6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C5AB32C01A8F9091002258B6 /* main.m */; }; C5AB32C31A8F9091002258B6 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C5AB32C21A8F9091002258B6 /* Images.xcassets */; }; - C5AB32C61A8F9091002258B6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C5AB32C41A8F9091002258B6 /* MainMenu.xib */; }; C5AB32E71A8F9B4E002258B6 /* GCDAsyncSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = C5AB32E41A8F9B4E002258B6 /* GCDAsyncSocket.m */; }; C5AB32E81A8F9B4E002258B6 /* GCDAsyncUdpSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = C5AB32E61A8F9B4E002258B6 /* GCDAsyncUdpSocket.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - C511DFE71AA4DEBF00DEE15F /* black@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "black@2x.png"; sourceTree = ""; }; - C511DFE81AA4DEBF00DEE15F /* blue@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "blue@2x.png"; sourceTree = ""; }; - C511DFE91AA4DEBF00DEE15F /* cyan@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cyan@2x.png"; sourceTree = ""; }; - C511DFEA1AA4DEBF00DEE15F /* green@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "green@2x.png"; sourceTree = ""; }; - C511DFEB1AA4DEBF00DEE15F /* orange@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "orange@2x.png"; sourceTree = ""; }; - C511DFEC1AA4DEBF00DEE15F /* purple@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "purple@2x.png"; sourceTree = ""; }; - C511DFED1AA4DEBF00DEE15F /* red@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "red@2x.png"; sourceTree = ""; }; - C511DFEE1AA4DEBF00DEE15F /* white@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "white@2x.png"; sourceTree = ""; }; - C511DFEF1AA4DEBF00DEE15F /* yellow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "yellow@2x.png"; sourceTree = ""; }; - C511DFF91AA4E16600DEE15F /* alternate@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "alternate@2x.png"; sourceTree = ""; }; + 7648BAAD1AA5A3C0003DA28F /* alternate@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "alternate@2x.png"; sourceTree = ""; }; + 7648BAAE1AA5A3C0003DA28F /* black@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "black@2x.png"; sourceTree = ""; }; + 7648BAAF1AA5A3C0003DA28F /* blue@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "blue@2x.png"; sourceTree = ""; }; + 7648BAB01AA5A3C0003DA28F /* cyan@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "cyan@2x.png"; sourceTree = ""; }; + 7648BAB11AA5A3C0003DA28F /* green@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "green@2x.png"; sourceTree = ""; }; + 7648BAB21AA5A3C0003DA28F /* orange@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "orange@2x.png"; sourceTree = ""; }; + 7648BAB31AA5A3C0003DA28F /* purple@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "purple@2x.png"; sourceTree = ""; }; + 7648BAB41AA5A3C0003DA28F /* red@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "red@2x.png"; sourceTree = ""; }; + 7648BAB61AA5A3C0003DA28F /* white@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "white@2x.png"; sourceTree = ""; }; + 7648BAB71AA5A3C0003DA28F /* yellow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "yellow@2x.png"; sourceTree = ""; }; C511DFFB1AA4E9CF00DEE15F /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; C5AB32B81A8F9091002258B6 /* AnyBar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AnyBar.app; sourceTree = BUILT_PRODUCTS_DIR; }; C5AB32BC1A8F9091002258B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -62,22 +61,29 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 7648BAAC1AA5A3C0003DA28F /* Resources */ = { + isa = PBXGroup; + children = ( + 7648BAAD1AA5A3C0003DA28F /* alternate@2x.png */, + 7648BAAE1AA5A3C0003DA28F /* black@2x.png */, + 7648BAAF1AA5A3C0003DA28F /* blue@2x.png */, + 7648BAB01AA5A3C0003DA28F /* cyan@2x.png */, + 7648BAB11AA5A3C0003DA28F /* green@2x.png */, + 7648BAB21AA5A3C0003DA28F /* orange@2x.png */, + 7648BAB31AA5A3C0003DA28F /* purple@2x.png */, + 7648BAB41AA5A3C0003DA28F /* red@2x.png */, + 7648BAB61AA5A3C0003DA28F /* white@2x.png */, + 7648BAB71AA5A3C0003DA28F /* yellow@2x.png */, + ); + path = Resources; + sourceTree = ""; + }; C5AB32AF1A8F9091002258B6 = { isa = PBXGroup; children = ( - C511DFFB1AA4E9CF00DEE15F /* README.md */, - C511DFF91AA4E16600DEE15F /* alternate@2x.png */, - C511DFE71AA4DEBF00DEE15F /* black@2x.png */, - C511DFE81AA4DEBF00DEE15F /* blue@2x.png */, - C511DFE91AA4DEBF00DEE15F /* cyan@2x.png */, - C511DFEA1AA4DEBF00DEE15F /* green@2x.png */, - C511DFEB1AA4DEBF00DEE15F /* orange@2x.png */, - C511DFEC1AA4DEBF00DEE15F /* purple@2x.png */, - C511DFED1AA4DEBF00DEE15F /* red@2x.png */, - C511DFEE1AA4DEBF00DEE15F /* white@2x.png */, - C511DFEF1AA4DEBF00DEE15F /* yellow@2x.png */, C5AB32BA1A8F9091002258B6 /* AnyBar */, C5AB32B91A8F9091002258B6 /* Products */, + C511DFFB1AA4E9CF00DEE15F /* README.md */, ); sourceTree = ""; }; @@ -100,6 +106,7 @@ C5AB32BE1A8F9091002258B6 /* AppDelegate.m */, C5AB32C21A8F9091002258B6 /* Images.xcassets */, C5AB32C41A8F9091002258B6 /* MainMenu.xib */, + 7648BAAC1AA5A3C0003DA28F /* Resources */, C5AB32BB1A8F9091002258B6 /* Supporting Files */, ); path = AnyBar; @@ -171,18 +178,18 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - C511DFF31AA4DEBF00DEE15F /* green@2x.png in Resources */, - C511DFFA1AA4E16600DEE15F /* alternate@2x.png in Resources */, - C511DFF21AA4DEBF00DEE15F /* cyan@2x.png in Resources */, - C511DFF71AA4DEBF00DEE15F /* white@2x.png in Resources */, - C511DFF51AA4DEBF00DEE15F /* purple@2x.png in Resources */, - C511DFF41AA4DEBF00DEE15F /* orange@2x.png in Resources */, - C511DFF61AA4DEBF00DEE15F /* red@2x.png in Resources */, - C511DFF81AA4DEBF00DEE15F /* yellow@2x.png in Resources */, - C511DFF11AA4DEBF00DEE15F /* blue@2x.png in Resources */, + 7648BABF1AA5A3C0003DA28F /* red@2x.png in Resources */, + 7648BAC21AA5A3C0003DA28F /* yellow@2x.png in Resources */, + 7648BABE1AA5A3C0003DA28F /* purple@2x.png in Resources */, + 7648BAC11AA5A3C0003DA28F /* white@2x.png in Resources */, C5AB32C31A8F9091002258B6 /* Images.xcassets in Resources */, - C511DFF01AA4DEBF00DEE15F /* black@2x.png in Resources */, - C5AB32C61A8F9091002258B6 /* MainMenu.xib in Resources */, + 7648BAB81AA5A3C0003DA28F /* alternate@2x.png in Resources */, + 7648BABC1AA5A3C0003DA28F /* green@2x.png in Resources */, + 7648BAB91AA5A3C0003DA28F /* black@2x.png in Resources */, + 7648BABB1AA5A3C0003DA28F /* cyan@2x.png in Resources */, + 7648BABA1AA5A3C0003DA28F /* blue@2x.png in Resources */, + 7648BABD1AA5A3C0003DA28F /* orange@2x.png in Resources */, + 7648BAC31AA5A50B003DA28F /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -195,7 +202,6 @@ files = ( C5AB32E81A8F9B4E002258B6 /* GCDAsyncUdpSocket.m in Sources */, C5AB32E71A8F9B4E002258B6 /* GCDAsyncSocket.m in Sources */, - C511DFFC1AA4E9CF00DEE15F /* README.md in Sources */, C5AB32C11A8F9091002258B6 /* main.m in Sources */, C5AB32BF1A8F9091002258B6 /* AppDelegate.m in Sources */, ); @@ -298,6 +304,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = AnyBar/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -309,6 +316,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = AnyBar/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.9; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; diff --git a/AnyBar/AppDelegate.m b/AnyBar/AppDelegate.m index c95b18a..5de21b6 100644 --- a/AnyBar/AppDelegate.m +++ b/AnyBar/AppDelegate.m @@ -8,72 +8,192 @@ #import "AppDelegate.h" -@interface AppDelegate () +static NSString* const AppConfigDirectoryName = @".AnyBar"; +static NSString* const PortEnvironmentName = @"ANYBAR_PORT"; +static NSString* const DefaultPort = @"1738"; +static NSString* const DefaultImageName = @"white@2x.png"; +static NSString* const AlternateImageName = @"alternate@2x.png"; +static const int UdpPortMin = 0; +static const int UdpPortMax = 65535; -@property (weak) IBOutlet NSWindow *window; +@interface AppDelegate() + +@property (weak, nonatomic) IBOutlet NSWindow *window; @property (strong, nonatomic) NSStatusItem *statusItem; -@property GCDAsyncUdpSocket *udpSocket; +@property (strong, nonatomic) GCDAsyncUdpSocket *udpSocket; @end @implementation AppDelegate -- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; - _statusItem.image = [NSImage imageNamed:@"white@2x.png"]; - _statusItem.alternateImage = [NSImage imageNamed:@"alternate@2x.png"]; - _statusItem.highlightMode = YES; - - NSString *portStr = [[[NSProcessInfo processInfo] environment] objectForKey:@"ANYBAR_PORT"]; - if (!portStr) { - portStr = @"1738"; +-(void)applicationDidFinishLaunching:(NSNotification *)aNotification { + NSImage *defaultImage = [NSImage imageNamed:DefaultImageName]; + NSImage *warnImage = [NSImage imageNamed:NSImageNameStatusUnavailable]; + int port = -1; + + self.statusItem = [self initializeStatusBarItem]; + [self updateStatusImage: defaultImage]; + + @try { + port = [self getUdpPort]; + + _udpSocket = [self initializeUdpSocket: port]; + } + @catch(NSException *ex) { + NSLog(@"Error: %@: %@", ex.name, ex.reason); + + [self updateStatusImage:warnImage]; + } + @finally { + NSString *portTitle = [NSString stringWithFormat:@"UDP port: %@", + port >= 0 ? [NSNumber numberWithInt:port] : @"unavailable"]; + NSString *quitTitle = @"Quit"; + _statusItem.menu = [self initializeStatusBarMenu:@{ + portTitle: [NSValue valueWithPointer:nil], + quitTitle: [NSValue valueWithPointer:@selector(terminate:)] + }]; + } +} + +-(void)applicationWillTerminate:(NSNotification *)aNotification { + [self shutdownUdpSocket: _udpSocket]; + _udpSocket = nil; + + [[NSStatusBar systemStatusBar] removeStatusItem:_statusItem]; + _statusItem = nil; +} + +-(int) getUdpPort { + int port = [self readIntFromEnvironmentVariable:PortEnvironmentName usingDefault:DefaultPort]; + + if (port < UdpPortMin || port > UdpPortMax) { + @throw([NSException exceptionWithName:@"Argument Exception" + reason:[NSString stringWithFormat:@"UDP Port range is invalid: %d", port] + userInfo:@{@"argument": [NSNumber numberWithInt:port]}]); + + } + + return port; +} + +-(GCDAsyncUdpSocket*)initializeUdpSocket:(int)port { + NSError *error = nil; + GCDAsyncUdpSocket *udpSocket = [[GCDAsyncUdpSocket alloc] + initWithDelegate:self + delegateQueue:dispatch_get_main_queue()]; + + [udpSocket bindToPort:port error:&error]; + if (error) { + @throw([NSException exceptionWithName:@"UDP Exception" + reason:[NSString stringWithFormat:@"Binding to %d failed", port] + userInfo:@{@"error": error}]); + } + + [udpSocket beginReceiving:&error]; + if (error) { + @throw([NSException exceptionWithName:@"UDP Exception" + reason:[NSString stringWithFormat:@"Receiving from %d failed", port] + userInfo:@{@"error": error}]); + } + + return udpSocket; +} + +-(void)shutdownUdpSocket:(GCDAsyncUdpSocket*)sock { + if (sock != nil) { + [sock close]; } - int port = [portStr intValue]; - - NSMenu *menu = [[NSMenu alloc] init]; - NSString *portTitle = [@"UDP port " stringByAppendingString:portStr]; - [menu addItemWithTitle:portTitle action:nil keyEquivalent:@""]; - [menu addItemWithTitle:@"Quit" action:@selector(terminate:) keyEquivalent:@""]; - _statusItem.menu = menu; - - _udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; - NSError *error = nil; //maybe we should check this error - [_udpSocket bindToPort:port error:&error]; - [_udpSocket beginReceiving:&error]; } -- (void)applicationWillTerminate:(NSNotification *)aNotification { +-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data + fromAddress:(NSData *)address withFilterContext:(id)filterContext { + [self processUdpSocketMsg:sock withData:data fromAddress:address]; } -- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data - fromAddress:(NSData *)address -withFilterContext:(id)filterContext -{ +-(void)processUdpSocketMsg:(GCDAsyncUdpSocket *)sock withData:(NSData *)data + fromAddress:(NSData *)address { NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + if ([msg isEqualToString:@"quit"]) { [[NSApplication sharedApplication] terminate:nil]; - } else { + } + else { + NSImage *image = nil; NSString *fileName = [msg stringByAppendingString:@"@2x"]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *bundledFile = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:fileName, msg] ofType:@"png"]; - - BOOL fileExistsInBundle = [fileManager fileExistsAtPath:bundledFile]; - if (fileExistsInBundle) { - _statusItem.image = [[NSImage alloc] initWithContentsOfFile:bundledFile]; - } else { - // let's lookup for the file in ~/.AnyBar - NSString *fallbackFile = [NSString stringWithFormat:@"%@/.AnyBar/%@.png", NSHomeDirectory(), fileName]; - BOOL fallbackFileExists = [fileManager fileExistsAtPath:fallbackFile]; - if (fallbackFileExists) { - _statusItem.image = [[NSImage alloc] initWithContentsOfFile:fallbackFile]; - } else { - // Logging to Console.app - NSLog(@"No image for the command %@ found", msg); + + BOOL fileExists = [fileManager fileExistsAtPath:bundledFile]; + if (fileExists) { + image = [[NSImage alloc] initWithContentsOfFile:bundledFile]; + } + else { + // Let's lookup for the file in ~/.AnyBar + NSString *fallbackFile = [NSString stringWithFormat:@"%@/%@/%@.png", + NSHomeDirectory(), AppConfigDirectoryName, fileName]; + fileExists = [fileManager fileExistsAtPath:fallbackFile]; + if (fileExists) { + image = [[NSImage alloc] initWithContentsOfFile:fallbackFile]; } } + + if (fileExists && image != nil) { + [self updateStatusImage: image]; + } + else { + NSLog(@"No image for the command %@ found", msg); + } } } +-(NSStatusItem*) initializeStatusBarItem { + NSStatusItem *statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; + statusItem.alternateImage = [NSImage imageNamed:AlternateImageName]; + statusItem.highlightMode = YES; + return statusItem; +} + +-(NSMenu*) initializeStatusBarMenu:(NSDictionary*)menuDictionary { + NSMenu *menu = [[NSMenu alloc] init]; + + [menuDictionary enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSValue* val, BOOL *stop) { + SEL action = nil; + [val getValue:&action]; + [menu addItemWithTitle:key action:action keyEquivalent:@""]; + }]; + + return menu; +} + +-(void) updateStatusImage:(NSImage*) image { + _statusItem.image = image; +} + +-(int) readIntFromEnvironmentVariable:(NSString*) envVariable usingDefault:(NSString*) defStr { + int intVal = -1; + + NSString *envStr = [[[NSProcessInfo processInfo] + environment] objectForKey:envVariable]; + if (!envStr) { + envStr = defStr; + } + + NSNumberFormatter *nFormatter = [[NSNumberFormatter alloc] init]; + nFormatter.numberStyle = NSNumberFormatterDecimalStyle; + NSNumber *number = [nFormatter numberFromString:envStr]; + + if (!number) { + @throw([NSException exceptionWithName:@"Argument Exception" + reason:[NSString stringWithFormat:@"Parsing integer from %@ failed", envStr] + userInfo:@{@"argument": envStr}]); + + } + + intVal = [number intValue]; + + return intVal; +} @end + diff --git a/alternate@2x.png b/AnyBar/Resources/alternate@2x.png similarity index 100% rename from alternate@2x.png rename to AnyBar/Resources/alternate@2x.png diff --git a/black@2x.png b/AnyBar/Resources/black@2x.png similarity index 100% rename from black@2x.png rename to AnyBar/Resources/black@2x.png diff --git a/blue@2x.png b/AnyBar/Resources/blue@2x.png similarity index 100% rename from blue@2x.png rename to AnyBar/Resources/blue@2x.png diff --git a/cyan@2x.png b/AnyBar/Resources/cyan@2x.png similarity index 100% rename from cyan@2x.png rename to AnyBar/Resources/cyan@2x.png diff --git a/green@2x.png b/AnyBar/Resources/green@2x.png similarity index 100% rename from green@2x.png rename to AnyBar/Resources/green@2x.png diff --git a/orange@2x.png b/AnyBar/Resources/orange@2x.png similarity index 100% rename from orange@2x.png rename to AnyBar/Resources/orange@2x.png diff --git a/purple@2x.png b/AnyBar/Resources/purple@2x.png similarity index 100% rename from purple@2x.png rename to AnyBar/Resources/purple@2x.png diff --git a/red@2x.png b/AnyBar/Resources/red@2x.png similarity index 100% rename from red@2x.png rename to AnyBar/Resources/red@2x.png diff --git a/screenshot.png b/AnyBar/Resources/screenshot.png similarity index 100% rename from screenshot.png rename to AnyBar/Resources/screenshot.png diff --git a/white@2x.png b/AnyBar/Resources/white@2x.png similarity index 100% rename from white@2x.png rename to AnyBar/Resources/white@2x.png diff --git a/yellow@2x.png b/AnyBar/Resources/yellow@2x.png similarity index 100% rename from yellow@2x.png rename to AnyBar/Resources/yellow@2x.png diff --git a/README.md b/README.md index bcdf6e6..f69b173 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ AnyBar is a small indicator for your menubar that does one simple thing: it displays color dot. What color means is up to you. When to change color is also up to you. - + ## Download @@ -42,4 +42,4 @@ or ANYBAR_PORT=1788 ./AnyBar.app/Contents/MacOS/AnyBar & ``` -(this way you can run multiple instances of AnyBar) \ No newline at end of file +(this way you can run multiple instances of AnyBar) diff --git a/Tests/Makefile b/Tests/Makefile new file mode 100644 index 0000000..e18f4d0 --- /dev/null +++ b/Tests/Makefile @@ -0,0 +1,42 @@ +SHELL:=bash +SLEEPT:=3s +PORTS:=1738 1739 1740 + +all: tests + +tests: anybar + @echo "Starting..." && \ + echo -n " WHITE " && \ + for port in $(PORTS) ; do ANYBAR_PORT=$$port open -n ../build/Debug/AnyBar.app ; done && \ + sleep $(SLEEPT) && \ + echo -n " ORANGE " && \ + for port in $(PORTS) ; do echo -n "orange" | nc -4u -w0 localhost $$port ; done && \ + sleep $(SLEEPT) && \ + echo -n " RGB " && \ + echo -n "red" | nc -4u -w0 localhost 1738 && \ + echo -n "green" | nc -4u -w0 localhost 1739 && \ + echo -n "blue" | nc -4u -w0 localhost 1740 && \ + sleep $(SLEEPT) && \ + echo -n " BLACK " && \ + for port in $(PORTS) ; do echo -n "black" | nc -4u -w0 localhost $$port ; done && \ + sleep $(SLEEPT) && \ + echo -n " WHITE " && \ + for port in $(PORTS) ; do echo -n "white" | nc -4u -w0 localhost $$port ; done && \ + sleep $(SLEEPT) && \ + echo && \ + echo "Stopping..." && \ + for port in $(PORTS) ; do echo -n "quit" | nc -4u -w0 localhost $$port ; done && \ + sleep $(SLEEPT) && \ + echo "Done." + +anybar: ../AnyBar.xcodeproj + @xcodebuild -configuration Debug -target AnyBar -project $< build + +clean: ../AnyBar.xcodeproj + -@xcodebuild -configuration Debug -target AnyBar -project $< clean 2>&1>/dev/null + -@rm -rf ../build + +.PHONY: all tests anybar clean + +.SILENT: clean +