Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(366)

Side by Side Diff: chrome/browser/autofill/autofill_dialog_controller_mac.mm

Issue 2673006: AutoFill Profiles dialog implemented according to new mocks on Mac (Closed)
Patch Set: Revisions based on review comments. Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "chrome/browser/autofill/autofill_dialog_controller_mac.h" 5 #import "chrome/browser/autofill/autofill_dialog_controller_mac.h"
6 #include "app/l10n_util.h" 6 #include "app/l10n_util.h"
7 #include "app/resource_bundle.h"
7 #include "base/mac_util.h" 8 #include "base/mac_util.h"
8 #include "base/sys_string_conversions.h" 9 #include "base/sys_string_conversions.h"
9 #import "chrome/browser/autofill/autofill_address_model_mac.h" 10 #import "chrome/browser/autofill/autofill_address_model_mac.h"
10 #import "chrome/browser/autofill/autofill_address_view_controller_mac.h" 11 #import "chrome/browser/autofill/autofill_address_sheet_controller_mac.h"
11 #import "chrome/browser/autofill/autofill_credit_card_model_mac.h" 12 #import "chrome/browser/autofill/autofill_credit_card_model_mac.h"
12 #import "chrome/browser/autofill/autofill_credit_card_view_controller_mac.h" 13 #import "chrome/browser/autofill/autofill_credit_card_sheet_controller_mac.h"
13 #import "chrome/browser/autofill/personal_data_manager.h" 14 #import "chrome/browser/autofill/personal_data_manager.h"
14 #include "chrome/browser/browser_process.h" 15 #include "chrome/browser/browser_process.h"
15 #import "chrome/browser/cocoa/disclosure_view_controller.h"
16 #import "chrome/browser/cocoa/section_separator_view.h"
17 #import "chrome/browser/cocoa/window_size_autosaver.h" 16 #import "chrome/browser/cocoa/window_size_autosaver.h"
18 #include "chrome/browser/pref_service.h" 17 #include "chrome/browser/pref_service.h"
19 #include "chrome/browser/profile.h" 18 #include "chrome/browser/profile.h"
20 #include "chrome/common/pref_names.h" 19 #include "chrome/common/pref_names.h"
21 #include "grit/generated_resources.h" 20 #include "grit/generated_resources.h"
21 #include "grit/theme_resources.h"
22
23 // Delegate protocol that needs to be in place for the AutoFillTableView's
24 // handling of delete and backspace keys.
25 @protocol DeleteKeyDelegate
26 - (IBAction)deleteSelection:(id)sender;
27 @end
28
29 // A subclass of NSTableView that allows for deleting selected elements using
30 // the delete or backspace keys.
31 @interface AutoFillTableView : NSTableView {
32 }
33 @end
34
35 @implementation AutoFillTableView
36
37 // We override the keyDown method to dispatch the |deleteSelection:| action
38 // when the user presses the delete or backspace keys. Note a delegate must
39 // be present that conforms to the DeleteKeyDelegate protocol.
40 - (void)keyDown:(NSEvent *)event {
41 id object = [self delegate];
42 unichar c = [[event characters] characterAtIndex: 0];
43
44 // If the user pressed delete and the delegate supports deleteSelection:
45 if ((c == NSDeleteFunctionKey ||
46 c == NSDeleteCharFunctionKey ||
47 c == NSDeleteCharacter) &&
48 [object respondsToSelector:@selector(deleteSelection:)]) {
49 id <DeleteKeyDelegate> delegate = (id <DeleteKeyDelegate>) object;
50
51 [delegate deleteSelection:self];
52 } else {
53 [super keyDown:event];
54 }
55 }
56
57 @end
22 58
23 // Private interface. 59 // Private interface.
24 @interface AutoFillDialogController (PrivateAPI) 60 @interface AutoFillDialogController (PrivateMethods)
25 // Asyncronous handler for when PersonalDataManager data loads. The 61 // Asyncronous handler for when PersonalDataManager data loads. The
26 // personal data manager notifies the dialog with this method when the 62 // personal data manager notifies the dialog with this method when the
27 // data loading is complete and ready to be used. 63 // data loading is complete and ready to be used.
28 - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles 64 - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles
29 creditCards:(const std::vector<CreditCard*>&)creditCards; 65 creditCards:(const std::vector<CreditCard*>&)creditCards;
66
67 // Returns true if |row| is an index to a valid profile in |tableView_|, and
68 // false otherwise.
69 - (BOOL)isProfileRow:(NSInteger)row;
70
71 // Returns true if |row| is an index to the profile group row in |tableView_|,
72 // and false otherwise.
73 - (BOOL)isProfileGroupRow:(NSInteger)row;
74
75 // Returns true if |row| is an index to a valid credit card in |tableView_|, and
76 // false otherwise.
77 - (BOOL)isCreditCardRow:(NSInteger)row;
78
79 // Returns true if |row| is the index to the credit card group row in
80 // |tableView_|, and false otherwise.
81 - (BOOL)isCreditCardGroupRow:(NSInteger)row;
82
83 // Returns the index to |profiles_| of the corresponding |row| in |tableView_|.
84 - (size_t)profileIndexFromRow:(NSInteger)row;
85
86 // Returns the index to |creditCards_| of the corresponding |row| in
87 // |tableView_|.
88 - (size_t)creditCardIndexFromRow:(NSInteger)row;
89
90 // Returns the |row| in |tableView_| that corresponds to the index |i| into
91 // |profiles_|.
92 - (NSInteger)rowFromProfileIndex:(size_t)i;
93
94 // Returns the |row| in |tableView_| that corresponds to the index |i| into
95 // |creditCards_|.
96 - (NSInteger)rowFromCreditCardIndex:(size_t)row;
97
98 // Invokes the modal dialog.
99 - (void)runModalDialog;
100
30 @end 101 @end
31 102
32 namespace AutoFillDialogControllerInternal { 103 namespace AutoFillDialogControllerInternal {
33 104
34 // PersonalDataManagerObserver facilitates asynchronous loading of 105 // PersonalDataManagerObserver facilitates asynchronous loading of
35 // PersonalDataManager data before showing the AutoFill settings data to the 106 // PersonalDataManager data before showing the AutoFill settings data to the
36 // user. It acts as a C++-based delegate for the |AutoFillDialogController|. 107 // user. It acts as a C++-based delegate for the |AutoFillDialogController|.
37 class PersonalDataManagerObserver : public PersonalDataManager::Observer { 108 class PersonalDataManagerObserver : public PersonalDataManager::Observer {
38 public: 109 public:
39 explicit PersonalDataManagerObserver( 110 explicit PersonalDataManagerObserver(
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 // The data is ready so display our data. Notify the dialog controller that 158 // The data is ready so display our data. Notify the dialog controller that
88 // the data is ready. Once done we clear the observer. 159 // the data is ready. Once done we clear the observer.
89 void PersonalDataManagerObserver::OnPersonalDataLoaded() { 160 void PersonalDataManagerObserver::OnPersonalDataLoaded() {
90 RemoveObserver(); 161 RemoveObserver();
91 [controller_ onPersonalDataLoaded:personal_data_manager_->web_profiles() 162 [controller_ onPersonalDataLoaded:personal_data_manager_->web_profiles()
92 creditCards:personal_data_manager_->credit_cards()]; 163 creditCards:personal_data_manager_->credit_cards()];
93 } 164 }
94 165
95 } // namespace AutoFillDialogControllerInternal 166 } // namespace AutoFillDialogControllerInternal
96 167
97 @interface AutoFillDialogController (PrivateMethods)
98 - (void)runModalDialog;
99 - (void)installChildViews;
100 @end
101
102 @implementation AutoFillDialogController 168 @implementation AutoFillDialogController
103 169
104 @synthesize auxiliaryEnabled = auxiliaryEnabled_; 170 @synthesize auxiliaryEnabled = auxiliaryEnabled_;
171 @synthesize itemIsSelected = itemIsSelected_;
105 172
106 + (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer 173 + (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer
107 profile:(Profile*)profile 174 profile:(Profile*)profile
108 importedProfile:(AutoFillProfile*) importedProfile 175 importedProfile:(AutoFillProfile*) importedProfile
109 importedCreditCard:(CreditCard*) importedCreditCard { 176 importedCreditCard:(CreditCard*) importedCreditCard {
110 AutoFillDialogController* controller = 177 AutoFillDialogController* controller =
111 [AutoFillDialogController controllerWithObserver:observer 178 [AutoFillDialogController controllerWithObserver:observer
112 profile:profile 179 profile:profile
113 importedProfile:importedProfile 180 importedProfile:importedProfile
114 importedCreditCard:importedCreditCard]; 181 importedCreditCard:importedCreditCard];
115 182
116 // Only run modal dialog if it is not already being shown. 183 // Only run modal dialog if it is not already being shown.
117 if (![controller isWindowLoaded]) { 184 if (![controller isWindowLoaded]) {
118 [controller runModalDialog]; 185 [controller runModalDialog];
119 } 186 }
120 } 187 }
121 188
122 - (void)awakeFromNib { 189 - (void)awakeFromNib {
123 [addressSectionBox_ setShowTopLine:FALSE];
124
125 PersonalDataManager* personal_data_manager = 190 PersonalDataManager* personal_data_manager =
126 profile_->GetPersonalDataManager(); 191 profile_->GetPersonalDataManager();
127 DCHECK(personal_data_manager); 192 DCHECK(personal_data_manager);
128 193
129 if (personal_data_manager->IsDataLoaded()) { 194 if (personal_data_manager->IsDataLoaded()) {
130 // |personalDataManager| data is loaded, we can proceed with the contents. 195 // |personalDataManager| data is loaded, we can proceed with the contents.
131 [self onPersonalDataLoaded:personal_data_manager->web_profiles() 196 [self onPersonalDataLoaded:personal_data_manager->web_profiles()
132 creditCards:personal_data_manager->credit_cards()]; 197 creditCards:personal_data_manager->credit_cards()];
133 } else { 198 } else {
134 // |personalDataManager| data is NOT loaded, so we load it here, installing 199 // |personalDataManager| data is NOT loaded, so we load it here, installing
135 // our observer. 200 // our observer.
136 personalDataManagerObserver_.reset( 201 personalDataManagerObserver_.reset(
137 new AutoFillDialogControllerInternal::PersonalDataManagerObserver( 202 new AutoFillDialogControllerInternal::PersonalDataManagerObserver(
138 self, personal_data_manager, profile_)); 203 self, personal_data_manager, profile_));
139 personal_data_manager->SetObserver(personalDataManagerObserver_.get()); 204 personal_data_manager->SetObserver(personalDataManagerObserver_.get());
140 } 205 }
206
207 // Explicitly load the data in the table before window displays to avoid
208 // nasty flicker as tables update.
209 [tableView_ reloadData];
210
211 // Set up edit when double-clicking on a table row.
212 [tableView_ setDoubleAction:@selector(editSelection:)];
141 } 213 }
142 214
143 // NSWindow Delegate callback. When the window closes the controller can 215 // NSWindow Delegate callback. When the window closes the controller can
144 // be released. 216 // be released.
145 - (void)windowWillClose:(NSNotification *)notification { 217 - (void)windowWillClose:(NSNotification *)notification {
146 // Force views to go away so they properly remove their observations. 218 [tableView_ setDataSource:nil];
147 addressFormViewControllers_.reset(); 219 [tableView_ setDelegate:nil];
148 creditCardFormViewControllers_.reset();
149 [self autorelease]; 220 [self autorelease];
150 } 221 }
151 222
152 // Called when the user clicks the save button. 223 // Called when the user clicks the save button.
153 - (IBAction)save:(id)sender { 224 - (IBAction)save:(id)sender {
154 // Call |makeFirstResponder:| to commit pending text field edits.
155 [[self window] makeFirstResponder:[self window]];
156
157 // If we have an |observer_| then communicate the changes back. 225 // If we have an |observer_| then communicate the changes back.
158 if (observer_) { 226 if (observer_) {
159 profiles_.clear();
160 profiles_.resize([addressFormViewControllers_ count]);
161 int i = 0;
162 for (AutoFillAddressViewController* addressFormViewController in
163 addressFormViewControllers_.get()) {
164 // Initialize the profile here. The default initializer does not fully
165 // initialize.
166 profiles_[i] = AutoFillProfile(ASCIIToUTF16(""), 0);
167 [addressFormViewController copyModelToProfile:&profiles_[i]];
168 i++;
169 }
170 creditCards_.clear();
171 creditCards_.resize([creditCardFormViewControllers_ count]);
172 int j = 0;
173 for (AutoFillCreditCardViewController* creditCardFormViewController in
174 creditCardFormViewControllers_.get()) {
175 // Initialize the credit card here. The default initializer does not
176 // fully initialize.
177 creditCards_[j] = CreditCard(ASCIIToUTF16(""), 0);
178 [creditCardFormViewController copyModelToCreditCard:&creditCards_[j]];
179 j++;
180 }
181 profile_->GetPrefs()->SetBoolean(prefs::kAutoFillAuxiliaryProfilesEnabled, 227 profile_->GetPrefs()->SetBoolean(prefs::kAutoFillAuxiliaryProfilesEnabled,
182 auxiliaryEnabled_); 228 auxiliaryEnabled_);
183 observer_->OnAutoFillDialogApply(&profiles_, &creditCards_); 229 observer_->OnAutoFillDialogApply(&profiles_, &creditCards_);
184 } 230 }
185 [self closeDialog]; 231 [self closeDialog];
186 } 232 }
187 233
188 // Called when the user clicks the cancel button. All we need to do is stop 234 // Called when the user clicks the cancel button. All we need to do is stop
189 // the modal session. 235 // the modal session.
190 - (IBAction)cancel:(id)sender { 236 - (IBAction)cancel:(id)sender {
191 [self closeDialog]; 237 [self closeDialog];
192 } 238 }
193 239
194 // Adds new address to bottom of list. A new address controller is created 240 // Invokes the "Add" sheet for address information. If user saves then the new
195 // and its view is inserted into the view hierarchy. 241 // information is added to |profiles_| in |addressAddDidEnd:| method.
196 - (IBAction)addNewAddress:(id)sender { 242 - (IBAction)addNewAddress:(id)sender {
197 // Insert relative to top of section, or below last address. 243 DCHECK(!addressSheetController.get());
198 NSView* insertionPoint; 244
199 NSUInteger count = [addressFormViewControllers_.get() count]; 245 // Create a new default address.
200 if (count == 0) { 246 string16 newName = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_ADDRESS);
201 insertionPoint = addressSection_; 247 AutoFillProfile newAddress(newName, 0);
202 } else { 248
203 insertionPoint = [[addressFormViewControllers_.get() 249 // Create a new address sheet controller in "Add" mode.
204 objectAtIndex:[addressFormViewControllers_.get() count] - 1] view]; 250 addressSheetController.reset(
205 } 251 [[AutoFillAddressSheetController alloc]
206 252 initWithProfile:newAddress
207 // Create a new default address, and add it to our array of controllers. 253 mode:kAutoFillAddressAddMode]);
208 string16 new_address_name = l10n_util::GetStringUTF16( 254
209 IDS_AUTOFILL_NEW_ADDRESS); 255 // Show the sheet.
210 AutoFillProfile newProfile(new_address_name, 0); 256 [NSApp beginSheet:[addressSheetController window]
211 scoped_nsobject<AutoFillAddressViewController> addressViewController( 257 modalForWindow:[self window]
212 [[AutoFillAddressViewController alloc] 258 modalDelegate:self
213 initWithProfile:newProfile 259 didEndSelector:@selector(addressAddDidEnd:returnCode:contextInfo:)
214 disclosure:NSOnState 260 contextInfo:NULL];
215 controller:self]); 261 }
216 [self willChangeValueForKey:@"addressLabels"]; 262
217 [addressFormViewControllers_.get() addObject:addressViewController]; 263 // Invokes the "Add" sheet for credit card information. If user saves then the
218 [self didChangeValueForKey:@"addressLabels"]; 264 // new information is added to |creditCards_| in |creditCardAddDidEnd:| method.
219
220 // Embed the new address into our target view.
221 [childView_ addSubview:[addressViewController view]
222 positioned:NSWindowBelow relativeTo:insertionPoint];
223 [[addressViewController view] setFrameOrigin:NSMakePoint(0, 0)];
224
225 [self notifyAddressChange:self];
226
227 // Recalculate key view loop to account for change in view tree.
228 [[self window] recalculateKeyViewLoop];
229 }
230
231 // Adds new credit card to bottom of list. A new credit card controller is
232 // created and its view is inserted into the view hierarchy.
233 - (IBAction)addNewCreditCard:(id)sender { 265 - (IBAction)addNewCreditCard:(id)sender {
234 // Insert relative to top of section, or below last address. 266 DCHECK(!creditCardSheetController.get());
235 NSView* insertionPoint; 267
236 NSUInteger count = [creditCardFormViewControllers_.get() count]; 268 // Create a new default credit card.
237 if (count == 0) { 269 string16 newName = l10n_util::GetStringUTF16(IDS_AUTOFILL_NEW_CREDITCARD);
238 insertionPoint = creditCardSection_; 270 CreditCard newCreditCard(newName, 0);
239 } else { 271
240 insertionPoint = [[creditCardFormViewControllers_.get() 272 // Create a new address sheet controller in "Add" mode.
241 objectAtIndex:[creditCardFormViewControllers_.get() count] - 1] view]; 273 creditCardSheetController.reset(
242 } 274 [[AutoFillCreditCardSheetController alloc]
243
244 // Create a new default credit card, and add it to our array of controllers.
245 string16 new_credit_card_name = l10n_util::GetStringUTF16(
246 IDS_AUTOFILL_NEW_CREDITCARD);
247 CreditCard newCreditCard(new_credit_card_name, 0);
248 scoped_nsobject<AutoFillCreditCardViewController> creditCardViewController(
249 [[AutoFillCreditCardViewController alloc]
250 initWithCreditCard:newCreditCard 275 initWithCreditCard:newCreditCard
251 disclosure:NSOnState 276 mode:kAutoFillCreditCardAddMode
252 controller:self]); 277 controller:self]);
253 [self willChangeValueForKey:@"creditCardLabels"]; 278
254 [creditCardFormViewControllers_.get() addObject:creditCardViewController]; 279 // Show the sheet.
255 [self didChangeValueForKey:@"creditCardLabels"]; 280 [NSApp beginSheet:[creditCardSheetController window]
256 281 modalForWindow:[self window]
257 // Embed the new address into our target view. 282 modalDelegate:self
258 [childView_ addSubview:[creditCardViewController view] 283 didEndSelector:@selector(creditCardAddDidEnd:returnCode:contextInfo:)
259 positioned:NSWindowBelow relativeTo:insertionPoint]; 284 contextInfo:NULL];
260 [[creditCardViewController view] setFrameOrigin:NSMakePoint(0, 0)]; 285 }
261 286
262 // Recalculate key view loop to account for change in view tree. 287 // Add address sheet was dismissed. Non-zero |returnCode| indicates a save.
263 [[self window] recalculateKeyViewLoop]; 288 - (void)addressAddDidEnd:(NSWindow*)sheet
264 } 289 returnCode:(int)returnCode
265 290 contextInfo:(void*)contextInfo {
266 - (IBAction)deleteAddress:(id)sender { 291 DCHECK(contextInfo == NULL);
267 NSUInteger i = [addressFormViewControllers_.get() indexOfObject:sender]; 292
268 DCHECK(i != NSNotFound); 293 if (returnCode) {
269 294 // Create a new address and save it to the |profiles_| list.
270 // Remove controller's view from superview and remove from list of 295 AutoFillProfile newAddress(string16(), 0);
271 // controllers. Note on lifetime: removing view from super view decrements 296 [addressSheetController copyModelToProfile:&newAddress];
272 // refcount of view, removing controller from array decrements refcount of 297 profiles_.push_back(newAddress);
273 // controller which in-turn decrement refcount of view. Both should dealloc 298
274 // at this point. 299 // Refresh the view based on new data.
275 [[sender view] removeFromSuperview]; 300 [tableView_ reloadData];
276 [self willChangeValueForKey:@"addressLabels"]; 301
277 [addressFormViewControllers_.get() removeObjectAtIndex:i]; 302 // Update the selection to the newly added item.
278 [self didChangeValueForKey:@"addressLabels"]; 303 NSInteger row = [self rowFromProfileIndex:profiles_.size() - 1];
279 304 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row]
280 [self notifyAddressChange:self]; 305 byExtendingSelection:NO];
281 306 }
282 // Recalculate key view loop to account for change in view tree. 307 [sheet orderOut:self];
283 [[self window] recalculateKeyViewLoop]; 308 addressSheetController.reset(nil);
284 } 309 }
285 310
286 - (IBAction)deleteCreditCard:(id)sender { 311 // Add credit card sheet was dismissed. Non-zero |returnCode| indicates a save.
287 NSUInteger i = [creditCardFormViewControllers_.get() indexOfObject:sender]; 312 - (void)creditCardAddDidEnd:(NSWindow *)sheet
288 DCHECK(i != NSNotFound); 313 returnCode:(int)returnCode
289 314 contextInfo:(void *)contextInfo {
290 // Remove controller's view from superview and remove from list of 315 DCHECK(contextInfo == NULL);
291 // controllers. Note on lifetime: removing view from super view decrements 316
292 // refcount of view, removing controller from array decrements refcount of 317 if (returnCode) {
293 // controller which in-turn decrement refcount of view. Both should dealloc 318 // Create a new credit card and save it to the |creditCards_| list.
294 // at this point. 319 CreditCard newCreditCard(string16(), 0);
295 [[sender view] removeFromSuperview]; 320 [creditCardSheetController copyModelToCreditCard:&newCreditCard];
296 [self willChangeValueForKey:@"creditCardLabels"]; 321 creditCards_.push_back(newCreditCard);
297 [creditCardFormViewControllers_.get() removeObjectAtIndex:i]; 322
298 [self didChangeValueForKey:@"creditCardLabels"]; 323 // Refresh the view based on new data.
299 324 [tableView_ reloadData];
300 // Recalculate key view loop to account for change in view tree. 325
301 [[self window] recalculateKeyViewLoop]; 326 // Update the selection to the newly added item.
302 } 327 NSInteger row = [self rowFromCreditCardIndex:creditCards_.size() - 1];
303 328 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row]
304 // Credit card controllers are dependent upon the address labels. So we notify 329 byExtendingSelection:NO];
305 // them here that something has changed. 330 }
306 - (IBAction)notifyAddressChange:(id)sender { 331 [sheet orderOut:self];
307 for (AutoFillCreditCardViewController* creditCardFormViewController in 332 creditCardSheetController.reset(nil);
308 creditCardFormViewControllers_.get()) { 333 }
309 [creditCardFormViewController onAddressesChanged:self]; 334
310 } 335 // Deletes selected item, either address or credit card depending on the item
336 // selected.
337 - (IBAction)deleteSelection:(id)sender {
338 NSInteger selectedRow = [tableView_ selectedRow];
339 if ([self isProfileRow:selectedRow]) {
340 profiles_.erase(profiles_.begin() + [self profileIndexFromRow:selectedRow]);
341
342 // Select the previous row if possible, else current row, else deselect all.
343 if ([self tableView:tableView_ shouldSelectRow:selectedRow-1]) {
344 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow-1]
345 byExtendingSelection:NO];
346 } else if ([self tableView:tableView_ shouldSelectRow:selectedRow]) {
347 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow]
348 byExtendingSelection:NO];
349 } else {
350 [tableView_ selectRowIndexes:[NSIndexSet indexSet]
351 byExtendingSelection:NO];
352 }
353 [tableView_ reloadData];
354 } else if ([self isCreditCardRow:selectedRow]) {
355 creditCards_.erase(
356 creditCards_.begin() + [self creditCardIndexFromRow:selectedRow]);
357
358 // Select the previous row if possible, else current row, else deselect all.
359 if ([self tableView:tableView_ shouldSelectRow:selectedRow-1]) {
360 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow-1]
361 byExtendingSelection:NO];
362 } else if ([self tableView:tableView_ shouldSelectRow:selectedRow]) {
363 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow]
364 byExtendingSelection:NO];
365 } else {
366 [tableView_ selectRowIndexes:[NSIndexSet indexSet]
367 byExtendingSelection:NO];
368 }
369 [tableView_ reloadData];
370 }
371 }
372
373 // Edits the selected item, either address or credit card depending on the item
374 // selected.
375 - (IBAction)editSelection:(id)sender {
376 NSInteger selectedRow = [tableView_ selectedRow];
377 if ([self isProfileRow:selectedRow]) {
378 if (!addressSheetController.get()) {
379 int i = [self profileIndexFromRow:selectedRow];
380
381 // Create a new address sheet controller in "Edit" mode.
382 addressSheetController.reset(
383 [[AutoFillAddressSheetController alloc]
384 initWithProfile:profiles_[i]
385 mode:kAutoFillAddressEditMode]);
386
387 // Show the sheet.
388 [NSApp beginSheet:[addressSheetController window]
389 modalForWindow:[self window]
390 modalDelegate:self
391 didEndSelector:@selector(addressEditDidEnd:returnCode:contextInfo:)
392 contextInfo:&profiles_[i]];
393 }
394 } else if ([self isCreditCardRow:selectedRow]) {
395 if (!creditCardSheetController.get()) {
396 int i = [self creditCardIndexFromRow:selectedRow];
397
398 // Create a new credit card sheet controller in "Edit" mode.
399 creditCardSheetController.reset(
400 [[AutoFillCreditCardSheetController alloc]
401 initWithCreditCard:creditCards_[i]
402 mode:kAutoFillCreditCardEditMode
403 controller:self]);
404
405 // Show the sheet.
406 [NSApp beginSheet:[creditCardSheetController window]
407 modalForWindow:[self window]
408 modalDelegate:self
409 didEndSelector:@selector(creditCardEditDidEnd:returnCode:contextInfo:)
410 contextInfo:&creditCards_[i]];
411 }
412 }
413 }
414
415 // Edit address sheet was dismissed. Non-zero |returnCode| indicates a save.
416 - (void)addressEditDidEnd:(NSWindow *)sheet
417 returnCode:(int)returnCode
418 contextInfo:(void *)contextInfo {
419 DCHECK(contextInfo != NULL);
420 if (returnCode) {
421 AutoFillProfile* profile = static_cast<AutoFillProfile*>(contextInfo);
422 [addressSheetController copyModelToProfile:profile];
423 [tableView_ reloadData];
424 }
425 [sheet orderOut:self];
426 addressSheetController.reset(nil);
427 }
428
429 // Edit credit card sheet was dismissed. Non-zero |returnCode| indicates a
430 // save.
431 - (void)creditCardEditDidEnd:(NSWindow *)sheet
432 returnCode:(int)returnCode
433 contextInfo:(void *)contextInfo {
434 DCHECK(contextInfo != NULL);
435 if (returnCode) {
436 CreditCard* creditCard = static_cast<CreditCard*>(contextInfo);
437 [creditCardSheetController copyModelToCreditCard:creditCard];
438 [tableView_ reloadData];
439 }
440 [sheet orderOut:self];
441 creditCardSheetController.reset(nil);
442 }
443
444 // NSTableView Delegate method.
445 - (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row {
446 if ([self isProfileGroupRow:row] || [self isCreditCardGroupRow:row])
447 return YES;
448 return NO;
449 }
450
451 // NSTableView Delegate method.
452 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row {
453 return ![self tableView:tableView isGroupRow:row];
454 }
455
456 // NSTableView Delegate method.
457 - (id)tableView:(NSTableView *)tableView
458 objectValueForTableColumn:(NSTableColumn *)tableColumn
459 row:(NSInteger)row {
460 if ([[tableColumn identifier] isEqualToString:@"Spacer"])
461 return @"";
462
463 // Check that we're initialized before supplying data.
464 if (tableView == tableView_) {
465
466 // Section label.
467 if ([self isProfileGroupRow:row])
468 if ([[tableColumn identifier] isEqualToString:@"Label"])
469 return @"Addresses";
470 else
471 return @"";
472
473 if (row < 0)
474 return @"";
475
476 // Data row.
477 if ([self isProfileRow:row]) {
478 if ([[tableColumn identifier] isEqualToString:@"Label"])
479 return SysUTF16ToNSString(
480 profiles_[[self profileIndexFromRow:row]].Label());
481
482 if ([[tableColumn identifier] isEqualToString:@"Summary"])
483 return SysUTF16ToNSString(
484 profiles_[[self profileIndexFromRow:row]].PreviewSummary());
485
486 return @"";
487 }
488
489 // Section label.
490 if ([self isCreditCardGroupRow:row])
491 if ([[tableColumn identifier] isEqualToString:@"Label"])
492 return @"Credit Cards";
493 else
494 return @"";
495
496 // Data row.
497 if ([self isCreditCardRow:row]) {
498 if ([[tableColumn identifier] isEqualToString:@"Label"])
499 return SysUTF16ToNSString(
500 creditCards_[[self creditCardIndexFromRow:row]].Label());
501
502 if ([[tableColumn identifier] isEqualToString:@"Summary"])
503 return SysUTF16ToNSString(
504 creditCards_[
505 [self creditCardIndexFromRow:row]].PreviewSummary());
506
507 return @"";
508 }
509 }
510
511 return @"";
512 }
513
514 // We implement this delegate method to update our |itemIsSelected| property.
515 // The "Edit..." and "Remove" buttons' enabled state depends on having a
516 // valid selection in the table.
517 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification {
518 if ([tableView_ selectedRow] >= 0)
519 [self setItemIsSelected:YES];
520 else
521 [self setItemIsSelected:NO];
522 }
523
524 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
525 if (tableView == tableView_) {
526 // 1 section header, the profiles, 1 section header, the credit cards.
527 return 1 + profiles_.size() + 1 + creditCards_.size();
528 }
529
530 return 0;
311 } 531 }
312 532
313 - (NSArray*)addressLabels { 533 - (NSArray*)addressLabels {
314 NSUInteger capacity = [addressFormViewControllers_ count]; 534 NSUInteger capacity = profiles_.size();
315 NSMutableArray* array = [NSMutableArray arrayWithCapacity:capacity]; 535 NSMutableArray* array = [NSMutableArray arrayWithCapacity:capacity];
316 536
317 for (AutoFillAddressViewController* addressFormViewController in 537 std::vector<AutoFillProfile>::iterator i;
318 addressFormViewControllers_.get()) { 538 for (i = profiles_.begin(); i != profiles_.end(); ++i) {
319 [array addObject:[[addressFormViewController addressModel] label]]; 539 [array addObject:SysUTF16ToNSString(i->Label())];
320 } 540 }
321 541
322 return array; 542 return array;
323 }
324
325 - (NSArray*)creditCardLabels {
326 NSUInteger capacity = [creditCardFormViewControllers_ count];
327 NSMutableArray* array = [NSMutableArray arrayWithCapacity:capacity];
328
329 for (AutoFillCreditCardViewController* creditCardFormViewController in
330 creditCardFormViewControllers_.get()) {
331 [array addObject:[[creditCardFormViewController creditCardModel] label]];
332 }
333
334 return array;
335 } 543 }
336 544
337 @end 545 @end
338 546
339 @implementation AutoFillDialogController (ExposedForUnitTests) 547 @implementation AutoFillDialogController (ExposedForUnitTests)
340 548
341 + (AutoFillDialogController*)controllerWithObserver: 549 + (AutoFillDialogController*)controllerWithObserver:
342 (AutoFillDialogObserver*)observer 550 (AutoFillDialogObserver*)observer
343 profile:(Profile*)profile 551 profile:(Profile*)profile
344 importedProfile:(AutoFillProfile*)importedProfile 552 importedProfile:(AutoFillProfile*)importedProfile
345 importedCreditCard:(CreditCard*)importedCreditCard { 553 importedCreditCard:(CreditCard*)importedCreditCard {
346 554
347 // Deallocation is done upon window close. See |windowWillClose:|. 555 // Deallocation is done upon window close. See |windowWillClose:|.
348 AutoFillDialogController* controller = 556 AutoFillDialogController* controller =
349 [[self alloc] initWithObserver:observer 557 [[self alloc] initWithObserver:observer
350 profile:profile 558 profile:profile
351 importedProfile:importedProfile 559 importedProfile:importedProfile
352 importedCreditCard:importedCreditCard]; 560 importedCreditCard:importedCreditCard];
353 return controller; 561 return controller;
354 } 562 }
355 563
356 564
357 // This is the designated initializer for this class. 565 // This is the designated initializer for this class.
358 // |profiles| are non-retained immutable list of autofill profiles. 566 // |profiles| are non-retained immutable list of autofill profiles.
359 // |creditCards| are non-retained immutable list of credit card info. 567 // |creditCards| are non-retained immutable list of credit card info.
360 - (id)initWithObserver:(AutoFillDialogObserver*)observer 568 - (id)initWithObserver:(AutoFillDialogObserver*)observer
361 profile:(Profile*)profile 569 profile:(Profile*)profile
362 importedProfile:(AutoFillProfile*)importedProfile 570 importedProfile:(AutoFillProfile*)importedProfile
363 importedCreditCard:(CreditCard*)importedCreditCard { 571 importedCreditCard:(CreditCard*)importedCreditCard {
364 CHECK(profile); 572 DCHECK(profile);
365 // Use initWithWindowNibPath: instead of initWithWindowNibName: so we 573 // Use initWithWindowNibPath: instead of initWithWindowNibName: so we
366 // can override it in a unit test. 574 // can override it in a unit test.
367 NSString* nibpath = [mac_util::MainAppBundle() 575 NSString* nibpath = [mac_util::MainAppBundle()
368 pathForResource:@"AutoFillDialog" 576 pathForResource:@"AutoFillDialog"
369 ofType:@"nib"]; 577 ofType:@"nib"];
370 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { 578 if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
579 // Initialize member variables based on input.
371 observer_ = observer; 580 observer_ = observer;
372 profile_ = profile; 581 profile_ = profile;
373 importedProfile_ = importedProfile; 582 importedProfile_ = importedProfile;
374 importedCreditCard_ = importedCreditCard; 583 importedCreditCard_ = importedCreditCard;
375 584
376 // Use property here to trigger KVO binding. 585 // Use property here to trigger KVO binding.
377 [self setAuxiliaryEnabled:profile_->GetPrefs()->GetBoolean( 586 [self setAuxiliaryEnabled:profile_->GetPrefs()->GetBoolean(
378 prefs::kAutoFillAuxiliaryProfilesEnabled)]; 587 prefs::kAutoFillAuxiliaryProfilesEnabled)];
379 588
380 // Do not use [NSMutableArray array] here; we need predictable destruction 589 // Do not use [NSMutableArray array] here; we need predictable destruction
381 // which will be prevented by having a reference held by an autorelease 590 // which will be prevented by having a reference held by an autorelease
382 // pool. 591 // pool.
383
384 // Initialize array of sub-controllers.
385 addressFormViewControllers_.reset(
386 [[NSMutableArray alloc] initWithCapacity:0]);
387
388 // Initialize array of sub-controllers.
389 creditCardFormViewControllers_.reset(
390 [[NSMutableArray alloc] initWithCapacity:0]);
391 } 592 }
392 return self; 593 return self;
393 } 594 }
394 595
395 // Close the dialog. 596 // Close the dialog.
396 - (void)closeDialog { 597 - (void)closeDialog {
397 [[self window] close]; 598 [[self window] close];
398 [NSApp stopModal]; 599 [NSApp stopModal];
399 } 600 }
400 601
401 - (NSMutableArray*)addressFormViewControllers { 602 - (AutoFillAddressSheetController*)addressSheetController {
402 return addressFormViewControllers_.get(); 603 return addressSheetController.get();
403 } 604 }
404 605
405 - (NSMutableArray*)creditCardFormViewControllers { 606 - (AutoFillCreditCardSheetController*)creditCardSheetController {
406 return creditCardFormViewControllers_.get(); 607 return creditCardSheetController.get();
608 }
609
610 - (void)selectAddressAtIndex:(size_t)i {
611 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:
612 [self rowFromProfileIndex:i]]
613 byExtendingSelection:NO];
614 }
615
616 - (void)selectCreditCardAtIndex:(size_t)i {
617 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:
618 [self rowFromCreditCardIndex:i]]
619 byExtendingSelection:NO];
407 } 620 }
408 621
409 @end 622 @end
410 623
411 @implementation AutoFillDialogController (PrivateMethods) 624 @implementation AutoFillDialogController (PrivateMethods)
412 625
413 // Run application modal. 626 // Run application modal.
414 - (void)runModalDialog { 627 - (void)runModalDialog {
415 // Use stored window geometry if it exists. 628 // Use stored window geometry if it exists.
416 if (g_browser_process && g_browser_process->local_state()) { 629 if (g_browser_process && g_browser_process->local_state()) {
417 sizeSaver_.reset([[WindowSizeAutosaver alloc] 630 sizeSaver_.reset([[WindowSizeAutosaver alloc]
418 initWithWindow:[self window] 631 initWithWindow:[self window]
419 prefService:g_browser_process->local_state() 632 prefService:g_browser_process->local_state()
420 path:prefs::kAutoFillDialogPlacement 633 path:prefs::kAutoFillDialogPlacement
421 state:kSaveWindowPos]); 634 state:kSaveWindowPos]);
422 } 635 }
423 636
424 [NSApp runModalForWindow:[self window]]; 637 [NSApp runModalForWindow:[self window]];
425 } 638 }
426 639
427 // Install controller and views for the address form and the credit card form.
428 // They are installed into the appropriate sibling order so that they can be
429 // arranged vertically by the VerticalLayoutView class. We insert the views
430 // into the |childView_| but we hold onto the controllers and release them in
431 // our dealloc once the dialog closes.
432 - (void)installChildViews {
433 NSView* insertionPoint;
434 insertionPoint = addressSection_;
435 for (size_t i = 0; i < profiles_.size(); i++) {
436 // Special case for first address, we want to show full contents.
437 NSCellStateValue disclosureState = (i == 0) ? NSOnState : NSOffState;
438 scoped_nsobject<AutoFillAddressViewController> addressViewController(
439 [[AutoFillAddressViewController alloc]
440 initWithProfile:profiles_[i]
441 disclosure:disclosureState
442 controller:self]);
443 [self willChangeValueForKey:@"addressLabels"];
444 [addressFormViewControllers_.get() addObject:addressViewController];
445 [self didChangeValueForKey:@"addressLabels"];
446
447 // Embed the child view into our (owned by us) target view.
448 [childView_ addSubview:[addressViewController view]
449 positioned:NSWindowBelow relativeTo:insertionPoint];
450 insertionPoint = [addressViewController view];
451 [[addressViewController view] setFrameOrigin:NSMakePoint(0, 0)];
452 }
453
454 insertionPoint = creditCardSection_;
455 for (size_t i = 0; i < creditCards_.size(); i++) {
456 scoped_nsobject<AutoFillCreditCardViewController> creditCardViewController(
457 [[AutoFillCreditCardViewController alloc]
458 initWithCreditCard:creditCards_[i]
459 disclosure:NSOffState
460 controller:self]);
461 [self willChangeValueForKey:@"creditCardLabels"];
462 [creditCardFormViewControllers_.get() addObject:creditCardViewController];
463 [self didChangeValueForKey:@"creditCardLabels"];
464
465 // Embed the child view into our (owned by us) target view.
466 [childView_ addSubview:[creditCardViewController view]
467 positioned:NSWindowBelow relativeTo:insertionPoint];
468 insertionPoint = [creditCardViewController view];
469 [[creditCardViewController view] setFrameOrigin:NSMakePoint(0, 0)];
470 }
471 }
472
473 - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles 640 - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles
474 creditCards:(const std::vector<CreditCard*>&)creditCards { 641 creditCards:(const std::vector<CreditCard*>&)creditCards {
475 if (importedProfile_) { 642 if (importedProfile_) {
476 profiles_.push_back(*importedProfile_); 643 profiles_.push_back(*importedProfile_);
477 } 644 }
478 645
479 if (importedCreditCard_) { 646 if (importedCreditCard_) {
480 creditCards_.push_back(*importedCreditCard_); 647 creditCards_.push_back(*importedCreditCard_);
481 } 648 }
482 649
483 // If we're not using imported data then use the data fetch from the web db. 650 // If we're not using imported data then use the data fetch from the web db.
484 if (!importedProfile_ && !importedCreditCard_) { 651 if (!importedProfile_ && !importedCreditCard_) {
485 // Make local copy of |profiles|. 652 // Make local copy of |profiles|.
486 for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin(); 653 for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin();
487 iter != profiles.end(); ++iter) 654 iter != profiles.end(); ++iter)
488 profiles_.push_back(**iter); 655 profiles_.push_back(**iter);
489 656
490 // Make local copy of |creditCards|. 657 // Make local copy of |creditCards|.
491 for (std::vector<CreditCard*>::const_iterator iter = creditCards.begin(); 658 for (std::vector<CreditCard*>::const_iterator iter = creditCards.begin();
492 iter != creditCards.end(); ++iter) 659 iter != creditCards.end(); ++iter)
493 creditCards_.push_back(**iter); 660 creditCards_.push_back(**iter);
494 } 661 }
662 }
495 663
496 [self installChildViews]; 664 - (BOOL)isProfileRow:(NSInteger)row {
665 if (row > 0 && static_cast<size_t>(row) <= profiles_.size())
666 return YES;
667 return NO;
668 }
669
670 - (BOOL)isProfileGroupRow:(NSInteger)row {
671 if (row == 0)
672 return YES;
673 return NO;
674 }
675
676 - (BOOL)isCreditCardRow:(NSInteger)row {
677 if (row > 0 &&
678 static_cast<size_t>(row) >= profiles_.size() + 2 &&
679 static_cast<size_t>(row) <= profiles_.size() + creditCards_.size() + 1)
680 return YES;
681 return NO;
682 }
683
684 - (BOOL)isCreditCardGroupRow:(NSInteger)row {
685 if (row > 0 && static_cast<size_t>(row) == profiles_.size() + 1)
686 return YES;
687 return NO;
688 }
689
690 - (size_t)profileIndexFromRow:(NSInteger)row {
691 DCHECK([self isProfileRow:row]);
692 return static_cast<size_t>(row) - 1;
693 }
694
695 - (size_t)creditCardIndexFromRow:(NSInteger)row {
696 DCHECK([self isCreditCardRow:row]);
697 return static_cast<size_t>(row) - (profiles_.size() + 2);
698 }
699
700 - (NSInteger)rowFromProfileIndex:(size_t)i {
701 return 1 + i;
702 }
703
704 - (NSInteger)rowFromCreditCardIndex:(size_t)i {
705 return 1 + profiles_.size() + 1 + i;
497 } 706 }
498 707
499 @end 708 @end
709
710 // An NSValueTransformer subclass for use in validation of empty data entry
711 // fields. Transforms a nil or empty string into a warning image. This data
712 // transformer is used in the address and credit card sheets for empty label
713 // strings.
714 @interface MissingAlertTransformer : NSValueTransformer {
715 }
716 @end
717
718 @implementation MissingAlertTransformer
719 + (Class)transformedValueClass {
720 return [NSImage class];
721 }
722
723 + (BOOL)allowsReverseTransformation {
724 return NO;
725 }
726
727 - (id)transformedValue:(id)string {
728 if (string == nil || [string length] == 0) {
729 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
730 NSImage* image = rb.GetNSImageNamed(IDR_WARNING);
731 DCHECK(image);
732 return image;
733 }
734 return nil;
735 }
736
737 @end
738
739 // An NSValueTransformer subclass for use in validation of phone number
740 // fields. Transforms an invalid phone number string into a warning image.
741 // This data transformer is used in the credit card sheet for invalid phone and
742 // fax numbers.
743 @interface InvalidPhoneTransformer : NSValueTransformer {
744 }
745 @end
746
747 @implementation InvalidPhoneTransformer
748 + (Class)transformedValueClass {
749 return [NSImage class];
750 }
751
752 + (BOOL)allowsReverseTransformation {
753 return NO;
754 }
755
756 - (id)transformedValue:(id)string {
757 if (string != nil && [string length] != 0) {
758 // TODO(dhollowa): Using SetInfo() call to validate phone number. Should
759 // have explicit validation method. More robust validation is needed as
760 // well eventually.
761 AutoFillProfile profile(string16(), 0);
762 profile.SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER),
763 base::SysNSStringToUTF16(string));
764 if (profile.GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER)).empty()) {
765 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
766 NSImage* image = rb.GetNSImageNamed(IDR_WARNING);
767 DCHECK(image);
768 return image;
769 }
770 }
771 return nil;
772 }
773
774 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698