adium 2344:b5f68ee73ac3: An image uploader plugin. Found in the ...
commits at adium.im
commits at adium.im
Wed May 27 01:49:30 UTC 2009
details: http://hg.adium.im/adium/rev/b5f68ee73ac3
revision: 2344:b5f68ee73ac3
author: Zachary West <zacw at adium.im>
date: Tue May 26 21:49:21 2009 -0400
An image uploader plugin. Found in the same locations as "Replace with Shortened URL."
Currently the only supported service is pic.im, and even then not fully since we don't have an API key for uploading yet. Once we get one it will be simple to finish this plugin off.
I'm not willing to add any services that require a username/password, so most of the traditional services will never be available.
diffstat:
Adium.xcodeproj/project.pbxproj | 30 +
Plugins/Image Uploading Plugin/AIImageUploaderPlugin.h | 42 +
Plugins/Image Uploading Plugin/AIImageUploaderPlugin.m | 287 +++++++++
Plugins/Image Uploading Plugin/AIImageUploaderWindowController.h | 29 +
Plugins/Image Uploading Plugin/AIImageUploaderWindowController.m | 98 +++
Plugins/Image Uploading Plugin/AIPicImImageUploader.h | 27 +
Plugins/Image Uploading Plugin/AIPicImImageUploader.m | 285 +++++++++
Plugins/Image Uploading Plugin/ImageUploaderProgress.nib/classes.nib | 211 +++++++
Plugins/Image Uploading Plugin/ImageUploaderProgress.nib/info.nib | 20 +
Plugins/Image Uploading Plugin/ImageUploaderProgress.nib/keyedobjects.nib |
Source/AICoreComponentLoader.m | 1 +
11 files changed, 1030 insertions(+), 0 deletions(-)
diffs (truncated from 1119 to 1000 lines):
diff -r e20f4841e08d -r b5f68ee73ac3 Adium.xcodeproj/project.pbxproj
--- a/Adium.xcodeproj/project.pbxproj Tue May 26 14:27:00 2009 -0400
+++ b/Adium.xcodeproj/project.pbxproj Tue May 26 21:49:21 2009 -0400
@@ -151,6 +151,10 @@
11BD73AE0F5A53EA007D438A /* facebook.png in Resources */ = {isa = PBXBuildFile; fileRef = 345D68950F1FD563002F2D01 /* facebook.png */; };
11BD73D30F5A54BB007D438A /* twitter-small.png in Resources */ = {isa = PBXBuildFile; fileRef = 11BD73D10F5A54BB007D438A /* twitter-small.png */; };
11BD73D40F5A54BB007D438A /* twitter.png in Resources */ = {isa = PBXBuildFile; fileRef = 11BD73D20F5A54BB007D438A /* twitter.png */; };
+ 11BE289A0FCC7C13000E6A10 /* AIPicImImageUploader.m in Sources */ = {isa = PBXBuildFile; fileRef = 11BE28990FCC7C13000E6A10 /* AIPicImImageUploader.m */; };
+ 11BE28DF0FCC7D2B000E6A10 /* AIImageUploaderPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 11BE28DE0FCC7D2B000E6A10 /* AIImageUploaderPlugin.m */; };
+ 11BE29640FCCA8DA000E6A10 /* ImageUploaderProgress.nib in Resources */ = {isa = PBXBuildFile; fileRef = 11BE29630FCCA8DA000E6A10 /* ImageUploaderProgress.nib */; };
+ 11BE29680FCCA9E3000E6A10 /* AIImageUploaderWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 11BE29670FCCA9E3000E6A10 /* AIImageUploaderWindowController.m */; };
11C7CAF30BB58F4100296D7F /* AINudgeBuzzHandlerPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 11C7CAF10BB58F4100296D7F /* AINudgeBuzzHandlerPlugin.m */; };
11C97DFD0F7D26C200E8DD37 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 11C97DFC0F7D26C200E8DD37 /* dsa_pub.pem */; };
11CA00100C35EC5100A6BB8D /* SS_PreferencePaneProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 344836910BC8510B0083723B /* SS_PreferencePaneProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1884,6 +1888,13 @@
11AE52010F68CA5000BE8077 /* AIPurpleOscarAccountViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIPurpleOscarAccountViewController.m; path = "Plugins/Purple Service/AIPurpleOscarAccountViewController.m"; sourceTree = "<group>"; };
11BD73D10F5A54BB007D438A /* twitter-small.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "twitter-small.png"; path = "Plugins/Twitter Plugin/twitter-small.png"; sourceTree = "<group>"; };
11BD73D20F5A54BB007D438A /* twitter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = twitter.png; path = "Plugins/Twitter Plugin/twitter.png"; sourceTree = "<group>"; };
+ 11BE28980FCC7C13000E6A10 /* AIPicImImageUploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIPicImImageUploader.h; path = "Plugins/Image Uploading Plugin/AIPicImImageUploader.h"; sourceTree = "<group>"; };
+ 11BE28990FCC7C13000E6A10 /* AIPicImImageUploader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIPicImImageUploader.m; path = "Plugins/Image Uploading Plugin/AIPicImImageUploader.m"; sourceTree = "<group>"; };
+ 11BE28DD0FCC7D2B000E6A10 /* AIImageUploaderPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIImageUploaderPlugin.h; path = "Plugins/Image Uploading Plugin/AIImageUploaderPlugin.h"; sourceTree = "<group>"; };
+ 11BE28DE0FCC7D2B000E6A10 /* AIImageUploaderPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIImageUploaderPlugin.m; path = "Plugins/Image Uploading Plugin/AIImageUploaderPlugin.m"; sourceTree = "<group>"; };
+ 11BE29630FCCA8DA000E6A10 /* ImageUploaderProgress.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = ImageUploaderProgress.nib; path = "Plugins/Image Uploading Plugin/ImageUploaderProgress.nib"; sourceTree = "<group>"; };
+ 11BE29660FCCA9E3000E6A10 /* AIImageUploaderWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AIImageUploaderWindowController.h; path = "Plugins/Image Uploading Plugin/AIImageUploaderWindowController.h"; sourceTree = "<group>"; };
+ 11BE29670FCCA9E3000E6A10 /* AIImageUploaderWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AIImageUploaderWindowController.m; path = "Plugins/Image Uploading Plugin/AIImageUploaderWindowController.m"; sourceTree = "<group>"; };
11C157D804A88E04008E0C76 /* AIEmoticonController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = AIEmoticonController.h; path = Source/AIEmoticonController.h; sourceTree = "<group>"; };
11C157D904A88E04008E0C76 /* AIEmoticonController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = AIEmoticonController.m; path = Source/AIEmoticonController.m; sourceTree = "<group>"; };
11C7CAF00BB58F4100296D7F /* AINudgeBuzzHandlerPlugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AINudgeBuzzHandlerPlugin.h; sourceTree = "<group>"; };
@@ -4866,6 +4877,20 @@
name = Frameworks;
sourceTree = "<group>";
};
+ 11BE28E30FCC7DA8000E6A10 /* Image Uploading */ = {
+ isa = PBXGroup;
+ children = (
+ 11BE29630FCCA8DA000E6A10 /* ImageUploaderProgress.nib */,
+ 11BE29660FCCA9E3000E6A10 /* AIImageUploaderWindowController.h */,
+ 11BE29670FCCA9E3000E6A10 /* AIImageUploaderWindowController.m */,
+ 11BE28DD0FCC7D2B000E6A10 /* AIImageUploaderPlugin.h */,
+ 11BE28DE0FCC7D2B000E6A10 /* AIImageUploaderPlugin.m */,
+ 11BE28980FCC7C13000E6A10 /* AIPicImImageUploader.h */,
+ 11BE28990FCC7C13000E6A10 /* AIPicImImageUploader.m */,
+ );
+ name = "Image Uploading";
+ sourceTree = "<group>";
+ };
11C157D504A88DE0008E0C76 /* Emoticons */ = {
isa = PBXGroup;
children = (
@@ -7014,6 +7039,7 @@
4BCAC53407B59F9C006641B9 /* Other */ = {
isa = PBXGroup;
children = (
+ 11BE28E30FCC7DA8000E6A10 /* Image Uploading */,
34FDD6E305632BFD00F5B30F /* ESFileTransferController.h */,
34FDD6E405632BFD00F5B30F /* ESFileTransferController.m */,
F57938FB033E745F01A8010A /* AILoginController.h */,
@@ -9769,6 +9795,7 @@
113900B10F85BEF60081A418 /* AIURLHandlerPreferences.nib in Resources */,
11D135D90FBE4C65000B0A5E /* AITwitterAccountView.nib in Resources */,
11A2F10A0FC8FC1A00C3F05C /* AIMessageAlertsAdvancedPreferences.nib in Resources */,
+ 11BE29640FCCA8DA000E6A10 /* ImageUploaderProgress.nib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -10424,6 +10451,9 @@
112E7FA60FC86BB400657119 /* AITwitterActionsHTMLFilter.m in Sources */,
11A2F1220FC8FC9D00C3F05C /* AIMessageAlertsAdvancedPreferences.m in Sources */,
638BC1FC0FC932E000CE7600 /* AIObjectDebug.m in Sources */,
+ 11BE289A0FCC7C13000E6A10 /* AIPicImImageUploader.m in Sources */,
+ 11BE28DF0FCC7D2B000E6A10 /* AIImageUploaderPlugin.m in Sources */,
+ 11BE29680FCCA9E3000E6A10 /* AIImageUploaderWindowController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff -r e20f4841e08d -r b5f68ee73ac3 Plugins/Image Uploading Plugin/AIImageUploaderPlugin.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Image Uploading Plugin/AIImageUploaderPlugin.h Tue May 26 21:49:21 2009 -0400
@@ -0,0 +1,42 @@
+//
+// AIImageUploaderPlugin.h
+// Adium
+//
+// Created by Zachary West on 2009-05-26.
+// Copyright 2009 Adium. All rights reserved.
+//
+
+#define PREF_KEY_DEFAULT_IMAGE_UPLOADER @"Default Image Uploader"
+
+#define IMAGE_UPLOAD_MENU_TITLE AILocalizedString(@"Replace with Uploaded Image", nil)
+
+typedef enum {
+ AISuccessfulUpload = 1,
+ AIErrorUpload
+} AIImageUploaderCode;
+
+ at class AIImageUploaderWindowController, AIChat;
+
+ at interface AIImageUploaderPlugin : AIPlugin {
+ NSMutableArray *uploaders;
+ NSString *defaultService;
+
+ NSDictionary *windowControllers;
+ NSDictionary *uploadInstances;
+}
+
+ at property (copy, nonatomic) NSString *defaultService;
+
+- (void)errorWithMessage:(NSString *)message forChat:(AIChat *)chat;
+- (void)uploadedURL:(NSString *)url forChat:(AIChat *)chat;
+- (void)updateProgressPercent:(CGFloat)percent forChat:(AIChat *)chat;
+- (void)cancelForChat:(AIChat *)chat;
+
+ at end
+
+ at protocol AIImageUploader
+// Service name should be unique across all image uploaders.
++ (NSString *)serviceName;
++ (id)uploadImage:(NSImage *)image forUploader:(AIImageUploaderPlugin *)uploader inChat:(AIChat *)chat;
+- (void)cancel;
+ at end
diff -r e20f4841e08d -r b5f68ee73ac3 Plugins/Image Uploading Plugin/AIImageUploaderPlugin.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Image Uploading Plugin/AIImageUploaderPlugin.m Tue May 26 21:49:21 2009 -0400
@@ -0,0 +1,287 @@
+//
+// AIImageUploaderPlugin.m
+// Adium
+//
+// Created by Zachary West on 2009-05-26.
+// Copyright 2009 Adium. All rights reserved.
+//
+
+#import "AIImageUploaderPlugin.h"
+#import "AIPicImImageUploader.h"
+#import "AIImageUploaderWindowController.h"
+
+#import <Adium/AIContentControllerProtocol.h>
+#import <Adium/AIPreferenceControllerProtocol.h>
+#import <Adium/AIMenuControllerProtocol.h>
+#import <Adium/AIInterfaceControllerProtocol.h>
+#import <Adium/AIChat.h>
+
+#import <AIUtilities/AIWindowAdditions.h>
+#import <AIUtilities/AIMenuAdditions.h>
+
+ at interface AIImageUploaderPlugin()
+- (NSImage *)currentImage;
+- (void)uploadImage;
+
+- (void)insertImageAddress:(NSString *)inAddress intoTextView:(NSTextView *)textView;
+ at end
+
+ at implementation AIImageUploaderPlugin
+- (void)installPlugin
+{
+ uploaders = [[NSMutableArray alloc] init];
+ windowControllers = [[NSMutableDictionary alloc] init];
+ uploadInstances = [[NSMutableDictionary alloc] init];
+
+ [uploaders addObject:[AIPicImImageUploader class]];
+
+ NSMenuItem *menuItem;
+
+ NSMenu *subMenu = [[NSMenu alloc] init];
+ [subMenu setDelegate:self];
+
+ // Edit menu
+ menuItem = [[[NSMenuItem alloc] initWithTitle:IMAGE_UPLOAD_MENU_TITLE
+ target:self
+ action:@selector(uploadImage)
+ keyEquivalent:@"K"
+ keyMask:NSCommandKeyMask | NSAlternateKeyMask] autorelease];
+
+ [menuItem setSubmenu:subMenu];
+
+ [adium.menuController addMenuItem:menuItem toLocation:LOC_Edit_Links];
+
+ // Context menu
+ menuItem = [[[NSMenuItem alloc] initWithTitle:IMAGE_UPLOAD_MENU_TITLE
+ target:self
+ action:@selector(uploadImage)
+ keyEquivalent:@""] autorelease];
+
+ [menuItem setSubmenu:[[subMenu copy] autorelease]];
+
+ [adium.menuController addContextualMenuItem:menuItem toLocation:Context_TextView_Edit];
+
+ [adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_FORMATTING];
+}
+
+- (void)uninstallPlugin
+{
+ [adium.preferenceController unregisterPreferenceObserver:self];
+
+ [defaultService release];
+ [windowControllers release];
+ [uploadInstances release];
+ [uploaders release];
+}
+
+#pragma mark Preferences
+ at synthesize defaultService;
+
+- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
+{
+ if (object)
+ return;
+
+ if (!key || [key isEqualToString:PREF_KEY_DEFAULT_IMAGE_UPLOADER]) {
+ self.defaultService = [prefDict objectForKey:PREF_KEY_DEFAULT_IMAGE_UPLOADER];
+ }
+}
+
+#pragma mark Services
+/*!
+ * @brief Set the submenu as a menu of all possible services
+ */
+- (void)menuNeedsUpdate:(NSMenu *)menu
+{
+ [menu removeAllItems];
+
+ for (Class <AIImageUploader> service in uploaders) {
+ NSMenuItem *newItem = [menu addItemWithTitle:[service serviceName]
+ target:self
+ action:@selector(setImageUploader:)
+ keyEquivalent:@""];
+
+ [newItem setRepresentedObject:[service serviceName]];
+
+ [newItem setState:[[service serviceName] isEqualToString:defaultService]];
+ }
+}
+
+/*!
+ * @brief Set the default upload service, then upload.
+ */
+- (void)setImageUploader:(NSMenuItem *)menuItem
+{
+ NSString *serviceName = [menuItem representedObject];
+
+ [adium.preferenceController setPreference:serviceName
+ forKey:PREF_KEY_DEFAULT_IMAGE_UPLOADER
+ group:PREF_GROUP_FORMATTING];
+
+ [self uploadImage];
+}
+
+/*!
+ * @brief If we have a selected image, we can do something.
+ */
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ return [self currentImage] != nil;
+}
+
+#pragma mark Image uploading
+/*!
+ * @brief The current selected image in the text view.
+ *
+ * @return the NSImage in the text view which is selected
+ */
+- (NSImage *)currentImage
+{
+ NSTextView *textView = (NSTextView *)[[NSApp keyWindow] earliestResponderOfClass:[NSTextView class]];
+ if (textView) {
+ NSAttributedString *text = textView.textStorage;
+ NSRange selectedRange = textView.selectedRange;
+
+ //If we have some text and the start of the selection is not at the end of the string...
+ if (text.length && selectedRange.length) {
+ NSTextAttachment *attachment = [text attribute:NSAttachmentAttributeName atIndex:selectedRange.location effectiveRange:NULL];
+
+ if (attachment) {
+ NSImage *image = nil;
+
+ if ([attachment respondsToSelector:@selector(image)])
+ image = [attachment performSelector:@selector(image)];
+ else if ([[attachment attachmentCell] respondsToSelector:@selector(image)])
+ image = [[attachment attachmentCell] performSelector:@selector(image)];
+
+ return image;
+ }
+ }
+ }
+
+ return nil;
+}
+
+/*!
+ * @brief Upload the image.
+ *
+ * Tells the current default service to perform an upload, creates the progress window, and waits.
+ */
+- (void)uploadImage
+{
+ Class <AIImageUploader> uploader = nil;
+
+ for (Class <AIImageUploader> service in uploaders) {
+ uploader = service;
+
+ if ([[service serviceName] isEqualToString:defaultService]) {
+ break;
+ }
+ }
+
+ AIChat *chat = adium.interfaceController.activeChat;
+ NSImage *currentImage = [self currentImage];
+
+ AILogWithSignature(@"Beginning upload for %@ to %@", currentImage, [uploader serviceName]);
+
+ AILogWithSignature(@"Beginning upload for %@ to %@", currentImage, [uploader serviceName]);
+
+ AIImageUploaderWindowController *controller = [AIImageUploaderWindowController displayProgressInWindow:[NSApp keyWindow]
+ delegate:self
+ chat:chat];
+
+ controller.indeterminate = YES;
+
+ id <AIImageUploader> uploadInstance = [uploader uploadImage:currentImage forUploader:self inChat:chat];
+
+ [windowControllers setValue:controller forKey:chat.internalObjectID];
+ [uploadInstances setValue:uploadInstance forKey:chat.internalObjectID];
+}
+
+/*!
+ * @brief Request a URL, insert into text view
+ *
+ * @param inAddress The NSString to insert
+ * @param textView the NSTextView to insert the address itno
+ *
+ * Replaces the selected image in textView with the given address.
+ */
+- (void)insertImageAddress:(NSString *)inAddress intoTextView:(NSTextView *)textView
+{
+ NSRange selectedRange = textView.selectedRange;
+
+ AILogWithSignature(@"Inserting %@ into text view", inAddress);
+
+ // Replace the current selection with the new URL
+ NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithDictionary:[textView.attributedString attributesAtIndex:selectedRange.location effectiveRange:nil]];
+ [attrs setObject:inAddress forKey:NSLinkAttributeName];
+
+ [textView.textStorage replaceCharactersInRange:selectedRange
+ withAttributedString:[[[NSAttributedString alloc] initWithString:inAddress attributes:attrs] autorelease]];
+
+ // Select the inserted URL
+ textView.selectedRange = NSMakeRange(selectedRange.location, inAddress.length);
+
+ // Post a notification that we've changed the text
+ [[NSNotificationCenter defaultCenter] postNotificationName:NSTextDidChangeNotification
+ object:textView];
+}
+
+- (void)errorWithMessage:(NSString *)message forChat:(AIChat *)chat
+{
+ [adium.interfaceController handleErrorMessage:AILocalizedString(@"Error during image upload", nil)
+ withDescription:message];
+
+ [self uploadedURL:nil forChat:chat];
+}
+
+/*!
+ * @brief The upload has finished
+ *
+ * @param url The URL or nil if failed
+ * @param chat The AIChat for this upload
+*/
+- (void)uploadedURL:(NSString *)url forChat:(AIChat *)chat
+{
+ [[windowControllers objectForKey:chat.internalObjectID] closeWindow:nil];
+
+ [windowControllers setValue:nil forKey:chat.internalObjectID];
+ [uploadInstances setValue:nil forKey:chat.internalObjectID];
+
+ AILogWithSignature(@"Received %@ for %@", url, chat);
+
+ if (url) {
+ NSWindow *window = ((AIWindowController *)chat.chatContainer.windowController).window;
+ NSTextView *textView = (NSTextView *)[window earliestResponderOfClass:[NSTextView class]];
+
+ [self insertImageAddress:url intoTextView:textView];
+ }
+}
+
+/*!
+ * @brief Update the progress's percent
+ *
+ * @param percent The percent value, between 0.0-1.0, of the upload.
+ * @param chat The AIChat for the upload
+ */
+- (void)updateProgressPercent:(CGFloat)percent forChat:(AIChat *)chat
+{
+ [[windowControllers objectForKey:chat.internalObjectID] setIndeterminate:NO];
+ [[windowControllers objectForKey:chat.internalObjectID] setProgress:percent];
+}
+
+/*!
+ * @brief Cancel an update
+ *
+ * @param chat The AIChat to cancel for
+ */
+- (void)cancelForChat:(AIChat *)chat
+{
+ [[uploadInstances objectForKey:chat.internalObjectID] cancel];
+ [uploadInstances setValue:nil forKey:chat.internalObjectID];
+ [windowControllers setValue:nil forKey:chat.internalObjectID];
+
+ AILogWithSignature(@"Cancelled image upload for %@", chat);
+}
+
+ at end
diff -r e20f4841e08d -r b5f68ee73ac3 Plugins/Image Uploading Plugin/AIImageUploaderWindowController.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Image Uploading Plugin/AIImageUploaderWindowController.h Tue May 26 21:49:21 2009 -0400
@@ -0,0 +1,29 @@
+//
+// AIImageUploaderWindowController.h
+// Adium
+//
+// Created by Zachary West on 2009-05-26.
+// Copyright 2009 Adium. All rights reserved.
+//
+
+#import <Adium/AIWindowController.h>
+#import "AIImageUploaderPlugin.h"
+
+ at interface AIImageUploaderWindowController : AIWindowController {
+ IBOutlet NSTextField *label_uploadingImage;
+ IBOutlet NSProgressIndicator *progressIndicator;
+ IBOutlet NSButton *button_cancel;
+
+ AIChat *chat;
+ AIImageUploaderPlugin *delegate;
+}
+
+ at property (nonatomic) BOOL indeterminate;
+ at property (nonatomic) CGFloat progress;
+
++ (id)displayProgressInWindow:(NSWindow *)window
+ delegate:(id)inDelegate
+ chat:(AIChat *)inChat;
+- (IBAction)cancel:(id)sender;
+
+ at end
diff -r e20f4841e08d -r b5f68ee73ac3 Plugins/Image Uploading Plugin/AIImageUploaderWindowController.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Image Uploading Plugin/AIImageUploaderWindowController.m Tue May 26 21:49:21 2009 -0400
@@ -0,0 +1,98 @@
+//
+// AIImageUploaderWindowController.m
+// Adium
+//
+// Created by Zachary West on 2009-05-26.
+// Copyright 2009 Adium. All rights reserved.
+//
+
+#import "AIImageUploaderWindowController.h"
+
+#import <AIUtilities/AIStringAdditions.h>
+
+#import <AIUtilities/AIStringAdditions.h>
+
+ at interface AIImageUploaderWindowController()
+- (id)initWithWindowNibName:(NSString *)nibName
+ delegate:(id)inDelegate
+ chat:(AIChat *)inChat;
+ at end
+
+ at implementation AIImageUploaderWindowController
++ (id)displayProgressInWindow:(NSWindow *)window
+ delegate:(id)inDelegate
+ chat:(AIChat *)inChat
+{
+ AIImageUploaderWindowController *newController = [[self alloc] initWithWindowNibName:@"ImageUploaderProgress"
+ delegate:inDelegate
+ chat:inChat];
+
+ [NSApp beginSheet:newController.window
+ modalForWindow:window
+ modalDelegate:newController
+ didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
+ contextInfo:nil];
+
+ return newController;
+}
+
+- (id)initWithWindowNibName:(NSString *)nibName
+ delegate:(id)inDelegate
+ chat:(AIChat *)inChat
+{
+ if ((self = [super initWithWindowNibName:nibName])) {
+ chat = inChat;
+ delegate = inDelegate;
+ }
+
+ return self;
+}
+
+- (void)windowDidLoad
+{
+ [super windowDidLoad];
+
+ [label_uploadingImage setLocalizedString:[AILocalizedString(@"Uploading image to server", nil) stringByAppendingEllipsis]];
+ [button_cancel setLocalizedString:AILocalizedStringFromTable(@"Cancel", @"Buttons", nil)];
+}
+
+- (void)windowDidLoad
+{
+ [super windowDidLoad];
+
+ [label_uploadingImage setLocalizedString:[AILocalizedString(@"Uploading image to server", nil) stringByAppendingEllipsis]];
+ [button_cancel setLocalizedString:AILocalizedStringFromTable(@"Cancel", @"Buttons", nil)];
+}
+
+- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
+{
+ [sheet orderOut:nil];
+}
+
+- (IBAction)cancel:(id)sender
+{
+ [delegate cancelForChat:chat];
+ [self closeWindow:nil];
+}
+
+- (BOOL)indeterminate
+{
+ return progressIndicator.isIndeterminate;
+}
+
+- (void)setIndeterminate:(BOOL)indeterminate
+{
+ [progressIndicator setIndeterminate:indeterminate];
+}
+
+- (CGFloat)progress
+{
+ return progressIndicator.doubleValue;
+}
+
+- (void)setProgress:(CGFloat)percent
+{
+ [progressIndicator setDoubleValue:percent];
+}
+
+ at end
diff -r e20f4841e08d -r b5f68ee73ac3 Plugins/Image Uploading Plugin/AIPicImImageUploader.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Image Uploading Plugin/AIPicImImageUploader.h Tue May 26 21:49:21 2009 -0400
@@ -0,0 +1,27 @@
+//
+// AIPicImImageUploader.h
+// Adium
+//
+// Created by Zachary West on 2009-05-26.
+// Copyright 2009 Adium. All rights reserved.
+//
+
+#import "AIImageUploaderPlugin.h"
+
+ at interface AIPicImImageUploader : NSObject <AIImageUploader> {
+ AIChat *chat;
+ NSImage *image;
+ AIImageUploaderPlugin *uploader;
+
+ NSMutableData *receivedData;
+ NSURLConnection *connection;
+ NSXMLParser *responseParser;
+
+ // Parsing
+ NSMutableDictionary *lastElement;
+ NSString *currentElementName;
+ NSMutableDictionary *currentElement;
+ NSMutableDictionary *response;
+}
+
+ at end
diff -r e20f4841e08d -r b5f68ee73ac3 Plugins/Image Uploading Plugin/AIPicImImageUploader.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Image Uploading Plugin/AIPicImImageUploader.m Tue May 26 21:49:21 2009 -0400
@@ -0,0 +1,285 @@
+//
+// AIPicImImageUploader.m
+// Adium
+//
+// Created by Zachary West on 2009-05-26.
+// Copyright 2009 Adium. All rights reserved.
+//
+
+#import "AIPicImImageUploader.h"
+
+#import <Adium/AIInterfaceControllerProtocol.h>
+#import <AIUtilities/AIStringAdditions.h>
+
+#define MULTIPART_FORM_BOUNDARY @"bf5faadd239c17e35f91e6dafe1d2f96"
+#define PIC_IM_URL @"http://api.tr.im/api/picim_url.xml"
+
+ at interface AIPicImImageUploader()
+- (id)initWithImage:(NSImage *)inImage
+ uploader:(AIImageUploaderPlugin *)inUploader
+ chat:(AIChat *)inChat;
+- (void)uploadImage;
+- (void)finishResponse;
+ at end
+
+ at implementation AIPicImImageUploader
++ (NSString *)serviceName
+{
+ return @"pic.im";
+}
+
++ (id)uploadImage:(NSImage *)image forUploader:(AIImageUploaderPlugin *)uploader inChat:(AIChat *)chat;
+{
+ return [[[self alloc] initWithImage:image uploader:uploader chat:chat] autorelease];
+}
+
+- (id)initWithImage:(NSImage *)inImage
+ uploader:(AIImageUploaderPlugin *)inUploader
+ chat:(AIChat *)inChat
+{
+ if ((self = [super init])) {
+ image = [inImage retain];
+ uploader = inUploader;
+ chat = inChat;
+
+ [self uploadImage];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [response release];
+ [responseParser release];
+ [image release];
+ [connection release];
+ [receivedData release];
+
+ [super dealloc];
+}
+
+#pragma mark Image upload
+
+- (void)uploadImage
+{
+ NSBitmapImageRep *bitmapImageRep = nil;
+
+ for (NSImageRep *rep in image.representations) {
+ if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
+ bitmapImageRep = (NSBitmapImageRep *)rep;
+ break;
+ }
+ }
+
+ // This probably won't happen.
+ if (!bitmapImageRep) {
+ [uploader errorWithMessage:AILocalizedString(@"Unknown image type", nil) forChat:chat];
+ return;
+ }
+
+ NSMutableData *body = [NSMutableData data];
+
+ [body appendData:[[NSString stringWithFormat:@"--%@\r\n", MULTIPART_FORM_BOUNDARY] dataUsingEncoding:NSUTF8StringEncoding]];
+ [body appendData:[@"Content-Disposition: form-data; name=\"media\"; filename=\"image.png\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
+ [body appendData:[@"Content-Type: image/png\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
+ [body appendData:[bitmapImageRep representationUsingType:NSPNGFileType properties:nil]];
+ [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", MULTIPART_FORM_BOUNDARY] dataUsingEncoding:NSUTF8StringEncoding]];
+
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:PIC_IM_URL]];
+
+ [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", MULTIPART_FORM_BOUNDARY] forHTTPHeaderField:@"Content-type"];
+ [request setHTTPMethod:@"POST"];
+ [request setHTTPBody:body];
+
+ connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
+
+ if (connection) {
+ receivedData = [[NSMutableData data] retain];
+ } else {
+ [uploader errorWithMessage:AILocalizedString(@"Unable to connect", nil) forChat:chat];
+ }
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
+{
+ [receivedData setLength:0];
+}
+
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
+{
+ [receivedData appendData:data];
+}
+
+- (void)connection:(NSURLConnection *)inConnection
+ didFailWithError:(NSError *)error
+{
+ [connection release]; connection = nil;
+ [receivedData release]; receivedData = nil;
+
+ [uploader errorWithMessage:error.localizedDescription forChat:chat];
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection *)inConnection
+{
+ AILogWithSignature(@"%@", [NSString stringWithData:receivedData encoding:NSUTF8StringEncoding]);
+
+ response = [[NSMutableDictionary alloc] init];
+
+ responseParser = [[NSXMLParser alloc] initWithData:receivedData];
+ [responseParser setDelegate:self];
+ [responseParser parse];
+
+ [connection release]; connection = nil;
+ [receivedData release]; receivedData = nil;
+}
+
+- (void)cancel
+{
+ [connection cancel];
+
+ [connection release]; connection = nil;
+ [receivedData release]; receivedData = nil;
+}
+
+#pragma mark Response parsing
+
+- (void)parserDidStartDocument:(NSXMLParser *)parser
+{
+ currentElement = response;
+}
+
+- (void)parser:(NSXMLParser *)parser
+parseErrorOccurred:(NSError *)error
+{
+ AILogWithSignature(@"%@", [error localizedDescription]);
+
+ [responseParser release]; responseParser = nil;
+
+ [uploader errorWithMessage:[error localizedDescription] forChat:chat];
+}
+
+- (void)parser:(NSXMLParser *)parser
+didStartElement:(NSString *)elementName
+ namespaceURI:(NSString *)namespaceURI
+ qualifiedName:(NSString *)qualifiedName
+ attributes:(NSDictionary *)attributes
+{
+ if (elementName) {
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+
+ [currentElement setValue:dict forKey:elementName];
+
+ lastElement = currentElement;
+ currentElement = dict;
+ }
+
+ [currentElement addEntriesFromDictionary:attributes];
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
+{
+ // We don't see anything more than 2-deep. We don't need to check more.
+ currentElement = lastElement;
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
+{
+ if (![currentElement objectForKey:@"value"]) {
+ [currentElement setValue:[NSMutableString string] forKey:@"value"];
+ }
+
+ [[currentElement objectForKey:@"value"] appendString:string];
+}
+
+- (void)parserDidEndDocument:(NSXMLParser *)parser
+{
+ [responseParser release]; responseParser = nil;
+
+ [self finishResponse];
+}
+
+- (void)finishResponse
+{
+ NSDictionary *status = [[response objectForKey:@"trim"] objectForKey:@"status"];
+
+ if ([[status objectForKey:@"result"] isCaseInsensitivelyEqualToString:@"error"]) {
+ [uploader errorWithMessage:[status objectForKey:@"message"] forChat:chat];
+ } else {
+ // TODO when api key :(
+ }
+
+ NSLog(@"%@", response);
+}
+
+#pragma mark Response parsing
+
+- (void)parserDidStartDocument:(NSXMLParser *)parser
+{
+ currentElement = response;
+}
+
+- (void)parser:(NSXMLParser *)parser
+parseErrorOccurred:(NSError *)error
+{
+ AILogWithSignature(@"%@", [error localizedDescription]);
+
+ [responseParser release]; responseParser = nil;
+
+ [uploader errorWithMessage:[error localizedDescription] forChat:chat];
+}
+
+- (void)parser:(NSXMLParser *)parser
+didStartElement:(NSString *)elementName
+ namespaceURI:(NSString *)namespaceURI
+ qualifiedName:(NSString *)qualifiedName
+ attributes:(NSDictionary *)attributes
+{
+ if (elementName) {
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+
+ [currentElement setValue:dict forKey:elementName];
+
+ lastElement = currentElement;
+ currentElement = dict;
+ }
+
+ [currentElement addEntriesFromDictionary:attributes];
+}
+
+- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
+{
+ // We don't see anything more than 2-deep. We don't need to check more.
+ currentElement = lastElement;
+}
+
+- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
+{
+ if (![currentElement objectForKey:@"value"]) {
+ [currentElement setValue:[NSMutableString string] forKey:@"value"];
+ }
+
+ [[currentElement objectForKey:@"value"] appendString:string];
+}
+
+- (void)parserDidEndDocument:(NSXMLParser *)parser
+{
+ [responseParser release]; responseParser = nil;
+
+ [self finishResponse];
+}
+
+- (void)finishResponse
+{
+ NSDictionary *status = [[response objectForKey:@"trim"] objectForKey:@"status"];
+
+ if ([[status objectForKey:@"result"] isCaseInsensitivelyEqualToString:@"error"]) {
+ [uploader errorWithMessage:[status objectForKey:@"message"] forChat:chat];
+ } else {
+ // TODO when api key :(
+ }
+
+ NSLog(@"%@", response);
+}
+
+ at end
diff -r e20f4841e08d -r b5f68ee73ac3 Plugins/Image Uploading Plugin/ImageUploaderProgress.nib/classes.nib
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Image Uploading Plugin/ImageUploaderProgress.nib/classes.nib Tue May 26 21:49:21 2009 -0400
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBClasses</key>
+ <array>
+ <dict>
+ <key>CLASS</key>
+ <string>NSApplication</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>RBSplitView</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>OUTLETS</key>
+ <dict>
+ <key>delegate</key>
+ <string>id</string>
+ </dict>
+ <key>SUPERCLASS</key>
+ <string>RBSplitSubview</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSTextField</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSControl</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>RBSplitSubview</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSView</string>
+ </dict>
+ <dict>
+ <key>ACTIONS</key>
+ <dict>
+ <key>cancel</key>
+ <string>id</string>
+ </dict>
+ <key>CLASS</key>
+ <string>AIImageUploaderWindowController</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>OUTLETS</key>
+ <dict>
+ <key>button_cancel</key>
+ <string>NSButton</string>
+ <key>delegate</key>
+ <string>id</string>
+ <key>label_uploadingImage</key>
+ <string>NSTextField</string>
+ <key>progressIndicator</key>
+ <string>NSProgressIndicator</string>
+ </dict>
+ <key>SUPERCLASS</key>
+ <string>AIWindowController</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSMenu</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSObject</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSWindowController</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
+ <dict>
+ <key>ACTIONS</key>
+ <dict>
+ <key>adiumPrint</key>
+ <string>id</string>
+ <key>didAdjustSubviews</key>
+ <string>RBSplitView</string>
+ <key>prefsWindowWillClose</key>
+ <string>SS_PrefsController</string>
+ <key>toggleFindPanel</key>
+ <string>id</string>
+ <key>willAdjustSubviews</key>
+ <string>RBSplitView</string>
+ </dict>
+ <key>CLASS</key>
+ <string>NSObject</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>AILocalizationButton</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>OUTLETS</key>
+ <dict>
+ <key>view_anchorToLeftSide</key>
+ <string>NSView</string>
+ <key>view_anchorToRightSide</key>
+ <string>NSView</string>
+ <key>window_anchorOnLeftSide</key>
+ <string>NSWindow</string>
+ <key>window_anchorOnRightSide</key>
+ <string>NSWindow</string>
+ </dict>
+ <key>SUPERCLASS</key>
+ <string>NSButton</string>
+ </dict>
+ <dict>
+ <key>CLASS</key>
+ <string>NSWindow</string>
+ <key>LANGUAGE</key>
+ <string>ObjC</string>
+ <key>SUPERCLASS</key>
+ <string>NSResponder</string>
+ </dict>
More information about the commits
mailing list