OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "chrome/browser/cocoa/search_engine_dialog_controller.h" |
| 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "app/l10n_util_mac.h" |
| 10 #include "app/resource_bundle.h" |
| 11 #include "base/mac_util.h" |
| 12 #include "base/nsimage_cache_mac.h" |
| 13 #include "base/sys_string_conversions.h" |
| 14 #include "base/time.h" |
| 15 #include "chrome/browser/profile.h" |
| 16 #include "chrome/browser/search_engines/template_url.h" |
| 17 #include "chrome/browser/search_engines/template_url_model.h" |
| 18 #include "chrome/browser/search_engines/template_url_model_observer.h" |
| 19 #include "grit/generated_resources.h" |
| 20 #include "grit/theme_resources.h" |
| 21 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
| 22 |
| 23 // Horizontal spacing between search engine choices. |
| 24 const int kSearchEngineSpacing = 20; |
| 25 |
| 26 // Vertical spacing between the search engine logo and the button underneath. |
| 27 const int kLogoButtonSpacing = 10; |
| 28 |
| 29 // Width of a label used in place of a logo. |
| 30 const int kLogoLabelWidth = 170; |
| 31 |
| 32 // Height of a label used in place of a logo. |
| 33 const int kLogoLabelHeight = 25; |
| 34 |
| 35 @interface SearchEngineDialogController (Private) |
| 36 - (void)onTemplateURLModelChanged; |
| 37 - (void)buildSearchEngineView; |
| 38 - (NSView*)viewForSearchEngine:(const TemplateURL*)engine |
| 39 atIndex:(size_t)index; |
| 40 - (IBAction)searchEngineSelected:(id)sender; |
| 41 @end |
| 42 |
| 43 class SearchEngineDialogControllerBridge : public TemplateURLModelObserver { |
| 44 public: |
| 45 SearchEngineDialogControllerBridge(SearchEngineDialogController* controller); |
| 46 |
| 47 // TemplateURLModelObserver |
| 48 virtual void OnTemplateURLModelChanged(); |
| 49 |
| 50 private: |
| 51 SearchEngineDialogController* controller_; |
| 52 }; |
| 53 |
| 54 SearchEngineDialogControllerBridge::SearchEngineDialogControllerBridge( |
| 55 SearchEngineDialogController* controller) : controller_(controller) { |
| 56 } |
| 57 |
| 58 void SearchEngineDialogControllerBridge::OnTemplateURLModelChanged() { |
| 59 [controller_ onTemplateURLModelChanged]; |
| 60 MessageLoop::current()->QuitNow(); |
| 61 } |
| 62 |
| 63 @implementation SearchEngineDialogController |
| 64 |
| 65 @synthesize profile = profile_; |
| 66 @synthesize randomize = randomize_; |
| 67 |
| 68 - (id)init { |
| 69 NSString* nibpath = |
| 70 [mac_util::MainAppBundle() pathForResource:@"SearchEngineDialog" |
| 71 ofType:@"nib"]; |
| 72 self = [super initWithWindowNibPath:nibpath owner:self]; |
| 73 if (self != nil) { |
| 74 bridge_.reset(new SearchEngineDialogControllerBridge(self)); |
| 75 } |
| 76 return self; |
| 77 } |
| 78 |
| 79 - (void)dealloc { |
| 80 [super dealloc]; |
| 81 } |
| 82 |
| 83 - (IBAction)showWindow:(id)sender { |
| 84 searchEnginesModel_ = profile_->GetTemplateURLModel(); |
| 85 searchEnginesModel_->AddObserver(bridge_.get()); |
| 86 |
| 87 if (searchEnginesModel_->loaded()) { |
| 88 [self onTemplateURLModelChanged]; |
| 89 } else { |
| 90 searchEnginesModel_->Load(); |
| 91 MessageLoop::current()->Run(); |
| 92 } |
| 93 } |
| 94 |
| 95 - (void)onTemplateURLModelChanged { |
| 96 searchEnginesModel_->RemoveObserver(bridge_.get()); |
| 97 |
| 98 // Add the search engines in the search_engines_model_ to the buttons list. |
| 99 // The first three will always be from prepopulated data. |
| 100 std::vector<const TemplateURL*> templateUrls = |
| 101 searchEnginesModel_->GetTemplateURLs(); |
| 102 |
| 103 // If we have fewer than two search engines, end the search engine dialog |
| 104 // immediately, leaving the imported default search engine setting intact. |
| 105 if (templateUrls.size() < 2) { |
| 106 return; |
| 107 } |
| 108 |
| 109 NSWindow* win = [self window]; |
| 110 |
| 111 [win setBackgroundColor:[NSColor whiteColor]]; |
| 112 |
| 113 NSImage* headerImage = ResourceBundle::GetSharedInstance(). |
| 114 GetNSImageNamed(IDR_SEARCH_ENGINE_DIALOG_TOP); |
| 115 [headerImageView_ setImage:headerImage]; |
| 116 |
| 117 // Is the user's default search engine included in the first three |
| 118 // prepopulated set? If not, we need to expand the dialog to include a fourth |
| 119 // engine. |
| 120 const TemplateURL* defaultSearchEngine = |
| 121 searchEnginesModel_->GetDefaultSearchProvider(); |
| 122 |
| 123 std::vector<const TemplateURL*>::iterator engineIter = |
| 124 templateUrls.begin(); |
| 125 for (int i = 0; engineIter != templateUrls.end(); ++i, ++engineIter) { |
| 126 if (i < 3) { |
| 127 choices_.push_back(*engineIter); |
| 128 } else { |
| 129 if (*engineIter == defaultSearchEngine) |
| 130 choices_.push_back(*engineIter); |
| 131 } |
| 132 } |
| 133 |
| 134 // Randomize the order of the logos if the option has been set. |
| 135 if (randomize_) { |
| 136 int seed = static_cast<int>(base::Time::Now().ToInternalValue()); |
| 137 srand(seed); |
| 138 std::random_shuffle(choices_.begin(), choices_.end()); |
| 139 } |
| 140 |
| 141 [self buildSearchEngineView]; |
| 142 |
| 143 // Display the dialog. |
| 144 NSInteger choice = [NSApp runModalForWindow:win]; |
| 145 searchEnginesModel_->SetDefaultSearchProvider(choices_.at(choice)); |
| 146 } |
| 147 |
| 148 - (void)buildSearchEngineView { |
| 149 scoped_nsobject<NSMutableArray> searchEngineViews |
| 150 ([[NSMutableArray alloc] init]); |
| 151 |
| 152 for (size_t i = 0; i < choices_.size(); ++i) |
| 153 [searchEngineViews addObject:[self viewForSearchEngine:choices_.at(i) |
| 154 atIndex:i]]; |
| 155 |
| 156 NSSize newOverallSize = NSZeroSize; |
| 157 for (NSView* view in searchEngineViews.get()) { |
| 158 NSRect engineFrame = [view frame]; |
| 159 engineFrame.origin = NSMakePoint(newOverallSize.width, 0); |
| 160 [searchEngineView_ addSubview:view]; |
| 161 [view setFrame:engineFrame]; |
| 162 newOverallSize = NSMakeSize( |
| 163 newOverallSize.width + NSWidth(engineFrame) + kSearchEngineSpacing, |
| 164 std::max(newOverallSize.height, NSHeight(engineFrame))); |
| 165 } |
| 166 newOverallSize.width -= kSearchEngineSpacing; |
| 167 |
| 168 // Resize the window to fit (and because it's bound on all sides it will |
| 169 // resize the search engine view). |
| 170 NSSize currentOverallSize = [searchEngineView_ bounds].size; |
| 171 NSSize deltaSize = NSMakeSize( |
| 172 newOverallSize.width - currentOverallSize.width, |
| 173 newOverallSize.height - currentOverallSize.height); |
| 174 NSSize windowDeltaSize = [searchEngineView_ convertSize:deltaSize toView:nil]; |
| 175 NSRect windowFrame = [[self window] frame]; |
| 176 windowFrame.size.width += windowDeltaSize.width; |
| 177 windowFrame.size.height += windowDeltaSize.height; |
| 178 [[self window] setFrame:windowFrame display:NO]; |
| 179 } |
| 180 |
| 181 - (NSView*)viewForSearchEngine:(const TemplateURL*)engine |
| 182 atIndex:(size_t)index { |
| 183 bool useImages = false; |
| 184 #if defined(GOOGLE_CHROME_BUILD) |
| 185 useImages = true; |
| 186 #endif |
| 187 |
| 188 // Make the engine identifier. |
| 189 NSView* engineIdentifier = nil; // either the logo or the text label |
| 190 |
| 191 int logoId = engine->logo_id(); |
| 192 if (useImages && logoId > 0) { |
| 193 NSImage* logoImage = |
| 194 ResourceBundle::GetSharedInstance().GetNSImageNamed(logoId); |
| 195 NSRect logoBounds = NSZeroRect; |
| 196 logoBounds.size = [logoImage size]; |
| 197 NSImageView* logoView = |
| 198 [[[NSImageView alloc] initWithFrame:logoBounds] autorelease]; |
| 199 [logoView setImage:logoImage]; |
| 200 [logoView setEditable:NO]; |
| 201 |
| 202 // Tooltip text provides accessibility. |
| 203 [logoView setToolTip:base::SysWideToNSString(engine->short_name())]; |
| 204 engineIdentifier = logoView; |
| 205 } else { |
| 206 // No logo -- we must show a text label. |
| 207 NSRect labelBounds = NSMakeRect(0, 0, kLogoLabelWidth, kLogoLabelHeight); |
| 208 NSTextField* labelField = |
| 209 [[[NSTextField alloc] initWithFrame:labelBounds] autorelease]; |
| 210 [labelField setBezeled:NO]; |
| 211 [labelField setEditable:NO]; |
| 212 [labelField setSelectable:NO]; |
| 213 |
| 214 scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( |
| 215 [[NSMutableParagraphStyle alloc] init]); |
| 216 [paragraphStyle setAlignment:NSCenterTextAlignment]; |
| 217 NSDictionary* attrs = [NSDictionary dictionaryWithObjectsAndKeys: |
| 218 [NSFont boldSystemFontOfSize:13], NSFontAttributeName, |
| 219 paragraphStyle.get(), NSParagraphStyleAttributeName, |
| 220 nil]; |
| 221 |
| 222 NSString* value = base::SysWideToNSString(engine->short_name()); |
| 223 scoped_nsobject<NSAttributedString> attrValue( |
| 224 [[NSAttributedString alloc] initWithString:value |
| 225 attributes:attrs]); |
| 226 |
| 227 [labelField setAttributedStringValue:attrValue.get()]; |
| 228 |
| 229 engineIdentifier = labelField; |
| 230 } |
| 231 |
| 232 // Make the "Choose" button. |
| 233 scoped_nsobject<NSButton> chooseButton( |
| 234 [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 34)]); |
| 235 [chooseButton setBezelStyle:NSRoundedBezelStyle]; |
| 236 [[chooseButton cell] setFont:[NSFont systemFontOfSize: |
| 237 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; |
| 238 [chooseButton setTitle:l10n_util::GetNSStringWithFixup(IDS_FR_SEARCH_CHOOSE)]; |
| 239 [GTMUILocalizerAndLayoutTweaker sizeToFitView:chooseButton.get()]; |
| 240 [chooseButton setTag:index]; |
| 241 [chooseButton setTarget:self]; |
| 242 [chooseButton setAction:@selector(searchEngineSelected:)]; |
| 243 |
| 244 // Put 'em together. |
| 245 NSRect engineIdentifierFrame = [engineIdentifier frame]; |
| 246 NSRect chooseButtonFrame = [chooseButton frame]; |
| 247 |
| 248 NSRect containingViewFrame = NSZeroRect; |
| 249 containingViewFrame.size.width += engineIdentifierFrame.size.width; |
| 250 containingViewFrame.size.height += engineIdentifierFrame.size.height; |
| 251 containingViewFrame.size.height += kLogoButtonSpacing; |
| 252 containingViewFrame.size.height += chooseButtonFrame.size.height; |
| 253 |
| 254 NSView* containingView = |
| 255 [[[NSView alloc] initWithFrame:containingViewFrame] autorelease]; |
| 256 |
| 257 [containingView addSubview:engineIdentifier]; |
| 258 engineIdentifierFrame.origin.y = |
| 259 chooseButtonFrame.size.height + kLogoButtonSpacing; |
| 260 [engineIdentifier setFrame:engineIdentifierFrame]; |
| 261 |
| 262 [containingView addSubview:chooseButton]; |
| 263 chooseButtonFrame.origin.x = |
| 264 int((containingViewFrame.size.width - chooseButtonFrame.size.width) / 2); |
| 265 [chooseButton setFrame:chooseButtonFrame]; |
| 266 |
| 267 return containingView; |
| 268 } |
| 269 |
| 270 - (NSFont*)mainLabelFont { |
| 271 return [NSFont boldSystemFontOfSize:13]; |
| 272 } |
| 273 |
| 274 - (IBAction)searchEngineSelected:(id)sender { |
| 275 [[self window] close]; |
| 276 [NSApp stopModalWithCode:[sender tag]]; |
| 277 } |
| 278 |
| 279 @end |
OLD | NEW |