Simplify the "status message" contact/account property into one getter method. Correct error -1728 from AS on contact's status messages. Fixes #13460.
The totally undocumented -1728 error appears to be caused by runtime type disagreeing with event type. "status message" is apparently assumed to only have 1 code, so specifying one for contacts and one for accounts was confusing it when it was going to fetch it.
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 <Adium/AIListObject.h>
18 #import <Adium/AIContactControllerProtocol.h>
19 #import <Adium/AIListContact.h>
20 #import <Adium/AIListGroup.h>
21 #import <Adium/AIService.h>
22 #import <Adium/AIUserIcons.h>
23 #import <AIUtilities/AIMutableOwnerArray.h>
24 #import <AIUtilities/AIImageAdditions.h>
25 #import <Adium/AIContactObserverManager.h>
26 #import <Adium/AIContactHidingController.h>
27 #import <Adium/AIStatus.h>
29 #define ObjectStatusCache @"Object Status Cache"
30 #define DisplayName @"Display Name"
31 #define LongDisplayName @"Long Display Name"
33 #define Group @"Group"
34 #define DisplayServiceID @"DisplayServiceID"
35 #define FormattedUID @"FormattedUID"
36 #define AlwaysVisible @"AlwaysVisible"
38 @interface AIListObject ()
39 - (void)setContainingGroup:(AIListGroup *)inGroup;
40 - (void)setupObservedValues;
41 - (void)updateOrderCache;
46 * @brief Base class for all contacts, groups, and accounts
48 @implementation AIListObject
53 * Designated initializer for AIListObject
55 - (id)initWithUID:(NSString *)inUID service:(AIService *)inService
57 if ((self = [super init])) {
58 m_groups = [[NSMutableSet alloc] initWithCapacity:1];
63 // Delay until the next run loop so bookmarks can instantiate their values first.
64 [self performSelector:@selector(setupObservedValues) withObject:nil afterDelay:0.0];
75 [UID release]; UID = nil;
76 [internalObjectID release]; internalObjectID = nil;
77 [m_groups release]; m_groups = nil;
82 - (void)setupObservedValues
84 [self setValue:[self preferenceForKey:@"Visible" group:PREF_GROUP_ALWAYS_VISIBLE]
85 forProperty:AlwaysVisible
89 //Identification -------------------------------------------------------------------------------------------------------
90 #pragma mark Identification
93 * @brief UID for this object
95 * The UID is the name of the object. If the object's name is not case sensitive, it is normalized. If the object's
96 * name should be compared ignoring spaces, it has no spaces. For an account, this is the account name. For a contact,
97 * this is the screen name, buddy name, etc.
102 * @brief Service of this object
107 * @brief Internal ID for this object
109 * An object ID generated by Adium that is shared by all objects which are, to most intents and purposes, identical to
110 * this object. Ths ID is composed of the service ID and UID, so any object with identical services and object IDs
111 * will have the same value here.
113 - (NSString *)internalObjectID
115 if (!internalObjectID) {
116 internalObjectID = [[AIListObject internalObjectIDForServiceID:self.service.serviceID UID:self.UID] retain];
118 return internalObjectID;
122 * @brief Generate an internal object ID
124 * @result The internalObjectID for an object with the specified serviceID and UID
126 + (NSString *)internalObjectIDForServiceID:(NSString *)inServiceID UID:(NSString *)inUID
128 return [NSString stringWithFormat:@"%@.%@",inServiceID, inUID];
131 //Visibility -----------------------------------------------------------------------------------------------------------
132 #pragma mark Visibility
135 * @brief Sets if list object should always be visible
137 - (void)setAlwaysVisible:(BOOL)inVisible
139 [self setPreference:[NSNumber numberWithBool:inVisible]
141 group:PREF_GROUP_ALWAYS_VISIBLE];
143 // This causes our container to update our visibility.
144 [self setValue:[NSNumber numberWithBool:inVisible]
145 forProperty:AlwaysVisible
150 * @brief Should this object ignore visibility settings?
152 * @returns If object should always be visible
154 - (BOOL)alwaysVisible
156 return [self boolValueForProperty:AlwaysVisible];
159 //Grouping / Ownership -------------------------------------------------------------------------------------------------
160 #pragma mark Grouping / Ownership
164 return [[m_groups copy] autorelease];
167 - (void) addContainingGroup:(AIListGroup *)inGroup
169 NSParameterAssert(inGroup && [inGroup canContainObject:self]);
170 if (![self.groups containsObject:inGroup]) {
173 [m_groups addObject:inGroup];
177 - (void) removeContainingGroup:(AIListGroup *)group
179 NSParameterAssert(group != nil && [m_groups containsObject:group]);
180 [m_groups removeObject:group];
183 - (NSSet *)containingObjects
188 - (void)removeFromGroup:(AIListObject <AIContainingObject> *)group
190 NSString *error = [NSString stringWithFormat:@"%@ needs an implementation of -removeFromGroup:", NSStringFromClass([self class])];
195 * @brief Set the local grouping for this object
197 * PRIVATE: This is only for use by AIListObjects conforming to the AIContainingObject protocol.
199 - (void)setContainingGroup:(AIListGroup *)inGroup
201 [m_groups removeAllObjects];
203 [self addContainingGroup:inGroup];
206 - (void) moveContainedObject:(AIListObject *)listObject toIndex:(NSInteger)index
208 AIListObject<AIContainingObject> *container = (AIListObject<AIContainingObject> *)self;
210 // We can't enforce this, since we're asked to set it for objects we don't yet *officially* contain.
211 //NSAssert([container.containedObjects containsObject:listObject], @"Asked to set an index for an object which doesn't exist.");
214 //Moved to the top of a group. New index is between 0 and the lowest current index
215 [container listObject:listObject didSetOrderIndex: self.smallestOrder / 2.0];
217 } else if (index >= container.visibleCount) {
218 //Moved to the bottom of a group. New index is one higher than the highest current index
219 [container listObject:listObject didSetOrderIndex: self.largestOrder + 1.0];
222 //Moved somewhere in the middle. New index is the average of the next largest and smallest index
223 AIListObject *previousObject = [container.visibleContainedObjects objectAtIndex:index-1];
224 AIListObject *nextObject = [container.visibleContainedObjects objectAtIndex:index];
225 CGFloat nextLowest = [container orderIndexForObject:previousObject];
226 CGFloat nextHighest = [container orderIndexForObject:nextObject];
228 /* XXX - Fixme as per below
229 * It's possible that nextLowest > nextHighest if ordering is not strictly based on the ordering indexes themselves.
230 * For example, a group sorted by status then manually could look like (status - ordering index):
234 * Offline Contact - 110
235 * Offline Contact - 113
236 * Offline Contact - 125
238 * Dropping between Away Contact and Offline Contact should make an Away Contact be > 120 but an Offline Contact be < 110.
239 * Only the sort controller knows the answer as to where this contact should be positioned in the end.
242 [container listObject: listObject didSetOrderIndex: (nextHighest + nextLowest) / 2.0];
246 //Properties ------------------------------------------------------------------------------------------------------
247 #pragma mark Properties
249 * @brief Called after properties have been modified; informs the contact controller.
251 * @param keys The properties
252 * @param silent YES indicates that this should not trigger 'noisy' notifications - it is appropriate for notifications as an account signs on and notes tons of contacts.
254 - (void)didModifyProperties:(NSSet *)keys silent:(BOOL)silent
256 [[AIContactObserverManager sharedManager] listObjectStatusChanged:self
257 modifiedStatusKeys:keys
261 * @brief Called after status changes have been modified and notifications posted
263 * When we notify of queued status changes, our containing group should notify as well so it can stay in sync with
264 * any changes it may have made in object:didChangeValueForProperty:notify:
266 * @param silent YES indicates that this should not trigger 'noisy' notifications - it is appropriate for notifications as an account signs on and notes tons of contacts.
268 - (void)didNotifyOfChangedPropertiesSilently:(BOOL)silent
270 //Let our containing objects know about the notification request
271 for (AIListContact<AIContainingObject> *container in self.containingObjects)
272 [container notifyOfChangedPropertiesSilently:silent];
276 * @brief Notification of changed properties
278 * Subclasses may wish to override these - they must be sure to call super's implementation, too!
280 - (void)object:(id)inObject didChangeValueForProperty:(NSString *)key notify:(NotifyTiming)notify
282 //Inform our containing groups about the new property value
283 for (AIListContact<AIContainingObject> *container in self.containingObjects)
284 [container object:self didChangeValueForProperty:key notify:notify];
286 [super object:inObject didChangeValueForProperty:key notify:notify];
289 //AIMutableOwnerArray delegate ------------------------------------------------------------------------------------------
290 #pragma mark AIMutableOwnerArray delegate
293 * @brief One of our mutable owners set an object
295 * A mutable owner array (one of our displayArrays) set an object
297 - (void)mutableOwnerArray:(AIMutableOwnerArray *)inArray didSetObject:(id)anObject withOwner:(id)inOwner priorityLevel:(float)priority
299 for (AIListContact<AIContainingObject> *container in self.containingObjects)
300 [container listObject:self mutableOwnerArray:inArray didSetObject:anObject withOwner:inOwner priorityLevel:priority];
304 * @brief Another object changed one of our mutable owner arrays
306 * Empty implementation by default - we do not need to take any action when a mutable owner array changes
308 - (void)listObject:(AIListObject *)listObject mutableOwnerArray:(AIMutableOwnerArray *)inArray didSetObject:(id)anObject withOwner:(AIListObject *)inOwner priorityLevel:(float)priority
313 //Object specific preferences ------------------------------------------------------------------------------------------
314 #pragma mark Object specific preferences
316 * @brief Set a preference value
318 - (void)setPreference:(id)value forKey:(NSString *)key group:(NSString *)group
320 [adium.preferenceController setPreference:value forKey:key group:group object:self];
322 - (void)setPreferences:(NSDictionary *)prefs inGroup:(NSString *)group
324 [adium.preferenceController setPreferences:prefs inGroup:group object:self];
327 - (void)setFormattedUID:(NSString *)inFormattedUID notify:(NotifyTiming)notify
329 [self setValue:inFormattedUID
330 forProperty:FormattedUID
335 * @brief Retrieve a preference value
337 - (id)preferenceForKey:(NSString *)key group:(NSString *)group
339 return [adium.preferenceController preferenceForKey:key group:group objectIgnoringInheritance:self];
343 * @brief Path for storing our reference file
345 - (NSString *)pathToPreferences
347 return OBJECT_PREFS_PATH;
350 //Display Name -------------------------------------------------------------------------------------
351 #pragma mark Display Name
353 * A list object basically has 4 different variations of display.
355 * - UID, the base UID of the contact "aiser123"
356 * - formattedUID, formating or alteration of the UID provided by the account code "AIser 123"
357 * - DisplayName, short formatted name provided by plugins "Adam Iser"
358 * - LongDisplayName, long formatted name provided by plugins "Adam Iser (AIser 123)"
360 * A value will always be returned by these methods, so if there is no long display name present it will fall back to
361 * display name, formattedUID, and finally UID (which is guaranteed to be present). Use whichever one seems best
362 * suited for what is being displayed.
366 * @brief Server-formatted UID
368 * @result NSString of the server-formatted UID if present; otherwise the same as the UID
370 - (NSString *)formattedUID
372 NSString *outName = [self valueForProperty:FormattedUID];
373 return outName ? outName : UID;
377 * @brief Long display name
379 * Though in many cases the same as the display name, a long display name allows additional information about the object
380 * to be displayed. One preference, for example, sets a long display names formatted as "Alias (Username)".
382 - (NSString *)longDisplayName
384 NSString *outName = [self displayArrayObjectForKey:LongDisplayName];
386 return outName ? outName : self.displayName;
390 * @brief Display name
392 * Display name, drawing first from any externally-provided display name, then falling back to
395 - (NSString *)displayName
397 NSString *displayName = [self displayArrayObjectForKey:DisplayName];
398 return displayName ? displayName : self.formattedUID;
402 * @brief The way this object's name should be spoken
404 * If not found, the display name is returned.
406 - (NSString *)phoneticName
408 NSString *phoneticName = [self displayArrayObjectForKey:@"Phonetic Name"];
409 return phoneticName ? phoneticName : self.displayName;
413 - (void)setDisplayName:(NSString *)alias
415 if ([alias length] == 0) alias = nil;
417 NSString *oldAlias = [self preferenceForKey:@"Alias" group:PREF_GROUP_ALIASES];
419 if ((!alias && oldAlias) ||
420 (alias && !([alias isEqualToString:oldAlias]))) {
422 AILogWithSignature(@"%@: %@", self, alias);
423 [self setPreference:alias forKey:@"Alias" group:PREF_GROUP_ALIASES];
425 //XXX - There must be a cleaner way to do this alias stuff! This works for now :)
426 [[NSNotificationCenter defaultCenter] postNotificationName:Contact_ApplyDisplayName
428 userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
433 #pragma mark Key-Value Pairing
434 - (NSImage *)userIcon
436 return [self internalUserIcon];
438 - (NSImage *)internalUserIcon
440 return [AIUserIcons userIconForObject:self];
443 - (NSData *)userIconData
445 NSImage *userIcon = [self userIcon];
446 return ([userIcon PNGRepresentation]);
448 - (void)setUserIconData:(NSData *)inData
450 [AIUserIcons setManuallySetUserIconData:inData forObject:self];
453 - (NSInteger)idleTime
455 return [self integerValueForProperty:@"Idle"];
458 //A standard listObject is never a stranger
467 notes = [self preferenceForKey:@"Notes" group:PREF_GROUP_NOTES];
468 if (!notes) notes = [self valueForProperty:@"Notes"];
472 - (void)setNotes:(NSString *)notes
474 if ([notes length] == 0) notes = nil;
476 NSString *oldNotes = [self preferenceForKey:@"Notes" group:PREF_GROUP_NOTES];
477 if ((!notes && oldNotes) ||
478 (notes && (![notes isEqualToString:oldNotes]))) {
480 [self setPreference:notes forKey:@"Notes" group:PREF_GROUP_NOTES];
484 #pragma mark Status states
487 * @brief The name for the specific status of this object
489 * The statusName provides further detail after the statusType. It may be a string such as @"Busy" or @"BRB".
490 * Possible values are determined by installed services; many default possibilities are listed in AIStatusController.h.
492 * The statusName may be nil if no additional status information is available for the contact. For example, an AIM
493 * contact will never have a statusName value, as the possibilities enumerated by AIStatusType -- and therefore returned
494 * by -AIListObject.statusType -- cover all possibilities. An ICQ contact, on the other hand, might have a statusType
495 * of AIAwayStatusType and then a statusName of @"Not Available" or @"DND".
497 * @result The statusName, or nil none exists
499 - (NSString *)statusName
501 return [self valueForProperty:@"StatusName"];
505 * @brief The general type of this object's status
507 * @result The AIStatusType for this object, indicating if it is available, away, invisible, offline, etc.
509 - (AIStatusType)statusType
512 NSNumber *statusTypeNumber = [self valueForProperty:@"StatusType"];
513 if (statusTypeNumber)
514 return [statusTypeNumber intValue];
515 return AIAvailableStatusType;
517 return AIOfflineStatusType;
521 * @brief Store the status name and type for this object
523 * This is used by account code to let the object know its name and status type
524 * @param statusName The statusName, which further specifies the statusType, or nil if none is available
525 * @param statusType The AIStatusType describing this object's status
526 * @param notify The NotifyTiming for this operation
528 - (void)setStatusWithName:(NSString *)statusName statusType:(AIStatusType)statusType notify:(NotifyTiming)notify
530 AIStatusType currentStatusType = self.statusType;
531 NSString *oldStatusName = self.statusName;
533 if (currentStatusType != statusType) {
534 [self setValue:[NSNumber numberWithInt:statusType] forProperty:@"StatusType" notify:NotifyLater];
537 if ((!statusName && oldStatusName) || (statusName && ![statusName isEqualToString:oldStatusName])) {
538 [self setValue:statusName forProperty:@"StatusName" notify:NotifyLater];
541 if (notify) [self notifyOfChangedPropertiesSilently:NO];
545 * @brief Return the status message for this object
547 * The statusMessage may supplement the statusType and statusName with a message describing the object's status; in AIM,
548 * for example, both available and away statuses can have an associated, user-set message.
550 * @result The NSAttributedString statusMessagae, or nil if none is set
552 - (NSAttributedString *)statusMessage
554 return [self valueForProperty:@"StatusMessage"];
558 * @brief Return the status message for this object as NSString
560 * The statusMessageString may supplement the statusType and statusName with a message describing the object's status; in AIM,
561 * for example, both available and away statuses can have an associated, user-set message.
563 * @result The NSString statusMessage, or nil if none is set
565 - (NSString *)statusMessageString;
567 return [[self valueForProperty:@"StatusMessage"] string];
571 * @brief Is this object connected via a mobile device?
573 * The default implementation simply returns NO. Only an AIListContact can be mobile... but a base implementation here
574 * makes code elsewhere much simpler.
582 * @brief Is this contact blocked?
584 * @result A boolean indicating if the object is blocked
592 * @brief Set the current status message
594 * @param statusMessage Status message. May be nil.
595 * @param notify How to notify of the change. See -[ESObjectWithProperties setValue:forProperty:notify:].
597 - (void)setStatusMessage:(NSAttributedString *)statusMessage notify:(NotifyTiming)notify
599 if (!statusMessage ||
600 ![[self valueForProperty:@"StatusMessage"] isEqualToAttributedString:statusMessage]) {
601 [self setValue:statusMessage forProperty:@"StatusMessage" notify:notify];
605 - (void)setBaseAvailableStatusAndNotify:(NotifyTiming)notify
607 [self setStatusWithName:nil
608 statusType:AIAvailableStatusType
610 [self setStatusMessage:nil
613 if (notify) [self notifyOfChangedPropertiesSilently:NO];
618 return [self boolValueForProperty:@"Online"];
621 - (AIStatusSummary)statusSummary
624 if (self.statusType == AIAwayStatusType || self.statusType == AIInvisibleStatusType)
625 return [self boolValueForProperty:@"IsIdle"] ? AIAwayAndIdleStatus : AIAwayStatus;
627 if ([self boolValueForProperty:@"IsIdle"])
630 return AIAvailableStatus;
633 //We don't know the status of an stranger who isn't showing up as online
634 return self.isStranger ? AIUnknownStatus : AIOfflineStatus;
637 - (void)notifyOfChangedPropertiesSilently:(BOOL)silent
639 [super notifyOfChangedPropertiesSilently:silent];
643 * @brief Are sounds for this object muted?
645 - (BOOL)soundsAreMuted
650 #pragma mark Methods for AIContainingObject-compliant classes to inherit
651 - (void)listObject:(AIListObject *)listObject didSetOrderIndex:(float)orderIndexForObject
653 NSDictionary *dict = [self preferenceForKey:@"OrderIndexDictionary"
654 group:ObjectStatusCache];
655 NSMutableDictionary *newDict = (dict ? [[dict mutableCopy] autorelease] : [NSMutableDictionary dictionary]);
657 // Sanity check - are we trying to assign infinity?
658 if (orderIndexForObject == INFINITY) {
659 AILogWithSignature(@"Correcting for INFINITY index, inObj=%@ allObj=%@", listObject, [newDict allKeysForObject:[NSNumber numberWithFloat:INFINITY]]);
661 // Remove any objects that currently are currently set to INFINITY, they'll regenerate their position to the last place.
662 for (NSString *key in [newDict allKeysForObject:[NSNumber numberWithFloat:INFINITY]]) {
663 [newDict removeObjectForKey:key];
666 // Update the preference.
667 [self setPreference:newDict
668 forKey:@"OrderIndexDictionary"
669 group:ObjectStatusCache];
671 // Update our largest cache.
672 [self updateOrderCache];
674 // Assume an index of largest+1
675 orderIndexForObject = self.largestOrder + 1;
678 NSNumber *orderIndexForObjectNumber = [NSNumber numberWithFloat:orderIndexForObject];
680 //Prevent setting an order index which we already have
681 NSArray *existingKeys = [dict allKeysForObject:orderIndexForObjectNumber];
682 while (existingKeys.count && ![existingKeys isEqualToArray:[NSArray arrayWithObject:listObject.internalObjectID]]) {
683 if (existingKeys.count == 1) {
684 AILogWithSignature(@"*** Warning: %@ had order index %f, but %@ already had an object with that order index. Setting to %f instead. Incrementing.",
685 listObject, orderIndexForObject, self, orderIndexForObject+1);
687 orderIndexForObject++;
688 orderIndexForObjectNumber = [NSNumber numberWithFloat:orderIndexForObject];
689 existingKeys = [dict allKeysForObject:orderIndexForObjectNumber];
692 /* How could this happen? -evands */
693 AILogWithSignature(@"More than one object has %f! We'll grant it to %@", orderIndexForObject, listObject);
695 for (NSString *key in [existingKeys objectEnumerator]) {
696 [newDict removeObjectForKey:key];
703 [newDict setObject:orderIndexForObjectNumber
704 forKey:listObject.internalObjectID];
706 [self setPreference:newDict
707 forKey:@"OrderIndexDictionary"
708 group:ObjectStatusCache];
710 [self updateOrderCache];
714 - (float)orderIndexForObject:(AIListObject *)listObject
716 NSDictionary *dict = [self preferenceForKey:@"OrderIndexDictionary"
717 group:ObjectStatusCache
719 NSNumber *orderIndexForObjectNumber = [dict objectForKey:listObject.internalObjectID];
720 float orderIndexForObject = (orderIndexForObjectNumber ? [orderIndexForObjectNumber floatValue] : 0);
722 //Evan: I don't know how we got up to infinity.. perhaps pref corruption in a previous version?
723 //In any case, check against it; if we stored it, reset to a reasonable number.
724 //XXX is this still needed?
725 if (!(orderIndexForObject < INFINITY)) orderIndexForObject = 0;
727 if (!orderIndexForObject) {
728 orderIndexForObject = self.largestOrder + 1;
729 [(AIListObject<AIContainingObject> *)self listObject:listObject didSetOrderIndex: orderIndexForObject];
732 return orderIndexForObject;
735 - (CGFloat)smallestOrder
737 if (!cachedSmallestOrder) {
738 [self updateOrderCache];
741 return cachedSmallestOrder;
744 - (CGFloat)largestOrder
746 if (!cachedLargestOrder) {
747 [self updateOrderCache];
750 return cachedLargestOrder;
753 - (void)updateOrderCache
755 CGFloat smallest = INFINITY, largest = 0;
757 NSDictionary *orderIndex = [self preferenceForKey:@"OrderIndexDictionary" group:ObjectStatusCache];
759 for (NSNumber *index in orderIndex.allValues) {
760 smallest = MIN(smallest, index.floatValue);
761 largest = MAX(largest, index.floatValue);
764 cachedSmallestOrder = (smallest == INFINITY ? 1 : smallest);
765 cachedLargestOrder = largest;
768 #pragma mark Comparison
770 - (BOOL)isEqual:(id)anObject
772 return ([anObject isMemberOfClass:[self class]] &&
773 [[(AIListObject *)anObject internalObjectID] isEqualToString:self.internalObjectID]);
777 - (NSComparisonResult)compare:(AIListObject *)other {
778 NSParameterAssert([other isKindOfClass:[AIListObject class]]);
779 return [self.internalObjectID caseInsensitiveCompare:other.internalObjectID];
783 - (NSImage *)menuIcon
785 return [AIUserIcons menuUserIconForObject:self];
788 - (NSImage *)statusIcon
790 NSImage *statusIcon = [self valueForProperty:@"List State Icon"];
791 if (!statusIcon) statusIcon = [self valueForProperty:@"List Status Icon"];
792 if (!statusIcon) statusIcon = [AIStatusIcons statusIconForUnknownStatusWithIconType:AIStatusIconList
793 direction:AIIconNormal];
797 #pragma mark Debugging
798 - (NSString *)description
800 return [NSString stringWithFormat:@"<%@:%x %@>",NSStringFromClass([self class]), self, self.internalObjectID];
803 #pragma mark Applescript
804 - (int)scriptingStatusType
806 AIStatusType statusType = self.statusType;
807 switch (statusType) {
808 case AIAvailableStatusType:
809 return AIAvailableStatusTypeAS;
810 case AIOfflineStatusType:
811 return AIOfflineStatusTypeAS;
812 case AIAwayStatusType:
813 return AIAwayStatusTypeAS;
814 case AIInvisibleStatusType:
815 return AIInvisibleStatusTypeAS;
821 * @brief Returns the current status message as rich text
823 - (NSTextStorage *)scriptingStatusMessage
825 return [[[NSTextStorage alloc] initWithAttributedString:self.statusMessage] autorelease];
831 * Trivial plugin compatibility; these methods were removed from the public API
832 * but have trivial new-API implementations
834 @implementation AIListObject (PluginCompatibility)
835 - (NSString *)serviceID
837 return self.service.serviceID;