| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 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 #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" | |
| 6 | |
| 7 #include "base/sys_string_conversions.h" | |
| 8 #include "chrome/browser/autocomplete/autocomplete_edit.h" | |
| 9 #include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" | |
| 10 | |
| 11 // Thin Obj-C bridge class that is the delegate of the omnibox field. | |
| 12 // It intercepts various control delegate methods and vectors them to | |
| 13 // the edit view. | |
| 14 | |
| 15 @interface AutocompleteFieldDelegate : NSObject { | |
| 16 @private | |
| 17 AutocompleteEditViewMac* edit_view_; // weak, owns us. | |
| 18 } | |
| 19 - initWithEditView:(AutocompleteEditViewMac*)view; | |
| 20 @end | |
| 21 | |
| 22 AutocompleteEditViewMac::AutocompleteEditViewMac( | |
| 23 AutocompleteEditController* controller, | |
| 24 ToolbarModel* toolbar_model, | |
| 25 Profile* profile, | |
| 26 CommandUpdater* command_updater) | |
| 27 : model_(new AutocompleteEditModel(this, controller, profile)), | |
| 28 popup_view_(new AutocompletePopupViewMac(this, model_.get(), profile)), | |
| 29 controller_(controller), | |
| 30 toolbar_model_(toolbar_model), | |
| 31 command_updater_(command_updater), | |
| 32 field_(nil), | |
| 33 edit_helper_([[AutocompleteFieldDelegate alloc] initWithEditView:this]) { | |
| 34 DCHECK(controller); | |
| 35 DCHECK(toolbar_model); | |
| 36 DCHECK(profile); | |
| 37 DCHECK(command_updater); | |
| 38 } | |
| 39 | |
| 40 AutocompleteEditViewMac::~AutocompleteEditViewMac() { | |
| 41 // TODO(shess): Having to be aware of destructor ordering in this | |
| 42 // way seems brittle. There must be a better way. | |
| 43 | |
| 44 // Destroy popup view before this object in case it tries to call us | |
| 45 // back in the destructor. Likewise for destroying the model before | |
| 46 // this object. | |
| 47 popup_view_.reset(); | |
| 48 model_.reset(); | |
| 49 | |
| 50 // Disconnect field_ from edit_helper_ so that we don't get calls | |
| 51 // after destruction. | |
| 52 [field_ setDelegate:nil]; | |
| 53 } | |
| 54 | |
| 55 // TODO(shess): This is the minimal change which seems to unblock | |
| 56 // getting the minimal Omnibox code checked in without making the | |
| 57 // world worse. Browser::TabSelectedAt() calls this when the tab | |
| 58 // changes, but that is only wired up for Windows. I do not yet | |
| 59 // understand that code well enough to go for it. Once wired up, then | |
| 60 // code can be removed at: | |
| 61 // [TabContentsController defocusLocationBar] | |
| 62 // [TabStripController selectTabWithContents:...] | |
| 63 void AutocompleteEditViewMac::SaveStateToTab(TabContents* tab) { | |
| 64 // TODO(shess): Actually save the state to the tab area. | |
| 65 | |
| 66 // Drop the popup before we change to another tab. | |
| 67 ClosePopup(); | |
| 68 } | |
| 69 | |
| 70 void AutocompleteEditViewMac::OpenURL(const GURL& url, | |
| 71 WindowOpenDisposition disposition, | |
| 72 PageTransition::Type transition, | |
| 73 const GURL& alternate_nav_url, | |
| 74 size_t selected_line, | |
| 75 const std::wstring& keyword) { | |
| 76 // TODO(shess): Why is the caller passing an invalid url in the | |
| 77 // first place? Make sure that case isn't being dropped on the | |
| 78 // floor. | |
| 79 if (!url.is_valid()) { | |
| 80 return; | |
| 81 } | |
| 82 | |
| 83 model_->SendOpenNotification(selected_line, keyword); | |
| 84 | |
| 85 if (disposition != NEW_BACKGROUND_TAB) | |
| 86 RevertAll(); // Revert the box to its unedited state. | |
| 87 controller_->OnAutocompleteAccept(url, disposition, transition, | |
| 88 alternate_nav_url); | |
| 89 } | |
| 90 | |
| 91 std::wstring AutocompleteEditViewMac::GetText() const { | |
| 92 return base::SysNSStringToWide([field_ stringValue]); | |
| 93 } | |
| 94 | |
| 95 void AutocompleteEditViewMac::SetWindowTextAndCaretPos(const std::wstring& text, | |
| 96 size_t caret_pos) { | |
| 97 UpdateAndStyleText(text, text.size()); | |
| 98 } | |
| 99 | |
| 100 void AutocompleteEditViewMac::SelectAll(bool reversed) { | |
| 101 // TODO(shess): Figure out what reversed implies. The gtk version | |
| 102 // has it imply inverting the selection front to back, but I don't | |
| 103 // even know if that makes sense for Mac. | |
| 104 UpdateAndStyleText(GetText(), 0); | |
| 105 } | |
| 106 | |
| 107 void AutocompleteEditViewMac::RevertAll() { | |
| 108 ClosePopup(); | |
| 109 model_->Revert(); | |
| 110 | |
| 111 std::wstring tt = GetText(); | |
| 112 UpdateAndStyleText(tt, tt.size()); | |
| 113 controller_->OnChanged(); | |
| 114 } | |
| 115 | |
| 116 void AutocompleteEditViewMac::UpdatePopup() { | |
| 117 model_->SetInputInProgress(true); | |
| 118 if (!model_->has_focus()) | |
| 119 return; | |
| 120 | |
| 121 // TODO(shess): | |
| 122 // Shouldn't inline autocomplete when the caret/selection isn't at | |
| 123 // the end of the text. | |
| 124 // | |
| 125 // One option would seem to be to check for a non-nil field | |
| 126 // editor, and check it's selected range against its length. | |
| 127 model_->StartAutocomplete(false); | |
| 128 } | |
| 129 | |
| 130 void AutocompleteEditViewMac::ClosePopup() { | |
| 131 popup_view_->StopAutocomplete(); | |
| 132 } | |
| 133 | |
| 134 void AutocompleteEditViewMac::UpdateAndStyleText( | |
| 135 const std::wstring& display_text, size_t user_text_length) { | |
| 136 NSString* ss = base::SysWideToNSString(display_text); | |
| 137 NSMutableAttributedString* as = | |
| 138 [[[NSMutableAttributedString alloc] initWithString:ss] autorelease]; | |
| 139 | |
| 140 url_parse::Parsed parts; | |
| 141 AutocompleteInput::Parse(display_text, model_->GetDesiredTLD(), | |
| 142 &parts, NULL); | |
| 143 bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0); | |
| 144 if (emphasize) { | |
| 145 // TODO(shess): Pull color out as a constant. | |
| 146 [as addAttribute:NSForegroundColorAttributeName | |
| 147 value:[NSColor greenColor] | |
| 148 range:NSMakeRange((NSInteger)parts.host.begin, | |
| 149 (NSInteger)parts.host.end())]; | |
| 150 } | |
| 151 | |
| 152 // TODO(shess): GTK has this as a member var, figure out why. | |
| 153 ToolbarModel::SecurityLevel scheme_security_level = | |
| 154 toolbar_model_->GetSchemeSecurityLevel(); | |
| 155 | |
| 156 // Emphasize the scheme for security UI display purposes (if necessary). | |
| 157 if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() && | |
| 158 (scheme_security_level != ToolbarModel::NORMAL)) { | |
| 159 // TODO(shess): Pull colors out as constants. | |
| 160 NSColor* color; | |
| 161 if (scheme_security_level == ToolbarModel::SECURE) { | |
| 162 color = [NSColor blueColor]; | |
| 163 } else { | |
| 164 color = [NSColor blackColor]; | |
| 165 } | |
| 166 [as addAttribute:NSForegroundColorAttributeName value:color | |
| 167 range:NSMakeRange((NSInteger)parts.scheme.begin, | |
| 168 (NSInteger)parts.scheme.end())]; | |
| 169 } | |
| 170 | |
| 171 // TODO(shess): Check that this updates the model's sense of focus | |
| 172 // correctly. | |
| 173 [field_ setObjectValue:as]; | |
| 174 if (![field_ currentEditor]) { | |
| 175 [field_ becomeFirstResponder]; | |
| 176 DCHECK_EQ(field_, [[field_ window] firstResponder]); | |
| 177 } | |
| 178 | |
| 179 NSRange selected_range = NSMakeRange(user_text_length, [as length]); | |
| 180 // TODO(shess): What if it didn't get first responder, and there is | |
| 181 // no field editor? This will do nothing. Well, at least it won't | |
| 182 // crash. Think of something more productive to do, or prove that | |
| 183 // it cannot occur and DCHECK appropriately. | |
| 184 [[field_ currentEditor] setSelectedRange:selected_range]; | |
| 185 } | |
| 186 | |
| 187 void AutocompleteEditViewMac::OnTemporaryTextMaybeChanged( | |
| 188 const std::wstring& display_text, bool save_original_selection) { | |
| 189 // TODO(shess): I believe this is for when the user arrows around | |
| 190 // the popup, will be restored if they hit escape. Figure out if | |
| 191 // that is for certain it. | |
| 192 if (save_original_selection) { | |
| 193 saved_temporary_text_ = GetText(); | |
| 194 } | |
| 195 | |
| 196 UpdateAndStyleText(display_text, display_text.size()); | |
| 197 } | |
| 198 | |
| 199 bool AutocompleteEditViewMac::OnInlineAutocompleteTextMaybeChanged( | |
| 200 const std::wstring& display_text, size_t user_text_length) { | |
| 201 // TODO(shess): Make sure that this actually works. The round trip | |
| 202 // to native form and back may mean that it's the same but not the | |
| 203 // same. | |
| 204 if (display_text == GetText()) { | |
| 205 return false; | |
| 206 } | |
| 207 | |
| 208 UpdateAndStyleText(display_text, user_text_length); | |
| 209 return true; | |
| 210 } | |
| 211 | |
| 212 void AutocompleteEditViewMac::OnRevertTemporaryText() { | |
| 213 UpdateAndStyleText(saved_temporary_text_, saved_temporary_text_.size()); | |
| 214 saved_temporary_text_.clear(); | |
| 215 } | |
| 216 | |
| 217 void AutocompleteEditViewMac::OnUpOrDownKeyPressed(int dir) { | |
| 218 model_->OnUpOrDownKeyPressed(dir); | |
| 219 } | |
| 220 void AutocompleteEditViewMac::OnEscapeKeyPressed() { | |
| 221 model_->OnEscapeKeyPressed(); | |
| 222 } | |
| 223 void AutocompleteEditViewMac::OnSetFocus(bool f) { | |
| 224 model_->OnSetFocus(f); | |
| 225 } | |
| 226 void AutocompleteEditViewMac::OnKillFocus() { | |
| 227 model_->OnKillFocus(); | |
| 228 } | |
| 229 void AutocompleteEditViewMac::AcceptInput( | |
| 230 WindowOpenDisposition disposition, bool for_drop) { | |
| 231 model_->AcceptInput(disposition, for_drop); | |
| 232 } | |
| 233 void AutocompleteEditViewMac::OnAfterPossibleChange( | |
| 234 const std::wstring& new_text, | |
| 235 bool selection_differs, | |
| 236 bool text_differs, | |
| 237 bool just_deleted_text, | |
| 238 bool at_end_of_edit) { | |
| 239 model_->OnAfterPossibleChange(new_text, selection_differs, text_differs, | |
| 240 just_deleted_text, at_end_of_edit); | |
| 241 } | |
| 242 void AutocompleteEditViewMac::SetField(NSTextField* field) { | |
| 243 field_ = field; | |
| 244 [field_ setDelegate:edit_helper_]; | |
| 245 | |
| 246 // The popup code needs the field for sizing and placement. | |
| 247 popup_view_->SetField(field_); | |
| 248 } | |
| 249 | |
| 250 void AutocompleteEditViewMac::FocusLocation() { | |
| 251 [[field_ window] makeFirstResponder:field_]; | |
| 252 } | |
| 253 | |
| 254 @implementation AutocompleteFieldDelegate | |
| 255 | |
| 256 - initWithEditView:(AutocompleteEditViewMac*)view { | |
| 257 self = [super init]; | |
| 258 if (self) { | |
| 259 edit_view_ = view; | |
| 260 } | |
| 261 return self; | |
| 262 } | |
| 263 | |
| 264 - (BOOL)control:(NSControl*)control | |
| 265 textView:(NSTextView*)textView doCommandBySelector:(SEL)cmd { | |
| 266 if (cmd == @selector(moveDown:)) { | |
| 267 edit_view_->OnUpOrDownKeyPressed(1); | |
| 268 return YES; | |
| 269 } | |
| 270 | |
| 271 if (cmd == @selector(moveUp:)) { | |
| 272 edit_view_->OnUpOrDownKeyPressed(-1); | |
| 273 return YES; | |
| 274 } | |
| 275 | |
| 276 if (cmd == @selector(cancelOperation:)) { | |
| 277 edit_view_->OnEscapeKeyPressed(); | |
| 278 return YES; | |
| 279 } | |
| 280 | |
| 281 if (cmd == @selector(insertNewline:)) { | |
| 282 edit_view_->AcceptInput(CURRENT_TAB, false); | |
| 283 return YES; | |
| 284 } | |
| 285 | |
| 286 return NO; | |
| 287 } | |
| 288 | |
| 289 - (void)controlTextDidBeginEditing:(NSNotification*)aNotification { | |
| 290 edit_view_->OnSetFocus(false); | |
| 291 } | |
| 292 | |
| 293 - (void)controlTextDidChange:(NSNotification*)aNotification { | |
| 294 // TODO(shess): Make this more efficient? Or not. For now, just | |
| 295 // pass in the current text, indicating that the text and | |
| 296 // selection differ, ignoring deletions, and assuming that we're | |
| 297 // at the end of the text. | |
| 298 edit_view_->OnAfterPossibleChange(edit_view_->GetText(), | |
| 299 true, true, false, true); | |
| 300 } | |
| 301 | |
| 302 - (void)controlTextDidEndEditing:(NSNotification*)aNotification { | |
| 303 edit_view_->OnKillFocus(); | |
| 304 | |
| 305 // TODO(shess): Figure out where the selection belongs. On GTK, | |
| 306 // it's set to the start of the text. | |
| 307 } | |
| 308 | |
| 309 @end | |
| OLD | NEW |