5 // Created by Evan Schoenberg on 12/4/05.
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"
28 WIZARD_TAB_WELCOME = 0,
29 WIZARD_TAB_FIREIMPORT = 1,
30 WIZARD_TAB_ADD_ACCOUNTS = 2,
35 * @classs AdiumSetupWizard
36 * @brief Class responsible for the first-run setup wizard
38 @implementation AdiumSetupWizard
41 * @brief Run the wizard
45 AdiumSetupWizard *setupWizardWindowController;
47 setupWizardWindowController = [[self alloc] initWithWindowNibName:@"SetupWizard"];
49 //Configure and show window
50 [setupWizardWindowController showWindow:nil];
51 [[setupWizardWindowController window] orderFront:nil];
55 * @brief Localized some common items' titles
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)];
63 [button_alternate setLocalizedString:AILocalizedString(@"Skip Import","button title for skipping the import of another client in the setup wizard")];
67 * @brief The window loaded
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"];
90 if([fileManager fileExistsAtPath:fireDir isDirectory:&isDir] && isDir)
92 if([progress_processing respondsToSelector:@selector(setHidden:)])
93 [progress_processing setHidden:YES];
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.
107 - (void)windowWillClose:(id)sender
109 [super windowWillClose:sender];
115 * @brief Start the progress indicator to let the user we are processing
117 - (void)activateProgressIndicator
119 [progress_processing setHidden:NO];
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
129 - (BOOL)didCompleteTabViewItemWithIdentifier:(NSString *)identifier
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
142 NSString *password = [textField_password secureStringValue];
144 if (password && [password length] != 0) {
145 [[adium accountController] setPassword:password forAccount:account];
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];
151 //Put new accounts online by default
152 [account setShouldBeOnline:YES];
154 addedAnAccount = YES;
157 //Successful without having a UID entered if they already added at least one account; unsuccessful otherwise.
158 success = addedAnAccount;
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];
172 * @brief The Continue button, which is also the Done button, was pressed
174 - (IBAction)nextTab:(id)sender
176 NSTabViewItem *currentTabViewItem = [tabView selectedTabViewItem];
177 if ([self didCompleteTabViewItemWithIdentifier:[currentTabViewItem identifier]]) {
178 if ([tabView indexOfTabViewItem:currentTabViewItem] == WIZARD_TAB_DONE) {
182 } else if ([[currentTabViewItem identifier] isEqualToString:WELCOME_IDENTIFIER]) {
184 //We can import; go to next tab
185 [tabView selectNextTabViewItem:self];
188 [tabView selectTabViewItemAtIndex:WIZARD_TAB_FIREIMPORT + 1];
191 //Go to the next tab view item
192 [tabView selectNextTabViewItem:self];
200 * @brief The Back button was pressed
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];
208 [tabView selectPreviousTabViewItem:self];
212 * @brief The alternate (third) button was pressed; its behavior will vary by tab view item
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]) {
223 [self tabView:tabView willSelectTabViewItem:currentTabViewItem];
227 } else if ([identifier isEqualToString:IMPORT_IDENTIFIER]) {
229 [tabView selectNextTabViewItem:self];
234 * @brief Set up the Account Setup tab for a given service
236 - (void)configureAccountSetupForService:(AIService *)service
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]];
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
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
277 [textField_addAccount setStringValue:AILocalizedString(@"Add an Instant Messaging Account",nil)];
278 [textView_addAccountMessage setDrawsBackground:NO];
279 [[textView_addAccountMessage enclosingScrollView] setDrawsBackground:NO];
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;
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];
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
327 [[textView_welcomeMessage textStorage] setAttributedString:welcomeMessage];
329 [textField_welcome setStringValue:AILocalizedString(@"Welcome to Adium!",nil)];
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")];
339 //Hide go back on the first tab
340 [button_goBack setEnabled:([tabView indexOfTabViewItem:tabViewItem] != WIZARD_TAB_WELCOME)];
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")];
349 [button_continue setLocalizedString:AILocalizedString(@"Continue","'done' button title")];
354 * @brief The selected service in the account configuration tab view item was changed
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];