diff options
Diffstat (limited to 'ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands')
20 files changed, 1315 insertions, 0 deletions
| diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.h new file mode 100644 index 00000000..f65ba61e --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.h @@ -0,0 +1,21 @@ +#import <Foundation/Foundation.h> + +#import "WindowController.h" + +@interface App : NSObject { + +} + +@property (nonatomic, retain) WebView *webView; + +- (id) initWithWebView:(WebView *)view; + +- (void) terminate; +- (void) activate; +- (void) hide; +- (void) unhide; +- (void) beep; +- (void) bounce; +- (void) setCustomUserAgent:(NSString *)userAgentString; +- (NSNumber*) systemIdleTime; +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.m new file mode 100644 index 00000000..6d47a17e --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/App.m @@ -0,0 +1,128 @@ +#import "App.h" + +#import "JSEventHelper.h" + +@implementation App + +@synthesize webView; + +- (id) initWithWebView:(WebView *) view{ +    self = [super init]; +     +    if (self) { +        self.webView = view; +        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self  +                                                               selector: @selector(receiveSleepNotification:)  +                                                                   name: NSWorkspaceWillSleepNotification object: NULL]; +        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self  +                                                               selector: @selector(receiveWakeNotification:)  +                                                                   name: NSWorkspaceDidWakeNotification object: NULL]; +        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self +                                                               selector: @selector(receiveActivateNotification:) +                                                                   name: NSWorkspaceDidActivateApplicationNotification object: NULL]; +    } + +    return self; +} + +- (void) terminate { +    [NSApp terminate:nil]; +} + +- (void) activate { +    [NSApp activateIgnoringOtherApps:YES]; +} + +- (void) hide { +    [NSApp hide:nil]; +} + +- (void) unhide { +    [NSApp unhide:nil]; +} + +- (void)beep { +    NSBeep(); +} + +- (void) bounce { +    [NSApp requestUserAttention:NSInformationalRequest]; +} + +- (void)setCustomUserAgent:(NSString *)userAgentString { +    [self.webView setCustomUserAgent: userAgentString]; +} + +- (void) open:(NSString*)url { +    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]]; +} + +- (void) launch:(NSString *)name { +    [[NSWorkspace sharedWorkspace] launchApplication:name]; +} + +- (void)receiveSleepNotification:(NSNotification*)note{ +    [JSEventHelper triggerEvent:@"sleep" forWebView:self.webView]; +} + +- (void) receiveWakeNotification:(NSNotification*)note{ +    [JSEventHelper triggerEvent:@"wake" forWebView:self.webView]; +} + +- (void) receiveActivateNotification:(NSNotification*)notification{ +    NSDictionary* userInfo = [notification userInfo]; +    NSRunningApplication* runningApplication = [userInfo objectForKey:NSWorkspaceApplicationKey]; +    if (runningApplication) { +        NSMutableDictionary* applicationDidGetFocusDict = [[NSMutableDictionary alloc] initWithCapacity:2]; +        [applicationDidGetFocusDict setObject:runningApplication.localizedName +                                       forKey:@"localizedName"]; +        [applicationDidGetFocusDict setObject:[runningApplication.bundleURL absoluteString] +                                       forKey:@"bundleURL"]; +         +        [JSEventHelper triggerEvent:@"appActivated" withArgs:applicationDidGetFocusDict forWebView:self.webView]; +    } +} + + + + +/* + To get the elapsed time since the previous input event—keyboard, mouse, or tablet—specify kCGAnyInputEventType. + */ +- (NSNumber*)systemIdleTime { +    CFTimeInterval timeSinceLastEvent = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateHIDSystemState, kCGAnyInputEventType); +     +    return [NSNumber numberWithDouble:timeSinceLastEvent]; +} + + + + ++ (NSString*) webScriptNameForSelector:(SEL)selector +{ +	id	result = nil; +	 +	if (selector == @selector(open:)) { +		result = @"open"; +	} else if (selector == @selector(launch:)) { +        result = @"launch"; +    } else if (selector == @selector(setCustomUserAgent:)) { +        result = @"setCustomUserAgent"; +    } else if (selector == @selector(systemIdleTime)) { +        result = @"systemIdleTime"; +    } + +    return result; +} + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector +{ +    return NO; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name +{ +	return YES; +} + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.h new file mode 100755 index 00000000..65d6b6d4 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.h @@ -0,0 +1,18 @@ +// +//  Command.h +//  MacGap +// +//  Created by Joe Hildebrand on 1/10/12. +//  Copyright (c) 2012 Twitter. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <Webkit/WebScriptObject.h> + +@interface Command : NSObject { +    JSContextRef context; +} + +- (id) initWithContext:(JSContextRef)aContext; + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.m new file mode 100755 index 00000000..39b85630 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Command.m @@ -0,0 +1,28 @@ +// +//  Command.m +//  MacGap +// +//  Created by Joe Hildebrand on 1/10/12. +//  Copyright (c) 2012 Twitter. All rights reserved. +// + +#import "Command.h" +#import <JavaScriptCore/JSContextRef.h> + +@implementation Command  + +- (id) initWithContext:(JSContextRef)aContext { +    self = [super init]; +    if (!self) +        return nil; +    context = aContext; +    JSGlobalContextRetain((JSGlobalContextRef)context); +    return self; +} + +- (void)dealloc +{ +    if (context) +        JSGlobalContextRelease((JSGlobalContextRef)context); +} +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.h new file mode 100644 index 00000000..b3c533d7 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.h @@ -0,0 +1,11 @@ +#import <Foundation/Foundation.h> + +@interface Dock : NSObject { +     +} +- (void) setBadge:(NSString*)value; +- (NSString *) badge; + +@property (readwrite, copy) NSString *badge; + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.m new file mode 100644 index 00000000..a4494d16 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Dock.m @@ -0,0 +1,31 @@ +#import "Dock.h" + +@implementation Dock + +@synthesize badge; + +- (void) setBadge:(NSString *)value +{ +    NSDockTile *tile = [[NSApplication sharedApplication] dockTile]; +    [tile setBadgeLabel:value]; +} + +- (NSString *) badge +{ +    NSDockTile *tile = [[NSApplication sharedApplication] dockTile]; +    return [tile badgeLabel]; +} + +#pragma mark WebScripting Protocol + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector +{ +    return NO; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name +{ +	return NO; +} + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.h new file mode 100755 index 00000000..d765978f --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.h @@ -0,0 +1,31 @@ +// +//  MenuItemProxy.h +//  MacGap +// +//  Created by Joe Hildebrand on 1/15/12. +//  Copyright (c) 2012 Twitter. All rights reserved. +// + +#import "Command.h" +#import "CallbackDelegate.h" + +@class MenuProxy; + +@interface MenuItemProxy : Command { +    NSMenuItem *item; +    CallbackDelegate *callback; +} + ++ (MenuItemProxy*) proxyWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem; + +- (MenuProxy*)addSubmenu; + +- (void) remove; +- (void) setCallback:(WebScriptObject*)aCallback; +- (void) setKey:(NSString*)keyCommand; +- (void) setTitle:(NSString*)title; +- (void) enable; +- (void) disable; +- (MenuProxy*)submenu; + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.m new file mode 100755 index 00000000..7b9702cc --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuItemProxy.m @@ -0,0 +1,150 @@ +// +//  MenuItemProxy.m +//  MacGap +// +//  Created by Joe Hildebrand on 1/15/12. +//  Copyright (c) 2012 Twitter. All rights reserved. +// + +#import "MenuItemProxy.h" +#import "MenuProxy.h" + +@implementation MenuItemProxy + +- (id) initWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem +{ +    NSAssert(anItem, @"anItem required"); +    self = [super initWithContext:aContext]; +    if (!self) +        return nil; +    item = anItem; +    item.representedObject = self; + +    return self; +} + ++ (MenuItemProxy*) proxyWithContext:(JSContextRef)aContext andMenuItem:(NSMenuItem*)anItem +{ +    MenuItemProxy *proxy = [anItem representedObject]; +    if (proxy) +    { +        NSLog(@"MIP Cache hit"); +        NSAssert([proxy class] == [MenuItemProxy class], @"Bad proxy"); +        return proxy; +    } +    return [[MenuItemProxy alloc] initWithContext:aContext andMenuItem:anItem]; +} + +- (NSString*) description +{ +    return [item description]; +} + +- (MenuProxy*)addSubmenu +{ +    NSMenu *s = [item submenu]; +    if (!s) +    { +        s = [[NSMenu alloc] initWithTitle:@"FFFFFFOOOOO"]; +        [item setSubmenu:s]; +    } +    return [MenuProxy proxyWithContext:context andMenu:s]; +} + +- (void) remove +{ +    NSMenu *menu = [item menu]; +    [menu removeItem:item]; +} + +- (void)callCallback:(id)sender +{ +    [callback callWithParams:[sender title], nil]; +} + +- (void) setCallback:(WebScriptObject*)aCallback +{ +    NSAssert(item, @"item required"); +    callback = [[CallbackDelegate alloc] initWithContext:context forCallback:aCallback]; +    [item setAction:@selector(callCallback:)]; +    [item setTarget:self]; +} + +- (void)setKey:(NSString*)keyCommand +{ +    NSString *aKey = [MenuProxy getKeyFromString:keyCommand]; +    [item setKeyEquivalent:aKey]; +     +    NSUInteger modifiers = [MenuProxy getModifiersFromString:keyCommand]; +    [item setKeyEquivalentModifierMask:modifiers]; +} + +- (void) setTitle:(NSString*)title +{ +    [item setTitle:title]; +} + +- (MenuProxy*)submenu; +{ +    // TODO: make this work as a property +    NSMenu *s = [item submenu]; +    if (!s) +        return nil; +    return [MenuProxy proxyWithContext:context andMenu:s]; +} + +- (void) enable +{ +    [item setEnabled:YES]; +} + +- (void) disable +{ +    [item setEnabled:NO]; +} + +#pragma mark WebScripting protocol + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector +{ +    return [self webScriptNameForSelector:selector] == nil; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name +{ +	return YES; +} + ++ (NSString*) webScriptNameForSelector:(SEL)selector +{ +	id	result = nil; +	 +    if (selector == @selector(addSubmenu)) { +		result = @"addSubmenu"; +	} +	else if (selector == @selector(remove)) { +		result = @"remove"; +	} +	else if (selector == @selector(setCallback:)) { +		result = @"setCallback"; +	} +	else if (selector == @selector(setKey:)) { +		result = @"setKey"; +	} +	else if (selector == @selector(setTitle:)) { +		result = @"setTitle"; +	} +	else if (selector == @selector(submenu)) { +		result = @"submenu"; +	} +    else if (selector == @selector(enable)) { +		result = @"enable"; +	} +    else if (selector == @selector(disable)) { +		result = @"disable"; +	} +	 +	return result; +} + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.h new file mode 100755 index 00000000..afd6c6ed --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.h @@ -0,0 +1,31 @@ +// +//  MenuProxy.h +//  MacGap +// +//  Created by Joe Hildebrand on 1/14/12. +//  Copyright (c) 2012 Twitter. All rights reserved. +// + +#import "Command.h" + +@class MenuItemProxy; + +@interface MenuProxy : Command { +    NSMenu *menu; +} + ++ (MenuProxy*)proxyWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu; + +- (MenuItemProxy*)addItemWithTitle:(NSString*)title +                     keyEquivalent:(NSString*)aKey +                          callback:(WebScriptObject*)aCallback +                           atIndex:(NSInteger)index; + +- (MenuItemProxy*)addSeparator; +- (MenuItemProxy*)itemForKey:(id)key; +- (MenuProxy*)removeItem:(id)key; + ++ (NSString*)getKeyFromString:(NSString*)keyCommand; ++ (NSUInteger*)getModifiersFromString:(NSString*)keyCommand; + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.m new file mode 100755 index 00000000..5bc10a76 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/MenuProxy.m @@ -0,0 +1,233 @@ +// +//  MenuProxy.m +//  MacGap +// +//  Created by Joe Hildebrand on 1/14/12. +//  Copyright (c) 2012 Twitter. All rights reserved. +// + +#import <objc/runtime.h> +#import <JavaScriptCore/JavaScript.h> + +#import "MenuProxy.h" +#import "MenuItemProxy.h" + +static char REPRESENTED_OBJECT; + +@interface NSMenu (represented) +@property (strong) id representedObject; +@end + +@implementation NSMenu (represented) + +- (id) representedObject +{ +    return objc_getAssociatedObject(self, &REPRESENTED_OBJECT); +} + +- (void) setRepresentedObject:(id)representedObject +{ +    objc_setAssociatedObject(self,  +                             &REPRESENTED_OBJECT, +                             representedObject,  +                             OBJC_ASSOCIATION_RETAIN); +} + +@end + +@implementation MenuProxy + +- (id) initWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu +{ +    self = [super initWithContext:aContext]; +    if (!self) +        return nil; +    menu = aMenu; +    menu.representedObject = self; +    return self; +} + ++ (MenuProxy*)proxyWithContext:(JSContextRef)aContext andMenu:(NSMenu*)aMenu +{ +    // singleton-ish. +    MenuProxy *ret = [aMenu representedObject]; +    if (ret) +    { +        NSLog(@"MP cache hit"); +        return ret; +    } +    return [[MenuProxy alloc] initWithContext:aContext andMenu:aMenu]; +} + +- (void) dealloc +{ +    menu.representedObject = nil; +} + +- (NSString*) description +{ +    return [menu description]; +} + +static BOOL isNullish(id o) +{ +    if (!o) +        return YES; +    if ([o isKindOfClass:[WebUndefined class]]) +        return YES; +    return NO; +} + +- (MenuItemProxy*)addItemWithTitle:(NSString*)title +                     keyEquivalent:(NSString*)keyCommand +                          callback:(WebScriptObject*)aCallback +                           atIndex:(NSInteger)index +{ +    if (isNullish(title)) +        title = @""; +     +    NSString *aKey = [MenuProxy getKeyFromString:keyCommand]; +    NSMenuItem *item = nil; +     +    if(index) { +        item = [menu insertItemWithTitle:title action:nil keyEquivalent:aKey atIndex:index ]; +    } else { +        item = [menu addItemWithTitle:title action:nil keyEquivalent:aKey ]; +         +    } +     +    // Set the modifiers. +    NSUInteger modifiers = [MenuProxy getModifiersFromString:keyCommand]; +    [item setKeyEquivalentModifierMask:modifiers]; +    +    if(!menu.supermenu) { +        NSMenu *s = [[NSMenu alloc] initWithTitle:title]; +        [item setSubmenu:s]; +    } +    +    MenuItemProxy *mip = [MenuItemProxy proxyWithContext:context andMenuItem:item]; +    if (!isNullish(aCallback)) +        [mip setCallback:aCallback]; +     +    +    return mip; +} + ++ (NSString*)getKeyFromString:(NSString*)keyCommand { +    if (isNullish(keyCommand)) +        keyCommand = @""; + +    // Obtain the key (if there are modifiers, it will be the last character). +    NSString *aKey = @""; +    if ([keyCommand length] > 0) { +        aKey = [keyCommand substringFromIndex:[keyCommand length] - 1]; +    } + +    return aKey; +} + ++ (NSUInteger*)getModifiersFromString:(NSString*)keyCommand { +    // aKeys may optionally specify one or more modifiers. +    NSUInteger modifiers = 0; +     +    if ([keyCommand rangeOfString:@"caps"].location != NSNotFound) modifiers += NSAlphaShiftKeyMask; +    if ([keyCommand rangeOfString:@"shift"].location != NSNotFound) modifiers += NSShiftKeyMask; +    if ([keyCommand rangeOfString:@"cmd"].location != NSNotFound) modifiers += NSCommandKeyMask; +    if ([keyCommand rangeOfString:@"ctrl"].location != NSNotFound) modifiers += NSControlKeyMask; +    if ([keyCommand rangeOfString:@"opt"].location != NSNotFound) modifiers += NSAlternateKeyMask; +    if ([keyCommand rangeOfString:@"alt"].location != NSNotFound) modifiers += NSAlternateKeyMask; + +    return modifiers; +} + +- (MenuItemProxy*)addSeparator +{ +    NSMenuItem *sep = [NSMenuItem separatorItem]; +    [menu addItem:sep]; +    return [MenuItemProxy proxyWithContext:context andMenuItem:sep]; +} + +- (MenuItemProxy*)itemForKey:(id)key +{ +    if (isNullish(key)) +        return nil; +    NSMenuItem *item = nil; +    if ([key isKindOfClass:[NSNumber class]]) +    { +        item = [menu itemAtIndex:[key intValue]]; +    } +    else if ([key isKindOfClass:[NSString class]]) +    { +        item = [menu itemWithTitle:key]; +        if (!item) +        { +            // Try again, with ... appended. e.g. "Save..." +            item = [menu itemWithTitle: +                    [key stringByAppendingString:@"\u2026"]]; +        } +    } +    if (!item) +        return nil; + +    return [MenuItemProxy proxyWithContext:context andMenuItem:item];     +} + +- (MenuProxy*)removeItem:(id)key +{ +    if (isNullish(key)) +        return nil; +     +    NSMenuItem *item = nil; +    if ([key isKindOfClass:[NSNumber class]]) +    { +        item = [menu itemAtIndex:[key intValue]]; +    } +    else if ([key isKindOfClass:[NSString class]]) +    { +        item = [menu itemWithTitle:key]; +        if (!item) +        { +            // Try again, with ... appended. e.g. "Save..." +            item = [menu itemWithTitle: +                    [key stringByAppendingString:@"\u2026"]]; +        } +    } +    if (!item) +        return nil; +     +    [menu removeItem:item]; +    return [MenuProxy proxyWithContext:context andMenu:menu]; +} + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector +{ +    return [self webScriptNameForSelector:selector] == nil; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name +{ +	return YES; +} + ++ (NSString*) webScriptNameForSelector:(SEL)selector +{ +	id	result = nil; +    +    if (selector == @selector(addItemWithTitle:keyEquivalent:callback:atIndex:)) { +		result = @"addItem"; +	} +    else if (selector == @selector(addSeparator)) { +        result = @"addSeparator"; +    } +	else if (selector == @selector(itemForKey:)) { +		result = @"getItem"; +	} +    else if (selector == @selector(removeItem:)) { +		result = @"removeMenu"; +	} +    +	return result; +} + + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.h new file mode 100644 index 00000000..51077a43 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.h @@ -0,0 +1,26 @@ +// +//  Notice.h +//  MacGap +// +//  Created by Christian Sullivan on 7/26/12. +//  Copyright (c) 2012 Twitter. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import "WindowController.h" + +#define APP_NOTICE_NOTIFICATION @"Notice" + +@interface Notice : NSObject <NSUserNotificationCenterDelegate> { +     +} + +@property (nonatomic, retain) WebView *webView; + +- (id) initWithWebView:(WebView *)view; +- (void) notify:(NSDictionary*)message; +- (void) close:(NSString*)notificationId; ++ (BOOL) available; + +@end + diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.m new file mode 100644 index 00000000..a4095f9f --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Notice.m @@ -0,0 +1,108 @@ +// +//  Notice.m +//  MacGap +// +//  Created by Christian Sullivan on 7/26/12. +//  Copyright (c) 2012 Twitter. All rights reserved. +// + +#import "Notice.h" + +#import "JSEventHelper.h" + +@implementation Notice + +- (id) initWithWebView:(WebView*)view +{ +    if(self = [super init]) { +        self.webView = view; +	    [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; +    } +    return self; +} + +- (void) notify:(NSDictionary *)message { +    NSUserNotification *notification = [[NSUserNotification alloc] init]; +    [notification setTitle:[message valueForKey:@"title"]]; +    [notification setInformativeText:[message valueForKey:@"content"]]; +    [notification setDeliveryDate:[NSDate dateWithTimeInterval:0 sinceDate:[NSDate date]]]; +    BOOL playSound = true; // optional parameter, false only when {sound: false} +    @try { +        NSNumber *s = [message valueForKey:@"sound"]; +        if ([[s className] isEqual: @"__NSCFBoolean"]) { +            playSound = [s boolValue]; +        } +    } +    @catch (NSException *exception) { +    } +    if (playSound) { +        [notification setSoundName:NSUserNotificationDefaultSoundName]; +    } +    NSString *id = @""; // optional, needed for close +    @try { +        id = [message valueForKey:@"id"]; +    } +    @catch (NSException *exception) { +    } +    [notification setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:id, @"id", nil]]; +    NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; +    [center scheduleNotification:notification]; +} + +// close all notifications with id == notificationId or close all notifications if notificationId == "*" +- (void) close:(NSString*)notificationId { +    NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; +    for(NSUserNotification * deliveredNote in center.deliveredNotifications) { +        if ([notificationId isEqualToString:@"*"] || [deliveredNote.userInfo[@"id"] isEqualToString:notificationId]) { +            [center removeDeliveredNotification: deliveredNote]; +        } +    } +} + ++ (BOOL) available { +    if ([NSUserNotificationCenter respondsToSelector:@selector(defaultUserNotificationCenter)]) +        return YES; +     +    return NO; +} + +- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification +{ +    NSString *notificationId = [notification.userInfo valueForKey:@"id"]; +    [JSEventHelper triggerEvent:@"macgap.notify.activated" forDetail:notificationId forWebView:self.webView]; +} + +#pragma mark WebScripting Protocol + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector +{ +    BOOL result = YES; +    if (selector == @selector(notify:)) +        result = NO; +    if (selector == @selector(close:)) +        result = NO; +     +    return result; +} + ++ (NSString*) webScriptNameForSelector:(SEL)selector +{ +	id	result = nil; +	 +	if (selector == @selector(notify:)) { +		result = @"notify"; +	} +	if (selector == @selector(close:)) { +		result = @"close"; +	} +	 +	return result; +} + +// right now exclude all properties (eg keys) ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name +{ +	return YES; +} + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.h new file mode 100644 index 00000000..f931340d --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.h @@ -0,0 +1,21 @@ +#import <Foundation/Foundation.h> + +@interface Path : NSObject { +     +} + +- (NSString *) application; +- (NSString *) resource; +- (NSString *) documents; +- (NSString *) library; +- (NSString *) home; +- (NSString *) temp; + +@property (readonly,copy) NSString* application; +@property (readonly,copy) NSString* resource; +@property (readonly,copy) NSString* documents; +@property (readonly,copy) NSString* library; +@property (readonly,copy) NSString* home; +@property (readonly,copy) NSString* temp; + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.m new file mode 100644 index 00000000..8c54100f --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Path.m @@ -0,0 +1,53 @@ +#import "Path.h" + +@implementation Path + +@synthesize application; +@synthesize resource; +@synthesize documents; +@synthesize library; +@synthesize home; +@synthesize temp; + +- (NSString *)application { +    return [[NSBundle mainBundle] bundlePath]; +} + +- (NSString *)resource { +    return [[NSBundle mainBundle] resourcePath]; +} + +- (NSString *)documents { +    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); +    return [paths objectAtIndex:0]; +} + +- (NSString *)library { +    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); +    NSLog( @"%@", paths ); +    return [paths objectAtIndex:0]; +} + +- (NSString *)home { +    return NSHomeDirectory(); +} + +- (NSString *)temp { +    return NSTemporaryDirectory(); +} + +#pragma mark WebScripting Protocol + +/* checks whether a selector is acceptable to be called from JavaScript */ ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector +{ +    return NO; +} + +// right now exclude all properties (eg keys) ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name +{ +	return NO; +} + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.h new file mode 100644 index 00000000..06707643 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.h @@ -0,0 +1,17 @@ +#import <Cocoa/Cocoa.h> +#import "Command.h" +#import "CallbackDelegate.h" + + +@interface Sound : Command { + +} + +// pending callbacks for sounds being played, to keep +// ARC from freeing them too early +@property (nonatomic, strong) NSMutableSet *pending; + +- (void) play:(NSString*)file onComplete:(WebScriptObject*)callback; +- (void) playSystem:(NSString*)name onComplete:(WebScriptObject*)callback; + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.m new file mode 100644 index 00000000..9f4a44db --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/Sound.m @@ -0,0 +1,97 @@ +#import "Sound.h" + + +@interface PlayDelegate : CallbackDelegate <NSSoundDelegate> { +} + +@property (nonatomic, weak) Sound *sound; + +- (id) initWithContext:(JSContextRef)aContext +           forCallback:(WebScriptObject*)aCallback +             withSound:(Sound*)aSound; +@end + +@implementation PlayDelegate + +@synthesize sound; + +- (id) initWithContext:(JSContextRef)aContext +           forCallback:(WebScriptObject*)aCallback +             withSound:(Sound*)aSound +{ +        self = [super initWithContext:aContext forCallback:aCallback]; +        if (!self) +                return nil; +        sound = aSound; +        return self; +} + +- (void)sound:(NSSound *)aSound didFinishPlaying:(BOOL)finishedPlaying { +        [self callWithParams:[aSound name], nil]; +        [sound.pending removeObject:self]; +} + +@end + +@implementation Sound + +@synthesize pending; + +- (id) initWithContext:(JSContextRef)aContext { +    self = [super initWithContext:aContext]; +    if (!self) { +        return nil; +    } +     +    pending = [NSMutableSet new]; +    return self; +} + +- (void) playSound:(NSSound*)sound onComplete:(WebScriptObject*)callback { +    if (callback != (id)[WebUndefined undefined]) { +        PlayDelegate *d = [[PlayDelegate alloc] initWithContext:context +                                                    forCallback:callback +                                                      withSound:self]; +        [pending addObject:d]; +        [sound setDelegate:d]; +    } +    [sound play]; +} + +- (void) play:(NSString*)file onComplete:(WebScriptObject*)callback { +	NSURL* fileUrl  = [NSURL fileURLWithPath:[[Utils sharedInstance] pathForResource:file]]; +	DebugNSLog(@"Sound file:%@", [fileUrl description]); +	 +	NSSound* sound = [[NSSound alloc] initWithContentsOfURL:fileUrl byReference:YES]; +    [self playSound:sound onComplete:callback]; +} + +- (void) playSystem:(NSString*)name onComplete:(WebScriptObject*)callback { +    NSSound *systemSound = [NSSound soundNamed:name]; +    [self playSound:systemSound onComplete:callback]; +} + +#pragma mark WebScripting Protocol + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { +    return [self webScriptNameForSelector:selector] == nil; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name { +	return YES; +} + ++ (NSString*) webScriptNameForSelector:(SEL)selector { +    id	result = nil; + +    if (selector == @selector(play:onComplete:)) { +            result = @"play"; +    } +    else if (selector == @selector(playSystem:onComplete:)) { +            result = @"playSystem"; +    } + +    return result; +} + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.h new file mode 100644 index 00000000..269191b3 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.h @@ -0,0 +1,43 @@ +// +//  UserDefaults.h +//  MacGap +// +//  Created by Jeff Hanbury on 16/04/2014. +//  Copyright (c) 2014 Twitter. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#import "WindowController.h" + +@interface UserDefaults : NSObject + +@property (nonatomic, retain) WebView *webView; + +- (id) initWithWebView:(WebView *)view; +- (NSString*) getMyDefaults; +- (NSDictionary*) myDefaultsDictionary; +- (void) removeObjectForKey:(NSString*)key; +- (NSArray*) getUserDefaultsKeys; + +- (NSString*) addPrefix:(NSString*)key; + +- (void) setString:(NSString*)key withValue:(NSString*)value; +- (NSString*) getString:(NSString*)key; + +- (void) setInteger:(NSString*)key withValue:(NSString*)value; +- (NSNumber*) getInteger:(NSString*)key; + +- (void) setBool:(NSString*)key withValue:(NSString*)value; +- (NSNumber*) getBool:(NSString*)key; + +- (void) setFloat:(NSString*)key withValue:(NSString*)value; +- (NSNumber*) getFloat:(NSString*)key; + +// Could also be implemented: +//– setObject:forKey: +//– setDouble:forKey: +//– setURL:forKey: + +@end + diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.m new file mode 100644 index 00000000..48568710 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/UserDefaults.m @@ -0,0 +1,211 @@ +// +//  UserDefaults.m +//  MacGap +// +//  Created by Jeff Hanbury on 16/04/2014. +//  Copyright (c) 2014 Twitter. All rights reserved. +// + +#import "UserDefaults.h" +#import "JSEventHelper.h" + +@interface UserDefaults() { + +} + +-(void) setupNotificationCenter; + +@end + + +@implementation UserDefaults + +- (id) initWithWebView:(WebView *) view{ +    self = [super init]; +     +    if (self) { +        self.webView = view; +        [self setupNotificationCenter]; +    } +     +    return self; +} + + +-(void) setupNotificationCenter{ +    [[NSNotificationCenter defaultCenter] addObserver:self +                                             selector:@selector(defaultsChanged:) +                                                 name:NSUserDefaultsDidChangeNotification +                                               object:nil]; +} + +- (void)defaultsChanged:(NSNotification *)notification { +    NSDictionary* returnDict = [self myDefaultsDictionary]; +    [JSEventHelper triggerEvent:@"userDefaultsChanged" withArgs:returnDict forWebView:self.webView]; +} + +- (NSString*) getMyDefaults { +    NSDictionary* myDefaults = [self myDefaultsDictionary]; + +    return [[Utils sharedInstance] convertDictionaryToJSON:myDefaults]; +} + +- (NSDictionary*) myDefaultsDictionary { +    NSString* prefix = [kWebScriptNamespace stringByAppendingString:@"_"]; +    NSMutableDictionary* returnDict = [[NSMutableDictionary alloc] init]; + +    // Get the user defaults. +    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; +     +    // Build up a dictionary containing just the items beginning with our +    // prefix. +    for (NSString* key in [self getUserDefaultsKeys]) { +        if ([key hasPrefix:prefix]) { +            id val = [defaults valueForKey:key]; +            [returnDict setObject:val forKey:key]; +        } +    } + +    return returnDict; +} + +- (NSArray*) getUserDefaultsKeys { +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    return [[prefs dictionaryRepresentation] allKeys]; +} + +- (void) removeObjectForKey:(NSString*)key { +    NSString* prefixedKey; +    prefixedKey = [self addPrefix:key]; + +    [[NSUserDefaults standardUserDefaults] removeObjectForKey:prefixedKey]; +    [[NSUserDefaults standardUserDefaults] synchronize]; +} + +// Check we have a standard prefix for JS-modified keys, for security purposes. +// If not, add it. This stops JavaScript from ever being able to modify keys +// it did not create. +- (NSString*) addPrefix:(NSString*)key { +    NSString* prefix; +    prefix = [kWebScriptNamespace stringByAppendingString:@"_"]; +     +    if (![key hasPrefix:prefix]) { +        key = [prefix stringByAppendingString:key]; +    } +    return key; +} + +// String + +- (void) setString:(NSString*)key withValue:(NSString*)value { +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    NSString* prefixedKey; +    prefixedKey = [self addPrefix:key]; +    [prefs setObject:value forKey:prefixedKey]; +} + +- (NSString*) getString:(NSString *)key { +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    return [prefs stringForKey:key]; +} + +// All the following must convert their type to NSNumber for JavaScript. + +// Integer + +- (void) setInteger:(NSString*)key withValue:(NSString*)value { +    NSString* prefixedKey; +    prefixedKey = [self addPrefix:key]; + +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    NSInteger myInt = [value intValue]; +    [prefs setInteger:myInt forKey:prefixedKey]; +} + +- (NSNumber*) getInteger:(NSString *)key { +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    return [NSNumber numberWithInteger:[prefs integerForKey:key]]; +} + +// Boolean + +- (void) setBool:(NSString*)key withValue:(NSString*)value { +    NSString* prefixedKey; +    prefixedKey = [self addPrefix:key]; + +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    BOOL myBool = [value boolValue]; +    [prefs setBool:myBool forKey:prefixedKey]; +} + +- (NSNumber*) getBool:(NSString *)key { +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    return [NSNumber numberWithBool:[prefs boolForKey:key]]; +} + +// Float + +- (void) setFloat:(NSString*)key withValue:(NSString*)value { +    NSString* prefixedKey; +    prefixedKey = [self addPrefix:key]; + +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    float myFloat = [value floatValue]; +    [prefs setFloat:myFloat forKey:prefixedKey]; +} + +- (NSNumber*) getFloat:(NSString *)key { +    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; +    return [NSNumber numberWithFloat:[prefs floatForKey:key]]; +} + + +#pragma mark WebScripting Protocol + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { +    return NO; +} + ++ (NSString*) webScriptNameForSelector:(SEL)selector { +	id	result = nil; +	 +	if (selector == @selector(getMyDefaults)) { +		result = @"getMyDefaults"; +    } +     +	if (selector == @selector(removeObjectForKey:)) { +		result = @"removeObjectForKey"; +    } +     +    else if (selector == @selector(setString:withValue:)) { +		result = @"setString"; +    } else if (selector == @selector(getString:)) { +        result = @"getString"; +    } +     +    else if (selector == @selector(setInteger:withValue:)) { +		result = @"setInteger"; +    } else if (selector == @selector(getInteger:)) { +        result = @"getInteger"; +    } +     +    else if (selector == @selector(setBool:withValue:)) { +		result = @"setBool"; +    } else if (selector == @selector(getBool:)) { +        result = @"getBool"; +    } + +    else if (selector == @selector(setFloat:withValue:)) { +		result = @"setFloat"; +    } else if (selector == @selector(getFloat:)) { +        result = @"getFloat"; +    } + +	return result; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name { +	return NO; +} + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.h b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.h new file mode 100644 index 00000000..62c7b7e8 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.h @@ -0,0 +1,9 @@ +@interface Fonts : NSObject { +} + +- (NSArray*) availableFonts; +- (NSArray*) availableFontFamilies; +- (NSArray*) availableMembersOfFontFamily:(NSString*)fontFamily; +- (CGFloat)  defaultLineHeightForFont:(NSString *)theFontName ofSize:(CGFloat)theFontSize; + +@end diff --git a/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.m b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.m new file mode 100644 index 00000000..b17818a5 --- /dev/null +++ b/ext/mac-ui-macgap1-wrapper/src/MacGap/Classes/Commands/fonts.m @@ -0,0 +1,48 @@ +#import "fonts.h" + +@implementation Fonts + + +- (NSArray*) availableFonts { +    return [[NSFontManager sharedFontManager] availableFonts]; +} + +- (NSArray*) availableFontFamilies { +    return [[NSFontManager sharedFontManager] availableFontFamilies]; +} + +- (NSArray*) availableMembersOfFontFamily:(NSString *)fontFamily { +    return [[NSFontManager sharedFontManager] availableMembersOfFontFamily:fontFamily]; +} + +- (CGFloat) defaultLineHeightForFont:(NSString*)theFontName ofSize:(CGFloat)theFontSize { +    NSFont *theFont = [NSFont fontWithName:theFontName size:theFontSize]; +    NSLayoutManager *lm = [[NSLayoutManager alloc] init]; +     +    return [lm defaultLineHeightForFont:theFont]; +} + + +#pragma mark WebScripting Protocol + ++ (BOOL) isSelectorExcludedFromWebScript:(SEL)selector { +    return NO; +} + ++ (NSString*) webScriptNameForSelector:(SEL)selector { +	id	result = nil; +	 +	if (selector == @selector(availableMembersOfFontFamily:)) { +		result = @"availableMembersOfFontFamily"; +    } else if (selector == @selector(defaultLineHeightForFont:ofSize:)) { +        result = @"defaultLineHeightForFont"; +    } + +	return result; +} + ++ (BOOL) isKeyExcludedFromWebScript:(const char*)name { +	return NO; +} + +@end | 
