/lilypad/Sources/LPChatController.m
Objective C | 2505 lines | 1802 code | 555 blank | 148 comment | 324 complexity | f3da6a704229ecf524559baf8c9aa706 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.1, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- //
- // LPChatController.m
- // Lilypad
- //
- // Copyright (C) 2006-2008 PT.COM, All rights reserved.
- // Authors: Joao Pavao <jpavao@co.sapo.pt>
- // Jason Kim <jason@512k.org>
- //
- // For more information on licensing, read the README file.
- // Para mais informaes sobre o licenciamento, leia o ficheiro README.
- //
- #import "LPChatController.h"
- #import "LPCommon.h"
- #import "LPAccount.h"
- #import "LPRoster.h"
- #import "LPContact.h"
- #import "LPContactEntry.h"
- #import "LPChat.h"
- #import "LPChatsManager.h"
- #import "LPFileTransfersManager.h"
- #import "NSString+HTMLAdditions.h"
- #import "LPChatWebView.h"
- #import "LPChatTextField.h"
- #import "LPChatViewsController.h"
- #import "LPColorBackgroundView.h"
- #import "NSxString+EmoticonAdditions.h"
- #import "LPAccountsController.h"
- #import "LPAudiblesDrawerController.h"
- #import "LPAudibleSet.h"
- #import "LPChatJavaScriptInterface.h"
- #import "LPPubManager.h"
- #import "LPEventNotificationsHandler.h"
- #import "CTBadge.h"
- #import "LPRecentMessagesStore.h"
- #import "LPFileTransfer.h"
- #import "LPJIDEntryView.h"
- #import "LPSapoAgents+MenuAdditions.h"
- #import "IconFamily.h"
- #import <AddressBook/AddressBook.h>
- #define INPUT_LINE_HISTORY_ITEMS_MAX 10
- // Toolbar item identifiers
- static NSString *ToolbarInfoIdentifier = @"ToolbarInfoIdentifier";
- static NSString *ToolbarFileSendIdentifier = @"ToolbarFileSendIdentifier";
- static NSString *ToolbarSendSMSIdentifier = @"ToolbarSendSMSIdentifier";
- static NSString *ToolbarHistoryIdentifier = @"ToolbarHistoryIdentifier";
- @interface LPChatController () // Private Methods
- - (void)p_syncChatOwnerName;
- - (void)p_syncViewsWithContact;
- - (void)p_syncStatusMessageTextFieldWithContact;
- - (void)p_setChat:(LPChat *)chat;
- - (NSAttributedString *)p_attributedTitleOfJIDMenuItemForContactEntry:(LPContactEntry *)entry withFont:(NSFont *)font;
- - (NSMenuItem *)p_popupMenuHeaderItemForAccount:(LPAccount *)account;
- - (NSMenuItem *)p_popupMenuItemForEntry:(LPContactEntry *)entry;
- - (void)p_moveJIDMenuItem:(NSMenuItem *)menuItem toIndex:(int)targetIndex inMenu:(NSMenu *)menu;
- - (void)p_syncJIDsPopupMenu;
- - (void)p_setSendFieldHidden:(BOOL)hiddenFlag animate:(BOOL)animateFlag;
- - (void)p_fixResizeIndicator;
- - (NSMutableSet *)p_pendingAudiblesSet;
- - (void)p_appendStandardMessageBlockWithInnerHTML:(NSString *)innerHTML timestamp:(NSDate *)timestamp inbound:(BOOL)isInbound saveInHistory:(BOOL)shouldSave scrollMode:(LPScrollToVisibleMode)scrollMode;
- - (void)p_appendMessageToWebView:(NSString *)message subject:(NSString *)subject timestamp:(NSDate *)timestamp inbound:(BOOL)isInbound;
- - (void)p_appendAudibleWithResourceName:(NSString *)resourceName inbound:(BOOL)inbound;
- - (void)p_appendStoredRecentMessagesToWebView;
- - (void)p_resizeInputFieldToContentsSize:(NSSize)newSize;
- - (void)p_updateChatBackgroundColorFromDefaults;
- - (void)p_setupToolbar;
- - (void)p_setupChatDocumentTitle;
- - (void)p_setSaveChatTranscriptEnabled:(BOOL)flag;
- - (void)p_displayAndReloadPubBannerIfNeeded;
- - (void)p_incrementUnreadMessagesCount;
- - (void)p_resetUnreadMessagesCount;
- - (void)p_updateMiniwindowImage;
- - (void)p_notifyUserAboutReceivedMessage:(NSString *)msgText notificationsHandlerSelector:(SEL)selector;
- - (void)p_reevaluateJIDPanelOKButtonEnabled;
- @end
- #pragma mark -
- @implementation LPChatController
- + (void)initialize
- {
- if (self == [LPChatController class]) {
- [self setKeys:[NSArray arrayWithObject:@"numberOfUnreadMessages"]
- triggerChangeNotificationsForDependentKey:@"windowTitleSuffix"];
- }
- }
- - initWithDelegate:(id)delegate
- {
- return [self initWithChat:nil delegate:delegate isIncoming:NO];
- }
- // Designated Initializer
- - initWithChat:(LPChat *)chat delegate:(id)delegate isIncoming:(BOOL)incomingFlag
- {
- if (self = [self initWithWindowNibName:@"Chat"]) {
- [self p_setChat:chat];
- [self setContact:[chat contact]];
-
- m_dateStarted = [[NSDate alloc] init];
-
- [self setDelegate:delegate];
-
- m_collapsedHeightWhenLastWentOffline = 0.0;
-
- // Setup KVO
- NSUserDefaultsController *prefsCtrl = [NSUserDefaultsController sharedUserDefaultsController];
-
- [prefsCtrl addObserver:self forKeyPath:@"values.ChatBackgroundColor" options:0 context:NULL];
- [prefsCtrl addObserver:self forKeyPath:@"values.DisplayEmoticonImages" options:0 context:NULL];
-
-
- NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
- LPAudibleSet *as = [LPAudibleSet defaultAudibleSet];
- [nc addObserver:self
- selector:@selector(audibleSetDidFinishLoadingAudible:)
- name:LPAudibleSetAudibleDidFinishLoadingNotification
- object:as];
- [nc addObserver:self
- selector:@selector(audibleSetDidFinishLoadingAudible:)
- name:LPAudibleSetAudibleDidFailLoadingNotification
- object:as];
-
- // File Transfers status messages
- [nc addObserver:self
- selector:@selector(fileTransferStateDidChange:)
- name:LPFileTransferDidChangeStateNotification
- object:nil];
-
- // Chat History
- [prefsCtrl addObserver:self forKeyPath:@"values.SaveChatTranscripts" options:0 context:NULL];
- [self p_setSaveChatTranscriptEnabled:[[prefsCtrl valueForKeyPath:@"values.SaveChatTranscripts"] boolValue]];
-
- // Input line history
- m_inputLineHistory = [[NSMutableArray alloc] init];
-
- LPAccountsController *accountsController = [LPAccountsController sharedAccountsController];
- [accountsController addObserver:self forKeyPath:@"name" options:0 context:NULL];
- [accountsController addObserver:self forKeyPath:@"online" options:0 context:NULL];
-
- m_dontMakeKeyOnFirstShowWindow = incomingFlag;
- }
-
- return self;
- }
- - initWithIncomingChat:(LPChat *)newChat delegate:(id)delegate
- {
- return [self initWithChat:newChat delegate:delegate isIncoming:YES];
- }
- - initOutgoingWithContact:(LPContact *)contact delegate:(id)delegate
- {
- LPChat *newChat = [[LPChatsManager chatsManager] startChatWithContact:contact];
-
- if (newChat) {
- self = [self initWithChat:newChat delegate:delegate isIncoming:NO];
- }
- else {
- [self release];
- self = nil;
- }
- return self;
- }
- - initOutgoingWithContactEntry:(LPContactEntry *)contactEntry delegate:(id)delegate
- {
- LPChat *newChat = [[LPChatsManager chatsManager] startChatWithContactEntry:contactEntry];
-
- if (newChat) {
- self = [self initWithChat:newChat delegate:delegate isIncoming:NO];
- }
- else {
- [self release];
- self = nil;
- }
- return self;
- }
- - (void)dealloc
- {
- NSUserDefaultsController *prefsCtrl = [NSUserDefaultsController sharedUserDefaultsController];
- [prefsCtrl removeObserver:self forKeyPath:@"values.ChatBackgroundColor"];
- [prefsCtrl removeObserver:self forKeyPath:@"values.DisplayEmoticonImages"];
- [prefsCtrl removeObserver:self forKeyPath:@"values.SaveChatTranscripts"];
-
- [[NSNotificationCenter defaultCenter] removeObserver:self];
-
- LPAccountsController *accountsController = [LPAccountsController sharedAccountsController];
- [accountsController removeObserver:self forKeyPath:@"name"];
- [accountsController removeObserver:self forKeyPath:@"online"];
-
- [self p_setChat:nil];
- [self setContact:nil];
- [self setDelegate:nil];
-
- [m_dateStarted release];
-
- [m_inputLineHistory release];
-
- [m_autoSaveChatTranscriptTimer invalidate];
- [m_autoSaveChatTranscriptTimer release];
-
- [m_unreadMessagesBadge release];
-
- [m_audibleResourceNamesWaitingForLoadCompletion release];
- [m_chatJSInterface release];
- [super dealloc];
- }
- - (void)p_syncChatOwnerName
- {
-
- NSString *currentOwnerName = [m_chatViewsController ownerName];
- NSString *globalName = [[LPAccountsController sharedAccountsController] name];
- NSString *newOwnerName = ( [globalName length] > 0 ?
- globalName :
- [[[m_chat activeContactEntry] account] JID] );
-
- if (![currentOwnerName isEqualToString:newOwnerName])
- [m_chatViewsController setOwnerName:newOwnerName];
- }
- - (void)p_syncViewsWithContact
- {
- if ([self isWindowLoaded]) {
- [m_chatController setContent:[self chat]];
- [m_contactController setContent:[self contact]];
-
- [m_chatWebView setChat:m_chat];
- [self p_syncChatOwnerName];
-
- [m_topControlsBar setBackgroundColor:
- [NSColor colorWithPatternImage:( [[m_chat activeContactEntry] isOnline] ?
- [NSImage imageNamed:@"chatIDBackground"] :
- [NSImage imageNamed:@"chatIDBackground_Offline"] )]];
-
- [self p_syncStatusMessageTextFieldWithContact];
-
- // Update the addresses popup
- [self p_syncJIDsPopupMenu];
- [m_addressesPopUp setEnabled:([[m_contact chatContactEntries] count] > 0)];
-
- [self p_setSendFieldHidden:(![[[[self chat] activeContactEntry] account] isOnline] || [[self chat] activeContactEntry] == nil) animate:YES];
- [self p_updateMiniwindowImage];
-
- // Make sure the toolbar items are correctly enabled/disabled
- [[self window] update];
- }
- }
- - (void)p_syncStatusMessageTextFieldWithContact
- {
- NSAttributedString *attributedStatusMessage = [m_contact attributedStatusMessage];
-
- if ([attributedStatusMessage length] == 0) {
- [m_statusMessageTextField setStringValue:@""];
- }
- else {
- NSMutableAttributedString *newStatusMessage = [attributedStatusMessage mutableCopy];
-
- unsigned int location = 0;
- NSRange effectiveRange = { 0, 0 };
- NSRange wholeStrRange = NSMakeRange(0, [newStatusMessage length]);
-
- while (NSLocationInRange(location, wholeStrRange)) {
- NSColor *existingFGColor = [newStatusMessage attribute:NSForegroundColorAttributeName
- atIndex:location
- effectiveRange:&effectiveRange];
-
- // If there isn't a foreground color already defined by the string at this range, then add our own.
- if (existingFGColor == nil) {
- [newStatusMessage addAttribute:NSForegroundColorAttributeName
- value:[m_statusMessageTextField textColor]
- range:effectiveRange];
- }
-
- location = NSMaxRange(effectiveRange);
- }
-
- [newStatusMessage addAttribute:NSFontAttributeName
- value:[m_statusMessageTextField font]
- range:wholeStrRange];
-
- [m_statusMessageTextField setAttributedStringValue:newStatusMessage];
- [newStatusMessage release];
- }
- }
- - (void)windowDidLoad
- {
- [self p_setupToolbar];
-
- [m_audiblesController setChatController:self];
-
- // Workaround for centering the icons.
- [m_segmentedButton setLabel:nil forSegment:0];
- [m_segmentedButton setLabel:nil forSegment:1];
- [[m_segmentedButton cell] setToolTip:NSLocalizedString(@"Choose Emoticon", @"") forSegment:0];
- [[m_segmentedButton cell] setToolTip:NSLocalizedString(@"Toggle Audibles Drawer", @"") forSegment:1];
- // IB displays a round segmented button that apparently needs less space than the on that ends up
- // showing in the app (the flat segmented button used in metal windows).
- [m_segmentedButton sizeToFit];
-
- [m_topControlsBar setBorderColor:[NSColor colorWithCalibratedWhite:0.60 alpha:1.0]];
-
-
- [m_pubElementsView setShadedBackgroundWithOrientation:LPVerticalBackgroundShading
- minEdgeColor:[NSColor colorWithCalibratedWhite:0.79 alpha:1.0]
- maxEdgeColor:[NSColor colorWithCalibratedWhite:0.49 alpha:1.0]];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(p_JIDsMenuWillPop:)
- name:NSPopUpButtonWillPopUpNotification
- object:m_addressesPopUp];
-
- [m_addressesPopUp setAutoenablesItems:NO];
- [self p_syncViewsWithContact];
-
- // Post the saved recent messages
- [self p_appendStoredRecentMessagesToWebView];
-
- if ([m_chat activeContactEntry]) {
- // Post a "system message" to start
- NSString *initialSystemMessage = nil;
-
- if ([[[LPAccountsController sharedAccountsController] accounts] count] > 1) {
- initialSystemMessage = [NSString stringWithFormat:NSLocalizedString(@"Chat started with contact \"%@\" thru account \"%@\"",
- @"status message written to the text transcript of a chat window"),
- [[m_chat activeContactEntry] humanReadableAddress],
- [[[m_chat activeContactEntry] account] description]];
- }
- else {
- initialSystemMessage = [NSString stringWithFormat:NSLocalizedString(@"Chat started with contact \"%@\"",
- @"status message written to the text transcript of a chat window"),
- [[m_chat activeContactEntry] humanReadableAddress]];
- }
-
- [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:[initialSystemMessage stringByEscapingHTMLEntities]
- divClass:@"systemMessage"
- scrollToVisibleMode:LPScrollWithJump];
- }
- }
- - (void)showWindow:(id)sender
- {
- if (m_contact == nil) {
- NSWindow *win = [self window];
- BOOL wasVisible = [win isVisible];
-
- [super showWindow:sender];
-
- if (!wasVisible) {
- [self p_reevaluateJIDPanelOKButtonEnabled];
- [m_chooseJIDPanelJIDEntryView addObserver:self forKeyPath:@"account.online" options:0 context:NULL];
-
- [NSApp beginSheet:m_chooseJIDPanel modalForWindow:win modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
- }
- }
- else {
- BOOL windowWasNotLoadedYet = (![self isWindowLoaded]);
- NSWindow *win = [self window];
-
- NSRect savedWindowFrame = [[self class] savedWindowFrameForChatWithContactNamed:[[self contact] name]];
- if (!NSIsEmptyRect(savedWindowFrame)) {
- [win setFrame:savedWindowFrame display:YES];
- }
-
- if (windowWasNotLoadedYet && m_dontMakeKeyOnFirstShowWindow && [NSApp mainWindow] != nil) {
- [win orderWindow:NSWindowBelow relativeTo:[[NSApp mainWindow] windowNumber]];
- }
- else {
- [super showWindow:sender];
- }
- }
- }
- - (id)delegate
- {
- return m_delegate;
- }
- - (void)setDelegate:(id)delegate
- {
- m_delegate = delegate;
- }
- - (LPChat *)chat
- {
- return [[m_chat retain] autorelease];
- }
- - (void)p_setChat:(LPChat *)chat
- {
- if (m_chat != chat) {
- [m_chat endChat];
-
- [self willChangeValueForKey:@"chat"];
-
- [m_chat removeObserver:self forKeyPath:@"activeContactEntry.account.pubManager.chatBotAdsBaseURL"];
- [m_chat removeObserver:self forKeyPath:@"activeContactEntry.account.online"];
- [m_chat removeObserver:self forKeyPath:@"activeContactEntry.online"];
- [m_chat removeObserver:self forKeyPath:@"activeContactEntry"];
-
- [m_chat release];
- m_chat = [chat retain];
- [chat setDelegate:self];
-
- [m_chat addObserver:self forKeyPath:@"activeContactEntry" options:0 context:NULL];
- [m_chat addObserver:self forKeyPath:@"activeContactEntry.online" options:0 context:NULL];
- [m_chat addObserver:self forKeyPath:@"activeContactEntry.account.online" options:0 context:NULL];
- [m_chat addObserver:self forKeyPath:@"activeContactEntry.account.pubManager.chatBotAdsBaseURL" options:0 context:NULL];
-
- // Post a "system message" to start
- NSString *systemMessage;
- if ([m_chat activeContactEntry]) {
- if ([[[LPAccountsController sharedAccountsController] accounts] count] > 1) {
- systemMessage = [NSString stringWithFormat:NSLocalizedString(@"Chat changed to contact \"%@\" thru account \"%@\"",
- @"status message written to the text transcript of a chat window"),
- [[m_chat activeContactEntry] humanReadableAddress],
- [[[m_chat activeContactEntry] account] description]];
- }
- else {
- systemMessage = [NSString stringWithFormat:NSLocalizedString(@"Chat changed to contact \"%@\"",
- @"status message written to the text transcript of a chat window"),
- [[m_chat activeContactEntry] humanReadableAddress]];
- }
- }
- else {
- systemMessage = [NSString stringWithFormat:NSLocalizedString(@"Chat ended.", @"status message written to the text transcript of a chat window")];
- }
- [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:[systemMessage stringByEscapingHTMLEntities]
- divClass:@"systemMessage"
- scrollToVisibleMode:LPScrollWithAnimationIfAtBottom];
-
- [m_chatJSInterface setAccount:[[m_chat activeContactEntry] account]];
-
- [self didChangeValueForKey:@"chat"];
- }
- }
- - (LPContact *)contact
- {
- return [[m_contact retain] autorelease];
- }
- - (void)setContact:(LPContact *)contact
- {
- if (m_contact != contact) {
- BOOL hadContact = (m_contact != nil);
-
- [m_contact removeObserver:self forKeyPath:@"avatar"];
- [m_contact removeObserver:self forKeyPath:@"chatContactEntries"];
- [m_contact removeObserver:self forKeyPath:@"contactEntries"];
- [m_contact removeObserver:self forKeyPath:@"attributedStatusMessage"];
-
- [m_contact release];
- m_contact = [contact retain];
-
- [m_contact addObserver:self forKeyPath:@"attributedStatusMessage" options:0 context:NULL];
- [m_contact addObserver:self forKeyPath:@"contactEntries" options:0 context:NULL];
- [m_contact addObserver:self forKeyPath:@"chatContactEntries" options:0 context:NULL];
- [m_contact addObserver:self forKeyPath:@"avatar" options:0 context:NULL];
-
- [self p_syncViewsWithContact];
- if (!hadContact)
- [self p_setupChatDocumentTitle];
-
- if (contact != nil) {
- // Show the PUB banner only for contacts with the corresponding capability.
- // Check only some seconds from now so that the core has time to fetch the capabilities of the contact.
- [self performSelector:@selector(p_displayAndReloadPubBannerIfNeeded) withObject:nil afterDelay:3.0];
- }
- else {
- // Make sure that the delayed perform of p_displayAndReloadPubBannerIfNeeded doesn't fire
- [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(p_displayAndReloadPubBannerIfNeeded) object:nil];
- }
- }
- }
- - (NSDate *)dateStarted
- {
- return [[m_dateStarted retain] autorelease];
- }
- - (unsigned int)numberOfUnreadMessages
- {
- return m_nrUnreadMessages;
- }
- - (NSString *)windowTitleSuffix
- {
- return (m_nrUnreadMessages > 0 ?
- [NSString stringWithFormat:NSLocalizedString(@" (%d unread)", @"chat window title suffix"), m_nrUnreadMessages] :
- @"");
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
- {
- if ([keyPath isEqualToString:@"values.ChatBackgroundColor"]) {
- [self p_updateChatBackgroundColorFromDefaults];
- }
- else if ([keyPath isEqualToString:@"values.DisplayEmoticonImages"]) {
- BOOL displayImages = [[object valueForKeyPath:keyPath] boolValue];
- [m_chatViewsController showEmoticonsAsImages:displayImages];
- }
- else if ([keyPath isEqualToString:@"values.SaveChatTranscripts"]) {
- NSUserDefaultsController *prefsCtrl = [NSUserDefaultsController sharedUserDefaultsController];
- [self p_setSaveChatTranscriptEnabled:[[prefsCtrl valueForKeyPath:@"values.SaveChatTranscripts"] boolValue]];
- }
- else if ([keyPath isEqualToString:@"online"]) {
- // [LPAccountsController sharedAccountsController] online status
- [[self window] update];
- }
- else if ([keyPath isEqualToString:@"name"]) {
- // [LPAccountsController sharedAccountsController] name
- [self p_syncChatOwnerName];
- }
- else if ([keyPath isEqualToString:@"attributedStatusMessage"]) {
- [self p_syncStatusMessageTextFieldWithContact];
- }
- else if ([keyPath isEqualToString:@"contactEntries"]) {
- // Check whether all JIDs have been removed.
- if ([[m_contact contactEntries] count] == 0) {
- [self performSelector:@selector(close) withObject:nil afterDelay:0.0];
- }
- }
- else if ([keyPath isEqualToString:@"chatContactEntries"]) {
- [self p_syncJIDsPopupMenu];
- [m_addressesPopUp setEnabled:([[m_contact chatContactEntries] count] > 0)];
- }
- else if ([keyPath isEqualToString:@"avatar"]) {
- [self p_updateMiniwindowImage];
- }
- else if ([keyPath isEqualToString:@"activeContactEntry.online"]) {
- // Changes to the activeContactEntry will also trigger a change notification for the activeContactEntry.online
- // keypath. So, everything that must be done when any of these two keypaths change is being taken care of in here.
- [self p_syncViewsWithContact];
- }
- else if ([keyPath isEqualToString:@"activeContactEntry"]) {
- LPContactEntry *entry = [m_chat activeContactEntry];
-
- int idx = [m_addressesPopUp indexOfItemWithRepresentedObject:entry];
- if (idx >= 0)
- [m_addressesPopUp selectItemAtIndex:[m_addressesPopUp indexOfItemWithRepresentedObject:entry]];
-
- // Post a "system message" to signal the change
- NSString *systemMessage;
- if (entry) {
- if ([[[LPAccountsController sharedAccountsController] accounts] count] > 1) {
- systemMessage = [NSString stringWithFormat:NSLocalizedString(@"Chat changed to contact \"%@\" thru account \"%@\"",
- @"status message written to the text transcript of a chat window"),
- [[m_chat activeContactEntry] humanReadableAddress],
- [[[m_chat activeContactEntry] account] description]];
- }
- else {
- systemMessage = [NSString stringWithFormat:NSLocalizedString(@"Chat changed to contact \"%@\"",
- @"status message written to the text transcript of a chat window"),
- [[m_chat activeContactEntry] humanReadableAddress]];
- }
- }
- else {
- systemMessage = [NSString stringWithFormat:NSLocalizedString(@"Chat ended.", @"status message written to the text transcript of a chat window")];
- }
-
- [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:[systemMessage stringByEscapingHTMLEntities]
- divClass:@"systemMessage"
- scrollToVisibleMode:LPScrollWithAnimationIfAtBottom];
-
- [m_chatJSInterface setAccount:[entry account]];
- }
- else if ([keyPath isEqualToString:@"activeContactEntry.account.online"]) {
- // Account online status (Chat window)
- [self p_setSendFieldHidden:(![[object valueForKeyPath:keyPath] boolValue] || [m_chat activeContactEntry] == nil)
- animate:YES];
- }
- else if ([keyPath isEqualToString:@"account.online"]) {
- // Account online status (JID Entry Panel)
- [self p_reevaluateJIDPanelOKButtonEnabled];
- }
- else if ([keyPath isEqualToString:@"activeContactEntry.account.pubManager.chatBotAdsBaseURL"]) {
- [self p_displayAndReloadPubBannerIfNeeded];
- }
- else {
- [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
- }
- }
- - (void)setMessageTextEntryString:(NSString *)messageText
- {
- [m_inputTextField setStringValue:messageText];
- [m_inputTextField performSelector:@selector(calcContentSize) withObject:nil afterDelay:0.0];
-
- m_lastInputTextFieldStringLength = [messageText length];
- }
- - (void)sendAudibleWithResourceName:(NSString *)audibleName
- {
- [self p_appendAudibleWithResourceName:audibleName inbound:NO];
- [m_chat sendAudibleWithResourceName:audibleName];
- }
- - (NSString *)p_fileNameHTMLForFileTransfer:(LPFileTransfer *)ft
- {
- int transferID = [ft ID];
-
- NSString *fileNameWithLink = [NSString stringWithFormat:
- @"<a href=\"javascript:window.chatJSInterface.openFileOfTransfer(%d);\" title=\"%@\">%@</a>",
- transferID,
- NSLocalizedString(@"Open file", @""),
- [[ft filename] stringByEscapingHTMLEntities]];
- NSString *revealLink = [NSString stringWithFormat:
- @"<a href=\"javascript:window.chatJSInterface.revealFileOfTransfer(%d);\" title=\"%@\"><img src=\"file://%@\"/></a>",
- transferID,
- NSLocalizedString(@"Reveal in Finder", @""),
- [[NSBundle mainBundle] pathForImageResource:@"TransferReveal"]];
-
- return [NSString stringWithFormat:@"\"%@\" %@", fileNameWithLink, revealLink];
- }
- - (void)updateInfoForFileTransfer:(LPFileTransfer *)ft
- {
- LPContactEntry *peerContactEntry = [ft peerContactEntry];
- LPFileTransferState newState = [ft state];
- LPFileTransferType type = [ft type];
- int transferID = [ft ID];
-
- if ([peerContactEntry contact] == [self contact]) {
-
- NSString *htmlText = nil;
- NSString *divClass = nil;
-
- switch (newState) {
- case LPFileTransferPackaging:
- case LPFileTransferWaitingToBeAccepted:
- {
- NSString *elementID = [NSString stringWithFormat:@"fileTransfer_%d", transferID];
-
- if (![m_chatViewsController existsElementWithID:elementID]) {
- if (type == LPIncomingTransfer)
- {
- // Links for JavaScript -> Objective-C actions
- NSString *acceptLink = [NSString stringWithFormat:
- @"<a href=\"javascript:window.chatJSInterface.acceptTransfer(%d);\">%@</a>",
- transferID,
- [NSLocalizedString(@"accept", @"for file transfers listed in chat windows") stringByEscapingHTMLEntities]];
- NSString *rejectLink = [NSString stringWithFormat:
- @"<a href=\"javascript:window.chatJSInterface.rejectTransfer(%d);\">%@</a>",
- transferID,
- [NSLocalizedString(@"reject", @"for file transfers listed in chat windows") stringByEscapingHTMLEntities]];
-
- // Message text
- NSString *str1 = [NSString stringWithFormat:NSLocalizedString(@"Receiving file %@.",
- @"for file transfers listed in chat windows"),
- [self p_fileNameHTMLForFileTransfer:ft]];
- NSString *str2 = [NSString stringWithFormat:NSLocalizedString(@"You may %@ or %@ the transfer.",
- @"for file transfers listed in chat windows"),
- acceptLink, rejectLink];
-
- htmlText = [NSString stringWithFormat:@"%@<br/><span id=\"%@\">%@</span>",
- str1, elementID, str2];
- }
- else
- {
- NSString *str1 = [NSString stringWithFormat: NSLocalizedString(@"Sending file %@.",
- @"for file transfers listed in chat windows"),
- [self p_fileNameHTMLForFileTransfer:ft]];
-
- NSString *str2 = ( newState == LPFileTransferPackaging ?
- NSLocalizedString(@"<b>(packaging...)</b>", @"") :
- @"" );
-
- htmlText = [NSString stringWithFormat:@"%@<br/><span id=\"%@\">%@</span>",
- str1, elementID, str2];
- }
- divClass = @"smsReceivedReplyBlock";
- }
- else if (newState == LPFileTransferWaitingToBeAccepted) {
- [m_chatViewsController setInnerHTML:NSLocalizedString(@"", @"") forElementWithID:elementID];
- }
-
- break;
- }
-
- case LPFileTransferWasNotAccepted:
- {
- NSString *elementID = [NSString stringWithFormat:@"fileTransfer_%d", transferID];
- [m_chatViewsController setInnerHTML:NSLocalizedString(@"<b>(rejected)</b>", @"") forElementWithID:elementID];
- break;
- }
-
- case LPFileTransferRunning:
- {
- NSString *elementID = [NSString stringWithFormat:@"fileTransfer_%d", transferID];
- [m_chatViewsController setInnerHTML:NSLocalizedString(@"<b>(transferring...)</b>", @"") forElementWithID:elementID];
- break;
- }
-
- case LPFileTransferAbortedWithError:
- {
- NSString *elementID = [NSString stringWithFormat:@"fileTransfer_%d", transferID];
- NSString *formatStr = NSLocalizedString(@"<b>(error: %@)</b>", @"");
- NSString *html = [NSString stringWithFormat:formatStr, [[ft lastErrorMessage] stringByEscapingHTMLEntities]];
- [m_chatViewsController setInnerHTML:html forElementWithID:elementID];
-
- divClass = @"systemMessage";
- htmlText = [NSString stringWithFormat:
- NSLocalizedString(@"Transfer of file %@ was <b>aborted</b> with an error: %@.", @""),
- [self p_fileNameHTMLForFileTransfer:ft], [[ft lastErrorMessage] stringByEscapingHTMLEntities]];
- break;
- }
-
- case LPFileTransferCancelled:
- {
- NSString *elementID = [NSString stringWithFormat:@"fileTransfer_%d", transferID];
- [m_chatViewsController setInnerHTML:NSLocalizedString(@"<b>(cancelled)</b>", @"") forElementWithID:elementID];
-
- divClass = @"systemMessage";
- htmlText = [NSString stringWithFormat:
- NSLocalizedString(@"Transfer of file %@ was <b>cancelled</b>.", @""),
- [self p_fileNameHTMLForFileTransfer:ft]];
- break;
- }
-
- case LPFileTransferCompleted:
- {
- NSString *elementID = [NSString stringWithFormat:@"fileTransfer_%d", transferID];
- [m_chatViewsController setInnerHTML:NSLocalizedString(@"<b>(completed)</b>", @"") forElementWithID:elementID];
-
- divClass = @"systemMessage";
- htmlText = [NSString stringWithFormat:
- NSLocalizedString(@"Transfer of file %@ has <b>completed successfully</b>.", @""),
- [self p_fileNameHTMLForFileTransfer:ft]];
- break;
- }
-
- default:
- break;
- }
-
- if (htmlText) {
- [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:htmlText
- divClass:divClass
- scrollToVisibleMode:LPScrollWithAnimationIfAtBottom];
- }
- }
- }
- #pragma mark -
- #pragma mark Window Frame Save & Restore
- static NSMutableDictionary *s_windowFramesDictionary = nil;
- + (NSMutableDictionary *)p_windowFramesDictionary
- {
- if (s_windowFramesDictionary == nil) {
- NSString *framesDictionaryFilepath = [LPOurApplicationSupportFolderPath() stringByAppendingPathComponent:@"WindowFrames.plist"];
- s_windowFramesDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:framesDictionaryFilepath];
- if (s_windowFramesDictionary == nil) {
- s_windowFramesDictionary = [[NSMutableDictionary alloc] init];
- }
- }
-
- return s_windowFramesDictionary;
- }
- + (void)p_saveWindowFramesDictionary
- {
- if (s_windowFramesDictionary != nil) {
- NSString *framesDictionaryFilepath = [LPOurApplicationSupportFolderPath() stringByAppendingPathComponent:@"WindowFrames.plist"];
- [s_windowFramesDictionary writeToFile:framesDictionaryFilepath atomically:YES];
- }
- }
- + (NSRect)savedWindowFrameForChatWithContactNamed:(NSString *)contactName
- {
- NSParameterAssert(contactName);
-
- NSString *rectStr = [[self p_windowFramesDictionary] objectForKey:contactName];
-
- if (rectStr != nil) {
- return NSRectFromString(rectStr);
- } else {
- return NSZeroRect;
- }
- }
- + (void)saveWindowFrame:(NSRect)frame forChatWithContactNamed:(NSString *)contactName
- {
- NSParameterAssert(contactName);
-
- [[self p_windowFramesDictionary] setObject:NSStringFromRect(frame) forKey:contactName];
- [self p_saveWindowFramesDictionary];
- }
- #pragma mark -
- #pragma mark Actions
- - (IBAction)segmentClicked:(id)sender
- {
- int clickedSegment = [sender selectedSegment];
- int clickedSegmentTag = [[sender cell] tagForSegment:clickedSegment];
-
- if (clickedSegmentTag == 0) { // emoticons
- NSWindow *win = [self window];
- NSRect buttonFrame = [sender frame];
- NSPoint topRight = [win convertBaseToScreen:[[sender superview] convertPoint:buttonFrame.origin
- toView:nil]];
-
- [sender setImage:[NSImage imageNamed:@"emoticonIconPressed"] forSegment:clickedSegment];
- [(NSView *)sender display];
- [m_chatViewsController pickEmoticonWithMenuTopRightAt:NSMakePoint(topRight.x + [sender widthForSegment:clickedSegment], topRight.y)
- parentWindow:[self window]];
- [sender setImage:[NSImage imageNamed:@"emoticonIconUnpressed"] forSegment:clickedSegment];
- [(NSView *)sender display];
- }
- else if (clickedSegmentTag == 1) { // audibles
- NSDrawerState state = [m_audiblesController drawerState];
-
- if (state == NSDrawerClosedState || state == NSDrawerClosingState) {
- // Will be open afterwards
- [sender setImage:[NSImage imageNamed:@"bocasIconPressed"] forSegment:clickedSegment];
- }
- else {
- // Will be closed afterwards
- [sender setImage:[NSImage imageNamed:@"bocasIconUnpressed"] forSegment:clickedSegment];
- }
-
- [m_audiblesController toggleDrawer:sender];
- }
- }
- - (IBAction)sendMessage:(id)sender
- {
- NSAttributedString *attributedMessage = [m_inputTextField attributedStringValue];
- NSString *message = [attributedMessage stringByFlatteningAttachedEmoticons];
-
- // Check if the text is all made of whitespace.
- static NSCharacterSet *requiredCharacters = nil;
- if (requiredCharacters == nil) {
- requiredCharacters = [[[NSCharacterSet whitespaceAndNewlineCharacterSet] invertedSet] retain];
- }
-
- if ([message rangeOfCharacterFromSet:requiredCharacters].location != NSNotFound) {
- [self p_appendMessageToWebView:message subject:nil timestamp:[NSDate date] inbound:NO];
- [m_chat sendMessageWithPlainTextVariant:message XHTMLVariant:nil URLs:nil];
- m_hasAlreadyProcessedSomeMessages = YES;
- }
-
- // Store it in the input line history
- if ([m_inputLineHistory count] > 0)
- [m_inputLineHistory replaceObjectAtIndex:0 withObject:attributedMessage];
- else
- [m_inputLineHistory addObject:attributedMessage];
-
- if ([m_inputLineHistory count] > INPUT_LINE_HISTORY_ITEMS_MAX)
- [m_inputLineHistory removeObjectsInRange:NSMakeRange(INPUT_LINE_HISTORY_ITEMS_MAX, [m_inputLineHistory count] - INPUT_LINE_HISTORY_ITEMS_MAX)];
- [m_inputLineHistory insertObject:@"" atIndex:0];
- m_currentInputLineHistoryEntryIndex = 0;
-
- // Prepare the window to take another message from the user
- [[self window] makeFirstResponder:m_inputTextField];
- [self setMessageTextEntryString:@""];
- }
- - (IBAction)sendSMS:(id)sender
- {
- if ([m_delegate respondsToSelector:@selector(chatController:sendSMSToContact:)]) {
- [m_delegate chatController:self sendSMSToContact:[self contact]];
- }
- }
- - (IBAction)sendFile:(id)sender
- {
- NSOpenPanel *op = [NSOpenPanel openPanel];
-
- [op setPrompt:NSLocalizedString(@"Send", @"button for the file selection sheet")];
-
- [op setCanChooseFiles:YES];
- [op setCanChooseDirectories:NO];
- [op setResolvesAliases:YES];
- [op setAllowsMultipleSelection:NO];
-
- [op beginSheetForDirectory:nil
- file:nil
- types:nil
- modalForWindow:[self window]
- modalDelegate:self
- didEndSelector:@selector(p_openPanelDidEnd:returnCode:contextInfo:)
- contextInfo:NULL];
- }
- - (void)p_openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
- {
- if (returnCode == NSOKButton) {
- LPContactEntry *contactEntry = [m_chat activeContactEntry];
-
- if (contactEntry && [contactEntry canDoFileTransfer])
- [[LPFileTransfersManager fileTransfersManager] startSendingFile:[panel filename]
- toContactEntry:[m_chat activeContactEntry]];
- }
- }
- - (IBAction)editContact:(id)sender
- {
- if ([m_delegate respondsToSelector:@selector(chatController:editContact:)]) {
- [m_delegate chatController:self editContact:[self contact]];
- }
- }
- - (IBAction)selectChatAddress:(id)sender
- {
- LPContactEntry *selectedEntry = [sender representedObject];
- [m_chat setActiveContactEntry:selectedEntry];
- [m_contact setPreferredContactEntry:selectedEntry];
- }
- - (IBAction)saveDocumentTo:(id)sender
- {
- NSSavePanel *sp = [NSSavePanel savePanel];
-
- [sp setCanSelectHiddenExtension:YES];
- [sp setRequiredFileType:@"webarchive"];
-
- [sp beginSheetForDirectory:nil
- file:[m_chatViewsController chatDocumentTitle]
- modalForWindow:[self window]
- modalDelegate:self
- didEndSelector:@selector(p_savePanelDidEnd:returnCode:contextInfo:)
- contextInfo:NULL];
- }
- - (void)p_savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
- {
- if (returnCode == NSOKButton) {
- NSError *error;
- if (![m_chatViewsController saveDocumentToFile:[sheet filename] hideExtension:[sheet isExtensionHidden] error:&error]) {
- [self presentError:error];
- }
- }
- }
- - (IBAction)printDocument:(id)sender
- {
- NSPrintOperation *op = [NSPrintOperation printOperationWithView:[[[m_chatWebView mainFrame] frameView] documentView]];
- [op runOperationModalForWindow:[self window]
- delegate:nil
- didRunSelector:NULL
- contextInfo:NULL];
- }
- #pragma mark Searching
- - (IBAction)showFindPanel:(id)sender
- {
- [[LPChatFindPanelController sharedFindPanel] showWindow:sender];
- }
- - (IBAction)findNext:(id)sender
- {
- LPChatFindPanelController *findPanel = [LPChatFindPanelController sharedFindPanel];
- NSString *searchStr = [findPanel searchString];
- BOOL found = NO;
-
- if ([searchStr length] > 0)
- found = [m_chatWebView searchFor:searchStr direction:YES caseSensitive:NO wrap:YES];
-
- [findPanel searchStringWasFound:found];
- }
- - (IBAction)findPrevious:(id)sender
- {
- LPChatFindPanelController *findPanel = [LPChatFindPanelController sharedFindPanel];
- NSString *searchStr = [findPanel searchString];
- BOOL found = NO;
-
- if ([searchStr length] > 0)
- found = [m_chatWebView searchFor:searchStr direction:NO caseSensitive:NO wrap:YES];
-
- [findPanel searchStringWasFound:found];
- }
- - (IBAction)useSelectionForFind:(id)sender
- {
- NSString *selectedString = nil;
- id firstResponder = [[self window] firstResponder];
-
- if ([firstResponder isKindOfClass:[NSText class]])
- selectedString = [[firstResponder string] substringWithRange:[firstResponder selectedRange]];
- else if ([firstResponder isDescendantOf:m_chatWebView])
- selectedString = [[m_chatWebView selectedDOMRange] toString];
-
- if ([selectedString length] > 0)
- [[LPChatFindPanelController sharedFindPanel] setSearchString:selectedString];
- }
- #pragma mark Action Validation
- - (BOOL)p_validateAction:(SEL)action
- {
- // The sendSMS: action is not validated in here so that its menu item is always enabled. This makes it easier for the user to
- // get a window for sending SMS messages regardless of the current state of the GUI. If the contact supports sending SMS
- // it will be added automatically to the list of recipients for the message. Otherwise, the Send SMS window will show up
- // without any recipients. OTOH, the toolbar button for sending SMS messages is validated in the toolbar item validation method
- // and is disabled if the contact doesn't support sending SMS messages. This way we get an easy to check visual cue for the
- // capabilities of the contact.
-
- if (action == @selector(sendFile:)) {
- return ([[m_chat activeContactEntry] canDoFileTransfer] &&
- [[m_chat activeContactEntry] isOnline]);
- }
- else if (action == @selector(useSelectionForFind:)) {
- id firstResponder = [[self window] firstResponder];
-
- if ([firstResponder isKindOfClass:[NSText class]])
- return ([firstResponder selectedRange].length > 0);
- else if ([firstResponder isDescendantOf:m_chatWebView])
- return ([[[m_chatWebView selectedDOMRange] toString] length] > 0);
- else
- return NO;
- }
- else {
- return YES;
- }
- }
- - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
- {
- return [self p_validateAction:[menuItem action]];
- }
- #pragma mark Choose JID Panel
- - (void)p_reevaluateJIDPanelOKButtonEnabled
- {
- [m_chooseJIDPanelOKButton setEnabled:([[[m_chooseJIDPanelJIDEntryView JIDEntryTextField] stringValue] length] > 0
- && [[m_chooseJIDPanelJIDEntryView account] isOnline])];
- }
- - (IBAction)chooseJIDPanelOK:(id)sender
- {
- // Cleanup the sheet
- NSWindow *sheet = [[self window] attachedSheet];
-
- [NSApp endSheet:sheet];
- [sheet orderOut:nil];
- [m_chooseJIDPanelJIDEntryView removeObserver:self forKeyPath:@"account.online"];
-
- LPAccount *account = [m_chooseJIDPanelJIDEntryView account];
- NSString *jid = [m_chooseJIDPanelJIDEntryView enteredJID];
-
- LPContactEntry *contactEntry = [[LPRoster roster] contactEntryForAddress:jid
- account:account
- createNewHiddenWithNameIfNotFound:jid];
-
- LPChatsManager *chatsManager = [LPChatsManager chatsManager];
- LPChat *chat = [chatsManager chatForContact:[contactEntry contact]];
-
- if (chat == nil) {
- chat = [chatsManager startChatWithContactEntry:contactEntry];
-
- [self p_setChat:chat];
- [self setContact:[chat contact]];
- }
- else {
- if ([m_delegate respondsToSelector:@selector(chatController:orderChatWithContactEntryToFront:)]) {
- [m_delegate chatController:self orderChatWithContactEntryToFront:contactEntry];
- } else {
- NSBeep();
- NSLog(@"%@'s delegate should implement the method %@",
- NSStringFromClass([self class]), @"chatController:orderChatWithContactEntryToFront:");
- }
-
- [self close];
- }
- }
- - (IBAction)chooseJIDPanelCancel:(id)sender
- {
- NSWindow *sheet = [[self window] attachedSheet];
-
- [NSApp endSheet:sheet];
- [sheet orderOut:nil];
- [m_chooseJIDPanelJIDEntryView removeObserver:self forKeyPath:@"account.online"];
-
- [self close];
- }
- - (IBAction)copyStatusMessage:(id)sender
- {
- NSPasteboard *pboard = [NSPasteboard generalPasteboard];
-
- [pboard declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] owner:nil];
- [pboard setString:[[self contact] statusMessage] forType:NSStringPboardType];
- }
- #pragma mark -
- #pragma mark LPChat Delegate Methods
- - (void)chat:(LPChat *)chat didReceiveErrorMessage:(NSString *)message
- {
- // Post a "system message"
- NSString *systemMessage = [NSString stringWithFormat:@"ERROR: %@", message];
- [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:[systemMessage stringByEscapingHTMLEntities]
- divClass:@"systemMessage"
- scrollToVisibleMode:LPScrollWithAnimationIfAtBottom];
- }
- - (void)chat:(LPChat *)chat didReceiveMessageFromNick:(NSString *)nick subject:(NSString *)subject plainTextVariant:(NSString *)plainTextMessage XHTMLVariant:(NSString *)XHTMLMessage URLs:(NSArray *)URLs
- {
- // DEBUG: this is useful for testing the code that handles the display of
- // received SMS messages without having to actually waste SMS messages.
- // if ([plainTextMessage hasPrefix:@"sms: "]) {
- // [self chat:chat didReceiveSMSFrom:@"00351964301673@phone.im.sapo.pt" withBody:plainTextMessage date:[NSDate date] newCredit:99 newFreeMessages:88 newTotalSentThisMonth:77];
- // return;
- // }
-
- // Add in the URLs
- NSString *messageBody = plainTextMessage;
-
- if (URLs && [URLs count] > 0) {
- NSMutableString *messageWithURLs = [NSMutableString stringWithString:messageBody];
-
- NSEnumerator *urlEnum = [URLs objectEnumerator];
- NSString *url;
- while (url = [urlEnum nextObject]) {
- [messageWithURLs appendFormat:@" | %@", url];
- }
- messageBody = messageWithURLs;
- }
-
- // Don't do everything at the same time. Allow the scroll animation to run first so that it doesn't appear choppy.
- [[m_chatViewsController grabMethodForAfterScrollingWithTarget:self]
- p_notifyUserAboutReceivedMessage:messageBody
- notificationsHandlerSelector:( !m_hasAlreadyProcessedSomeMessages ?
- @selector(notifyReceptionOfFirstMessage:fromContact:) :
- @selector(notifyReceptionOfMessage:fromContact:) )];
- [self p_appendMessageToWebView:messageBody subject:subject timestamp:[NSDate date] inbound:YES];
- m_hasAlreadyProcessedSomeMessages = YES;
- }
- - (void)chat:(LPChat *)chat didReceiveSystemMessage:(NSString *)message
- {
- // Post a "system message"
- NSString *systemMessage = [NSString stringWithFormat:@"System Message: %@", message];
- [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:[systemMessage stringByEscapingHTMLEntities]
- divClass:@"systemMessage"
- scrollToVisibleMode:LPScrollWithAnimationIfAtBottom];
- }
- - (void)chat:(LPChat *)chat didReceiveResultOfSMSSentTo:(NSString *)destinationPhoneNr withBody:(NSString *)msgBody resultCode:(int)result nrUsedMsgs:(int)nrUsedMsgs nrUsedChars:(int)nrUsedChars newCredit:(int)newCredit newFreeMessages:(int)newFreeMessages newTotalSentThisMonth:(int)newTotalSentThisMonth
- {
- // DEBUG:
- // NSString *text = [NSString stringWithFormat:@"SMS SENT to %@ (message: \"%@\"). Result: %d , %d msgs used, %d chars used, new credit: %d , new free msgs: %d , new total sent: %d", destinationPhoneNr, msgBody, result, nrUsedMsgs, nrUsedChars, newCredit, newFreeMessages, newTotalSentThisMonth];
-
- NSString *phoneNr = ( [destinationPhoneNr isPhoneJID] ?
- [destinationPhoneNr userPresentablePhoneNrRepresentation] :
- destinationPhoneNr );
- NSString *htmlText = nil;
-
- if (result == 1) {
- // Success
- htmlText = [NSString stringWithFormat:
- NSLocalizedString(@"SMS <b>sent</b> to \"%@\" at %@<br/>Used: %d message(s), total of %d characters.<p>\"<b>%@</b>\"</p>", @""),
- [phoneNr stringByEscapingHTMLEntities],
- [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:nil],
- nrUsedMsgs, nrUsedChars,
- [msgBody stringByEscapingHTMLEntities]
- //newCredit, newFreeMessages, newTotalSentThisMonth
- ];
- }
- else {
- // Failure
- htmlText = [NSString stringWithFormat:
- NSLocalizedString(@"<b>Failed</b> to send SMS to \"%@\" at %@.<p>\"<b>%@</b>\"</p>", @""),
- [phoneNr stringByEscapingHTMLEntities],
- [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:nil],
- [msgBody stringByEscapingHTMLEntities]];
- }
-
- [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:htmlText
- divClass:@"smsSentReplyBlock"
- scrollToVisibleMode:LPScrollWithAnimationIfAtBottom];
- }
- - (void)chat:(LPChat *)chat didReceiveSMSFrom:(NSString *)sourcePhoneNr withBody:(NSString *)msgBody date:(NSDate *)date newCredit:(int)newCredit newFreeMessages:(int)newFreeMessages newTotalSentThisMonth:(int)newTotalSentThisMonth
- {
- // DEBUG:
- // NSString *text = [NSString stringWithFormat:@"SMS RECEIVED on %@ from %@: \"%@\". New credit: %d , new free msgs: %d , new total sent: %d", date, sourcePhoneNr, msgBody, newCredit, newFreeMessages, newTotalSentThisMonth];
-
- NSString *phoneNr = ( [sourcePhoneNr isPhoneJID] ?
- [sourcePhoneNr userPresentablePhoneNrRepresentation] :
- sourcePhoneNr );
- NSString *htmlText = [NSString stringWithFormat:
- NSLocalizedString(@"SMS <b>received</b> from \"%@\" at %@<p>\"<b>%@</b>\"</p>", @""),
- // We don't use the date provided by the server because it is nil sometimes
- [phoneNr stringByEscapingHTMLEntities],
- [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:nil],
- [m_chatViewsController HTMLifyRawMessageString:msgBody]];
-
- // Don't do everything at the same time. Allow the scroll animation to run first so that it doesn't appear choppy.
- [[m_chatViewsController grabMethodForAfterScrollingWithTarget:self]
- p_notifyUserAboutReceivedMessage:msgBody
- notificationsHandlerSelector:@selector(notifyReceptionOfSMSMessage:fromContact:)];
-
- [m_chatViewsController appendDIVBlockToWebViewWithInnerHTML:htmlText
- divClass:@"smsReceivedReplyBlock"
- scrollToVisibleMode:LPScrollWithAnimationIfAtBottom];
-
- LPContactEntry *activeEntry = [m_chat activeContactEntry];
- [[LPRecentMessagesStore sharedMessagesStore] storeRawHTMLBlock:htmlText
- withDIVClass:@"smsReceivedReplyBlock"
- forJID:[activeEntry address]
- thruAccountJID:[[activeEntry account] JID]];
- }
- - (void)chat:(LPChat *)chat didReceiveAudibleWithResourceName:(NSString *)resourceName msgBody:(NSString *)body msgHTMLBody:(NSString *)htmlBody
- {
- LPAudibleSet *set = [LPAudibleSet defaultAudibleSet];
-
- if ([set isValidAudibleResourceName:resourceName]) {
- NSString *localPath = [set filepathForAudibleWithName:resourceName];
-
- if (localPath == nil) {
- // We don't have this audible in local storage yet. Start loading it and insert it into the webview later.
-
- [[self p_pendingAudiblesSet] addObject:resourceName];
- [set startLoadingAudibleFromServer:resourceName];
- } else {
- [self p_appendAudibleWithResourceName:resourceName inbound:YES];
- }
- }
- else {
- [self chat:chat didReceiveErrorMessage:[NSString stringWithFormat:@"Received an unknown audible: \"%@\"",
- resourceName]];
- // Send an error back to the other contact
- [m_chat sendInvalidAudibleErrorWithMessage:@"Bad Request: the audible that was sent is unknown!"
- originalResourceName:resourceName
- originalBody:body
- originalHTMLBody:htmlBody];
- }
- }
- #pragma mark -
- #pragma mark LPJIDEntryView Notifications
- - (void)JIDEntryViewEnteredJIDDidChange:(LPJIDEntryView *)view;
- {
- [self p_reevaluateJIDPanelOKButtonEnabled];
- }
- #pragma mark -
- #pragma mark LPAudibleSet Notifications
- - (void)audibleSetDidFinishLoadingAudible:(NSNotification *)notification
- {
- NSString *audibleResourceName = [[notification userInfo] objectForKey:@"LPAudibleName"];
-
- if ([[self p_pendingAudiblesSet] containsObject:audibleResourceName]) {
- [[self p_pendingAudiblesSet] removeObject:audibleResourceName];
- [self p_appendAudibleWithResourceName:audibleResourceName inbound:YES];
- }
- }
- #pragma mark -
- #pragma mark LPFileTransfer Notifications
- - (void)fileTransferStateDidChange:(NSNotification *)notification
- {
- LPFileTransfer *ft = [notification object];
- LPContactEntry *peerContactEntry = [ft peerContactEntry];
-
- if ([peerContactEntry contact] == [self contact]) {
- [self updateInfoForFileTransfer:ft];
- }
- }
- #pragma mark -
- #pragma mark NSResponder Methods
- - (void)keyDown:(NSEvent *)theEvent
- {
- /* If a keyDown event reaches this low in the responder chain then it means that no text field is
- active to process the event. Activate the input text field and reroute the event that was received
- back to it. */
- if ([m_inputTextField canBecomeKeyView]) {
- NSWindow *window = [self window];
- [window makeFirstResponder:m_inputTextField];
- [[window firstResponder] keyDown:theEvent];
- } else {
- [super keyDown:theEvent];
- }
- }
- #pragma mark -
- #pragma mark Private Methods
- #pragma mark ** JIDs Popup Menu
- - (NSAttributedString *)p_attributedTitleOfJIDMenuItemForContactEntry:(LPContactEntry *)entry withFont:(NSFont *)font
- {
- LPStatus entryStatus = [entry status];
-
- NSString *menuItemTitle = ( (entryStatus == LPStatusInvisible || entryStatus == LPStatusOffline) ?
- [NSString stringWithFormat:@"%@ %C %@",
- [entry humanReadableAddress], 0x2014 /* em-dash */,
- NSLocalizedStringFromTable(LPStatusStringFromStatus([entry status]), @"Status", @"")] :
- [entry humanReadableAddress] );
-
- NSDictionary *attribs = ( [entry isOnline] ?
- [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName] :
- [NSDictionary dictionaryWithObjectsAndKeys:
- font, NSFontAttributeName,
- [NSColor grayColor], NSForegroundColorAttributeName, nil] );
-
- return ( (menuItemTitle != nil && attribs != nil) ?
- [[[NSAttributedString alloc] initWithString:menuItemTitle attributes:attribs] autorelease] :
- nil );
- }
- - (NSMenuItem *)p_popupMenuHeaderItemForAccount:(LPAccount *)account
- {
- id item = nil;
- int idx = [m_addressesPopUp indexOfItemWithRepresentedObject:account];
-
- if (idx >= 0) {
- item = [m_addressesPopUp itemAtIndex:idx];
- }
- else {
- item = [[NSMenuItem alloc] initWithTitle:@"" action:NULL keyEquivalent:@""];
-
- [item setTitle:[NSString stringWithFormat:NSLocalizedString(@"Account \"%@\"", @"Chat and SMS window popup menu"), [account description]]];
- [item setIndentationLevel:0];
- [item setEnabled:NO];
- [item setRepresentedObject:account];
-
- [item autorelease];
- }
-
- return item;
- }
- - (NSMenuItem *)p_popupMenuItemForEntry:(LPContactEntry *)entry
- {
- id item = nil;
- int idx = [m_addressesPopUp indexOfItemWithRepresentedObject:entry];
-
- if (idx >= 0) {
- item = [m_addressesPopUp itemAtIndex:idx];
- }
- else {
- item = [[NSMenuItem alloc] initWithTitle:@"" action:@selector(selectChatAddress:) keyEquivalent:@""];
-
- NSAttributedString *attributedTitle =
- [self p_attribut…
Large files files are truncated, but you can click here to view the full file