Put NSAutoreleasePool usage around other distributed notification observer methods
[adiumx.git] / Source / AdiumSetupWizard.m
blob0770592e51ad21a98d4a2601189280e635088747
1 //
2 //  AdiumSetupWizard.m
3 //  Adium
4 //
5 //  Created by Evan Schoenberg on 12/4/05.
6 //
8 #import "AdiumSetupWizard.h"
9 #import <Adium/AIAccountControllerProtocol.h>
10 #import <Adium/AIContentControllerProtocol.h>
11 #import "SetupWizardBackgroundView.h"
12 #import "GBFireImporter.h"
13 #import <AIUtilities/AIImageAdditions.h>
14 #import "AIServiceMenu.h"
15 #import <Adium/AIService.h>
16 #import <Adium/AIAccount.h>
17 #import <AIUtilities/AITextFieldAdditions.h>
18 #import <AIUtilities/AIStringFormatter.h>
19 #import <AIUtilities/AIFileManagerAdditions.h>
20 #import "AIHTMLDecoder.h"
22 #define ACCOUNT_SETUP_IDENTIFIER        @"account_setup"
23 #define IMPORT_IDENTIFIER                       @"import_client"
24 #define WELCOME_IDENTIFIER                      @"welcome"
25 #define DONE_IDENTIFIER                         @"done"
27 enum{
28         WIZARD_TAB_WELCOME = 0,
29         WIZARD_TAB_FIREIMPORT = 1,
30         WIZARD_TAB_ADD_ACCOUNTS = 2,
31         WIZARD_TAB_DONE = 3
34 /*!
35  * @classs AdiumSetupWizard
36  * @brief Class responsible for the first-run setup wizard
37  */
38 @implementation AdiumSetupWizard
40 /*!
41  * @brief Run the wizard
42  */
43 + (void)runWizard
45         AdiumSetupWizard *setupWizardWindowController;
46         
47         setupWizardWindowController = [[self alloc] initWithWindowNibName:@"SetupWizard"];
48         
49         //Configure and show window
50         [setupWizardWindowController showWindow:nil];
51         [[setupWizardWindowController window] orderFront:nil];
54 /*!
55  * @brief Localized some common items' titles
56  */
57 - (void)localizeItems
59         [button_goBack setLocalizedString:AILocalizedString(@"Go Back","'go back' button title")];
60         [textField_passwordLabel setLocalizedString:AILocalizedString(@"Password:", "Label for the password field in the account preferences")];
61         [textField_serviceLabel setLocalizedString:AILocalizedString(@"Service:",nil)];
62         
63         [button_alternate setLocalizedString:AILocalizedString(@"Skip Import","button title for skipping the import of another client in the setup wizard")];
66 /*!
67  * @brief The window loaded
68  */
69 - (void)windowDidLoad
71         [[self window] setTitle:AILocalizedString(@"Adium Setup Assistant",nil)];
73         //Ensure the first tab view item is selected
74         [tabView selectTabViewItemAtIndex:WIZARD_TAB_WELCOME];
75         [self tabView:tabView willSelectTabViewItem:[tabView selectedTabViewItem]];
77         //Configure our background view; it should display the image transparently where our tabView overlaps it
78         [backgroundView setBackgroundImage:[NSImage imageNamed:@"AdiumyButler"
79                                                                                                   forClass:[self class]]];
80         NSRect tabViewFrame = [tabView frame];
81         NSRect backgroundViewFrame = [backgroundView frame];
82         tabViewFrame.origin.x -= backgroundViewFrame.origin.x;
83         tabViewFrame.origin.y -= backgroundViewFrame.origin.y;
84         [backgroundView setTransparentRect:tabViewFrame];
86         //Check to see if we can import Fire's settings
87         NSFileManager *fileManager = [NSFileManager defaultManager];
88         NSString *fireDir = [[fileManager userApplicationSupportFolder] stringByAppendingPathComponent:@"Fire"];
89         BOOL isDir = NO;
90         if([fileManager fileExistsAtPath:fireDir isDirectory:&isDir] && isDir)
91                 canImport = YES;
92         if([progress_processing respondsToSelector:@selector(setHidden:)])
93                 [progress_processing setHidden:YES];
94         
95         [self localizeItems];
96         
97         [[self window] center];
99         [super windowDidLoad];
102 - (IBAction)promptForMultiples:(id)sender
104         // Since we have multiple dedicated importers in 1.1+ it's better to direct the user as needed
105         NSAlert *multipleImportPrompt = [NSAlert alertWithMessageText:AILocalizedString(@"Have you used other chat clients?", "Title which introduces import assistants during setup")
106                                                                                                         defaultButton:AILocalizedStringFromTable(@"Continue", @"Buttons", nil)
107                                                                                                   alternateButton:AILocalizedString(@"Import from Fire", "Fire is another OS X instant messaging client; the name probably should not be localized")
108                                                                                                           otherButton:AILocalizedString(@"Import from iChat", "iChat is the OS X instant messaging client which ships with OS X; the name probably should not be localized")
109                                                                                 informativeTextWithFormat:AILocalizedString(@"Adium includes assistants to import your accounts, settings, and transcripts from other clients. Choose a client below to open its assistant, or press Continue to skip importing.", nil)];
110         [multipleImportPrompt beginSheetModalForWindow:[self window] 
111                                                                          modalDelegate:self 
112                                                                         didEndSelector:@selector(multipleImportAlertDidEnd:returnCode:contextInfo:) 
113                                                                            contextInfo:nil];    
116 - (void)multipleImportAlertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo
118         if(returnCode == NSAlertOtherReturn)
119         {
120                 [adium performSelector:@selector(importFromiChat:)
121                                         withObject:nil
122                                         afterDelay:0.5];
123                 [[self window] close];
124         }
125         if(returnCode == NSAlertAlternateReturn)
126         {
127                 // not yet supported, but should open the manual Fire importer
128         }
132  * @brief Perform behaviors before the window closes
134  * As our window is closing, we auto-release this window controller instance.
135  */
136 - (void)windowWillClose:(id)sender
138         [super windowWillClose:sender];
139         
140         [self autorelease];
144  * @brief Start the progress indicator to let the user we are processing
145  */
146 - (void)activateProgressIndicator
148         [progress_processing setHidden:NO];
149         
150         //start the progress spinner (using multi-threading)
151         [progress_processing setUsesThreadedAnimation:YES];
152         [progress_processing startAnimation:nil];
156  * @brief A tab view item was completed; post-process any entered data
157  */
158 - (BOOL)didCompleteTabViewItemWithIdentifier:(NSString *)identifier
160         BOOL success = YES;
162         if ([identifier isEqualToString:ACCOUNT_SETUP_IDENTIFIER]) {
163                 NSString        *UID = [textField_username stringValue];
165                 if (UID && [UID length]) {
166                         AIService       *service = [[popUp_services selectedItem] representedObject];
167                         AIAccount       *account = [[adium accountController] createAccountWithService:service
168                                                                                                                                                                    UID:UID];
169                         
170                         //Save the password
171                         NSString                *password = [textField_password secureStringValue];
172                         
173                         if (password && [password length] != 0) {
174                                 [[adium accountController] setPassword:password forAccount:account];
175                         }
176                         AILog(@"AdiumSetupWizard: Creating account %@ on service %@",account,service);
177                         //New accounts need to be added to our account list once they're configured
178                         [[adium accountController] addAccount:account];
179                         
180                         //Put new accounts online by default
181                         [account setShouldBeOnline:YES];
182                         
183                         addedAnAccount = YES;
185                 } else {
186                         //Successful without having a UID entered if they already added at least one account; unsuccessful otherwise.
187                         success = addedAnAccount;
188                 }
189         } else if ([identifier isEqualToString:IMPORT_IDENTIFIER]) {
190                 [self activateProgressIndicator];
191                 success = [GBFireImporter importFireConfiguration];
192                 addedAnAccount = [[[adium accountController] accounts] count] != 0;
193                 [progress_processing stopAnimation:nil];
194                 [progress_processing setHidden:YES];
195         }
196         
197         return success;
201  * @brief The Continue button, which is also the Done button, was pressed
202  */
203 - (IBAction)nextTab:(id)sender
205         NSTabViewItem *currentTabViewItem = [tabView selectedTabViewItem];
206         if ([self didCompleteTabViewItemWithIdentifier:[currentTabViewItem identifier]]) {
207                 if ([tabView indexOfTabViewItem:currentTabViewItem] == WIZARD_TAB_DONE) {
208                         //Done
209                         [self  close];
210                         
211                 } else if ([[currentTabViewItem identifier] isEqualToString:WELCOME_IDENTIFIER]) {
212                         if(canImport)
213                                 //We can import; go to next tab
214                                 [tabView selectNextTabViewItem:self];
215                         else
216                                 //No import; skip it
217                                 [tabView selectTabViewItemAtIndex:WIZARD_TAB_FIREIMPORT + 1];
218                         
219                 } else {
220                         //Go to the next tab view item
221                         [tabView selectNextTabViewItem:self];           
222                 }
223         } else {
224                 NSBeep();
225         }
229  * @brief The Back button was pressed
230  */
231 - (IBAction)previousTab:(id)sender
233         NSTabViewItem *currentTabViewItem = [tabView selectedTabViewItem];
234         if([[currentTabViewItem identifier] isEqualToString:ACCOUNT_SETUP_IDENTIFIER] && !canImport)
235                 [tabView selectTabViewItemAtIndex:WIZARD_TAB_FIREIMPORT - 1];
236         else
237                 [tabView selectPreviousTabViewItem:self];
241  * @brief The alternate (third) button was pressed; its behavior will vary by tab view item
242  */
243 - (IBAction)pressedAlternateButton:(id)sender
245         NSTabViewItem   *currentTabViewItem = [tabView selectedTabViewItem];
246         NSString                *identifier = [currentTabViewItem identifier];
248         if ([identifier isEqualToString:ACCOUNT_SETUP_IDENTIFIER]) {
249                 //Configure the account
250                 if ([self didCompleteTabViewItemWithIdentifier:identifier]) {
251                         //Reconfigure
252                         [self tabView:tabView willSelectTabViewItem:currentTabViewItem];
253                 } else {
254                         NSBeep();
255                 }
256         } else if ([identifier isEqualToString:IMPORT_IDENTIFIER]) {
257                 //skip the import
258                 [tabView selectNextTabViewItem:self];
259         }
263  * @brief Set up the Account Setup tab for a given service
264  */
265 - (void)configureAccountSetupForService:(AIService *)service
267         //UID Label
268         [textField_usernameLabel setStringValue:[[service userNameLabel] stringByAppendingString:AILocalizedString(@":", "Colon which will be appended after a label such as 'User Name', before an input field")]];
270         //UID formatter and placeholder
271         [textField_username setFormatter:
272                 [AIStringFormatter stringFormatterAllowingCharacters:[service allowedCharactersForAccountName]
273                                                                                                           length:[service allowedLengthForAccountName]
274                                                                                            caseSensitive:[service caseSensitive]
275                                                                                                 errorMessage:AILocalizedString(@"The characters you're entering are not valid for an account name on this service.", nil)]];
276         [[textField_username cell] setPlaceholderString:[service UIDPlaceholder]];
277         
278         BOOL showPasswordField = ![service supportsPassword];
279         [textField_passwordLabel setHidden:showPasswordField];
280         [textField_password setHidden:showPasswordField];
283 - (BOOL)showAlternateButtonForIdentifier:(NSString *)identifier
285         return [identifier isEqualToString:ACCOUNT_SETUP_IDENTIFIER] || [identifier isEqualToString:IMPORT_IDENTIFIER]; 
289  * @brief The tab view is about to select a tab view item
290  */
291 - (void)tabView:(NSTabView *)inTabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem
293         NSString *identifier = [tabViewItem identifier];
295         //The continue button is only initially enabled if the user has added at least one account
296         [button_continue setEnabled:YES];
298         if ([identifier isEqualToString:ACCOUNT_SETUP_IDENTIFIER]) {
299                 //Set the services menu if it hasn't already been set
300                 if (!setupAccountTabViewItem) {
301                         [popUp_services setMenu:[AIServiceMenu menuOfServicesWithTarget:self
302                                                                                                                  activeServicesOnly:NO
303                                                                                                                         longDescription:YES
304                                                                                                                                          format:nil]];
305                         
306                         [textField_addAccount setStringValue:AILocalizedString(@"Add an Instant Messaging Account",nil)];
307                         [textView_addAccountMessage setDrawsBackground:NO];
308                         [[textView_addAccountMessage enclosingScrollView] setDrawsBackground:NO];
309                         
310                         NSAttributedString *accountMessage = [AIHTMLDecoder decodeHTML:
311                                 AILocalizedString(@"<HTML>To chat with your friends, family, and coworkers, you must have an instant messaging account on the same service they do. Choose a service, name, and password below; if you don't have an account yet, click <A HREF=\"http://trac.adiumx.com/wiki/CreatingAnAccount#Sigingupforanaccount\">here</A> for more information.\n\nAdium supports as many accounts as you want to add; you can always add more in the Accounts pane of the Adium Preferences.</HTML>", nil)
312                                                                                                          withDefaultAttributes:[[textView_addAccountMessage textStorage] attributesAtIndex:0
313                                                                                                                                                                                                                                                 effectiveRange:NULL]];
314                         [[textView_addAccountMessage textStorage] setAttributedString:accountMessage];
315                         setupAccountTabViewItem = YES;
316                 }
318                 AIService *service = [[popUp_services selectedItem] representedObject];
319                 [textField_username setStringValue:@""];
320                 [[self window] makeFirstResponder:textField_username];
322                 [textField_password setStringValue:@""];
324                 //The continue button is only initially enabled if the user has added at least one account
325                 [button_continue setEnabled:addedAnAccount];
326                 [button_alternate setLocalizedString:AILocalizedString(@"Add Another","button title for adding another account in the setup wizard")];
327                 [button_alternate setEnabled:NO];
329                 [self configureAccountSetupForService:service];
331         } else if ([identifier isEqualToString:IMPORT_IDENTIFIER]) {
332                 [textField_import setStringValue:AILocalizedString(@"Import Fire's Settings", nil)];
333                 [textView_importMessage setDrawsBackground:NO];
334                 [[textView_importMessage enclosingScrollView] setDrawsBackground:NO];
336                 NSAttributedString *importMessage = [AIHTMLDecoder decodeHTML:
337                         AILocalizedString(@"<HTML>Adium has detected that you have previously used Fire.  You may choose to import settings from Fire by pressing continue below.  If you choose to not use these settings, simply skip this step.<HTML>",nil)
338                                                                                                 withDefaultAttributes:[[textView_importMessage textStorage] attributesAtIndex:0
339                                                                                                                                                                                                                            effectiveRange:NULL]];
340                 [[textView_importMessage textStorage] setAttributedString:importMessage];
341                 [button_alternate setLocalizedString:AILocalizedString(@"Skip Import","button title for skipping the import of another client in the setup wizard")];
342                 [button_alternate setEnabled:YES];
343                 
344         } else if ([identifier isEqualToString:WELCOME_IDENTIFIER]) {
345                 [textView_welcomeMessage setDrawsBackground:NO];
346                 [[textView_welcomeMessage enclosingScrollView] setDrawsBackground:NO];
347                 NSAttributedString *welcomeMessage = [AIHTMLDecoder decodeHTML:
348                         AILocalizedString(@"<HTML>Adium is <i>your</i> instant messaging solution.<br><br>Chat with whomever you want, whenever you want, however you want.  Multiple messaging services or accounts? Just one account? Work? Play? Both? No problem; Adium has you covered.<br><br>Adium is fast, free, and fun, with an interface you'll love to use day in and day out. :)<br><br>This assistant will help you set up your instant messaging accounts and get started chatting.<br><br>Click <b>Continue</b> and the duck will take it from here.</HTML>",nil)
349                                                                                                  withDefaultAttributes:[[textView_addAccountMessage textStorage] attributesAtIndex:0
350                                                                                                                                                                                                                                         effectiveRange:NULL]];
351                 //Turn that smiley into an emoticon :)
352                 welcomeMessage = [[adium contentController] filterAttributedString:welcomeMessage
353                                                                                                                    usingFilterType:AIFilterDisplay
354                                                                                                                                  direction:AIFilterIncoming
355                                                                                                                                    context:nil];
356                 [[textView_welcomeMessage textStorage] setAttributedString:welcomeMessage];
358                 [textField_welcome setStringValue:AILocalizedString(@"Welcome to Adium!",nil)];
359                 
360         } else if ([identifier isEqualToString:DONE_IDENTIFIER]) {
361                 [textView_doneMessage setDrawsBackground:NO];
362                 [[textView_doneMessage enclosingScrollView] setDrawsBackground:NO];
363                 [textView_doneMessage setString:AILocalizedString(@"Adium is now ready for you. \n\nThe Status indicator at the top of your Contact List and in the Status menu lets you determine whether others see you as Available or Away or, alternately, if you are Offline. Select Custom to type your own status message.\n\nDouble-click a name in your Contact List to begin a conversation.  You can add contacts to your Contact List via the Contact menu.\n\nWant to customize your Adium experience? Check out the Adium Preferences and Xtras Manager via the Adium menu.\n\nEnjoy! Click Done to begin using Adium.", nil)],
365                 [textField_done setStringValue:AILocalizedString(@"Congratulations!","Header line in the last pane of the Adium setup wizard")];
366         }
368         //Hide go back on the first tab
369         [button_goBack setEnabled:([tabView indexOfTabViewItem:tabViewItem] != WIZARD_TAB_WELCOME)];
370         
371         [button_alternate setHidden:![self showAlternateButtonForIdentifier:identifier]];
373         //Set the done / continue button properly
374         if ([tabView indexOfTabViewItem:tabViewItem] == WIZARD_TAB_DONE) {
375                 [button_continue setLocalizedString:AILocalizedString(@"Done","'done' button title")];
377         } else {
378                 [button_continue setLocalizedString:AILocalizedString(@"Continue","'done' button title")];
379         }
383  * @brief The selected service in the account configuration tab view item was changed
384  */
385 - (void)selectServiceType:(id)sender
387         [self configureAccountSetupForService:[[popUp_services selectedItem] representedObject]];
390 - (void)controlTextDidChange:(NSNotification *)aNotification
392         if ([aNotification object] == textField_username) {
393                 BOOL shouldEnable = ([[textField_username stringValue] length] > 0);
394                 //Allow continuing if they have typed something or they already added an account
395                 [button_continue setEnabled:(shouldEnable || addedAnAccount)];
397                 //Allow adding another only if they have typed something
398                 [button_alternate setEnabled:shouldEnable];
399                 
400         }
403 @end