1// Copyright (c) 2012 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/browser/edit_search_engine_cocoa_controller.h" 6 7#include "base/logging.h" 8#include "base/mac/bundle_locations.h" 9#import "base/mac/mac_util.h" 10#include "base/strings/string16.h" 11#include "base/strings/sys_string_conversions.h" 12#include "chrome/browser/search_engines/template_url.h" 13#include "chrome/browser/search_engines/ui_thread_search_terms_data.h" 14#include "grit/generated_resources.h" 15#include "grit/theme_resources.h" 16#include "grit/ui_resources.h" 17#include "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" 18#include "ui/base/l10n/l10n_util_mac.h" 19#include "ui/base/resource/resource_bundle.h" 20#include "ui/gfx/image/image.h" 21 22namespace { 23 24void ShiftOriginY(NSView* view, CGFloat amount) { 25 NSPoint origin = [view frame].origin; 26 origin.y += amount; 27 [view setFrameOrigin:origin]; 28} 29 30} // namespace 31 32@implementation EditSearchEngineCocoaController 33 34- (id)initWithProfile:(Profile*)profile 35 delegate:(EditSearchEngineControllerDelegate*)delegate 36 templateURL:(TemplateURL*)url { 37 DCHECK(profile); 38 NSString* nibpath = [base::mac::FrameworkBundle() 39 pathForResource:@"EditSearchEngine" 40 ofType:@"nib"]; 41 if ((self = [super initWithWindowNibPath:nibpath owner:self])) { 42 profile_ = profile; 43 templateURL_ = url; 44 controller_.reset( 45 new EditSearchEngineController(templateURL_, delegate, profile_)); 46 } 47 return self; 48} 49 50- (void)awakeFromNib { 51 DCHECK([self window]); 52 DCHECK_EQ(self, [[self window] delegate]); 53 54 // Make sure the url description field fits the text in it. 55 CGFloat descriptionShift = [GTMUILocalizerAndLayoutTweaker 56 sizeToFitFixedWidthTextField:urlDescriptionField_]; 57 58 // Move the label container above the url description. 59 ShiftOriginY(labelContainer_, descriptionShift); 60 // There was no way via view containment to use a helper view to move all 61 // the textfields and images at once, most move them all on their own so 62 // they stay above the url description. 63 ShiftOriginY(nameField_, descriptionShift); 64 ShiftOriginY(keywordField_, descriptionShift); 65 ShiftOriginY(urlField_, descriptionShift); 66 ShiftOriginY(nameImage_, descriptionShift); 67 ShiftOriginY(keywordImage_, descriptionShift); 68 ShiftOriginY(urlImage_, descriptionShift); 69 70 // Resize the containing box for the name/keyword/url fields/images since it 71 // also contains the url description (which just grew). 72 [[fieldAndImageContainer_ contentView] setAutoresizesSubviews:NO]; 73 NSRect rect = [fieldAndImageContainer_ frame]; 74 rect.size.height += descriptionShift; 75 [fieldAndImageContainer_ setFrame:rect]; 76 [[fieldAndImageContainer_ contentView] setAutoresizesSubviews:YES]; 77 78 // Resize the window. 79 NSWindow* window = [self window]; 80 NSSize windowDelta = NSMakeSize(0, descriptionShift); 81 [GTMUILocalizerAndLayoutTweaker 82 resizeWindowWithoutAutoResizingSubViews:window 83 delta:windowDelta]; 84 85 if (templateURL_) { 86 // Defaults to |..._NEW_WINDOW_TITLE|. 87 [window setTitle:l10n_util::GetNSString( 88 IDS_SEARCH_ENGINES_EDITOR_EDIT_WINDOW_TITLE)]; 89 [nameField_ setStringValue: 90 base::SysUTF16ToNSString(templateURL_->short_name())]; 91 [keywordField_ setStringValue: 92 base::SysUTF16ToNSString(templateURL_->keyword())]; 93 [urlField_ setStringValue: 94 base::SysUTF16ToNSString(templateURL_->url_ref().DisplayURL( 95 UIThreadSearchTermsData(profile_)))]; 96 [urlField_ setEnabled:(templateURL_->prepopulate_id() == 0)]; 97 } 98 // When creating a new keyword, this will mark the fields as "invalid" and 99 // will not let the user save. If this is an edit, then this will set all 100 // the images to the "valid" state. 101 [self validateFields]; 102} 103 104// When the window closes, clean ourselves up. 105- (void)windowWillClose:(NSNotification*)notif { 106 [self autorelease]; 107} 108 109// Performs the logic of closing the window. If we are a sheet, then it ends the 110// modal session; otherwise, it closes the window. 111- (void)doClose { 112 if ([[self window] isSheet]) { 113 [NSApp endSheet:[self window]]; 114 } else { 115 [[self window] close]; 116 } 117} 118 119- (IBAction)cancel:(id)sender { 120 [self doClose]; 121} 122 123- (IBAction)save:(id)sender { 124 DCHECK([self validateFields]); 125 base::string16 title = base::SysNSStringToUTF16([nameField_ stringValue]); 126 base::string16 keyword = 127 base::SysNSStringToUTF16([keywordField_ stringValue]); 128 std::string url = base::SysNSStringToUTF8([urlField_ stringValue]); 129 controller_->AcceptAddOrEdit(title, keyword, url); 130 [self doClose]; 131} 132 133// Delegate method for the text fields. 134 135- (void)controlTextDidChange:(NSNotification*)notif { 136 [self validateFields]; 137} 138 139- (void)controlTextDidEndEditing:(NSNotification*)notif { 140 [self validateFields]; 141} 142 143// Private ///////////////////////////////////////////////////////////////////// 144 145// Sets the appropriate image and tooltip based on a boolean |valid|. 146- (void)setIsValid:(BOOL)valid 147 toolTip:(int)messageID 148 forImageView:(NSImageView*)imageView 149 textField:(NSTextField*)textField { 150 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 151 NSImage* image = valid ? rb.GetNativeImageNamed(IDR_INPUT_GOOD).ToNSImage() 152 : rb.GetNativeImageNamed(IDR_INPUT_ALERT).ToNSImage(); 153 [imageView setImage:image]; 154 155 NSString* toolTip = nil; 156 if (!valid) 157 toolTip = l10n_util::GetNSString(messageID); 158 [textField setToolTip:toolTip]; 159 [imageView setToolTip:toolTip]; 160} 161 162// This sets the image state for all the controls and enables or disables the 163// done button. Returns YES if all the fields are valid. 164- (BOOL)validateFields { 165 base::string16 title = base::SysNSStringToUTF16([nameField_ stringValue]); 166 BOOL titleValid = controller_->IsTitleValid(title); 167 [self setIsValid:titleValid 168 toolTip:IDS_SEARCH_ENGINES_INVALID_TITLE_TT 169 forImageView:nameImage_ 170 textField:nameField_]; 171 172 base::string16 keyword = 173 base::SysNSStringToUTF16([keywordField_ stringValue]); 174 BOOL keywordValid = controller_->IsKeywordValid(keyword); 175 [self setIsValid:keywordValid 176 toolTip:IDS_SEARCH_ENGINES_INVALID_KEYWORD_TT 177 forImageView:keywordImage_ 178 textField:keywordField_]; 179 180 std::string url = base::SysNSStringToUTF8([urlField_ stringValue]); 181 BOOL urlValid = controller_->IsURLValid(url); 182 [self setIsValid:urlValid 183 toolTip:IDS_SEARCH_ENGINES_INVALID_URL_TT 184 forImageView:urlImage_ 185 textField:urlField_]; 186 187 BOOL isValid = (titleValid && keywordValid && urlValid); 188 [doneButton_ setEnabled:isValid]; 189 return isValid; 190} 191 192@end 193