Added Deployment-Debug_PPC-Only build style to FriBidi. Fixes #9021
[adiumx.git] / Source / AINewContactWindowController.m
blob616387dfa5b70ea09405e9fb7915ed0d01e6f2a5
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/AIAccountControllerProtocol.h>
18 #import <Adium/AIContactControllerProtocol.h>
19 #import <Adium/AIInterfaceControllerProtocol.h>
20 #import "AINewContactWindowController.h"
21 #import "AINewGroupWindowController.h"
22 #import "OWABSearchWindowController.h"
23 #import "ESAddressBookIntegrationPlugin.h"
24 #import <AIUtilities/AIMenuAdditions.h>
25 #import <AIUtilities/AIPopUpButtonAdditions.h>
26 #import <AIUtilities/AIStringAdditions.h>
27 #import <Adium/AIAccount.h>
28 #import <Adium/AIListContact.h>
29 #import <Adium/AIListGroup.h>
30 #import <Adium/AILocalizationTextField.h>
31 #import <Adium/AIService.h>
32 #import <Adium/AIServiceIcons.h>
33 #import <Adium/AIServiceMenu.h>
34 #import <AddressBook/ABPerson.h>
36 #define ADD_CONTACT_PROMPT_NIB  @"AddContact"
37 #define DEFAULT_GROUP_NAME              AILocalizedString(@"Contacts",nil)
39 @interface AINewContactWindowController (PRIVATE)
40 - (id)initWithWindowNibName:(NSString *)windowNibName contactName:(NSString *)inName service:(AIService *)inService  account:(AIAccount *)inAccount;
41 - (void)buildGroupMenu;
42 - (void)buildContactTypeMenu;
43 - (void)configureForCurrentServiceType;
44 - (void)ensureValidContactTypeSelection;
45 - (void)updateAccountList;
46 - (void)_setServiceType:(AIService *)inService;
47 - (void)selectServiceType:(id)sender;
48 @end
50 /*!
51  * @class AINewContactWindowController
52  * @brief Window controller for adding a new contact
53  */
54 @implementation AINewContactWindowController
56 /*!
57  * @brief Prompt for adding a new contact.
58  *
59  * @param parentWindow Window on which to show the prompt as a sheet. Pass nil for a panel prompt. 
60  * @param inName Initial value for the contact name field
61  * @param inService <tt>AIService</tt> for determining the initial service type selection
62  * @param inAccount If non-nil, the one AIAccount which should be initially enabled for adding this contact
63  */
64 + (void)promptForNewContactOnWindow:(NSWindow *)parentWindow name:(NSString *)inName service:(AIService *)inService account:(AIAccount *)inAccount
66         AINewContactWindowController    *newContactWindow;
67         
68         newContactWindow = [[self alloc] initWithWindowNibName:ADD_CONTACT_PROMPT_NIB contactName:inName service:inService account:inAccount];
69         
70         if (parentWindow) {
71                 [parentWindow makeKeyAndOrderFront:nil];
72                 
73                 [NSApp beginSheet:[newContactWindow window]
74                    modalForWindow:parentWindow
75                         modalDelegate:newContactWindow
76                    didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:)
77                           contextInfo:nil];
78         } else {
79                 [newContactWindow showWindow:nil];
80                 [[newContactWindow window] makeKeyAndOrderFront:nil];
81         }
82         
85 /*!
86  * @brief Initialize
87  */
88 - (id)initWithWindowNibName:(NSString *)windowNibName contactName:(NSString *)inName service:(AIService *)inService account:(AIAccount *)inAccount
90     self = [super initWithWindowNibName:windowNibName];
92         service = [inService retain];
93         initialAccount = [inAccount retain];
94         contactName = [inName retain];
95         uniqueID = nil;
96         
97         return self;
101  * @brief Deallocate
102  */
103 - (void)dealloc
105         [accounts release];
106         [contactName release];
107         [service release];
108         [initialAccount release];
109         [uniqueID release];
110         [checkedAccounts release];
112         [[NSNotificationCenter defaultCenter] removeObserver:self];
114     [super dealloc];
118  * @brief Setup the window before it is displayed
119  */
120 - (void)windowDidLoad
122         [[self window] center];
124         //Localized Strings
125         [[self window] setTitle:AILocalizedString(@"Add Contact",nil)];
126         [textField_type setLocalizedString:AILocalizedString(@"Contact Type:","Contact type service dropdown label in Add Contact")];
127         [textField_alias setLocalizedString:AILocalizedString(@"Alias:",nil)];
128         [textField_inGroup setLocalizedString:AILocalizedString(@"In Group:",nil)];
129         [textField_addToAccounts setLocalizedString:AILocalizedString(@"On Accounts:",nil)];
130         
131         [textField_searchInAB setAlwaysMoveRightAnchoredWindow:YES];
132         [textField_searchInAB setLocalizedString:AILocalizedString(@"Search In Address Book",nil)];
134         [button_add setLocalizedString:AILocalizedString(@"Add",nil)];
135         [button_cancel setLocalizedString:AILocalizedString(@"Cancel",nil)];
137         //Configure the rest of the window
138         [self buildGroupMenu];
139         [self buildContactTypeMenu];
140         [self configureForCurrentServiceType];
141         if (contactName) [textField_contactName setStringValue:contactName];    
142         
143         //Observe account list and status changes
144         [[adium notificationCenter] addObserver:self
145                                                                    selector:@selector(accountListChanged:)
146                                                                            name:Account_ListChanged
147                                                                          object:nil];
148         [[adium contactController] registerListObjectObserver:self];
152  * @brief Window is closing
153  */
154 - (void)windowWillClose:(id)sender
156         [super windowWillClose:sender];
157         [[adium contactController] unregisterListObjectObserver:self];
158         [[adium notificationCenter] removeObserver:self];
162  * @brief Called as the user list edit sheet closes, dismisses the sheet
163  */
164 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
166     [sheet orderOut:nil];
170  * @brief Cancel
171  */
172 - (IBAction)cancel:(id)sender
174         [self closeWindow:nil];
178  * @brief Perform the addition of the contact
179  */
180 - (IBAction)addContact:(id)sender
182         NSString                *UID = [service normalizeUID:[textField_contactName stringValue] removeIgnoredCharacters:YES];
183         NSString                *alias = [textField_contactAlias stringValue];
184         NSEnumerator    *enumerator;
185         AIListGroup             *group;
186         AIAccount               *account;
187         NSMutableArray  *contactArray = [NSMutableArray array];
188         
189         //Group
190         group = ([popUp_targetGroup numberOfItems] ?
191                         [[popUp_targetGroup selectedItem] representedObject] : 
192                         nil);
193         
194         if (!group) group = [[adium contactController] groupWithUID:DEFAULT_GROUP_NAME];
195         
196         AILogWithSignature(@"checkedAccounts is %@", checkedAccounts);
198         //Add contact to our accounts
199         enumerator = [accounts objectEnumerator];
200         while ((account = [enumerator nextObject])) {
201                 if ([account contactListEditable] &&
202                    [[account preferenceForKey:KEY_ADD_CONTACT_TO group:PREF_GROUP_ADD_CONTACT] boolValue]) {
203                         AILogWithSignature(@"Accont %@ was checked per its preference; we'll add %@ to it", account, UID);
204                         AIListContact   *contact = [[adium contactController] contactWithService:service
205                                                                                                                                                          account:account
206                                                                                                                                                                  UID:UID];
207                         
208                         if (contact) {
209                                 if (alias && [alias length]) [contact setDisplayName:alias];
210                                 [contactArray addObject:contact];
211                                 
212                                 //Remember the ABPerson's unique ID associated with this contact
213                                 if (uniqueID)
214                                         [contact setPreference:uniqueID forKey:KEY_AB_UNIQUE_ID group:PREF_GROUP_ADDRESSBOOK];
216                                 //Force this contact to show up on the user's list for a little bit, even if it is offline
217                                 //Otherwise they have no good feedback that a contact was added at all.
218                                 [contact setStatusObject:[NSNumber numberWithBool:YES] forKey:@"New Object" notify:YES];
219                                 [contact setStatusObject:[NSNumber numberWithBool:NO] forKey:@"New Object" afterDelay:10.0];
220                         }
221                 }
222         }
224         //Add them to our local group
225         AILogWithSignature(@"Adding %@ to %@", contactArray, group);
226         [[adium contactController] addContacts:contactArray toGroup:group];
228         [self closeWindow:nil];
232  * @brief Display a sheet for searching a person within the AB database.
233  */
234 - (IBAction)searchInAB:(id)sender
236         OWABSearchWindowController *abSearchWindow;
237         abSearchWindow = [[OWABSearchWindowController promptForNewPersonSearchOnWindow:[self window]
238                                                                                                                                         initialService:service] retain];
239         [abSearchWindow setDelegate:self];
243  * @brief Callback from OWABSearchWindowController
244  */
245 - (void)absearchWindowControllerDidSelectPerson:(OWABSearchWindowController *)controller
247         ABPerson *selectedPerson = [controller selectedPerson];
248         
249         if (selectedPerson) {
250                 NSString *selectedScreenName = [controller selectedScreenName];
251                 NSString *selectedName = [controller selectedName];
252                 AIService *selectedService = [controller selectedService];
253                 
254                 if (selectedScreenName)
255                         [textField_contactName setStringValue:[service normalizeUID:selectedScreenName removeIgnoredCharacters:YES]];
256                 
257                 if (selectedName)
258                         [textField_contactAlias setStringValue:selectedName];
259                 
260                 if (selectedService) {
261                         [popUp_contactType selectItemWithTitle:[selectedService shortDescription]];
262                         [self selectServiceType:nil];
263                 }
264                 
265                 uniqueID = [[selectedPerson uniqueId] retain];
266         }
267         
268         //Clean up
269         [controller release];
273 //Service Type ---------------------------------------------------------------------------------------------------------
274 #pragma mark Service Type
276  * @brief Build and configure the menu of contact service types
277  */
278 - (void)buildContactTypeMenu
280         //Rebuild the menu
281         [popUp_contactType setMenu:[AIServiceMenu menuOfServicesWithTarget:self
282                                                                                                         activeServicesOnly:YES
283                                                                                                            longDescription:NO
284                                                                                                                                 format:nil]];
285         
286         //Ensure our selection is still valid
287         [self ensureValidContactTypeSelection];
291  * @brief Ensures that the selected contact type is valid, selecting another if it isn't
292  */
293 - (void)ensureValidContactTypeSelection
295         int                     serviceIndex = -1;
296         
297         //Force our menu to update.. it needs to be correctly validated for the code below to work
298         [[popUp_contactType menu] update];
300         //Find the menu item for our current service
301         if (service) serviceIndex = [popUp_contactType indexOfItemWithRepresentedObject:service];               
303         //If our service is not available we'll have to pick another one
304         if (service && (serviceIndex == -1 || ![[popUp_contactType itemAtIndex:serviceIndex] isEnabled])) {
305                 [self _setServiceType:nil];
306         }
308         //If we don't have a service, pick the first available one
309         if (!service) {
310                 [self _setServiceType:[[[popUp_contactType menu] firstEnabledMenuItem] representedObject]];
311         }
313         //Update our menu and window for the current service
314         [popUp_contactType selectItemWithRepresentedObject:service];
315         [self configureForCurrentServiceType];
319  * @brief Configure any service-dependent controls in our window for the current service
320  */
321 - (void)configureForCurrentServiceType
323         NSString        *userNameLabel = [service contactUserNameLabel];
324         
325         //Update the service icon
326         [imageView_service setImage:[AIServiceIcons serviceIconForService:service
327                                                                                                                                  type:AIServiceIconLarge
328                                                                                                                         direction:AIIconNormal]];
329         [textField_contactNameLabel setLocalizedString:[(userNameLabel ? userNameLabel :
330                                                                                                          AILocalizedString(@"Contact ID",nil)) stringByAppendingString:AILocalizedString(@":", "Colon which will be appended after a label such as 'User Name', before an input field")]];
332         //And the list of accounts
333         [self updateAccountList];
337  * @brief User selected a new service type
338  */
339 - (void)selectServiceType:(id)sender
340 {       
341         [self _setServiceType:[[popUp_contactType selectedItem] representedObject]];
342         [self configureForCurrentServiceType];
346  * @brief Set the current service type
347  */
348 - (void)_setServiceType:(AIService *)inService
350         if (inService != service) {
351                 [service release];
352                 service = [inService retain];
353         }
357  * Validate a menu item
358  */
359 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
361         NSEnumerator    *enumerator = [[[adium accountController] accountsCompatibleWithService:[menuItem representedObject]] objectEnumerator];
362         AIAccount               *account;
363         
364         while ((account = [enumerator nextObject])) {
365                 if ([account contactListEditable]) return YES;
366         }
367         
368         return NO;
372  * @brief Update our contact type menu when user accounts change
373  */
374 - (void)accountListChanged:(NSNotification *)notification
376         [self buildContactTypeMenu];
380  * @brief Update our contact type when account availability changes
381  */
382 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
384         if ([inObject isKindOfClass:[AIAccount class]]) {
385                 [self ensureValidContactTypeSelection];
386         }
388         return nil;
392 //Add to Group ---------------------------------------------------------------------------------------------------------
393 #pragma mark Add to Group
395  * @brief Build the menu of available destination groups
396  */
397 - (void)buildGroupMenu
399         AIListObject    *selectedObject;
400         NSMenu                  *menu;
401         //Rebuild the menu
402         menu = [[adium contactController] menuOfAllGroupsInGroup:nil withTarget:self];
404         //Add a default group name to the menu if there are no groups listed
405         if ([menu numberOfItems] == 0) {
406                 [menu addItemWithTitle:DEFAULT_GROUP_NAME
407                                                 target:self
408                                                 action:@selector(selectGroup:)
409                                  keyEquivalent:@""];
410         }
411         
412         [menu addItem:[NSMenuItem separatorItem]];
413         [menu addItemWithTitle:[AILocalizedString(@"New Group",nil) stringByAppendingEllipsis]
414                                         target:self
415                                         action:@selector(newGroup:)
416                          keyEquivalent:@""];
417         
418         //Select the group of the currently selected object on the contact list
419         selectedObject = [[adium interfaceController] selectedListObject];
420         while (selectedObject && ![selectedObject isKindOfClass:[AIListGroup class]]) {
421                 selectedObject = [selectedObject containingObject];
422         }
424         [popUp_targetGroup setMenu:menu];
426         //If there was no selected group, just select the first item
427         if (selectedObject) {
428                 if (![popUp_targetGroup selectItemWithRepresentedObject:selectedObject]) {
429                         [popUp_targetGroup selectItemAtIndex:0];                        
430                 }
432         } else {
433                 [popUp_targetGroup selectItemAtIndex:0];
434         }
438  * @brief Prompt the user to add a new group immediately
439  */
440 - (void)newGroup:(id)sender
442         AINewGroupWindowController      *newGroupWindowController;
443         
444         newGroupWindowController = [AINewGroupWindowController promptForNewGroupOnWindow:[self window]];
446         //Observe for the New Group window to close
447         [[adium notificationCenter] addObserver:self
448                                                                    selector:@selector(newGroupDidEnd:) 
449                                                                            name:@"NewGroupWindowControllerDidEnd"
450                                                                          object:[newGroupWindowController window]];     
453 - (void)newGroupDidEnd:(NSNotification *)inNotification
455         NSWindow        *window = [inNotification object];
457         if ([[window windowController] isKindOfClass:[AINewGroupWindowController class]]) {
458                 NSString        *newGroupUID = [[window windowController] newGroupUID];
459                 AIListGroup *group = [[adium contactController] existingGroupWithUID:newGroupUID];
461                 //Rebuild the group menu
462                 [self buildGroupMenu];
463                 
464                 /* Select the new group if it exists; otherwise select the first group (so we don't still have New Group... selected).
465                  * If the user cancelled, group will be nil since the group doesn't exist.
466                  */
467                 if (![popUp_targetGroup selectItemWithRepresentedObject:group]) {
468                         [popUp_targetGroup selectItemAtIndex:0];                        
469                 }
470                 
471                 [[self window] performSelector:@selector(makeKeyAndOrderFront:)
472                                                         withObject:self
473                                                         afterDelay:0];
474         }
476         //Stop observing
477         [[adium notificationCenter] removeObserver:self
478                                                                                   name:@"NewGroupWindowControllerDidEnd" 
479                                                                                 object:window];
482 //Add to Accounts ------------------------------------------------------------------------------------------------------
483 #pragma mark Add to Accounts
485  * @brief Update the accounts list
486  */
487 - (void)updateAccountList
488 {       
489         [accounts release];
490         accounts = [[[adium accountController] accountsCompatibleWithService:service] retain];
491         
492         [checkedAccounts release];
493         checkedAccounts = [[NSMutableSet alloc] init];
495         //Select accounts by default
496         if (initialAccount && [accounts containsObject:initialAccount]) {
497                 [checkedAccounts addObject:initialAccount];
499         } else {
500                 NSEnumerator    *enumerator;
501                 AIAccount               *anAccount;
503                 enumerator = [accounts objectEnumerator];
504                 while ((anAccount = [enumerator nextObject])) {
505                         if ([[anAccount preferenceForKey:KEY_ADD_CONTACT_TO group:PREF_GROUP_ADD_CONTACT] boolValue])
506                                 [checkedAccounts addObject:anAccount];
507                 }
508         }
510         [tableView_accounts reloadData];
514  * @brief Rows in the accounts table view
515  */
516 - (int)numberOfRowsInTableView:(NSTableView *)tableView
518         return [accounts count];
522  * @brief Object value for columns in the accounts table view
523  */
524 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
526         NSString        *identifier = [tableColumn identifier];
527         
528         if ([identifier isEqualToString:@"check"]) {
529                 return ([[accounts objectAtIndex:row] contactListEditable] ?
530                                 [NSNumber numberWithBool:[checkedAccounts containsObject:[accounts objectAtIndex:row]]] :
531                                 [NSNumber numberWithBool:NO]);
532         
533         } else if ([identifier isEqualToString:@"account"]) {
534                 return [[accounts objectAtIndex:row] explicitFormattedUID];
535                 
536         } else {
537                 return @"";
539         }
543  * @brief Will display cell
545  * Enable/disable account checkbox as appropriate
546  */
547 - (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row
549         NSString        *identifier = [tableColumn identifier];
550         
551         if ([identifier isEqualToString:@"check"]) {
552                 [cell setEnabled:[[accounts objectAtIndex:row] contactListEditable]];
553         }
557  * @brief Set the enabled/disabled state for an account in the account list
558  */
559 - (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row
561         NSString        *identifier = [tableColumn identifier];
563         if ([identifier isEqualToString:@"check"]) {
564                 [[accounts objectAtIndex:row] setPreference:[NSNumber numberWithBool:[object boolValue]] 
565                                                                                          forKey:KEY_ADD_CONTACT_TO 
566                                                                                           group:PREF_GROUP_ADD_CONTACT];
567                 if ([object boolValue]) {
568                         [checkedAccounts addObject:[accounts objectAtIndex:row]];
569                 } else {
570                         [checkedAccounts removeObject:[accounts objectAtIndex:row]];                    
571                 }
572         }
576  * @brief Empty selector called by the group popUp menu
577  */
578 - (void)selectGroup:(id)sender
583 @end