Prevent sending 0-byte files. Fixes #8711.
[adiumx.git] / Source / AIExtendedStatusPlugin.m
blobb03dcfb9354c13b1abefee1734a4101e05a3b4e1
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 "AIExtendedStatusPlugin.h"
18 #import <Adium/AIContactControllerProtocol.h>
19 #import <Adium/AIContentControllerProtocol.h>
20 #import <Adium/AIPreferenceControllerProtocol.h>
21 #import <AIUtilities/AIMutableOwnerArray.h>
22 #import <AIUtilities/AIAttributedStringAdditions.h>
23 #import <AIUtilities/AIMutableStringAdditions.h>
24 #import <AIUtilities/AIDateFormatterAdditions.h>
25 #import <Adium/AIAbstractListController.h>
26 #import <Adium/AIListObject.h>
27 #import <Adium/AIListContact.h>
29 #define STATUS_MAX_LENGTH       100
31 @interface AIExtendedStatusPlugin (PRIVATE)
32 - (void)preferencesChanged:(NSNotification *)notification;
33 @end
35 /*!
36  * @class AIExtendedStatusPlugin
37  * @brief Manage the 'extended status' shown in the contact list
38  *
39  * If the contact list layout calls for displaying a status message or idle time (or both), this component manages
40  * generating the appropriate string, storing it in the @"ExtendedStatus" status object, and updating it as necessary.
41  */
42 @implementation AIExtendedStatusPlugin
44 /*!
45  * @brief Install
46  */
47 - (void)installPlugin
49         [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_LIST_LAYOUT];
50         
51         whitespaceAndNewlineCharacterSet = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain];
54 /*!
55  * @brief Uninstall
56  */
57 - (void)uninstallPlugin
59         [[adium preferenceController] unregisterPreferenceObserver:self];
60         [[adium contactController] unregisterListObjectObserver:self];
63 /*!
64  * @brief Preferences changes
65  *
66  * PREF_GROUP_LIST_LAYOUT changed; update our list objects if needed.
67  */
68 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
69                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
71         BOOL oldShowStatus = showStatus;
72         BOOL oldShowIdle = showIdle;
73         
74         EXTENDED_STATUS_STYLE statusStyle = [[prefDict objectForKey:KEY_LIST_LAYOUT_EXTENDED_STATUS_STYLE] intValue];
75         showStatus = ((statusStyle == STATUS_ONLY) || (statusStyle == IDLE_AND_STATUS));
76         showIdle = ((statusStyle == IDLE_ONLY) || (statusStyle == IDLE_AND_STATUS));
77         
78         if (firstTime) {
79                 [[adium contactController] registerListObjectObserver:self];
80         } else {
81                 if ((oldShowStatus != showStatus) || (oldShowIdle != showIdle)) {
82                         [[adium contactController] updateAllListObjectsForObserver:self];
83                 }
84         }
87 /*!
88  * @brief Update list object's extended status messages
89  */
90 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
92         NSSet           *modifiedAttributes = nil;
94         /* Work at the parent contact (metacontact, etc.) level for extended status, since that's what's displayed in the contact list.
95          * We completely ignore status updates sent for an object which isn't the highest-level up (e.g. is within a metacontact).
96          */
97     if ((inModifiedKeys == nil || 
98                  (showIdle && [inModifiedKeys containsObject:@"Idle"]) ||
99                  (showStatus && ([inModifiedKeys containsObject:@"StatusMessage"] ||
100                                                  [inModifiedKeys containsObject:@"ContactListDisplayName"] ||
101                                                  [inModifiedKeys containsObject:@"StatusName"]))) &&
102                 [inObject isKindOfClass:[AIListContact class]] &&
103                 ([(AIListContact *)inObject parentContact] == inObject)) {
104                 NSMutableString *statusMessage = nil;
105                 NSString                *finalMessage = nil;
106                 int                             idle;
108                 if (showStatus) {
109                         NSAttributedString *filteredMessage;
111                         filteredMessage = [[adium contentController] filterAttributedString:[(AIListContact *)inObject contactListStatusMessage]
112                                                                                                                                 usingFilterType:AIFilterContactList
113                                                                                                                                           direction:AIFilterIncoming
114                                                                                                                                                 context:inObject];
115                         statusMessage = [[[[filteredMessage string] stringByTrimmingCharactersInSet:whitespaceAndNewlineCharacterSet] mutableCopy] autorelease];
117                         //Incredibly long status messages are slow to size, so we crop them to a reasonable length
118                         int statusMessageLength = [statusMessage length];
119                         if (statusMessageLength == 0) {
120                                 statusMessage = nil;
122                         } else if (statusMessageLength > STATUS_MAX_LENGTH) {
123                                 [statusMessage deleteCharactersInRange:NSMakeRange(STATUS_MAX_LENGTH,
124                                                                                                                                    [statusMessage length] - STATUS_MAX_LENGTH)];
125                         }
127                         /* Linebreaks in the status message cause vertical alignment issues. */
128                         [statusMessage convertNewlinesToSlashes];       
129                 }
131                 idle = (showIdle ? [inObject integerStatusObjectForKey:@"Idle"] : 0);
133                 //
134                 if (idle > 0 && statusMessage) {
135                         finalMessage = [NSString stringWithFormat:@"(%@) %@",[self idleStringForMinutes:idle], statusMessage];
136                 } else if (idle > 0) {
137                         finalMessage = [NSString stringWithFormat:@"(%@)",[self idleStringForMinutes:idle]];
138                 } else {
139                         finalMessage = statusMessage;
140                 }
142                 [[inObject displayArrayForKey:@"ExtendedStatus"] setObject:finalMessage withOwner:self];
143                 modifiedAttributes = [NSSet setWithObject:@"ExtendedStatus"];
144         }
145         
146    return modifiedAttributes;
151  * @brief Determine the idle string
153  * @param minutes Number of minutes idle
154  * @result A localized string to display for the idle time
155  */
156 - (NSString *)idleStringForMinutes:(int)minutes //input is actualy minutes
158         // Cap Idletime at 599400 minutes (999 hours)
159         return ((minutes > 599400) ? AILocalizedString(@"Idle",nil) : [NSDateFormatter stringForApproximateTimeInterval:(minutes * 60) abbreviated:YES]);
162 @end