| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "chrome/browser/autofill/autofill_dialog_controller_mac.h" |
| #include "app/l10n_util.h" |
| #include "app/resource_bundle.h" |
| #include "base/mac_util.h" |
| #include "base/sys_string_conversions.h" |
| #include "chrome/browser/browser.h" |
| #include "chrome/browser/browser_list.h" |
| #import "chrome/browser/autofill/autofill_address_model_mac.h" |
| #import "chrome/browser/autofill/autofill_address_sheet_controller_mac.h" |
| #import "chrome/browser/autofill/autofill_credit_card_model_mac.h" |
| #import "chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.h" |
| #import "chrome/browser/autofill/personal_data_manager.h" |
| #include "chrome/browser/browser_process.h" |
| #import "chrome/browser/cocoa/window_size_autosaver.h" |
| #include "chrome/browser/pref_service.h" |
| #include "chrome/browser/profile.h" |
| #include "chrome/common/pref_names.h" |
| #include "grit/generated_resources.h" |
| #include "grit/app_resources.h" |
| #include "grit/theme_resources.h" |
| |
| namespace { |
| |
| // Update profile labels passed as |input|. When profile data changes as a |
| // result of adding new profiles, edititing existing profiles, or deleting a |
| // profile, then the list of profiles need to have their derived labels |
| // recomputed. |
| void UpdateProfileLabels(std::vector<AutoFillProfile>* input) { |
| DCHECK(input); |
| std::vector<AutoFillProfile*> profiles; |
| profiles.resize(input->size()); |
| for (size_t i = 0; i < input->size(); ++i) { |
| profiles[i] = &(*input)[i]; |
| } |
| AutoFillProfile::AdjustInferredLabels(&profiles); |
| } |
| |
| } // namespace |
| |
| // Delegate protocol that needs to be in place for the AutoFillTableView's |
| // handling of delete and backspace keys. |
| @protocol DeleteKeyDelegate |
| - (IBAction)deleteSelection:(id)sender; |
| @end |
| |
| // A subclass of NSTableView that allows for deleting selected elements using |
| // the delete or backspace keys. |
| @interface AutoFillTableView : NSTableView { |
| } |
| @end |
| |
| @implementation AutoFillTableView |
| |
| // We override the keyDown method to dispatch the |deleteSelection:| action |
| // when the user presses the delete or backspace keys. Note a delegate must |
| // be present that conforms to the DeleteKeyDelegate protocol. |
| - (void)keyDown:(NSEvent *)event { |
| id object = [self delegate]; |
| unichar c = [[event characters] characterAtIndex: 0]; |
| |
| // If the user pressed delete and the delegate supports deleteSelection: |
| if ((c == NSDeleteFunctionKey || |
| c == NSDeleteCharFunctionKey || |
| c == NSDeleteCharacter) && |
| [object respondsToSelector:@selector(deleteSelection:)]) { |
| id <DeleteKeyDelegate> delegate = (id <DeleteKeyDelegate>) object; |
| |
| [delegate deleteSelection:self]; |
| } else { |
| [super keyDown:event]; |
| } |
| } |
| |
| @end |
| |
| // Private interface. |
| @interface AutoFillDialogController (PrivateMethods) |
| // Asyncronous handler for when PersonalDataManager data loads. The |
| // personal data manager notifies the dialog with this method when the |
| // data loading is complete and ready to be used. |
| - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles |
| creditCards:(const std::vector<CreditCard*>&)creditCards; |
| |
| // Returns true if |row| is an index to a valid profile in |tableView_|, and |
| // false otherwise. |
| - (BOOL)isProfileRow:(NSInteger)row; |
| |
| // Returns true if |row| is an index to the profile group row in |tableView_|, |
| // and false otherwise. |
| - (BOOL)isProfileGroupRow:(NSInteger)row; |
| |
| // Returns true if |row| is an index to a valid credit card in |tableView_|, and |
| // false otherwise. |
| - (BOOL)isCreditCardRow:(NSInteger)row; |
| |
| // Returns true if |row| is the index to the credit card group row in |
| // |tableView_|, and false otherwise. |
| - (BOOL)isCreditCardGroupRow:(NSInteger)row; |
| |
| // Returns the index to |profiles_| of the corresponding |row| in |tableView_|. |
| - (size_t)profileIndexFromRow:(NSInteger)row; |
| |
| // Returns the index to |creditCards_| of the corresponding |row| in |
| // |tableView_|. |
| - (size_t)creditCardIndexFromRow:(NSInteger)row; |
| |
| // Returns the |row| in |tableView_| that corresponds to the index |i| into |
| // |profiles_|. |
| - (NSInteger)rowFromProfileIndex:(size_t)i; |
| |
| // Returns the |row| in |tableView_| that corresponds to the index |i| into |
| // |creditCards_|. |
| - (NSInteger)rowFromCreditCardIndex:(size_t)row; |
| |
| // Invokes the modal dialog. |
| - (void)runModalDialog; |
| |
| @end |
| |
| namespace AutoFillDialogControllerInternal { |
| |
| // PersonalDataManagerObserver facilitates asynchronous loading of |
| // PersonalDataManager data before showing the AutoFill settings data to the |
| // user. It acts as a C++-based delegate for the |AutoFillDialogController|. |
| class PersonalDataManagerObserver : public PersonalDataManager::Observer { |
| public: |
| explicit PersonalDataManagerObserver( |
| AutoFillDialogController* controller, |
| PersonalDataManager* personal_data_manager, |
| Profile* profile) |
| : controller_(controller), |
| personal_data_manager_(personal_data_manager), |
| profile_(profile) { |
| } |
| |
| virtual ~PersonalDataManagerObserver(); |
| |
| // Notifies the observer that the PersonalDataManager has finished loading. |
| virtual void OnPersonalDataLoaded(); |
| |
| private: |
| // Utility method to remove |this| from |personal_data_manager_| as an |
| // observer. |
| void RemoveObserver(); |
| |
| // The dialog controller to be notified when the data loading completes. |
| // Weak reference. |
| AutoFillDialogController* controller_; |
| |
| // The object in which we are registered as an observer. We hold on to |
| // it to facilitate un-registering ourself in the destructor and in the |
| // |OnPersonalDataLoaded| method. This may be NULL. |
| // Weak reference. |
| PersonalDataManager* personal_data_manager_; |
| |
| // Profile of caller. Held as weak reference. May not be NULL. |
| Profile* profile_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(PersonalDataManagerObserver); |
| }; |
| |
| // During destruction ensure that we are removed from the |
| // |personal_data_manager_| as an observer. |
| PersonalDataManagerObserver::~PersonalDataManagerObserver() { |
| RemoveObserver(); |
| } |
| |
| void PersonalDataManagerObserver::RemoveObserver() { |
| if (personal_data_manager_) { |
| personal_data_manager_->RemoveObserver(this); |
| } |
| } |
| |
| // The data is ready so display our data. Notify the dialog controller that |
| // the data is ready. Once done we clear the observer. |
| void PersonalDataManagerObserver::OnPersonalDataLoaded() { |
| RemoveObserver(); |
| [controller_ onPersonalDataLoaded:personal_data_manager_->web_profiles() |
| creditCards:personal_data_manager_->credit_cards()]; |
| } |
| |
| } // namespace AutoFillDialogControllerInternal |
| |
| @implementation AutoFillDialogController |
| |
| @synthesize autoFillEnabled = autoFillEnabled_; |
| @synthesize auxiliaryEnabled = auxiliaryEnabled_; |
| @synthesize itemIsSelected = itemIsSelected_; |
| @synthesize multipleSelected = multipleSelected_; |
| |
| + (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer |
| profile:(Profile*)profile |
| importedProfile:(AutoFillProfile*) importedProfile |
| importedCreditCard:(CreditCard*) importedCreditCard { |
| AutoFillDialogController* controller = |
| [AutoFillDialogController controllerWithObserver:observer |
| profile:profile |
| importedProfile:importedProfile |
| importedCreditCard:importedCreditCard]; |
| |
| // Only run modal dialog if it is not already being shown. |
| if (![controller isWindowLoaded]) { |
| [controller runModalDialog]; |
| } |
| } |
| |
| - (void)awakeFromNib { |
| PersonalDataManager* personal_data_manager = |
| profile_->GetPersonalDataManager(); |
| DCHECK(personal_data_manager); |
| |
| if (personal_data_manager->IsDataLoaded()) { |
| // |personalDataManager| data is loaded, we can proceed with the contents. |
| [self onPersonalDataLoaded:personal_data_manager->web_profiles() |
| creditCards:personal_data_manager->credit_cards()]; |
| } else { |
| // |personalDataManager| data is NOT loaded, so we load it here, installing |
| // our observer. |
| personalDataManagerObserver_.reset( |
| new AutoFillDialogControllerInternal::PersonalDataManagerObserver( |
| self, personal_data_manager, profile_)); |
| personal_data_manager->SetObserver(personalDataManagerObserver_.get()); |
| } |
| |
| // Explicitly load the data in the table before window displays to avoid |
| // nasty flicker as tables update. |
| [tableView_ reloadData]; |
| |
| // Set up edit when double-clicking on a table row. |
| [tableView_ setDoubleAction:@selector(editSelection:)]; |
| } |
| |
| // NSWindow Delegate callback. When the window closes the controller can |
| // be released. |
| - (void)windowWillClose:(NSNotification *)notification { |
| [tableView_ setDataSource:nil]; |
| [tableView_ setDelegate:nil]; |
| [self autorelease]; |
| } |
| |
| // Called when the user clicks the save button. |
| - (IBAction)save:(id)sender { |
| // If we have an |observer_| then communicate the changes back. |
| if (observer_) { |
| profile_->GetPrefs()->SetBoolean(prefs::kAutoFillEnabled, autoFillEnabled_); |
| profile_->GetPrefs()->SetBoolean(prefs::kAutoFillAuxiliaryProfilesEnabled, |
| auxiliaryEnabled_); |
| observer_->OnAutoFillDialogApply(&profiles_, &creditCards_); |
| } |
| [self closeDialog]; |
| } |
| |
| // Called when the user clicks the cancel button. All we need to do is stop |
| // the modal session. |
| - (IBAction)cancel:(id)sender { |
| [self closeDialog]; |
| } |
| |
| // Invokes the "Add" sheet for address information. If user saves then the new |
| // information is added to |profiles_| in |addressAddDidEnd:| method. |
| - (IBAction)addNewAddress:(id)sender { |
| DCHECK(!addressSheetController.get()); |
| |
| // Create a new default address. |
| string16 newName = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_ADDRESS); |
| AutoFillProfile newAddress(newName, 0); |
| |
| // Create a new address sheet controller in "Add" mode. |
| addressSheetController.reset( |
| [[AutoFillAddressSheetController alloc] |
| initWithProfile:newAddress |
| mode:kAutoFillAddressAddMode]); |
| |
| // Show the sheet. |
| [NSApp beginSheet:[addressSheetController window] |
| modalForWindow:[self window] |
| modalDelegate:self |
| didEndSelector:@selector(addressAddDidEnd:returnCode:contextInfo:) |
| contextInfo:NULL]; |
| } |
| |
| // Invokes the "Add" sheet for credit card information. If user saves then the |
| // new information is added to |creditCards_| in |creditCardAddDidEnd:| method. |
| - (IBAction)addNewCreditCard:(id)sender { |
| DCHECK(!creditCardSheetController.get()); |
| |
| // Create a new default credit card. |
| string16 newName = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_CREDITCARD); |
| CreditCard newCreditCard(newName, 0); |
| |
| // Create a new address sheet controller in "Add" mode. |
| creditCardSheetController.reset( |
| [[AutoFillCreditCardSheetController alloc] |
| initWithCreditCard:newCreditCard |
| mode:kAutoFillCreditCardAddMode |
| controller:self]); |
| |
| // Show the sheet. |
| [NSApp beginSheet:[creditCardSheetController window] |
| modalForWindow:[self window] |
| modalDelegate:self |
| didEndSelector:@selector(creditCardAddDidEnd:returnCode:contextInfo:) |
| contextInfo:NULL]; |
| } |
| |
| // Add address sheet was dismissed. Non-zero |returnCode| indicates a save. |
| - (void)addressAddDidEnd:(NSWindow*)sheet |
| returnCode:(int)returnCode |
| contextInfo:(void*)contextInfo { |
| DCHECK(contextInfo == NULL); |
| |
| if (returnCode) { |
| // Create a new address and save it to the |profiles_| list. |
| AutoFillProfile newAddress(string16(), 0); |
| [addressSheetController copyModelToProfile:&newAddress]; |
| if (!newAddress.IsEmpty()) { |
| profiles_.push_back(newAddress); |
| |
| // Refresh the view based on new data. |
| UpdateProfileLabels(&profiles_); |
| [tableView_ reloadData]; |
| |
| // Update the selection to the newly added item. |
| NSInteger row = [self rowFromProfileIndex:profiles_.size() - 1]; |
| [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row] |
| byExtendingSelection:NO]; |
| } |
| } |
| [sheet orderOut:self]; |
| addressSheetController.reset(nil); |
| } |
| |
| // Add credit card sheet was dismissed. Non-zero |returnCode| indicates a save. |
| - (void)creditCardAddDidEnd:(NSWindow *)sheet |
| returnCode:(int)returnCode |
| contextInfo:(void *)contextInfo { |
| DCHECK(contextInfo == NULL); |
| |
| if (returnCode) { |
| // Create a new credit card and save it to the |creditCards_| list. |
| CreditCard newCreditCard(string16(), 0); |
| [creditCardSheetController copyModelToCreditCard:&newCreditCard]; |
| if (!newCreditCard.IsEmpty()) { |
| creditCards_.push_back(newCreditCard); |
| |
| // Refresh the view based on new data. |
| [tableView_ reloadData]; |
| |
| // Update the selection to the newly added item. |
| NSInteger row = [self rowFromCreditCardIndex:creditCards_.size() - 1]; |
| [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row] |
| byExtendingSelection:NO]; |
| } |
| } |
| [sheet orderOut:self]; |
| creditCardSheetController.reset(nil); |
| } |
| |
| // Deletes selected items; either addresses, credit cards, or a mixture of the |
| // two depending on the items selected. |
| - (IBAction)deleteSelection:(id)sender { |
| NSIndexSet* selection = [tableView_ selectedRowIndexes]; |
| NSInteger selectedRow = [tableView_ selectedRow]; |
| |
| // Loop through from last to first deleting selected items as we go. |
| for (NSUInteger i = [selection lastIndex]; |
| i != NSNotFound; |
| i = [selection indexLessThanIndex:i]) { |
| // We keep track of the "top most" selection in the list so we know where |
| // to set new selection below. |
| selectedRow = i; |
| |
| if ([self isProfileRow:i]) { |
| profiles_.erase( |
| profiles_.begin() + [self profileIndexFromRow:i]); |
| } else if ([self isCreditCardRow:i]) { |
| creditCards_.erase( |
| creditCards_.begin() + [self creditCardIndexFromRow:i]); |
| } |
| } |
| |
| // Select the previous row if possible, else current row, else deselect all. |
| if ([self tableView:tableView_ shouldSelectRow:selectedRow-1]) { |
| [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow-1] |
| byExtendingSelection:NO]; |
| } else if ([self tableView:tableView_ shouldSelectRow:selectedRow]) { |
| [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow] |
| byExtendingSelection:NO]; |
| } else { |
| [tableView_ deselectAll:self]; |
| } |
| |
| UpdateProfileLabels(&profiles_); |
| [tableView_ reloadData]; |
| } |
| |
| // Edits the selected item, either address or credit card depending on the item |
| // selected. |
| - (IBAction)editSelection:(id)sender { |
| NSInteger selectedRow = [tableView_ selectedRow]; |
| if ([self isProfileRow:selectedRow]) { |
| if (!addressSheetController.get()) { |
| int i = [self profileIndexFromRow:selectedRow]; |
| |
| // Create a new address sheet controller in "Edit" mode. |
| addressSheetController.reset( |
| [[AutoFillAddressSheetController alloc] |
| initWithProfile:profiles_[i] |
| mode:kAutoFillAddressEditMode]); |
| |
| // Show the sheet. |
| [NSApp beginSheet:[addressSheetController window] |
| modalForWindow:[self window] |
| modalDelegate:self |
| didEndSelector:@selector(addressEditDidEnd:returnCode:contextInfo:) |
| contextInfo:&profiles_[i]]; |
| } |
| } else if ([self isCreditCardRow:selectedRow]) { |
| if (!creditCardSheetController.get()) { |
| int i = [self creditCardIndexFromRow:selectedRow]; |
| |
| // Create a new credit card sheet controller in "Edit" mode. |
| creditCardSheetController.reset( |
| [[AutoFillCreditCardSheetController alloc] |
| initWithCreditCard:creditCards_[i] |
| mode:kAutoFillCreditCardEditMode |
| controller:self]); |
| |
| // Show the sheet. |
| [NSApp beginSheet:[creditCardSheetController window] |
| modalForWindow:[self window] |
| modalDelegate:self |
| didEndSelector:@selector(creditCardEditDidEnd:returnCode:contextInfo:) |
| contextInfo:&creditCards_[i]]; |
| } |
| } |
| } |
| |
| // Navigates to the AutoFill help url. |
| - (IBAction)openHelp:(id)sender { |
| Browser* browser = BrowserList::GetLastActive(); |
| |
| if (!browser || !browser->GetSelectedTabContents()) |
| browser = Browser::Create(profile_); |
| browser->OpenAutoFillHelpTabAndActivate(); |
| } |
| |
| // Edit address sheet was dismissed. Non-zero |returnCode| indicates a save. |
| - (void)addressEditDidEnd:(NSWindow *)sheet |
| returnCode:(int)returnCode |
| contextInfo:(void *)contextInfo { |
| DCHECK(contextInfo != NULL); |
| if (returnCode) { |
| AutoFillProfile* profile = static_cast<AutoFillProfile*>(contextInfo); |
| [addressSheetController copyModelToProfile:profile]; |
| |
| if (profile->IsEmpty()) |
| [tableView_ deselectAll:self]; |
| profiles_.erase( |
| std::remove_if(profiles_.begin(), profiles_.end(), |
| std::mem_fun_ref(&AutoFillProfile::IsEmpty)), |
| profiles_.end()); |
| |
| UpdateProfileLabels(&profiles_); |
| [tableView_ reloadData]; |
| } |
| [sheet orderOut:self]; |
| addressSheetController.reset(nil); |
| } |
| |
| // Edit credit card sheet was dismissed. Non-zero |returnCode| indicates a |
| // save. |
| - (void)creditCardEditDidEnd:(NSWindow *)sheet |
| returnCode:(int)returnCode |
| contextInfo:(void *)contextInfo { |
| DCHECK(contextInfo != NULL); |
| if (returnCode) { |
| CreditCard* creditCard = static_cast<CreditCard*>(contextInfo); |
| [creditCardSheetController copyModelToCreditCard:creditCard]; |
| |
| if (creditCard->IsEmpty()) |
| [tableView_ deselectAll:self]; |
| creditCards_.erase( |
| std::remove_if( |
| creditCards_.begin(), creditCards_.end(), |
| std::mem_fun_ref(&CreditCard::IsEmpty)), |
| creditCards_.end()); |
| [tableView_ reloadData]; |
| } |
| [sheet orderOut:self]; |
| creditCardSheetController.reset(nil); |
| } |
| |
| // NSTableView Delegate method. |
| - (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row { |
| if ([self isProfileGroupRow:row] || [self isCreditCardGroupRow:row]) |
| return YES; |
| return NO; |
| } |
| |
| // NSTableView Delegate method. |
| - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row { |
| return ![self tableView:tableView isGroupRow:row]; |
| } |
| |
| // NSTableView Delegate method. |
| - (id)tableView:(NSTableView *)tableView |
| objectValueForTableColumn:(NSTableColumn *)tableColumn |
| row:(NSInteger)row { |
| if ([[tableColumn identifier] isEqualToString:@"Spacer"]) |
| return @""; |
| |
| // Check that we're initialized before supplying data. |
| if (tableView != tableView_) |
| return @""; |
| |
| // Section label. |
| if ([self isProfileGroupRow:row]) { |
| if ([[tableColumn identifier] isEqualToString:@"Summary"]) |
| return l10n_util::GetNSString(IDS_AUTOFILL_ADDRESSES_GROUP_NAME); |
| else |
| return @""; |
| } |
| |
| if (row < 0) |
| return @""; |
| |
| // Data row. |
| if ([self isProfileRow:row]) { |
| if ([[tableColumn identifier] isEqualToString:@"Summary"]) { |
| return SysUTF16ToNSString( |
| profiles_[[self profileIndexFromRow:row]].Label()); |
| } |
| |
| return @""; |
| } |
| |
| // Section label. |
| if ([self isCreditCardGroupRow:row]) { |
| if ([[tableColumn identifier] isEqualToString:@"Summary"]) |
| return l10n_util::GetNSString(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME); |
| else |
| return @""; |
| } |
| |
| // Data row. |
| if ([self isCreditCardRow:row]) { |
| if ([[tableColumn identifier] isEqualToString:@"Summary"]) { |
| return SysUTF16ToNSString( |
| creditCards_[ |
| [self creditCardIndexFromRow:row]].PreviewSummary()); |
| } |
| |
| return @""; |
| } |
| |
| return @""; |
| } |
| |
| // We implement this delegate method to update our |itemIsSelected| and |
| // |multipleSelected| properties. |
| // The "Edit..." and "Remove" buttons' enabled state depends on having a |
| // valid selection in the table. The "Edit..." button depends on having |
| // exactly one item selected. |
| - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { |
| if ([tableView_ selectedRow] >= 0) |
| [self setItemIsSelected:YES]; |
| else |
| [self setItemIsSelected:NO]; |
| |
| [self setMultipleSelected:([[tableView_ selectedRowIndexes] count] > 1UL)]; |
| } |
| |
| - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { |
| if (tableView == tableView_) { |
| // 1 section header, the profiles, 1 section header, the credit cards. |
| return 1 + profiles_.size() + 1 + creditCards_.size(); |
| } |
| |
| return 0; |
| } |
| |
| - (NSArray*)addressLabels { |
| NSUInteger capacity = profiles_.size(); |
| NSMutableArray* array = [NSMutableArray arrayWithCapacity:capacity]; |
| |
| std::vector<AutoFillProfile>::iterator i; |
| for (i = profiles_.begin(); i != profiles_.end(); ++i) { |
| FieldTypeSet fields; |
| i->GetAvailableFieldTypes(&fields); |
| if (fields.find(ADDRESS_HOME_LINE1) == fields.end() && |
| fields.find(ADDRESS_HOME_LINE2) == fields.end() && |
| fields.find(ADDRESS_HOME_APT_NUM) == fields.end() && |
| fields.find(ADDRESS_HOME_CITY) == fields.end() && |
| fields.find(ADDRESS_HOME_STATE) == fields.end() && |
| fields.find(ADDRESS_HOME_ZIP) == fields.end() && |
| fields.find(ADDRESS_HOME_COUNTRY) == fields.end()) { |
| // No address information in this profile; it's useless as a billing |
| // address. |
| continue; |
| } |
| [array addObject:SysUTF16ToNSString(i->Label())]; |
| } |
| |
| return array; |
| } |
| |
| @end |
| |
| @implementation AutoFillDialogController (ExposedForUnitTests) |
| |
| + (AutoFillDialogController*)controllerWithObserver: |
| (AutoFillDialogObserver*)observer |
| profile:(Profile*)profile |
| importedProfile:(AutoFillProfile*)importedProfile |
| importedCreditCard:(CreditCard*)importedCreditCard { |
| |
| // Deallocation is done upon window close. See |windowWillClose:|. |
| AutoFillDialogController* controller = |
| [[self alloc] initWithObserver:observer |
| profile:profile |
| importedProfile:importedProfile |
| importedCreditCard:importedCreditCard]; |
| return controller; |
| } |
| |
| |
| // This is the designated initializer for this class. |
| // |profiles| are non-retained immutable list of autofill profiles. |
| // |creditCards| are non-retained immutable list of credit card info. |
| - (id)initWithObserver:(AutoFillDialogObserver*)observer |
| profile:(Profile*)profile |
| importedProfile:(AutoFillProfile*)importedProfile |
| importedCreditCard:(CreditCard*)importedCreditCard { |
| DCHECK(profile); |
| // Use initWithWindowNibPath: instead of initWithWindowNibName: so we |
| // can override it in a unit test. |
| NSString* nibpath = [mac_util::MainAppBundle() |
| pathForResource:@"AutoFillDialog" |
| ofType:@"nib"]; |
| if ((self = [super initWithWindowNibPath:nibpath owner:self])) { |
| // Initialize member variables based on input. |
| observer_ = observer; |
| profile_ = profile; |
| importedProfile_ = importedProfile; |
| importedCreditCard_ = importedCreditCard; |
| |
| // Use property here to trigger KVO binding. |
| [self setAutoFillEnabled:profile_->GetPrefs()->GetBoolean( |
| prefs::kAutoFillEnabled)]; |
| |
| // Use property here to trigger KVO binding. |
| [self setAuxiliaryEnabled:profile_->GetPrefs()->GetBoolean( |
| prefs::kAutoFillAuxiliaryProfilesEnabled)]; |
| |
| // Do not use [NSMutableArray array] here; we need predictable destruction |
| // which will be prevented by having a reference held by an autorelease |
| // pool. |
| } |
| return self; |
| } |
| |
| // Close the dialog. |
| - (void)closeDialog { |
| [[self window] close]; |
| [NSApp stopModal]; |
| } |
| |
| - (AutoFillAddressSheetController*)addressSheetController { |
| return addressSheetController.get(); |
| } |
| |
| - (AutoFillCreditCardSheetController*)creditCardSheetController { |
| return creditCardSheetController.get(); |
| } |
| |
| - (void)selectAddressAtIndex:(size_t)i { |
| [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: |
| [self rowFromProfileIndex:i]] |
| byExtendingSelection:NO]; |
| } |
| |
| - (void)selectCreditCardAtIndex:(size_t)i { |
| [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: |
| [self rowFromCreditCardIndex:i]] |
| byExtendingSelection:NO]; |
| } |
| |
| - (void)addSelectedAddressAtIndex:(size_t)i { |
| [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: |
| [self rowFromProfileIndex:i]] |
| byExtendingSelection:YES]; |
| } |
| |
| - (void)addSelectedCreditCardAtIndex:(size_t)i { |
| [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: |
| [self rowFromCreditCardIndex:i]] |
| byExtendingSelection:YES]; |
| } |
| |
| - (BOOL)editButtonEnabled { |
| return [editButton_ isEnabled]; |
| } |
| |
| @end |
| |
| @implementation AutoFillDialogController (PrivateMethods) |
| |
| // Run application modal. |
| - (void)runModalDialog { |
| // Use stored window geometry if it exists. |
| if (g_browser_process && g_browser_process->local_state()) { |
| sizeSaver_.reset([[WindowSizeAutosaver alloc] |
| initWithWindow:[self window] |
| prefService:g_browser_process->local_state() |
| path:prefs::kAutoFillDialogPlacement]); |
| } |
| |
| [NSApp runModalForWindow:[self window]]; |
| } |
| |
| - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles |
| creditCards:(const std::vector<CreditCard*>&)creditCards { |
| if (importedProfile_) { |
| profiles_.push_back(*importedProfile_); |
| } |
| |
| if (importedCreditCard_) { |
| creditCards_.push_back(*importedCreditCard_); |
| } |
| |
| // If we're not using imported data then use the data fetch from the web db. |
| if (!importedProfile_ && !importedCreditCard_) { |
| // Make local copy of |profiles|. |
| for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin(); |
| iter != profiles.end(); ++iter) |
| profiles_.push_back(**iter); |
| |
| // Make local copy of |creditCards|. |
| for (std::vector<CreditCard*>::const_iterator iter = creditCards.begin(); |
| iter != creditCards.end(); ++iter) |
| creditCards_.push_back(**iter); |
| } |
| |
| UpdateProfileLabels(&profiles_); |
| } |
| |
| - (BOOL)isProfileRow:(NSInteger)row { |
| if (row > 0 && static_cast<size_t>(row) <= profiles_.size()) |
| return YES; |
| return NO; |
| } |
| |
| - (BOOL)isProfileGroupRow:(NSInteger)row { |
| if (row == 0) |
| return YES; |
| return NO; |
| } |
| |
| - (BOOL)isCreditCardRow:(NSInteger)row { |
| if (row > 0 && |
| static_cast<size_t>(row) >= profiles_.size() + 2 && |
| static_cast<size_t>(row) <= profiles_.size() + creditCards_.size() + 1) |
| return YES; |
| return NO; |
| } |
| |
| - (BOOL)isCreditCardGroupRow:(NSInteger)row { |
| if (row > 0 && static_cast<size_t>(row) == profiles_.size() + 1) |
| return YES; |
| return NO; |
| } |
| |
| - (size_t)profileIndexFromRow:(NSInteger)row { |
| DCHECK([self isProfileRow:row]); |
| return static_cast<size_t>(row) - 1; |
| } |
| |
| - (size_t)creditCardIndexFromRow:(NSInteger)row { |
| DCHECK([self isCreditCardRow:row]); |
| return static_cast<size_t>(row) - (profiles_.size() + 2); |
| } |
| |
| - (NSInteger)rowFromProfileIndex:(size_t)i { |
| return 1 + i; |
| } |
| |
| - (NSInteger)rowFromCreditCardIndex:(size_t)i { |
| return 1 + profiles_.size() + 1 + i; |
| } |
| |
| @end |
| |
| // An NSValueTransformer subclass for use in validation of phone number |
| // fields. Transforms an invalid phone number string into a warning image. |
| // This data transformer is used in the credit card sheet for invalid phone and |
| // fax numbers. |
| @interface InvalidPhoneTransformer : NSValueTransformer { |
| } |
| @end |
| |
| @implementation InvalidPhoneTransformer |
| + (Class)transformedValueClass { |
| return [NSImage class]; |
| } |
| |
| + (BOOL)allowsReverseTransformation { |
| return NO; |
| } |
| |
| - (id)transformedValue:(id)string { |
| NSImage* image = nil; |
| ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| |
| // We display no validation icon when input has not yet been entered. |
| if (string == nil || [string length] == 0) |
| return nil; |
| |
| // If we have input then display alert icon if we have an invalid number. |
| if (string != nil && [string length] != 0) { |
| // TODO(dhollowa): Using SetInfo() call to validate phone number. Should |
| // have explicit validation method. More robust validation is needed as |
| // well eventually. |
| AutoFillProfile profile(string16(), 0); |
| profile.SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER), |
| base::SysNSStringToUTF16(string)); |
| if (profile.GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER)).empty()) { |
| image = rb.GetNSImageNamed(IDR_INPUT_ALERT); |
| DCHECK(image); |
| return image; |
| } |
| } |
| |
| // No alert icon, so must be valid input. |
| if (!image) { |
| image = rb.GetNSImageNamed(IDR_INPUT_GOOD); |
| DCHECK(image); |
| return image; |
| } |
| |
| return nil; |
| } |
| |
| @end |