Bug Summary

File:Source/AIAdvancedInspectorPane.m
Location:line 368, column 4
Description:Memory Leak
Code is compiled without garbage collection.

Annotated Source Code

1//
2// AIAdvancedInspectorPane.m
3// Adium
4//
5// Created by Elliott Harris on 1/17/08.
6// Copyright 2008 The Adium Team. All rights reserved.
7//
8
9#import "AIAdvancedInspectorPane.h"
10#import <Adium/AIAccountMenu.h>
11#import <AIUtilities/AIParagraphStyleAdditions.h>
12
13#define ADVANCED_NIB_NAME (@"AIAdvancedInspectorPane")
14
15@interface AIAdvancedInspectorPane(PRIVATE)
16- (void)updateGroupList;
17-(void)reloadPopup;
18@end
19
20@interface NSMenuItem (NSMenItem_AdvancedInspectorPane)
21- (void)setAttributes:(NSDictionary *)attributes;
22@end
23
24@implementation AIAdvancedInspectorPane
25
26- (id) init
27{
28 self = [super init];
29 if (self != nil0) {
30 [NSBundle loadNibNamed:[self nibName] owner:self];
31
32 //Load Encryption menus
33 [popUp_encryption setMenu:[[adium contentController] encryptionMenuNotifyingTarget:self withDefault:YES( BOOL ) 1]];
34 [[popUp_encryption menu] setAutoenablesItems:NO( BOOL ) 0];
35
36 //Configure Table view
37 [accountsTableView setUsesAlternatingRowBackgroundColors:YES( BOOL ) 1];
38 [accountsTableView setAcceptsFirstMouse:YES( BOOL ) 1];
39
40 //[[[accountsTableView tableColumnWithIdentifier:@"account"] headerCell] setTitle:AILocalizedString(@"Account",nil)];
41 [[[accountsTableView tableColumnWithIdentifier:@"contact"] headerCell] setTitle:AILocalizedString[ [ NSBundle bundleForClass : [ self class ] ] localizedStringForKey
: ( @ "Contact" ) value : @ "" table : ( 0 ) ]
(@"Contact","This header for the table in the Accounts tab of the Get Info window indicates the name of the contact within a metacontact")];
42 [[[accountsTableView tableColumnWithIdentifier:@"group"] headerCell] setTitle:AILocalizedString[ [ NSBundle bundleForClass : [ self class ] ] localizedStringForKey
: ( @ "Group" ) value : @ "" table : ( 0 ) ]
(@"Group",nil)];
43 contactsColumnIsInAccountsTableView = YES( BOOL ) 1; //It's in the table view in the nib.
44
45 //Observe contact list changes
46 [[adium notificationCenter] addObserver:self
47 selector:@selector(contactListChanged)
48 name:Contact_ListChanged@ "Contact_ListChanged"
49 object:nil0];
50 //Observe account changes
51 [[adium notificationCenter] addObserver:self
52 selector:@selector(accountListChanged)
53 name:Account_ListChanged@ "Account_ListChanged"
54 object:nil0];
55
56 [self updateGroupList];
57 accountMenu = [[AIAccountMenu accountMenuWithDelegate:self
58 submenuType:AIAccountNoSubmenu
59 showTitleVerbs:NO( BOOL ) 0] retain];
60
61 [accountsTableView sizeToFit];
62 }
63
64 return self;
65}
66
67- (void) dealloc
68{
69 [accountMenu release]; accountMenu = nil0;
70 [accounts release]; accounts = nil0;
71 [contacts release]; contacts = nil0;
72 [displayedObject release]; displayedObject = nil0;
73 [inspectorContentView release]; inspectorContentView = nil0;
74
75 [[adium notificationCenter] removeObserver:self];
76 [super dealloc];
77}
78
79
80-(NSString *)nibName
81{
82 return ADVANCED_NIB_NAME( @ "AIAdvancedInspectorPane" );
83}
84
85-(NSView *)inspectorContentView
86{
87 return inspectorContentView;
88}
89
90-(void)updateForListObject:(AIListObject *)inObject
91{
92 if (displayedObject != inObject) {
93 //Update the table view to have or not have the "Individual Contact" column, as appropriate.
94 //It should have the column when our list object is a metacontact.
95 if ([inObject isKindOfClass:[AIMetaContact class]]) {
96 if (!contactsColumnIsInAccountsTableView) {
97 //Add the column.
98 [accountsTableView addTableColumn:contactsColumn];
99 //It was added as last; move to the middle.
100 [accountsTableView moveColumn:1 toColumn:0];
101 //Set all of the table view's columns to be the same width.
102 float columnWidth = [accountsTableView frame].size.width / 2.0;
103 //NSLog(@"Setting columnWidth to: %f / 2.0 == %f", [accountsTableView frame].size.width, columnWidth);
104 [[accountsTableView tableColumns] setValue:[NSNumber numberWithFloat:columnWidth] forKey:@"width"];
105 [accountsTableView sizeToFit];
106 //We don't need it retained anymore.
107 [contactsColumn release];
108
109 contactsColumnIsInAccountsTableView = YES( BOOL ) 1;
110 }
111 } else if(contactsColumnIsInAccountsTableView) {
112 //Remove the column.
113 //Note that the column is in the table in the nib, so it is in the table view before we have been configured for the first time.
114 //And be sure to retain it before removing it from the view.
115 [contactsColumn retain];
116 [accountsTableView removeTableColumn:contactsColumn];
117 //Set both of the table view's columns to be the same width.
118 float columnWidth = [accountsTableView frame].size.width;
119 //NSLog(@"Setting columnWidth to: %f", [accountsTableView frame].size.width);
120 [[accountsTableView tableColumns] setValue:[NSNumber numberWithFloat:columnWidth] forKey:@"width"];
121 [accountsTableView sizeToFit];
122
123 contactsColumnIsInAccountsTableView = NO( BOOL ) 0;
124 }
125
126 [displayedObject release];
127 displayedObject = ([inObject isKindOfClass:[AIListContact class]] ?
128 [(AIListContact *)inObject parentContact] :
129 inObject);
130 [displayedObject retain];
131
132 //Rebuild the account list
133 [self reloadPopup];
134 }
135
136 NSNumber *encryption;
137
138 encryption = [inObject preferenceForKey:KEY_ENCRYPTED_CHAT_PREFERENCE@ "Encrypted Chat Preference" group:GROUP_ENCRYPTION@ "Encryption"];
139
140 if(!encryption) {
141 [popUp_encryption compatibleSelectItemWithTag:EncryptedChat_Default];
142 }
143
144 [popUp_encryption compatibleSelectItemWithTag:[encryption intValue]];
145
146 [checkBox_alwaysShow setEnabled:![inObject isKindOfClass:[AIListGroup class]]];
147 [checkBox_alwaysShow setState:[inObject alwaysVisible]];
148}
149
150- (IBActionvoid)selectedEncryptionPreference:(id)sender
151{
152 if(!displayedObject)
153 return;
154 [displayedObject setPreference:[NSNumber numberWithInt:[sender tag]]
155 forKey:KEY_ENCRYPTED_CHAT_PREFERENCE@ "Encrypted Chat Preference"
156 group:GROUP_ENCRYPTION@ "Encryption"];
157}
158
159- (IBActionvoid)setVisible:(id)sender
160{
161 if(!displayedObject)
162 return;
163
164 [displayedObject setAlwaysVisible:[checkBox_alwaysShow state]];
165}
166
167#pragma mark Accounts Table View methods
168
169/*!
170 * @brief Update our list of groups
171 */
172- (void)updateGroupList
173{
174 //Get the new groups
175 NSMenu *groupMenu = [[adium contactController] menuOfAllGroupsInGroup:nil0 withTarget:self];
176 NSMenuItem *notListedMenuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString[ [ NSBundle bundleForClass : [ self class ] ] localizedStringForKey
: ( @ "(Not Listed)" ) value : @ "" table : ( 0 ) ]
(@"(Not Listed)", nil)
177 target:self
178 action:@selector(selectGroup:)
179 keyEquivalent:@""
180 representedObject:nil0];
181 [groupMenu insertItem:notListedMenuItem atIndex:0];
182 [notListedMenuItem release];
183 [groupMenu insertItem:[NSMenuItem separatorItem] atIndex:1];
184
185 [[groupMenu itemArray] makeObjectsPerformSelector:@selector(setAttributes:)
186 withObject:[NSDictionary dictionaryWithObjectsAndKeys:
187 [NSFont menuFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]], NSFontAttributeName,
188 [NSParagraphStyle styleWithAlignment:NSLeftTextAlignment
189 lineBreakMode:NSLineBreakByTruncatingTail], NSParagraphStyleAttributeName,
190 nil0]];
191
192 [[[accountsTableView tableColumnWithIdentifier:@"group"] dataCell] setMenu:groupMenu];
193
194 //Refresh our table
195 [accountsTableView reloadData];
196}
197
198- (NSArray *)accountsForCurrentObject
199{
200 if ([displayedObject isKindOfClass:[AIMetaContact class]]) {
201 NSMutableSet *set = [NSMutableSet set];
202 NSEnumerator *enumerator = [[[(AIMetaContact *)displayedObject listContacts] valueForKey:@"service"] objectEnumerator];
203 AIService *service;
204 while ((service = [enumerator nextObject])) {
205 [set addObjectsFromArray:[[adium accountController] accountsCompatibleWithService:service]];
206 }
207
208 return [set allObjects];
209
210 } else if ([displayedObject isKindOfClass:[AIListContact class]]) {
211 return [[adium accountController] accountsCompatibleWithService:[displayedObject service]];
212
213 } else {
214 return nil0;
215 }
216}
217
218- (NSArray *)contactsForCurrentObjectCompatibleWithAccount:(AIAccount *)inAccount
219{
220 if ([displayedObject isKindOfClass:[AIMetaContact class]]) {
221 NSMutableArray *array = [NSMutableArray array];
222 NSEnumerator *enumerator = [[(AIMetaContact *)displayedObject listContacts] objectEnumerator];
223 AIListContact *contact;
224 while ((contact = [enumerator nextObject])) {
225 if ([[contact serviceClass] isEqualToString:[inAccount serviceClass]]) {
226 [array addObject:[[adium contactController] contactWithService:[contact service]
227 account:inAccount
228 UID:[contact UID]]];
229 }
230 }
231
232 return array;
233
234 } else if ([displayedObject isKindOfClass:[AIListContact class]]) {
235 return [NSArray arrayWithObject:displayedObject];
236
237 } else {
238 return nil0;
239 }
240}
241
242-(void)reloadPopup
243{
244 [accounts release]; accounts = nil0;
245 accounts = [[self accountsForCurrentObject] retain];
246
247 [accountMenu rebuildMenu];
248}
249
250- (void)accountMenu:(AIAccountMenu *)inAccountMenu didSelectAccount:(AIAccount *)inAccount
251{
252 [contacts release]; contacts = nil0;
253 if (inAccount)
254 contacts = [[self contactsForCurrentObjectCompatibleWithAccount:inAccount] retain];
255
256 //Refresh our table
257 [accountsTableView reloadData];
258}
259
260- (BOOL)accountMenu:(AIAccountMenu *)inAccountMenu shouldIncludeAccount:(AIAccount *)inAccount
261{
262 return [accounts containsObject:inAccount];
263}
264
265- (void)accountMenu:(AIAccountMenu *)inAccountMenu didRebuildMenuItems:(NSArray *)menuItems
266{
267 [popUp_accounts setMenu:[inAccountMenu menu]];
268
269 //Select an account and redisplay
270 [self accountMenu:inAccountMenu didSelectAccount:([popUp_accounts numberOfItems] ?
271 [[popUp_accounts selectedItem] representedObject] :
272 nil0)];
273}
274
275- (NSControlSize)controlSizeForAccountMenu:(AIAccountMenu *)inAccountMenu
276{
277 return NSSmallControlSize;
278}
279
280- (void)accountListChanged
281{
282 [self reloadPopup];
283}
284
285- (void)contactListChanged
286{
287 /* Prevent reentry, as Heisenberg knows out that observing contacts may change them. */
288 if (!rebuildingContacts) {
289 rebuildingContacts = YES( BOOL ) 1;
290 [self accountMenu:accountMenu didSelectAccount:([popUp_accounts numberOfItems] ?
291 [[popUp_accounts selectedItem] representedObject] :
292 nil0)];
293 rebuildingContacts = NO( BOOL ) 0;
294 }
295}
296
297#pragma mark Accounts Table View Data Sources
298
299/*!
300 * @brief Number of table view rows
301 */
302- (int)numberOfRowsInTableView:(NSTableView *)tableView
303{
304 return [contacts count];
305}
306
307/*!
308 * @brief Table view object value
309 */
310- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
311{
312 id result = @"";
313
314 NSString *identifier = [tableColumn identifier];
315
316 //if ([identifier isEqualToString:@"account"]) {
317// AIAccount *account = [accounts objectAtIndex:row];
318// NSString *accountFormattedUID = [account formattedUID];
319//
320// if ([account online]) {
321// result = accountFormattedUID;
322//
323// } else {
324// //Gray the names of offline accounts
325// NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSColor grayColor] forKey:NSForegroundColorAttributeName];
326// NSAttributedString *string = [[NSAttributedString alloc] initWithString:accountFormattedUID attributes:attributes];
327// result = [string autorelease];
328// }
329
330 /*} else*/ if ([identifier isEqualToString:@"contact"]) {
331 AIListObject *contact = [contacts objectAtIndex:row];
332 result = [contact formattedUID];
333 }
334
335 return result;
336}
337
338/*!
339 * @brief Table view will display a cell
340 */
341- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row
342{
343 NSString *identifier = [tableColumn identifier];
344 AIAccount *account;
345 AIListContact *exactContact;
346 BOOL accountOnline;
347
348 //account = [accounts objectAtIndex:row];
349 account = [[popUp_accounts selectedItem] representedObject];
350 accountOnline = [account online];
351
352 exactContact = [contacts objectAtIndex:row];
353
354 //Disable cells for offline accounts
355 [cell setEnabled:accountOnline];
356
357 //Select active group
[1] Taking true branch.
358 if ([identifier isEqualToString:@"group"]) {
[2] Taking false branch.
359 if (accountOnline) {
360 AIListGroup *group;
361
362 if ((group = [[adium contactController] remoteGroupForContact:exactContact])) {
363 [cell selectItemWithRepresentedObject:group];
364 } else {
365 [cell selectItemAtIndex:0];
366 }
367 } else {
[3] Method returns an object with a +1 retain count (owning reference).
[4] Object allocated on line 368 is no longer referenced after this point and has a retain count of +1 (object leaked).
368 [cell setAttributedTitle:[[NSAttributedString alloc] initWithString:AILocalizedString[ [ NSBundle bundleForClass : [ self class ] ] localizedStringForKey
: ( @ "(Unavailable)" ) value : @ "" table : ( 0 ) ]
(@"(Unavailable)",nil)
369 attributes:[NSDictionary dictionaryWithObjectsAndKeys:
370 [NSFont menuFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]], NSFontAttributeName,
371 [NSParagraphStyle styleWithAlignment:NSLeftTextAlignment
372 lineBreakMode:NSLineBreakByTruncatingTail], NSParagraphStyleAttributeName,
373 nil0]]];
374 }
375 }
376}
377
378/*!
379 * @brief Empty. This method is the target of our menus, and needed for menu validation.
380 */
381- (void)selectGroup:(id)sender {};
382
383/*!
384 * @brief Table view set object value
385 */
386- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row
387{
388 NSString *identifier = [tableColumn identifier];
389
390 if ([identifier isEqualToString:@"group"]) {
391 NSMenu *menu = [[tableColumn dataCell] menu];
392 int menuIndex = [object intValue];
393
394 if (menuIndex >= 0 && menuIndex < [menu numberOfItems]) {
395 AIListGroup *group = [[menu itemAtIndex:menuIndex] representedObject];
396 AIListContact *contactOnClickedRow = [contacts objectAtIndex:row];
397 AIListContact *exactContact;
398
399 //Retrieve an AIListContact on this account
400 exactContact = [[adium contactController] contactWithService:[contactOnClickedRow service]
401 account:[[popUp_accounts selectedItem] representedObject]
402 UID:[contactOnClickedRow UID]];
403
404 if (group) {
405 if (![[group UID] isEqualToString:[exactContact remoteGroupName]]) {
406 if ([exactContact remoteGroupName]) {
407 //Move contact
408 [[adium contactController] moveContact:exactContact intoObject:group];
409
410 } else {
411 [[adium contactController] addContacts:[NSArray arrayWithObject:exactContact]
412 toGroup:group];
413 }
414 }
415
416 } else {
417 //User selected not listed, so we'll remove that contact
418 [[adium contactController] removeListObjects:[NSArray arrayWithObject:exactContact]];
419 }
420 }
421 }
422}
423
424@end
425
426@implementation NSMenuItem (NSMenItem_AdvancedInspectorPane)
427- (void)setAttributes:(NSDictionary *)attributes
428{
429 [self setAttributedTitle:[[[NSAttributedString alloc] initWithString:[self title]
430 attributes:attributes] autorelease]];
431}
432@end