Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" | 5 #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" |
| 6 | 6 |
| 7 #include "base/sys_string_conversions.h" | 7 #include "base/sys_string_conversions.h" |
| 8 #include "chrome/browser/autocomplete/autocomplete_edit.h" | 8 #include "chrome/browser/autocomplete/autocomplete_edit.h" |
| 9 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" | 9 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" |
| 10 #include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" | 10 #include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" |
| 11 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 12 | |
| 13 namespace { | |
| 14 | |
| 15 // Store's the model and view state across tab switches. | |
| 16 struct AutocompleteEditViewMacState { | |
| 17 AutocompleteEditViewMacState(const AutocompleteEditModel::State model_state, | |
| 18 const bool has_focus, const NSRange& selection) | |
| 19 : model_state(model_state), | |
| 20 has_focus(has_focus), | |
| 21 selection(selection) { | |
| 22 } | |
| 23 | |
| 24 const AutocompleteEditModel::State model_state; | |
| 25 const bool has_focus; | |
| 26 const NSRange selection; | |
| 27 }; | |
| 28 | |
| 29 // Returns a lazily initialized property bag accessor for saving our | |
| 30 // state in a TabContents. | |
| 31 PropertyAccessor<AutocompleteEditViewMacState>* GetStateAccessor() { | |
| 32 static PropertyAccessor<AutocompleteEditViewMacState> state; | |
|
pink (ping after 24hrs)
2009/05/08 21:18:26
how does this work with multiple windows if there'
Scott Hess - ex-Googler
2009/05/08 22:50:21
Oh, I hope I don't hurt myself, here ... brief sum
pink (ping after 24hrs)
2009/05/11 13:50:11
I figured it was something like this. Maybe a comm
| |
| 33 return &state; | |
| 34 } | |
| 35 | |
| 36 // Accessors for storing and getting the state from the tab. | |
| 37 void StoreStateToTab(TabContents* tab, | |
| 38 const AutocompleteEditViewMacState& state) { | |
| 39 GetStateAccessor()->SetProperty(tab->property_bag(), state); | |
| 40 } | |
| 41 const AutocompleteEditViewMacState* GetStateFromTab(const TabContents* tab) { | |
| 42 return GetStateAccessor()->GetProperty(tab->property_bag()); | |
| 43 } | |
| 44 | |
| 45 } // namespace | |
| 11 | 46 |
| 12 // Thin Obj-C bridge class that is the delegate of the omnibox field. | 47 // Thin Obj-C bridge class that is the delegate of the omnibox field. |
| 13 // It intercepts various control delegate methods and vectors them to | 48 // It intercepts various control delegate methods and vectors them to |
| 14 // the edit view. | 49 // the edit view. |
| 15 | 50 |
| 16 @interface AutocompleteFieldDelegate : NSObject { | 51 @interface AutocompleteFieldDelegate : NSObject { |
| 17 @private | 52 @private |
| 18 AutocompleteEditViewMac* edit_view_; // weak, owns us. | 53 AutocompleteEditViewMac* edit_view_; // weak, owns us. |
| 19 } | 54 } |
| 20 - initWithEditView:(AutocompleteEditViewMac*)view; | 55 - initWithEditView:(AutocompleteEditViewMac*)view; |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 50 // back in the destructor. Likewise for destroying the model before | 85 // back in the destructor. Likewise for destroying the model before |
| 51 // this object. | 86 // this object. |
| 52 popup_view_.reset(); | 87 popup_view_.reset(); |
| 53 model_.reset(); | 88 model_.reset(); |
| 54 | 89 |
| 55 // Disconnect field_ from edit_helper_ so that we don't get calls | 90 // Disconnect field_ from edit_helper_ so that we don't get calls |
| 56 // after destruction. | 91 // after destruction. |
| 57 [field_ setDelegate:nil]; | 92 [field_ setDelegate:nil]; |
| 58 } | 93 } |
| 59 | 94 |
| 60 // TODO(shess): This is the minimal change which seems to unblock | |
| 61 // getting the minimal Omnibox code checked in without making the | |
| 62 // world worse. Browser::TabSelectedAt() calls this when the tab | |
| 63 // changes, but that is only wired up for Windows. I do not yet | |
| 64 // understand that code well enough to go for it. Once wired up, then | |
| 65 // code can be removed at: | |
| 66 // [TabContentsController defocusLocationBar] | |
| 67 // [TabStripController selectTabWithContents:...] | |
| 68 void AutocompleteEditViewMac::SaveStateToTab(TabContents* tab) { | 95 void AutocompleteEditViewMac::SaveStateToTab(TabContents* tab) { |
| 69 // TODO(shess): Actually save the state to the tab area. | 96 DCHECK(tab); |
| 70 | 97 |
| 71 // Drop the popup before we change to another tab. | 98 NSRange range; |
| 72 ClosePopup(); | 99 if (model_->has_focus()) { |
| 100 range = GetSelectedRange(); | |
| 101 } else { | |
| 102 // If we are not focussed, there is no selection. Manufacture | |
| 103 // something reasonable in case it starts to matter in the future. | |
| 104 range = NSMakeRange(0, [[field_ stringValue] length]); | |
| 105 } | |
| 106 | |
| 107 AutocompleteEditViewMacState state(model_->GetStateForTabSwitch(), | |
| 108 model_->has_focus(), range); | |
| 109 StoreStateToTab(tab, state); | |
| 110 } | |
| 111 | |
| 112 void AutocompleteEditViewMac::Update( | |
| 113 const TabContents* tab_for_state_restoring) { | |
| 114 // TODO(shess): It seems like if the tab is non-NULL, then this code | |
| 115 // shouldn't need to be called at all. When coded that way, I find | |
| 116 // that the field isn't always updated correctly. Figure out why | |
| 117 // this is. Maybe this method should be refactored into more | |
| 118 // specific cases. | |
| 119 const std::wstring text = toolbar_model_->GetText(); | |
| 120 const bool user_visible = model_->UpdatePermanentText(text); | |
| 121 | |
| 122 if (tab_for_state_restoring) { | |
| 123 RevertAll(); | |
| 124 | |
| 125 const AutocompleteEditViewMacState* state = | |
| 126 GetStateFromTab(tab_for_state_restoring); | |
| 127 if (state) { | |
| 128 // Should restore the user's text via SetUserText(). | |
| 129 model_->RestoreState(state->model_state); | |
| 130 | |
| 131 // Restore user's selection. | |
| 132 // TODO(shess): The model_ does not restore the focus state. If | |
| 133 // field_ was in focus when we switched away, I presume it | |
| 134 // should be in focus when we switch back. Figure out if model_ | |
| 135 // not restoring focus is an oversight, or intentional for some | |
| 136 // subtle reason. | |
| 137 if (state->has_focus) { | |
| 138 FocusLocation(); | |
| 139 DCHECK([field_ currentEditor]); | |
| 140 [[field_ currentEditor] setSelectedRange:state->selection]; | |
| 141 } | |
| 142 } | |
| 143 } else if (user_visible) { | |
| 144 // Restore everything to the baseline look. | |
| 145 RevertAll(); | |
| 146 // TODO(shess): Figure out how this case is used, to make sure | |
| 147 // we're getting the selection and popup right. | |
| 148 | |
| 149 } else { | |
| 150 // TODO(shess): Figure out how this case is used, to make sure | |
| 151 // we're getting the selection and popup right. | |
| 152 // UpdateAndStyleText() approximates the inner part of Revertall() | |
| 153 // which under GTK is called EmphasizeURLComponents(), and is | |
| 154 // expected to change when I start feeding in the styling code. | |
| 155 UpdateAndStyleText(text, 0); | |
| 156 } | |
| 73 } | 157 } |
| 74 | 158 |
| 75 void AutocompleteEditViewMac::OpenURL(const GURL& url, | 159 void AutocompleteEditViewMac::OpenURL(const GURL& url, |
| 76 WindowOpenDisposition disposition, | 160 WindowOpenDisposition disposition, |
| 77 PageTransition::Type transition, | 161 PageTransition::Type transition, |
| 78 const GURL& alternate_nav_url, | 162 const GURL& alternate_nav_url, |
| 79 size_t selected_line, | 163 size_t selected_line, |
| 80 const std::wstring& keyword) { | 164 const std::wstring& keyword) { |
| 81 // TODO(shess): Why is the caller passing an invalid url in the | 165 // TODO(shess): Why is the caller passing an invalid url in the |
| 82 // first place? Make sure that case isn't being dropped on the | 166 // first place? Make sure that case isn't being dropped on the |
| 83 // floor. | 167 // floor. |
| 84 if (!url.is_valid()) { | 168 if (!url.is_valid()) { |
| 85 return; | 169 return; |
| 86 } | 170 } |
| 87 | 171 |
| 88 model_->SendOpenNotification(selected_line, keyword); | 172 model_->SendOpenNotification(selected_line, keyword); |
| 89 | 173 |
| 90 if (disposition != NEW_BACKGROUND_TAB) | 174 if (disposition != NEW_BACKGROUND_TAB) |
| 91 RevertAll(); // Revert the box to its unedited state. | 175 RevertAll(); // Revert the box to its unedited state. |
| 92 controller_->OnAutocompleteAccept(url, disposition, transition, | 176 controller_->OnAutocompleteAccept(url, disposition, transition, |
| 93 alternate_nav_url); | 177 alternate_nav_url); |
| 94 } | 178 } |
| 95 | 179 |
| 96 std::wstring AutocompleteEditViewMac::GetText() const { | 180 std::wstring AutocompleteEditViewMac::GetText() const { |
| 97 return base::SysNSStringToWide([field_ stringValue]); | 181 return base::SysNSStringToWide([field_ stringValue]); |
| 98 } | 182 } |
| 99 | 183 |
| 184 void AutocompleteEditViewMac::SetUserText(const std::wstring& text, | |
| 185 const std::wstring& display_text, | |
| 186 bool update_popup) { | |
| 187 model_->SetUserText(text); | |
| 188 UpdateAndStyleText(display_text, display_text.size()); | |
| 189 if (update_popup) { | |
| 190 UpdatePopup(); | |
| 191 } | |
| 192 } | |
| 193 | |
| 100 NSRange AutocompleteEditViewMac::GetSelectedRange() const { | 194 NSRange AutocompleteEditViewMac::GetSelectedRange() const { |
| 101 DCHECK([field_ currentEditor]); | 195 DCHECK([field_ currentEditor]); |
| 102 return [[field_ currentEditor] selectedRange]; | 196 return [[field_ currentEditor] selectedRange]; |
| 103 } | 197 } |
| 104 | 198 |
| 105 void AutocompleteEditViewMac::SetWindowTextAndCaretPos(const std::wstring& text, | 199 void AutocompleteEditViewMac::SetWindowTextAndCaretPos(const std::wstring& text, |
| 106 size_t caret_pos) { | 200 size_t caret_pos) { |
| 107 UpdateAndStyleText(text, text.size()); | 201 UpdateAndStyleText(text, text.size()); |
| 108 } | 202 } |
| 109 | 203 |
| 110 void AutocompleteEditViewMac::SelectAll(bool reversed) { | 204 void AutocompleteEditViewMac::SelectAll(bool reversed) { |
| 111 // TODO(shess): Figure out what reversed implies. The gtk version | 205 // TODO(shess): Figure out what reversed implies. The gtk version |
| 112 // has it imply inverting the selection front to back, but I don't | 206 // has it imply inverting the selection front to back, but I don't |
| 113 // even know if that makes sense for Mac. | 207 // even know if that makes sense for Mac. |
| 114 UpdateAndStyleText(GetText(), 0); | 208 UpdateAndStyleText(GetText(), 0); |
| 115 } | 209 } |
| 116 | 210 |
| 117 void AutocompleteEditViewMac::RevertAll() { | 211 void AutocompleteEditViewMac::RevertAll() { |
| 118 ClosePopup(); | 212 ClosePopup(); |
| 119 model_->Revert(); | 213 model_->Revert(); |
| 120 | 214 |
| 121 std::wstring tt = GetText(); | 215 std::wstring tt = GetText(); |
| 122 UpdateAndStyleText(tt, tt.size()); | 216 UpdateAndStyleText(tt, 0); |
| 123 controller_->OnChanged(); | 217 controller_->OnChanged(); |
| 124 } | 218 } |
| 125 | 219 |
| 126 void AutocompleteEditViewMac::UpdatePopup() { | 220 void AutocompleteEditViewMac::UpdatePopup() { |
| 127 model_->SetInputInProgress(true); | 221 model_->SetInputInProgress(true); |
| 128 if (!model_->has_focus()) | 222 if (!model_->has_focus()) |
| 129 return; | 223 return; |
| 130 | 224 |
| 131 // TODO(shess): | 225 // TODO(shess): |
| 132 // Shouldn't inline autocomplete when the caret/selection isn't at | 226 // Shouldn't inline autocomplete when the caret/selection isn't at |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 149 | 243 |
| 150 url_parse::Parsed parts; | 244 url_parse::Parsed parts; |
| 151 AutocompleteInput::Parse(display_text, model_->GetDesiredTLD(), | 245 AutocompleteInput::Parse(display_text, model_->GetDesiredTLD(), |
| 152 &parts, NULL); | 246 &parts, NULL); |
| 153 bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0); | 247 bool emphasize = model_->CurrentTextIsURL() && (parts.host.len > 0); |
| 154 if (emphasize) { | 248 if (emphasize) { |
| 155 // TODO(shess): Pull color out as a constant. | 249 // TODO(shess): Pull color out as a constant. |
| 156 [as addAttribute:NSForegroundColorAttributeName | 250 [as addAttribute:NSForegroundColorAttributeName |
| 157 value:[NSColor greenColor] | 251 value:[NSColor greenColor] |
| 158 range:NSMakeRange((NSInteger)parts.host.begin, | 252 range:NSMakeRange((NSInteger)parts.host.begin, |
| 159 (NSInteger)parts.host.end())]; | 253 (NSInteger)parts.host.len)]; |
| 160 } | 254 } |
| 161 | 255 |
| 162 // TODO(shess): GTK has this as a member var, figure out why. | 256 // TODO(shess): GTK has this as a member var, figure out why. |
| 163 ToolbarModel::SecurityLevel scheme_security_level = | 257 ToolbarModel::SecurityLevel scheme_security_level = |
| 164 toolbar_model_->GetSchemeSecurityLevel(); | 258 toolbar_model_->GetSchemeSecurityLevel(); |
| 165 | 259 |
| 166 // Emphasize the scheme for security UI display purposes (if necessary). | 260 // Emphasize the scheme for security UI display purposes (if necessary). |
| 167 if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() && | 261 if (!model_->user_input_in_progress() && parts.scheme.is_nonempty() && |
| 168 (scheme_security_level != ToolbarModel::NORMAL)) { | 262 (scheme_security_level != ToolbarModel::NORMAL)) { |
| 169 // TODO(shess): Pull colors out as constants. | 263 // TODO(shess): Pull colors out as constants. |
| 170 NSColor* color; | 264 NSColor* color; |
| 171 if (scheme_security_level == ToolbarModel::SECURE) { | 265 if (scheme_security_level == ToolbarModel::SECURE) { |
| 172 color = [NSColor blueColor]; | 266 color = [NSColor blueColor]; |
| 173 } else { | 267 } else { |
| 174 color = [NSColor blackColor]; | 268 color = [NSColor blackColor]; |
| 175 } | 269 } |
| 176 [as addAttribute:NSForegroundColorAttributeName value:color | 270 [as addAttribute:NSForegroundColorAttributeName value:color |
| 177 range:NSMakeRange((NSInteger)parts.scheme.begin, | 271 range:NSMakeRange((NSInteger)parts.scheme.begin, |
| 178 (NSInteger)parts.scheme.end())]; | 272 (NSInteger)parts.scheme.len)]; |
| 179 } | 273 } |
| 180 | 274 |
| 181 // TODO(shess): Check that this updates the model's sense of focus | 275 // TODO(shess): Check that this updates the model's sense of focus |
| 182 // correctly. | 276 // correctly. |
| 183 [field_ setObjectValue:as]; | 277 [field_ setObjectValue:as]; |
| 184 if (![field_ currentEditor]) { | 278 if (![field_ currentEditor]) { |
| 185 [field_ becomeFirstResponder]; | 279 [field_ becomeFirstResponder]; |
| 186 DCHECK_EQ(field_, [[field_ window] firstResponder]); | 280 DCHECK_EQ([field_ currentEditor], [[field_ window] firstResponder]); |
| 187 } | 281 } |
| 188 | 282 |
| 189 NSRange selected_range = NSMakeRange(user_text_length, [as length]); | 283 NSRange selected_range = NSMakeRange(user_text_length, [as length]); |
| 190 // TODO(shess): What if it didn't get first responder, and there is | 284 // TODO(shess): What if it didn't get first responder, and there is |
| 191 // no field editor? This will do nothing. Well, at least it won't | 285 // no field editor? This will do nothing. Well, at least it won't |
| 192 // crash. Think of something more productive to do, or prove that | 286 // crash. Think of something more productive to do, or prove that |
| 193 // it cannot occur and DCHECK appropriately. | 287 // it cannot occur and DCHECK appropriately. |
| 194 [[field_ currentEditor] setSelectedRange:selected_range]; | 288 [[field_ currentEditor] setSelectedRange:selected_range]; |
| 195 } | 289 } |
| 196 | 290 |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 274 } | 368 } |
| 275 void AutocompleteEditViewMac::OnKillFocus() { | 369 void AutocompleteEditViewMac::OnKillFocus() { |
| 276 model_->OnKillFocus(); | 370 model_->OnKillFocus(); |
| 277 } | 371 } |
| 278 void AutocompleteEditViewMac::AcceptInput( | 372 void AutocompleteEditViewMac::AcceptInput( |
| 279 WindowOpenDisposition disposition, bool for_drop) { | 373 WindowOpenDisposition disposition, bool for_drop) { |
| 280 model_->AcceptInput(disposition, for_drop); | 374 model_->AcceptInput(disposition, for_drop); |
| 281 } | 375 } |
| 282 | 376 |
| 283 void AutocompleteEditViewMac::FocusLocation() { | 377 void AutocompleteEditViewMac::FocusLocation() { |
| 284 [[field_ window] makeFirstResponder:field_]; | 378 // -makeFirstResponder: will select the entire field_. If we're |
| 379 // already firstResponder, it's likely that we want to retain the | |
| 380 // current selection. | |
| 381 if (![field_ currentEditor]) { | |
| 382 [[field_ window] makeFirstResponder:field_]; | |
| 383 } | |
| 285 } | 384 } |
| 286 | 385 |
| 287 @implementation AutocompleteFieldDelegate | 386 @implementation AutocompleteFieldDelegate |
| 288 | 387 |
| 289 - initWithEditView:(AutocompleteEditViewMac*)view { | 388 - initWithEditView:(AutocompleteEditViewMac*)view { |
| 290 self = [super init]; | 389 self = [super init]; |
| 291 if (self) { | 390 if (self) { |
| 292 edit_view_ = view; | 391 edit_view_ = view; |
| 293 } | 392 } |
| 294 return self; | 393 return self; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 349 } | 448 } |
| 350 | 449 |
| 351 - (void)controlTextDidEndEditing:(NSNotification*)aNotification { | 450 - (void)controlTextDidEndEditing:(NSNotification*)aNotification { |
| 352 edit_view_->OnKillFocus(); | 451 edit_view_->OnKillFocus(); |
| 353 | 452 |
| 354 // TODO(shess): Figure out where the selection belongs. On GTK, | 453 // TODO(shess): Figure out where the selection belongs. On GTK, |
| 355 // it's set to the start of the text. | 454 // it's set to the start of the text. |
| 356 } | 455 } |
| 357 | 456 |
| 358 @end | 457 @end |
| OLD | NEW |