blob: fe28c531bb78ff6da69a67418761b4541c58cff2 [file] [log] [blame]
// 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