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

Side by Side Diff: chrome/browser/ui/cocoa/preferences_window_controller.mm

Issue 6339002: [Mac] Consolidate all files relating to preferences in a subdir of c/b/ui/coc... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 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 | Annotate | Revision Log
OLDNEW
(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 #import "chrome/browser/ui/cocoa/preferences_window_controller.h"
6
7 #include <algorithm>
8
9 #include "app/l10n_util.h"
10 #include "app/l10n_util_mac.h"
11 #include "app/resource_bundle.h"
12 #include "base/logging.h"
13 #include "base/mac/mac_util.h"
14 #include "base/mac/scoped_aedesc.h"
15 #include "base/string16.h"
16 #include "base/string_util.h"
17 #include "base/sys_string_conversions.h"
18 #include "chrome/browser/autofill/autofill_dialog.h"
19 #include "chrome/browser/autofill/autofill_type.h"
20 #include "chrome/browser/autofill/personal_data_manager.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/download/download_manager.h"
23 #include "chrome/browser/download/download_prefs.h"
24 #include "chrome/browser/extensions/extension_service.h"
25 #include "chrome/browser/google/google_util.h"
26 #include "chrome/browser/instant/instant_confirm_dialog.h"
27 #include "chrome/browser/instant/instant_controller.h"
28 #include "chrome/browser/metrics/metrics_service.h"
29 #include "chrome/browser/metrics/user_metrics.h"
30 #include "chrome/browser/net/url_fixer_upper.h"
31 #include "chrome/browser/policy/managed_prefs_banner_base.h"
32 #include "chrome/browser/prefs/pref_service.h"
33 #include "chrome/browser/prefs/session_startup_pref.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/renderer_host/resource_dispatcher_host.h"
36 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
37 #include "chrome/browser/shell_integration.h"
38 #include "chrome/browser/sync/profile_sync_service.h"
39 #include "chrome/browser/sync/sync_ui_util.h"
40 #include "chrome/browser/tab_contents/tab_contents.h"
41 #include "chrome/browser/ui/browser.h"
42 #include "chrome/browser/ui/browser_list.h"
43 #import "chrome/browser/ui/cocoa/clear_browsing_data_controller.h"
44 #import "chrome/browser/ui/cocoa/content_settings_dialog_controller.h"
45 #import "chrome/browser/ui/cocoa/custom_home_pages_model.h"
46 #import "chrome/browser/ui/cocoa/font_language_settings_controller.h"
47 #import "chrome/browser/ui/cocoa/import_settings_dialog.h"
48 #import "chrome/browser/ui/cocoa/keyword_editor_cocoa_controller.h"
49 #import "chrome/browser/ui/cocoa/l10n_util.h"
50 #import "chrome/browser/ui/cocoa/search_engine_list_model.h"
51 #import "chrome/browser/ui/cocoa/vertical_gradient_view.h"
52 #import "chrome/browser/ui/cocoa/window_size_autosaver.h"
53 #include "chrome/browser/ui/options/options_util.h"
54 #include "chrome/browser/ui/options/options_window.h"
55 #include "chrome/browser/ui/options/show_options_url.h"
56 #include "chrome/common/notification_details.h"
57 #include "chrome/common/notification_observer.h"
58 #include "chrome/common/notification_type.h"
59 #include "chrome/common/pref_names.h"
60 #include "chrome/common/url_constants.h"
61 #include "chrome/installer/util/google_update_settings.h"
62 #include "grit/chromium_strings.h"
63 #include "grit/generated_resources.h"
64 #include "grit/locale_settings.h"
65 #include "grit/theme_resources.h"
66 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h"
67 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
68
69 namespace {
70
71 // Colors for the managed preferences warning banner.
72 static const double kBannerGradientColorTop[3] =
73 {255.0 / 255.0, 242.0 / 255.0, 183.0 / 255.0};
74 static const double kBannerGradientColorBottom[3] =
75 {250.0 / 255.0, 230.0 / 255.0, 145.0 / 255.0};
76 static const double kBannerStrokeColor = 135.0 / 255.0;
77
78 // Tag id for retrieval via viewWithTag in NSView (from IB).
79 static const uint32 kBasicsStartupPageTableTag = 1000;
80
81 bool IsNewTabUIURLString(const GURL& url) {
82 return url == GURL(chrome::kChromeUINewTabURL);
83 }
84
85 // Helper that sizes two buttons to fit in a row keeping their spacing, returns
86 // the total horizontal size change.
87 CGFloat SizeToFitButtonPair(NSButton* leftButton, NSButton* rightButton) {
88 CGFloat widthShift = 0.0;
89
90 NSSize delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:leftButton];
91 DCHECK_EQ(delta.height, 0.0) << "Height changes unsupported";
92 widthShift += delta.width;
93
94 if (widthShift != 0.0) {
95 NSPoint origin = [rightButton frame].origin;
96 origin.x += widthShift;
97 [rightButton setFrameOrigin:origin];
98 }
99 delta = [GTMUILocalizerAndLayoutTweaker sizeToFitView:rightButton];
100 DCHECK_EQ(delta.height, 0.0) << "Height changes unsupported";
101 widthShift += delta.width;
102
103 return widthShift;
104 }
105
106 // The different behaviors for the "pref group" auto sizing.
107 enum AutoSizeGroupBehavior {
108 kAutoSizeGroupBehaviorVerticalToFit,
109 kAutoSizeGroupBehaviorVerticalFirstToFit,
110 kAutoSizeGroupBehaviorHorizontalToFit,
111 kAutoSizeGroupBehaviorHorizontalFirstGrows,
112 kAutoSizeGroupBehaviorFirstTwoAsRowVerticalToFit
113 };
114
115 // Helper to tweak the layout of the "pref groups" and also ripple any height
116 // changes from one group to the next groups' origins.
117 // |views| is an ordered list of views with first being the label for the
118 // group and the rest being top down or left to right ordering of the views.
119 // The label is assumed to already be the same height as all the views it is
120 // next too.
121 CGFloat AutoSizeGroup(NSArray* views, AutoSizeGroupBehavior behavior,
122 CGFloat verticalShift) {
123 DCHECK_GE([views count], 2U) << "Should be at least a label and a control";
124 NSTextField* label = [views objectAtIndex:0];
125 DCHECK([label isKindOfClass:[NSTextField class]])
126 << "First view should be the label for the group";
127
128 // Auto size the label to see if we need more vertical space for its localized
129 // string.
130 CGFloat labelHeightChange =
131 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:label];
132
133 CGFloat localVerticalShift = 0.0;
134 switch (behavior) {
135 case kAutoSizeGroupBehaviorVerticalToFit: {
136 // Walk bottom up doing the sizing and moves.
137 for (NSUInteger index = [views count] - 1; index > 0; --index) {
138 NSView* view = [views objectAtIndex:index];
139 NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view);
140 DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height";
141 if (localVerticalShift) {
142 NSPoint origin = [view frame].origin;
143 origin.y += localVerticalShift;
144 [view setFrameOrigin:origin];
145 }
146 localVerticalShift += delta.height;
147 }
148 break;
149 }
150 case kAutoSizeGroupBehaviorVerticalFirstToFit: {
151 // Just size the top one.
152 NSView* view = [views objectAtIndex:1];
153 NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view);
154 DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height";
155 localVerticalShift += delta.height;
156 break;
157 }
158 case kAutoSizeGroupBehaviorHorizontalToFit: {
159 // Walk left to right doing the sizing and moves.
160 // NOTE: Don't worry about vertical, assume it always fits.
161 CGFloat horizontalShift = 0.0;
162 NSUInteger count = [views count];
163 for (NSUInteger index = 1; index < count; ++index) {
164 NSView* view = [views objectAtIndex:index];
165 NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view);
166 DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height";
167 if (horizontalShift) {
168 NSPoint origin = [view frame].origin;
169 origin.x += horizontalShift;
170 [view setFrameOrigin:origin];
171 }
172 horizontalShift += delta.width;
173 }
174 break;
175 }
176 case kAutoSizeGroupBehaviorHorizontalFirstGrows: {
177 // Walk right to left doing the sizing and moves, then apply the space
178 // collected into the first.
179 // NOTE: Don't worry about vertical, assume it always all fits.
180 CGFloat horizontalShift = 0.0;
181 for (NSUInteger index = [views count] - 1; index > 1; --index) {
182 NSView* view = [views objectAtIndex:index];
183 NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view);
184 DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height";
185 horizontalShift -= delta.width;
186 NSPoint origin = [view frame].origin;
187 origin.x += horizontalShift;
188 [view setFrameOrigin:origin];
189 }
190 if (horizontalShift) {
191 NSView* view = [views objectAtIndex:1];
192 NSSize delta = NSMakeSize(horizontalShift, 0.0);
193 [GTMUILocalizerAndLayoutTweaker
194 resizeViewWithoutAutoResizingSubViews:view
195 delta:delta];
196 }
197 break;
198 }
199 case kAutoSizeGroupBehaviorFirstTwoAsRowVerticalToFit: {
200 // Start out like kAutoSizeGroupBehaviorVerticalToFit but don't do
201 // the first two. Then handle the two as a row, but apply any
202 // vertical shift.
203 // All but the first two (in the row); walk bottom up.
204 for (NSUInteger index = [views count] - 1; index > 2; --index) {
205 NSView* view = [views objectAtIndex:index];
206 NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view);
207 DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height";
208 if (localVerticalShift) {
209 NSPoint origin = [view frame].origin;
210 origin.y += localVerticalShift;
211 [view setFrameOrigin:origin];
212 }
213 localVerticalShift += delta.height;
214 }
215 // Deal with the two for the horizontal row. Size the second one.
216 CGFloat horizontalShift = 0.0;
217 NSView* view = [views objectAtIndex:2];
218 NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view);
219 DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height";
220 horizontalShift -= delta.width;
221 NSPoint origin = [view frame].origin;
222 origin.x += horizontalShift;
223 if (localVerticalShift) {
224 origin.y += localVerticalShift;
225 }
226 [view setFrameOrigin:origin];
227 // Now expand the first item in the row to consume the space opened up.
228 view = [views objectAtIndex:1];
229 if (horizontalShift) {
230 NSSize delta = NSMakeSize(horizontalShift, 0.0);
231 [GTMUILocalizerAndLayoutTweaker
232 resizeViewWithoutAutoResizingSubViews:view
233 delta:delta];
234 }
235 // And move it up by any amount needed from the previous items.
236 if (localVerticalShift) {
237 NSPoint origin = [view frame].origin;
238 origin.y += localVerticalShift;
239 [view setFrameOrigin:origin];
240 }
241 break;
242 }
243 default:
244 NOTREACHED();
245 break;
246 }
247
248 // If the label grew more then the views, the other views get an extra shift.
249 // Otherwise, move the label to its top is aligned with the other views.
250 CGFloat nonLabelShift = 0.0;
251 if (labelHeightChange > localVerticalShift) {
252 // Since the lable is taller, centering the other views looks best, just
253 // shift the views by 1/2 of the size difference.
254 nonLabelShift = (labelHeightChange - localVerticalShift) / 2.0;
255 } else {
256 NSPoint origin = [label frame].origin;
257 origin.y += localVerticalShift - labelHeightChange;
258 [label setFrameOrigin:origin];
259 }
260
261 // Apply the input shift requested along with any the shift from label being
262 // taller then the rest of the group.
263 for (NSView* view in views) {
264 NSPoint origin = [view frame].origin;
265 origin.y += verticalShift;
266 if (view != label) {
267 origin.y += nonLabelShift;
268 }
269 [view setFrameOrigin:origin];
270 }
271
272 // Return how much the group grew.
273 return localVerticalShift + nonLabelShift;
274 }
275
276 // Helper to remove a view and move everything above it down to take over the
277 // space.
278 void RemoveViewFromView(NSView* view, NSView* toRemove) {
279 // Sort bottom up so we can spin over what is above it.
280 NSArray* views =
281 [[view subviews] sortedArrayUsingFunction:cocoa_l10n_util::CompareFrameY
282 context:NULL];
283
284 // Find where |toRemove| was.
285 NSUInteger index = [views indexOfObject:toRemove];
286 DCHECK_NE(index, NSNotFound);
287 NSUInteger count = [views count];
288 CGFloat shrinkHeight = 0;
289 if (index < (count - 1)) {
290 // If we're not the topmost control, the amount to shift is the bottom of
291 // |toRemove| to the bottom of the view above it.
292 CGFloat shiftDown =
293 NSMinY([[views objectAtIndex:index + 1] frame]) -
294 NSMinY([toRemove frame]);
295
296 // Now cycle over the views above it moving them down.
297 for (++index; index < count; ++index) {
298 NSView* view = [views objectAtIndex:index];
299 NSPoint origin = [view frame].origin;
300 origin.y -= shiftDown;
301 [view setFrameOrigin:origin];
302 }
303
304 shrinkHeight = shiftDown;
305 } else if (index > 0) {
306 // If we're the topmost control, there's nothing to shift but we want to
307 // shrink until the top edge of the second-topmost control, unless it is
308 // actually higher than the topmost control (since we're sorting by the
309 // bottom edge).
310 shrinkHeight = std::max(0.f,
311 NSMaxY([toRemove frame]) -
312 NSMaxY([[views objectAtIndex:index - 1] frame]));
313 }
314 // If we only have one control, don't do any resizing (for now).
315
316 // Remove |toRemove|.
317 [toRemove removeFromSuperview];
318
319 [GTMUILocalizerAndLayoutTweaker
320 resizeViewWithoutAutoResizingSubViews:view
321 delta:NSMakeSize(0, -shrinkHeight)];
322 }
323
324 // Simply removes all the views in |toRemove|.
325 void RemoveGroupFromView(NSView* view, NSArray* toRemove) {
326 for (NSView* viewToRemove in toRemove) {
327 RemoveViewFromView(view, viewToRemove);
328 }
329 }
330
331 // Helper to tweak the layout of the "Under the Hood" content by autosizing all
332 // the views and moving things up vertically. Special case the two controls for
333 // download location as they are horizontal, and should fill the row. Special
334 // case "Content Settings" and "Clear browsing data" as they are horizontal as
335 // well.
336 CGFloat AutoSizeUnderTheHoodContent(NSView* view,
337 NSPathControl* downloadLocationControl,
338 NSButton* downloadLocationButton) {
339 CGFloat verticalShift = 0.0;
340
341 // Loop bottom up through the views sizing and shifting.
342 NSArray* views =
343 [[view subviews] sortedArrayUsingFunction:cocoa_l10n_util::CompareFrameY
344 context:NULL];
345 for (NSView* view in views) {
346 NSSize delta = cocoa_l10n_util::WrapOrSizeToFit(view);
347 DCHECK_GE(delta.height, 0.0) << "Should NOT shrink in height";
348 if (verticalShift) {
349 NSPoint origin = [view frame].origin;
350 origin.y += verticalShift;
351 [view setFrameOrigin:origin];
352 }
353 verticalShift += delta.height;
354
355 // The Download Location controls go in a row with the button aligned to the
356 // right edge and the path control using all the rest of the space.
357 if (view == downloadLocationButton) {
358 NSPoint origin = [downloadLocationButton frame].origin;
359 origin.x -= delta.width;
360 [downloadLocationButton setFrameOrigin:origin];
361 NSSize controlSize = [downloadLocationControl frame].size;
362 controlSize.width -= delta.width;
363 [downloadLocationControl setFrameSize:controlSize];
364 }
365 }
366
367 return verticalShift;
368 }
369
370 } // namespace
371
372 //-------------------------------------------------------------------------
373
374 @interface PreferencesWindowController(Private)
375 // Callback when preferences are changed. |prefName| is the name of the
376 // pref that has changed.
377 - (void)prefChanged:(std::string*)prefName;
378 // Callback when sync state has changed. syncService_ needs to be
379 // queried to find out what happened.
380 - (void)syncStateChanged;
381 // Record the user performed a certain action and save the preferences.
382 - (void)recordUserAction:(const UserMetricsAction&) action;
383 - (void)registerPrefObservers;
384 - (void)configureInstant;
385
386 // KVC setter methods.
387 - (void)setNewTabPageIsHomePageIndex:(NSInteger)val;
388 - (void)setHomepageURL:(NSString*)urlString;
389 - (void)setRestoreOnStartupIndex:(NSInteger)type;
390 - (void)setShowHomeButton:(BOOL)value;
391 - (void)setPasswordManagerEnabledIndex:(NSInteger)value;
392 - (void)setIsUsingDefaultTheme:(BOOL)value;
393 - (void)setShowAlternateErrorPages:(BOOL)value;
394 - (void)setUseSuggest:(BOOL)value;
395 - (void)setDnsPrefetch:(BOOL)value;
396 - (void)setSafeBrowsing:(BOOL)value;
397 - (void)setMetricsReporting:(BOOL)value;
398 - (void)setAskForSaveLocation:(BOOL)value;
399 - (void)setFileHandlerUIEnabled:(BOOL)value;
400 - (void)setTranslateEnabled:(BOOL)value;
401 - (void)setTabsToLinks:(BOOL)value;
402 - (void)displayPreferenceViewForPage:(OptionsPage)page
403 animate:(BOOL)animate;
404 - (void)resetSubViews;
405 - (void)initBannerStateForPage:(OptionsPage)page;
406
407 // KVC getter methods.
408 - (BOOL)fileHandlerUIEnabled;
409 @end
410
411 namespace PreferencesWindowControllerInternal {
412
413 // A C++ class registered for changes in preferences. Bridges the
414 // notification back to the PWC.
415 class PrefObserverBridge : public NotificationObserver,
416 public ProfileSyncServiceObserver {
417 public:
418 PrefObserverBridge(PreferencesWindowController* controller)
419 : controller_(controller) {}
420
421 virtual ~PrefObserverBridge() {}
422
423 // Overridden from NotificationObserver:
424 virtual void Observe(NotificationType type,
425 const NotificationSource& source,
426 const NotificationDetails& details) {
427 if (type == NotificationType::PREF_CHANGED)
428 [controller_ prefChanged:Details<std::string>(details).ptr()];
429 }
430
431 // Overridden from ProfileSyncServiceObserver.
432 virtual void OnStateChanged() {
433 [controller_ syncStateChanged];
434 }
435
436 private:
437 PreferencesWindowController* controller_; // weak, owns us
438 };
439
440 // Tracks state for a managed prefs banner and triggers UI updates through the
441 // PreferencesWindowController as appropriate.
442 class ManagedPrefsBannerState : public policy::ManagedPrefsBannerBase {
443 public:
444 virtual ~ManagedPrefsBannerState() { }
445
446 explicit ManagedPrefsBannerState(PreferencesWindowController* controller,
447 OptionsPage page,
448 PrefService* local_state,
449 PrefService* prefs)
450 : policy::ManagedPrefsBannerBase(local_state, prefs, page),
451 controller_(controller),
452 page_(page) { }
453
454 BOOL IsVisible() {
455 return DetermineVisibility();
456 }
457
458 protected:
459 // Overridden from ManagedPrefsBannerBase.
460 virtual void OnUpdateVisibility() {
461 [controller_ switchToPage:page_ animate:YES];
462 }
463
464 private:
465 PreferencesWindowController* controller_; // weak, owns us
466 OptionsPage page_; // current options page
467 };
468
469 } // namespace PreferencesWindowControllerInternal
470
471 @implementation PreferencesWindowController
472
473 @synthesize restoreButtonsEnabled = restoreButtonsEnabled_;
474 @synthesize restoreURLsEnabled = restoreURLsEnabled_;
475 @synthesize showHomeButtonEnabled = showHomeButtonEnabled_;
476 @synthesize defaultSearchEngineEnabled = defaultSearchEngineEnabled_;
477 @synthesize passwordManagerChoiceEnabled = passwordManagerChoiceEnabled_;
478 @synthesize passwordManagerButtonEnabled = passwordManagerButtonEnabled_;
479 @synthesize autoFillSettingsButtonEnabled = autoFillSettingsButtonEnabled_;
480 @synthesize showAlternateErrorPagesEnabled = showAlternateErrorPagesEnabled_;
481 @synthesize useSuggestEnabled = useSuggestEnabled_;
482 @synthesize dnsPrefetchEnabled = dnsPrefetchEnabled_;
483 @synthesize safeBrowsingEnabled = safeBrowsingEnabled_;
484 @synthesize metricsReportingEnabled = metricsReportingEnabled_;
485 @synthesize proxiesConfigureButtonEnabled = proxiesConfigureButtonEnabled_;
486
487 - (id)initWithProfile:(Profile*)profile initialPage:(OptionsPage)initialPage {
488 DCHECK(profile);
489 // Use initWithWindowNibPath:: instead of initWithWindowNibName: so we
490 // can override it in a unit test.
491 NSString* nibPath = [base::mac::MainAppBundle()
492 pathForResource:@"Preferences"
493 ofType:@"nib"];
494 if ((self = [super initWithWindowNibPath:nibPath owner:self])) {
495 profile_ = profile->GetOriginalProfile();
496 initialPage_ = initialPage;
497 prefs_ = profile->GetPrefs();
498 DCHECK(prefs_);
499 observer_.reset(
500 new PreferencesWindowControllerInternal::PrefObserverBridge(self));
501
502 // Set up the model for the custom home page table. The KVO observation
503 // tells us when the number of items in the array changes. The normal
504 // observation tells us when one of the URLs of an item changes.
505 customPagesSource_.reset([[CustomHomePagesModel alloc]
506 initWithProfile:profile_]);
507 const SessionStartupPref startupPref =
508 SessionStartupPref::GetStartupPref(prefs_);
509 [customPagesSource_ setURLs:startupPref.urls];
510
511 // Set up the model for the default search popup. Register for notifications
512 // about when the model changes so we can update the selection in the view.
513 searchEngineModel_.reset(
514 [[SearchEngineListModel alloc]
515 initWithModel:profile->GetTemplateURLModel()]);
516 [[NSNotificationCenter defaultCenter]
517 addObserver:self
518 selector:@selector(searchEngineModelChanged:)
519 name:kSearchEngineListModelChangedNotification
520 object:searchEngineModel_.get()];
521
522 // This needs to be done before awakeFromNib: because the bindings set up
523 // in the nib rely on it.
524 [self registerPrefObservers];
525
526 // Use one animation so we can stop it if the user clicks quickly, and
527 // start the new animation.
528 animation_.reset([[NSViewAnimation alloc] init]);
529 // Make this the delegate so it can remove the old view at the end of the
530 // animation (once it is faded out).
531 [animation_ setDelegate:self];
532 [animation_ setAnimationBlockingMode:NSAnimationNonblocking];
533
534 // TODO(akalin): handle incognito profiles? The windows version of this
535 // (in chrome/browser/ui/views/options/content_page_view.cc) just does what
536 // we do below.
537 syncService_ = profile_->GetProfileSyncService();
538
539 // TODO(akalin): This color is taken from kSyncLabelErrorBgColor in
540 // content_page_view.cc. Either decomp that color out into a
541 // function/variable that is referenced by both this file and
542 // content_page_view.cc, or maybe pick a more suitable color.
543 syncErrorBackgroundColor_.reset(
544 [[NSColor colorWithDeviceRed:0xff/255.0
545 green:0x9a/255.0
546 blue:0x9a/255.0
547 alpha:1.0] retain]);
548
549 // Disable the |autoFillSettingsButton_| if we have no
550 // |personalDataManager|.
551 PersonalDataManager* personalDataManager =
552 profile_->GetPersonalDataManager();
553 [autoFillSettingsButton_ setHidden:(personalDataManager == NULL)];
554 bool autofill_disabled_by_policy =
555 autoFillEnabled_.IsManaged() && !autoFillEnabled_.GetValue();
556 [self setAutoFillSettingsButtonEnabled:!autofill_disabled_by_policy];
557 [self setPasswordManagerChoiceEnabled:!askSavePasswords_.IsManaged()];
558 [self setPasswordManagerButtonEnabled:
559 !askSavePasswords_.IsManaged() || askSavePasswords_.GetValue()];
560
561 // Initialize the enabled state of the elements on the general tab.
562 [self setShowHomeButtonEnabled:!showHomeButton_.IsManaged()];
563 [self setEnabledStateOfRestoreOnStartup];
564 [self setDefaultSearchEngineEnabled:![searchEngineModel_ isDefaultManaged]];
565
566 // Initialize UI state for the advanced page.
567 [self setShowAlternateErrorPagesEnabled:!alternateErrorPages_.IsManaged()];
568 [self setUseSuggestEnabled:!useSuggest_.IsManaged()];
569 [self setDnsPrefetchEnabled:!dnsPrefetch_.IsManaged()];
570 [self setSafeBrowsingEnabled:!safeBrowsing_.IsManaged()];
571 [self setMetricsReportingEnabled:!metricsReporting_.IsManaged()];
572 proxyPrefs_.reset(
573 PrefSetObserver::CreateProxyPrefSetObserver(prefs_, observer_.get()));
574 [self setProxiesConfigureButtonEnabled:!proxyPrefs_->IsManaged()];
575 }
576 return self;
577 }
578
579 - (void)awakeFromNib {
580
581 // Validate some assumptions in debug builds.
582
583 // "Basics", "Personal Stuff", and "Under the Hood" views should be the same
584 // width. They should be the same width so they are laid out to look as good
585 // as possible at that width with controls just having to wrap if their text
586 // is too long.
587 DCHECK_EQ(NSWidth([basicsView_ frame]), NSWidth([personalStuffView_ frame]))
588 << "Basics and Personal Stuff should be the same widths";
589 DCHECK_EQ(NSWidth([basicsView_ frame]), NSWidth([underTheHoodView_ frame]))
590 << "Basics and Under the Hood should be the same widths";
591 // "Under the Hood" content should always be skinnier than the scroller it
592 // goes into (we resize it).
593 DCHECK_LE(NSWidth([underTheHoodContentView_ frame]),
594 [underTheHoodScroller_ contentSize].width)
595 << "The Under the Hood content should be narrower than the content "
596 "of the scroller it goes into";
597
598 #if !defined(GOOGLE_CHROME_BUILD)
599 // "Enable logging" (breakpad and stats) is only in Google Chrome builds,
600 // remove the checkbox and slide everything above it down.
601 RemoveViewFromView(underTheHoodContentView_, enableLoggingCheckbox_);
602 #endif // !defined(GOOGLE_CHROME_BUILD)
603
604 // There are four problem children within the groups:
605 // Basics - Default Browser
606 // Personal Stuff - Sync
607 // Personal Stuff - Themes
608 // Personal Stuff - Browser Data
609 // These four have buttons that with some localizations are wider then the
610 // view. So the four get manually laid out before doing the general work so
611 // the views/window can be made wide enough to fit them. The layout in the
612 // general pass is a noop for these buttons (since they are already sized).
613
614 // Size the default browser button.
615 const NSUInteger kDefaultBrowserGroupCount = 3;
616 const NSUInteger kDefaultBrowserButtonIndex = 1;
617 DCHECK_EQ([basicsGroupDefaultBrowser_ count], kDefaultBrowserGroupCount)
618 << "Expected only two items in Default Browser group";
619 NSButton* defaultBrowserButton =
620 [basicsGroupDefaultBrowser_ objectAtIndex:kDefaultBrowserButtonIndex];
621 NSSize defaultBrowserChange =
622 [GTMUILocalizerAndLayoutTweaker sizeToFitView:defaultBrowserButton];
623 DCHECK_EQ(defaultBrowserChange.height, 0.0)
624 << "Button should have been right height in nib";
625
626 [self configureInstant];
627
628 // Size the sync row.
629 CGFloat syncRowChange = SizeToFitButtonPair(syncButton_,
630 syncCustomizeButton_);
631
632 // Size the themes row.
633 const NSUInteger kThemeGroupCount = 3;
634 const NSUInteger kThemeResetButtonIndex = 1;
635 const NSUInteger kThemeThemesButtonIndex = 2;
636 DCHECK_EQ([personalStuffGroupThemes_ count], kThemeGroupCount)
637 << "Expected only two items in Themes group";
638 CGFloat themeRowChange = SizeToFitButtonPair(
639 [personalStuffGroupThemes_ objectAtIndex:kThemeResetButtonIndex],
640 [personalStuffGroupThemes_ objectAtIndex:kThemeThemesButtonIndex]);
641
642 // Size the Privacy and Clear buttons that make a row in Under the Hood.
643 CGFloat privacyRowChange = SizeToFitButtonPair(contentSettingsButton_,
644 clearDataButton_);
645 // Under the Hood view is narrower (then the other panes) in the nib, subtract
646 // out the amount it was already going to grow to match the other panes when
647 // calculating how much the row needs things to grow.
648 privacyRowChange -=
649 ([underTheHoodScroller_ contentSize].width -
650 NSWidth([underTheHoodContentView_ frame]));
651
652 // Find the most any row changed in size.
653 CGFloat maxWidthChange = std::max(defaultBrowserChange.width, syncRowChange);
654 maxWidthChange = std::max(maxWidthChange, themeRowChange);
655 maxWidthChange = std::max(maxWidthChange, privacyRowChange);
656
657 // If any grew wider, make the views wider. If they all shrank, they fit the
658 // existing view widths, so no change is needed//.
659 if (maxWidthChange > 0.0) {
660 NSSize viewSize = [basicsView_ frame].size;
661 viewSize.width += maxWidthChange;
662 [basicsView_ setFrameSize:viewSize];
663 viewSize = [personalStuffView_ frame].size;
664 viewSize.width += maxWidthChange;
665 [personalStuffView_ setFrameSize:viewSize];
666 }
667
668 // Now that we have the width needed for Basics and Personal Stuff, lay out
669 // those pages bottom up making sure the strings fit and moving things up as
670 // needed.
671
672 CGFloat newWidth = NSWidth([basicsView_ frame]);
673 CGFloat verticalShift = 0.0;
674 verticalShift += AutoSizeGroup(basicsGroupDefaultBrowser_,
675 kAutoSizeGroupBehaviorVerticalFirstToFit,
676 verticalShift);
677 verticalShift += AutoSizeGroup(
678 basicsGroupSearchEngine_,
679 kAutoSizeGroupBehaviorFirstTwoAsRowVerticalToFit,
680 verticalShift);
681 verticalShift += AutoSizeGroup(basicsGroupToolbar_,
682 kAutoSizeGroupBehaviorVerticalToFit,
683 verticalShift);
684 verticalShift += AutoSizeGroup(basicsGroupHomePage_,
685 kAutoSizeGroupBehaviorVerticalToFit,
686 verticalShift);
687 verticalShift += AutoSizeGroup(basicsGroupStartup_,
688 kAutoSizeGroupBehaviorVerticalFirstToFit,
689 verticalShift);
690 [GTMUILocalizerAndLayoutTweaker
691 resizeViewWithoutAutoResizingSubViews:basicsView_
692 delta:NSMakeSize(0.0, verticalShift)];
693
694 verticalShift = 0.0;
695 verticalShift += AutoSizeGroup(personalStuffGroupThemes_,
696 kAutoSizeGroupBehaviorHorizontalToFit,
697 verticalShift);
698 verticalShift += AutoSizeGroup(personalStuffGroupBrowserData_,
699 kAutoSizeGroupBehaviorVerticalToFit,
700 verticalShift);
701 verticalShift += AutoSizeGroup(personalStuffGroupAutofill_,
702 kAutoSizeGroupBehaviorVerticalToFit,
703 verticalShift);
704 verticalShift += AutoSizeGroup(personalStuffGroupPasswords_,
705 kAutoSizeGroupBehaviorVerticalToFit,
706 verticalShift);
707 // TODO(akalin): Here we rely on the initial contents of the sync
708 // group's text field/link field to be large enough to hold all
709 // possible messages so that we don't have to re-layout when sync
710 // state changes. This isn't perfect, since e.g. some sync messages
711 // use the user's e-mail address (which may be really long), and the
712 // link field is usually not shown (leaving a big empty space).
713 // Rethink sync preferences UI for Mac.
714 verticalShift += AutoSizeGroup(personalStuffGroupSync_,
715 kAutoSizeGroupBehaviorVerticalToFit,
716 verticalShift);
717 [GTMUILocalizerAndLayoutTweaker
718 resizeViewWithoutAutoResizingSubViews:personalStuffView_
719 delta:NSMakeSize(0.0, verticalShift)];
720
721 if (syncService_) {
722 syncService_->AddObserver(observer_.get());
723 // Update the controls according to the initial state.
724 [self syncStateChanged];
725 } else {
726 // If sync is disabled we don't want to show the sync controls at all.
727 RemoveGroupFromView(personalStuffView_, personalStuffGroupSync_);
728 }
729
730 // Make the window as wide as the views.
731 NSWindow* prefsWindow = [self window];
732 NSView* prefsContentView = [prefsWindow contentView];
733 NSRect frame = [prefsContentView convertRect:[prefsWindow frame]
734 fromView:nil];
735 frame.size.width = newWidth;
736 frame = [prefsContentView convertRect:frame toView:nil];
737 [prefsWindow setFrame:frame display:NO];
738
739 // The Under the Hood prefs is a scroller, it shouldn't get any border, so it
740 // gets resized to be as wide as the window ended up.
741 NSSize underTheHoodSize = [underTheHoodView_ frame].size;
742 underTheHoodSize.width = newWidth;
743 [underTheHoodView_ setFrameSize:underTheHoodSize];
744
745 // Widen the Under the Hood content so things can rewrap to the full width.
746 NSSize underTheHoodContentSize = [underTheHoodContentView_ frame].size;
747 underTheHoodContentSize.width = [underTheHoodScroller_ contentSize].width;
748 [underTheHoodContentView_ setFrameSize:underTheHoodContentSize];
749
750 // Now that Under the Hood is the right width, auto-size to the new width to
751 // get the final height.
752 verticalShift = AutoSizeUnderTheHoodContent(underTheHoodContentView_,
753 downloadLocationControl_,
754 downloadLocationButton_);
755 [GTMUILocalizerAndLayoutTweaker
756 resizeViewWithoutAutoResizingSubViews:underTheHoodContentView_
757 delta:NSMakeSize(0.0, verticalShift)];
758 underTheHoodContentSize = [underTheHoodContentView_ frame].size;
759
760 // Put the Under the Hood content view into the scroller and scroll it to the
761 // top.
762 [underTheHoodScroller_ setDocumentView:underTheHoodContentView_];
763 [underTheHoodContentView_ scrollPoint:
764 NSMakePoint(0, underTheHoodContentSize.height)];
765
766 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
767 NSImage* alertIcon = rb.GetNativeImageNamed(IDR_WARNING);
768 DCHECK(alertIcon);
769 [managedPrefsBannerWarningImage_ setImage:alertIcon];
770
771 [self initBannerStateForPage:initialPage_];
772 [self switchToPage:initialPage_ animate:NO];
773
774 // Save/restore position based on prefs.
775 if (g_browser_process && g_browser_process->local_state()) {
776 sizeSaver_.reset([[WindowSizeAutosaver alloc]
777 initWithWindow:[self window]
778 prefService:g_browser_process->local_state()
779 path:prefs::kPreferencesWindowPlacement]);
780 }
781
782 // Initialize the banner gradient and stroke color.
783 NSColor* bannerStartingColor =
784 [NSColor colorWithCalibratedRed:kBannerGradientColorTop[0]
785 green:kBannerGradientColorTop[1]
786 blue:kBannerGradientColorTop[2]
787 alpha:1.0];
788 NSColor* bannerEndingColor =
789 [NSColor colorWithCalibratedRed:kBannerGradientColorBottom[0]
790 green:kBannerGradientColorBottom[1]
791 blue:kBannerGradientColorBottom[2]
792 alpha:1.0];
793 scoped_nsobject<NSGradient> bannerGradient(
794 [[NSGradient alloc] initWithStartingColor:bannerStartingColor
795 endingColor:bannerEndingColor]);
796 [managedPrefsBannerView_ setGradient:bannerGradient];
797
798 NSColor* bannerStrokeColor =
799 [NSColor colorWithCalibratedWhite:kBannerStrokeColor
800 alpha:1.0];
801 [managedPrefsBannerView_ setStrokeColor:bannerStrokeColor];
802
803 // Set accessibility related attributes.
804 NSTableView* tableView = [basicsView_ viewWithTag:kBasicsStartupPageTableTag];
805 NSString* description =
806 l10n_util::GetNSStringWithFixup(IDS_OPTIONS_STARTUP_SHOW_PAGES);
807 [tableView accessibilitySetOverrideValue:description
808 forAttribute:NSAccessibilityDescriptionAttribute];
809 }
810
811 - (void)dealloc {
812 if (syncService_) {
813 syncService_->RemoveObserver(observer_.get());
814 }
815 [[NSNotificationCenter defaultCenter] removeObserver:self];
816 [animation_ setDelegate:nil];
817 [animation_ stopAnimation];
818 [super dealloc];
819 }
820
821 // Xcode 3.1.x version of Interface Builder doesn't do a lot for editing
822 // toolbars in XIB. So the toolbar's delegate is set to the controller so it
823 // can tell the toolbar what items are selectable.
824 - (NSArray*)toolbarSelectableItemIdentifiers:(NSToolbar*)toolbar {
825 DCHECK(toolbar == toolbar_);
826 return [[toolbar_ items] valueForKey:@"itemIdentifier"];
827 }
828
829 // Register our interest in the preferences we're displaying so if anything
830 // else in the UI changes them we will be updated.
831 - (void)registerPrefObservers {
832 if (!prefs_) return;
833
834 // Basics panel
835 registrar_.Init(prefs_);
836 registrar_.Add(prefs::kURLsToRestoreOnStartup, observer_.get());
837 restoreOnStartup_.Init(prefs::kRestoreOnStartup, prefs_, observer_.get());
838 newTabPageIsHomePage_.Init(prefs::kHomePageIsNewTabPage,
839 prefs_, observer_.get());
840 homepage_.Init(prefs::kHomePage, prefs_, observer_.get());
841 showHomeButton_.Init(prefs::kShowHomeButton, prefs_, observer_.get());
842 instantEnabled_.Init(prefs::kInstantEnabled, prefs_, observer_.get());
843
844 // Personal Stuff panel
845 askSavePasswords_.Init(prefs::kPasswordManagerEnabled,
846 prefs_, observer_.get());
847 autoFillEnabled_.Init(prefs::kAutoFillEnabled, prefs_, observer_.get());
848 currentTheme_.Init(prefs::kCurrentThemeID, prefs_, observer_.get());
849
850 // Under the hood panel
851 alternateErrorPages_.Init(prefs::kAlternateErrorPagesEnabled,
852 prefs_, observer_.get());
853 useSuggest_.Init(prefs::kSearchSuggestEnabled, prefs_, observer_.get());
854 dnsPrefetch_.Init(prefs::kDnsPrefetchingEnabled, prefs_, observer_.get());
855 safeBrowsing_.Init(prefs::kSafeBrowsingEnabled, prefs_, observer_.get());
856 autoOpenFiles_.Init(
857 prefs::kDownloadExtensionsToOpen, prefs_, observer_.get());
858 translateEnabled_.Init(prefs::kEnableTranslate, prefs_, observer_.get());
859 tabsToLinks_.Init(prefs::kWebkitTabsToLinks, prefs_, observer_.get());
860
861 // During unit tests, there is no local state object, so we fall back to
862 // the prefs object (where we've explicitly registered this pref so we
863 // know it's there).
864 PrefService* local = g_browser_process->local_state();
865 if (!local)
866 local = prefs_;
867 metricsReporting_.Init(prefs::kMetricsReportingEnabled,
868 local, observer_.get());
869 defaultDownloadLocation_.Init(prefs::kDownloadDefaultDirectory, prefs_,
870 observer_.get());
871 askForSaveLocation_.Init(prefs::kPromptForDownload, prefs_, observer_.get());
872
873 // We don't need to observe changes in this value.
874 lastSelectedPage_.Init(prefs::kOptionsWindowLastTabIndex, local, NULL);
875 }
876
877 // Called when the window wants to be closed.
878 - (BOOL)windowShouldClose:(id)sender {
879 // Stop any animation and clear the delegate to avoid stale pointers.
880 [animation_ setDelegate:nil];
881 [animation_ stopAnimation];
882
883 return YES;
884 }
885
886 // Called when the user hits the escape key. Closes the window.
887 - (void)cancel:(id)sender {
888 [[self window] performClose:self];
889 }
890
891 // Record the user performed a certain action and save the preferences.
892 - (void)recordUserAction:(const UserMetricsAction &)action {
893 UserMetrics::RecordAction(action, profile_);
894 if (prefs_)
895 prefs_->ScheduleSavePersistentPrefs();
896 }
897
898 // Returns the set of keys that |key| depends on for its value so it can be
899 // re-computed when any of those change as well.
900 + (NSSet*)keyPathsForValuesAffectingValueForKey:(NSString*)key {
901 NSSet* paths = [super keyPathsForValuesAffectingValueForKey:key];
902 if ([key isEqualToString:@"isHomepageURLEnabled"]) {
903 paths = [paths setByAddingObject:@"newTabPageIsHomePageIndex"];
904 paths = [paths setByAddingObject:@"homepageURL"];
905 } else if ([key isEqualToString:@"restoreURLsEnabled"]) {
906 paths = [paths setByAddingObject:@"restoreOnStartupIndex"];
907 } else if ([key isEqualToString:@"isHomepageChoiceEnabled"]) {
908 paths = [paths setByAddingObject:@"newTabPageIsHomePageIndex"];
909 paths = [paths setByAddingObject:@"homepageURL"];
910 } else if ([key isEqualToString:@"newTabPageIsHomePageIndex"]) {
911 paths = [paths setByAddingObject:@"homepageURL"];
912 } else if ([key isEqualToString:@"hompageURL"]) {
913 paths = [paths setByAddingObject:@"newTabPageIsHomePageIndex"];
914 } else if ([key isEqualToString:@"isDefaultBrowser"]) {
915 paths = [paths setByAddingObject:@"defaultBrowser"];
916 } else if ([key isEqualToString:@"defaultBrowserTextColor"]) {
917 paths = [paths setByAddingObject:@"defaultBrowser"];
918 } else if ([key isEqualToString:@"defaultBrowserText"]) {
919 paths = [paths setByAddingObject:@"defaultBrowser"];
920 }
921 return paths;
922 }
923
924 // Launch the Keychain Access app.
925 - (void)launchKeychainAccess {
926 NSString* const kKeychainBundleId = @"com.apple.keychainaccess";
927 [[NSWorkspace sharedWorkspace]
928 launchAppWithBundleIdentifier:kKeychainBundleId
929 options:0L
930 additionalEventParamDescriptor:nil
931 launchIdentifier:nil];
932 }
933
934 //-------------------------------------------------------------------------
935 // Basics panel
936
937 // Sets the home page preferences for kNewTabPageIsHomePage and kHomePage. If a
938 // blank or null-host URL is passed in we revert to using NewTab page
939 // as the Home page. Note: using SetValue() causes the observers not to fire,
940 // which is actually a good thing as we could end up in a state where setting
941 // the homepage to an empty url would automatically reset the prefs back to
942 // using the NTP, so we'd be never be able to change it.
943 - (void)setHomepage:(const GURL&)homepage {
944 if (IsNewTabUIURLString(homepage)) {
945 newTabPageIsHomePage_.SetValueIfNotManaged(true);
946 homepage_.SetValueIfNotManaged(std::string());
947 } else if (!homepage.is_valid()) {
948 newTabPageIsHomePage_.SetValueIfNotManaged(true);
949 if (!homepage.has_host())
950 homepage_.SetValueIfNotManaged(std::string());
951 } else {
952 homepage_.SetValueIfNotManaged(homepage.spec());
953 }
954 }
955
956 // Callback when preferences are changed by someone modifying the prefs backend
957 // externally. |prefName| is the name of the pref that has changed. Unlike on
958 // Windows, we don't need to use this method for initializing, that's handled by
959 // Cocoa Bindings.
960 // Handles prefs for the "Basics" panel.
961 - (void)basicsPrefChanged:(std::string*)prefName {
962 if (*prefName == prefs::kRestoreOnStartup) {
963 const SessionStartupPref startupPref =
964 SessionStartupPref::GetStartupPref(prefs_);
965 [self setRestoreOnStartupIndex:startupPref.type];
966 [self setEnabledStateOfRestoreOnStartup];
967 } else if (*prefName == prefs::kURLsToRestoreOnStartup) {
968 [customPagesSource_ reloadURLs];
969 [self setEnabledStateOfRestoreOnStartup];
970 } else if (*prefName == prefs::kHomePageIsNewTabPage) {
971 NSInteger useNewTabPage = newTabPageIsHomePage_.GetValue() ? 0 : 1;
972 [self setNewTabPageIsHomePageIndex:useNewTabPage];
973 } else if (*prefName == prefs::kHomePage) {
974 NSString* value = base::SysUTF8ToNSString(homepage_.GetValue());
975 [self setHomepageURL:value];
976 } else if (*prefName == prefs::kShowHomeButton) {
977 [self setShowHomeButton:showHomeButton_.GetValue() ? YES : NO];
978 [self setShowHomeButtonEnabled:!showHomeButton_.IsManaged()];
979 } else if (*prefName == prefs::kInstantEnabled) {
980 [self configureInstant];
981 }
982 }
983
984 // Returns the index of the selected cell in the "on startup" matrix based
985 // on the "restore on startup" pref. The ordering of the cells is in the
986 // same order as the pref.
987 - (NSInteger)restoreOnStartupIndex {
988 const SessionStartupPref pref = SessionStartupPref::GetStartupPref(prefs_);
989 return pref.type;
990 }
991
992 // A helper function that takes the startup session type, grabs the URLs to
993 // restore, and saves it all in prefs.
994 - (void)saveSessionStartupWithType:(SessionStartupPref::Type)type {
995 SessionStartupPref pref;
996 pref.type = type;
997 pref.urls = [customPagesSource_.get() URLs];
998 SessionStartupPref::SetStartupPref(prefs_, pref);
999 }
1000
1001 // Sets the pref based on the index of the selected cell in the matrix and
1002 // marks the appropriate user metric.
1003 - (void)setRestoreOnStartupIndex:(NSInteger)type {
1004 SessionStartupPref::Type startupType =
1005 static_cast<SessionStartupPref::Type>(type);
1006 switch (startupType) {
1007 case SessionStartupPref::DEFAULT:
1008 [self recordUserAction:UserMetricsAction("Options_Startup_Homepage")];
1009 break;
1010 case SessionStartupPref::LAST:
1011 [self recordUserAction:UserMetricsAction("Options_Startup_LastSession")];
1012 break;
1013 case SessionStartupPref::URLS:
1014 [self recordUserAction:UserMetricsAction("Options_Startup_Custom")];
1015 break;
1016 default:
1017 NOTREACHED();
1018 }
1019 [self saveSessionStartupWithType:startupType];
1020 }
1021
1022 // Enables or disables the restoreOnStartup elements
1023 - (void) setEnabledStateOfRestoreOnStartup {
1024 const SessionStartupPref startupPref =
1025 SessionStartupPref::GetStartupPref(prefs_);
1026 [self setRestoreButtonsEnabled:!SessionStartupPref::TypeIsManaged(prefs_)];
1027 [self setRestoreURLsEnabled:!SessionStartupPref::URLsAreManaged(prefs_) &&
1028 [self restoreOnStartupIndex] == SessionStartupPref::URLS];
1029 }
1030
1031 // Getter for the |customPagesSource| property for bindings.
1032 - (CustomHomePagesModel*)customPagesSource {
1033 return customPagesSource_.get();
1034 }
1035
1036 // Called when the selection in the table changes. If a flag is set indicating
1037 // that we're waiting for a special select message, edit the cell. Otherwise
1038 // just ignore it, we don't normally care.
1039 - (void)tableViewSelectionDidChange:(NSNotification*)aNotification {
1040 if (pendingSelectForEdit_) {
1041 NSTableView* table = [aNotification object];
1042 NSUInteger selectedRow = [table selectedRow];
1043 [table editColumn:0 row:selectedRow withEvent:nil select:YES];
1044 pendingSelectForEdit_ = NO;
1045 }
1046 }
1047
1048 // Called when the user hits the (+) button for adding a new homepage to the
1049 // list. This will also attempt to make the new item editable so the user can
1050 // just start typing.
1051 - (IBAction)addHomepage:(id)sender {
1052 [customPagesArrayController_ add:sender];
1053
1054 // When the new item is added to the model, the array controller will select
1055 // it. We'll watch for that notification (because we are the table view's
1056 // delegate) and then make the cell editable. Note that this can't be
1057 // accomplished simply by subclassing the array controller's add method (I
1058 // did try). The update of the table is asynchronous with the controller
1059 // updating the model.
1060 pendingSelectForEdit_ = YES;
1061 }
1062
1063 // Called when the user hits the (-) button for removing the selected items in
1064 // the homepage table. The controller does all the work.
1065 - (IBAction)removeSelectedHomepages:(id)sender {
1066 [customPagesArrayController_ remove:sender];
1067 }
1068
1069 // Add all entries for all open browsers with our profile.
1070 - (IBAction)useCurrentPagesAsHomepage:(id)sender {
1071 std::vector<GURL> urls;
1072 for (BrowserList::const_iterator browserIter = BrowserList::begin();
1073 browserIter != BrowserList::end(); ++browserIter) {
1074 Browser* browser = *browserIter;
1075 if (browser->profile() != profile_)
1076 continue; // Only want entries for open profile.
1077
1078 for (int tabIndex = 0; tabIndex < browser->tab_count(); ++tabIndex) {
1079 TabContents* tab = browser->GetTabContentsAt(tabIndex);
1080 if (tab->ShouldDisplayURL()) {
1081 const GURL url = browser->GetTabContentsAt(tabIndex)->GetURL();
1082 if (!url.is_empty())
1083 urls.push_back(url);
1084 }
1085 }
1086 }
1087 [customPagesSource_ setURLs:urls];
1088 }
1089
1090 enum { kHomepageNewTabPage, kHomepageURL };
1091
1092 // Here's a table describing the desired characteristics of the homepage choice
1093 // radio value, it's enabled state and the URL field enabled state. They depend
1094 // on the values of the managed bits for homepage (m_hp) and
1095 // homepageIsNewTabPage (m_ntp) preferences, as well as the value of the
1096 // homepageIsNewTabPage preference (ntp) and whether the homepage preference
1097 // is equal to the new tab page URL (hpisntp).
1098 //
1099 // m_hp m_ntp ntp hpisntp | choice value | choice enabled | URL field enabled
1100 // --------------------------------------------------------------------------
1101 // 0 0 0 0 | homepage | 1 | 1
1102 // 0 0 0 1 | new tab page | 1 | 0
1103 // 0 0 1 0 | new tab page | 1 | 0
1104 // 0 0 1 1 | new tab page | 1 | 0
1105 // 0 1 0 0 | homepage | 0 | 1
1106 // 0 1 0 1 | homepage | 0 | 1
1107 // 0 1 1 0 | new tab page | 0 | 0
1108 // 0 1 1 1 | new tab page | 0 | 0
1109 // 1 0 0 0 | homepage | 1 | 0
1110 // 1 0 0 1 | new tab page | 0 | 0
1111 // 1 0 1 0 | new tab page | 1 | 0
1112 // 1 0 1 1 | new tab page | 0 | 0
1113 // 1 1 0 0 | homepage | 0 | 0
1114 // 1 1 0 1 | new tab page | 0 | 0
1115 // 1 1 1 0 | new tab page | 0 | 0
1116 // 1 1 1 1 | new tab page | 0 | 0
1117 //
1118 // thus, we have:
1119 //
1120 // choice value is new tab page === ntp || (hpisntp && (m_hp || !m_ntp))
1121 // choice enabled === !m_ntp && !(m_hp && hpisntp)
1122 // URL field enabled === !ntp && !mhp && !(hpisntp && !m_ntp)
1123 //
1124 // which also make sense if you think about them.
1125
1126 // Checks whether the homepage URL refers to the new tab page.
1127 - (BOOL)isHomepageNewTabUIURL {
1128 return IsNewTabUIURLString(GURL(homepage_.GetValue().c_str()));
1129 }
1130
1131 // Returns the index of the selected cell in the "home page" marix based on
1132 // the "new tab is home page" pref. Sadly, the ordering is reversed from the
1133 // pref value.
1134 - (NSInteger)newTabPageIsHomePageIndex {
1135 return newTabPageIsHomePage_.GetValue() ||
1136 ([self isHomepageNewTabUIURL] &&
1137 (homepage_.IsManaged() || !newTabPageIsHomePage_.IsManaged())) ?
1138 kHomepageNewTabPage : kHomepageURL;
1139 }
1140
1141 // Sets the pref based on the given index into the matrix and marks the
1142 // appropriate user metric.
1143 - (void)setNewTabPageIsHomePageIndex:(NSInteger)index {
1144 bool useNewTabPage = index == kHomepageNewTabPage ? true : false;
1145 if (useNewTabPage) {
1146 [self recordUserAction:UserMetricsAction("Options_Homepage_UseNewTab")];
1147 } else {
1148 [self recordUserAction:UserMetricsAction("Options_Homepage_UseURL")];
1149 if ([self isHomepageNewTabUIURL])
1150 homepage_.SetValueIfNotManaged(std::string());
1151 }
1152 newTabPageIsHomePage_.SetValueIfNotManaged(useNewTabPage);
1153 }
1154
1155 // Check whether the new tab and URL homepage radios should be enabled, i.e. if
1156 // the corresponding preference is not managed through configuration policy.
1157 - (BOOL)isHomepageChoiceEnabled {
1158 return !newTabPageIsHomePage_.IsManaged() &&
1159 !(homepage_.IsManaged() && [self isHomepageNewTabUIURL]);
1160 }
1161
1162 // Returns whether or not the homepage URL text field should be enabled
1163 // based on if the new tab page is the home page.
1164 - (BOOL)isHomepageURLEnabled {
1165 return !newTabPageIsHomePage_.GetValue() && !homepage_.IsManaged() &&
1166 !([self isHomepageNewTabUIURL] && !newTabPageIsHomePage_.IsManaged());
1167 }
1168
1169 // Returns the homepage URL.
1170 - (NSString*)homepageURL {
1171 NSString* value = base::SysUTF8ToNSString(homepage_.GetValue());
1172 return [self isHomepageNewTabUIURL] ? nil : value;
1173 }
1174
1175 // Sets the homepage URL to |urlString| with some fixing up.
1176 - (void)setHomepageURL:(NSString*)urlString {
1177 // If the text field contains a valid URL, sync it to prefs. We run it
1178 // through the fixer upper to allow input like "google.com" to be converted
1179 // to something valid ("http://google.com").
1180 std::string unfixedURL = urlString ? base::SysNSStringToUTF8(urlString) :
1181 chrome::kChromeUINewTabURL;
1182 [self setHomepage:URLFixerUpper::FixupURL(unfixedURL, std::string())];
1183 }
1184
1185 // Returns whether the home button should be checked based on the preference.
1186 - (BOOL)showHomeButton {
1187 return showHomeButton_.GetValue() ? YES : NO;
1188 }
1189
1190 // Sets the backend pref for whether or not the home button should be displayed
1191 // based on |value|.
1192 - (void)setShowHomeButton:(BOOL)value {
1193 if (value)
1194 [self recordUserAction:UserMetricsAction(
1195 "Options_Homepage_ShowHomeButton")];
1196 else
1197 [self recordUserAction:UserMetricsAction(
1198 "Options_Homepage_HideHomeButton")];
1199 showHomeButton_.SetValueIfNotManaged(value ? true : false);
1200 }
1201
1202 // Getter for the |searchEngineModel| property for bindings.
1203 - (id)searchEngineModel {
1204 return searchEngineModel_.get();
1205 }
1206
1207 // Bindings for the search engine popup. We not binding directly to the model
1208 // in order to siphon off the setter so we can record the metric. If we're
1209 // doing it with one, might as well do it with both.
1210 - (NSUInteger)searchEngineSelectedIndex {
1211 return [searchEngineModel_ defaultIndex];
1212 }
1213
1214 - (void)setSearchEngineSelectedIndex:(NSUInteger)index {
1215 [self recordUserAction:UserMetricsAction("Options_SearchEngineChanged")];
1216 [searchEngineModel_ setDefaultIndex:index];
1217 }
1218
1219 // Called when the search engine model changes. Update the selection in the
1220 // popup by tickling the bindings with the new value.
1221 - (void)searchEngineModelChanged:(NSNotification*)notify {
1222 [self setSearchEngineSelectedIndex:[self searchEngineSelectedIndex]];
1223 [self setDefaultSearchEngineEnabled:![searchEngineModel_ isDefaultManaged]];
1224
1225 }
1226
1227 - (IBAction)manageSearchEngines:(id)sender {
1228 [KeywordEditorCocoaController showKeywordEditor:profile_];
1229 }
1230
1231 - (IBAction)toggleInstant:(id)sender {
1232 if (instantEnabled_.GetValue()) {
1233 InstantController::Disable(profile_);
1234 } else {
1235 [instantCheckbox_ setState:NSOffState];
1236 browser::ShowInstantConfirmDialogIfNecessary([self window], profile_);
1237 }
1238 }
1239
1240 // Sets the state of the Instant checkbox and adds the type information to the
1241 // label.
1242 - (void)configureInstant {
1243 bool enabled = instantEnabled_.GetValue();
1244 NSInteger state = enabled ? NSOnState : NSOffState;
1245 [instantCheckbox_ setState:state];
1246 }
1247
1248 - (IBAction)learnMoreAboutInstant:(id)sender {
1249 browser::ShowOptionsURL(profile_, browser::InstantLearnMoreURL());
1250 }
1251
1252 // Called when the user clicks the button to make Chromium the default
1253 // browser. Registers http and https.
1254 - (IBAction)makeDefaultBrowser:(id)sender {
1255 [self willChangeValueForKey:@"defaultBrowser"];
1256
1257 ShellIntegration::SetAsDefaultBrowser();
1258 [self recordUserAction:UserMetricsAction("Options_SetAsDefaultBrowser")];
1259 // If the user made Chrome the default browser, then he/she arguably wants
1260 // to be notified when that changes.
1261 prefs_->SetBoolean(prefs::kCheckDefaultBrowser, true);
1262
1263 // Tickle KVO so that the UI updates.
1264 [self didChangeValueForKey:@"defaultBrowser"];
1265 }
1266
1267 // Returns the Chromium default browser state.
1268 - (ShellIntegration::DefaultBrowserState)isDefaultBrowser {
1269 return ShellIntegration::IsDefaultBrowser();
1270 }
1271
1272 // Returns the text color of the "chromium is your default browser" text (green
1273 // for yes, red for no).
1274 - (NSColor*)defaultBrowserTextColor {
1275 ShellIntegration::DefaultBrowserState state = [self isDefaultBrowser];
1276 return (state == ShellIntegration::IS_DEFAULT_BROWSER) ?
1277 [NSColor colorWithCalibratedRed:0.0 green:135.0/255.0 blue:0 alpha:1.0] :
1278 [NSColor colorWithCalibratedRed:135.0/255.0 green:0 blue:0 alpha:1.0];
1279 }
1280
1281 // Returns the text for the "chromium is your default browser" string dependent
1282 // on if Chromium actually is or not.
1283 - (NSString*)defaultBrowserText {
1284 ShellIntegration::DefaultBrowserState state = [self isDefaultBrowser];
1285 int stringId;
1286 if (state == ShellIntegration::IS_DEFAULT_BROWSER)
1287 stringId = IDS_OPTIONS_DEFAULTBROWSER_DEFAULT;
1288 else if (state == ShellIntegration::NOT_DEFAULT_BROWSER)
1289 stringId = IDS_OPTIONS_DEFAULTBROWSER_NOTDEFAULT;
1290 else
1291 stringId = IDS_OPTIONS_DEFAULTBROWSER_UNKNOWN;
1292 string16 text =
1293 l10n_util::GetStringFUTF16(stringId,
1294 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
1295 return base::SysUTF16ToNSString(text);
1296 }
1297
1298 //-------------------------------------------------------------------------
1299 // User Data panel
1300
1301 // Since passwords and forms are radio groups, 'enabled' is index 0 and
1302 // 'disabled' is index 1. Yay.
1303 const int kEnabledIndex = 0;
1304 const int kDisabledIndex = 1;
1305
1306 // Callback when preferences are changed. |prefName| is the name of the pref
1307 // that has changed. Unlike on Windows, we don't need to use this method for
1308 // initializing, that's handled by Cocoa Bindings.
1309 // Handles prefs for the "Personal Stuff" panel.
1310 - (void)userDataPrefChanged:(std::string*)prefName {
1311 if (*prefName == prefs::kPasswordManagerEnabled) {
1312 [self setPasswordManagerEnabledIndex:askSavePasswords_.GetValue() ?
1313 kEnabledIndex : kDisabledIndex];
1314 [self setPasswordManagerChoiceEnabled:!askSavePasswords_.IsManaged()];
1315 [self setPasswordManagerButtonEnabled:
1316 !askSavePasswords_.IsManaged() || askSavePasswords_.GetValue()];
1317 }
1318 if (*prefName == prefs::kAutoFillEnabled) {
1319 bool autofill_disabled_by_policy =
1320 autoFillEnabled_.IsManaged() && !autoFillEnabled_.GetValue();
1321 [self setAutoFillSettingsButtonEnabled:!autofill_disabled_by_policy];
1322 }
1323 if (*prefName == prefs::kCurrentThemeID) {
1324 [self setIsUsingDefaultTheme:currentTheme_.GetValue().length() == 0];
1325 }
1326 }
1327
1328 // Called to launch the Keychain Access app to show the user's stored
1329 // passwords.
1330 - (IBAction)showSavedPasswords:(id)sender {
1331 [self recordUserAction:UserMetricsAction("Options_ShowPasswordsExceptions")];
1332 [self launchKeychainAccess];
1333 }
1334
1335 // Called to show the Auto Fill Settings dialog.
1336 - (IBAction)showAutoFillSettings:(id)sender {
1337 [self recordUserAction:UserMetricsAction("Options_ShowAutoFillSettings")];
1338
1339 PersonalDataManager* personalDataManager = profile_->GetPersonalDataManager();
1340 if (!personalDataManager) {
1341 // Should not reach here because button is disabled when
1342 // |personalDataManager| is NULL.
1343 NOTREACHED();
1344 return;
1345 }
1346
1347 ShowAutoFillDialog(NULL, personalDataManager, profile_);
1348 }
1349
1350 // Called to import data from other browsers (Safari, Firefox, etc).
1351 - (IBAction)importData:(id)sender {
1352 UserMetrics::RecordAction(UserMetricsAction("Import_ShowDlg"), profile_);
1353 [ImportSettingsDialogController showImportSettingsDialogForProfile:profile_];
1354 }
1355
1356 - (IBAction)resetThemeToDefault:(id)sender {
1357 [self recordUserAction:UserMetricsAction("Options_ThemesReset")];
1358 profile_->ClearTheme();
1359 }
1360
1361 - (IBAction)themesGallery:(id)sender {
1362 [self recordUserAction:UserMetricsAction("Options_ThemesGallery")];
1363 Browser* browser = BrowserList::GetLastActive();
1364
1365 if (!browser || !browser->GetSelectedTabContents())
1366 browser = Browser::Create(profile_);
1367 browser->OpenThemeGalleryTabAndActivate();
1368 }
1369
1370 // Called when the "stop syncing" confirmation dialog started by
1371 // doSyncAction is finished. Stop syncing only If the user clicked
1372 // OK.
1373 - (void)stopSyncAlertDidEnd:(NSAlert*)alert
1374 returnCode:(int)returnCode
1375 contextInfo:(void*)contextInfo {
1376 DCHECK(syncService_ && !syncService_->IsManaged());
1377 if (returnCode == NSAlertFirstButtonReturn) {
1378 syncService_->DisableForUser();
1379 ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS);
1380 }
1381 }
1382
1383 // Called when the user clicks the multi-purpose sync button in the
1384 // "Personal Stuff" pane.
1385 - (IBAction)doSyncAction:(id)sender {
1386 DCHECK(syncService_ && !syncService_->IsManaged());
1387 if (syncService_->HasSyncSetupCompleted()) {
1388 // If sync setup has completed that means the sync button was a
1389 // "stop syncing" button. Bring up a confirmation dialog before
1390 // actually stopping syncing (see stopSyncAlertDidEnd).
1391 scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]);
1392 [alert addButtonWithTitle:l10n_util::GetNSStringWithFixup(
1393 IDS_SYNC_STOP_SYNCING_CONFIRM_BUTTON_LABEL)];
1394 [alert addButtonWithTitle:l10n_util::GetNSStringWithFixup(
1395 IDS_CANCEL)];
1396 [alert setMessageText:l10n_util::GetNSStringWithFixup(
1397 IDS_SYNC_STOP_SYNCING_DIALOG_TITLE)];
1398 [alert setInformativeText:l10n_util::GetNSStringFWithFixup(
1399 IDS_SYNC_STOP_SYNCING_EXPLANATION_LABEL,
1400 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))];
1401 [alert setAlertStyle:NSWarningAlertStyle];
1402 const SEL kEndSelector =
1403 @selector(stopSyncAlertDidEnd:returnCode:contextInfo:);
1404 [alert beginSheetModalForWindow:[self window]
1405 modalDelegate:self
1406 didEndSelector:kEndSelector
1407 contextInfo:NULL];
1408 } else {
1409 // Otherwise, the sync button was a "sync my bookmarks" button.
1410 // Kick off the sync setup process.
1411 syncService_->ShowLoginDialog(NULL);
1412 ProfileSyncService::SyncEvent(ProfileSyncService::START_FROM_OPTIONS);
1413 }
1414 }
1415
1416 // Called when the user clicks on the link to the privacy dashboard.
1417 - (IBAction)showPrivacyDashboard:(id)sender {
1418 Browser* browser = BrowserList::GetLastActive();
1419
1420 if (!browser || !browser->GetSelectedTabContents())
1421 browser = Browser::Create(profile_);
1422 browser->OpenPrivacyDashboardTabAndActivate();
1423 }
1424
1425 // Called when the user clicks the "Customize Sync" button in the
1426 // "Personal Stuff" pane. Spawns a dialog-modal sheet that cleans
1427 // itself up on close.
1428 - (IBAction)doSyncCustomize:(id)sender {
1429 syncService_->ShowConfigure(NULL);
1430 }
1431
1432 - (IBAction)doSyncReauthentication:(id)sender {
1433 DCHECK(syncService_ && !syncService_->IsManaged());
1434 syncService_->ShowLoginDialog(NULL);
1435 }
1436
1437 - (void)setPasswordManagerEnabledIndex:(NSInteger)value {
1438 if (value == kEnabledIndex)
1439 [self recordUserAction:UserMetricsAction(
1440 "Options_PasswordManager_Enable")];
1441 else
1442 [self recordUserAction:UserMetricsAction(
1443 "Options_PasswordManager_Disable")];
1444 askSavePasswords_.SetValueIfNotManaged(value == kEnabledIndex ? true : false);
1445 }
1446
1447 - (NSInteger)passwordManagerEnabledIndex {
1448 return askSavePasswords_.GetValue() ? kEnabledIndex : kDisabledIndex;
1449 }
1450
1451 - (void)setIsUsingDefaultTheme:(BOOL)value {
1452 if (value)
1453 [self recordUserAction:UserMetricsAction(
1454 "Options_IsUsingDefaultTheme_Enable")];
1455 else
1456 [self recordUserAction:UserMetricsAction(
1457 "Options_IsUsingDefaultTheme_Disable")];
1458 }
1459
1460 - (BOOL)isUsingDefaultTheme {
1461 return currentTheme_.GetValue().length() == 0;
1462 }
1463
1464 //-------------------------------------------------------------------------
1465 // Under the hood panel
1466
1467 // Callback when preferences are changed. |prefName| is the name of the pref
1468 // that has changed. Unlike on Windows, we don't need to use this method for
1469 // initializing, that's handled by Cocoa Bindings.
1470 // Handles prefs for the "Under the hood" panel.
1471 - (void)underHoodPrefChanged:(std::string*)prefName {
1472 if (*prefName == prefs::kAlternateErrorPagesEnabled) {
1473 [self setShowAlternateErrorPages:
1474 alternateErrorPages_.GetValue() ? YES : NO];
1475 [self setShowAlternateErrorPagesEnabled:!alternateErrorPages_.IsManaged()];
1476 }
1477 else if (*prefName == prefs::kSearchSuggestEnabled) {
1478 [self setUseSuggest:useSuggest_.GetValue() ? YES : NO];
1479 [self setUseSuggestEnabled:!useSuggest_.IsManaged()];
1480 }
1481 else if (*prefName == prefs::kDnsPrefetchingEnabled) {
1482 [self setDnsPrefetch:dnsPrefetch_.GetValue() ? YES : NO];
1483 [self setDnsPrefetchEnabled:!dnsPrefetch_.IsManaged()];
1484 }
1485 else if (*prefName == prefs::kSafeBrowsingEnabled) {
1486 [self setSafeBrowsing:safeBrowsing_.GetValue() ? YES : NO];
1487 [self setSafeBrowsingEnabled:!safeBrowsing_.IsManaged()];
1488 }
1489 else if (*prefName == prefs::kMetricsReportingEnabled) {
1490 [self setMetricsReporting:metricsReporting_.GetValue() ? YES : NO];
1491 [self setMetricsReportingEnabled:!metricsReporting_.IsManaged()];
1492 }
1493 else if (*prefName == prefs::kDownloadDefaultDirectory) {
1494 // Poke KVO.
1495 [self willChangeValueForKey:@"defaultDownloadLocation"];
1496 [self didChangeValueForKey:@"defaultDownloadLocation"];
1497 }
1498 else if (*prefName == prefs::kPromptForDownload) {
1499 [self setAskForSaveLocation:askForSaveLocation_.GetValue() ? YES : NO];
1500 }
1501 else if (*prefName == prefs::kEnableTranslate) {
1502 [self setTranslateEnabled:translateEnabled_.GetValue() ? YES : NO];
1503 }
1504 else if (*prefName == prefs::kWebkitTabsToLinks) {
1505 [self setTabsToLinks:tabsToLinks_.GetValue() ? YES : NO];
1506 }
1507 else if (*prefName == prefs::kDownloadExtensionsToOpen) {
1508 // Poke KVC.
1509 [self setFileHandlerUIEnabled:[self fileHandlerUIEnabled]];
1510 }
1511 else if (proxyPrefs_->IsObserved(*prefName)) {
1512 [self setProxiesConfigureButtonEnabled:!proxyPrefs_->IsManaged()];
1513 }
1514 }
1515
1516 // Set the new download path and notify the UI via KVO.
1517 - (void)downloadPathPanelDidEnd:(NSOpenPanel*)panel
1518 code:(NSInteger)returnCode
1519 context:(void*)context {
1520 if (returnCode == NSOKButton) {
1521 [self recordUserAction:UserMetricsAction("Options_SetDownloadDirectory")];
1522 NSURL* path = [[panel URLs] lastObject]; // We only allow 1 item.
1523 [self willChangeValueForKey:@"defaultDownloadLocation"];
1524 defaultDownloadLocation_.SetValue(base::SysNSStringToUTF8([path path]));
1525 [self didChangeValueForKey:@"defaultDownloadLocation"];
1526 }
1527 }
1528
1529 // Bring up an open panel to allow the user to set a new downloads location.
1530 - (void)browseDownloadLocation:(id)sender {
1531 NSOpenPanel* panel = [NSOpenPanel openPanel];
1532 [panel setAllowsMultipleSelection:NO];
1533 [panel setCanChooseFiles:NO];
1534 [panel setCanChooseDirectories:YES];
1535 NSString* path = base::SysUTF8ToNSString(defaultDownloadLocation_.GetValue());
1536 [panel beginSheetForDirectory:path
1537 file:nil
1538 types:nil
1539 modalForWindow:[self window]
1540 modalDelegate:self
1541 didEndSelector:@selector(downloadPathPanelDidEnd:code:context:)
1542 contextInfo:NULL];
1543 }
1544
1545 // Called to clear user's browsing data. This puts up an application-modal
1546 // dialog to guide the user through clearing the data.
1547 - (IBAction)clearData:(id)sender {
1548 [ClearBrowsingDataController
1549 showClearBrowsingDialogForProfile:profile_];
1550 }
1551
1552 // Opens the "Content Settings" dialog.
1553 - (IBAction)showContentSettings:(id)sender {
1554 [ContentSettingsDialogController
1555 showContentSettingsForType:CONTENT_SETTINGS_TYPE_DEFAULT
1556 profile:profile_];
1557 }
1558
1559 - (IBAction)privacyLearnMore:(id)sender {
1560 GURL url = google_util::AppendGoogleLocaleParam(
1561 GURL(chrome::kPrivacyLearnMoreURL));
1562 // We open a new browser window so the Options dialog doesn't get lost
1563 // behind other windows.
1564 browser::ShowOptionsURL(profile_, url);
1565 }
1566
1567 - (IBAction)resetAutoOpenFiles:(id)sender {
1568 profile_->GetDownloadManager()->download_prefs()->ResetAutoOpen();
1569 [self recordUserAction:UserMetricsAction("Options_ResetAutoOpenFiles")];
1570 }
1571
1572 - (IBAction)openProxyPreferences:(id)sender {
1573 NSArray* itemsToOpen = [NSArray arrayWithObject:[NSURL fileURLWithPath:
1574 @"/System/Library/PreferencePanes/Network.prefPane"]];
1575
1576 const char* proxyPrefCommand = "Proxies";
1577 base::mac::ScopedAEDesc<> openParams;
1578 OSStatus status = AECreateDesc('ptru',
1579 proxyPrefCommand,
1580 strlen(proxyPrefCommand),
1581 openParams.OutPointer());
1582 LOG_IF(ERROR, status != noErr) << "Failed to create open params: " << status;
1583
1584 LSLaunchURLSpec launchSpec = { 0 };
1585 launchSpec.itemURLs = (CFArrayRef)itemsToOpen;
1586 launchSpec.passThruParams = openParams;
1587 launchSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents;
1588 LSOpenFromURLSpec(&launchSpec, NULL);
1589 }
1590
1591 // Returns whether the alternate error page checkbox should be checked based
1592 // on the preference.
1593 - (BOOL)showAlternateErrorPages {
1594 return alternateErrorPages_.GetValue() ? YES : NO;
1595 }
1596
1597 // Sets the backend pref for whether or not the alternate error page checkbox
1598 // should be displayed based on |value|.
1599 - (void)setShowAlternateErrorPages:(BOOL)value {
1600 if (value)
1601 [self recordUserAction:UserMetricsAction(
1602 "Options_LinkDoctorCheckbox_Enable")];
1603 else
1604 [self recordUserAction:UserMetricsAction(
1605 "Options_LinkDoctorCheckbox_Disable")];
1606 alternateErrorPages_.SetValueIfNotManaged(value ? true : false);
1607 }
1608
1609 // Returns whether the suggest checkbox should be checked based on the
1610 // preference.
1611 - (BOOL)useSuggest {
1612 return useSuggest_.GetValue() ? YES : NO;
1613 }
1614
1615 // Sets the backend pref for whether or not the suggest checkbox should be
1616 // displayed based on |value|.
1617 - (void)setUseSuggest:(BOOL)value {
1618 if (value)
1619 [self recordUserAction:UserMetricsAction(
1620 "Options_UseSuggestCheckbox_Enable")];
1621 else
1622 [self recordUserAction:UserMetricsAction(
1623 "Options_UseSuggestCheckbox_Disable")];
1624 useSuggest_.SetValueIfNotManaged(value ? true : false);
1625 }
1626
1627 // Returns whether the DNS prefetch checkbox should be checked based on the
1628 // preference.
1629 - (BOOL)dnsPrefetch {
1630 return dnsPrefetch_.GetValue() ? YES : NO;
1631 }
1632
1633 // Sets the backend pref for whether or not the DNS prefetch checkbox should be
1634 // displayed based on |value|.
1635 - (void)setDnsPrefetch:(BOOL)value {
1636 if (value)
1637 [self recordUserAction:UserMetricsAction(
1638 "Options_DnsPrefetchCheckbox_Enable")];
1639 else
1640 [self recordUserAction:UserMetricsAction(
1641 "Options_DnsPrefetchCheckbox_Disable")];
1642 dnsPrefetch_.SetValueIfNotManaged(value ? true : false);
1643 }
1644
1645 // Returns whether the safe browsing checkbox should be checked based on the
1646 // preference.
1647 - (BOOL)safeBrowsing {
1648 return safeBrowsing_.GetValue() ? YES : NO;
1649 }
1650
1651 // Sets the backend pref for whether or not the safe browsing checkbox should be
1652 // displayed based on |value|.
1653 - (void)setSafeBrowsing:(BOOL)value {
1654 if (value)
1655 [self recordUserAction:UserMetricsAction(
1656 "Options_SafeBrowsingCheckbox_Enable")];
1657 else
1658 [self recordUserAction:UserMetricsAction(
1659 "Options_SafeBrowsingCheckbox_Disable")];
1660 safeBrowsing_.SetValueIfNotManaged(value ? true : false);
1661 SafeBrowsingService* safeBrowsingService =
1662 g_browser_process->resource_dispatcher_host()->safe_browsing_service();
1663 MessageLoop::current()->PostTask(
1664 FROM_HERE,
1665 NewRunnableMethod(safeBrowsingService,
1666 &SafeBrowsingService::OnEnable,
1667 safeBrowsing_.GetValue()));
1668 }
1669
1670 // Returns whether the metrics reporting checkbox should be checked based on the
1671 // preference.
1672 - (BOOL)metricsReporting {
1673 return metricsReporting_.GetValue() ? YES : NO;
1674 }
1675
1676 // Sets the backend pref for whether or not the metrics reporting checkbox
1677 // should be displayed based on |value|.
1678 - (void)setMetricsReporting:(BOOL)value {
1679 if (value)
1680 [self recordUserAction:UserMetricsAction(
1681 "Options_MetricsReportingCheckbox_Enable")];
1682 else
1683 [self recordUserAction:UserMetricsAction(
1684 "Options_MetricsReportingCheckbox_Disable")];
1685
1686 // TODO(pinkerton): windows shows a dialog here telling the user they need to
1687 // restart for this to take effect. http://crbug.com/34653
1688 metricsReporting_.SetValueIfNotManaged(value ? true : false);
1689
1690 bool enabled = metricsReporting_.GetValue();
1691 GoogleUpdateSettings::SetCollectStatsConsent(enabled);
1692 bool update_pref = GoogleUpdateSettings::GetCollectStatsConsent();
1693 if (enabled != update_pref) {
1694 DVLOG(1) << "GENERAL SECTION: Unable to set crash report status to "
1695 << enabled;
1696 }
1697 // Only change the pref if GoogleUpdateSettings::GetCollectStatsConsent
1698 // succeeds.
1699 enabled = update_pref;
1700
1701 MetricsService* metrics = g_browser_process->metrics_service();
1702 DCHECK(metrics);
1703 if (metrics) {
1704 metrics->SetUserPermitsUpload(enabled);
1705 if (enabled)
1706 metrics->Start();
1707 else
1708 metrics->Stop();
1709 }
1710 }
1711
1712 - (NSURL*)defaultDownloadLocation {
1713 NSString* pathString =
1714 base::SysUTF8ToNSString(defaultDownloadLocation_.GetValue());
1715 return [NSURL fileURLWithPath:pathString];
1716 }
1717
1718 - (BOOL)askForSaveLocation {
1719 return askForSaveLocation_.GetValue();
1720 }
1721
1722 - (void)setAskForSaveLocation:(BOOL)value {
1723 if (value) {
1724 [self recordUserAction:UserMetricsAction(
1725 "Options_AskForSaveLocation_Enable")];
1726 } else {
1727 [self recordUserAction:UserMetricsAction(
1728 "Options_AskForSaveLocation_Disable")];
1729 }
1730 askForSaveLocation_.SetValue(value);
1731 }
1732
1733 - (BOOL)fileHandlerUIEnabled {
1734 if (!profile_->GetDownloadManager()) // Not set in unit tests.
1735 return NO;
1736 return profile_->GetDownloadManager()->download_prefs()->IsAutoOpenUsed();
1737 }
1738
1739 - (void)setFileHandlerUIEnabled:(BOOL)value {
1740 [resetFileHandlersButton_ setEnabled:value];
1741 }
1742
1743 - (BOOL)translateEnabled {
1744 return translateEnabled_.GetValue();
1745 }
1746
1747 - (void)setTranslateEnabled:(BOOL)value {
1748 if (value) {
1749 [self recordUserAction:UserMetricsAction("Options_Translate_Enable")];
1750 } else {
1751 [self recordUserAction:UserMetricsAction("Options_Translate_Disable")];
1752 }
1753 translateEnabled_.SetValue(value);
1754 }
1755
1756 - (BOOL)tabsToLinks {
1757 return tabsToLinks_.GetValue();
1758 }
1759
1760 - (void)setTabsToLinks:(BOOL)value {
1761 if (value) {
1762 [self recordUserAction:UserMetricsAction("Options_TabsToLinks_Enable")];
1763 } else {
1764 [self recordUserAction:UserMetricsAction("Options_TabsToLinks_Disable")];
1765 }
1766 tabsToLinks_.SetValue(value);
1767 }
1768
1769 - (void)fontAndLanguageEndSheet:(NSWindow*)sheet
1770 returnCode:(NSInteger)returnCode
1771 contextInfo:(void*)context {
1772 [sheet close];
1773 [sheet orderOut:self];
1774 fontLanguageSettings_ = nil;
1775 }
1776
1777 - (IBAction)changeFontAndLanguageSettings:(id)sender {
1778 // Intentionally leak the controller as it will clean itself up when the
1779 // sheet closes.
1780 fontLanguageSettings_ =
1781 [[FontLanguageSettingsController alloc] initWithProfile:profile_];
1782 [NSApp beginSheet:[fontLanguageSettings_ window]
1783 modalForWindow:[self window]
1784 modalDelegate:self
1785 didEndSelector:@selector(fontAndLanguageEndSheet:returnCode:contextInfo:)
1786 contextInfo:nil];
1787 }
1788
1789 // Called to launch the Keychain Access app to show the user's stored
1790 // certificates. Note there's no way to script the app to auto-select the
1791 // certificates.
1792 - (IBAction)showCertificates:(id)sender {
1793 [self recordUserAction:UserMetricsAction("Options_ManagerCerts")];
1794 [self launchKeychainAccess];
1795 }
1796
1797 - (IBAction)resetToDefaults:(id)sender {
1798 // The alert will clean itself up in the did-end selector.
1799 NSAlert* alert = [[NSAlert alloc] init];
1800 [alert setMessageText:l10n_util::GetNSString(IDS_OPTIONS_RESET_MESSAGE)];
1801 NSButton* resetButton = [alert addButtonWithTitle:
1802 l10n_util::GetNSString(IDS_OPTIONS_RESET_OKLABEL)];
1803 [resetButton setKeyEquivalent:@""];
1804 NSButton* cancelButton = [alert addButtonWithTitle:
1805 l10n_util::GetNSString(IDS_OPTIONS_RESET_CANCELLABEL)];
1806 [cancelButton setKeyEquivalent:@"\r"];
1807
1808 [alert beginSheetModalForWindow:[self window]
1809 modalDelegate:self
1810 didEndSelector:@selector(resetToDefaults:returned:context:)
1811 contextInfo:nil];
1812 }
1813
1814 - (void)resetToDefaults:(NSAlert*)alert
1815 returned:(NSInteger)code
1816 context:(void*)context {
1817 if (code == NSAlertFirstButtonReturn) {
1818 OptionsUtil::ResetToDefaults(profile_);
1819 }
1820 [alert autorelease];
1821 }
1822
1823 //-------------------------------------------------------------------------
1824
1825 // Callback when preferences are changed. |prefName| is the name of the
1826 // pref that has changed and should not be NULL.
1827 - (void)prefChanged:(std::string*)prefName {
1828 DCHECK(prefName);
1829 if (!prefName) return;
1830 [self basicsPrefChanged:prefName];
1831 [self userDataPrefChanged:prefName];
1832 [self underHoodPrefChanged:prefName];
1833 }
1834
1835 // Callback when sync service state has changed.
1836 //
1837 // TODO(akalin): Decomp this out since a lot of it is copied from the
1838 // Windows version.
1839 // TODO(akalin): Change the background of the status label/link on error.
1840 - (void)syncStateChanged {
1841 DCHECK(syncService_);
1842
1843 string16 statusLabel, linkLabel;
1844 sync_ui_util::MessageType status =
1845 sync_ui_util::GetStatusLabels(syncService_, &statusLabel, &linkLabel);
1846 bool managed = syncService_->IsManaged();
1847
1848 [syncButton_ setEnabled:!syncService_->WizardIsVisible()];
1849 NSString* buttonLabel;
1850 if (syncService_->HasSyncSetupCompleted()) {
1851 buttonLabel = l10n_util::GetNSStringWithFixup(
1852 IDS_SYNC_STOP_SYNCING_BUTTON_LABEL);
1853 [syncCustomizeButton_ setHidden:false];
1854 } else if (syncService_->SetupInProgress()) {
1855 buttonLabel = l10n_util::GetNSStringWithFixup(
1856 IDS_SYNC_NTP_SETUP_IN_PROGRESS);
1857 [syncCustomizeButton_ setHidden:true];
1858 } else {
1859 buttonLabel = l10n_util::GetNSStringWithFixup(
1860 IDS_SYNC_START_SYNC_BUTTON_LABEL);
1861 [syncCustomizeButton_ setHidden:true];
1862 }
1863 [syncCustomizeButton_ setEnabled:!managed];
1864 [syncButton_ setTitle:buttonLabel];
1865 [syncButton_ setEnabled:!managed];
1866
1867 [syncStatus_ setStringValue:base::SysUTF16ToNSString(statusLabel)];
1868 [syncLink_ setHidden:linkLabel.empty()];
1869 [syncLink_ setTitle:base::SysUTF16ToNSString(linkLabel)];
1870 [syncLink_ setEnabled:!managed];
1871
1872 NSButtonCell* syncLinkCell = static_cast<NSButtonCell*>([syncLink_ cell]);
1873 if (!syncStatusNoErrorBackgroundColor_) {
1874 DCHECK(!syncLinkNoErrorBackgroundColor_);
1875 // We assume that the sync controls start off in a non-error
1876 // state.
1877 syncStatusNoErrorBackgroundColor_.reset(
1878 [[syncStatus_ backgroundColor] retain]);
1879 syncLinkNoErrorBackgroundColor_.reset(
1880 [[syncLinkCell backgroundColor] retain]);
1881 }
1882 if (status == sync_ui_util::SYNC_ERROR) {
1883 [syncStatus_ setBackgroundColor:syncErrorBackgroundColor_];
1884 [syncLinkCell setBackgroundColor:syncErrorBackgroundColor_];
1885 } else {
1886 [syncStatus_ setBackgroundColor:syncStatusNoErrorBackgroundColor_];
1887 [syncLinkCell setBackgroundColor:syncLinkNoErrorBackgroundColor_];
1888 }
1889 }
1890
1891 // Show the preferences window.
1892 - (IBAction)showPreferences:(id)sender {
1893 [self showWindow:sender];
1894 }
1895
1896 - (IBAction)toolbarButtonSelected:(id)sender {
1897 DCHECK([sender isKindOfClass:[NSToolbarItem class]]);
1898 OptionsPage page = [self getPageForToolbarItem:sender];
1899 [self displayPreferenceViewForPage:page animate:YES];
1900 }
1901
1902 // Helper to update the window to display a preferences view for a page.
1903 - (void)displayPreferenceViewForPage:(OptionsPage)page
1904 animate:(BOOL)animate {
1905 NSWindow* prefsWindow = [self window];
1906
1907 // Needs to go *after* the call to [self window], which triggers
1908 // awakeFromNib if necessary.
1909 NSView* prefsView = [self getPrefsViewForPage:page];
1910 NSView* contentView = [prefsWindow contentView];
1911
1912 // Make sure we aren't being told to display the same thing again.
1913 if (currentPrefsView_ == prefsView &&
1914 managedPrefsBannerVisible_ == bannerState_->IsVisible()) {
1915 return;
1916 }
1917
1918 // Remember new options page as current page.
1919 if (page != OPTIONS_PAGE_DEFAULT)
1920 lastSelectedPage_.SetValue(page);
1921
1922 // Stop any running animation, and reset the subviews to the new state. We
1923 // re-add any views we need for animation later.
1924 [animation_ stopAnimation];
1925 NSView* oldPrefsView = currentPrefsView_;
1926 currentPrefsView_ = prefsView;
1927 [self resetSubViews];
1928
1929 // Update the banner state.
1930 [self initBannerStateForPage:page];
1931 BOOL showBanner = bannerState_->IsVisible();
1932
1933 // Update the window title.
1934 NSToolbarItem* toolbarItem = [self getToolbarItemForPage:page];
1935 [prefsWindow setTitle:[toolbarItem label]];
1936
1937 // Calculate new frames for the subviews.
1938 NSRect prefsViewFrame = [prefsView frame];
1939 NSRect contentViewFrame = [contentView frame];
1940 NSRect bannerViewFrame = [managedPrefsBannerView_ frame];
1941
1942 // Determine what height the managed prefs banner will use.
1943 CGFloat bannerViewHeight = showBanner ? NSHeight(bannerViewFrame) : 0.0;
1944
1945 if (animate) {
1946 // NSViewAnimation doesn't seem to honor subview resizing as it animates the
1947 // Window's frame. So instead of trying to get the top in the right place,
1948 // just set the origin where it should be at the end, and let the fade/size
1949 // slide things into the right spot.
1950 prefsViewFrame.origin.y = 0.0;
1951 } else {
1952 // The prefView is anchored to the top of its parent, so set its origin so
1953 // that the top is where it should be. When the window's frame is set, the
1954 // origin will be adjusted to keep it in the right spot.
1955 prefsViewFrame.origin.y = NSHeight(contentViewFrame) -
1956 NSHeight(prefsViewFrame) - bannerViewHeight;
1957 }
1958 bannerViewFrame.origin.y = NSHeight(prefsViewFrame);
1959 bannerViewFrame.size.width = NSWidth(contentViewFrame);
1960 [prefsView setFrame:prefsViewFrame];
1961
1962 // Figure out the size of the window.
1963 NSRect windowFrame = [contentView convertRect:[prefsWindow frame]
1964 fromView:nil];
1965 CGFloat titleToolbarHeight =
1966 NSHeight(windowFrame) - NSHeight(contentViewFrame);
1967 windowFrame.size.height =
1968 NSHeight(prefsViewFrame) + titleToolbarHeight + bannerViewHeight;
1969 DCHECK_GE(NSWidth(windowFrame), NSWidth(prefsViewFrame))
1970 << "Initial width set wasn't wide enough.";
1971 windowFrame = [contentView convertRect:windowFrame toView:nil];
1972 windowFrame.origin.y = NSMaxY([prefsWindow frame]) - NSHeight(windowFrame);
1973
1974 // Now change the size.
1975 if (animate) {
1976 NSMutableArray* animations = [NSMutableArray arrayWithCapacity:4];
1977 if (oldPrefsView != prefsView) {
1978 // Fade between prefs views if they change.
1979 [contentView addSubview:oldPrefsView
1980 positioned:NSWindowBelow
1981 relativeTo:nil];
1982 [animations addObject:
1983 [NSDictionary dictionaryWithObjectsAndKeys:
1984 oldPrefsView, NSViewAnimationTargetKey,
1985 NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey,
1986 nil]];
1987 [animations addObject:
1988 [NSDictionary dictionaryWithObjectsAndKeys:
1989 prefsView, NSViewAnimationTargetKey,
1990 NSViewAnimationFadeInEffect, NSViewAnimationEffectKey,
1991 nil]];
1992 } else {
1993 // Make sure the prefs pane ends up in the right position in case we
1994 // manipulate the banner.
1995 [animations addObject:
1996 [NSDictionary dictionaryWithObjectsAndKeys:
1997 prefsView, NSViewAnimationTargetKey,
1998 [NSValue valueWithRect:prefsViewFrame],
1999 NSViewAnimationEndFrameKey,
2000 nil]];
2001 }
2002 if (showBanner != managedPrefsBannerVisible_) {
2003 // Slide the warning banner in or out of view.
2004 [animations addObject:
2005 [NSDictionary dictionaryWithObjectsAndKeys:
2006 managedPrefsBannerView_, NSViewAnimationTargetKey,
2007 [NSValue valueWithRect:bannerViewFrame],
2008 NSViewAnimationEndFrameKey,
2009 nil]];
2010 }
2011 // Window resize animation.
2012 [animations addObject:
2013 [NSDictionary dictionaryWithObjectsAndKeys:
2014 prefsWindow, NSViewAnimationTargetKey,
2015 [NSValue valueWithRect:windowFrame], NSViewAnimationEndFrameKey,
2016 nil]];
2017 [animation_ setViewAnimations:animations];
2018 // The default duration is 0.5s, which actually feels slow in here, so speed
2019 // it up a bit.
2020 [animation_ gtm_setDuration:0.2
2021 eventMask:NSLeftMouseUpMask];
2022 [animation_ startAnimation];
2023 } else {
2024 // If not animating, odds are we don't want to display either (because it
2025 // is initial window setup).
2026 [prefsWindow setFrame:windowFrame display:NO];
2027 [managedPrefsBannerView_ setFrame:bannerViewFrame];
2028 }
2029
2030 managedPrefsBannerVisible_ = showBanner;
2031 }
2032
2033 - (void)resetSubViews {
2034 // Reset subviews to current prefs view and banner, remove any views that
2035 // might have been left over from previous state or animation.
2036 NSArray* subviews = [NSArray arrayWithObjects:
2037 currentPrefsView_, managedPrefsBannerView_, nil];
2038 [[[self window] contentView] setSubviews:subviews];
2039 [[self window] setInitialFirstResponder:currentPrefsView_];
2040 }
2041
2042 - (void)animationDidEnd:(NSAnimation*)animation {
2043 DCHECK_EQ(animation_.get(), animation);
2044 // Animation finished, reset subviews to current prefs view and the banner.
2045 [self resetSubViews];
2046 }
2047
2048 // Reinitializes the banner state tracker object to watch for managed bits of
2049 // preferences relevant to the given options |page|.
2050 - (void)initBannerStateForPage:(OptionsPage)page {
2051 page = [self normalizePage:page];
2052
2053 // During unit tests, there is no local state object, so we fall back to
2054 // the prefs object (where we've explicitly registered this pref so we
2055 // know it's there).
2056 PrefService* local = g_browser_process->local_state();
2057 if (!local)
2058 local = prefs_;
2059 bannerState_.reset(
2060 new PreferencesWindowControllerInternal::ManagedPrefsBannerState(
2061 self, page, local, prefs_));
2062 }
2063
2064 - (void)switchToPage:(OptionsPage)page animate:(BOOL)animate {
2065 [self displayPreferenceViewForPage:page animate:animate];
2066 NSToolbarItem* toolbarItem = [self getToolbarItemForPage:page];
2067 [toolbar_ setSelectedItemIdentifier:[toolbarItem itemIdentifier]];
2068 }
2069
2070 // Called when the window is being closed. Send out a notification that the user
2071 // is done editing preferences. Make sure there are no pending field editors
2072 // by clearing the first responder.
2073 - (void)windowWillClose:(NSNotification*)notification {
2074 // Setting the first responder to the window ends any in-progress field
2075 // editor. This will update the model appropriately so there's nothing left
2076 // to do.
2077 if (![[self window] makeFirstResponder:[self window]]) {
2078 // We've hit a recalcitrant field editor, force it to go away.
2079 [[self window] endEditingFor:nil];
2080 }
2081 [self autorelease];
2082 }
2083
2084 - (void)controlTextDidEndEditing:(NSNotification*)notification {
2085 [customPagesSource_ validateURLs];
2086 }
2087
2088 @end
2089
2090 @implementation PreferencesWindowController(Testing)
2091
2092 - (IntegerPrefMember*)lastSelectedPage {
2093 return &lastSelectedPage_;
2094 }
2095
2096 - (NSToolbar*)toolbar {
2097 return toolbar_;
2098 }
2099
2100 - (NSView*)basicsView {
2101 return basicsView_;
2102 }
2103
2104 - (NSView*)personalStuffView {
2105 return personalStuffView_;
2106 }
2107
2108 - (NSView*)underTheHoodView {
2109 return underTheHoodView_;
2110 }
2111
2112 - (OptionsPage)normalizePage:(OptionsPage)page {
2113 if (page == OPTIONS_PAGE_DEFAULT) {
2114 // Get the last visited page from local state.
2115 page = static_cast<OptionsPage>(lastSelectedPage_.GetValue());
2116 if (page == OPTIONS_PAGE_DEFAULT) {
2117 page = OPTIONS_PAGE_GENERAL;
2118 }
2119 }
2120 return page;
2121 }
2122
2123 - (NSToolbarItem*)getToolbarItemForPage:(OptionsPage)page {
2124 NSUInteger pageIndex = (NSUInteger)[self normalizePage:page];
2125 NSArray* items = [toolbar_ items];
2126 NSUInteger itemCount = [items count];
2127 DCHECK_GE(pageIndex, 0U);
2128 if (pageIndex >= itemCount) {
2129 NOTIMPLEMENTED();
2130 pageIndex = 0;
2131 }
2132 DCHECK_GT(itemCount, 0U);
2133 return [items objectAtIndex:pageIndex];
2134 }
2135
2136 - (OptionsPage)getPageForToolbarItem:(NSToolbarItem*)toolbarItem {
2137 // Tags are set in the nib file.
2138 switch ([toolbarItem tag]) {
2139 case 0: // Basics
2140 return OPTIONS_PAGE_GENERAL;
2141 case 1: // Personal Stuff
2142 return OPTIONS_PAGE_CONTENT;
2143 case 2: // Under the Hood
2144 return OPTIONS_PAGE_ADVANCED;
2145 default:
2146 NOTIMPLEMENTED();
2147 return OPTIONS_PAGE_GENERAL;
2148 }
2149 }
2150
2151 - (NSView*)getPrefsViewForPage:(OptionsPage)page {
2152 // The views will be NULL if this is mistakenly called before awakeFromNib.
2153 DCHECK(basicsView_);
2154 DCHECK(personalStuffView_);
2155 DCHECK(underTheHoodView_);
2156 page = [self normalizePage:page];
2157 switch (page) {
2158 case OPTIONS_PAGE_GENERAL:
2159 return basicsView_;
2160 case OPTIONS_PAGE_CONTENT:
2161 return personalStuffView_;
2162 case OPTIONS_PAGE_ADVANCED:
2163 return underTheHoodView_;
2164 case OPTIONS_PAGE_DEFAULT:
2165 case OPTIONS_PAGE_COUNT:
2166 LOG(DFATAL) << "Invalid page value " << page;
2167 }
2168 return basicsView_;
2169 }
2170
2171 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698