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 |