2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import "AdiumTyping.h"
18 #import <Adium/AIInterfaceControllerProtocol.h>
19 #import <Adium/AIContactControllerProtocol.h>
20 #import <Adium/AIContentControllerProtocol.h>
21 #import "AIContentTyping.h"
22 #import <Adium/AIChat.h>
23 #import <Adium/AIAccount.h>
25 #define OUR_TYPING_STATE @"OurTypingState"
26 #define ENTERED_TEXT_TIMER @"EnteredTextTimer"
28 #define DELAY_BEFORE_PAUSING_TYPING 3.0 //Wait for 3 seconds of inactivity before pausing typing
29 #define DELAY_BEFORE_CLEARING_TYPING 2.0 //Wait 2 seconds before clearing the typing flag
31 @interface AdiumTyping (PRIVATE)
32 - (void)setTypingState:(AITypingState)typingState ofChat:(AIChat *)chat;
33 - (void)monitorTypingInChat:(AIChat *)chat;
34 - (void)stopMonitoringTypingInChat:(AIChat *)chat;
37 @implementation AdiumTyping
44 if ((self = [super init])) {
45 [[adium notificationCenter] addObserver:self
46 selector:@selector(didSendMessage:)
47 name:Interface_DidSendEnteredMessage
49 [[adium notificationCenter] addObserver:self
50 selector:@selector(chatWillClose:)
60 [[adium notificationCenter] removeObserver:self];
66 * @brief Update the typing status of a chat
68 * @param hasEnteredText YES if there is text entered for the chat
69 * @param chat AIChat where typing has occured
71 - (void)userIsTypingContentForChat:(AIChat *)chat hasEnteredText:(BOOL)hasEnteredText
73 //Cancel any existing delayed perform selectors waiting to clear our typing flag
74 [NSObject cancelPreviousPerformRequestsWithTarget:self
75 selector:@selector(_clearUserTypingForChat:)
78 //To prevent "Flickering" of our typing state, we wait a short period of time before clearing our typing flag.
79 //Setting our typing flag always happens immediately.
81 [self monitorTypingInChat:chat];
82 [self setTypingState:AITyping ofChat:chat];
84 [self performSelector:@selector(_clearUserTypingForChat:)
86 afterDelay:DELAY_BEFORE_CLEARING_TYPING];
91 * @brief Clear the typing state of a chat
93 - (void)_clearUserTypingForChat:(AIChat *)chat
95 [self stopMonitoringTypingInChat:chat];
96 [self setTypingState:AINotTyping ofChat:chat];
100 * @brief Clear typing after a message is sent
102 - (void)didSendMessage:(NSNotification *)notification
104 AIChat *chat = [notification object];
106 if (![[chat account] suppressTypingNotificationChangesAfterSend]) {
107 [self _clearUserTypingForChat:chat];
109 //Some protocols implicitly clear typing when a message is sent. For these protocols we'll just update our
110 //typing state locally. There is no need to send out a typing notification and doing so may actually cause
111 //undesirable behavior.
112 [chat setStatusObject:nil forKey:OUR_TYPING_STATE notify:NotifyNever];
117 * @brief Clear all typing information we've placed on a chat before it closes
119 - (void)chatWillClose:(NSNotification *)notification
121 AIChat *chat = [notification object];
123 [self setTypingState:AINotTyping ofChat:chat];
124 [self stopMonitoringTypingInChat:chat];
128 //Typing State ---------------------------------------------------------------------------------------------------------
129 #pragma mark Typing State
131 * @brief Send an AIContentTyping object for an AITypingState on a given chat
133 * The chat determines whether the notification should be sent or not, based on the account preference and, possibly,
134 * the temporary suppression status object.
136 - (void)setTypingState:(AITypingState)typingState ofChat:(AIChat *)chat
138 if ([chat integerStatusObjectForKey:OUR_TYPING_STATE] != typingState) {
139 AIContentTyping *contentObject;
141 //Send typing content object (It will go directly to the account since typing content isn't tracked or filtered)
142 //XXX - Typing isn't content
143 contentObject = [AIContentTyping typingContentInChat:chat
144 withSource:[chat account]
146 typingState:typingState];
147 [[adium contentController] sendContentObject:contentObject];
150 [chat setStatusObject:(typingState == AINotTyping ? nil : [NSNumber numberWithInt:typingState])
151 forKey:OUR_TYPING_STATE
157 //Typing "Time-Out" ----------------------------------------------------------------------------------------------------
158 #pragma mark Typing "Time-Out"
160 * @brief Monitor a chat to detect pauses in our user's typing
162 - (void)monitorTypingInChat:(AIChat *)chat
164 NSTimer *existingTimer = [chat statusObjectForKey:ENTERED_TEXT_TIMER];
167 //If a timer exists, it is cheaper to reset it rather than create a new one
168 [existingTimer setFireDate:[NSDate dateWithTimeIntervalSinceNow:DELAY_BEFORE_PAUSING_TYPING]];
171 //If no timer exists, create one for the chat
172 existingTimer = [NSTimer scheduledTimerWithTimeInterval:DELAY_BEFORE_PAUSING_TYPING
174 selector:@selector(_typingHasPausedInChat:)
177 [chat setStatusObject:existingTimer forKey:ENTERED_TEXT_TIMER notify:NotifyNever];
183 * @brief Stop monitoring typing in a chat
185 - (void)stopMonitoringTypingInChat:(AIChat *)chat
187 [[chat statusObjectForKey:ENTERED_TEXT_TIMER] invalidate];
188 [chat setStatusObject:nil forKey:ENTERED_TEXT_TIMER notify:NotifyNever];
192 * @brief Invoked when the user
194 - (void)_typingHasPausedInChat:(NSTimer *)inTimer
196 AIChat *chat = [inTimer userInfo];
198 [self setTypingState:AIEnteredText ofChat:chat];
199 [self stopMonitoringTypingInChat:chat];