summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa
diff options
context:
space:
mode:
authoravi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-27 20:25:04 +0000
committeravi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-27 20:25:04 +0000
commit3ef4755e9b102647240a5c281690332fe44be34b (patch)
tree9b26ce5b65f9b24efd356dd5a7f8eec3b114e3cc /chrome/browser/cocoa
parent011b93dc3cc8eb6ef80626a2de340ee66f4ea663 (diff)
downloadchromium_src-3ef4755e9b102647240a5c281690332fe44be34b.zip
chromium_src-3ef4755e9b102647240a5c281690332fe44be34b.tar.gz
chromium_src-3ef4755e9b102647240a5c281690332fe44be34b.tar.bz2
Add search engine selection dialog for Mac, do new-style first-run.
SearchEngineDialog.xib: creation FirstRunDialog.xib: removal of import and cancel UI elements BUG=47651 TEST=reset profile, run. Make sure goto/chromefirstrun is satisfied. Review URL: http://codereview.chromium.org/3223010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60699 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r--chrome/browser/cocoa/first_run_dialog.h15
-rw-r--r--chrome/browser/cocoa/first_run_dialog.mm108
-rw-r--r--chrome/browser/cocoa/search_engine_dialog_controller.h45
-rw-r--r--chrome/browser/cocoa/search_engine_dialog_controller.mm279
4 files changed, 369 insertions, 78 deletions
diff --git a/chrome/browser/cocoa/first_run_dialog.h b/chrome/browser/cocoa/first_run_dialog.h
index a96ed17..3e575a3 100644
--- a/chrome/browser/cocoa/first_run_dialog.h
+++ b/chrome/browser/cocoa/first_run_dialog.h
@@ -13,14 +13,8 @@
// us improve Chromium.
@interface FirstRunDialogController : NSWindowController {
@private
- BOOL userDidCancel_;
BOOL statsEnabled_;
- BOOL statsCheckboxHidden_;
BOOL makeDefaultBrowser_;
- BOOL importBookmarks_;
- int browserImportSelectedIndex_;
- NSArray* browserImportList_;
- BOOL browserImportListHidden_;
IBOutlet NSArray* objectsToSize_;
IBOutlet NSButton* statsCheckbox_;
@@ -30,21 +24,12 @@
// Called when the "Start Google Chrome" button is pressed.
- (IBAction)ok:(id)sender;
-// Cancel button calls this.
-- (IBAction)cancel:(id)sender;
-
// Called when the "Learn More" button is pressed.
- (IBAction)learnMore:(id)sender;
// Properties for bindings.
-@property(assign, nonatomic) BOOL userDidCancel;
@property(assign, nonatomic) BOOL statsEnabled;
-@property(assign, nonatomic) BOOL statsCheckboxHidden;
@property(assign, nonatomic) BOOL makeDefaultBrowser;
-@property(assign, nonatomic) BOOL importBookmarks;
-@property(assign, nonatomic) int browserImportSelectedIndex;
-@property(retain, nonatomic) NSArray* browserImportList;
-@property(assign, nonatomic) BOOL browserImportListHidden;
@end
diff --git a/chrome/browser/cocoa/first_run_dialog.mm b/chrome/browser/cocoa/first_run_dialog.mm
index c71750e..123b80b 100644
--- a/chrome/browser/cocoa/first_run_dialog.mm
+++ b/chrome/browser/cocoa/first_run_dialog.mm
@@ -1,19 +1,21 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/cocoa/first_run_dialog.h"
#include "app/l10n_util_mac.h"
-#include "base/logging.h"
#include "base/mac_util.h"
-#import "base/scoped_nsobject.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/prefs/pref_service.h"
-#include "chrome/common/pref_names.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
#include "grit/locale_settings.h"
#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
+@interface FirstRunDialogController (PrivateMethods)
+// Show the dialog.
+- (void)show;
+@end
+
namespace {
// Compare function for -[NSArray sortedArrayUsingFunction:context:] that
@@ -29,18 +31,30 @@ NSInteger CompareFrameY(id view1, id view2, void* context) {
return NSOrderedSame;
}
+class FirstRunShowBridge : public base::RefCounted<FirstRunShowBridge> {
+ public:
+ FirstRunShowBridge(FirstRunDialogController* controller);
+
+ void ShowDialog();
+ private:
+ FirstRunDialogController* controller_;
+};
+
+FirstRunShowBridge::FirstRunShowBridge(
+ FirstRunDialogController* controller) : controller_(controller) {
+}
+
+void FirstRunShowBridge::ShowDialog() {
+ [controller_ show];
+ MessageLoop::current()->QuitNow();
+}
+
};
@implementation FirstRunDialogController
-@synthesize userDidCancel = userDidCancel_;
@synthesize statsEnabled = statsEnabled_;
-@synthesize statsCheckboxHidden = statsCheckboxHidden_;
@synthesize makeDefaultBrowser = makeDefaultBrowser_;
-@synthesize importBookmarks = importBookmarks_;
-@synthesize browserImportSelectedIndex = browserImportSelectedIndex_;
-@synthesize browserImportList = browserImportList_;
-@synthesize browserImportListHidden = browserImportListHidden_;
- (id)init {
NSString* nibpath =
@@ -50,31 +64,28 @@ NSInteger CompareFrameY(id view1, id view2, void* context) {
if (self != nil) {
// Bound to the dialog checkbox, default to true.
statsEnabled_ = YES;
- importBookmarks_ = YES;
-
-#if defined(GOOGLE_CHROME_BUILD)
- // If the send stats option is controlled by enterprise configuration
- // management, hide the checkbox.
- const PrefService::Preference* metrics_reporting_pref =
- g_browser_process->local_state()->FindPreference(
- prefs::kMetricsReportingEnabled);
- if (metrics_reporting_pref && metrics_reporting_pref->IsManaged())
- statsCheckboxHidden_ = YES;
-#else
- // In Chromium builds all stats reporting is disabled so there's no reason
- // to display the checkbox - the setting is always OFF.
- statsCheckboxHidden_ = YES;
-#endif // !GOOGLE_CHROME_BUILD
}
return self;
}
- (void)dealloc {
- [browserImportList_ release];
[super dealloc];
}
- (IBAction)showWindow:(id)sender {
+ // The main MessageLoop has not yet run, but has been spun. If we call
+ // -[NSApplication runModalForWindow:] we will hang <http://crbug.com/54248>.
+ // Therefore the main MessageLoop is run so things work.
+
+ scoped_refptr<FirstRunShowBridge> bridge = new FirstRunShowBridge(self);
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(bridge.get(),
+ &FirstRunShowBridge::ShowDialog));
+ MessageLoop::current()->Run();
+}
+
+- (void)show {
NSWindow* win = [self window];
// Only support the sizing the window once.
@@ -106,15 +117,13 @@ NSInteger CompareFrameY(id view1, id view2, void* context) {
[win setFrame:windowFrame display:NO];
}
- // The stats checkbox (if visible) gets some really long text, so it gets
- // word wrapped and then sized.
+ // The stats checkbox gets some really long text, so it gets word wrapped
+ // and then sized.
DCHECK(statsCheckbox_);
CGFloat statsCheckboxHeightChange = 0.0;
- if (![self statsCheckboxHidden]) {
- [GTMUILocalizerAndLayoutTweaker wrapButtonTitleForWidth:statsCheckbox_];
- statsCheckboxHeightChange =
- [GTMUILocalizerAndLayoutTweaker sizeToFitView:statsCheckbox_].height;
- }
+ [GTMUILocalizerAndLayoutTweaker wrapButtonTitleForWidth:statsCheckbox_];
+ statsCheckboxHeightChange =
+ [GTMUILocalizerAndLayoutTweaker sizeToFitView:statsCheckbox_].height;
// Walk bottom up shuffling for all the hidden views.
NSArray* subViews =
@@ -172,42 +181,15 @@ NSInteger CompareFrameY(id view1, id view2, void* context) {
[NSApp runModalForWindow:win];
}
-- (void)closeDialog {
+- (IBAction)ok:(id)sender {
[[self window] close];
[NSApp stopModal];
}
-- (IBAction)ok:(id)sender {
- [self closeDialog];
-}
-
-- (IBAction)cancel:(id)sender {
- [self closeDialog];
- [self setUserDidCancel:YES];
-}
-
- (IBAction)learnMore:(id)sender {
NSString* urlStr = l10n_util::GetNSString(IDS_LEARN_MORE_REPORTING_URL);
NSURL* learnMoreUrl = [NSURL URLWithString:urlStr];
[[NSWorkspace sharedWorkspace] openURL:learnMoreUrl];
}
-// Custom property getters
-
-- (BOOL)importBookmarks {
- // If the UI for browser import is hidden, report the choice as off.
- if ([self browserImportListHidden]) {
- return NO;
- }
- return importBookmarks_;
-}
-
-- (BOOL)statsEnabled {
- // If the UI for stats is hidden, report the choice as off.
- if ([self statsCheckboxHidden]) {
- return NO;
- }
- return statsEnabled_;
-}
-
@end
diff --git a/chrome/browser/cocoa/search_engine_dialog_controller.h b/chrome/browser/cocoa/search_engine_dialog_controller.h
new file mode 100644
index 0000000..a410614
--- /dev/null
+++ b/chrome/browser/cocoa/search_engine_dialog_controller.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include <vector>
+
+#import "base/scoped_nsobject.h"
+#include "base/scoped_ptr.h"
+
+class Profile;
+class SearchEngineDialogControllerBridge;
+class TemplateURL;
+class TemplateURLModel;
+
+// Class that acts as a controller for the search engine choice dialog.
+@interface SearchEngineDialogController : NSWindowController {
+ @private
+ // Our current profile.
+ Profile* profile_;
+
+ // If logos are to be displayed in random order. Used for UX testing.
+ bool randomize_;
+
+ // Owned by the profile_.
+ TemplateURLModel* searchEnginesModel_;
+
+ // Bridge to the C++ world.
+ scoped_ptr<SearchEngineDialogControllerBridge> bridge_;
+
+ // Offered search engine choices.
+ std::vector<const TemplateURL*> choices_;
+
+ IBOutlet NSImageView* headerImageView_;
+ IBOutlet NSView* searchEngineView_;
+}
+
+@property(assign, nonatomic) Profile* profile;
+@property(assign, nonatomic) bool randomize;
+
+// Properties for bindings.
+@property(readonly) NSFont* mainLabelFont;
+
+@end
diff --git a/chrome/browser/cocoa/search_engine_dialog_controller.mm b/chrome/browser/cocoa/search_engine_dialog_controller.mm
new file mode 100644
index 0000000..4863c07
--- /dev/null
+++ b/chrome/browser/cocoa/search_engine_dialog_controller.mm
@@ -0,0 +1,279 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "chrome/browser/cocoa/search_engine_dialog_controller.h"
+
+#include <algorithm>
+
+#include "app/l10n_util_mac.h"
+#include "app/resource_bundle.h"
+#include "base/mac_util.h"
+#include "base/nsimage_cache_mac.h"
+#include "base/sys_string_conversions.h"
+#include "base/time.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/search_engines/template_url_model_observer.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
+
+// Horizontal spacing between search engine choices.
+const int kSearchEngineSpacing = 20;
+
+// Vertical spacing between the search engine logo and the button underneath.
+const int kLogoButtonSpacing = 10;
+
+// Width of a label used in place of a logo.
+const int kLogoLabelWidth = 170;
+
+// Height of a label used in place of a logo.
+const int kLogoLabelHeight = 25;
+
+@interface SearchEngineDialogController (Private)
+- (void)onTemplateURLModelChanged;
+- (void)buildSearchEngineView;
+- (NSView*)viewForSearchEngine:(const TemplateURL*)engine
+ atIndex:(size_t)index;
+- (IBAction)searchEngineSelected:(id)sender;
+@end
+
+class SearchEngineDialogControllerBridge : public TemplateURLModelObserver {
+ public:
+ SearchEngineDialogControllerBridge(SearchEngineDialogController* controller);
+
+ // TemplateURLModelObserver
+ virtual void OnTemplateURLModelChanged();
+
+ private:
+ SearchEngineDialogController* controller_;
+};
+
+SearchEngineDialogControllerBridge::SearchEngineDialogControllerBridge(
+ SearchEngineDialogController* controller) : controller_(controller) {
+}
+
+void SearchEngineDialogControllerBridge::OnTemplateURLModelChanged() {
+ [controller_ onTemplateURLModelChanged];
+ MessageLoop::current()->QuitNow();
+}
+
+@implementation SearchEngineDialogController
+
+@synthesize profile = profile_;
+@synthesize randomize = randomize_;
+
+- (id)init {
+ NSString* nibpath =
+ [mac_util::MainAppBundle() pathForResource:@"SearchEngineDialog"
+ ofType:@"nib"];
+ self = [super initWithWindowNibPath:nibpath owner:self];
+ if (self != nil) {
+ bridge_.reset(new SearchEngineDialogControllerBridge(self));
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [super dealloc];
+}
+
+- (IBAction)showWindow:(id)sender {
+ searchEnginesModel_ = profile_->GetTemplateURLModel();
+ searchEnginesModel_->AddObserver(bridge_.get());
+
+ if (searchEnginesModel_->loaded()) {
+ [self onTemplateURLModelChanged];
+ } else {
+ searchEnginesModel_->Load();
+ MessageLoop::current()->Run();
+ }
+}
+
+- (void)onTemplateURLModelChanged {
+ searchEnginesModel_->RemoveObserver(bridge_.get());
+
+ // Add the search engines in the search_engines_model_ to the buttons list.
+ // The first three will always be from prepopulated data.
+ std::vector<const TemplateURL*> templateUrls =
+ searchEnginesModel_->GetTemplateURLs();
+
+ // If we have fewer than two search engines, end the search engine dialog
+ // immediately, leaving the imported default search engine setting intact.
+ if (templateUrls.size() < 2) {
+ return;
+ }
+
+ NSWindow* win = [self window];
+
+ [win setBackgroundColor:[NSColor whiteColor]];
+
+ NSImage* headerImage = ResourceBundle::GetSharedInstance().
+ GetNSImageNamed(IDR_SEARCH_ENGINE_DIALOG_TOP);
+ [headerImageView_ setImage:headerImage];
+
+ // Is the user's default search engine included in the first three
+ // prepopulated set? If not, we need to expand the dialog to include a fourth
+ // engine.
+ const TemplateURL* defaultSearchEngine =
+ searchEnginesModel_->GetDefaultSearchProvider();
+
+ std::vector<const TemplateURL*>::iterator engineIter =
+ templateUrls.begin();
+ for (int i = 0; engineIter != templateUrls.end(); ++i, ++engineIter) {
+ if (i < 3) {
+ choices_.push_back(*engineIter);
+ } else {
+ if (*engineIter == defaultSearchEngine)
+ choices_.push_back(*engineIter);
+ }
+ }
+
+ // Randomize the order of the logos if the option has been set.
+ if (randomize_) {
+ int seed = static_cast<int>(base::Time::Now().ToInternalValue());
+ srand(seed);
+ std::random_shuffle(choices_.begin(), choices_.end());
+ }
+
+ [self buildSearchEngineView];
+
+ // Display the dialog.
+ NSInteger choice = [NSApp runModalForWindow:win];
+ searchEnginesModel_->SetDefaultSearchProvider(choices_.at(choice));
+}
+
+- (void)buildSearchEngineView {
+ scoped_nsobject<NSMutableArray> searchEngineViews
+ ([[NSMutableArray alloc] init]);
+
+ for (size_t i = 0; i < choices_.size(); ++i)
+ [searchEngineViews addObject:[self viewForSearchEngine:choices_.at(i)
+ atIndex:i]];
+
+ NSSize newOverallSize = NSZeroSize;
+ for (NSView* view in searchEngineViews.get()) {
+ NSRect engineFrame = [view frame];
+ engineFrame.origin = NSMakePoint(newOverallSize.width, 0);
+ [searchEngineView_ addSubview:view];
+ [view setFrame:engineFrame];
+ newOverallSize = NSMakeSize(
+ newOverallSize.width + NSWidth(engineFrame) + kSearchEngineSpacing,
+ std::max(newOverallSize.height, NSHeight(engineFrame)));
+ }
+ newOverallSize.width -= kSearchEngineSpacing;
+
+ // Resize the window to fit (and because it's bound on all sides it will
+ // resize the search engine view).
+ NSSize currentOverallSize = [searchEngineView_ bounds].size;
+ NSSize deltaSize = NSMakeSize(
+ newOverallSize.width - currentOverallSize.width,
+ newOverallSize.height - currentOverallSize.height);
+ NSSize windowDeltaSize = [searchEngineView_ convertSize:deltaSize toView:nil];
+ NSRect windowFrame = [[self window] frame];
+ windowFrame.size.width += windowDeltaSize.width;
+ windowFrame.size.height += windowDeltaSize.height;
+ [[self window] setFrame:windowFrame display:NO];
+}
+
+- (NSView*)viewForSearchEngine:(const TemplateURL*)engine
+ atIndex:(size_t)index {
+ bool useImages = false;
+#if defined(GOOGLE_CHROME_BUILD)
+ useImages = true;
+#endif
+
+ // Make the engine identifier.
+ NSView* engineIdentifier = nil; // either the logo or the text label
+
+ int logoId = engine->logo_id();
+ if (useImages && logoId > 0) {
+ NSImage* logoImage =
+ ResourceBundle::GetSharedInstance().GetNSImageNamed(logoId);
+ NSRect logoBounds = NSZeroRect;
+ logoBounds.size = [logoImage size];
+ NSImageView* logoView =
+ [[[NSImageView alloc] initWithFrame:logoBounds] autorelease];
+ [logoView setImage:logoImage];
+ [logoView setEditable:NO];
+
+ // Tooltip text provides accessibility.
+ [logoView setToolTip:base::SysWideToNSString(engine->short_name())];
+ engineIdentifier = logoView;
+ } else {
+ // No logo -- we must show a text label.
+ NSRect labelBounds = NSMakeRect(0, 0, kLogoLabelWidth, kLogoLabelHeight);
+ NSTextField* labelField =
+ [[[NSTextField alloc] initWithFrame:labelBounds] autorelease];
+ [labelField setBezeled:NO];
+ [labelField setEditable:NO];
+ [labelField setSelectable:NO];
+
+ scoped_nsobject<NSMutableParagraphStyle> paragraphStyle(
+ [[NSMutableParagraphStyle alloc] init]);
+ [paragraphStyle setAlignment:NSCenterTextAlignment];
+ NSDictionary* attrs = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSFont boldSystemFontOfSize:13], NSFontAttributeName,
+ paragraphStyle.get(), NSParagraphStyleAttributeName,
+ nil];
+
+ NSString* value = base::SysWideToNSString(engine->short_name());
+ scoped_nsobject<NSAttributedString> attrValue(
+ [[NSAttributedString alloc] initWithString:value
+ attributes:attrs]);
+
+ [labelField setAttributedStringValue:attrValue.get()];
+
+ engineIdentifier = labelField;
+ }
+
+ // Make the "Choose" button.
+ scoped_nsobject<NSButton> chooseButton(
+ [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 34)]);
+ [chooseButton setBezelStyle:NSRoundedBezelStyle];
+ [[chooseButton cell] setFont:[NSFont systemFontOfSize:
+ [NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
+ [chooseButton setTitle:l10n_util::GetNSStringWithFixup(IDS_FR_SEARCH_CHOOSE)];
+ [GTMUILocalizerAndLayoutTweaker sizeToFitView:chooseButton.get()];
+ [chooseButton setTag:index];
+ [chooseButton setTarget:self];
+ [chooseButton setAction:@selector(searchEngineSelected:)];
+
+ // Put 'em together.
+ NSRect engineIdentifierFrame = [engineIdentifier frame];
+ NSRect chooseButtonFrame = [chooseButton frame];
+
+ NSRect containingViewFrame = NSZeroRect;
+ containingViewFrame.size.width += engineIdentifierFrame.size.width;
+ containingViewFrame.size.height += engineIdentifierFrame.size.height;
+ containingViewFrame.size.height += kLogoButtonSpacing;
+ containingViewFrame.size.height += chooseButtonFrame.size.height;
+
+ NSView* containingView =
+ [[[NSView alloc] initWithFrame:containingViewFrame] autorelease];
+
+ [containingView addSubview:engineIdentifier];
+ engineIdentifierFrame.origin.y =
+ chooseButtonFrame.size.height + kLogoButtonSpacing;
+ [engineIdentifier setFrame:engineIdentifierFrame];
+
+ [containingView addSubview:chooseButton];
+ chooseButtonFrame.origin.x =
+ int((containingViewFrame.size.width - chooseButtonFrame.size.width) / 2);
+ [chooseButton setFrame:chooseButtonFrame];
+
+ return containingView;
+}
+
+- (NSFont*)mainLabelFont {
+ return [NSFont boldSystemFontOfSize:13];
+}
+
+- (IBAction)searchEngineSelected:(id)sender {
+ [[self window] close];
+ [NSApp stopModalWithCode:[sender tag]];
+}
+
+@end