Added `-[NSArray validateAsPropertyList]` and `-[NSDictionary validateAsPropertyList...
[adiumx.git] / Source / ESContactInfoListController.m
blob8452405892fe2d35683d2125b2b18429fc78d272
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 <Adium/AIContactControllerProtocol.h>
18 #import "AIContactInfoWindowController.h"
19 #import "ESContactInfoListController.h"
20 #import <Adium/AIListGroup.h>
21 #import <Adium/AIListObject.h>
22 #import "AIListOutlineView.h"
23 #import <Adium/AIMetaContact.h>
25 @implementation ESContactInfoListController
27 //The superclass's implementation does not expand metaContacts
28 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
30     if (item == nil) {
31                 if (hideRoot) {
32                         if ([contactList isKindOfClass:[AIMetaContact class]]) {
33                                 return ((index >= 0) && (index < [(AIMetaContact *)contactList uniqueContainedObjectsCount]) ?
34                                                 [(AIMetaContact *)contactList uniqueObjectAtIndex:index] : 
35                                                 nil);
36                         } else {
37                                 return ((index >= 0) && (index < [(AIListGroup *)contactList containedObjectsCount]) ?
38                                                 [contactList objectAtIndex:index] :
39                                                 nil);
40                         }
41                 } else {
42                         return contactList;
43                 }
44     } else {
45                 if ([item isKindOfClass:[AIMetaContact class]]) {
46                         return ((index >= 0) && (index < [(AIMetaContact *)item uniqueContainedObjectsCount]) ? 
47                                         [(AIMetaContact *)item uniqueObjectAtIndex:index] : 
48                                         nil);
49                 } else {
50                         return ((index >= 0) && (index < [(AIListGroup *)item containedObjectsCount]) ?
51                                         [item objectAtIndex:index] :
52                                         nil);
53                 }
54     }
57 //The superclass's implementation does not expand metaContacts
58 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
60     if (item == nil) {
61                 if (hideRoot) {
62                         if ([contactList isKindOfClass:[AIMetaContact class]]) {
63                                 return [(AIMetaContact *)contactList uniqueContainedObjectsCount];
64                         } else {
65                                 return [(AIListGroup *)contactList containedObjectsCount];
66                         }
67                 } else {
68                         return 1;
69                 }
70     } else {
71                 if ([item isKindOfClass:[AIMetaContact class]]) {
72                         return [(AIMetaContact *)item uniqueContainedObjectsCount];
73                 } else {
74                         return [(AIListGroup *)item containedObjectsCount];
75                 }
76     }
79 //The superclass's implementation does not expand metaContacts
80 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
82     if ([item isKindOfClass:[AIMetaContact class]] || [item isKindOfClass:[AIListGroup class]]) {
83         return YES;
84     } else {
85         return NO;
86     }
89 /*!
90  * @brief Change the info window as the selection changes
91  *
92  * We want to configure for contact-specific information when a contact is selected in the drawer
93  * If no row is selected, we configure for the contactList root (the metaContact itself)
94  */
95 - (void)outlineViewSelectionDidChange:(NSNotification *)aNotification
97         if (!aNotification || [aNotification object] == contactListView) {
98                 int selectedRow = [contactListView selectedRow];
100                 [delegate contactInfoListControllerSelectionDidChangeToListObject:((selectedRow != -1) ?
101                                                                                                                                                    [contactListView itemAtRow:selectedRow] :
102                                                                                                                                                    contactList)];
103         }
107  * @brief Remove the selected rows from the metaContact
108  */
109 - (void)outlineViewDeleteSelectedRows:(NSOutlineView *)outlineView
111         [(AIContactInfoWindowController *)delegate removeContact:outlineView];
115  * @brief Validate a drag and drop operation
116  */
117 - (NSDragOperation)outlineView:(NSOutlineView*)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
119         NSPasteboard    *draggingPasteboard = [info draggingPasteboard];
120     NSString            *avaliableType = [draggingPasteboard availableTypeFromArray:[NSArray arrayWithObject:@"AIListObject"]];
121         
122         //No dropping into contacts
123     if ([avaliableType isEqualToString:@"AIListObject"]) {
124                 
125                 id      primaryDragItem = nil;
126                 
127                 //If we don't have drag items, we are dragging from another instance; build our own dragItems array
128                 //using the supplied internalObjectIDs
129                 if (!dragItems) {
130                         if ([[draggingPasteboard availableTypeFromArray:[NSArray arrayWithObject:@"AIListObjectUniqueIDs"]] isEqualToString:@"AIListObjectUniqueIDs"]) {
131                                 NSArray                 *dragItemsUniqueIDs;
132                                 NSMutableArray  *arrayOfDragItems;
133                                 NSString                *uniqueID;
134                                 NSEnumerator    *enumerator;
135                                         
136                                 dragItemsUniqueIDs = [draggingPasteboard propertyListForType:@"AIListObjectUniqueIDs"];
137                                 arrayOfDragItems = [NSMutableArray array];
138                                 
139                                 enumerator = [dragItemsUniqueIDs objectEnumerator];
140                                 while ((uniqueID = [enumerator nextObject])) {
141                                         [arrayOfDragItems addObject:[[adium contactController] existingListObjectWithUniqueID:uniqueID]];
142                                 }
144                                 //We will release this when the drag is completed
145                                 dragItems = [arrayOfDragItems retain];
146                         }
147                 }
149                 primaryDragItem = [dragItems objectAtIndex:0];                  
151                 if ([primaryDragItem isKindOfClass:[AIListGroup class]]) {
152                         //Disallow dragging groups into the contact info window
153                         return NSDragOperationNone;
154                         
155                 } else {
156                         //Disallow dragging contacts into anything besides the contact list
157                         if (index == NSOutlineViewDropOnItemIndex) {
159                                 //The contactList is 'nil' to the outlineView
160                                 [outlineView setDropItem:nil dropChildIndex:[outlineView rowForItem:item]];
161                         }
162                 }
163         }
164         
165         return NSDragOperationPrivate;
169 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
171         NSPasteboard    *draggingPasteboard = [info draggingPasteboard];
172     NSString    *availableType = [draggingPasteboard availableTypeFromArray:[NSArray arrayWithObject:@"AIListObject"]];
174         //No longer in a drag, so allow tooltips again
175     if ([availableType isEqualToString:@"AIListObject"]) {
176                 
177                 //If we don't have drag items, we are dragging from another instance; build our own dragItems array
178                 //using the supplied internalObjectIDs
179                 if (!dragItems) {
180                         if ([[draggingPasteboard availableTypeFromArray:[NSArray arrayWithObject:@"AIListObjectUniqueIDs"]] isEqualToString:@"AIListObjectUniqueIDs"]) {
181                                 NSArray                 *dragItemsUniqueIDs;
182                                 NSMutableArray  *arrayOfDragItems;
183                                 NSString                *uniqueID;
184                                 NSEnumerator    *enumerator;
185                                 
186                                 dragItemsUniqueIDs = [draggingPasteboard propertyListForType:@"AIListObjectUniqueIDs"];
187                                 arrayOfDragItems = [NSMutableArray array];
188                                 
189                                 enumerator = [dragItemsUniqueIDs objectEnumerator];
190                                 while ((uniqueID = [enumerator nextObject])) {
191                                         [arrayOfDragItems addObject:[[adium contactController] existingListObjectWithUniqueID:uniqueID]];
192                                 }
193                                 
194                                 //We will release this when the drag is completed
195                                 dragItems = [arrayOfDragItems retain];
196                         }
197                 }
199                 //The tree root is not associated with our root contact list group, so we need to make that association here
200                 if (item == nil) item = contactList;
201                 
202                 //Move the list object to its new location
203                 if ([item isKindOfClass:[AIMetaContact class]]) {
204                         
205                         NSMutableArray  *realDragItems = [NSMutableArray array];
206                         NSEnumerator    *enumerator;
207                         AIListObject    *aDragItem;
208                         
209                         if (index == [outlineView numberOfRows]) {
210                                 index = [item containedObjectsCount];
211                         } else {
212                                 //The outline view has one unique contact for each service/UID combination, while the metacontact
213                                 //has a containedObjectsArray with multiple contacts for each.  We want to find which item is at
214                                 //our drop row, then determine the index in the metacontact of that item.  That's the index we move to.
215                                 index = [(AIMetaContact *)item indexOfObject:[outlineView itemAtRow:index]];
216                         }
217                         
218                         enumerator = [dragItems objectEnumerator];
219                         while ((aDragItem = [enumerator nextObject])) {
220                                 if ([aDragItem isMemberOfClass:[AIListContact class]]) {
221                                         //For listContacts, add all contacts with the same service and UID (on all accounts)
222                                         [realDragItems addObjectsFromArray:[[[adium contactController] allContactsWithService:[aDragItem service] 
223                                                                                                                                                                                                           UID:[aDragItem UID]
224                                                                                                                                                                                          existingOnly:YES] allObjects]];
225                                 } else {
226                                         [realDragItems addObject:aDragItem];
227                                 }
228                         }
229                         
230                         [[adium contactController] moveListObjects:realDragItems intoObject:item index:index];
231                         [outlineView reloadData];
232                 }
233         }
235         //Call super and return its value
236     return [super outlineView:outlineView acceptDrop:info item:item childIndex:index];
239 //Due to a bug in NSDrawer, convertPoint:fromView reports a point too low by the trailingOffset 
240 //when our contact list is in a drawer.
241 - (AIListObject *)contactListItemAtScreenPoint:(NSPoint)screenPoint
243         NSPoint                 viewPoint = [contactListView convertPoint:[[contactListView window] convertScreenToBase:screenPoint] fromView:[[contactListView window] contentView]];
244         
245         viewPoint.y += [(AIContactInfoWindowController *)delegate drawerTrailingOffset];
246         
247         AIListObject    *hoveredObject = [contactListView itemAtRow:[contactListView rowAtPoint:viewPoint]];
248         
249         return hoveredObject;
252 //We want to just show UIDs whereever possible
253 - (BOOL)useAliasesInContactListAsRequested
255         return NO;
258 //We don't want to change text colors based on the user's status or state
259 - (BOOL)shouldUseContactTextColors{
260         return NO;
262         
263 @end