OLD | NEW |
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 Loading... |
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 |
OLD | NEW |