This repository has been archived on 2025-08-18. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
Bungloo/Mac/Controller.m

538 lines
19 KiB
Objective-C

//
// Controller.m
// Tentia
//
// Created by Jeena on 15.04.10.
// Licence: BSD (see attached LICENCE.txt file).
//
#import "Controller.h"
#import "NewMessageWindow.h"
#import "PostModel.h"
#import "NSData+Base64.h"
@implementation Controller
@synthesize showProfileTextField;
@synthesize openProfileWindow;
@synthesize loginViewWindow;
@synthesize loginEntityTextField;
@synthesize loginActivityIndicator;
@synthesize timelineView, timelineViewWindow, mentionsView, mentionsViewWindow, conversationView, conversationViewWindow, profileView, profileViewWindow;
@synthesize globalHotkeyMenuItem, viewDelegate;
@synthesize logoLayer;
@synthesize oauthView, accessToken;
- (void)awakeFromNib
{
[timelineViewWindow setExcludedFromWindowsMenu:YES];
[mentionsViewWindow setExcludedFromWindowsMenu:YES];
[self initHotKeys];
[GrowlApplicationBridge setGrowlDelegate:self];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(openNewMessageWindow:)
name:@"openNewMessageWindow"
object:nil];
[nc addObserver:self
selector:@selector(sendTweet:)
name:@"sendTweet"
object:nil];
[nc addObserver:self
selector:@selector(authentificationSucceded:)
name:@"authentificationSucceded"
object:nil];
[nc addObserver:self
selector:@selector(getTweetUpdates:)
name:@"getTweetUpdates"
object:nil];
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:self
andSelector:@selector(handleGetURLEvent:withReplyEvent:)
forEventClass:kInternetEventClass
andEventID:kAEGetURL];
viewDelegate = [[ViewDelegate alloc] init];
accessToken = [[AccessToken alloc] init];
BOOL forceLogin = NO;
if (![accessToken stringForKey:@"version-0.6.0-new-login"]) {
[self logout:self];
forceLogin = YES;
[accessToken setString:nil forKey:@"entity"];
[accessToken setString:@"yes" forKey:@"version-0.6.0-new-login"];
}
if (forceLogin || ![accessToken stringForKey:@"user_access_token"] || ![accessToken secret]) {
[timelineViewWindow performClose:self];
[mentionsViewWindow performClose:self];
[self.loginViewWindow makeKeyAndOrderFront:self];
[self initOauth];
} else {
[timelineViewWindow makeKeyAndOrderFront:self];
[self initWebViews];
}
}
# pragma mark Init
- (void)initOauth
{
if (!oauthView) {
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/Webkit/"];
NSURL *url = [NSURL fileURLWithPath:path];
NSString *index_string = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@index.html", path] encoding:NSUTF8StringEncoding error:nil];
oauthView = [[WebView alloc] init];
//WebPreferences* prefs = [oauthView preferences];
//[prefs _setLocalStorageDatabasePath:@"~/Library/Application Support/Tentia"];
//[prefs setLocalStorageEnabled:YES];
viewDelegate.oauthView = oauthView;
[[oauthView mainFrame] loadHTMLString:index_string baseURL:url];
[oauthView setFrameLoadDelegate:viewDelegate];
[oauthView setPolicyDelegate:viewDelegate];
[oauthView setUIDelegate:viewDelegate];
[[oauthView windowScriptObject] setValue:self forKey:@"controller"];
//[oauthView stringByEvaluatingJavaScriptFromString:@"function HostAppGo() { start('oauth'); };"];
} else {
//[oauthView stringByEvaluatingJavaScriptFromString:@"start('oauth');"];
}
}
- (void)initWebViews
{
if (viewDelegate.timelineView != timelineView)
{
[self initOauth];
//NSString *localStoragePath = @"~/Library/Application Support/Tentia";
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/Webkit/"];
NSURL *url = [NSURL fileURLWithPath:path];
NSString *index_string = [NSString stringWithContentsOfFile:[NSString stringWithFormat:@"%@index.html", path] encoding:NSUTF8StringEncoding error:nil];
viewDelegate.timelineView = timelineView;
[[timelineView mainFrame] loadHTMLString:index_string baseURL:url];
[timelineView setFrameLoadDelegate:viewDelegate];
[timelineView setPolicyDelegate:viewDelegate];
[timelineView setUIDelegate:viewDelegate];
[[timelineView windowScriptObject] setValue:self forKey:@"controller"];
//WebPreferences* prefs = [timelineView preferences];
//[prefs _setLocalStorageDatabasePath:localStoragePath];
//[prefs setLocalStorageEnabled:YES];
viewDelegate.mentionsView = mentionsView;
[[mentionsView mainFrame] loadHTMLString:index_string baseURL:url];
[mentionsView setFrameLoadDelegate:viewDelegate];
[mentionsView setPolicyDelegate:viewDelegate];
[mentionsView setUIDelegate:viewDelegate];
[[mentionsView windowScriptObject] setValue:self forKey:@"controller"];
//prefs = [mentionsView preferences];
//[prefs _setLocalStorageDatabasePath:localStoragePath];
//[prefs setLocalStorageEnabled:YES];
viewDelegate.conversationView = conversationView;
[[conversationView mainFrame] loadHTMLString:index_string baseURL:url];
[conversationView setFrameLoadDelegate:viewDelegate];
[conversationView setPolicyDelegate:viewDelegate];
[conversationView setUIDelegate:viewDelegate];
[[conversationView windowScriptObject] setValue:self forKey:@"controller"];
//prefs = [conversationView preferences];
//[prefs _setLocalStorageDatabasePath:localStoragePath];
//[prefs setLocalStorageEnabled:YES];
viewDelegate.profileView = profileView;
[[profileView mainFrame] loadHTMLString:index_string baseURL:url];
[profileView setFrameLoadDelegate:viewDelegate];
[profileView setPolicyDelegate:viewDelegate];
[profileView setUIDelegate:viewDelegate];
[[profileView windowScriptObject] setValue:self forKey:@"controller"];
//prefs = [profileView preferences];
//[prefs _setLocalStorageDatabasePath:localStoragePath];
//[prefs setLocalStorageEnabled:YES];
}
else
{
[timelineView stringByEvaluatingJavaScriptFromString:@"start('timeline')"];
[mentionsView stringByEvaluatingJavaScriptFromString:@"start('mentions')"];
[conversationView stringByEvaluatingJavaScriptFromString:@"start('conversation')"];
[profileView stringByEvaluatingJavaScriptFromString:@"start('profile')"];
}
}
- (void)initHotKeys
{
NSInteger newTweetKey = kVK_ANSI_M; // http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes
NSInteger newTweetModifierKey = controlKey + cmdKey + optionKey; // cmdKey 256, shitfKey 512, optionKey 2048, controlKey 4096
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger defaultsNewTweetKey = (NSInteger)[defaults integerForKey:@"newTweetKey"];
if ([defaults objectForKey:@"newTweetKey"] != nil)
{
newTweetKey = defaultsNewTweetKey;
}
else
{
[defaults setInteger:newTweetKey forKey:@"newTweetKey"];
}
NSInteger defaultsNewTweetModifierKey = (NSInteger)[defaults integerForKey:@"newTweetModifierKey"];
if ([defaults objectForKey:@"newTweetModifierKey"] != nil)
{
newTweetModifierKey = defaultsNewTweetModifierKey;
}
else
{
[defaults setInteger:newTweetModifierKey forKey:@"newTweetModifierKey"];
}
[defaults synchronize];
NSUInteger cocoaModifiers = 0;
if (newTweetModifierKey & shiftKey) cocoaModifiers = cocoaModifiers | NSShiftKeyMask;
if (newTweetModifierKey & optionKey) cocoaModifiers = cocoaModifiers | NSAlternateKeyMask;
if (newTweetModifierKey & controlKey) cocoaModifiers = cocoaModifiers | NSControlKeyMask;
if (newTweetModifierKey & cmdKey) cocoaModifiers = cocoaModifiers | NSCommandKeyMask;
[globalHotkeyMenuItem setKeyEquivalent:[Constants stringFromVirtualKeyCode:newTweetKey]];
[globalHotkeyMenuItem setKeyEquivalentModifierMask:cocoaModifiers];
/* CARBON from http://github.com/Xjs/drama-button/blob/carbon/Drama_ButtonAppDelegate.m */
EventTypeSpec eventType;
eventType.eventClass = kEventClassKeyboard;
eventType.eventKind = kEventHotKeyPressed;
InstallApplicationEventHandler(&handler, 1, &eventType, NULL, NULL);
EventHotKeyID g_HotKeyID;
g_HotKeyID.id = 1;
EventHotKeyRef g_HotKeyRef;
RegisterEventHotKey(newTweetKey, newTweetModifierKey, g_HotKeyID, GetApplicationEventTarget(), 0, &g_HotKeyRef);
/* end CARBON */
}
- (void)alertTitle:(NSString *)title withMessage:(NSString *)message
{
NSAlert *alert = [NSAlert alertWithMessageText:title
defaultButton:@"OK" alternateButton:nil otherButton:nil
informativeTextWithFormat:@"%@", message];
[alert runModal];
}
- (void)authentificationSucceded:(id)sender
{
[loginActivityIndicator stopAnimation:self];
[self initWebViews];
[loginViewWindow performClose:self];
}
- (void)authentificationDidNotSucceed:(NSString *)errorMessage
{
[loginActivityIndicator stopAnimation:self];
[self alertTitle:@"Authenication error" withMessage:errorMessage];
}
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
{
return NO;
}
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name
{
return NO;
}
- (void)setString:(NSString *)string forKey:(NSString *)aKey
{
[self.accessToken setString:string forKey:aKey];
}
- (void)setSecret:(NSString *)string
{
[self.accessToken setSecret:string];
}
- (NSString *)secret
{
return [self.accessToken secret];
}
- (NSString *)stringForKey:(NSString *)aKey
{
return [self.accessToken stringForKey:aKey];
}
#pragma mark Notifications
-(BOOL)applicationShouldOpenUntitledFile:(NSApplication *)theApplication
{
return NO;
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
{
[timelineViewWindow makeKeyAndOrderFront:self];
return NO;
}
- (IBAction)openNewMessageWindow:(id)sender
{
[NSApp activateIgnoringOtherApps:YES];
[[NSDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:nil];
}
- (void)openNewMessageWindowInReplyTo:(NSString *)userName statusId:(NSString *)statusId withString:(NSString *)string isPrivate:(BOOL)isPrivate
{
[NSApp activateIgnoringOtherApps:YES];
NewMessageWindow *newMessage = (NewMessageWindow *)[[NSDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:nil];
[newMessage inReplyTo:userName statusId:statusId withString:string];
[newMessage setIsPrivate:isPrivate];
}
- (void)openNewMessageWindowWithString:(NSString *)aString
{
[NSApp activateIgnoringOtherApps:YES];
NSRange range = [aString rangeOfString:@"oauthtoken"];
if (range.length > 0)
{
[oauthView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"tentia_instance.requestAccessToken('%@')", aString]];
}
else
{
NewMessageWindow *newTweet = (NewMessageWindow *)[[NSDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:nil];
[newTweet withString:aString];
}
}
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
NSString *text = [[[event paramDescriptorForKeyword:keyDirectObject] stringValue] substringFromIndex:8];
[self openNewMessageWindowWithString:[text stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
- (IBAction)sendTweet:(id)sender
{
PostModel *post = (PostModel *)[sender object];
NSString *text = [[post.text stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
text = [text stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
NSString *locationObject = @"null";
if (post.location) {
locationObject = [NSString stringWithFormat:@"[%f, %f]", post.location.coordinate.latitude, post.location.coordinate.longitude];
}
NSString *imageFilePath = @"null";
if (post.imageFilePath) {
NSError *error;
NSString *mimeType = [MimeType mimeTypeForFileAtPath:post.imageFilePath error:&error];
NSData *data = [[NSData alloc] initWithContentsOfFile:post.imageFilePath];
NSString *base64 = [data base64Encoding_xcd];
[data release];
imageFilePath = [NSString stringWithFormat:@"\"data:%@;base64,%@\"", mimeType, base64];
}
NSString *isPrivate = @"false";
if (post.isPrivate) {
isPrivate = @"true";
}
NSString *func = [NSString stringWithFormat:@"tentia_instance.sendNewMessage(\"%@\", \"%@\", \"%@\", %@, %@, %@)",
text,
post.inReplyTostatusId,
post.inReplyToEntity,
locationObject,
imageFilePath,
isPrivate];
[timelineView stringByEvaluatingJavaScriptFromString:func];
}
- (NSString *)pluginURL
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *pathToPlugin = [@"~/Library/Application Support/Tentia/Plugin.js" stringByExpandingTildeInPath];
if([fileManager fileExistsAtPath:pathToPlugin])
{
return [NSString stringWithFormat:@"%@", [NSURL fileURLWithPath:pathToPlugin]];
}
return nil;
}
- (void)unreadMentions:(int)count
{
if (![mentionsViewWindow isVisible] && count > 0)
{
[timelineViewWindow setTitle:[NSString stringWithFormat:@"Tentia (^%i)", count]];
[[[NSApplication sharedApplication] dockTile] setBadgeLabel:[NSString stringWithFormat:@"%i", count]];
}
else
{
[timelineViewWindow setTitle:[NSString stringWithFormat:@"Tentia"]];
[[[NSApplication sharedApplication] dockTile] setBadgeLabel:nil];
[mentionsView stringByEvaluatingJavaScriptFromString:@"tentia_instance.unread_mentions = 0;"];
}
}
- (void)notificateUserAboutMention:(NSString *)text fromName:(NSString *)name withPostId:(NSString *)postId andEntity:(NSString *)entity
{
[GrowlApplicationBridge
notifyWithTitle:[NSString stringWithFormat:@"Mentioned by %@ on Tent", name]
description:text
notificationName:@"Mention"
iconData:nil
priority:0
isSticky:NO
clickContext:[NSDictionary dictionaryWithObjectsAndKeys:
entity, @"entity",
postId, @"postId", nil]];
}
- (void)openURL:(NSString *)url
{
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]];
}
- (IBAction)showProfile:(id)sender
{
NSString *entity = [self.showProfileTextField stringValue];
if ([entity rangeOfString:@"."].location != NSNotFound && ([entity hasPrefix:@"http://"] || [entity hasPrefix:@"https://"])) {
NSString *func = [NSString stringWithFormat:@"tentia_instance.showProfileForEntity('%@')", entity];
[profileView stringByEvaluatingJavaScriptFromString:func];
[profileViewWindow makeKeyAndOrderFront:self];
[openProfileWindow performClose:self];
}
}
- (void)notificateViewsAboutDeletedPostWithId:(NSString *)postId byEntity:(NSString*)entity
{
NSString *fun = [NSString stringWithFormat:@"tentia_instance.postDeleted('%@', '%@')", postId, entity];
[timelineView stringByEvaluatingJavaScriptFromString:fun];
[mentionsView stringByEvaluatingJavaScriptFromString:fun];
[conversationView stringByEvaluatingJavaScriptFromString:fun];
[profileView stringByEvaluatingJavaScriptFromString:fun];
}
/*
- (void)storeAccessToken:(NSString *)_accessToken secret:(NSString *)secret userId:(NSString *)userId andScreenName:(NSString *)screenName
{
self.accessToken.accessToken = _accessToken;
self.accessToken.secret = secret;
self.accessToken.userId = userId;
self.accessToken.screenName = screenName;
[timelineViewWindow makeKeyAndOrderFront:self];
[[NSNotificationCenter defaultCenter] postNotificationName:@"authentificationSucceded" object:nil];
}*/
- (void)loggedIn
{
[loginActivityIndicator stopAnimation:self];
[self initWebViews];
[loginViewWindow performClose:self];
[timelineViewWindow makeKeyAndOrderFront:self];
}
- (IBAction)login:(id)sender
{
if ([[loginEntityTextField stringValue] length] > 0) {
[[loginEntityTextField window] makeFirstResponder:nil];
[loginActivityIndicator startAnimation:self];
[oauthView stringByEvaluatingJavaScriptFromString:@"tentia_instance.authenticate();"];
}
}
- (IBAction)logout:(id)sender
{
[oauthView stringByEvaluatingJavaScriptFromString:@"tentia_instance.logout();"];
[timelineViewWindow performClose:self];
[mentionsViewWindow performClose:self];
[conversationViewWindow performClose:self];
[profileViewWindow performClose:self];
[self.loginViewWindow makeKeyAndOrderFront:self];
[timelineView stringByEvaluatingJavaScriptFromString:@"tentia_instance.logout();"];
[mentionsView stringByEvaluatingJavaScriptFromString:@"tentia_instance.logout();"];
}
// Mentions window has been visible
- (void)windowDidBecomeKey:(NSNotification *)notification
{
if ([notification object] == mentionsViewWindow)
{
//[self unreadMentions:0];
[mentionsView stringByEvaluatingJavaScriptFromString:@"tentia_instance.setAllMentionsRead();"];
}
}
- (void)getTweetUpdates:(id)sender
{
[timelineView stringByEvaluatingJavaScriptFromString:@"tentia_instance.getNewData(true)"];
[mentionsView stringByEvaluatingJavaScriptFromString:@"tentia_instance.getNewData(true)"];
}
- (IBAction)showConversationForPostId:(NSString *)postId andEntity:(NSString *)entity
{
NSString *js = [NSString stringWithFormat:@"tentia_instance.showStatus('%@', '%@');", postId, entity];
[conversationView stringByEvaluatingJavaScriptFromString:js];
[conversationViewWindow makeKeyAndOrderFront:self];
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}
- (IBAction)clearCache:(id)sender
{
[timelineView stringByEvaluatingJavaScriptFromString:@"tentia_instance.cache.clear()"];
}
- (IBAction)showProfileForEntity:(NSString *)entity
{
NSString *js = [NSString stringWithFormat:@"tentia_instance.showProfileForEntity('%@');", entity];
[profileView stringByEvaluatingJavaScriptFromString:js];
[profileViewWindow makeKeyAndOrderFront:self];
}
- (void)growlNotificationWasClicked:(id)clickContext
{
NSDictionary *userInfo = (NSDictionary *)clickContext;
NSString *postId = [userInfo objectForKey:@"postId"];
NSString *entity = [userInfo objectForKey:@"entity"];
[self showConversationForPostId:postId andEntity:entity];
NSString *js = [NSString stringWithFormat:@"tentia_instance.mentionRead('%@', '%@');", postId, entity];
[mentionsView stringByEvaluatingJavaScriptFromString:js];
}
- (NSString *) applicationNameForGrowl
{
return @"Tentia";
}
/* CARBON */
OSStatus handler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
[[NSNotificationCenter defaultCenter] postNotificationName:@"openNewMessageWindow" object:nil];
return noErr;
}
@end