{{{
[adiumx.git] / Source / AdiumSetupWizard.m
blobfcd6e30def5ee7591bcb15d6839909c7e36fe5b1
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];
103  * @brief Perform behaviors before the window closes
105  * As our window is closing, we auto-release this window controller instance.
106  */
107 - (void)windowWillClose:(id)sender
109         [super windowWillClose:sender];
110         
111         [self autorelease];
115  * @brief Start the progress indicator to let the user we are processing
116  */
117 - (void)activateProgressIndicator
119         [progress_processing setHidden:NO];
120         
121         //start the progress spinner (using multi-threading)
122         [progress_processing setUsesThreadedAnimation:YES];
123         [progress_processing startAnimation:nil];
127  * @brief A tab view item was completed; post-process any entered data
128  */
129 - (BOOL)didCompleteTabViewItemWithIdentifier:(NSString *)identifier
131         BOOL success = YES;
133         if ([identifier isEqualToString:ACCOUNT_SETUP_IDENTIFIER]) {
134                 NSString        *UID = [textField_username stringValue];
136                 if (UID && [UID length]) {
137                         AIService       *service = [[popUp_services selectedItem] representedObject];
138                         AIAccount       *account = [[adium accountController] createAccountWithService:service
139                                                                                                                                                                    UID:UID];
140                         
141                         //Save the password
142                         NSString                *password = [textField_password secureStringValue];
143                         
144                         if (password && [password length] != 0) {
145                                 [[adium accountController] setPassword:password forAccount:account];
146                         }
147                         AILog(@"AdiumSetupWizard: Creating account %@ on service %@",account,service);
148                         //New accounts need to be added to our account list once they're configured
149                         [[adium accountController] addAccount:account];
150                         
151                         //Put new accounts online by default
152                         [account setShouldBeOnline:YES];
153                         
154                         addedAnAccount = YES;
156                 } else {
157                         //Successful without having a UID entered if they already added at least one account; unsuccessful otherwise.
158                         success = addedAnAccount;
159                 }
160         } else if ([identifier isEqualToString:IMPORT_IDENTIFIER]) {
161                 [self activateProgressIndicator];
162                 success = [GBFireImporter importFireConfiguration];
163                 addedAnAccount = [[[adium accountController] accounts] count] != 0;
164                 [progress_processing stopAnimation:nil];
165                 [progress_processing setHidden:YES];
166         }
167         
168         return success;
172  * @brief The Continue button, which is also the Done button, was pressed
173  */
174 - (IBAction)nextTab:(id)sender
176         NSTabViewItem *currentTabViewItem = [tabView selectedTabViewItem];
177         if ([self didCompleteTabViewItemWithIdentifier:[currentTabViewItem identifier]]) {
178                 if ([tabView indexOfTabViewItem:currentTabViewItem] == WIZARD_TAB_DONE) {
179                         //Done
180                         [self  close];
181                         
182                 } else if ([[currentTabViewItem identifier] isEqualToString:WELCOME_IDENTIFIER]) {
183                         if(canImport)
184                                 //We can import; go to next tab
185                                 [tabView selectNextTabViewItem:self];
186                         else
187                                 //No import; skip it
188                                 [tabView selectTabViewItemAtIndex:WIZARD_TAB_FIREIMPORT + 1];
189                         
190                 } else {
191                         //Go to the next tab view item
192                         [tabView selectNextTabViewItem:self];           
193                 }
194         } else {
195                 NSBeep();
196         }
200  * @brief The Back button was pressed
201  */
202 - (IBAction)previousTab:(id)sender
204         NSTabViewItem *currentTabViewItem = [tabView selectedTabViewItem];
205         if([[currentTabViewItem identifier] isEqualToString:ACCOUNT_SETUP_IDENTIFIER] && !canImport)
206                 [tabView selectTabViewItemAtIndex:WIZARD_TAB_FIREIMPORT - 1];
207         else
208                 [tabView selectPreviousTabViewItem:self];
212  * @brief The alternate (third) button was pressed; its behavior will vary by tab view item
213  */
214 - (IBAction)pressedAlternateButton:(id)sender
216         NSTabViewItem   *currentTabViewItem = [tabView selectedTabViewItem];
217         NSString                *identifier = [currentTabViewItem identifier];
219         if ([identifier isEqualToString:ACCOUNT_SETUP_IDENTIFIER]) {
220                 //Configure the account
221                 if ([self didCompleteTabViewItemWithIdentifier:identifier]) {
222                         //Reconfigure
223                         [self tabView:tabView willSelectTabViewItem:currentTabViewItem];
224                 } else {
225                         NSBeep();
226                 }
227         } else if ([identifier isEqualToString:IMPORT_IDENTIFIER]) {
228                 //skip the import
229                 [tabView selectNextTabViewItem:self];
230         }
234  * @brief Set up the Account Setup tab for a given service
235  */
236 - (void)configureAccountSetupForService:(AIService *)service
238         //UID Label
239         [textField_usernameLabel setStringValue:[[service userNameLabel] stringByAppendingString:@":"]];
241         //UID formatter and placeholder
242         [textField_username setFormatter:
243                 [AIStringFormatter stringFormatterAllowingCharacters:[service allowedCharactersForAccountName]
244                                                                                                           length:[service allowedLengthForAccountName]
245                                                                                            caseSensitive:[service caseSensitive]
246                                                                                                 errorMessage:AILocalizedString(@"The characters you're entering are not valid for an account name on this service.", nil)]];
247         [[textField_username cell] setPlaceholderString:[service UIDPlaceholder]];
248         
249         BOOL showPasswordField = ![service requiresPassword];
250         [textField_passwordLabel setHidden:showPasswordField];
251         [textField_password setHidden:showPasswordField];
254 - (BOOL)showAlternateButtonForIdentifier:(NSString *)identifier
256         return [identifier isEqualToString:ACCOUNT_SETUP_IDENTIFIER] || [identifier isEqualToString:IMPORT_IDENTIFIER]; 
260  * @brief The tab view is about to select a tab view item
261  */
262 - (void)tabView:(NSTabView *)inTabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem
264         NSString *identifier = [tabViewItem identifier];
266         //The continue button is only initially enabled if the user has added at least one account
267         [button_continue setEnabled:YES];
269         if ([identifier isEqualToString:ACCOUNT_SETUP_IDENTIFIER]) {
270                 //Set the services menu if it hasn't already been set
271                 if (!setupAccountTabViewItem) {
272                         [popUp_services setMenu:[AIServiceMenu menuOfServicesWithTarget:self
273                                                                                                                  activeServicesOnly:NO
274                                                                                                                         longDescription:YES
275                                                                                                                                          format:nil]];
276                         
277                         [textField_addAccount setStringValue:AILocalizedString(@"Add an Instant Messaging Account",nil)];
278                         [textView_addAccountMessage setDrawsBackground:NO];
279                         [[textView_addAccountMessage enclosingScrollView] setDrawsBackground:NO];
280                         
281                         NSAttributedString *accountMessage = [AIHTMLDecoder decodeHTML:
282                                 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)
283                                                                                                          withDefaultAttributes:[[textView_addAccountMessage textStorage] attributesAtIndex:0
284                                                                                                                                                                                                                                                 effectiveRange:NULL]];
285                         [[textView_addAccountMessage textStorage] setAttributedString:accountMessage];
286                         setupAccountTabViewItem = YES;
287                 }
289                 AIService *service = [[popUp_services selectedItem] representedObject];
290                 [textField_username setStringValue:@""];
291                 [[self window] makeFirstResponder:textField_username];
293                 [textField_password setStringValue:@""];
295                 //The continue button is only initially enabled if the user has added at least one account
296                 [button_continue setEnabled:addedAnAccount];
297                 [button_alternate setLocalizedString:AILocalizedString(@"Add Another","button title for adding another account in the setup wizard")];
298                 [button_alternate setEnabled:NO];
300                 [self configureAccountSetupForService:service];
302         } else if ([identifier isEqualToString:IMPORT_IDENTIFIER]) {
303                 [textField_import setStringValue:AILocalizedString(@"Import Fire's Settings", nil)];
304                 [textView_importMessage setDrawsBackground:NO];
305                 [[textView_importMessage enclosingScrollView] setDrawsBackground:NO];
307                 NSAttributedString *importMessage = [AIHTMLDecoder decodeHTML:
308                         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)
309                                                                                                 withDefaultAttributes:[[textView_importMessage textStorage] attributesAtIndex:0
310                                                                                                                                                                                                                            effectiveRange:NULL]];
311                 [[textView_importMessage textStorage] setAttributedString:importMessage];
312                 [button_alternate setLocalizedString:AILocalizedString(@"Skip Import","button title for skipping the import of another client in the setup wizard")];
313                 [button_alternate setEnabled:YES];
314                 
315         } else if ([identifier isEqualToString:WELCOME_IDENTIFIER]) {
316                 [textView_welcomeMessage setDrawsBackground:NO];
317                 [[textView_welcomeMessage enclosingScrollView] setDrawsBackground:NO];
318                 NSAttributedString *welcomeMessage = [AIHTMLDecoder decodeHTML:
319                         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)
320                                                                                                  withDefaultAttributes:[[textView_addAccountMessage textStorage] attributesAtIndex:0
321                                                                                                                                                                                                                                         effectiveRange:NULL]];
322                 //Turn that smiley into an emoticon :)
323                 welcomeMessage = [[adium contentController] filterAttributedString:welcomeMessage
324                                                                                                                    usingFilterType:AIFilterDisplay
325                                                                                                                                  direction:AIFilterIncoming
326                                                                                                                                    context:nil];
327                 [[textView_welcomeMessage textStorage] setAttributedString:welcomeMessage];
329                 [textField_welcome setStringValue:AILocalizedString(@"Welcome to Adium!",nil)];
330                 
331         } else if ([identifier isEqualToString:DONE_IDENTIFIER]) {
332                 [textView_doneMessage setDrawsBackground:NO];
333                 [[textView_doneMessage enclosingScrollView] setDrawsBackground:NO];
334                 [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)],
336                 [textField_done setStringValue:AILocalizedString(@"Congratulations!","Header line in the last pane of the Adium setup wizard")];
337         }
339         //Hide go back on the first tab
340         [button_goBack setEnabled:([tabView indexOfTabViewItem:tabViewItem] != WIZARD_TAB_WELCOME)];
341         
342         [button_alternate setHidden:![self showAlternateButtonForIdentifier:identifier]];
344         //Set the done / continue button properly
345         if ([tabView indexOfTabViewItem:tabViewItem] == WIZARD_TAB_DONE) {
346                 [button_continue setLocalizedString:AILocalizedString(@"Done","'done' button title")];
348         } else {
349                 [button_continue setLocalizedString:AILocalizedString(@"Continue","'done' button title")];
350         }
354  * @brief The selected service in the account configuration tab view item was changed
355  */
356 - (void)selectServiceType:(id)sender
358         [self configureAccountSetupForService:[[popUp_services selectedItem] representedObject]];
361 - (void)controlTextDidChange:(NSNotification *)aNotification
363         if ([aNotification object] == textField_username) {
364                 BOOL shouldEnable = ([[textField_username stringValue] length] > 0);
365                 //Allow continuing if they have typed something or they already added an account
366                 [button_continue setEnabled:(shouldEnable || addedAnAccount)];
368                 //Allow adding another only if they have typed something
369                 [button_alternate setEnabled:shouldEnable];
370                 
371         }
374 @end