Added a debug log to investigate #8685, as it worksforme. Refs #8685
[adiumx.git] / Source / AdiumTyping.m
blob199511afc88c869fec20be03c468cfbf07060e56
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
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.
8  * 
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.
12  * 
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.
15  */
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;
35 @end
37 @implementation AdiumTyping
39 /*!
40  * @brief Init
41  */
42  - (id)init
44         if ((self = [super init])) {
45                 [[adium notificationCenter] addObserver:self
46                                                                            selector:@selector(didSendMessage:)
47                                                                                    name:Interface_DidSendEnteredMessage
48                                                                                  object:nil];
49                 [[adium notificationCenter] addObserver:self
50                                                                            selector:@selector(chatWillClose:)
51                                                                                    name:Chat_WillClose
52                                                                                  object:nil];
53         }
54         
55         return self;
58 - (void)dealloc
60         [[adium notificationCenter] removeObserver:self];
61         
62         [super dealloc];
65 /*!
66  * @brief Update the typing status of a chat
67  * 
68  * @param hasEnteredText YES if there is text entered for the chat
69  * @param chat AIChat where typing has occured
70  */
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:)
76                                                                                            object:chat];
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.
80         if (hasEnteredText) {
81                 [self monitorTypingInChat:chat];
82                 [self setTypingState:AITyping ofChat:chat];
83         } else {
84                 [self performSelector:@selector(_clearUserTypingForChat:)
85                                    withObject:chat
86                                    afterDelay:DELAY_BEFORE_CLEARING_TYPING];
87         }
90 /*!
91  * @brief Clear the typing state of a chat
92  */
93 - (void)_clearUserTypingForChat:(AIChat *)chat
95         [self stopMonitoringTypingInChat:chat];
96         [self setTypingState:AINotTyping ofChat:chat];
99 /*!
100  * @brief Clear typing after a message is sent
101  */
102 - (void)didSendMessage:(NSNotification *)notification
104         AIChat  *chat = [notification object];
105         
106         if (![[chat account] suppressTypingNotificationChangesAfterSend]) {
107                 [self _clearUserTypingForChat:chat];
108         } else {
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];  
113         }
117  * @brief Clear all typing information we've placed on a chat before it closes
118  */
119 - (void)chatWillClose:(NSNotification *)notification
121     AIChat      *chat = [notification object];
122         
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.
135  */
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]
145                                                                                                  destination:nil
146                                                                                                  typingState:typingState];
147                 [[adium contentController] sendContentObject:contentObject];
148                 
149                 //Remember the state
150                 [chat setStatusObject:(typingState == AINotTyping ? nil : [NSNumber numberWithInt:typingState])
151                                            forKey:OUR_TYPING_STATE
152                                            notify:NotifyNever];
153         }
157 //Typing "Time-Out" ----------------------------------------------------------------------------------------------------
158 #pragma mark Typing "Time-Out"
160  * @brief Monitor a chat to detect pauses in our user's typing
161  */
162 - (void)monitorTypingInChat:(AIChat *)chat
164         NSTimer *existingTimer = [chat statusObjectForKey:ENTERED_TEXT_TIMER];
165         
166         if (existingTimer) {
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]];
169                 
170         } else {
171                 //If no timer exists, create one for the chat
172                 existingTimer = [NSTimer scheduledTimerWithTimeInterval:DELAY_BEFORE_PAUSING_TYPING
173                                                                                                                  target:self
174                                                                                                            selector:@selector(_typingHasPausedInChat:)
175                                                                                                            userInfo:chat
176                                                                                                                 repeats:NO];
177                 [chat setStatusObject:existingTimer forKey:ENTERED_TEXT_TIMER notify:NotifyNever];
178                 
179         }
183  * @brief Stop monitoring typing in a chat
184  */
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 
193  */
194 - (void)_typingHasPausedInChat:(NSTimer *)inTimer
196         AIChat  *chat = [inTimer userInfo];
197         
198         [self setTypingState:AIEnteredText ofChat:chat];
199         [self stopMonitoringTypingInChat:chat];
202 @end