OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/lazy_instance.h" | |
6 #include "base/mac/mac_util.h" | |
7 #include "base/sys_string_conversions.h" | |
8 #import "chrome/browser/autofill/autofill-inl.h" | |
9 #import "chrome/browser/autofill/autofill_address_model_mac.h" | |
10 #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_sheet_controller_mac.h" | |
13 #import "chrome/browser/autofill/autofill_dialog_controller_mac.h" | |
14 #import "chrome/browser/autofill/personal_data_manager.h" | |
15 #include "chrome/browser/browser_process.h" | |
16 #include "chrome/browser/prefs/pref_service.h" | |
17 #include "chrome/browser/profiles/profile.h" | |
18 #include "chrome/browser/ui/browser.h" | |
19 #include "chrome/browser/ui/browser_list.h" | |
20 #import "chrome/browser/ui/cocoa/window_size_autosaver.h" | |
21 #include "chrome/common/pref_names.h" | |
22 #include "content/common/notification_details.h" | |
23 #include "content/common/notification_observer.h" | |
24 #include "grit/app_resources.h" | |
25 #include "grit/generated_resources.h" | |
26 #include "grit/theme_resources.h" | |
27 #include "ui/base/l10n/l10n_util.h" | |
28 #include "ui/base/resource/resource_bundle.h" | |
29 #include "ui/gfx/image.h" | |
30 | |
31 namespace { | |
32 | |
33 // Type for singleton object that contains the instance of the visible | |
34 // dialog. | |
35 typedef std::map<Profile*, AutoFillDialogController*> ProfileControllerMap; | |
36 | |
37 static base::LazyInstance<ProfileControllerMap> g_profile_controller_map( | |
38 base::LINKER_INITIALIZED); | |
39 | |
40 } // namespace | |
41 | |
42 // Delegate protocol that needs to be in place for the AutoFillTableView's | |
43 // handling of delete and backspace keys. | |
44 @protocol DeleteKeyDelegate | |
45 - (IBAction)deleteSelection:(id)sender; | |
46 @end | |
47 | |
48 // A subclass of NSTableView that allows for deleting selected elements using | |
49 // the delete or backspace keys. | |
50 @interface AutoFillTableView : NSTableView { | |
51 } | |
52 @end | |
53 | |
54 @implementation AutoFillTableView | |
55 | |
56 // We override the keyDown method to dispatch the |deleteSelection:| action | |
57 // when the user presses the delete or backspace keys. Note a delegate must | |
58 // be present that conforms to the DeleteKeyDelegate protocol. | |
59 - (void)keyDown:(NSEvent *)event { | |
60 id object = [self delegate]; | |
61 unichar c = [[event characters] characterAtIndex: 0]; | |
62 | |
63 // If the user pressed delete and the delegate supports deleteSelection: | |
64 if ((c == NSDeleteFunctionKey || | |
65 c == NSDeleteCharFunctionKey || | |
66 c == NSDeleteCharacter) && | |
67 [object respondsToSelector:@selector(deleteSelection:)]) { | |
68 id <DeleteKeyDelegate> delegate = (id <DeleteKeyDelegate>) object; | |
69 | |
70 [delegate deleteSelection:self]; | |
71 } else { | |
72 [super keyDown:event]; | |
73 } | |
74 } | |
75 | |
76 @end | |
77 | |
78 // Private interface. | |
79 @interface AutoFillDialogController (PrivateMethods) | |
80 // Save profiles and credit card information after user modification. | |
81 - (void)save; | |
82 | |
83 // Asyncronous handler for when PersonalDataManager data loads. The | |
84 // personal data manager notifies the dialog with this method when the | |
85 // data loading is complete and ready to be used. | |
86 - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles | |
87 creditCards:(const std::vector<CreditCard*>&)creditCards; | |
88 | |
89 // Asyncronous handler for when PersonalDataManager data changes. The | |
90 // personal data manager notifies the dialog with this method when the | |
91 // data has changed. | |
92 - (void)onPersonalDataChanged:(const std::vector<AutoFillProfile*>&)profiles | |
93 creditCards:(const std::vector<CreditCard*>&)creditCards; | |
94 | |
95 // Called upon changes to AutoFill preferences that should be reflected in the | |
96 // UI. | |
97 - (void)preferenceDidChange:(const std::string&)preferenceName; | |
98 | |
99 // Adjust the selected index when underlying data changes. | |
100 // Selects the previous row if possible, else current row, else deselect all. | |
101 - (void) adjustSelectionOnDelete:(NSInteger)selectedRow; | |
102 | |
103 // Adjust the selected index when underlying data changes. | |
104 // Selects the current row if possible, else previous row, else deselect all. | |
105 - (void) adjustSelectionOnReload:(NSInteger)selectedRow; | |
106 | |
107 // Returns true if |row| is an index to a valid profile in |tableView_|, and | |
108 // false otherwise. | |
109 - (BOOL)isProfileRow:(NSInteger)row; | |
110 | |
111 // Returns true if |row| is an index to the profile group row in |tableView_|, | |
112 // and false otherwise. | |
113 - (BOOL)isProfileGroupRow:(NSInteger)row; | |
114 | |
115 // Returns true if |row| is an index to a valid credit card in |tableView_|, and | |
116 // false otherwise. | |
117 - (BOOL)isCreditCardRow:(NSInteger)row; | |
118 | |
119 // Returns true if |row| is the index to the credit card group row in | |
120 // |tableView_|, and false otherwise. | |
121 - (BOOL)isCreditCardGroupRow:(NSInteger)row; | |
122 | |
123 // Returns the index to |profiles_| of the corresponding |row| in |tableView_|. | |
124 - (size_t)profileIndexFromRow:(NSInteger)row; | |
125 | |
126 // Returns the index to |creditCards_| of the corresponding |row| in | |
127 // |tableView_|. | |
128 - (size_t)creditCardIndexFromRow:(NSInteger)row; | |
129 | |
130 // Returns the |row| in |tableView_| that corresponds to the index |i| into | |
131 // |profiles_|. | |
132 - (NSInteger)rowFromProfileIndex:(size_t)i; | |
133 | |
134 // Returns the |row| in |tableView_| that corresponds to the index |i| into | |
135 // |creditCards_|. | |
136 - (NSInteger)rowFromCreditCardIndex:(size_t)row; | |
137 | |
138 @end | |
139 | |
140 namespace AutoFillDialogControllerInternal { | |
141 | |
142 // PersonalDataManagerObserver facilitates asynchronous loading of | |
143 // PersonalDataManager data before showing the AutoFill settings data to the | |
144 // user. It acts as a C++-based delegate for the |AutoFillDialogController|. | |
145 class PersonalDataManagerObserver : public PersonalDataManager::Observer { | |
146 public: | |
147 explicit PersonalDataManagerObserver( | |
148 AutoFillDialogController* controller, | |
149 PersonalDataManager* personal_data_manager, | |
150 Profile* profile) | |
151 : controller_(controller), | |
152 personal_data_manager_(personal_data_manager), | |
153 profile_(profile) { | |
154 } | |
155 | |
156 virtual ~PersonalDataManagerObserver(); | |
157 | |
158 // Notifies the observer that the PersonalDataManager has finished loading. | |
159 virtual void OnPersonalDataLoaded(); | |
160 | |
161 // Notifies the observer that the PersonalDataManager data has changed. | |
162 virtual void OnPersonalDataChanged(); | |
163 | |
164 private: | |
165 // Utility method to remove |this| from |personal_data_manager_| as an | |
166 // observer. | |
167 void RemoveObserver(); | |
168 | |
169 // The dialog controller to be notified when the data loading completes. | |
170 // Weak reference. | |
171 AutoFillDialogController* controller_; | |
172 | |
173 // The object in which we are registered as an observer. We hold on to | |
174 // it to facilitate un-registering ourself in the destructor and in the | |
175 // |OnPersonalDataLoaded| method. This may be NULL. | |
176 // Weak reference. | |
177 PersonalDataManager* personal_data_manager_; | |
178 | |
179 // Profile of caller. Held as weak reference. May not be NULL. | |
180 Profile* profile_; | |
181 | |
182 private: | |
183 DISALLOW_COPY_AND_ASSIGN(PersonalDataManagerObserver); | |
184 }; | |
185 | |
186 // During destruction ensure that we are removed from the | |
187 // |personal_data_manager_| as an observer. | |
188 PersonalDataManagerObserver::~PersonalDataManagerObserver() { | |
189 RemoveObserver(); | |
190 } | |
191 | |
192 void PersonalDataManagerObserver::RemoveObserver() { | |
193 if (personal_data_manager_) { | |
194 personal_data_manager_->RemoveObserver(this); | |
195 } | |
196 } | |
197 | |
198 // The data has been loaded, notify the controller. | |
199 void PersonalDataManagerObserver::OnPersonalDataLoaded() { | |
200 [controller_ onPersonalDataLoaded:personal_data_manager_->web_profiles() | |
201 creditCards:personal_data_manager_->credit_cards()]; | |
202 } | |
203 | |
204 // The data has changed, notify the controller. | |
205 void PersonalDataManagerObserver::OnPersonalDataChanged() { | |
206 [controller_ onPersonalDataChanged:personal_data_manager_->web_profiles() | |
207 creditCards:personal_data_manager_->credit_cards()]; | |
208 } | |
209 | |
210 // Bridges preference changed notifications to the dialog controller. | |
211 class PreferenceObserver : public NotificationObserver { | |
212 public: | |
213 explicit PreferenceObserver(AutoFillDialogController* controller) | |
214 : controller_(controller) {} | |
215 | |
216 // Overridden from NotificationObserver: | |
217 virtual void Observe(NotificationType type, | |
218 const NotificationSource& source, | |
219 const NotificationDetails& details) { | |
220 if (type == NotificationType::PREF_CHANGED) { | |
221 const std::string* pref = Details<std::string>(details).ptr(); | |
222 if (pref) { | |
223 [controller_ preferenceDidChange:*pref]; | |
224 } | |
225 } | |
226 } | |
227 | |
228 private: | |
229 AutoFillDialogController* controller_; | |
230 | |
231 DISALLOW_COPY_AND_ASSIGN(PreferenceObserver); | |
232 }; | |
233 | |
234 } // namespace AutoFillDialogControllerInternal | |
235 | |
236 @implementation AutoFillDialogController | |
237 | |
238 @synthesize autoFillManaged = autoFillManaged_; | |
239 @synthesize autoFillManagedAndDisabled = autoFillManagedAndDisabled_; | |
240 @synthesize itemIsSelected = itemIsSelected_; | |
241 @synthesize multipleSelected = multipleSelected_; | |
242 | |
243 + (void)showAutoFillDialogWithObserver:(AutoFillDialogObserver*)observer | |
244 profile:(Profile*)profile { | |
245 AutoFillDialogController* controller = | |
246 [AutoFillDialogController controllerWithObserver:observer | |
247 profile:profile]; | |
248 [controller runModelessDialog]; | |
249 } | |
250 | |
251 - (void)awakeFromNib { | |
252 PersonalDataManager* personal_data_manager = | |
253 profile_->GetPersonalDataManager(); | |
254 DCHECK(personal_data_manager); | |
255 | |
256 if (personal_data_manager->IsDataLoaded()) { | |
257 // |personalDataManager| data is loaded, we can proceed with the contents. | |
258 [self onPersonalDataLoaded:personal_data_manager->web_profiles() | |
259 creditCards:personal_data_manager->credit_cards()]; | |
260 } | |
261 | |
262 // Register as listener to listen to subsequent data change notifications. | |
263 personalDataManagerObserver_.reset( | |
264 new AutoFillDialogControllerInternal::PersonalDataManagerObserver( | |
265 self, personal_data_manager, profile_)); | |
266 personal_data_manager->SetObserver(personalDataManagerObserver_.get()); | |
267 | |
268 // Explicitly load the data in the table before window displays to avoid | |
269 // nasty flicker as tables update. | |
270 [tableView_ reloadData]; | |
271 | |
272 // Set up edit when double-clicking on a table row. | |
273 [tableView_ setDoubleAction:@selector(editSelection:)]; | |
274 } | |
275 | |
276 // NSWindow Delegate callback. When the window closes the controller can | |
277 // be released. | |
278 - (void)windowWillClose:(NSNotification *)notification { | |
279 [tableView_ setDataSource:nil]; | |
280 [tableView_ setDelegate:nil]; | |
281 [self autorelease]; | |
282 | |
283 // Remove ourself from the map. | |
284 ProfileControllerMap* map = g_profile_controller_map.Pointer(); | |
285 ProfileControllerMap::iterator it = map->find(profile_); | |
286 if (it != map->end()) { | |
287 map->erase(it); | |
288 } | |
289 } | |
290 | |
291 // Invokes the "Add" sheet for address information. If user saves then the new | |
292 // information is added to |profiles_| in |addressAddDidEnd:| method. | |
293 - (IBAction)addNewAddress:(id)sender { | |
294 DCHECK(!addressSheetController.get()); | |
295 | |
296 // Create a new default address. | |
297 AutoFillProfile newAddress; | |
298 | |
299 // Create a new address sheet controller in "Add" mode. | |
300 addressSheetController.reset( | |
301 [[AutoFillAddressSheetController alloc] | |
302 initWithProfile:newAddress | |
303 mode:kAutoFillAddressAddMode]); | |
304 | |
305 // Show the sheet. | |
306 [NSApp beginSheet:[addressSheetController window] | |
307 modalForWindow:[self window] | |
308 modalDelegate:self | |
309 didEndSelector:@selector(addressAddDidEnd:returnCode:contextInfo:) | |
310 contextInfo:NULL]; | |
311 } | |
312 | |
313 // Invokes the "Add" sheet for credit card information. If user saves then the | |
314 // new information is added to |creditCards_| in |creditCardAddDidEnd:| method. | |
315 - (IBAction)addNewCreditCard:(id)sender { | |
316 DCHECK(!creditCardSheetController.get()); | |
317 | |
318 // Create a new default credit card. | |
319 CreditCard newCreditCard; | |
320 | |
321 // Create a new address sheet controller in "Add" mode. | |
322 creditCardSheetController.reset( | |
323 [[AutoFillCreditCardSheetController alloc] | |
324 initWithCreditCard:newCreditCard | |
325 mode:kAutoFillCreditCardAddMode]); | |
326 | |
327 // Show the sheet. | |
328 [NSApp beginSheet:[creditCardSheetController window] | |
329 modalForWindow:[self window] | |
330 modalDelegate:self | |
331 didEndSelector:@selector(creditCardAddDidEnd:returnCode:contextInfo:) | |
332 contextInfo:NULL]; | |
333 } | |
334 | |
335 // Add address sheet was dismissed. Non-zero |returnCode| indicates a save. | |
336 - (void)addressAddDidEnd:(NSWindow*)sheet | |
337 returnCode:(int)returnCode | |
338 contextInfo:(void*)contextInfo { | |
339 DCHECK(!contextInfo); | |
340 | |
341 if (returnCode) { | |
342 // Create a new address and save it to the |profiles_| list. | |
343 AutoFillProfile newAddress; | |
344 [addressSheetController copyModelToProfile:&newAddress]; | |
345 if (!newAddress.IsEmpty() && !FindByContents(profiles_, newAddress)) { | |
346 profiles_.push_back(newAddress); | |
347 | |
348 // Saving will save to the PDM and the table will refresh when PDM sends | |
349 // notification that the underlying model has changed. | |
350 [self save]; | |
351 | |
352 // Update the selection to the newly added item. | |
353 NSInteger row = [self rowFromProfileIndex:profiles_.size() - 1]; | |
354 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row] | |
355 byExtendingSelection:NO]; | |
356 } | |
357 } | |
358 [sheet orderOut:self]; | |
359 addressSheetController.reset(nil); | |
360 } | |
361 | |
362 // Add credit card sheet was dismissed. Non-zero |returnCode| indicates a save. | |
363 - (void)creditCardAddDidEnd:(NSWindow*)sheet | |
364 returnCode:(int)returnCode | |
365 contextInfo:(void*)contextInfo { | |
366 DCHECK(!contextInfo); | |
367 | |
368 if (returnCode) { | |
369 // Create a new credit card and save it to the |creditCards_| list. | |
370 CreditCard newCreditCard; | |
371 [creditCardSheetController copyModelToCreditCard:&newCreditCard]; | |
372 if (!newCreditCard.IsEmpty() && | |
373 !FindByContents(creditCards_, newCreditCard)) { | |
374 creditCards_.push_back(newCreditCard); | |
375 | |
376 // Saving will save to the PDM and the table will refresh when PDM sends | |
377 // notification that the underlying model has changed. | |
378 [self save]; | |
379 | |
380 // Update the selection to the newly added item. | |
381 NSInteger row = [self rowFromCreditCardIndex:creditCards_.size() - 1]; | |
382 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:row] | |
383 byExtendingSelection:NO]; | |
384 } | |
385 } | |
386 [sheet orderOut:self]; | |
387 creditCardSheetController.reset(nil); | |
388 } | |
389 | |
390 // Deletes selected items; either addresses, credit cards, or a mixture of the | |
391 // two depending on the items selected. | |
392 - (IBAction)deleteSelection:(id)sender { | |
393 NSIndexSet* selection = [tableView_ selectedRowIndexes]; | |
394 NSInteger selectedRow = [tableView_ selectedRow]; | |
395 | |
396 // Loop through from last to first deleting selected items as we go. | |
397 for (NSUInteger i = [selection lastIndex]; | |
398 i != NSNotFound; | |
399 i = [selection indexLessThanIndex:i]) { | |
400 // We keep track of the "top most" selection in the list so we know where | |
401 // to set new selection below. | |
402 selectedRow = i; | |
403 | |
404 if ([self isProfileRow:i]) { | |
405 profiles_.erase( | |
406 profiles_.begin() + [self profileIndexFromRow:i]); | |
407 } else if ([self isCreditCardRow:i]) { | |
408 creditCards_.erase( | |
409 creditCards_.begin() + [self creditCardIndexFromRow:i]); | |
410 } | |
411 } | |
412 | |
413 // Select the previous row if possible, else current row, else deselect all. | |
414 [self adjustSelectionOnDelete:selectedRow]; | |
415 | |
416 // Saving will save to the PDM and the table will refresh when PDM sends | |
417 // notification that the underlying model has changed. | |
418 [self save]; | |
419 } | |
420 | |
421 // Edits the selected item, either address or credit card depending on the item | |
422 // selected. | |
423 - (IBAction)editSelection:(id)sender { | |
424 NSInteger selectedRow = [tableView_ selectedRow]; | |
425 if ([self isProfileRow:selectedRow]) { | |
426 if (!addressSheetController.get()) { | |
427 int i = [self profileIndexFromRow:selectedRow]; | |
428 | |
429 // Create a new address sheet controller in "Edit" mode. | |
430 addressSheetController.reset( | |
431 [[AutoFillAddressSheetController alloc] | |
432 initWithProfile:profiles_[i] | |
433 mode:kAutoFillAddressEditMode]); | |
434 | |
435 // Show the sheet. | |
436 [NSApp beginSheet:[addressSheetController window] | |
437 modalForWindow:[self window] | |
438 modalDelegate:self | |
439 didEndSelector:@selector(addressEditDidEnd:returnCode:contextInfo:) | |
440 contextInfo:&profiles_[i]]; | |
441 } | |
442 } else if ([self isCreditCardRow:selectedRow]) { | |
443 if (!creditCardSheetController.get()) { | |
444 int i = [self creditCardIndexFromRow:selectedRow]; | |
445 | |
446 // Create a new credit card sheet controller in "Edit" mode. | |
447 creditCardSheetController.reset( | |
448 [[AutoFillCreditCardSheetController alloc] | |
449 initWithCreditCard:creditCards_[i] | |
450 mode:kAutoFillCreditCardEditMode]); | |
451 | |
452 // Show the sheet. | |
453 [NSApp beginSheet:[creditCardSheetController window] | |
454 modalForWindow:[self window] | |
455 modalDelegate:self | |
456 didEndSelector:@selector(creditCardEditDidEnd:returnCode:contextInfo:) | |
457 contextInfo:&creditCards_[i]]; | |
458 } | |
459 } | |
460 } | |
461 | |
462 // Navigates to the AutoFill help url. | |
463 - (IBAction)openHelp:(id)sender { | |
464 Browser* browser = BrowserList::GetLastActive(); | |
465 | |
466 if (!browser || !browser->GetSelectedTabContents()) | |
467 browser = Browser::Create(profile_); | |
468 browser->OpenAutoFillHelpTabAndActivate(); | |
469 } | |
470 | |
471 // Edit address sheet was dismissed. Non-zero |returnCode| indicates a save. | |
472 - (void)addressEditDidEnd:(NSWindow *)sheet | |
473 returnCode:(int)returnCode | |
474 contextInfo:(void *)contextInfo { | |
475 DCHECK(contextInfo != NULL); | |
476 if (returnCode) { | |
477 AutoFillProfile* profile = static_cast<AutoFillProfile*>(contextInfo); | |
478 [addressSheetController copyModelToProfile:profile]; | |
479 | |
480 if (profile->IsEmpty()) | |
481 [tableView_ deselectAll:self]; | |
482 profiles_.erase( | |
483 std::remove_if(profiles_.begin(), profiles_.end(), | |
484 std::mem_fun_ref(&AutoFillProfile::IsEmpty)), | |
485 profiles_.end()); | |
486 | |
487 // Saving will save to the PDM and the table will refresh when PDM sends | |
488 // notification that the underlying model has changed. | |
489 [self save]; | |
490 } | |
491 [sheet orderOut:self]; | |
492 addressSheetController.reset(nil); | |
493 } | |
494 | |
495 // Edit credit card sheet was dismissed. Non-zero |returnCode| indicates a | |
496 // save. | |
497 - (void)creditCardEditDidEnd:(NSWindow *)sheet | |
498 returnCode:(int)returnCode | |
499 contextInfo:(void *)contextInfo { | |
500 DCHECK(contextInfo != NULL); | |
501 if (returnCode) { | |
502 CreditCard* creditCard = static_cast<CreditCard*>(contextInfo); | |
503 [creditCardSheetController copyModelToCreditCard:creditCard]; | |
504 | |
505 if (creditCard->IsEmpty()) | |
506 [tableView_ deselectAll:self]; | |
507 creditCards_.erase( | |
508 std::remove_if( | |
509 creditCards_.begin(), creditCards_.end(), | |
510 std::mem_fun_ref(&CreditCard::IsEmpty)), | |
511 creditCards_.end()); | |
512 | |
513 // Saving will save to the PDM and the table will refresh when PDM sends | |
514 // notification that the underlying model has changed. | |
515 [self save]; | |
516 } | |
517 [sheet orderOut:self]; | |
518 creditCardSheetController.reset(nil); | |
519 } | |
520 | |
521 // NSTableView Delegate method. | |
522 - (BOOL)tableView:(NSTableView *)tableView isGroupRow:(NSInteger)row { | |
523 if ([self isProfileGroupRow:row] || [self isCreditCardGroupRow:row]) | |
524 return YES; | |
525 return NO; | |
526 } | |
527 | |
528 // NSTableView Delegate method. | |
529 - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row { | |
530 return [self isProfileRow:row] || [self isCreditCardRow:row]; | |
531 } | |
532 | |
533 // NSTableView Delegate method. | |
534 - (id)tableView:(NSTableView *)tableView | |
535 objectValueForTableColumn:(NSTableColumn *)tableColumn | |
536 row:(NSInteger)row { | |
537 if ([[tableColumn identifier] isEqualToString:@"Spacer"]) | |
538 return @""; | |
539 | |
540 // Check that we're initialized before supplying data. | |
541 if (tableView != tableView_) | |
542 return @""; | |
543 | |
544 // Section label. | |
545 if ([self isProfileGroupRow:row]) { | |
546 if ([[tableColumn identifier] isEqualToString:@"Summary"]) | |
547 return l10n_util::GetNSString(IDS_AUTOFILL_ADDRESSES_GROUP_NAME); | |
548 else | |
549 return @""; | |
550 } | |
551 | |
552 if (row < 0) | |
553 return @""; | |
554 | |
555 // Data row. | |
556 if ([self isProfileRow:row]) { | |
557 if ([[tableColumn identifier] isEqualToString:@"Summary"]) { | |
558 return SysUTF16ToNSString( | |
559 profiles_[[self profileIndexFromRow:row]].Label()); | |
560 } | |
561 | |
562 return @""; | |
563 } | |
564 | |
565 // Section label. | |
566 if ([self isCreditCardGroupRow:row]) { | |
567 if ([[tableColumn identifier] isEqualToString:@"Summary"]) | |
568 return l10n_util::GetNSString(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME); | |
569 else | |
570 return @""; | |
571 } | |
572 | |
573 // Data row. | |
574 if ([self isCreditCardRow:row]) { | |
575 if ([[tableColumn identifier] isEqualToString:@"Summary"]) { | |
576 return SysUTF16ToNSString( | |
577 creditCards_[ | |
578 [self creditCardIndexFromRow:row]].PreviewSummary()); | |
579 } | |
580 | |
581 return @""; | |
582 } | |
583 | |
584 return @""; | |
585 } | |
586 | |
587 // We implement this delegate method to update our |itemIsSelected| and | |
588 // |multipleSelected| properties. | |
589 // The "Edit..." and "Remove" buttons' enabled state depends on having a | |
590 // valid selection in the table. The "Edit..." button depends on having | |
591 // exactly one item selected. | |
592 - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { | |
593 if ([tableView_ selectedRow] >= 0) | |
594 [self setItemIsSelected:YES]; | |
595 else | |
596 [self setItemIsSelected:NO]; | |
597 | |
598 [self setMultipleSelected:([[tableView_ selectedRowIndexes] count] > 1UL)]; | |
599 } | |
600 | |
601 - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { | |
602 if (tableView == tableView_) { | |
603 // 1 section header, the profiles, 1 section header, the credit cards. | |
604 return 1 + profiles_.size() + 1 + creditCards_.size(); | |
605 } | |
606 | |
607 return 0; | |
608 } | |
609 | |
610 // Accessor for |autoFillEnabled| preference state. Note: a checkbox in Nib | |
611 // is bound to this via KVO. | |
612 - (BOOL)autoFillEnabled { | |
613 return autoFillEnabled_.GetValue(); | |
614 } | |
615 | |
616 // Setter for |autoFillEnabled| preference state. | |
617 - (void)setAutoFillEnabled:(BOOL)value { | |
618 autoFillEnabled_.SetValueIfNotManaged(value ? true : false); | |
619 } | |
620 | |
621 // Accessor for |auxiliaryEnabled| preference state. Note: a checkbox in Nib | |
622 // is bound to this via KVO. | |
623 - (BOOL)auxiliaryEnabled { | |
624 return auxiliaryEnabled_.GetValue(); | |
625 } | |
626 | |
627 // Setter for |auxiliaryEnabled| preference state. | |
628 - (void)setAuxiliaryEnabled:(BOOL)value { | |
629 if ([self autoFillEnabled]) | |
630 auxiliaryEnabled_.SetValueIfNotManaged(value ? true : false); | |
631 } | |
632 | |
633 @end | |
634 | |
635 @implementation AutoFillDialogController (ExposedForUnitTests) | |
636 | |
637 + (AutoFillDialogController*) | |
638 controllerWithObserver:(AutoFillDialogObserver*)observer | |
639 profile:(Profile*)profile { | |
640 profile = profile->GetOriginalProfile(); | |
641 | |
642 ProfileControllerMap* map = g_profile_controller_map.Pointer(); | |
643 DCHECK(map != NULL); | |
644 ProfileControllerMap::iterator it = map->find(profile); | |
645 if (it == map->end()) { | |
646 // We should have exactly 1 or 0 entry in the map, no more. That is, | |
647 // only one profile can have the AutoFill dialog up at a time. | |
648 DCHECK_EQ(map->size(), 0U); | |
649 | |
650 // Deallocation is done upon window close. See |windowWillClose:|. | |
651 AutoFillDialogController* controller = | |
652 [[self alloc] initWithObserver:observer profile:profile]; | |
653 it = map->insert(std::make_pair(profile, controller)).first; | |
654 } | |
655 | |
656 return it->second; | |
657 } | |
658 | |
659 | |
660 // This is the designated initializer for this class. | |
661 // |profiles| are non-retained immutable list of AutoFill profiles. | |
662 // |creditCards| are non-retained immutable list of credit card info. | |
663 - (id)initWithObserver:(AutoFillDialogObserver*)observer | |
664 profile:(Profile*)profile { | |
665 DCHECK(profile); | |
666 // Use initWithWindowNibPath: instead of initWithWindowNibName: so we | |
667 // can override it in a unit test. | |
668 NSString* nibpath = [base::mac::MainAppBundle() | |
669 pathForResource:@"AutoFillDialog" | |
670 ofType:@"nib"]; | |
671 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { | |
672 // Initialize member variables based on input. | |
673 observer_ = observer; | |
674 profile_ = profile; | |
675 | |
676 // Initialize the preference observer and watch kAutoFillEnabled. | |
677 preferenceObserver_.reset( | |
678 new AutoFillDialogControllerInternal::PreferenceObserver(self)); | |
679 autoFillEnabled_.Init(prefs::kAutoFillEnabled, profile_->GetPrefs(), | |
680 preferenceObserver_.get()); | |
681 | |
682 // Call |preferenceDidChange| in order to initialize UI state of the | |
683 // checkbox. | |
684 [self preferenceDidChange:prefs::kAutoFillEnabled]; | |
685 | |
686 // Initialize the preference observer and watch | |
687 // kAutoFillAuxiliaryProfilesEnabled. | |
688 auxiliaryEnabled_.Init(prefs::kAutoFillAuxiliaryProfilesEnabled, | |
689 profile_->GetPrefs(), | |
690 preferenceObserver_.get()); | |
691 | |
692 // Call |preferenceDidChange| in order to initialize UI state of the | |
693 // checkbox. | |
694 [self preferenceDidChange:prefs::kAutoFillAuxiliaryProfilesEnabled]; | |
695 | |
696 // Do not use [NSMutableArray array] here; we need predictable destruction | |
697 // which will be prevented by having a reference held by an autorelease | |
698 // pool. | |
699 } | |
700 return self; | |
701 } | |
702 | |
703 // Run modeless. | |
704 - (void)runModelessDialog { | |
705 // Use stored window geometry if it exists. | |
706 if (g_browser_process && g_browser_process->local_state()) { | |
707 sizeSaver_.reset([[WindowSizeAutosaver alloc] | |
708 initWithWindow:[self window] | |
709 prefService:g_browser_process->local_state() | |
710 path:prefs::kAutoFillDialogPlacement]); | |
711 } | |
712 | |
713 [self showWindow:nil]; | |
714 } | |
715 | |
716 // Close the dialog. | |
717 - (void)closeDialog { | |
718 [[self window] performClose:self]; | |
719 } | |
720 | |
721 - (AutoFillAddressSheetController*)addressSheetController { | |
722 return addressSheetController.get(); | |
723 } | |
724 | |
725 - (AutoFillCreditCardSheetController*)creditCardSheetController { | |
726 return creditCardSheetController.get(); | |
727 } | |
728 | |
729 - (void)selectAddressAtIndex:(size_t)i { | |
730 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: | |
731 [self rowFromProfileIndex:i]] | |
732 byExtendingSelection:NO]; | |
733 } | |
734 | |
735 - (void)selectCreditCardAtIndex:(size_t)i { | |
736 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: | |
737 [self rowFromCreditCardIndex:i]] | |
738 byExtendingSelection:NO]; | |
739 } | |
740 | |
741 - (void)addSelectedAddressAtIndex:(size_t)i { | |
742 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: | |
743 [self rowFromProfileIndex:i]] | |
744 byExtendingSelection:YES]; | |
745 } | |
746 | |
747 - (void)addSelectedCreditCardAtIndex:(size_t)i { | |
748 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex: | |
749 [self rowFromCreditCardIndex:i]] | |
750 byExtendingSelection:YES]; | |
751 } | |
752 | |
753 - (BOOL)editButtonEnabled { | |
754 return [editButton_ isEnabled]; | |
755 } | |
756 | |
757 - (std::vector<AutoFillProfile>&)profiles { | |
758 return profiles_; | |
759 } | |
760 | |
761 - (std::vector<CreditCard>&)creditCards { | |
762 return creditCards_; | |
763 } | |
764 | |
765 @end | |
766 | |
767 @implementation AutoFillDialogController (PrivateMethods) | |
768 | |
769 // Called when the user modifies the profiles or credit card information. | |
770 - (void)save { | |
771 // If we have an |observer_| then communicate the changes back, unless | |
772 // AutoFill has been disabled through policy in the mean time. | |
773 if (observer_ && !autoFillManagedAndDisabled_) { | |
774 // Make a working copy of profiles. |OnAutoFillDialogApply| can mutate | |
775 // |profiles_|. | |
776 std::vector<AutoFillProfile> profiles = profiles_; | |
777 | |
778 // Make a working copy of credit cards. |OnAutoFillDialogApply| can mutate | |
779 // |creditCards_|. | |
780 std::vector<CreditCard> creditCards = creditCards_; | |
781 | |
782 observer_->OnAutoFillDialogApply(&profiles, &creditCards); | |
783 } | |
784 } | |
785 | |
786 - (void)onPersonalDataLoaded:(const std::vector<AutoFillProfile*>&)profiles | |
787 creditCards:(const std::vector<CreditCard*>&)creditCards { | |
788 [self onPersonalDataChanged:profiles creditCards:creditCards]; | |
789 } | |
790 | |
791 - (void)onPersonalDataChanged:(const std::vector<AutoFillProfile*>&)profiles | |
792 creditCards:(const std::vector<CreditCard*>&)creditCards { | |
793 // Make local copy of |profiles|. | |
794 profiles_.clear(); | |
795 for (std::vector<AutoFillProfile*>::const_iterator iter = profiles.begin(); | |
796 iter != profiles.end(); ++iter) | |
797 profiles_.push_back(**iter); | |
798 | |
799 // Make local copy of |creditCards|. | |
800 creditCards_.clear(); | |
801 for (std::vector<CreditCard*>::const_iterator iter = creditCards.begin(); | |
802 iter != creditCards.end(); ++iter) | |
803 creditCards_.push_back(**iter); | |
804 | |
805 [self adjustSelectionOnReload:[tableView_ selectedRow]]; | |
806 [tableView_ reloadData]; | |
807 } | |
808 | |
809 - (void)preferenceDidChange:(const std::string&)preferenceName { | |
810 if (preferenceName == prefs::kAutoFillEnabled) { | |
811 [self setAutoFillEnabled:autoFillEnabled_.GetValue()]; | |
812 [self setAutoFillManaged:autoFillEnabled_.IsManaged()]; | |
813 [self setAutoFillManagedAndDisabled: | |
814 autoFillEnabled_.IsManaged() && !autoFillEnabled_.GetValue()]; | |
815 } else if (preferenceName == prefs::kAutoFillAuxiliaryProfilesEnabled) { | |
816 [self setAuxiliaryEnabled:auxiliaryEnabled_.GetValue()]; | |
817 } else { | |
818 NOTREACHED(); | |
819 } | |
820 } | |
821 | |
822 - (void) adjustSelectionOnDelete:(NSInteger)selectedRow { | |
823 if ([self tableView:tableView_ shouldSelectRow:selectedRow-1]) { | |
824 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow-1] | |
825 byExtendingSelection:NO]; | |
826 } else if ([self tableView:tableView_ shouldSelectRow:selectedRow]) { | |
827 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow] | |
828 byExtendingSelection:NO]; | |
829 } else { | |
830 [tableView_ deselectAll:self]; | |
831 } | |
832 } | |
833 | |
834 - (void) adjustSelectionOnReload:(NSInteger)selectedRow { | |
835 if ([self tableView:tableView_ shouldSelectRow:selectedRow]) { | |
836 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow] | |
837 byExtendingSelection:NO]; | |
838 } else if ([self tableView:tableView_ shouldSelectRow:selectedRow-1]) { | |
839 [tableView_ selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow-1] | |
840 byExtendingSelection:NO]; | |
841 } else { | |
842 [tableView_ deselectAll:self]; | |
843 } | |
844 } | |
845 | |
846 - (BOOL)isProfileRow:(NSInteger)row { | |
847 if (row > 0 && static_cast<size_t>(row) <= profiles_.size()) | |
848 return YES; | |
849 return NO; | |
850 } | |
851 | |
852 - (BOOL)isProfileGroupRow:(NSInteger)row { | |
853 if (row == 0) | |
854 return YES; | |
855 return NO; | |
856 } | |
857 | |
858 - (BOOL)isCreditCardRow:(NSInteger)row { | |
859 if (row > 0 && | |
860 static_cast<size_t>(row) >= profiles_.size() + 2 && | |
861 static_cast<size_t>(row) <= profiles_.size() + creditCards_.size() + 1) | |
862 return YES; | |
863 return NO; | |
864 } | |
865 | |
866 - (BOOL)isCreditCardGroupRow:(NSInteger)row { | |
867 if (row > 0 && static_cast<size_t>(row) == profiles_.size() + 1) | |
868 return YES; | |
869 return NO; | |
870 } | |
871 | |
872 - (size_t)profileIndexFromRow:(NSInteger)row { | |
873 DCHECK([self isProfileRow:row]); | |
874 return static_cast<size_t>(row) - 1; | |
875 } | |
876 | |
877 - (size_t)creditCardIndexFromRow:(NSInteger)row { | |
878 DCHECK([self isCreditCardRow:row]); | |
879 return static_cast<size_t>(row) - (profiles_.size() + 2); | |
880 } | |
881 | |
882 - (NSInteger)rowFromProfileIndex:(size_t)i { | |
883 return 1 + i; | |
884 } | |
885 | |
886 - (NSInteger)rowFromCreditCardIndex:(size_t)i { | |
887 return 1 + profiles_.size() + 1 + i; | |
888 } | |
889 | |
890 @end | |
891 | |
892 // An NSValueTransformer subclass for use in validation of phone number | |
893 // fields. Transforms an invalid phone number string into a warning image. | |
894 // This data transformer is used in the credit card sheet for invalid phone and | |
895 // fax numbers. | |
896 @interface InvalidPhoneTransformer : NSValueTransformer { | |
897 } | |
898 @end | |
899 | |
900 @implementation InvalidPhoneTransformer | |
901 + (Class)transformedValueClass { | |
902 return [NSImage class]; | |
903 } | |
904 | |
905 + (BOOL)allowsReverseTransformation { | |
906 return NO; | |
907 } | |
908 | |
909 - (id)transformedValue:(id)string { | |
910 NSImage* image = nil; | |
911 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
912 | |
913 // We display no validation icon when input has not yet been entered. | |
914 if (string == nil || [string length] == 0) | |
915 return nil; | |
916 | |
917 // If we have input then display alert icon if we have an invalid number. | |
918 if (string != nil && [string length] != 0) { | |
919 // TODO(dhollowa): Using SetInfo() call to validate phone number. Should | |
920 // have explicit validation method. More robust validation is needed as | |
921 // well eventually. | |
922 AutoFillProfile profile; | |
923 profile.SetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), | |
924 base::SysNSStringToUTF16(string)); | |
925 if (profile.GetFieldText(AutofillType(PHONE_HOME_WHOLE_NUMBER)).empty()) { | |
926 image = rb.GetNativeImageNamed(IDR_INPUT_ALERT); | |
927 DCHECK(image); | |
928 return image; | |
929 } | |
930 } | |
931 | |
932 // No alert icon, so must be valid input. | |
933 if (!image) { | |
934 image = rb.GetNativeImageNamed(IDR_INPUT_GOOD); | |
935 DCHECK(image); | |
936 return image; | |
937 } | |
938 | |
939 return nil; | |
940 } | |
941 | |
942 @end | |
OLD | NEW |