diff options
Diffstat (limited to 'chrome/browser/autofill/autofill_dialog_controller_mac.mm')
-rw-r--r-- | chrome/browser/autofill/autofill_dialog_controller_mac.mm | 711 |
1 files changed, 493 insertions, 218 deletions
diff --git a/chrome/browser/autofill/autofill_dialog_controller_mac.mm b/chrome/browser/autofill/autofill_dialog_controller_mac.mm index c867497..c39e3e8 100644 --- a/chrome/browser/autofill/autofill_dialog_controller_mac.mm +++ b/chrome/browser/autofill/autofill_dialog_controller_mac.mm @@ -4,29 +4,100 @@ #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" #import "chrome/browser/autofill/autofill_address_model_mac.h" -#import "chrome/browser/autofill/autofill_address_view_controller_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_view_controller_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/disclosure_view_controller.h" -#import "chrome/browser/cocoa/section_separator_view.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/theme_resources.h" + +// 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 (PrivateAPI) +@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 { @@ -94,14 +165,10 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { } // namespace AutoFillDialogControllerInternal -@interface AutoFillDialogController (PrivateMethods) -- (void)runModalDialog; -- (void)installChildViews; -@end - @implementation AutoFillDialogController @synthesize auxiliaryEnabled = auxiliaryEnabled_; +@synthesize itemIsSelected = itemIsSelected_; + (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer profile:(Profile*)profile @@ -120,8 +187,6 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { } - (void)awakeFromNib { - [addressSectionBox_ setShowTopLine:FALSE]; - PersonalDataManager* personal_data_manager = profile_->GetPersonalDataManager(); DCHECK(personal_data_manager); @@ -138,46 +203,27 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { 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 { - // Force views to go away so they properly remove their observations. - addressFormViewControllers_.reset(); - creditCardFormViewControllers_.reset(); + [tableView_ setDataSource:nil]; + [tableView_ setDelegate:nil]; [self autorelease]; } // Called when the user clicks the save button. - (IBAction)save:(id)sender { - // Call |makeFirstResponder:| to commit pending text field edits. - [[self window] makeFirstResponder:[self window]]; - // If we have an |observer_| then communicate the changes back. if (observer_) { - profiles_.clear(); - profiles_.resize([addressFormViewControllers_ count]); - int i = 0; - for (AutoFillAddressViewController* addressFormViewController in - addressFormViewControllers_.get()) { - // Initialize the profile here. The default initializer does not fully - // initialize. - profiles_[i] = AutoFillProfile(ASCIIToUTF16(""), 0); - [addressFormViewController copyModelToProfile:&profiles_[i]]; - i++; - } - creditCards_.clear(); - creditCards_.resize([creditCardFormViewControllers_ count]); - int j = 0; - for (AutoFillCreditCardViewController* creditCardFormViewController in - creditCardFormViewControllers_.get()) { - // Initialize the credit card here. The default initializer does not - // fully initialize. - creditCards_[j] = CreditCard(ASCIIToUTF16(""), 0); - [creditCardFormViewController copyModelToCreditCard:&creditCards_[j]]; - j++; - } profile_->GetPrefs()->SetBoolean(prefs::kAutoFillAuxiliaryProfilesEnabled, auxiliaryEnabled_); observer_->OnAutoFillDialogApply(&profiles_, &creditCards_); @@ -191,144 +237,306 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { [self closeDialog]; } -// Adds new address to bottom of list. A new address controller is created -// and its view is inserted into the view hierarchy. +// 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 { - // Insert relative to top of section, or below last address. - NSView* insertionPoint; - NSUInteger count = [addressFormViewControllers_.get() count]; - if (count == 0) { - insertionPoint = addressSection_; - } else { - insertionPoint = [[addressFormViewControllers_.get() - objectAtIndex:[addressFormViewControllers_.get() count] - 1] view]; + 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]; + profiles_.push_back(newAddress); + + // Refresh the view based on new data. + [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); +} - // Create a new default address, and add it to our array of controllers. - string16 new_address_name = l10n_util::GetStringUTF16( - IDS_AUTOFILL_NEW_ADDRESS); - AutoFillProfile newProfile(new_address_name, 0); - scoped_nsobject<AutoFillAddressViewController> addressViewController( - [[AutoFillAddressViewController alloc] - initWithProfile:newProfile - disclosure:NSOnState - controller:self]); - [self willChangeValueForKey:@"addressLabels"]; - [addressFormViewControllers_.get() addObject:addressViewController]; - [self didChangeValueForKey:@"addressLabels"]; +// 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]; + 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); +} - // Embed the new address into our target view. - [childView_ addSubview:[addressViewController view] - positioned:NSWindowBelow relativeTo:insertionPoint]; - [[addressViewController view] setFrameOrigin:NSMakePoint(0, 0)]; +// Deletes selected item, either address or credit card depending on the item +// selected. +- (IBAction)deleteSelection:(id)sender { + NSInteger selectedRow = [tableView_ selectedRow]; + if ([self isProfileRow:selectedRow]) { + profiles_.erase(profiles_.begin() + [self profileIndexFromRow:selectedRow]); + + // 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_ selectRowIndexes:[NSIndexSet indexSet] + byExtendingSelection:NO]; + } + [tableView_ reloadData]; + } else if ([self isCreditCardRow:selectedRow]) { + creditCards_.erase( + creditCards_.begin() + [self creditCardIndexFromRow:selectedRow]); + + // 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_ selectRowIndexes:[NSIndexSet indexSet] + byExtendingSelection:NO]; + } + [tableView_ reloadData]; + } +} - [self notifyAddressChange:self]; +// 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]]; + } + } +} - // Recalculate key view loop to account for change in view tree. - [[self window] recalculateKeyViewLoop]; +// 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]; + [tableView_ reloadData]; + } + [sheet orderOut:self]; + addressSheetController.reset(nil); } -// Adds new credit card to bottom of list. A new credit card controller is -// created and its view is inserted into the view hierarchy. -- (IBAction)addNewCreditCard:(id)sender { - // Insert relative to top of section, or below last address. - NSView* insertionPoint; - NSUInteger count = [creditCardFormViewControllers_.get() count]; - if (count == 0) { - insertionPoint = creditCardSection_; - } else { - insertionPoint = [[creditCardFormViewControllers_.get() - objectAtIndex:[creditCardFormViewControllers_.get() count] - 1] view]; +// 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]; + [tableView_ reloadData]; } + [sheet orderOut:self]; + creditCardSheetController.reset(nil); +} - // Create a new default credit card, and add it to our array of controllers. - string16 new_credit_card_name = l10n_util::GetStringUTF16( - IDS_AUTOFILL_NEW_CREDITCARD); - CreditCard newCreditCard(new_credit_card_name, 0); - scoped_nsobject<AutoFillCreditCardViewController> creditCardViewController( - [[AutoFillCreditCardViewController alloc] - initWithCreditCard:newCreditCard - disclosure:NSOnState - controller:self]); - [self willChangeValueForKey:@"creditCardLabels"]; - [creditCardFormViewControllers_.get() addObject:creditCardViewController]; - [self didChangeValueForKey:@"creditCardLabels"]; - - // Embed the new address into our target view. - [childView_ addSubview:[creditCardViewController view] - positioned:NSWindowBelow relativeTo:insertionPoint]; - [[creditCardViewController view] setFrameOrigin:NSMakePoint(0, 0)]; - - // Recalculate key view loop to account for change in view tree. - [[self window] recalculateKeyViewLoop]; -} - -- (IBAction)deleteAddress:(id)sender { - NSUInteger i = [addressFormViewControllers_.get() indexOfObject:sender]; - DCHECK(i != NSNotFound); - - // Remove controller's view from superview and remove from list of - // controllers. Note on lifetime: removing view from super view decrements - // refcount of view, removing controller from array decrements refcount of - // controller which in-turn decrement refcount of view. Both should dealloc - // at this point. - [[sender view] removeFromSuperview]; - [self willChangeValueForKey:@"addressLabels"]; - [addressFormViewControllers_.get() removeObjectAtIndex:i]; - [self didChangeValueForKey:@"addressLabels"]; - - [self notifyAddressChange:self]; - - // Recalculate key view loop to account for change in view tree. - [[self window] recalculateKeyViewLoop]; -} - -- (IBAction)deleteCreditCard:(id)sender { - NSUInteger i = [creditCardFormViewControllers_.get() indexOfObject:sender]; - DCHECK(i != NSNotFound); - - // Remove controller's view from superview and remove from list of - // controllers. Note on lifetime: removing view from super view decrements - // refcount of view, removing controller from array decrements refcount of - // controller which in-turn decrement refcount of view. Both should dealloc - // at this point. - [[sender view] removeFromSuperview]; - [self willChangeValueForKey:@"creditCardLabels"]; - [creditCardFormViewControllers_.get() removeObjectAtIndex:i]; - [self didChangeValueForKey:@"creditCardLabels"]; - - // Recalculate key view loop to account for change in view tree. - [[self window] recalculateKeyViewLoop]; -} - -// Credit card controllers are dependent upon the address labels. So we notify -// them here that something has changed. -- (IBAction)notifyAddressChange:(id)sender { - for (AutoFillCreditCardViewController* creditCardFormViewController in - creditCardFormViewControllers_.get()) { - [creditCardFormViewController onAddressesChanged:self]; +// 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_) { + + // Section label. + if ([self isProfileGroupRow:row]) + if ([[tableColumn identifier] isEqualToString:@"Label"]) + return @"Addresses"; + else + return @""; + + if (row < 0) + return @""; + + // Data row. + if ([self isProfileRow:row]) { + if ([[tableColumn identifier] isEqualToString:@"Label"]) + return SysUTF16ToNSString( + profiles_[[self profileIndexFromRow:row]].Label()); + + if ([[tableColumn identifier] isEqualToString:@"Summary"]) + return SysUTF16ToNSString( + profiles_[[self profileIndexFromRow:row]].PreviewSummary()); + + return @""; + } + + // Section label. + if ([self isCreditCardGroupRow:row]) + if ([[tableColumn identifier] isEqualToString:@"Label"]) + return @"Credit Cards"; + else + return @""; + + // Data row. + if ([self isCreditCardRow:row]) { + if ([[tableColumn identifier] isEqualToString:@"Label"]) + return SysUTF16ToNSString( + creditCards_[[self creditCardIndexFromRow:row]].Label()); + + if ([[tableColumn identifier] isEqualToString:@"Summary"]) + return SysUTF16ToNSString( + creditCards_[ + [self creditCardIndexFromRow:row]].PreviewSummary()); + + return @""; + } } + + return @""; } -- (NSArray*)addressLabels { - NSUInteger capacity = [addressFormViewControllers_ count]; - NSMutableArray* array = [NSMutableArray arrayWithCapacity:capacity]; +// We implement this delegate method to update our |itemIsSelected| property. +// The "Edit..." and "Remove" buttons' enabled state depends on having a +// valid selection in the table. +- (void)tableViewSelectionDidChange:(NSNotification *)aNotification { + if ([tableView_ selectedRow] >= 0) + [self setItemIsSelected:YES]; + else + [self setItemIsSelected:NO]; +} - for (AutoFillAddressViewController* addressFormViewController in - addressFormViewControllers_.get()) { - [array addObject:[[addressFormViewController addressModel] label]]; +- (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 array; + return 0; } -- (NSArray*)creditCardLabels { - NSUInteger capacity = [creditCardFormViewControllers_ count]; +- (NSArray*)addressLabels { + NSUInteger capacity = profiles_.size(); NSMutableArray* array = [NSMutableArray arrayWithCapacity:capacity]; - for (AutoFillCreditCardViewController* creditCardFormViewController in - creditCardFormViewControllers_.get()) { - [array addObject:[[creditCardFormViewController creditCardModel] label]]; + std::vector<AutoFillProfile>::iterator i; + for (i = profiles_.begin(); i != profiles_.end(); ++i) { + [array addObject:SysUTF16ToNSString(i->Label())]; } return array; @@ -361,13 +569,14 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { profile:(Profile*)profile importedProfile:(AutoFillProfile*)importedProfile importedCreditCard:(CreditCard*)importedCreditCard { - CHECK(profile); + 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; @@ -380,14 +589,6 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { // Do not use [NSMutableArray array] here; we need predictable destruction // which will be prevented by having a reference held by an autorelease // pool. - - // Initialize array of sub-controllers. - addressFormViewControllers_.reset( - [[NSMutableArray alloc] initWithCapacity:0]); - - // Initialize array of sub-controllers. - creditCardFormViewControllers_.reset( - [[NSMutableArray alloc] initWithCapacity:0]); } return self; } @@ -398,12 +599,24 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { [NSApp stopModal]; } -- (NSMutableArray*)addressFormViewControllers { - return addressFormViewControllers_.get(); +- (AutoFillAddressSheetController*)addressSheetController { + return addressSheetController.get(); +} + +- (AutoFillCreditCardSheetController*)creditCardSheetController { + return creditCardSheetController.get(); +} + +- (void)selectAddressAtIndex:(size_t)i { + [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: + [self rowFromProfileIndex:i]] + byExtendingSelection:NO]; } -- (NSMutableArray*)creditCardFormViewControllers { - return creditCardFormViewControllers_.get(); +- (void)selectCreditCardAtIndex:(size_t)i { + [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: + [self rowFromCreditCardIndex:i]] + byExtendingSelection:NO]; } @end @@ -424,52 +637,6 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { [NSApp runModalForWindow:[self window]]; } -// Install controller and views for the address form and the credit card form. -// They are installed into the appropriate sibling order so that they can be -// arranged vertically by the VerticalLayoutView class. We insert the views -// into the |childView_| but we hold onto the controllers and release them in -// our dealloc once the dialog closes. -- (void)installChildViews { - NSView* insertionPoint; - insertionPoint = addressSection_; - for (size_t i = 0; i < profiles_.size(); i++) { - // Special case for first address, we want to show full contents. - NSCellStateValue disclosureState = (i == 0) ? NSOnState : NSOffState; - scoped_nsobject<AutoFillAddressViewController> addressViewController( - [[AutoFillAddressViewController alloc] - initWithProfile:profiles_[i] - disclosure:disclosureState - controller:self]); - [self willChangeValueForKey:@"addressLabels"]; - [addressFormViewControllers_.get() addObject:addressViewController]; - [self didChangeValueForKey:@"addressLabels"]; - - // Embed the child view into our (owned by us) target view. - [childView_ addSubview:[addressViewController view] - positioned:NSWindowBelow relativeTo:insertionPoint]; - insertionPoint = [addressViewController view]; - [[addressViewController view] setFrameOrigin:NSMakePoint(0, 0)]; - } - - insertionPoint = creditCardSection_; - for (size_t i = 0; i < creditCards_.size(); i++) { - scoped_nsobject<AutoFillCreditCardViewController> creditCardViewController( - [[AutoFillCreditCardViewController alloc] - initWithCreditCard:creditCards_[i] - disclosure:NSOffState - controller:self]); - [self willChangeValueForKey:@"creditCardLabels"]; - [creditCardFormViewControllers_.get() addObject:creditCardViewController]; - [self didChangeValueForKey:@"creditCardLabels"]; - - // Embed the child view into our (owned by us) target view. - [childView_ addSubview:[creditCardViewController view] - positioned:NSWindowBelow relativeTo:insertionPoint]; - insertionPoint = [creditCardViewController view]; - [[creditCardViewController view] setFrameOrigin:NSMakePoint(0, 0)]; - } -} - - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles creditCards:(const std::vector<CreditCard*>&)creditCards { if (importedProfile_) { @@ -492,8 +659,116 @@ void PersonalDataManagerObserver::OnPersonalDataLoaded() { iter != creditCards.end(); ++iter) creditCards_.push_back(**iter); } +} + +- (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 empty data entry +// fields. Transforms a nil or empty string into a warning image. This data +// transformer is used in the address and credit card sheets for empty label +// strings. +@interface MissingAlertTransformer : NSValueTransformer { +} +@end + +@implementation MissingAlertTransformer ++ (Class)transformedValueClass { + return [NSImage class]; +} + ++ (BOOL)allowsReverseTransformation { + return NO; +} + +- (id)transformedValue:(id)string { + if (string == nil || [string length] == 0) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + NSImage* image = rb.GetNSImageNamed(IDR_WARNING); + DCHECK(image); + return image; + } + return nil; +} + +@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]; +} - [self installChildViews]; ++ (BOOL)allowsReverseTransformation { + return NO; +} + +- (id)transformedValue:(id)string { + 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()) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + NSImage* image = rb.GetNSImageNamed(IDR_WARNING); + DCHECK(image); + return image; + } + } + return nil; } @end |