root/trunk/lilypad/Sources/LPPrefsController.m @ 280

Revision 280, 33.6 KB (checked in by jppavao, 5 years ago)

Fixed an exception triggered in the account preferences pane that was introduced in [277]. References #72.

Line 
1//
2//  LPPrefsController.m
3//  Lilypad
4//
5//      Copyright (C) 2006-2008 PT.COM,  All rights reserved.
6//      Authors: Joao Pavao <jpavao@co.sapo.pt>
7//           Jason Kim <jason@512k.org>
8//
9//      For more information on licensing, read the README file.
10//      Para mais informações sobre o licenciamento, leia o ficheiro README.
11//
12
13#import "LPPrefsController.h"
14#import "LPKeychainManager.h"
15#import "LPAccountsController.h"
16#import "LPAccount.h"
17#import "LPAccountPrefsListCell.h"
18#import "LPSapoAgents.h"
19#import "NSString+ConcatAdditions.h"
20#import "LPCommon.h"
21
22
23
24static NSString *AccountUUIDsDraggedType = @"AccountUUIDsDraggedType";
25
26
27enum {
28        LPPrefsSAPOAccountKind,
29        LPPrefsNetcaboAccountKind,
30        LPPrefsTelepacAccountKind,
31        LPPrefsOtherJabberAccountKind
32};
33
34
35@interface LPPrefsController ()  // Private Methods
36
37- (void)p_updateDownloadsFolderMenu;
38
39- (NSSet *)p_allOurURLHandlersBundleIDs;
40- (NSDictionary *)p_infoDictForURLHandlerWithBundleID:(NSString *)bundleID;
41- (NSArray *)p_contentsOfOurURLHandlersMenu;
42- (void)p_updateURLHandlersMenu;
43- (void)p_updateURLHandlersMenuSelection;
44- (void)p_selectedDefaultURLHandler:(id)sender;
45- (void)p_selectOtherURLHandler:(id)sender;
46
47- (LPAccount *)p_selectedAccount;
48- (void)p_startObservingAccounts:(NSArray *)accounts;
49- (void)p_stopObservingAccounts:(NSArray *)accounts;
50- (NSInteger)p_selectedAccountKind;
51- (void)p_setSelectedAccountKind:(NSInteger)kind;
52- (void)p_syncJIDWithAccountViews;
53- (void)p_syncAccountViewsWithJID:(NSString *)jid;
54
55- (void)p_updateGUIForMSNTransportAgentOfAccount:(LPAccount *)account;
56- (void)p_updateGUIForTransportAgent:(NSString *)transportAgent ofAccount:(LPAccount *)account;
57- (void)p_setButtonEnabled:(NSButton *)btn afterDelay:(float)delay;
58- (void)p_setButtonDisabledAndCancelTimer:(NSButton *)btn;
59@end
60
61
62@implementation LPPrefsController
63
64+ (void)initialize
65{
66        if (self == [LPPrefsController class]) {
67                NSString        *downloadsFolderPath = nil;
68                NSArray         *foundFolders = ((floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_4) ?
69                                                                         nil :
70                                                                         NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES));
71               
72                if ([foundFolders count] == 0) {
73                        foundFolders = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
74                        if ([foundFolders count] == 0) {
75                                // Build the path manually (last resort)
76                                downloadsFolderPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Desktop"];
77                        }
78                }
79               
80                if (downloadsFolderPath == nil && [foundFolders count] > 0)
81                        downloadsFolderPath = [foundFolders objectAtIndex:0];
82               
83                [[NSUserDefaults standardUserDefaults] registerDefaults:
84                 [NSDictionary dictionaryWithObject:downloadsFolderPath forKey:@"DownloadsFolder"]];
85        }
86}
87
88- (void)dealloc
89{
90        [self p_stopObservingAccounts:[[self accountsController] accounts]];
91        [[self accountsController] removeObserver:self forKeyPath:@"accounts"];
92        [m_accountsController removeObserver:self forKeyPath:@"selectedObjects"];
93        [m_accountsController removeObserver:self forKeyPath:@"selection.JID"];
94       
95        [[NSNotificationCenter defaultCenter] removeObserver:self];
96       
97        [m_generalView release];
98        [m_accountsView release];
99        [m_advancedView release];
100        [m_msnRegistrationSheet release];
101        [m_defaultAccountController release];
102        [m_accountsController release];
103       
104        [super dealloc];
105}
106
107- (void)initializePrefPanes
108{
109        [self addPrefWithView:m_generalView
110                                        label:NSLocalizedString(@"General", @"preference pane label")
111                                        image:[NSImage imageNamed:@"GeneralPrefs"]
112                           identifier:@"GeneralPrefs"];
113       
114        [self addPrefWithView:m_accountsView
115                                        label:NSLocalizedString(@"Accounts", @"preference pane label")
116                                        image:[NSImage imageNamed:@"AccountsPrefs"]
117                           identifier:@"AccountsPrefs"];
118       
119        if ([[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeAdvancedPrefs"]) {
120                [self addAdvancedPrefsPane];
121        }
122}
123
124- (void)addAdvancedPrefsPane
125{
126        [self showPrefs:nil];
127        [self addPrefWithView:m_advancedView
128                                        label:NSLocalizedString(@"Advanced", @"preference pane label")
129                                        image:[NSImage imageNamed:@"AdvancedPrefs"]
130                           identifier:@"AdvancedPrefs"];
131}
132
133- (LPAccountsController *)accountsController
134{
135        return [LPAccountsController sharedAccountsController];
136}
137
138- (void)loadNib
139{
140        [NSBundle loadNibNamed:@"Preferences" owner:self];
141       
142       
143        // General Pane
144        [self p_updateDownloadsFolderMenu];
145        [self p_updateURLHandlersMenu];
146       
147       
148        // Accounts Pane
149        [[self accountsController] addObserver:self
150                                                                forKeyPath:@"accounts"
151                                                                   options:( NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew )
152                                                                   context:NULL];
153        [self p_startObservingAccounts:[[self accountsController] accounts]];
154       
155        [m_accountsController addObserver:self forKeyPath:@"selection.JID" options:0 context:NULL];
156        [m_accountsController addObserver:self forKeyPath:@"selectedObjects" options:0 context:NULL];
157       
158        [m_accountsTable registerForDraggedTypes:[NSArray arrayWithObject:AccountUUIDsDraggedType]];
159       
160        [self p_syncAccountViewsWithJID:[[self p_selectedAccount] JID]];
161       
162       
163        // Transport Pane
164        LPAccount       *account = [self p_selectedAccount];
165        NSString        *transportAgent = [[account sapoAgents] hostnameForService:@"msn"];
166       
167        [self p_updateGUIForTransportAgent:transportAgent ofAccount:account];
168       
169        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
170        [nc addObserver:self
171                   selector:@selector(accountDidChangeTransportInfo:)
172                           name:LPAccountDidChangeTransportInfoNotification
173                         object:account];
174        [nc addObserver:self
175                   selector:@selector(applicationWillBecomeActive:)
176                           name:NSApplicationWillBecomeActiveNotification
177                         object:NSApp];
178       
179        [m_accountsTable sizeLastColumnToFit];
180}
181
182
183#pragma mark -
184#pragma mark NSWindow Delegate Methods
185
186
187- (void)windowDidBecomeKey:(NSNotification *)aNotification
188{
189        if (m_needsToUpdateURLHandlerMenu) {
190                [self p_updateURLHandlersMenu];
191                m_needsToUpdateURLHandlerMenu = NO;
192        }
193}
194
195- (void)windowDidResignKey:(NSNotification *)aNotification
196{
197        // Try to commit any text field editing session
198        NSWindow *win = [self window];
199        [win makeFirstResponder:win];
200}
201
202- (void)windowWillClose:(NSNotification *)aNotification
203{
204        // The default account controller isn't committing changes automatically upon window close. Maybe it's because the
205        // views with the text fields are not inserted in any windows when the nib is loaded, I don't know. We'll have to
206        // do it manually.
207        [m_defaultAccountController commitEditing];
208        [m_accountsController commitEditing];
209}
210
211
212#pragma mark -
213#pragma mark Actions - General Prefs
214
215
216#pragma mark Downloads Folder
217
218- (void)p_updateDownloadsFolderMenu
219{
220        id                      folderItem = [m_downloadsFolderPopUpButton itemAtIndex:0];
221        NSString        *folderPath = LPDownloadsFolderPath();
222        NSString        *folderDisplayName = [[NSFileManager defaultManager] displayNameAtPath:folderPath];
223        NSImage         *folderImage = [[NSWorkspace sharedWorkspace] iconForFile:folderPath];
224       
225        [folderImage setSize:NSMakeSize(16.0, 16.0)];
226       
227        [folderItem setTitle:folderDisplayName];
228        [folderItem setImage:folderImage];
229}
230
231- (IBAction)chooseDownloadsFolder:(id)sender
232{
233        NSString *downloadsFolder = LPDownloadsFolderPath();
234       
235        NSOpenPanel     *op = [NSOpenPanel openPanel];
236       
237        [op setCanChooseFiles:NO];
238        [op setCanChooseDirectories:YES];
239        [op setCanCreateDirectories:YES];
240
241        [op setResolvesAliases:YES];
242        [op setAllowsMultipleSelection:NO];
243        [op setPrompt:NSLocalizedString(@"Select", @"")];
244       
245        [op beginSheetForDirectory:(downloadsFolder ? downloadsFolder : NSHomeDirectory())
246                                                  file:nil
247                                                 types:nil
248                                modalForWindow:[self window]
249                                 modalDelegate:self
250                                didEndSelector:@selector(p_selectDownloadsFolderPanelDidEnd:returnCode:contextInfo:)
251                                   contextInfo:NULL];
252}
253
254- (void)p_selectDownloadsFolderPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
255{
256        if (returnCode == NSOKButton) {
257                [[NSUserDefaults standardUserDefaults] setObject:[panel filename] forKey:@"DownloadsFolder"];
258                [self p_updateDownloadsFolderMenu];
259        }
260       
261        // Finish by re-selecting the item that references the selected folder (the first one)
262        [m_downloadsFolderPopUpButton selectItemAtIndex:0];
263}
264
265
266- (IBAction)openChatTranscriptsFolder:(id)sender
267{
268        NSString *folderPath = LPChatTranscriptsFolderPath();
269       
270        if (folderPath == nil) {
271                NSBeep();
272        }
273        else {
274                [[NSWorkspace sharedWorkspace] openFile:folderPath];
275        }
276}
277
278
279#pragma mark XMPP/Jabber URL Handler Popup Menu
280
281
282- (NSString *)defaultURLHandlerBundleID
283{
284        // Get the current default handler for our URLs
285        CFStringRef defaultXMPPHandler = LSCopyDefaultHandlerForURLScheme(CFSTR("xmpp"));
286       
287        if (defaultXMPPHandler == NULL)
288                defaultXMPPHandler = LSCopyDefaultHandlerForURLScheme(CFSTR("jabber"));
289       
290        return [(NSString *)defaultXMPPHandler autorelease];
291}
292
293- (void)setDefaultURLHandlerBundleID:(NSString *)bundleID
294{
295        LSSetDefaultHandlerForURLScheme(CFSTR("xmpp"), (CFStringRef)bundleID);
296        LSSetDefaultHandlerForURLScheme(CFSTR("jabber"), (CFStringRef)bundleID);
297       
298        // There's a lot of stuff that may need to be done: we may have to remove the previous handler because it
299        // may not be returned as an app capable of handling our URLs. Or we may need to add a new handler that was
300        // selected using the NSOpenPanel for the "select other" popup menu option.
301        // So, just update the whole menu.
302        [self p_updateURLHandlersMenu];
303}
304
305
306- (NSSet *)p_allOurURLHandlersBundleIDs
307{
308        // Get all the available handlers for our URLs from Launch Services
309        CFArrayRef allXMPPHandlers = LSCopyAllHandlersForURLScheme(CFSTR("xmpp"));
310        CFArrayRef allJabberHandlers = LSCopyAllHandlersForURLScheme(CFSTR("jabber"));
311       
312        // Mix them all together
313        NSMutableSet *allURLHandlers = [NSMutableSet set];
314        [allURLHandlers addObjectsFromArray:(NSArray *)allXMPPHandlers];
315        [allURLHandlers addObjectsFromArray:(NSArray *)allJabberHandlers];
316       
317        CFRelease(allXMPPHandlers);
318        CFRelease(allJabberHandlers);
319       
320        return allURLHandlers;
321}
322
323
324- (NSDictionary *)p_infoDictForURLHandlerWithBundleID:(NSString *)bundleID
325{
326        NSWorkspace             *ws = [NSWorkspace sharedWorkspace];
327        NSString                *appAbsolutePath = [ws absolutePathForAppBundleWithIdentifier:bundleID];
328        NSDictionary    *itemDescription = nil;
329       
330        if (appAbsolutePath) {
331                NSBundle        *appBundle = [NSBundle bundleWithPath:appAbsolutePath];
332               
333                NSString        *appName = [appBundle objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey];
334                NSString        *appVersion = [appBundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
335                NSImage         *appIcon = [ws iconForFile:appAbsolutePath];
336               
337                if ([appName length] > 0) {
338                        itemDescription = [NSDictionary dictionaryWithObjectsAndKeys:
339                                bundleID, @"BundleID",
340                                appName, @"AppName",
341                                appVersion, @"Version",
342                                appIcon, @"Icon", nil];
343                }
344        }
345       
346        return itemDescription;
347}
348
349
350- (NSArray *)p_contentsOfOurURLHandlersMenu
351{
352        NSMutableSet *bundleIDsForMenu = [NSMutableSet setWithSet:[self p_allOurURLHandlersBundleIDs]];
353       
354        // Add the default handler to the set of available handlers, in case it isn't in there already.
355        // The user may have chosen an application that isn't registered in the Launch Services database as being
356        // capable of handling this URL scheme.
357        NSString *defaultHandlerBundleID = [self defaultURLHandlerBundleID];
358        if ([defaultHandlerBundleID length] > 0)
359                [bundleIDsForMenu addObject:defaultHandlerBundleID];
360       
361       
362        // Build the actual list sorted by the application names
363        NSMutableArray *URLHandlersMenuContents = [NSMutableArray array];
364       
365        NSEnumerator *handlerBundleIDEnum = [bundleIDsForMenu objectEnumerator];
366        NSString *bundleID;
367        while (bundleID = [handlerBundleIDEnum nextObject]) {
368                NSDictionary *infoDict = [self p_infoDictForURLHandlerWithBundleID:bundleID];
369                if (infoDict)
370                        [URLHandlersMenuContents addObject:infoDict];
371        }
372       
373       
374        NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:@"AppName" ascending:YES
375                                                                                                                                selector:@selector(caseInsensitiveCompare:)];
376        [URLHandlersMenuContents sortUsingDescriptors:[NSArray arrayWithObject:descriptor]];
377        [descriptor release];
378       
379        return URLHandlersMenuContents;
380}
381
382
383- (void)p_updateURLHandlersMenu
384{
385        // Cleanup
386        [m_defaultURLHandlerPopUpButton removeAllItems];
387        [m_defaultURLHandlerPopUpButton setAutoenablesItems:NO];
388       
389        // Add all the applications known to be able to handle our URLs
390        NSEnumerator *handlersEnum = [[self p_contentsOfOurURLHandlersMenu] objectEnumerator];
391        NSDictionary *handlerDescription;
392        while (handlerDescription = [handlersEnum nextObject]) {
393                NSString *menuItemTitle = [NSString stringWithFormat:@"%@ (%@)",
394                        [handlerDescription objectForKey:@"AppName"],
395                        [handlerDescription objectForKey:@"Version"]];
396               
397                [m_defaultURLHandlerPopUpButton addItemWithTitle:menuItemTitle];
398               
399                NSMenuItem *menuItem = [m_defaultURLHandlerPopUpButton lastItem];
400               
401                [[handlerDescription objectForKey:@"Icon"] setSize:NSMakeSize(16.0, 16.0)];
402                [menuItem setImage:[handlerDescription objectForKey:@"Icon"]];
403                [menuItem setRepresentedObject:[handlerDescription objectForKey:@"BundleID"]];
404                [menuItem setTarget:self];
405                [menuItem setAction:@selector(p_selectedDefaultURLHandler:)];
406        }
407       
408        // Add the "Select Other" item
409        [[m_defaultURLHandlerPopUpButton menu] addItem:[NSMenuItem separatorItem]];
410       
411        [m_defaultURLHandlerPopUpButton addItemWithTitle:NSLocalizedString(@"Select Other App...", @"")];
412        NSMenuItem *item = [m_defaultURLHandlerPopUpButton lastItem];
413        [item setTarget:self];
414        [item setAction:@selector(p_selectOtherURLHandler:)];
415       
416        // Select the item for the current default handler
417        [self p_updateURLHandlersMenuSelection];
418}
419
420
421- (void)p_updateURLHandlersMenuSelection
422{
423        NSString *defaultHandlerBundleID = [self defaultURLHandlerBundleID];
424        int indexToSelect = ( [defaultHandlerBundleID length] > 0 ?
425                                                  [m_defaultURLHandlerPopUpButton indexOfItemWithRepresentedObject:defaultHandlerBundleID] :
426                                                  (-1) );
427       
428        // If there's no default item or if it isn't in the menu, then add a "none selected" item at the top
429        if (indexToSelect < 0) {
430                [m_defaultURLHandlerPopUpButton insertItemWithTitle:NSLocalizedString(@"<none selected>", @"") atIndex:0];
431                NSMenuItem *item = [m_defaultURLHandlerPopUpButton itemAtIndex:0];
432                [item setEnabled:NO];
433               
434                [[m_defaultURLHandlerPopUpButton menu] insertItem:[NSMenuItem separatorItem] atIndex:1];
435               
436                indexToSelect = 0;
437        }
438        [m_defaultURLHandlerPopUpButton selectItemAtIndex: indexToSelect];
439}
440
441
442- (void)p_selectedDefaultURLHandler:(id)sender
443{
444        // "sender" should be the selected menu item in the popup menu
445        [self setDefaultURLHandlerBundleID:[sender representedObject]];
446}
447
448
449- (void)p_selectOtherURLHandler:(id)sender
450{
451        NSOpenPanel     *op = [NSOpenPanel openPanel];
452       
453        [op setCanChooseDirectories:NO];
454        [op setCanCreateDirectories:NO];
455        [op setResolvesAliases:YES];
456        [op setAllowsMultipleSelection:NO];
457       
458        [op setPrompt:NSLocalizedString(@"Select", @"")];
459       
460        NSArray *applicationsDirPaths = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSSystemDomainMask, NO);
461        NSString *applicationsDirPath = ( [applicationsDirPaths count] > 0 ?
462                                                                          [applicationsDirPaths objectAtIndex:0] :
463                                                                          @"/Applications" );
464       
465        [op beginSheetForDirectory:applicationsDirPath
466                                                  file:nil
467                                                 types:[NSArray arrayWithObject:@"app"]
468                                modalForWindow:[self window]
469                                 modalDelegate:self
470                                didEndSelector:@selector(p_selectOtherURLHandlerPanelDidEnd:returnCode:contextInfo:)
471                                   contextInfo:NULL];
472}
473
474- (void)p_selectOtherURLHandlerPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
475{
476        if (returnCode == NSOKButton) {
477                NSBundle *appBundle = [NSBundle bundleWithPath:[panel filename]];
478                NSString *bundleID = [appBundle bundleIdentifier];
479               
480                if ([bundleID length] > 0) {
481                        [self setDefaultURLHandlerBundleID:bundleID];
482                }
483        }
484       
485        // Finish by selecting the currently default handler so that the "select other" menu item never gets selected
486        [self p_updateURLHandlersMenuSelection];
487}
488
489
490#pragma mark Notifications
491
492
493- (void)applicationWillBecomeActive:(NSNotification *)notif
494{
495        // Just in case the list of URL handlers or the default URL handler changed while we were in the background
496        m_needsToUpdateURLHandlerMenu = YES;
497}
498
499
500#pragma mark -
501#pragma mark Actions - Accounts Prefs
502
503
504- (LPAccount *)p_selectedAccount
505{
506        id selectedObjects = [m_accountsController selectedObjects];
507        return ([selectedObjects count] > 0 ? [selectedObjects objectAtIndex:0] : nil);
508}
509
510- (void)p_startObservingAccounts:(NSArray *)accounts
511{
512        NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [accounts count])];
513       
514        [accounts addObserver:self toObjectsAtIndexes:indexSet forKeyPath:@"enabled" options:0 context:NULL];
515        [accounts addObserver:self toObjectsAtIndexes:indexSet forKeyPath:@"status" options:0 context:NULL];
516        [accounts addObserver:self toObjectsAtIndexes:indexSet forKeyPath:@"description" options:0 context:NULL];
517        [accounts addObserver:self toObjectsAtIndexes:indexSet forKeyPath:@"automaticReconnectionStatus" options:0 context:NULL];
518}
519
520- (void)p_stopObservingAccounts:(NSArray *)accounts
521{
522        NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [accounts count])];
523       
524        [accounts removeObserver:self fromObjectsAtIndexes:indexSet forKeyPath:@"enabled"];
525        [accounts removeObserver:self fromObjectsAtIndexes:indexSet forKeyPath:@"status"];
526        [accounts removeObserver:self fromObjectsAtIndexes:indexSet forKeyPath:@"description"];
527        [accounts removeObserver:self fromObjectsAtIndexes:indexSet forKeyPath:@"automaticReconnectionStatus"];
528}
529
530- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
531{
532        if ([keyPath isEqualToString:@"accounts"]) {
533                NSKeyValueChange changeKind = [[change valueForKey:NSKeyValueChangeKindKey] intValue];
534               
535                if (changeKind == NSKeyValueChangeInsertion) {
536                        [self p_startObservingAccounts:[change objectForKey:NSKeyValueChangeNewKey]];
537                }
538                else if (changeKind == NSKeyValueChangeRemoval) {
539                        [self p_stopObservingAccounts:[change objectForKey:NSKeyValueChangeOldKey]];
540                }
541                [m_accountsTable setNeedsDisplay:YES];
542        }
543        else if ([keyPath isEqualToString:@"selectedObjects"]) {
544                [self p_updateGUIForMSNTransportAgentOfAccount:[self p_selectedAccount]];
545        }
546        else if ([keyPath isEqualToString:@"selection.JID"]) {
547                [self p_syncAccountViewsWithJID:[object valueForKeyPath:keyPath]];
548        }
549        else if ([keyPath isEqualToString:@"enabled"] ||
550                         [keyPath isEqualToString:@"status"] ||
551                         [keyPath isEqualToString:@"description"] ||
552                         [keyPath isEqualToString:@"automaticReconnectionStatus"]) {
553                [m_accountsTable setNeedsDisplay:YES];
554        }
555}
556
557- (IBAction)addAccount:(id)sender
558{
559        [[self accountsController] addNewAccount];
560}
561
562
563- (IBAction)removeAccount:(id)sender
564{
565        NSArray *selectedAccounts = [m_accountsController selectedObjects];
566        NSString *alertTitle = nil;
567        NSString *alertInfo = nil;
568       
569        if ([selectedAccounts count] == 0) {
570                NSBeep();
571        }
572        else {
573                if ([selectedAccounts count] > 1) {
574                        alertTitle = NSLocalizedString(@"Delete the selected accounts?", @"");
575                       
576                        alertInfo = [NSString stringWithFormat:
577                                                 NSLocalizedString(@"This will delete the accounts %@. You can't undo this action.", @""),
578                                                 [NSString concatenatedStringWithValuesForKey:@"description" ofObjects:selectedAccounts
579                                                                                                          useDoubleQuotes:YES maxNrListedItems:5]];
580                }
581                else {
582                        alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Delete the account \"%@\"?", @""),
583                                                  [[selectedAccounts objectAtIndex:0] description]];
584                        alertInfo = NSLocalizedString(@"You can't undo this action.", @"");
585                }
586               
587                NSAlert *alert = [[NSAlert alloc] init];
588               
589                [alert setMessageText:alertTitle];
590                [alert setInformativeText:alertInfo];
591                [alert addButtonWithTitle:NSLocalizedString(@"Delete", @"")];
592                [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"")];
593               
594                [alert beginSheetModalForWindow:[self window]
595                                                  modalDelegate:self
596                                                 didEndSelector:@selector(p_removeAccountAlertDidEnd:returnCode:contextInfo:)
597                                                        contextInfo:(void *)[selectedAccounts retain]];
598        }
599}
600
601- (void)p_removeAccountAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo
602{
603        NSArray *selectedAccounts = [(NSArray *)contextInfo autorelease];
604       
605        if (returnCode == NSAlertFirstButtonReturn) {
606                NSEnumerator *accountsEnum = [selectedAccounts objectEnumerator];
607                LPAccount *account;
608                LPAccountsController *ctrl = [self accountsController];
609               
610                while (account = [accountsEnum nextObject])
611                        [ctrl removeAccount:account];
612        }
613       
614        [alert autorelease];
615}
616
617
618- (IBAction)accountKindSelectionDidChange:(id)sender
619{
620        [self p_setSelectedAccountKind:[sender indexOfSelectedItem]];
621        [self p_syncJIDWithAccountViews];
622}
623
624
625- (NSInteger)p_selectedAccountKind
626{
627        return [m_accountKindPopUp indexOfSelectedItem];
628}
629
630- (void)p_setSelectedAccountKind:(NSInteger)kind
631{
632        NSString *theJIDLabel = @"Email:";
633        NSString *theJIDDomainLabel = @"";
634       
635        switch (kind) {
636                case LPPrefsSAPOAccountKind:
637                        theJIDDomainLabel = @"@ sapo.pt";
638                        break;
639                case LPPrefsNetcaboAccountKind:
640                        theJIDDomainLabel = @"@ netcabo.pt";
641                        break;
642                case LPPrefsTelepacAccountKind:
643                        theJIDDomainLabel = @"@ mail.telepac.pt";
644                        break;
645                case LPPrefsOtherJabberAccountKind:
646                        theJIDLabel = @"Jabber ID:";
647                        break;
648                default:
649                        kind = LPPrefsSAPOAccountKind;
650                        theJIDDomainLabel = @"@ sapo.pt";
651                        break;
652        }
653       
654        [m_accountKindPopUp selectItemAtIndex:kind];
655        [m_accountJIDLabel setStringValue:theJIDLabel];
656        [m_accountJIDDomainLabel setStringValue:theJIDDomainLabel];
657}
658
659- (void)p_syncJIDWithAccountViews
660{
661        NSString *suffix = @"";
662       
663        switch ([self p_selectedAccountKind]) {
664                case LPPrefsSAPOAccountKind:
665                        suffix = @"@sapo.pt";
666                        break;
667                case LPPrefsNetcaboAccountKind:
668                        suffix = @"@netcabo.pt";
669                        break;
670                case LPPrefsTelepacAccountKind:
671                        suffix = @"@mail.telepac.pt";
672                        break;
673        }
674       
675        [[self p_selectedAccount] setJID:([[m_accountJIDField stringValue] length] > 0 ?
676                                                                          [[m_accountJIDField stringValue] stringByAppendingString:suffix] :
677                                                                          suffix)];
678}
679
680- (void)p_syncAccountViewsWithJID:(NSString *)jid
681{
682        NSString *username = [jid JIDUsernameComponent];
683        NSString *hostname = [jid JIDHostnameComponent];
684       
685        if ([jid length] == 0 || [hostname isEqualToString:@"sapo.pt"]) {
686                [self p_setSelectedAccountKind:LPPrefsSAPOAccountKind];
687                [m_accountJIDField setStringValue:(username != nil ? username : @"")];
688        }
689        else if ([hostname isEqualToString:@"netcabo.pt"]) {
690                [self p_setSelectedAccountKind:LPPrefsNetcaboAccountKind];
691                [m_accountJIDField setStringValue:(username != nil ? username : @"")];
692        }
693        else if ([hostname isEqualToString:@"mail.telepac.pt"]) {
694                [self p_setSelectedAccountKind:LPPrefsTelepacAccountKind];
695                [m_accountJIDField setStringValue:(username != nil ? username : @"")];
696        }
697        else {
698                [self p_setSelectedAccountKind:LPPrefsOtherJabberAccountKind];
699                [m_accountJIDField setStringValue:(jid != nil ? jid : @"")];
700        }
701}
702
703
704#pragma mark Accounts NSTableView Data Source & Delegate
705
706
707- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
708{
709        return 0;
710}
711
712
713- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
714{
715        return nil;
716}
717
718
719- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard
720{
721        if ([rowIndexes count] > 1) {
722                return NO;
723        }
724        else {
725                [pboard declareTypes:[NSArray arrayWithObject:AccountUUIDsDraggedType] owner:nil];
726               
727                NSArray *accounts = [[self accountsController] accounts];
728                NSArray *draggedUUIDs = [[accounts objectsAtIndexes:rowIndexes] valueForKey:@"UUID"];
729               
730                [pboard setPropertyList:draggedUUIDs forType:AccountUUIDsDraggedType];
731               
732                return YES;
733        }
734}
735
736
737- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)operation
738{
739        NSPasteboard *pboard = [info draggingPasteboard];
740       
741        if ([[pboard types] containsObject:AccountUUIDsDraggedType]) {
742               
743                if (operation == NSTableViewDropOn) {
744                        [aTableView setDropRow:row dropOperation:NSTableViewDropAbove];
745                }
746               
747                return NSDragOperationGeneric;
748        }
749        else {
750                return NSDragOperationNone;
751        }
752}
753
754
755- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)operation
756{
757        BOOL dropWasAccepted = NO;
758       
759        NSPasteboard *pboard = [info draggingPasteboard];
760       
761        if ([[pboard types] containsObject:AccountUUIDsDraggedType]) {
762                NSArray *draggedUUIDs = [pboard propertyListForType:AccountUUIDsDraggedType];
763               
764                NSAssert(([draggedUUIDs count] == 1), @"[draggedUUIDs count] != 1");
765               
766                NSString *draggedAccountUUID = [draggedUUIDs objectAtIndex:0];
767                LPAccount *draggedAccount = [[self accountsController] accountForUUID:draggedAccountUUID];
768               
769                LPAccountsController *accountsController = [self accountsController];
770                int draggedAccountCurrentIndex = [[accountsController accounts] indexOfObject:draggedAccount];
771               
772                int targetIndex = (row > draggedAccountCurrentIndex ? row - 1 : row);
773                if (targetIndex != draggedAccountCurrentIndex) {
774                        // Move it!
775                        [accountsController moveAccount:draggedAccount toIndex:targetIndex];
776                        dropWasAccepted = YES;
777                }
778        }
779       
780        return dropWasAccepted;
781}
782
783
784#pragma mark -
785#pragma mark Actions - MSN Account Prefs - Private
786
787
788- (void)p_enableButton:(NSButton *)btn
789{
790        [btn setEnabled:YES];
791}
792
793- (void)p_setButtonEnabled:(NSButton *)btn afterDelay:(float)delay
794{
795        [btn setEnabled:NO];
796        [self performSelector:@selector(p_enableButton:) withObject:btn afterDelay:delay];
797}
798
799- (void)p_setButtonDisabledAndCancelTimer:(NSButton *)btn
800{
801        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(p_enableButton:) object:btn];
802        [btn setEnabled:NO];
803}
804
805
806- (void)p_updateGUIForMSNTransportAgentOfAccount:(LPAccount *)account
807{
808        [self p_updateGUIForTransportAgent:[[account sapoAgents] hostnameForService:@"msn"] ofAccount:account];
809}
810
811
812- (void)p_updateGUIForTransportAgent:(NSString *)transportAgent ofAccount:(LPAccount *)account
813{
814        if (account == [self p_selectedAccount]) {
815                BOOL            isRegistered = [account isRegisteredWithTransportAgent:transportAgent];
816                NSString        *registeredUsername = [account usernameRegisteredWithTransportAgent:transportAgent];
817                BOOL            isLoggedIn = [account isLoggedInWithTransportAgent:transportAgent];
818               
819                if ([account isOffline]) {
820                        [m_msnTransportStatusView setStringValue:
821                         NSLocalizedString(@"The current MSN state is unknown.",
822                                                           @"MSN transport state description")];
823                       
824                        [m_msnRegistrationButton setTitle:NSLocalizedString(@"Register...", @"MSN transport preferences button")];
825                        [m_msnRegistrationButton setEnabled:NO];
826                       
827                        [m_msnLoginButton setTitle:NSLocalizedString(@"Log In", @"MSN transport preferences button")];
828                        [self p_setButtonDisabledAndCancelTimer:m_msnLoginButton];
829                }
830                else {
831                        if (!isRegistered) {
832                                [m_msnTransportStatusView setStringValue:
833                                 NSLocalizedString(@"You're not currently registered to the MSN transport.",
834                                                                   @"MSN transport state description")];
835                               
836                                [m_msnRegistrationButton setTitle:NSLocalizedString(@"Register...", @"MSN transport preferences button")];
837                                [m_msnRegistrationButton setEnabled:YES];
838                               
839                                [m_msnLoginButton setTitle:NSLocalizedString(@"Log In", @"MSN transport preferences button")];
840                                [self p_setButtonDisabledAndCancelTimer:m_msnLoginButton];
841                        }
842                        else if (!isLoggedIn) {
843                                [m_msnTransportStatusView setStringValue:
844                                 [NSString stringWithFormat:
845                                  NSLocalizedString(@"You're currently registered to the MSN transport with the email \"%@\", but you're not logged in to the service.",
846                                                                        @"MSN transport state description"),
847                                  registeredUsername]];
848                               
849                                [m_msnRegistrationButton setTitle:NSLocalizedString(@"Unregister...", @"MSN transport preferences button")];
850                                [m_msnRegistrationButton setEnabled:YES];
851                               
852                                [m_msnLoginButton setTitle:NSLocalizedString(@"Log In", @"MSN transport preferences button")];
853                                [self p_setButtonEnabled:m_msnLoginButton afterDelay:0.0];
854                        }
855                        else {
856                                [m_msnTransportStatusView setStringValue:
857                                 [NSString stringWithFormat:
858                                  NSLocalizedString(@"You're currently registered to the MSN transport with the email \"%@\" and you're logged in to the service.",
859                                                                        @"MSN transport state description"),
860                                  registeredUsername]];
861                               
862                                [m_msnRegistrationButton setTitle:NSLocalizedString(@"Unregister...", @"MSN transport preferences button")];
863                                [m_msnRegistrationButton setEnabled:YES];
864                               
865                                [m_msnLoginButton setTitle:NSLocalizedString(@"Log In", @"MSN transport preferences button")];
866                                [self p_setButtonDisabledAndCancelTimer:m_msnLoginButton];
867                        }
868                }
869        }
870}
871
872
873#pragma mark Actions - MSN Account Prefs - Public
874
875
876- (IBAction)registerMSNTransport:(id)sender
877{
878        LPAccount       *account = [self p_selectedAccount];
879        NSString        *transportAgent = [[account sapoAgents] hostnameForService:@"msn"];
880       
881        BOOL            isRegistered = [account isRegisteredWithTransportAgent:transportAgent];
882       
883        if ([account isOnline]) {
884                if (!isRegistered) {
885                        // Register
886                        NSString *lastRegisteredEmail = [account lastRegisteredMSNEmail];
887                        NSString *lastRegisteredPassword = [account lastRegisteredMSNPassword];
888                       
889                        [m_msnEmailField setStringValue:(lastRegisteredEmail ? lastRegisteredEmail : @"")];
890                        [m_msnPasswordField setStringValue:(lastRegisteredPassword ? lastRegisteredPassword : @"")];
891                       
892                        [NSApp beginSheet:m_msnRegistrationSheet
893                           modalForWindow:[self window]
894                                modalDelegate:self
895                           didEndSelector:@selector(p_msnRegistrationSheetDidEnd:returnCode:contextInfo:)
896                                  contextInfo:NULL];
897                }
898                else {
899                        // Unregister
900                        NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Are you sure you want to unregister from the MSN transport?", @"Alert displayed when unregistering from the MSN transport")
901                                                                                         defaultButton:NSLocalizedString(@"OK", @"")
902                                                                                   alternateButton:NSLocalizedString(@"Cancel", @"")
903                                                                                           otherButton:nil
904                                                                 informativeTextWithFormat:NSLocalizedString(@"All your MSN contacts will be removed from the SAPO Messenger buddy list when you unregister from the MSN transport. If you moved contacts to different groups, these changes will be lost. You can't undo this action.", @"Alert displayed when unregistering from the MSN transport")];
905                       
906                        [alert beginSheetModalForWindow:[self window]
907                                                          modalDelegate:self
908                                                         didEndSelector:@selector(p_msnUnregistrationAlertDidEnd:returnCode:contextInfo:)
909                                                                contextInfo:NULL];
910                }
911        }
912}
913
914
915- (void)p_msnRegistrationSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
916{
917        [sheet orderOut:self];
918       
919        if (returnCode == NSOKButton) {
920                LPAccount       *account = [self p_selectedAccount];
921                NSString        *transportAgent = [[account sapoAgents] hostnameForService:@"msn"];
922               
923                if ([account isOnline]) {
924                        [account registerWithTransportAgent:transportAgent
925                                                                           username:[m_msnEmailField stringValue]
926                                                                           password:[m_msnPasswordField stringValue]];
927                }
928        }
929}
930
931
932- (void)p_msnUnregistrationAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo
933{
934        if (returnCode == NSAlertDefaultReturn) {
935                LPAccount       *account = [self p_selectedAccount];
936                NSString        *transportAgent = [[account sapoAgents] hostnameForService:@"msn"];
937               
938                if ([account isOnline]) {
939                        [account unregisterWithTransportAgent:transportAgent];
940                }
941        }
942}
943
944
945- (IBAction)loginToMSNTransport:(id)sender
946{
947        [self p_setButtonDisabledAndCancelTimer:m_msnLoginButton];
948        [self p_setButtonEnabled:m_msnLoginButton afterDelay:10.0];
949       
950        // Send a dummy presence so that the MSN transport can connect
951        LPAccount *account = [self p_selectedAccount];
952        [account setTargetStatus:[account status]];
953}
954
955
956#pragma mark MSN Registration Sheet
957
958
959- (IBAction)okRegisterMSN:(id)sender
960{
961        [NSApp endSheet:m_msnRegistrationSheet returnCode:NSOKButton];
962}
963
964
965- (IBAction)cancelRegisterMSN:(id)sender
966{
967        [NSApp endSheet:m_msnRegistrationSheet returnCode:NSCancelButton];
968}
969
970
971#pragma mark -
972#pragma mark Actions - Advanced Prefs
973
974
975- (NSArray *)appcastFeeds
976{
977        return [NSArray arrayWithObjects:
978                [NSDictionary dictionaryWithObjectsAndKeys:
979                        NSLocalizedString(@"Stable Releases", @"auto-update feed selection"),
980                        @"Label",
981                        @"http://messenger.sapo.pt/software_update/mac/feeds/sapomsgmac_stable.xml",
982                        @"AutoupdateURL",
983                        nil],
984                [NSDictionary dictionaryWithObjectsAndKeys:
985                        NSLocalizedString(@"Beta Releases", @"auto-update feed selection"),
986                        @"Label",
987                        @"http://messenger.sapo.pt/software_update/mac/feeds/sapomsgmac_beta.xml",
988                        @"AutoupdateURL",
989                        nil],
990                [NSDictionary dictionaryWithObjectsAndKeys:
991                        NSLocalizedString(@"Nightly Builds", @"auto-update feed selection"),
992                        @"Label",
993                        @"http://messenger.sapo.pt/software_update/mac/nightly_builds/appcast_feed.xml",
994                        @"AutoupdateURL",
995                        nil],
996                nil];
997}
998
999
1000#pragma mark -
1001#pragma mark Account Notifications
1002
1003
1004- (void)accountDidChangeTransportInfo:(NSNotification *)notif
1005{
1006        NSString *transportAgent = [[notif userInfo] objectForKey:@"TransportAgent"];
1007        [self p_updateGUIForTransportAgent:transportAgent ofAccount:[notif object]];
1008}
1009
1010
1011#pragma mark -
1012#pragma mark NSControl Delegate (text fields)
1013
1014
1015- (void)controlTextDidChange:(NSNotification *)aNotification
1016{
1017        NSTextField *textField = [aNotification object];
1018       
1019        if (textField == m_accountJIDField) {
1020                NSString *jid = [textField stringValue];
1021                NSString *username = [jid JIDUsernameComponent];
1022               
1023                if ([username length] > 0) {
1024                        NSString *hostname = [jid JIDHostnameComponent];
1025                        NSString *expectedHostname = nil;
1026                       
1027                        switch ([self p_selectedAccountKind]) {
1028                                case LPPrefsSAPOAccountKind:        expectedHostname = @"sapo.pt";         break;
1029                                case LPPrefsNetcaboAccountKind:     expectedHostname = @"netcabo.pt";      break;
1030                                case LPPrefsTelepacAccountKind:     expectedHostname = @"mail.telepac.pt"; break;
1031                                case LPPrefsOtherJabberAccountKind: expectedHostname = nil;                break;
1032                        }
1033                       
1034                        if ([expectedHostname length] == 0 || ![hostname isEqualToString:expectedHostname]) {
1035                                [self p_syncAccountViewsWithJID:jid];
1036                        }
1037                }
1038        }
1039        else if (textField == m_msnEmailField || textField == m_msnPasswordField) {
1040                [m_msnRegisterOKButton setEnabled:( ( [[m_msnEmailField stringValue] length] > 0 ) &&
1041                                                                                    ( [[m_msnPasswordField stringValue] length] > 0 ) )];
1042        }
1043}
1044
1045
1046- (void)controlTextDidEndEditing:(NSNotification *)aNotification
1047{
1048        NSTextField *textField = [aNotification object];
1049       
1050        if (textField == m_accountJIDField) {
1051                [self p_syncJIDWithAccountViews];
1052        }
1053}
1054
1055
1056@end
Note: See TracBrowser for help on using the browser.