diff options
author | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-09 16:22:48 +0000 |
---|---|---|
committer | pinkerton@chromium.org <pinkerton@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-09 16:22:48 +0000 |
commit | acd70444b3eba65ce7d867aa61bcdb3b63dd9369 (patch) | |
tree | 9e23bb455c45c0b8bb79cc08b01c046a99dc7c94 /chrome/browser | |
parent | 2af577d4f70b5392f63c4bed35f890ace92a4e0a (diff) | |
download | chromium_src-acd70444b3eba65ce7d867aa61bcdb3b63dd9369.zip chromium_src-acd70444b3eba65ce7d867aa61bcdb3b63dd9369.tar.gz chromium_src-acd70444b3eba65ce7d867aa61bcdb3b63dd9369.tar.bz2 |
Implement default search popup.
BUG=13151
TEST=default search popup persistance.
Review URL: http://codereview.chromium.org/119310
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17947 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
5 files changed, 289 insertions, 0 deletions
diff --git a/chrome/browser/cocoa/preferences_window_controller.h b/chrome/browser/cocoa/preferences_window_controller.h index e7f7734..a39531c 100644 --- a/chrome/browser/cocoa/preferences_window_controller.h +++ b/chrome/browser/cocoa/preferences_window_controller.h @@ -12,6 +12,7 @@ class PrefObserverBridge; class PrefService; class Profile; +@class SearchEngineListModel; // A window controller that handles the preferences window. The bulk of the // work is handled via Cocoa Bindings and getter/setter methods that wrap @@ -40,6 +41,7 @@ class Profile; StringPrefMember homepage_; BooleanPrefMember showHomeButton_; BooleanPrefMember showPageOptionButtons_; + scoped_nsobject<SearchEngineListModel> searchEngineModel_; // Used when creating a new home page url to make the new cell editable. BOOL pendingSelectForEdit_; diff --git a/chrome/browser/cocoa/preferences_window_controller.mm b/chrome/browser/cocoa/preferences_window_controller.mm index cd800cb..5df094c 100644 --- a/chrome/browser/cocoa/preferences_window_controller.mm +++ b/chrome/browser/cocoa/preferences_window_controller.mm @@ -12,6 +12,7 @@ #include "chrome/browser/browser_list.h" #import "chrome/browser/cocoa/clear_browsing_data_controller.h" #import "chrome/browser/cocoa/custom_home_pages_model.h" +#import "chrome/browser/cocoa/search_engine_list_model.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/profile.h" @@ -110,6 +111,17 @@ class PrefObserverBridge : public NotificationObserver { name:kHomepageEntryChangedNotification object:nil]; + // Set up the model for the default search popup. Register for notifications + // about when the model changes so we can update the selection in the view. + searchEngineModel_.reset( + [[SearchEngineListModel alloc] + initWithModel:profile->GetTemplateURLModel()]); + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(searchEngineModelChanged:) + name:kSearchEngineListModelChangedNotification + object:searchEngineModel_.get()]; + // This needs to be done before awakeFromNib: because the bindings set up // in the nib rely on it. [self registerPrefObservers]; @@ -471,6 +483,29 @@ enum { kHomepageNewTabPage, kHomepageURL }; showPageOptionButtons_.SetValue(value ? true : false); } +// Getter for the |searchEngineModel| property for bindings. +- (id)searchEngineModel { + return searchEngineModel_.get(); +} + +// Bindings for the search engine popup. We not binding directly to the model +// in order to siphon off the setter so we can record the metric. If we're +// doing it with one, might as well do it with both. +- (NSUInteger)searchEngineSelectedIndex { + return [searchEngineModel_ defaultIndex]; +} + +- (void)setSearchEngineSelectedIndex:(NSUInteger)index { + [self recordUserAction:L"Options_SearchEngineChanged"]; + [searchEngineModel_ setDefaultIndex:index]; +} + +// Called when the search engine model changes. Update the selection in the +// popup by tickling the bindings with the new value. +- (void)searchEngineModelChanged:(NSNotification*)notify { + [self setSearchEngineSelectedIndex:[self searchEngineSelectedIndex]]; +} + // Called when the user clicks the button to make Chromium the default // browser. Registers http and https. - (IBAction)makeDefaultBrowser:(id)sender { diff --git a/chrome/browser/cocoa/search_engine_list_model.h b/chrome/browser/cocoa/search_engine_list_model.h new file mode 100644 index 0000000..3d1fd58 --- /dev/null +++ b/chrome/browser/cocoa/search_engine_list_model.h @@ -0,0 +1,45 @@ +// Copyright (c) 2009 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. + +#ifndef CHROME_BROWSER_COCOA_SEARCH_ENGINE_LIST_MODEL_H_ +#define CHROME_BROWSER_COCOA_SEARCH_ENGINE_LIST_MODEL_H_ + +#import <Cocoa/Cocoa.h> + +#include "base/scoped_nsobject.h" +#include "base/scoped_ptr.h" + +class TemplateURLModel; +class SearchEngineObserver; + +// The model for the "default search engine" combobox in preferences. Bridges +// between the cross-platform TemplateURLModel and Cocoa while watching for +// changes to the cross-platform model. + +@interface SearchEngineListModel : NSObject { + @private + TemplateURLModel* model_; // weak, owned by Profile + scoped_ptr<SearchEngineObserver> observer_; // watches for model changes + scoped_nsobject<NSArray> engines_; +} + +// Initialize with the given template model. +- (id)initWithModel:(TemplateURLModel*)model; + +// Returns an array of NSString's corresponding to the user-visible names of the +// search engines. +- (NSArray*)searchEngines; + +// The index into |-searchEngines| of the current default search engine. The +// setter changes the back-end preference. +- (NSUInteger)defaultIndex; +- (void)setDefaultIndex:(NSUInteger)index; + +@end + +// Broadcast when the cross-platform model changes. This can be used to update +// any view state that may rely on the position of items in the list. +extern NSString* const kSearchEngineListModelChangedNotification; + +#endif // CHROME_BROWSER_COCOA_SEARCH_ENGINE_LIST_MODEL_H_ diff --git a/chrome/browser/cocoa/search_engine_list_model.mm b/chrome/browser/cocoa/search_engine_list_model.mm new file mode 100644 index 0000000..1900231 --- /dev/null +++ b/chrome/browser/cocoa/search_engine_list_model.mm @@ -0,0 +1,112 @@ +// Copyright (c) 2009 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_list_model.h" + +#include "base/sys_string_conversions.h" +#include "chrome/browser/search_engines/template_url.h" +#include "chrome/browser/search_engines/template_url_model.h" + +NSString* const kSearchEngineListModelChangedNotification = + @"kSearchEngineListModelChangedNotification"; + +@interface SearchEngineListModel(Private) +- (void)buildEngineList; +@end + +// C++ bridge from TemplateURLModel to our Obj-C model. When it's told about +// model changes, notifies us to rebuild the list. +class SearchEngineObserver : public TemplateURLModelObserver { + public: + SearchEngineObserver(SearchEngineListModel* notify) + : notify_(notify) { } + virtual ~SearchEngineObserver() { }; + + private: + // TemplateURLModelObserver methods. + virtual void OnTemplateURLModelChanged() { [notify_ buildEngineList]; } + + SearchEngineListModel* notify_; // weak, owns us +}; + +@implementation SearchEngineListModel + +// The windows code allows for a NULL |model| and checks for it throughout +// the code, though I'm not sure why. We follow suit. +- (id)initWithModel:(TemplateURLModel*)model { + if ((self = [super init])) { + model_ = model; + if (model_) { + observer_.reset(new SearchEngineObserver(self)); + model_->Load(); + model_->AddObserver(observer_.get()); + [self buildEngineList]; + } + } + return self; +} + +- (void)dealloc { + if (model_) + model_->RemoveObserver(observer_.get()); + [super dealloc]; +} + +// Returns an array of NSString's corresponding to the user-visible names of the +// search engines. +- (NSArray*)searchEngines { + return engines_.get(); +} + +- (void)setSearchEngines:(NSArray*)engines { + engines_.reset([engines retain]); + + // Tell anyone who's listening that something has changed so they need to + // adjust the UI. + [[NSNotificationCenter defaultCenter] + postNotificationName:kSearchEngineListModelChangedNotification + object:nil]; +} + +// Walks the model and builds an array of NSStrings to display to the user. +// Assumes there is a non-NULL model. +- (void)buildEngineList { + scoped_nsobject<NSMutableArray> engines([[NSMutableArray alloc] init]); + + typedef std::vector<const TemplateURL*> TemplateURLs; + TemplateURLs modelURLs = model_->GetTemplateURLs(); + for (size_t i = 0; i < modelURLs.size(); ++i) { + if (modelURLs[i]->ShowInDefaultList()) + [engines addObject:base::SysWideToNSString(modelURLs[i]->short_name())]; + } + + [self setSearchEngines:engines.get()]; +} + +// The index into |-searchEngines| of the current default search engine. +- (NSUInteger)defaultIndex { + if (!model_) return 0; + + const TemplateURL* defaultSearchProvider = model_->GetDefaultSearchProvider(); + if (defaultSearchProvider) { + typedef std::vector<const TemplateURL*> TemplateURLs; + TemplateURLs urls = model_->GetTemplateURLs(); + TemplateURLs::iterator i = + find(urls.begin(), urls.end(), defaultSearchProvider); + if (i != urls.end()) + return static_cast<int>(i - urls.begin()); + } + return 0; +} + +- (void)setDefaultIndex:(NSUInteger)index { + if (model_) { + typedef std::vector<const TemplateURL*> TemplateURLs; + TemplateURLs urls = model_->GetTemplateURLs(); + DCHECK(index < urls.size()); + model_->SetDefaultSearchProvider(urls[index]); + } +} + +@end diff --git a/chrome/browser/cocoa/search_engine_list_model_unittest.mm b/chrome/browser/cocoa/search_engine_list_model_unittest.mm new file mode 100644 index 0000000..9e592e7 --- /dev/null +++ b/chrome/browser/cocoa/search_engine_list_model_unittest.mm @@ -0,0 +1,95 @@ +// Copyright (c) 2009 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. + +#include "base/scoped_nsobject.h" +#include "chrome/browser/cocoa/browser_test_helper.h" +#import "chrome/browser/cocoa/search_engine_list_model.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/search_engines/template_url.h" +#include "chrome/browser/search_engines/template_url_model.h" +#include "testing/gtest/include/gtest/gtest.h" + +// A helper for NSNotifications. Makes a note that it's been called back. +@interface SearchEngineListHelper : NSObject { + @public + BOOL sawNotification_; +} +@end + +@implementation SearchEngineListHelper +- (void)entryChanged:(NSNotification*)notify { + sawNotification_ = YES; +} +@end + +class SearchEngineListModelTest : public testing::Test { + public: + SearchEngineListModelTest() { + // Build a fake set of template urls. + template_model_.reset(new TemplateURLModel(helper_.profile())); + TemplateURL* t_url = new TemplateURL(); + t_url->SetURL(L"http://www.google.com/foo/bar", 0, 0); + t_url->set_keyword(L"keyword"); + t_url->set_short_name(L"google"); + t_url->set_show_in_default_list(true); + template_model_->Add(t_url); + t_url = new TemplateURL(); + t_url->SetURL(L"http://www.google2.com/foo/bar", 0, 0); + t_url->set_keyword(L"keyword2"); + t_url->set_short_name(L"google2"); + t_url->set_show_in_default_list(true); + template_model_->Add(t_url); + EXPECT_EQ(template_model_->GetTemplateURLs().size(), 2U); + + model_.reset([[SearchEngineListModel alloc] + initWithModel:template_model_.get()]); + notification_helper_.reset([[SearchEngineListHelper alloc] init]); + [[NSNotificationCenter defaultCenter] + addObserver:notification_helper_.get() + selector:@selector(entryChanged:) + name:kSearchEngineListModelChangedNotification + object:nil]; + } + ~SearchEngineListModelTest() { + [[NSNotificationCenter defaultCenter] + removeObserver:notification_helper_.get()]; + } + + BrowserTestHelper helper_; + scoped_ptr<TemplateURLModel> template_model_; + scoped_nsobject<SearchEngineListModel> model_; + scoped_nsobject<SearchEngineListHelper> notification_helper_; +}; + +TEST_F(SearchEngineListModelTest, Init) { + scoped_nsobject<SearchEngineListModel> model( + [[SearchEngineListModel alloc] initWithModel:template_model_.get()]); +} + +TEST_F(SearchEngineListModelTest, Engines) { + NSArray* engines = [model_ searchEngines]; + // TODO(pinkerton): because the templates we create aren't truly parsable, + // they won't pass the "displayable" test and thus we don't get any results. + EXPECT_EQ([engines count], /* 2U */ 0U); +} + +TEST_F(SearchEngineListModelTest, Default) { + EXPECT_EQ([model_ defaultIndex], 0U); + + [model_ setDefaultIndex:1]; + EXPECT_EQ([model_ defaultIndex], 1U); +} + +// Make sure that when the back-end model changes that we get a notification. +TEST_F(SearchEngineListModelTest, Notification) { + // Add one more item to force a notification. + TemplateURL* t_url = new TemplateURL(); + t_url->SetURL(L"http://www.google3.com/foo/bar", 0, 0); + t_url->set_keyword(L"keyword3"); + t_url->set_short_name(L"google3"); + t_url->set_show_in_default_list(true); + template_model_->Add(t_url); + + EXPECT_TRUE(notification_helper_.get()->sawNotification_); +} |