1 // Copyright (c) 2011 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_win.h" 6 7 #include <algorithm> 8 #include <locale> 9 #include <string> 10 11 #include <richedit.h> 12 #include <textserv.h> 13 14 #include "app/win/iat_patch_function.h" 15 #include "base/auto_reset.h" 16 #include "base/basictypes.h" 17 #include "base/i18n/rtl.h" 18 #include "base/lazy_instance.h" 19 #include "base/memory/ref_counted.h" 20 #include "base/string_util.h" 21 #include "base/utf_string_conversions.h" 22 #include "chrome/app/chrome_command_ids.h" 23 #include "chrome/browser/autocomplete/autocomplete_accessibility.h" 24 #include "chrome/browser/autocomplete/autocomplete_match.h" 25 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" 26 #include "chrome/browser/autocomplete/keyword_provider.h" 27 #include "chrome/browser/browser_process.h" 28 #include "chrome/browser/command_updater.h" 29 #include "chrome/browser/metrics/user_metrics.h" 30 #include "chrome/browser/net/url_fixer_upper.h" 31 #include "chrome/browser/profiles/profile.h" 32 #include "chrome/browser/search_engines/template_url.h" 33 #include "chrome/browser/search_engines/template_url_model.h" 34 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" 35 #include "content/browser/tab_contents/tab_contents.h" 36 #include "content/common/notification_service.h" 37 #include "googleurl/src/url_util.h" 38 #include "grit/generated_resources.h" 39 #include "net/base/escape.h" 40 #include "skia/ext/skia_utils_win.h" 41 #include "ui/base/clipboard/clipboard.h" 42 #include "ui/base/clipboard/scoped_clipboard_writer.h" 43 #include "ui/base/dragdrop/drag_drop_types.h" 44 #include "ui/base/dragdrop/drag_source.h" 45 #include "ui/base/dragdrop/drop_target.h" 46 #include "ui/base/dragdrop/os_exchange_data.h" 47 #include "ui/base/dragdrop/os_exchange_data_provider_win.h" 48 #include "ui/base/keycodes/keyboard_codes.h" 49 #include "ui/base/l10n/l10n_util.h" 50 #include "ui/base/l10n/l10n_util_win.h" 51 #include "ui/gfx/canvas.h" 52 #include "ui/gfx/canvas_skia.h" 53 #include "views/controls/textfield/native_textfield_win.h" 54 #include "views/drag_utils.h" 55 #include "views/events/event_utils_win.h" 56 #include "views/focus/focus_util_win.h" 57 #include "views/widget/widget.h" 58 59 #pragma comment(lib, "oleacc.lib") // Needed for accessibility support. 60 #pragma comment(lib, "riched20.lib") // Needed for the richedit control. 61 62 /////////////////////////////////////////////////////////////////////////////// 63 // AutocompleteEditModel 64 65 namespace { 66 67 // A helper method for determining a valid DROPEFFECT given the allowed 68 // DROPEFFECTS. We prefer copy over link. 69 DWORD CopyOrLinkDropEffect(DWORD effect) { 70 if (effect & DROPEFFECT_COPY) 71 return DROPEFFECT_COPY; 72 if (effect & DROPEFFECT_LINK) 73 return DROPEFFECT_LINK; 74 return DROPEFFECT_NONE; 75 } 76 77 // A helper method for determining a valid drag operation given the allowed 78 // operation. We prefer copy over link. 79 int CopyOrLinkDragOperation(int drag_operation) { 80 if (drag_operation & ui::DragDropTypes::DRAG_COPY) 81 return ui::DragDropTypes::DRAG_COPY; 82 if (drag_operation & ui::DragDropTypes::DRAG_LINK) 83 return ui::DragDropTypes::DRAG_LINK; 84 return ui::DragDropTypes::DRAG_NONE; 85 } 86 87 // The AutocompleteEditState struct contains enough information about the 88 // AutocompleteEditModel and AutocompleteEditViewWin to save/restore a user's 89 // typing, caret position, etc. across tab changes. We explicitly don't 90 // preserve things like whether the popup was open as this might be weird. 91 struct AutocompleteEditState { 92 AutocompleteEditState(const AutocompleteEditModel::State& model_state, 93 const AutocompleteEditViewWin::State& view_state) 94 : model_state(model_state), 95 view_state(view_state) { 96 } 97 98 const AutocompleteEditModel::State model_state; 99 const AutocompleteEditViewWin::State view_state; 100 }; 101 102 // Returns true if the current point is far enough from the origin that it 103 // would be considered a drag. 104 bool IsDrag(const POINT& origin, const POINT& current) { 105 return views::View::ExceededDragThreshold(current.x - origin.x, 106 current.y - origin.y); 107 } 108 109 } // namespace 110 111 // EditDropTarget is the IDropTarget implementation installed on 112 // AutocompleteEditViewWin. EditDropTarget prefers URL over plain text. A drop 113 // of a URL replaces all the text of the edit and navigates immediately to the 114 // URL. A drop of plain text from the same edit either copies or moves the 115 // selected text, and a drop of plain text from a source other than the edit 116 // does a paste and go. 117 class AutocompleteEditViewWin::EditDropTarget : public ui::DropTarget { 118 public: 119 explicit EditDropTarget(AutocompleteEditViewWin* edit); 120 121 protected: 122 virtual DWORD OnDragEnter(IDataObject* data_object, 123 DWORD key_state, 124 POINT cursor_position, 125 DWORD effect); 126 virtual DWORD OnDragOver(IDataObject* data_object, 127 DWORD key_state, 128 POINT cursor_position, 129 DWORD effect); 130 virtual void OnDragLeave(IDataObject* data_object); 131 virtual DWORD OnDrop(IDataObject* data_object, 132 DWORD key_state, 133 POINT cursor_position, 134 DWORD effect); 135 136 private: 137 // If dragging a string, the drop highlight position of the edit is reset 138 // based on the mouse position. 139 void UpdateDropHighlightPosition(const POINT& cursor_screen_position); 140 141 // Resets the visual drop indicates we install on the edit. 142 void ResetDropHighlights(); 143 144 // The edit we're the drop target for. 145 AutocompleteEditViewWin* edit_; 146 147 // If true, the drag session contains a URL. 148 bool drag_has_url_; 149 150 // If true, the drag session contains a string. If drag_has_url_ is true, 151 // this is false regardless of whether the clipboard has a string. 152 bool drag_has_string_; 153 154 DISALLOW_COPY_AND_ASSIGN(EditDropTarget); 155 }; 156 157 AutocompleteEditViewWin::EditDropTarget::EditDropTarget( 158 AutocompleteEditViewWin* edit) 159 : ui::DropTarget(edit->m_hWnd), 160 edit_(edit), 161 drag_has_url_(false), 162 drag_has_string_(false) { 163 } 164 165 DWORD AutocompleteEditViewWin::EditDropTarget::OnDragEnter( 166 IDataObject* data_object, 167 DWORD key_state, 168 POINT cursor_position, 169 DWORD effect) { 170 ui::OSExchangeData os_data(new ui::OSExchangeDataProviderWin(data_object)); 171 drag_has_url_ = os_data.HasURL(); 172 drag_has_string_ = !drag_has_url_ && os_data.HasString(); 173 if (drag_has_url_) { 174 if (edit_->in_drag()) { 175 // The edit we're associated with originated the drag. No point in 176 // allowing the user to drop back on us. 177 drag_has_url_ = false; 178 } 179 // NOTE: it would be nice to visually show all the text is going to 180 // be replaced by selecting all, but this caused painting problems. In 181 // particular the flashing caret would appear outside the edit! For now 182 // we stick with no visual indicator other than that shown own the mouse 183 // cursor. 184 } 185 return OnDragOver(data_object, key_state, cursor_position, effect); 186 } 187 188 DWORD AutocompleteEditViewWin::EditDropTarget::OnDragOver( 189 IDataObject* data_object, 190 DWORD key_state, 191 POINT cursor_position, 192 DWORD effect) { 193 if (drag_has_url_) 194 return CopyOrLinkDropEffect(effect); 195 196 if (drag_has_string_) { 197 UpdateDropHighlightPosition(cursor_position); 198 if (edit_->drop_highlight_position() == -1 && edit_->in_drag()) 199 return DROPEFFECT_NONE; 200 if (edit_->in_drag()) { 201 // The edit we're associated with originated the drag. Do the normal drag 202 // behavior. 203 DCHECK((effect & DROPEFFECT_COPY) && (effect & DROPEFFECT_MOVE)); 204 return (key_state & MK_CONTROL) ? DROPEFFECT_COPY : DROPEFFECT_MOVE; 205 } 206 // Our edit didn't originate the drag, only allow link or copy. 207 return CopyOrLinkDropEffect(effect); 208 } 209 210 return DROPEFFECT_NONE; 211 } 212 213 void AutocompleteEditViewWin::EditDropTarget::OnDragLeave( 214 IDataObject* data_object) { 215 ResetDropHighlights(); 216 } 217 218 DWORD AutocompleteEditViewWin::EditDropTarget::OnDrop( 219 IDataObject* data_object, 220 DWORD key_state, 221 POINT cursor_position, 222 DWORD effect) { 223 effect = OnDragOver(data_object, key_state, cursor_position, effect); 224 225 ui::OSExchangeData os_data(new ui::OSExchangeDataProviderWin(data_object)); 226 views::DropTargetEvent event(os_data, cursor_position.x, cursor_position.y, 227 ui::DragDropTypes::DropEffectToDragOperation(effect)); 228 229 int drag_operation = edit_->OnPerformDropImpl(event, edit_->in_drag()); 230 231 if (!drag_has_url_) 232 ResetDropHighlights(); 233 234 return ui::DragDropTypes::DragOperationToDropEffect(drag_operation); 235 } 236 237 void AutocompleteEditViewWin::EditDropTarget::UpdateDropHighlightPosition( 238 const POINT& cursor_screen_position) { 239 if (drag_has_string_) { 240 POINT client_position = cursor_screen_position; 241 ::ScreenToClient(edit_->m_hWnd, &client_position); 242 int drop_position = edit_->CharFromPos(client_position); 243 if (edit_->in_drag()) { 244 // Our edit originated the drag, don't allow a drop if over the selected 245 // region. 246 LONG sel_start, sel_end; 247 edit_->GetSel(sel_start, sel_end); 248 if ((sel_start != sel_end) && (drop_position >= sel_start) && 249 (drop_position <= sel_end)) 250 drop_position = -1; 251 } else { 252 // A drop from a source other than the edit replaces all the text, so 253 // we don't show the drop location. See comment in OnDragEnter as to why 254 // we don't try and select all here. 255 drop_position = -1; 256 } 257 edit_->SetDropHighlightPosition(drop_position); 258 } 259 } 260 261 void AutocompleteEditViewWin::EditDropTarget::ResetDropHighlights() { 262 if (drag_has_string_) 263 edit_->SetDropHighlightPosition(-1); 264 } 265 266 267 /////////////////////////////////////////////////////////////////////////////// 268 // Helper classes 269 270 AutocompleteEditViewWin::ScopedFreeze::ScopedFreeze( 271 AutocompleteEditViewWin* edit, 272 ITextDocument* text_object_model) 273 : edit_(edit), 274 text_object_model_(text_object_model) { 275 // Freeze the screen. 276 if (text_object_model_) { 277 long count; 278 text_object_model_->Freeze(&count); 279 } 280 } 281 282 AutocompleteEditViewWin::ScopedFreeze::~ScopedFreeze() { 283 // Unfreeze the screen. 284 // NOTE: If this destructor is reached while the edit is being destroyed (for 285 // example, because we double-clicked the edit of a popup and caused it to 286 // transform to an unconstrained window), it will no longer have an HWND, and 287 // text_object_model_ may point to a destroyed object, so do nothing here. 288 if (edit_->IsWindow() && text_object_model_) { 289 long count; 290 text_object_model_->Unfreeze(&count); 291 if (count == 0) { 292 // We need to UpdateWindow() here in addition to InvalidateRect() because, 293 // as far as I can tell, the edit likes to synchronously erase its 294 // background when unfreezing, thus requiring us to synchronously redraw 295 // if we don't want flicker. 296 edit_->InvalidateRect(NULL, false); 297 edit_->UpdateWindow(); 298 } 299 } 300 } 301 302 AutocompleteEditViewWin::ScopedSuspendUndo::ScopedSuspendUndo( 303 ITextDocument* text_object_model) 304 : text_object_model_(text_object_model) { 305 // Suspend Undo processing. 306 if (text_object_model_) 307 text_object_model_->Undo(tomSuspend, NULL); 308 } 309 310 AutocompleteEditViewWin::ScopedSuspendUndo::~ScopedSuspendUndo() { 311 // Resume Undo processing. 312 if (text_object_model_) 313 text_object_model_->Undo(tomResume, NULL); 314 } 315 316 /////////////////////////////////////////////////////////////////////////////// 317 // AutocompleteEditViewWin 318 319 namespace { 320 321 // These are used to hook the CRichEditCtrl's calls to BeginPaint() and 322 // EndPaint() and provide a memory DC instead. See OnPaint(). 323 HWND edit_hwnd = NULL; 324 PAINTSTRUCT paint_struct; 325 326 // Intercepted method for BeginPaint(). Must use __stdcall convention. 327 HDC WINAPI BeginPaintIntercept(HWND hWnd, LPPAINTSTRUCT lpPaint) { 328 if (!edit_hwnd || (hWnd != edit_hwnd)) 329 return ::BeginPaint(hWnd, lpPaint); 330 331 *lpPaint = paint_struct; 332 return paint_struct.hdc; 333 } 334 335 // Intercepted method for EndPaint(). Must use __stdcall convention. 336 BOOL WINAPI EndPaintIntercept(HWND hWnd, const PAINTSTRUCT* lpPaint) { 337 return (edit_hwnd && (hWnd == edit_hwnd)) || ::EndPaint(hWnd, lpPaint); 338 } 339 340 // Returns a lazily initialized property bag accessor for saving our state in a 341 // TabContents. 342 PropertyAccessor<AutocompleteEditState>* GetStateAccessor() { 343 static PropertyAccessor<AutocompleteEditState> state; 344 return &state; 345 } 346 347 class PaintPatcher { 348 public: 349 PaintPatcher(); 350 ~PaintPatcher(); 351 352 void RefPatch(); 353 void DerefPatch(); 354 355 private: 356 size_t refcount_; 357 app::win::IATPatchFunction begin_paint_; 358 app::win::IATPatchFunction end_paint_; 359 360 DISALLOW_COPY_AND_ASSIGN(PaintPatcher); 361 }; 362 363 PaintPatcher::PaintPatcher() : refcount_(0) { 364 } 365 366 PaintPatcher::~PaintPatcher() { 367 DCHECK_EQ(0U, refcount_); 368 } 369 370 void PaintPatcher::RefPatch() { 371 if (refcount_ == 0) { 372 DCHECK(!begin_paint_.is_patched()); 373 DCHECK(!end_paint_.is_patched()); 374 begin_paint_.Patch(L"riched20.dll", "user32.dll", "BeginPaint", 375 &BeginPaintIntercept); 376 end_paint_.Patch(L"riched20.dll", "user32.dll", "EndPaint", 377 &EndPaintIntercept); 378 } 379 ++refcount_; 380 } 381 382 void PaintPatcher::DerefPatch() { 383 DCHECK(begin_paint_.is_patched()); 384 DCHECK(end_paint_.is_patched()); 385 --refcount_; 386 if (refcount_ == 0) { 387 begin_paint_.Unpatch(); 388 end_paint_.Unpatch(); 389 } 390 } 391 392 base::LazyInstance<PaintPatcher> g_paint_patcher(base::LINKER_INITIALIZED); 393 394 // twips are a unit of type measurement, and RichEdit controls use them 395 // to set offsets. 396 const int kTwipsPerInch = 1440; 397 398 } // namespace 399 400 AutocompleteEditViewWin::AutocompleteEditViewWin( 401 const gfx::Font& font, 402 AutocompleteEditController* controller, 403 ToolbarModel* toolbar_model, 404 LocationBarView* parent_view, 405 HWND hwnd, 406 Profile* profile, 407 CommandUpdater* command_updater, 408 bool popup_window_mode, 409 const views::View* location_bar) 410 : model_(new AutocompleteEditModel(this, controller, profile)), 411 popup_view_(new AutocompletePopupContentsView(font, this, model_.get(), 412 profile, location_bar)), 413 controller_(controller), 414 parent_view_(parent_view), 415 toolbar_model_(toolbar_model), 416 command_updater_(command_updater), 417 popup_window_mode_(popup_window_mode), 418 force_hidden_(false), 419 tracking_click_(), 420 tracking_double_click_(false), 421 double_click_time_(0), 422 can_discard_mousemove_(false), 423 ignore_ime_messages_(false), 424 delete_at_end_pressed_(false), 425 font_(font), 426 possible_drag_(false), 427 in_drag_(false), 428 initiated_drag_(false), 429 drop_highlight_position_(-1), 430 background_color_(skia::SkColorToCOLORREF(LocationBarView::GetColor( 431 ToolbarModel::NONE, LocationBarView::BACKGROUND))), 432 security_level_(ToolbarModel::NONE), 433 text_object_model_(NULL) { 434 // Dummy call to a function exported by riched20.dll to ensure it sets up an 435 // import dependency on the dll. 436 CreateTextServices(NULL, NULL, NULL); 437 438 saved_selection_for_focus_change_.cpMin = -1; 439 440 g_paint_patcher.Pointer()->RefPatch(); 441 442 Create(hwnd, 0, 0, 0, l10n_util::GetExtendedStyles()); 443 SetReadOnly(popup_window_mode_); 444 SetFont(font_.GetNativeFont()); 445 446 // NOTE: Do not use SetWordBreakProcEx() here, that is no longer supported as 447 // of Rich Edit 2.0 onward. 448 SendMessage(m_hWnd, EM_SETWORDBREAKPROC, 0, 449 reinterpret_cast<LPARAM>(&WordBreakProc)); 450 451 // Get the metrics for the font. 452 HDC dc = ::GetDC(NULL); 453 SelectObject(dc, font_.GetNativeFont()); 454 TEXTMETRIC tm = {0}; 455 GetTextMetrics(dc, &tm); 456 const float kXHeightRatio = 0.7f; // The ratio of a font's x-height to its 457 // cap height. Sadly, Windows doesn't 458 // provide a true value for a font's 459 // x-height in its text metrics, so we 460 // approximate. 461 font_x_height_ = static_cast<int>((static_cast<float>(font_.GetBaseline() - 462 tm.tmInternalLeading) * kXHeightRatio) + 0.5); 463 // The distance from the top of the field to the desired baseline of the 464 // rendered text. 465 const int kTextBaseline = popup_window_mode_ ? 15 : 18; 466 font_y_adjustment_ = kTextBaseline - font_.GetBaseline(); 467 468 // Get the number of twips per pixel, which we need below to offset our text 469 // by the desired number of pixels. 470 const long kTwipsPerPixel = kTwipsPerInch / GetDeviceCaps(dc, LOGPIXELSY); 471 ::ReleaseDC(NULL, dc); 472 473 // Set the default character style -- adjust to our desired baseline. 474 CHARFORMAT cf = {0}; 475 cf.dwMask = CFM_OFFSET; 476 cf.yOffset = -font_y_adjustment_ * kTwipsPerPixel; 477 SetDefaultCharFormat(cf); 478 479 SetBackgroundColor(background_color_); 480 481 // By default RichEdit has a drop target. Revoke it so that we can install our 482 // own. Revoke takes care of deleting the existing one. 483 RevokeDragDrop(m_hWnd); 484 485 // Register our drop target. RichEdit appears to invoke RevokeDropTarget when 486 // done so that we don't have to explicitly. 487 if (!popup_window_mode_) { 488 scoped_refptr<EditDropTarget> drop_target = new EditDropTarget(this); 489 RegisterDragDrop(m_hWnd, drop_target.get()); 490 } 491 } 492 493 AutocompleteEditViewWin::~AutocompleteEditViewWin() { 494 NotificationService::current()->Notify( 495 NotificationType::AUTOCOMPLETE_EDIT_DESTROYED, 496 Source<AutocompleteEditViewWin>(this), 497 NotificationService::NoDetails()); 498 499 // Explicitly release the text object model now that we're done with it, and 500 // before we free the library. If the library gets unloaded before this 501 // released, it becomes garbage. 502 text_object_model_->Release(); 503 504 // We balance our reference count and unpatch when the last instance has 505 // been destroyed. This prevents us from relying on the AtExit or static 506 // destructor sequence to do our unpatching, which is generally fragile. 507 g_paint_patcher.Pointer()->DerefPatch(); 508 } 509 510 views::View* AutocompleteEditViewWin::parent_view() const { 511 return parent_view_; 512 } 513 514 int AutocompleteEditViewWin::WidthOfTextAfterCursor() { 515 CHARRANGE selection; 516 GetSelection(selection); 517 const int start = std::max(0, static_cast<int>(selection.cpMax - 1)); 518 return WidthNeededToDisplay(GetText().substr(start)); 519 } 520 521 gfx::Font AutocompleteEditViewWin::GetFont() { 522 return font_; 523 } 524 525 void AutocompleteEditViewWin::SaveStateToTab(TabContents* tab) { 526 DCHECK(tab); 527 528 const AutocompleteEditModel::State model_state( 529 model_->GetStateForTabSwitch()); 530 531 CHARRANGE selection; 532 GetSelection(selection); 533 GetStateAccessor()->SetProperty(tab->property_bag(), 534 AutocompleteEditState( 535 model_state, 536 State(selection, saved_selection_for_focus_change_))); 537 } 538 539 void AutocompleteEditViewWin::Update( 540 const TabContents* tab_for_state_restoring) { 541 const bool visibly_changed_permanent_text = 542 model_->UpdatePermanentText(toolbar_model_->GetText()); 543 544 const ToolbarModel::SecurityLevel security_level = 545 toolbar_model_->GetSecurityLevel(); 546 const bool changed_security_level = (security_level != security_level_); 547 548 // Bail early when no visible state will actually change (prevents an 549 // unnecessary ScopedFreeze, and thus UpdateWindow()). 550 if (!changed_security_level && !visibly_changed_permanent_text && 551 !tab_for_state_restoring) 552 return; 553 554 // Update our local state as desired. We set security_level_ here so it will 555 // already be correct before we get to any RevertAll()s below and use it. 556 security_level_ = security_level; 557 558 // When we're switching to a new tab, restore its state, if any. 559 ScopedFreeze freeze(this, GetTextObjectModel()); 560 if (tab_for_state_restoring) { 561 // Make sure we reset our own state first. The new tab may not have any 562 // saved state, or it may not have had input in progress, in which case we 563 // won't overwrite all our local state. 564 RevertAll(); 565 566 const AutocompleteEditState* state = GetStateAccessor()->GetProperty( 567 tab_for_state_restoring->property_bag()); 568 if (state) { 569 model_->RestoreState(state->model_state); 570 571 // Restore user's selection. We do this after restoring the user_text 572 // above so we're selecting in the correct string. 573 SetSelectionRange(state->view_state.selection); 574 saved_selection_for_focus_change_ = 575 state->view_state.saved_selection_for_focus_change; 576 } 577 } else if (visibly_changed_permanent_text) { 578 // Not switching tabs, just updating the permanent text. (In the case where 579 // we _were_ switching tabs, the RevertAll() above already drew the new 580 // permanent text.) 581 582 // Tweak: if the edit was previously nonempty and had all the text selected, 583 // select all the new text. This makes one particular case better: the 584 // user clicks in the box to change it right before the permanent URL is 585 // changed. Since the new URL is still fully selected, the user's typing 586 // will replace the edit contents as they'd intended. 587 // 588 // NOTE: The selection can be longer than the text length if the edit is in 589 // in rich text mode and the user has selected the "phantom newline" at the 590 // end, so use ">=" instead of "==" to see if all the text is selected. In 591 // theory we prevent this case from ever occurring, but this is still safe. 592 CHARRANGE sel; 593 GetSelection(sel); 594 const bool was_reversed = (sel.cpMin > sel.cpMax); 595 const bool was_sel_all = (sel.cpMin != sel.cpMax) && 596 IsSelectAllForRange(sel); 597 598 RevertAll(); 599 600 if (was_sel_all) 601 SelectAll(was_reversed); 602 } else if (changed_security_level) { 603 // Only the security style changed, nothing else. Redraw our text using it. 604 EmphasizeURLComponents(); 605 } 606 } 607 608 void AutocompleteEditViewWin::OpenURL(const GURL& url, 609 WindowOpenDisposition disposition, 610 PageTransition::Type transition, 611 const GURL& alternate_nav_url, 612 size_t selected_line, 613 const string16& keyword) { 614 if (!url.is_valid()) 615 return; 616 617 // When we navigate, we first revert to the unedited state, then if necessary 618 // synchronously change the permanent text to the new URL. If we don't freeze 619 // here, the user could potentially see a flicker of the current URL before 620 // the new one reappears, which would look glitchy. 621 ScopedFreeze freeze(this, GetTextObjectModel()); 622 model_->OpenURL(url, disposition, transition, alternate_nav_url, 623 selected_line, keyword); 624 } 625 626 string16 AutocompleteEditViewWin::GetText() const { 627 const int len = GetTextLength() + 1; 628 string16 str; 629 GetWindowText(WriteInto(&str, len), len); 630 return str; 631 } 632 633 bool AutocompleteEditViewWin::IsEditingOrEmpty() const { 634 return model_->user_input_in_progress() || (GetTextLength() == 0); 635 } 636 637 int AutocompleteEditViewWin::GetIcon() const { 638 return IsEditingOrEmpty() ? 639 AutocompleteMatch::TypeToIcon(model_->CurrentTextType()) : 640 toolbar_model_->GetIcon(); 641 } 642 643 void AutocompleteEditViewWin::SetUserText(const string16& text) { 644 SetUserText(text, text, true); 645 } 646 647 void AutocompleteEditViewWin::SetUserText(const string16& text, 648 const string16& display_text, 649 bool update_popup) { 650 ScopedFreeze freeze(this, GetTextObjectModel()); 651 model_->SetUserText(text); 652 saved_selection_for_focus_change_.cpMin = -1; 653 SetWindowTextAndCaretPos(display_text, display_text.length()); 654 if (update_popup) 655 UpdatePopup(); 656 TextChanged(); 657 } 658 659 void AutocompleteEditViewWin::SetWindowTextAndCaretPos(const string16& text, 660 size_t caret_pos) { 661 SetWindowText(text.c_str()); 662 PlaceCaretAt(caret_pos); 663 } 664 665 void AutocompleteEditViewWin::SetForcedQuery() { 666 const string16 current_text(GetText()); 667 const size_t start = current_text.find_first_not_of(kWhitespaceWide); 668 if (start == string16::npos || (current_text[start] != '?')) 669 SetUserText(L"?"); 670 else 671 SetSelection(current_text.length(), start + 1); 672 } 673 674 bool AutocompleteEditViewWin::IsSelectAll() { 675 CHARRANGE selection; 676 GetSel(selection); 677 return IsSelectAllForRange(selection); 678 } 679 680 bool AutocompleteEditViewWin::DeleteAtEndPressed() { 681 return delete_at_end_pressed_; 682 } 683 684 void AutocompleteEditViewWin::GetSelectionBounds(string16::size_type* start, 685 string16::size_type* end) { 686 CHARRANGE selection; 687 GetSel(selection); 688 *start = static_cast<size_t>(selection.cpMin); 689 *end = static_cast<size_t>(selection.cpMax); 690 } 691 692 void AutocompleteEditViewWin::SelectAll(bool reversed) { 693 if (reversed) 694 SetSelection(GetTextLength(), 0); 695 else 696 SetSelection(0, GetTextLength()); 697 } 698 699 void AutocompleteEditViewWin::RevertAll() { 700 ScopedFreeze freeze(this, GetTextObjectModel()); 701 ClosePopup(); 702 model_->Revert(); 703 saved_selection_for_focus_change_.cpMin = -1; 704 TextChanged(); 705 } 706 707 void AutocompleteEditViewWin::UpdatePopup() { 708 ScopedFreeze freeze(this, GetTextObjectModel()); 709 model_->SetInputInProgress(true); 710 711 if (!model_->has_focus()) { 712 // When we're in the midst of losing focus, don't rerun autocomplete. This 713 // can happen when losing focus causes the IME to cancel/finalize a 714 // composition. We still want to note that user input is in progress, we 715 // just don't want to do anything else. 716 // 717 // Note that in this case the ScopedFreeze above was unnecessary; however, 718 // we're inside the callstack of OnKillFocus(), which has already frozen the 719 // edit, so this will never result in an unnecessary UpdateWindow() call. 720 return; 721 } 722 723 // Don't inline autocomplete when: 724 // * The user is deleting text 725 // * The caret/selection isn't at the end of the text 726 // * The user has just pasted in something that replaced all the text 727 // * The user is trying to compose something in an IME 728 CHARRANGE sel; 729 GetSel(sel); 730 model_->StartAutocomplete(sel.cpMax != sel.cpMin, 731 (sel.cpMax < GetTextLength()) || IsImeComposing()); 732 } 733 734 void AutocompleteEditViewWin::ClosePopup() { 735 model_->StopAutocomplete(); 736 } 737 738 void AutocompleteEditViewWin::SetFocus() { 739 ::SetFocus(m_hWnd); 740 parent_view_->GetWidget()->NotifyAccessibilityEvent( 741 parent_view_, 742 ui::AccessibilityTypes::EVENT_FOCUS, 743 false); 744 } 745 746 IAccessible* AutocompleteEditViewWin::GetIAccessible() { 747 if (!autocomplete_accessibility_) { 748 CComObject<AutocompleteAccessibility>* accessibility = NULL; 749 if (!SUCCEEDED(CComObject<AutocompleteAccessibility>::CreateInstance( 750 &accessibility)) || !accessibility) 751 return NULL; 752 753 // Wrap the created object in a smart pointer so it won't leak. 754 base::win::ScopedComPtr<IAccessible> accessibility_comptr(accessibility); 755 if (!SUCCEEDED(accessibility->Initialize(this))) 756 return NULL; 757 758 // Copy to the class smart pointer, and notify that an instance of 759 // IAccessible was allocated for m_hWnd. 760 autocomplete_accessibility_ = accessibility_comptr; 761 NotifyWinEvent(EVENT_OBJECT_CREATE, m_hWnd, OBJID_CLIENT, CHILDID_SELF); 762 } 763 // Detach to leave ref counting to the caller. 764 return autocomplete_accessibility_.Detach(); 765 } 766 767 void AutocompleteEditViewWin::SetDropHighlightPosition(int position) { 768 if (drop_highlight_position_ != position) { 769 RepaintDropHighlight(drop_highlight_position_); 770 drop_highlight_position_ = position; 771 RepaintDropHighlight(drop_highlight_position_); 772 } 773 } 774 775 void AutocompleteEditViewWin::MoveSelectedText(int new_position) { 776 const string16 selected_text(GetSelectedText()); 777 CHARRANGE sel; 778 GetSel(sel); 779 DCHECK((sel.cpMax != sel.cpMin) && (new_position >= 0) && 780 (new_position <= GetTextLength())); 781 782 ScopedFreeze freeze(this, GetTextObjectModel()); 783 OnBeforePossibleChange(); 784 785 // Nuke the selected text. 786 ReplaceSel(L"", TRUE); 787 788 // And insert it into the new location. 789 if (new_position >= sel.cpMin) 790 new_position -= (sel.cpMax - sel.cpMin); 791 PlaceCaretAt(new_position); 792 ReplaceSel(selected_text.c_str(), TRUE); 793 794 OnAfterPossibleChange(); 795 } 796 797 void AutocompleteEditViewWin::InsertText(int position, 798 const string16& text) { 799 DCHECK((position >= 0) && (position <= GetTextLength())); 800 ScopedFreeze freeze(this, GetTextObjectModel()); 801 OnBeforePossibleChange(); 802 SetSelection(position, position); 803 ReplaceSel(text.c_str()); 804 OnAfterPossibleChange(); 805 } 806 807 void AutocompleteEditViewWin::OnTemporaryTextMaybeChanged( 808 const string16& display_text, 809 bool save_original_selection) { 810 if (save_original_selection) 811 GetSelection(original_selection_); 812 813 // Set new text and cursor position. Sometimes this does extra work (e.g. 814 // when the new text and the old text are identical), but it's only called 815 // when the user manually changes the selected line in the popup, so that's 816 // not really a problem. Also, even when the text hasn't changed we'd want to 817 // update the caret, because if the user had the cursor in the middle of the 818 // text and then arrowed to another entry with the same text, we'd still want 819 // to move the caret. 820 ScopedFreeze freeze(this, GetTextObjectModel()); 821 SetWindowTextAndCaretPos(display_text, display_text.length()); 822 TextChanged(); 823 } 824 825 bool AutocompleteEditViewWin::OnInlineAutocompleteTextMaybeChanged( 826 const string16& display_text, 827 size_t user_text_length) { 828 // Update the text and selection. Because this can be called repeatedly while 829 // typing, we've careful not to freeze the edit unless we really need to. 830 // Also, unlike in the temporary text case above, here we don't want to update 831 // the caret/selection unless we have to, since this might make the user's 832 // caret position change without warning during typing. 833 if (display_text == GetText()) 834 return false; 835 836 ScopedFreeze freeze(this, GetTextObjectModel()); 837 SetWindowText(display_text.c_str()); 838 // Set a reversed selection to keep the caret in the same position, which 839 // avoids scrolling the user's text. 840 SetSelection(static_cast<LONG>(display_text.length()), 841 static_cast<LONG>(user_text_length)); 842 TextChanged(); 843 return true; 844 } 845 846 void AutocompleteEditViewWin::OnRevertTemporaryText() { 847 SetSelectionRange(original_selection_); 848 TextChanged(); 849 } 850 851 void AutocompleteEditViewWin::OnBeforePossibleChange() { 852 // Record our state. 853 text_before_change_ = GetText(); 854 GetSelection(sel_before_change_); 855 } 856 857 bool AutocompleteEditViewWin::OnAfterPossibleChange() { 858 return OnAfterPossibleChangeInternal(false); 859 } 860 861 bool AutocompleteEditViewWin::OnAfterPossibleChangeInternal( 862 bool force_text_changed) { 863 // Prevent the user from selecting the "phantom newline" at the end of the 864 // edit. If they try, we just silently move the end of the selection back to 865 // the end of the real text. 866 CHARRANGE new_sel; 867 GetSelection(new_sel); 868 const int length = GetTextLength(); 869 if ((new_sel.cpMin > length) || (new_sel.cpMax > length)) { 870 if (new_sel.cpMin > length) 871 new_sel.cpMin = length; 872 if (new_sel.cpMax > length) 873 new_sel.cpMax = length; 874 SetSelectionRange(new_sel); 875 } 876 const bool selection_differs = 877 ((new_sel.cpMin != new_sel.cpMax) || 878 (sel_before_change_.cpMin != sel_before_change_.cpMax)) && 879 ((new_sel.cpMin != sel_before_change_.cpMin) || 880 (new_sel.cpMax != sel_before_change_.cpMax)); 881 882 // See if the text or selection have changed since OnBeforePossibleChange(). 883 const string16 new_text(GetText()); 884 const bool text_differs = (new_text != text_before_change_) || 885 force_text_changed; 886 887 // When the user has deleted text, we don't allow inline autocomplete. Make 888 // sure to not flag cases like selecting part of the text and then pasting 889 // (or typing) the prefix of that selection. (We detect these by making 890 // sure the caret, which should be after any insertion, hasn't moved 891 // forward of the old selection start.) 892 const bool just_deleted_text = 893 (text_before_change_.length() > new_text.length()) && 894 (new_sel.cpMin <= std::min(sel_before_change_.cpMin, 895 sel_before_change_.cpMax)); 896 897 const bool something_changed = model_->OnAfterPossibleChange( 898 new_text, new_sel.cpMin, new_sel.cpMax, selection_differs, 899 text_differs, just_deleted_text, !IsImeComposing()); 900 901 if (selection_differs) 902 controller_->OnSelectionBoundsChanged(); 903 904 if (something_changed && text_differs) 905 TextChanged(); 906 907 if (text_differs) { 908 // Note that a TEXT_CHANGED event implies that the cursor/selection 909 // probably changed too, so we don't need to send both. 910 parent_view_->GetWidget()->NotifyAccessibilityEvent( 911 parent_view_, ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true); 912 } else if (selection_differs) { 913 // Notify assistive technology that the cursor or selection changed. 914 parent_view_->GetWidget()->NotifyAccessibilityEvent( 915 parent_view_, ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true); 916 } else if (delete_at_end_pressed_) { 917 model_->OnChanged(); 918 } 919 920 return something_changed; 921 } 922 923 gfx::NativeView AutocompleteEditViewWin::GetNativeView() const { 924 return m_hWnd; 925 } 926 927 CommandUpdater* AutocompleteEditViewWin::GetCommandUpdater() { 928 return command_updater_; 929 } 930 931 void AutocompleteEditViewWin::SetInstantSuggestion(const string16& suggestion, 932 bool animate_to_complete) { 933 parent_view_->SetInstantSuggestion(suggestion, animate_to_complete); 934 } 935 936 int AutocompleteEditViewWin::TextWidth() const { 937 return WidthNeededToDisplay(GetText()); 938 } 939 940 string16 AutocompleteEditViewWin::GetInstantSuggestion() const { 941 return parent_view_->GetInstantSuggestion(); 942 } 943 944 bool AutocompleteEditViewWin::IsImeComposing() const { 945 bool ime_composing = false; 946 HIMC context = ImmGetContext(m_hWnd); 947 if (context) { 948 ime_composing = !!ImmGetCompositionString(context, GCS_COMPSTR, NULL, 0); 949 ImmReleaseContext(m_hWnd, context); 950 } 951 return ime_composing; 952 } 953 954 views::View* AutocompleteEditViewWin::AddToView(views::View* parent) { 955 views::NativeViewHost* host = new views::NativeViewHost; 956 parent->AddChildView(host); 957 host->set_focus_view(parent); 958 host->Attach(GetNativeView()); 959 return host; 960 } 961 962 int AutocompleteEditViewWin::OnPerformDrop( 963 const views::DropTargetEvent& event) { 964 return OnPerformDropImpl(event, false); 965 } 966 967 int AutocompleteEditViewWin::OnPerformDropImpl( 968 const views::DropTargetEvent& event, 969 bool in_drag) { 970 const ui::OSExchangeData& data = event.data(); 971 972 if (data.HasURL()) { 973 GURL url; 974 string16 title; 975 if (data.GetURLAndTitle(&url, &title)) { 976 SetUserText(UTF8ToWide(url.spec())); 977 model()->AcceptInput(CURRENT_TAB, true); 978 return CopyOrLinkDragOperation(event.source_operations()); 979 } 980 } else if (data.HasString()) { 981 int string_drop_position = drop_highlight_position(); 982 string16 text; 983 if ((string_drop_position != -1 || !in_drag) && data.GetString(&text)) { 984 DCHECK(string_drop_position == -1 || 985 ((string_drop_position >= 0) && 986 (string_drop_position <= GetTextLength()))); 987 if (in_drag) { 988 if (event.source_operations()== ui::DragDropTypes::DRAG_MOVE) 989 MoveSelectedText(string_drop_position); 990 else 991 InsertText(string_drop_position, text); 992 } else { 993 PasteAndGo(CollapseWhitespace(text, true)); 994 } 995 return CopyOrLinkDragOperation(event.source_operations()); 996 } 997 } 998 999 return ui::DragDropTypes::DRAG_NONE; 1000 } 1001 1002 void AutocompleteEditViewWin::PasteAndGo(const string16& text) { 1003 if (CanPasteAndGo(text)) 1004 model_->PasteAndGo(); 1005 } 1006 1007 bool AutocompleteEditViewWin::SkipDefaultKeyEventProcessing( 1008 const views::KeyEvent& event) { 1009 ui::KeyboardCode key = event.key_code(); 1010 // We don't process ALT + numpad digit as accelerators, they are used for 1011 // entering special characters. We do translate alt-home. 1012 if (event.IsAltDown() && (key != ui::VKEY_HOME) && 1013 views::NativeTextfieldWin::IsNumPadDigit(key, 1014 views::IsExtendedKey(event))) 1015 return true; 1016 1017 // Skip accelerators for key combinations omnibox wants to crack. This list 1018 // should be synced with OnKeyDownOnlyWritable() (but for tab which is dealt 1019 // with above in LocationBarView::SkipDefaultKeyEventProcessing). 1020 // 1021 // We cannot return true for all keys because we still need to handle some 1022 // accelerators (e.g., F5 for reload the page should work even when the 1023 // Omnibox gets focused). 1024 switch (key) { 1025 case ui::VKEY_ESCAPE: { 1026 ScopedFreeze freeze(this, GetTextObjectModel()); 1027 return model_->OnEscapeKeyPressed(); 1028 } 1029 1030 case ui::VKEY_RETURN: 1031 return true; 1032 1033 case ui::VKEY_UP: 1034 case ui::VKEY_DOWN: 1035 return !event.IsAltDown(); 1036 1037 case ui::VKEY_DELETE: 1038 case ui::VKEY_INSERT: 1039 return !event.IsAltDown() && event.IsShiftDown() && 1040 !event.IsControlDown(); 1041 1042 case ui::VKEY_X: 1043 case ui::VKEY_V: 1044 return !event.IsAltDown() && event.IsControlDown(); 1045 1046 case ui::VKEY_BACK: 1047 case ui::VKEY_OEM_PLUS: 1048 return true; 1049 1050 default: 1051 return false; 1052 } 1053 } 1054 1055 void AutocompleteEditViewWin::HandleExternalMsg(UINT msg, 1056 UINT flags, 1057 const CPoint& screen_point) { 1058 if (msg == WM_CAPTURECHANGED) { 1059 SendMessage(msg, 0, NULL); 1060 return; 1061 } 1062 1063 CPoint client_point(screen_point); 1064 ::MapWindowPoints(NULL, m_hWnd, &client_point, 1); 1065 SendMessage(msg, flags, MAKELPARAM(client_point.x, client_point.y)); 1066 } 1067 1068 bool AutocompleteEditViewWin::IsCommandIdChecked(int command_id) const { 1069 return false; 1070 } 1071 1072 bool AutocompleteEditViewWin::IsCommandIdEnabled(int command_id) const { 1073 switch (command_id) { 1074 case IDS_UNDO: return !!CanUndo(); 1075 case IDC_CUT: return !!CanCut(); 1076 case IDC_COPY: return !!CanCopy(); 1077 case IDC_PASTE: return !!CanPaste(); 1078 case IDS_PASTE_AND_GO: return CanPasteAndGo(GetClipboardText()); 1079 case IDS_SELECT_ALL: return !!CanSelectAll(); 1080 case IDS_EDIT_SEARCH_ENGINES: 1081 return command_updater_->IsCommandEnabled(IDC_EDIT_SEARCH_ENGINES); 1082 default: 1083 NOTREACHED(); 1084 return false; 1085 } 1086 } 1087 1088 bool AutocompleteEditViewWin::GetAcceleratorForCommandId( 1089 int command_id, 1090 ui::Accelerator* accelerator) { 1091 return parent_view_->GetWidget()->GetAccelerator(command_id, accelerator); 1092 } 1093 1094 bool AutocompleteEditViewWin::IsItemForCommandIdDynamic(int command_id) const { 1095 // No need to change the default IDS_PASTE_AND_GO label unless this is a 1096 // search. 1097 return command_id == IDS_PASTE_AND_GO; 1098 } 1099 1100 string16 AutocompleteEditViewWin::GetLabelForCommandId( 1101 int command_id) const { 1102 DCHECK_EQ(IDS_PASTE_AND_GO, command_id); 1103 return l10n_util::GetStringUTF16(model_->is_paste_and_search() ? 1104 IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO); 1105 } 1106 1107 void AutocompleteEditViewWin::ExecuteCommand(int command_id) { 1108 ScopedFreeze freeze(this, GetTextObjectModel()); 1109 if (command_id == IDS_PASTE_AND_GO) { 1110 // This case is separate from the switch() below since we don't want to wrap 1111 // it in OnBefore/AfterPossibleChange() calls. 1112 model_->PasteAndGo(); 1113 return; 1114 } 1115 1116 OnBeforePossibleChange(); 1117 switch (command_id) { 1118 case IDS_UNDO: 1119 Undo(); 1120 break; 1121 1122 case IDC_CUT: 1123 Cut(); 1124 break; 1125 1126 case IDC_COPY: 1127 Copy(); 1128 break; 1129 1130 case IDC_PASTE: 1131 Paste(); 1132 break; 1133 1134 case IDS_SELECT_ALL: 1135 SelectAll(false); 1136 break; 1137 1138 case IDS_EDIT_SEARCH_ENGINES: 1139 command_updater_->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES); 1140 break; 1141 1142 default: 1143 NOTREACHED(); 1144 break; 1145 } 1146 OnAfterPossibleChange(); 1147 } 1148 1149 // static 1150 int CALLBACK AutocompleteEditViewWin::WordBreakProc(LPTSTR edit_text, 1151 int current_pos, 1152 int num_bytes, 1153 int action) { 1154 // TODO(pkasting): http://b/1111308 We should let other people, like ICU and 1155 // GURL, do the work for us here instead of writing all this ourselves. 1156 1157 // Sadly, even though the MSDN docs claim that the third parameter here is a 1158 // number of characters, they lie. It's a number of bytes. 1159 const int length = num_bytes / sizeof(wchar_t); 1160 1161 // With no clear guidance from the MSDN docs on how to handle "not found" in 1162 // the "find the nearest xxx..." cases below, I cap the return values at 1163 // [0, length]. Since one of these (0) is also a valid position, the return 1164 // values are thus ambiguous :( 1165 switch (action) { 1166 // Find nearest character before current position that begins a word. 1167 case WB_LEFT: 1168 case WB_MOVEWORDLEFT: { 1169 if (current_pos < 2) { 1170 // Either current_pos == 0, so we have a "not found" case and return 0, 1171 // or current_pos == 1, and the only character before this position is 1172 // at 0. 1173 return 0; 1174 } 1175 1176 // Look for a delimiter before the previous character; the previous word 1177 // starts immediately after. (If we looked for a delimiter before the 1178 // current character, we could stop on the immediate prior character, 1179 // which would mean we'd return current_pos -- which isn't "before the 1180 // current position".) 1181 const int prev_delim = 1182 WordBreakProc(edit_text, current_pos - 1, num_bytes, WB_LEFTBREAK); 1183 1184 if ((prev_delim == 0) && 1185 !WordBreakProc(edit_text, 0, num_bytes, WB_ISDELIMITER)) { 1186 // Got back 0, but position 0 isn't a delimiter. This was a "not 1187 // found" 0, so return one of our own. 1188 return 0; 1189 } 1190 1191 return prev_delim + 1; 1192 } 1193 1194 // Find nearest character after current position that begins a word. 1195 case WB_RIGHT: 1196 case WB_MOVEWORDRIGHT: { 1197 if (WordBreakProc(edit_text, current_pos, num_bytes, WB_ISDELIMITER)) { 1198 // The current character is a delimiter, so the next character starts 1199 // a new word. Done. 1200 return current_pos + 1; 1201 } 1202 1203 // Look for a delimiter after the current character; the next word starts 1204 // immediately after. 1205 const int next_delim = 1206 WordBreakProc(edit_text, current_pos, num_bytes, WB_RIGHTBREAK); 1207 if (next_delim == length) { 1208 // Didn't find a delimiter. Return length to signal "not found". 1209 return length; 1210 } 1211 1212 return next_delim + 1; 1213 } 1214 1215 // Determine if the current character delimits words. 1216 case WB_ISDELIMITER: 1217 return !!(WordBreakProc(edit_text, current_pos, num_bytes, WB_CLASSIFY) & 1218 WBF_BREAKLINE); 1219 1220 // Return the classification of the current character. 1221 case WB_CLASSIFY: 1222 if (IsWhitespace(edit_text[current_pos])) { 1223 // Whitespace normally breaks words, but the MSDN docs say that we must 1224 // not break on the CRs in a "CR, LF" or a "CR, CR, LF" sequence. Just 1225 // check for an arbitrarily long sequence of CRs followed by LF and 1226 // report "not a delimiter" for the current CR in that case. 1227 while ((current_pos < (length - 1)) && 1228 (edit_text[current_pos] == 0x13)) { 1229 if (edit_text[++current_pos] == 0x10) 1230 return WBF_ISWHITE; 1231 } 1232 return WBF_BREAKLINE | WBF_ISWHITE; 1233 } 1234 1235 // Punctuation normally breaks words, but the first two characters in 1236 // "://" (end of scheme) should not be breaks, so that "http://" will be 1237 // treated as one word. 1238 if (ispunct(edit_text[current_pos], std::locale()) && 1239 !SchemeEnd(edit_text, current_pos, length) && 1240 !SchemeEnd(edit_text, current_pos - 1, length)) 1241 return WBF_BREAKLINE; 1242 1243 // Normal character, no flags. 1244 return 0; 1245 1246 // Finds nearest delimiter before current position. 1247 case WB_LEFTBREAK: 1248 for (int i = current_pos - 1; i >= 0; --i) { 1249 if (WordBreakProc(edit_text, i, num_bytes, WB_ISDELIMITER)) 1250 return i; 1251 } 1252 return 0; 1253 1254 // Finds nearest delimiter after current position. 1255 case WB_RIGHTBREAK: 1256 for (int i = current_pos + 1; i < length; ++i) { 1257 if (WordBreakProc(edit_text, i, num_bytes, WB_ISDELIMITER)) 1258 return i; 1259 } 1260 return length; 1261 } 1262 1263 NOTREACHED(); 1264 return 0; 1265 } 1266 1267 // static 1268 bool AutocompleteEditViewWin::SchemeEnd(LPTSTR edit_text, 1269 int current_pos, 1270 int length) { 1271 return (current_pos >= 0) && 1272 ((length - current_pos) > 2) && 1273 (edit_text[current_pos] == ':') && 1274 (edit_text[current_pos + 1] == '/') && 1275 (edit_text[current_pos + 2] == '/'); 1276 } 1277 1278 void AutocompleteEditViewWin::OnChar(TCHAR ch, UINT repeat_count, UINT flags) { 1279 // Don't let alt-enter beep. Not sure this is necessary, as the standard 1280 // alt-enter will hit DiscardWMSysChar() and get thrown away, and 1281 // ctrl-alt-enter doesn't seem to reach here for some reason? At least not on 1282 // my system... still, this is harmless and maybe necessary in other locales. 1283 if (ch == VK_RETURN && (flags & KF_ALTDOWN)) 1284 return; 1285 1286 // Escape is processed in OnKeyDown. Don't let any WM_CHAR messages propagate 1287 // as we don't want the RichEdit to do anything funky. 1288 if (ch == VK_ESCAPE && !(flags & KF_ALTDOWN)) 1289 return; 1290 1291 if (ch == VK_TAB) { 1292 // Don't add tabs to the input. 1293 return; 1294 } 1295 1296 HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags); 1297 } 1298 1299 void AutocompleteEditViewWin::OnContextMenu(HWND window, const CPoint& point) { 1300 BuildContextMenu(); 1301 if (point.x == -1 || point.y == -1) { 1302 POINT p; 1303 GetCaretPos(&p); 1304 MapWindowPoints(HWND_DESKTOP, &p, 1); 1305 context_menu_->RunContextMenuAt(gfx::Point(p)); 1306 } else { 1307 context_menu_->RunContextMenuAt(gfx::Point(point)); 1308 } 1309 } 1310 1311 void AutocompleteEditViewWin::OnCopy() { 1312 string16 text(GetSelectedText()); 1313 if (text.empty()) 1314 return; 1315 1316 CHARRANGE sel; 1317 GURL url; 1318 bool write_url = false; 1319 GetSel(sel); 1320 // GetSel() doesn't preserve selection direction, so sel.cpMin will always be 1321 // the smaller value. 1322 model_->AdjustTextForCopy(sel.cpMin, IsSelectAll(), &text, &url, &write_url); 1323 ui::ScopedClipboardWriter scw(g_browser_process->clipboard()); 1324 scw.WriteText(text); 1325 if (write_url) { 1326 scw.WriteBookmark(text, url.spec()); 1327 scw.WriteHyperlink(EscapeForHTML(text), url.spec()); 1328 } 1329 } 1330 1331 void AutocompleteEditViewWin::OnCut() { 1332 OnCopy(); 1333 1334 // This replace selection will have no effect (even on the undo stack) if the 1335 // current selection is empty. 1336 ReplaceSel(L"", true); 1337 } 1338 1339 LRESULT AutocompleteEditViewWin::OnGetObject(UINT uMsg, 1340 WPARAM wparam, 1341 LPARAM lparam) { 1342 // Accessibility readers will send an OBJID_CLIENT message. 1343 if (lparam == OBJID_CLIENT) { 1344 // Re-attach for internal re-usage of accessibility pointer. 1345 autocomplete_accessibility_.Attach(GetIAccessible()); 1346 1347 if (autocomplete_accessibility_) { 1348 return LresultFromObject(IID_IAccessible, wparam, 1349 autocomplete_accessibility_); 1350 } 1351 } 1352 return 0; 1353 } 1354 1355 LRESULT AutocompleteEditViewWin::OnImeComposition(UINT message, 1356 WPARAM wparam, 1357 LPARAM lparam) { 1358 if (ignore_ime_messages_) { 1359 // This message was sent while we're in the middle of meddling with the 1360 // underlying edit control. If we handle it below, OnAfterPossibleChange() 1361 // can get bogus text for the edit, and rerun autocomplete, destructively 1362 // modifying the result set that we're in the midst of using. For example, 1363 // if SetWindowTextAndCaretPos() was called due to the user clicking an 1364 // entry in the popup, we're in the middle of executing SetSelectedLine(), 1365 // and changing the results can cause checkfailures. 1366 return DefWindowProc(message, wparam, lparam); 1367 } 1368 1369 ScopedFreeze freeze(this, GetTextObjectModel()); 1370 OnBeforePossibleChange(); 1371 LRESULT result = DefWindowProc(message, wparam, lparam); 1372 // Force an IME composition confirmation operation to trigger the text_changed 1373 // code in OnAfterPossibleChange(), even if identical contents are confirmed, 1374 // to make sure the model can update its internal states correctly. 1375 OnAfterPossibleChangeInternal((lparam & GCS_RESULTSTR) != 0); 1376 return result; 1377 } 1378 1379 void AutocompleteEditViewWin::OnKeyDown(TCHAR key, 1380 UINT repeat_count, 1381 UINT flags) { 1382 delete_at_end_pressed_ = false; 1383 1384 if (OnKeyDownAllModes(key, repeat_count, flags)) 1385 return; 1386 1387 // Make sure that we handle system key events like Alt-F4. 1388 if (popup_window_mode_) { 1389 DefWindowProc(GetCurrentMessage()->message, key, MAKELPARAM(repeat_count, 1390 flags)); 1391 return; 1392 } 1393 1394 if (OnKeyDownOnlyWritable(key, repeat_count, flags)) 1395 return; 1396 1397 // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many 1398 // different keys (backspace, ctrl-v, ...), so we call this in both cases. 1399 HandleKeystroke(GetCurrentMessage()->message, key, repeat_count, flags); 1400 } 1401 1402 void AutocompleteEditViewWin::OnKeyUp(TCHAR key, 1403 UINT repeat_count, 1404 UINT flags) { 1405 if (key == VK_CONTROL) 1406 model_->OnControlKeyChanged(false); 1407 1408 // On systems with RTL input languages, ctrl+shift toggles the reading order 1409 // (depending on which shift key is pressed). But by default the CRichEditCtrl 1410 // only changes the current reading order, and as soon as the user deletes all 1411 // the text, or we call SetWindowText(), it reverts to the "default" order. 1412 // To work around this, if the user hits ctrl+shift, we pass it to 1413 // DefWindowProc() while the edit is empty, which toggles the default reading 1414 // order; then we restore the user's input. 1415 if (!(flags & KF_ALTDOWN) && 1416 (((key == VK_CONTROL) && (GetKeyState(VK_SHIFT) < 0)) || 1417 ((key == VK_SHIFT) && (GetKeyState(VK_CONTROL) < 0)))) { 1418 ScopedFreeze freeze(this, GetTextObjectModel()); 1419 1420 string16 saved_text(GetText()); 1421 CHARRANGE saved_sel; 1422 GetSelection(saved_sel); 1423 1424 SetWindowText(L""); 1425 1426 DefWindowProc(WM_KEYUP, key, MAKELPARAM(repeat_count, flags)); 1427 1428 SetWindowText(saved_text.c_str()); 1429 SetSelectionRange(saved_sel); 1430 return; 1431 } 1432 1433 SetMsgHandled(false); 1434 } 1435 1436 void AutocompleteEditViewWin::OnKillFocus(HWND focus_wnd) { 1437 if (m_hWnd == focus_wnd) { 1438 // Focus isn't actually leaving. 1439 SetMsgHandled(false); 1440 return; 1441 } 1442 1443 // This must be invoked before ClosePopup. 1444 model_->OnWillKillFocus(focus_wnd); 1445 1446 // Close the popup. 1447 ClosePopup(); 1448 1449 // Save the user's existing selection to restore it later. 1450 GetSelection(saved_selection_for_focus_change_); 1451 1452 // Tell the model to reset itself. 1453 model_->OnKillFocus(); 1454 1455 // Let the CRichEditCtrl do its default handling. This will complete any 1456 // in-progress IME composition. We must do this after setting has_focus_ to 1457 // false so that UpdatePopup() will know not to rerun autocomplete. 1458 ScopedFreeze freeze(this, GetTextObjectModel()); 1459 DefWindowProc(WM_KILLFOCUS, reinterpret_cast<WPARAM>(focus_wnd), 0); 1460 1461 // Cancel any user selection and scroll the text back to the beginning of the 1462 // URL. We have to do this after calling DefWindowProc() because otherwise 1463 // an in-progress IME composition will be completed at the new caret position, 1464 // resulting in the string jumping unexpectedly to the front of the edit. 1465 PlaceCaretAt(0); 1466 } 1467 1468 void AutocompleteEditViewWin::OnLButtonDblClk(UINT keys, const CPoint& point) { 1469 // Save the double click info for later triple-click detection. 1470 tracking_double_click_ = true; 1471 double_click_point_ = point; 1472 double_click_time_ = GetCurrentMessage()->time; 1473 possible_drag_ = false; 1474 1475 // Modifying the selection counts as accepting any inline autocompletion, so 1476 // track "changes" made by clicking the mouse button. 1477 ScopedFreeze freeze(this, GetTextObjectModel()); 1478 OnBeforePossibleChange(); 1479 DefWindowProc(WM_LBUTTONDBLCLK, keys, 1480 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y)); 1481 OnAfterPossibleChange(); 1482 1483 gaining_focus_.reset(); // See NOTE in OnMouseActivate(). 1484 } 1485 1486 void AutocompleteEditViewWin::OnLButtonDown(UINT keys, const CPoint& point) { 1487 TrackMousePosition(kLeft, point); 1488 if (gaining_focus_.get()) { 1489 // When Chrome was already the activated app, we haven't reached 1490 // OnSetFocus() yet. When we get there, don't restore the saved selection, 1491 // since it will just screw up the user's interaction with the edit. 1492 saved_selection_for_focus_change_.cpMin = -1; 1493 1494 // Crazy hack: In this particular case, the CRichEditCtrl seems to have an 1495 // internal flag that discards the next WM_LBUTTONDOWN without processing 1496 // it, so that clicks on the edit when its owning app is not activated are 1497 // eaten rather than processed (despite whatever the return value of 1498 // DefWindowProc(WM_MOUSEACTIVATE, ...) may say). This behavior is 1499 // confusing and we want the click to be treated normally. So, to reset the 1500 // CRichEditCtrl's internal flag, we pass it an extra WM_LBUTTONDOWN here 1501 // (as well as a matching WM_LBUTTONUP, just in case we'd be confusing some 1502 // kind of state tracking otherwise). 1503 DefWindowProc(WM_LBUTTONDOWN, keys, MAKELPARAM(point.x, point.y)); 1504 DefWindowProc(WM_LBUTTONUP, keys, MAKELPARAM(point.x, point.y)); 1505 } 1506 1507 // Check for triple click, then reset tracker. Should be safe to subtract 1508 // double_click_time_ from the current message's time even if the timer has 1509 // wrapped in between. 1510 const bool is_triple_click = tracking_double_click_ && 1511 views::NativeTextfieldWin::IsDoubleClick(double_click_point_, point, 1512 GetCurrentMessage()->time - double_click_time_); 1513 tracking_double_click_ = false; 1514 1515 if (!gaining_focus_.get() && !is_triple_click) 1516 OnPossibleDrag(point); 1517 1518 1519 // Modifying the selection counts as accepting any inline autocompletion, so 1520 // track "changes" made by clicking the mouse button. 1521 ScopedFreeze freeze(this, GetTextObjectModel()); 1522 OnBeforePossibleChange(); 1523 DefWindowProc(WM_LBUTTONDOWN, keys, 1524 MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click), 1525 point.y)); 1526 OnAfterPossibleChange(); 1527 1528 gaining_focus_.reset(); 1529 } 1530 1531 void AutocompleteEditViewWin::OnLButtonUp(UINT keys, const CPoint& point) { 1532 // default processing should happen first so we can see the result of the 1533 // selection 1534 ScopedFreeze freeze(this, GetTextObjectModel()); 1535 DefWindowProc(WM_LBUTTONUP, keys, 1536 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y)); 1537 1538 SelectAllIfNecessary(kLeft, point); 1539 1540 tracking_click_[kLeft] = false; 1541 1542 possible_drag_ = false; 1543 } 1544 1545 void AutocompleteEditViewWin::OnMButtonDblClk(UINT /*keys*/, 1546 const CPoint& /*point*/) { 1547 gaining_focus_.reset(); // See NOTE in OnMouseActivate(). 1548 1549 // By default, the edit responds to middle-clicks by capturing the mouse and 1550 // ignoring all subsequent events until it receives another click (of any of 1551 // the left, middle, or right buttons). This bizarre behavior is not only 1552 // useless but can cause the UI to appear unresponsive if a user accidentally 1553 // middle-clicks the edit (instead of a tab above it), so we purposefully eat 1554 // this message (instead of calling SetMsgHandled(false)) to avoid triggering 1555 // this. 1556 } 1557 1558 void AutocompleteEditViewWin::OnMButtonDown(UINT /*keys*/, 1559 const CPoint& /*point*/) { 1560 tracking_double_click_ = false; 1561 1562 // See note in OnMButtonDblClk above. 1563 } 1564 1565 void AutocompleteEditViewWin::OnMButtonUp(UINT /*keys*/, 1566 const CPoint& /*point*/) { 1567 possible_drag_ = false; 1568 1569 // See note in OnMButtonDblClk above. 1570 } 1571 1572 LRESULT AutocompleteEditViewWin::OnMouseActivate(HWND window, 1573 UINT hit_test, 1574 UINT mouse_message) { 1575 // First, give other handlers a chance to handle the message to see if we are 1576 // actually going to activate and gain focus. 1577 LRESULT result = DefWindowProc(WM_MOUSEACTIVATE, 1578 reinterpret_cast<WPARAM>(window), 1579 MAKELPARAM(hit_test, mouse_message)); 1580 // Check if we're getting focus from a click. We have to do this here rather 1581 // than in OnXButtonDown() since in many scenarios OnSetFocus() will be 1582 // reached before OnXButtonDown(), preventing us from detecting this properly 1583 // there. Also in those cases, we need to already know in OnSetFocus() that 1584 // we should not restore the saved selection. 1585 if (!model_->has_focus() && 1586 ((mouse_message == WM_LBUTTONDOWN || mouse_message == WM_RBUTTONDOWN)) && 1587 (result == MA_ACTIVATE)) { 1588 DCHECK(!gaining_focus_.get()); 1589 gaining_focus_.reset(new ScopedFreeze(this, GetTextObjectModel())); 1590 // NOTE: Despite |mouse_message| being WM_XBUTTONDOWN here, we're not 1591 // guaranteed to call OnXButtonDown() later! Specifically, if this is the 1592 // second click of a double click, we'll reach here but later call 1593 // OnXButtonDblClk(). Make sure |gaining_focus_| gets reset both places, 1594 // or we'll have visual glitchiness and then DCHECK failures. 1595 1596 // Don't restore saved selection, it will just screw up our interaction 1597 // with this edit. 1598 saved_selection_for_focus_change_.cpMin = -1; 1599 } 1600 return result; 1601 } 1602 1603 void AutocompleteEditViewWin::OnMouseMove(UINT keys, const CPoint& point) { 1604 if (possible_drag_) { 1605 StartDragIfNecessary(point); 1606 // Don't fall through to default mouse handling, otherwise a second 1607 // drag session may start. 1608 return; 1609 } 1610 1611 if (tracking_click_[kLeft] && !IsDrag(click_point_[kLeft], point)) 1612 return; 1613 1614 tracking_click_[kLeft] = false; 1615 1616 // Return quickly if this can't change the selection/cursor, so we don't 1617 // create a ScopedFreeze (and thus do an UpdateWindow()) on every 1618 // WM_MOUSEMOVE. 1619 if (!(keys & MK_LBUTTON)) { 1620 DefWindowProc(WM_MOUSEMOVE, keys, MAKELPARAM(point.x, point.y)); 1621 return; 1622 } 1623 1624 // Clamp the selection to the visible text so the user can't drag to select 1625 // the "phantom newline". In theory we could achieve this by clipping the X 1626 // coordinate, but in practice the edit seems to behave nondeterministically 1627 // with similar sequences of clipped input coordinates fed to it. Maybe it's 1628 // reading the mouse cursor position directly? 1629 // 1630 // This solution has a minor visual flaw, however: if there's a visible cursor 1631 // at the edge of the text (only true when there's no selection), dragging the 1632 // mouse around outside that edge repaints the cursor on every WM_MOUSEMOVE 1633 // instead of allowing it to blink normally. To fix this, we special-case 1634 // this exact case and discard the WM_MOUSEMOVE messages instead of passing 1635 // them along. 1636 // 1637 // But even this solution has a flaw! (Argh.) In the case where the user has 1638 // a selection that starts at the edge of the edit, and proceeds to the middle 1639 // of the edit, and the user is dragging back past the start edge to remove 1640 // the selection, there's a redraw problem where the change between having the 1641 // last few bits of text still selected and having nothing selected can be 1642 // slow to repaint (which feels noticeably strange). This occurs if you only 1643 // let the edit receive a single WM_MOUSEMOVE past the edge of the text. I 1644 // think on each WM_MOUSEMOVE the edit is repainting its previous state, then 1645 // updating its internal variables to the new state but not repainting. To 1646 // fix this, we allow one more WM_MOUSEMOVE through after the selection has 1647 // supposedly been shrunk to nothing; this makes the edit redraw the selection 1648 // quickly so it feels smooth. 1649 CHARRANGE selection; 1650 GetSel(selection); 1651 const bool possibly_can_discard_mousemove = 1652 (selection.cpMin == selection.cpMax) && 1653 (((selection.cpMin == 0) && 1654 (ClipXCoordToVisibleText(point.x, false) > point.x)) || 1655 ((selection.cpMin == GetTextLength()) && 1656 (ClipXCoordToVisibleText(point.x, false) < point.x))); 1657 if (!can_discard_mousemove_ || !possibly_can_discard_mousemove) { 1658 can_discard_mousemove_ = possibly_can_discard_mousemove; 1659 ScopedFreeze freeze(this, GetTextObjectModel()); 1660 OnBeforePossibleChange(); 1661 // Force the Y coordinate to the center of the clip rect. The edit 1662 // behaves strangely when the cursor is dragged vertically: if the cursor 1663 // is in the middle of the text, drags inside the clip rect do nothing, 1664 // and drags outside the clip rect act as if the cursor jumped to the 1665 // left edge of the text. When the cursor is at the right edge, drags of 1666 // just a few pixels vertically end up selecting the "phantom newline"... 1667 // sometimes. 1668 RECT r; 1669 GetRect(&r); 1670 DefWindowProc(WM_MOUSEMOVE, keys, 1671 MAKELPARAM(point.x, (r.bottom - r.top) / 2)); 1672 OnAfterPossibleChange(); 1673 } 1674 } 1675 1676 void AutocompleteEditViewWin::OnPaint(HDC bogus_hdc) { 1677 // We need to paint over the top of the edit. If we simply let the edit do 1678 // its default painting, then do ours into the window DC, the screen is 1679 // updated in between and we can get flicker. To avoid this, we force the 1680 // edit to paint into a memory DC, which we also paint onto, then blit the 1681 // whole thing to the screen. 1682 1683 // Don't paint if not necessary. 1684 CRect paint_clip_rect; 1685 if (!GetUpdateRect(&paint_clip_rect, true)) 1686 return; 1687 1688 // Begin painting, and create a memory DC for the edit to paint into. 1689 CPaintDC paint_dc(m_hWnd); 1690 CDC memory_dc(CreateCompatibleDC(paint_dc)); 1691 CRect rect; 1692 GetClientRect(&rect); 1693 // NOTE: This next call uses |paint_dc| instead of |memory_dc| because 1694 // |memory_dc| contains a 1x1 monochrome bitmap by default, which will cause 1695 // |memory_bitmap| to be monochrome, which isn't what we want. 1696 CBitmap memory_bitmap(CreateCompatibleBitmap(paint_dc, rect.Width(), 1697 rect.Height())); 1698 HBITMAP old_bitmap = memory_dc.SelectBitmap(memory_bitmap); 1699 1700 // Tell our intercept functions to supply our memory DC to the edit when it 1701 // tries to call BeginPaint(). 1702 // 1703 // The sane way to do this would be to use WM_PRINTCLIENT to ask the edit to 1704 // paint into our desired DC. Unfortunately, the Rich Edit 3.0 that ships 1705 // with Windows 2000/XP/Vista doesn't handle WM_PRINTCLIENT correctly; it 1706 // treats it just like WM_PAINT and calls BeginPaint(), ignoring our provided 1707 // DC. The Rich Edit 6.0 that ships with Office 2007 handles this better, but 1708 // has other issues, and we can't redistribute that DLL anyway. So instead, 1709 // we use this scary hack. 1710 // 1711 // NOTE: It's possible to get nested paint calls (!) (try setting the 1712 // permanent URL to something longer than the edit width, then selecting the 1713 // contents of the edit, typing a character, and hitting <esc>), so we can't 1714 // DCHECK(!edit_hwnd_) here. Instead, just save off the old HWND, which most 1715 // of the time will be NULL. 1716 HWND old_edit_hwnd = edit_hwnd; 1717 edit_hwnd = m_hWnd; 1718 paint_struct = paint_dc.m_ps; 1719 paint_struct.hdc = memory_dc; 1720 DefWindowProc(WM_PAINT, reinterpret_cast<WPARAM>(bogus_hdc), 0); 1721 1722 // Make the selection look better. 1723 EraseTopOfSelection(&memory_dc, rect, paint_clip_rect); 1724 1725 // Draw a slash through the scheme if this is insecure. 1726 if (insecure_scheme_component_.is_nonempty()) 1727 DrawSlashForInsecureScheme(memory_dc, rect, paint_clip_rect); 1728 1729 // Draw the drop highlight. 1730 if (drop_highlight_position_ != -1) 1731 DrawDropHighlight(memory_dc, rect, paint_clip_rect); 1732 1733 // Blit the memory DC to the actual paint DC and clean up. 1734 BitBlt(paint_dc, rect.left, rect.top, rect.Width(), rect.Height(), memory_dc, 1735 rect.left, rect.top, SRCCOPY); 1736 memory_dc.SelectBitmap(old_bitmap); 1737 edit_hwnd = old_edit_hwnd; 1738 } 1739 1740 void AutocompleteEditViewWin::OnPaste() { 1741 // Replace the selection if we have something to paste. 1742 const string16 text(GetClipboardText()); 1743 if (!text.empty()) { 1744 // Record this paste, so we can do different behavior. 1745 model_->on_paste(); 1746 // Force a Paste operation to trigger the text_changed code in 1747 // OnAfterPossibleChange(), even if identical contents are pasted into the 1748 // text box. 1749 text_before_change_.clear(); 1750 ReplaceSel(text.c_str(), true); 1751 } 1752 } 1753 1754 void AutocompleteEditViewWin::OnRButtonDblClk(UINT /*keys*/, 1755 const CPoint& /*point*/) { 1756 gaining_focus_.reset(); // See NOTE in OnMouseActivate(). 1757 SetMsgHandled(false); 1758 } 1759 1760 void AutocompleteEditViewWin::OnRButtonDown(UINT /*keys*/, 1761 const CPoint& point) { 1762 TrackMousePosition(kRight, point); 1763 tracking_double_click_ = false; 1764 possible_drag_ = false; 1765 gaining_focus_.reset(); 1766 SetMsgHandled(false); 1767 } 1768 1769 void AutocompleteEditViewWin::OnRButtonUp(UINT /*keys*/, const CPoint& point) { 1770 SelectAllIfNecessary(kRight, point); 1771 tracking_click_[kRight] = false; 1772 SetMsgHandled(false); 1773 } 1774 1775 void AutocompleteEditViewWin::OnSetFocus(HWND focus_wnd) { 1776 views::FocusManager* focus_manager = parent_view_->GetFocusManager(); 1777 if (focus_manager) { 1778 // Notify the FocusManager that the focused view is now the location bar 1779 // (our parent view). 1780 focus_manager->SetFocusedView(parent_view_); 1781 } else { 1782 NOTREACHED(); 1783 } 1784 1785 model_->OnSetFocus(GetKeyState(VK_CONTROL) < 0); 1786 1787 // Restore saved selection if available. 1788 if (saved_selection_for_focus_change_.cpMin != -1) { 1789 SetSelectionRange(saved_selection_for_focus_change_); 1790 saved_selection_for_focus_change_.cpMin = -1; 1791 } 1792 1793 SetMsgHandled(false); 1794 } 1795 1796 LRESULT AutocompleteEditViewWin::OnSetText(const wchar_t* text) { 1797 // Ignore all IME messages while we process this WM_SETTEXT message. 1798 // When SetWindowText() is called while an IME is composing text, the IME 1799 // calls SendMessage() to send a WM_IME_COMPOSITION message. When we receive 1800 // this WM_IME_COMPOSITION message, we update the omnibox and may call 1801 // SetWindowText() again. To stop this recursive message-handler call, we 1802 // stop updating the omnibox while we process a WM_SETTEXT message. 1803 // We wouldn't need to do this update anyway, because either we're in the 1804 // middle of updating the omnibox already or the caller of SetWindowText() 1805 // is going to update the omnibox next. 1806 AutoReset<bool> auto_reset_ignore_ime_messages(&ignore_ime_messages_, true); 1807 return DefWindowProc(WM_SETTEXT, 0, reinterpret_cast<LPARAM>(text)); 1808 } 1809 1810 void AutocompleteEditViewWin::OnSysChar(TCHAR ch, 1811 UINT repeat_count, 1812 UINT flags) { 1813 // Nearly all alt-<xxx> combos result in beeping rather than doing something 1814 // useful, so we discard most. Exceptions: 1815 // * ctrl-alt-<xxx>, which is sometimes important, generates WM_CHAR instead 1816 // of WM_SYSCHAR, so it doesn't need to be handled here. 1817 // * alt-space gets translated by the default WM_SYSCHAR handler to a 1818 // WM_SYSCOMMAND to open the application context menu, so we need to allow 1819 // it through. 1820 if (ch == VK_SPACE) 1821 SetMsgHandled(false); 1822 } 1823 1824 void AutocompleteEditViewWin::OnWindowPosChanging(WINDOWPOS* window_pos) { 1825 if (force_hidden_) 1826 window_pos->flags &= ~SWP_SHOWWINDOW; 1827 SetMsgHandled(true); 1828 } 1829 1830 BOOL AutocompleteEditViewWin::OnMouseWheel(UINT flags, 1831 short delta, 1832 CPoint point) { 1833 // Forward the mouse-wheel message to the window under the mouse. 1834 if (!views::RerouteMouseWheel(m_hWnd, MAKEWPARAM(flags, delta), 1835 MAKELPARAM(point.x, point.y))) 1836 SetMsgHandled(false); 1837 return 0; 1838 } 1839 1840 void AutocompleteEditViewWin::HandleKeystroke(UINT message, 1841 TCHAR key, 1842 UINT repeat_count, 1843 UINT flags) { 1844 ScopedFreeze freeze(this, GetTextObjectModel()); 1845 OnBeforePossibleChange(); 1846 1847 if (key == ui::VKEY_HOME || key == ui::VKEY_END) { 1848 // DefWindowProc() might reset the keyboard layout when it receives a 1849 // keydown event for VKEY_HOME or VKEY_END. When the window was created 1850 // with WS_EX_LAYOUTRTL and the current keyboard layout is not a RTL one, 1851 // if the input text is pure LTR text, the layout changes to the first RTL 1852 // keyboard layout in keyboard layout queue; if the input text is 1853 // bidirectional text, the layout changes to the keyboard layout of the 1854 // first RTL character in input text. When the window was created without 1855 // WS_EX_LAYOUTRTL and the current keyboard layout is not a LTR one, if the 1856 // input text is pure RTL text, the layout changes to English; if the input 1857 // text is bidirectional text, the layout changes to the keyboard layout of 1858 // the first LTR character in input text. Such keyboard layout change 1859 // behavior is surprising and inconsistent with keyboard behavior 1860 // elsewhere, so reset the layout in this case. 1861 HKL layout = GetKeyboardLayout(0); 1862 DefWindowProc(message, key, MAKELPARAM(repeat_count, flags)); 1863 ActivateKeyboardLayout(layout, KLF_REORDER); 1864 } else { 1865 DefWindowProc(message, key, MAKELPARAM(repeat_count, flags)); 1866 } 1867 1868 // CRichEditCtrl automatically turns on IMF_AUTOKEYBOARD when the user 1869 // inputs an RTL character, making it difficult for the user to control 1870 // what language is set as they type. Force this off to make the edit's 1871 // behavior more stable. 1872 const int lang_options = SendMessage(EM_GETLANGOPTIONS, 0, 0); 1873 if (lang_options & IMF_AUTOKEYBOARD) 1874 SendMessage(EM_SETLANGOPTIONS, 0, lang_options & ~IMF_AUTOKEYBOARD); 1875 1876 OnAfterPossibleChange(); 1877 } 1878 1879 bool AutocompleteEditViewWin::OnKeyDownOnlyWritable(TCHAR key, 1880 UINT repeat_count, 1881 UINT flags) { 1882 // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than 1883 // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places 1884 // in this function even with a WM_SYSKEYDOWN handler. 1885 1886 // If adding a new key that could possibly be an accelerator then you'll need 1887 // to update LocationBarView::SkipDefaultKeyEventProcessing() as well. 1888 int count = repeat_count; 1889 switch (key) { 1890 case VK_RIGHT: 1891 // TODO(sky): figure out RTL. 1892 if (base::i18n::IsRTL()) 1893 return false; 1894 { 1895 CHARRANGE selection; 1896 GetSel(selection); 1897 return (selection.cpMin == selection.cpMax) && 1898 (selection.cpMin == GetTextLength()) && 1899 model_->CommitSuggestedText(true); 1900 } 1901 1902 case VK_RETURN: 1903 model_->AcceptInput((flags & KF_ALTDOWN) ? 1904 NEW_FOREGROUND_TAB : CURRENT_TAB, false); 1905 return true; 1906 1907 case VK_PRIOR: 1908 case VK_NEXT: 1909 count = model_->result().size(); 1910 // FALL THROUGH 1911 case VK_UP: 1912 case VK_DOWN: 1913 // Ignore alt + numpad, but treat alt + (non-numpad) like (non-numpad). 1914 if ((flags & KF_ALTDOWN) && !(flags & KF_EXTENDED)) 1915 return false; 1916 1917 model_->OnUpOrDownKeyPressed(((key == VK_PRIOR) || (key == VK_UP)) ? 1918 -count : count); 1919 return true; 1920 1921 // Hijacking Editing Commands 1922 // 1923 // We hijack the keyboard short-cuts for Cut, Copy, and Paste here so that 1924 // they go through our clipboard routines. This allows us to be smarter 1925 // about how we interact with the clipboard and avoid bugs in the 1926 // CRichEditCtrl. If we didn't hijack here, the edit control would handle 1927 // these internally with sending the WM_CUT, WM_COPY, or WM_PASTE messages. 1928 // 1929 // Cut: Shift-Delete and Ctrl-x are treated as cut. Ctrl-Shift-Delete and 1930 // Ctrl-Shift-x are not treated as cut even though the underlying 1931 // CRichTextEdit would treat them as such. Also note that we bring 1932 // up 'clear browsing data' on control-shift-delete. 1933 // Copy: Ctrl-Insert and Ctrl-c is treated as copy. Shift-Ctrl-c is not. 1934 // (This is handled in OnKeyDownAllModes().) 1935 // Paste: Shift-Insert and Ctrl-v are treated as paste. Ctrl-Shift-Insert 1936 // and Ctrl-Shift-v are not. 1937 // 1938 // This behavior matches most, but not all Windows programs, and largely 1939 // conforms to what users expect. 1940 1941 case VK_DELETE: 1942 if (flags & KF_ALTDOWN) 1943 return false; 1944 if (GetKeyState(VK_SHIFT) >= 0) { 1945 if (GetKeyState(VK_CONTROL) >= 0) { 1946 CHARRANGE selection; 1947 GetSel(selection); 1948 delete_at_end_pressed_ = ((selection.cpMin == selection.cpMax) && 1949 (selection.cpMin == GetTextLength())); 1950 } 1951 return false; 1952 } 1953 if (GetKeyState(VK_CONTROL) >= 0) { 1954 // Cut text if possible. 1955 CHARRANGE selection; 1956 GetSel(selection); 1957 if (selection.cpMin != selection.cpMax) { 1958 ScopedFreeze freeze(this, GetTextObjectModel()); 1959 OnBeforePossibleChange(); 1960 Cut(); 1961 OnAfterPossibleChange(); 1962 } else { 1963 if (model_->popup_model()->IsOpen()) { 1964 // This is a bit overloaded, but we hijack Shift-Delete in this 1965 // case to delete the current item from the pop-up. We prefer 1966 // cutting to this when possible since that's the behavior more 1967 // people expect from Shift-Delete, and it's more commonly useful. 1968 model_->popup_model()->TryDeletingCurrentItem(); 1969 } 1970 } 1971 } 1972 return true; 1973 1974 case 'X': 1975 if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0)) 1976 return false; 1977 if (GetKeyState(VK_SHIFT) >= 0) { 1978 ScopedFreeze freeze(this, GetTextObjectModel()); 1979 OnBeforePossibleChange(); 1980 Cut(); 1981 OnAfterPossibleChange(); 1982 } 1983 return true; 1984 1985 case VK_INSERT: 1986 // Ignore insert by itself, so we don't turn overtype mode on/off. 1987 if (!(flags & KF_ALTDOWN) && (GetKeyState(VK_SHIFT) >= 0) && 1988 (GetKeyState(VK_CONTROL) >= 0)) 1989 return true; 1990 // FALL THROUGH 1991 case 'V': 1992 if ((flags & KF_ALTDOWN) || 1993 (GetKeyState((key == 'V') ? VK_CONTROL : VK_SHIFT) >= 0)) 1994 return false; 1995 if (GetKeyState((key == 'V') ? VK_SHIFT : VK_CONTROL) >= 0) { 1996 ScopedFreeze freeze(this, GetTextObjectModel()); 1997 OnBeforePossibleChange(); 1998 Paste(); 1999 OnAfterPossibleChange(); 2000 } 2001 return true; 2002 2003 case VK_BACK: { 2004 if ((flags & KF_ALTDOWN) || model_->is_keyword_hint() || 2005 model_->keyword().empty()) 2006 return false; 2007 2008 { 2009 CHARRANGE selection; 2010 GetSel(selection); 2011 if ((selection.cpMin != selection.cpMax) || (selection.cpMin != 0)) 2012 return false; 2013 } 2014 2015 // We're showing a keyword and the user pressed backspace at the beginning 2016 // of the text. Delete the selected keyword. 2017 ScopedFreeze freeze(this, GetTextObjectModel()); 2018 model_->ClearKeyword(GetText()); 2019 return true; 2020 } 2021 2022 case VK_TAB: { 2023 if (model_->is_keyword_hint()) { 2024 // Accept the keyword. 2025 ScopedFreeze freeze(this, GetTextObjectModel()); 2026 model_->AcceptKeyword(); 2027 } else if (!IsCaretAtEnd()) { 2028 ScopedFreeze freeze(this, GetTextObjectModel()); 2029 OnBeforePossibleChange(); 2030 PlaceCaretAt(GetTextLength()); 2031 OnAfterPossibleChange(); 2032 } else { 2033 model_->CommitSuggestedText(true); 2034 } 2035 return true; 2036 } 2037 2038 case 0xbb: // Ctrl-'='. Triggers subscripting (even in plain text mode). 2039 // We don't use VK_OEM_PLUS in case the macro isn't defined. 2040 // (e.g., we don't have this symbol in embeded environment). 2041 return true; 2042 2043 default: 2044 return false; 2045 } 2046 } 2047 2048 bool AutocompleteEditViewWin::OnKeyDownAllModes(TCHAR key, 2049 UINT repeat_count, 2050 UINT flags) { 2051 // See KF_ALTDOWN comment atop OnKeyDownOnlyWritable(). 2052 2053 switch (key) { 2054 case VK_CONTROL: 2055 model_->OnControlKeyChanged(true); 2056 return false; 2057 2058 case VK_INSERT: 2059 case 'C': 2060 // See more detailed comments in OnKeyDownOnlyWritable(). 2061 if ((flags & KF_ALTDOWN) || (GetKeyState(VK_CONTROL) >= 0)) 2062 return false; 2063 if (GetKeyState(VK_SHIFT) >= 0) 2064 Copy(); 2065 return true; 2066 2067 default: 2068 return false; 2069 } 2070 } 2071 2072 void AutocompleteEditViewWin::GetSelection(CHARRANGE& sel) const { 2073 GetSel(sel); 2074 2075 // See if we need to reverse the direction of the selection. 2076 ITextDocument* const text_object_model = GetTextObjectModel(); 2077 if (!text_object_model) 2078 return; 2079 base::win::ScopedComPtr<ITextSelection> selection; 2080 const HRESULT hr = text_object_model->GetSelection(selection.Receive()); 2081 DCHECK_EQ(S_OK, hr); 2082 long flags; 2083 selection->GetFlags(&flags); 2084 if (flags & tomSelStartActive) 2085 std::swap(sel.cpMin, sel.cpMax); 2086 } 2087 2088 string16 AutocompleteEditViewWin::GetSelectedText() const { 2089 // Figure out the length of the selection. 2090 CHARRANGE sel; 2091 GetSel(sel); 2092 2093 // Grab the selected text. 2094 string16 str; 2095 GetSelText(WriteInto(&str, sel.cpMax - sel.cpMin + 1)); 2096 return str; 2097 } 2098 2099 void AutocompleteEditViewWin::SetSelection(LONG start, LONG end) { 2100 SetSel(start, end); 2101 2102 if (start <= end) 2103 return; 2104 2105 // We need to reverse the direction of the selection. 2106 ITextDocument* const text_object_model = GetTextObjectModel(); 2107 if (!text_object_model) 2108 return; 2109 base::win::ScopedComPtr<ITextSelection> selection; 2110 const HRESULT hr = text_object_model->GetSelection(selection.Receive()); 2111 DCHECK_EQ(S_OK, hr); 2112 selection->SetFlags(tomSelStartActive); 2113 } 2114 2115 void AutocompleteEditViewWin::PlaceCaretAt(string16::size_type pos) { 2116 SetSelection(static_cast<LONG>(pos), static_cast<LONG>(pos)); 2117 } 2118 2119 bool AutocompleteEditViewWin::IsSelectAllForRange(const CHARRANGE& sel) const { 2120 const int text_length = GetTextLength(); 2121 return ((sel.cpMin == 0) && (sel.cpMax >= text_length)) || 2122 ((sel.cpMax == 0) && (sel.cpMin >= text_length)); 2123 } 2124 2125 LONG AutocompleteEditViewWin::ClipXCoordToVisibleText( 2126 LONG x, bool is_triple_click) const { 2127 // Clip the X coordinate to the left edge of the text. Careful: 2128 // PosFromChar(0) may return a negative X coordinate if the beginning of the 2129 // text has scrolled off the edit, so don't go past the clip rect's edge. 2130 PARAFORMAT2 pf2; 2131 GetParaFormat(pf2); 2132 // Calculation of the clipped coordinate is more complicated if the paragraph 2133 // layout is RTL layout, or if there is RTL characters inside the LTR layout 2134 // paragraph. 2135 const bool ltr_text_in_ltr_layout = !(pf2.wEffects & PFE_RTLPARA) && 2136 !base::i18n::StringContainsStrongRTLChars(GetText()); 2137 const int length = GetTextLength(); 2138 RECT r; 2139 GetRect(&r); 2140 // The values returned by PosFromChar() seem to refer always 2141 // to the left edge of the character's bounding box. 2142 const LONG first_position_x = PosFromChar(0).x; 2143 LONG min_x = first_position_x; 2144 if (!ltr_text_in_ltr_layout) { 2145 for (int i = 1; i < length; ++i) 2146 min_x = std::min(min_x, PosFromChar(i).x); 2147 } 2148 const LONG left_bound = std::max(r.left, min_x); 2149 // PosFromChar(length) is a phantom character past the end of the text. It is 2150 // not necessarily a right bound; in RTL controls it may be a left bound. So 2151 // treat it as a right bound only if it is to the right of the first 2152 // character. 2153 LONG right_bound = r.right; 2154 LONG end_position_x = PosFromChar(length).x; 2155 if (end_position_x >= first_position_x) { 2156 right_bound = std::min(right_bound, end_position_x); // LTR case. 2157 } 2158 // For trailing characters that are 2 pixels wide or less (like "l" in some 2159 // fonts), we have a problem: 2160 // * Clicks on any pixel within the character will place the cursor before 2161 // the character. 2162 // * Clicks on the pixel just after the character will not allow triple- 2163 // click to work properly (true for any last character width). 2164 // So, we move to the last pixel of the character when this is a 2165 // triple-click, and moving to one past the last pixel in all other 2166 // scenarios. This way, all clicks that can move the cursor will place it at 2167 // the end of the text, but triple-click will still work. 2168 if (x < left_bound) { 2169 return (is_triple_click && ltr_text_in_ltr_layout) ? left_bound - 1 : 2170 left_bound; 2171 } 2172 if ((length == 0) || (x < right_bound)) 2173 return x; 2174 return is_triple_click ? (right_bound - 1) : right_bound; 2175 } 2176 2177 void AutocompleteEditViewWin::EmphasizeURLComponents() { 2178 ITextDocument* const text_object_model = GetTextObjectModel(); 2179 ScopedFreeze freeze(this, text_object_model); 2180 ScopedSuspendUndo suspend_undo(text_object_model); 2181 2182 // Save the selection. 2183 CHARRANGE saved_sel; 2184 GetSelection(saved_sel); 2185 2186 // See whether the contents are a URL with a non-empty host portion, which we 2187 // should emphasize. To check for a URL, rather than using the type returned 2188 // by Parse(), ask the model, which will check the desired page transition for 2189 // this input. This can tell us whether an UNKNOWN input string is going to 2190 // be treated as a search or a navigation, and is the same method the Paste 2191 // And Go system uses. 2192 url_parse::Component scheme, host; 2193 AutocompleteInput::ParseForEmphasizeComponents( 2194 GetText(), model_->GetDesiredTLD(), &scheme, &host); 2195 const bool emphasize = model_->CurrentTextIsURL() && (host.len > 0); 2196 2197 // Set the baseline emphasis. 2198 CHARFORMAT cf = {0}; 2199 cf.dwMask = CFM_COLOR; 2200 // If we're going to emphasize parts of the text, then the baseline state 2201 // should be "de-emphasized". If not, then everything should be rendered in 2202 // the standard text color. 2203 cf.crTextColor = skia::SkColorToCOLORREF(LocationBarView::GetColor( 2204 security_level_, 2205 emphasize ? LocationBarView::DEEMPHASIZED_TEXT : LocationBarView::TEXT)); 2206 // NOTE: Don't use SetDefaultCharFormat() instead of the below; that sets the 2207 // format that will get applied to text added in the future, not to text 2208 // already in the edit. 2209 SelectAll(false); 2210 SetSelectionCharFormat(cf); 2211 2212 if (emphasize) { 2213 // We've found a host name, give it more emphasis. 2214 cf.crTextColor = skia::SkColorToCOLORREF(LocationBarView::GetColor( 2215 security_level_, LocationBarView::TEXT)); 2216 SetSelection(host.begin, host.end()); 2217 SetSelectionCharFormat(cf); 2218 } 2219 2220 // Emphasize the scheme for security UI display purposes (if necessary). 2221 insecure_scheme_component_.reset(); 2222 if (!model_->user_input_in_progress() && scheme.is_nonempty() && 2223 (security_level_ != ToolbarModel::NONE)) { 2224 if (security_level_ == ToolbarModel::SECURITY_ERROR) { 2225 insecure_scheme_component_.begin = scheme.begin; 2226 insecure_scheme_component_.len = scheme.len; 2227 } 2228 cf.crTextColor = skia::SkColorToCOLORREF(LocationBarView::GetColor( 2229 security_level_, LocationBarView::SECURITY_TEXT)); 2230 SetSelection(scheme.begin, scheme.end()); 2231 SetSelectionCharFormat(cf); 2232 } 2233 2234 // Restore the selection. 2235 SetSelectionRange(saved_sel); 2236 } 2237 2238 void AutocompleteEditViewWin::EraseTopOfSelection( 2239 CDC* dc, const CRect& client_rect, const CRect& paint_clip_rect) { 2240 // Find the area we care about painting. We could calculate the rect 2241 // containing just the selected portion, but there's no harm in simply erasing 2242 // the whole top of the client area, and at least once I saw us manage to 2243 // select the "phantom newline" briefly, which looks very weird if not clipped 2244 // off at the same height. 2245 CRect erase_rect(client_rect.left, client_rect.top, client_rect.right, 2246 client_rect.top + font_y_adjustment_); 2247 erase_rect.IntersectRect(erase_rect, paint_clip_rect); 2248 2249 // Erase to the background color. 2250 if (!erase_rect.IsRectNull()) 2251 dc->FillSolidRect(&erase_rect, background_color_); 2252 } 2253 2254 void AutocompleteEditViewWin::DrawSlashForInsecureScheme( 2255 HDC hdc, 2256 const CRect& client_rect, 2257 const CRect& paint_clip_rect) { 2258 DCHECK(insecure_scheme_component_.is_nonempty()); 2259 2260 // Calculate the rect, in window coordinates, containing the portion of the 2261 // scheme where we'll be drawing the slash. Vertically, we draw across one 2262 // x-height of text, plus an additional 3 stroke diameters (the stroke width 2263 // plus a half-stroke width of space between the stroke and the text, both 2264 // above and below the text). 2265 const int font_top = client_rect.top + font_y_adjustment_; 2266 const SkScalar kStrokeWidthPixels = SkIntToScalar(2); 2267 const int kAdditionalSpaceOutsideFont = 2268 static_cast<int>(ceil(kStrokeWidthPixels * 1.5f)); 2269 const CRect scheme_rect(PosFromChar(insecure_scheme_component_.begin).x, 2270 font_top + font_.GetBaseline() - font_x_height_ - 2271 kAdditionalSpaceOutsideFont, 2272 PosFromChar(insecure_scheme_component_.end()).x, 2273 font_top + font_.GetBaseline() + 2274 kAdditionalSpaceOutsideFont); 2275 2276 // Clip to the portion we care about and translate to canvas coordinates 2277 // (see the canvas creation below) for use later. 2278 CRect canvas_clip_rect, canvas_paint_clip_rect; 2279 canvas_clip_rect.IntersectRect(scheme_rect, client_rect); 2280 canvas_paint_clip_rect.IntersectRect(canvas_clip_rect, paint_clip_rect); 2281 if (canvas_paint_clip_rect.IsRectNull()) 2282 return; // We don't need to paint any of this region, so just bail early. 2283 canvas_clip_rect.OffsetRect(-scheme_rect.left, -scheme_rect.top); 2284 canvas_paint_clip_rect.OffsetRect(-scheme_rect.left, -scheme_rect.top); 2285 2286 // Create a paint context for drawing the antialiased stroke. 2287 SkPaint paint; 2288 paint.setAntiAlias(true); 2289 paint.setStrokeWidth(kStrokeWidthPixels); 2290 paint.setStrokeCap(SkPaint::kRound_Cap); 2291 2292 // Create a canvas as large as |scheme_rect| to do our drawing, and initialize 2293 // it to fully transparent so any antialiasing will look nice when painted 2294 // atop the edit. 2295 gfx::CanvasSkia canvas(scheme_rect.Width(), scheme_rect.Height(), false); 2296 canvas.getDevice()->accessBitmap(true).eraseARGB(0, 0, 0, 0); 2297 2298 // Calculate the start and end of the stroke, which are just the lower left 2299 // and upper right corners of the canvas, inset by the radius of the endcap 2300 // so we don't clip the endcap off. 2301 const SkScalar kEndCapRadiusPixels = kStrokeWidthPixels / SkIntToScalar(2); 2302 const SkPoint start_point = { 2303 kEndCapRadiusPixels, 2304 SkIntToScalar(scheme_rect.Height()) - kEndCapRadiusPixels }; 2305 const SkPoint end_point = { 2306 SkIntToScalar(scheme_rect.Width()) - kEndCapRadiusPixels, 2307 kEndCapRadiusPixels }; 2308 2309 // Calculate the selection rectangle in canvas coordinates, which we'll use 2310 // to clip the stroke so we can draw the unselected and selected portions. 2311 CHARRANGE sel; 2312 GetSel(sel); 2313 const SkRect selection_rect = { 2314 SkIntToScalar(PosFromChar(sel.cpMin).x - scheme_rect.left), 2315 SkIntToScalar(0), 2316 SkIntToScalar(PosFromChar(sel.cpMax).x - scheme_rect.left), 2317 SkIntToScalar(scheme_rect.Height()) }; 2318 2319 // Draw the unselected portion of the stroke. 2320 canvas.save(); 2321 if (selection_rect.isEmpty() || 2322 canvas.clipRect(selection_rect, SkRegion::kDifference_Op)) { 2323 paint.setColor(LocationBarView::GetColor(security_level_, 2324 LocationBarView::SECURITY_TEXT)); 2325 canvas.drawLine(start_point.fX, start_point.fY, 2326 end_point.fX, end_point.fY, paint); 2327 } 2328 canvas.restore(); 2329 2330 // Draw the selected portion of the stroke. 2331 if (!selection_rect.isEmpty() && canvas.clipRect(selection_rect)) { 2332 paint.setColor(LocationBarView::GetColor(security_level_, 2333 LocationBarView::SELECTED_TEXT)); 2334 canvas.drawLine(start_point.fX, start_point.fY, 2335 end_point.fX, end_point.fY, paint); 2336 } 2337 2338 // Now copy what we drew to the target HDC. 2339 canvas.getTopPlatformDevice().drawToHDC(hdc, 2340 scheme_rect.left + canvas_paint_clip_rect.left - canvas_clip_rect.left, 2341 std::max(scheme_rect.top, client_rect.top) + canvas_paint_clip_rect.top - 2342 canvas_clip_rect.top, &canvas_paint_clip_rect); 2343 } 2344 2345 void AutocompleteEditViewWin::DrawDropHighlight(HDC hdc, 2346 const CRect& client_rect, 2347 const CRect& paint_clip_rect) { 2348 DCHECK_NE(-1, drop_highlight_position_); 2349 2350 const int highlight_y = client_rect.top + font_y_adjustment_; 2351 const int highlight_x = PosFromChar(drop_highlight_position_).x - 1; 2352 const CRect highlight_rect(highlight_x, 2353 highlight_y, 2354 highlight_x + 1, 2355 highlight_y + font_.GetHeight()); 2356 2357 // Clip the highlight to the region being painted. 2358 CRect clip_rect; 2359 clip_rect.IntersectRect(highlight_rect, paint_clip_rect); 2360 if (clip_rect.IsRectNull()) 2361 return; 2362 2363 HGDIOBJ last_pen = SelectObject(hdc, CreatePen(PS_SOLID, 1, RGB(0, 0, 0))); 2364 MoveToEx(hdc, clip_rect.left, clip_rect.top, NULL); 2365 LineTo(hdc, clip_rect.left, clip_rect.bottom); 2366 DeleteObject(SelectObject(hdc, last_pen)); 2367 } 2368 2369 void AutocompleteEditViewWin::TextChanged() { 2370 ScopedFreeze freeze(this, GetTextObjectModel()); 2371 EmphasizeURLComponents(); 2372 model_->OnChanged(); 2373 } 2374 2375 string16 AutocompleteEditViewWin::GetClipboardText() const { 2376 // Try text format. 2377 ui::Clipboard* clipboard = g_browser_process->clipboard(); 2378 if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(), 2379 ui::Clipboard::BUFFER_STANDARD)) { 2380 string16 text; 2381 clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &text); 2382 2383 // Note: Unlike in the find popup and textfield view, here we completely 2384 // remove whitespace strings containing newlines. We assume users are 2385 // most likely pasting in URLs that may have been split into multiple 2386 // lines in terminals, email programs, etc., and so linebreaks indicate 2387 // completely bogus whitespace that would just cause the input to be 2388 // invalid. 2389 return CollapseWhitespace(text, true); 2390 } 2391 2392 // Try bookmark format. 2393 // 2394 // It is tempting to try bookmark format first, but the URL we get out of a 2395 // bookmark has been cannonicalized via GURL. This means if a user copies 2396 // and pastes from the URL bar to itself, the text will get fixed up and 2397 // cannonicalized, which is not what the user expects. By pasting in this 2398 // order, we are sure to paste what the user copied. 2399 if (clipboard->IsFormatAvailable(ui::Clipboard::GetUrlWFormatType(), 2400 ui::Clipboard::BUFFER_STANDARD)) { 2401 std::string url_str; 2402 clipboard->ReadBookmark(NULL, &url_str); 2403 // pass resulting url string through GURL to normalize 2404 GURL url(url_str); 2405 if (url.is_valid()) 2406 return UTF8ToWide(url.spec()); 2407 } 2408 2409 return string16(); 2410 } 2411 2412 bool AutocompleteEditViewWin::CanPasteAndGo(const string16& text) const { 2413 return !popup_window_mode_ && model_->CanPasteAndGo(text); 2414 } 2415 2416 ITextDocument* AutocompleteEditViewWin::GetTextObjectModel() const { 2417 if (!text_object_model_) { 2418 // This is lazily initialized, instead of being initialized in the 2419 // constructor, in order to avoid hurting startup performance. 2420 base::win::ScopedComPtr<IRichEditOle, NULL> ole_interface; 2421 ole_interface.Attach(GetOleInterface()); 2422 if (ole_interface) { 2423 ole_interface.QueryInterface( 2424 __uuidof(ITextDocument), 2425 reinterpret_cast<void**>(&text_object_model_)); 2426 } 2427 } 2428 return text_object_model_; 2429 } 2430 2431 void AutocompleteEditViewWin::StartDragIfNecessary(const CPoint& point) { 2432 if (initiated_drag_ || !IsDrag(click_point_[kLeft], point)) 2433 return; 2434 2435 ui::OSExchangeData data; 2436 2437 DWORD supported_modes = DROPEFFECT_COPY; 2438 2439 CHARRANGE sel; 2440 GetSelection(sel); 2441 2442 // We're about to start a drag session, but the edit is expecting a mouse up 2443 // that it uses to reset internal state. If we don't send a mouse up now, 2444 // when the mouse moves back into the edit the edit will reset the selection. 2445 // So, we send the event now which resets the selection. We then restore the 2446 // selection and start the drag. We always send lbuttonup as otherwise we 2447 // might trigger a context menu (right up). This seems scary, but doesn't 2448 // seem to cause problems. 2449 { 2450 ScopedFreeze freeze(this, GetTextObjectModel()); 2451 DefWindowProc(WM_LBUTTONUP, 0, 2452 MAKELPARAM(click_point_[kLeft].x, click_point_[kLeft].y)); 2453 SetSelectionRange(sel); 2454 } 2455 2456 const string16 start_text(GetText()); 2457 string16 text_to_write(GetSelectedText()); 2458 GURL url; 2459 bool write_url; 2460 const bool is_all_selected = IsSelectAllForRange(sel); 2461 2462 // |sel| was set by GetSelection(), which preserves selection direction, so 2463 // sel.cpMin may not be the smaller value. 2464 model()->AdjustTextForCopy(std::min(sel.cpMin, sel.cpMax), is_all_selected, 2465 &text_to_write, &url, &write_url); 2466 2467 if (write_url) { 2468 string16 title; 2469 SkBitmap favicon; 2470 if (is_all_selected) 2471 model_->GetDataForURLExport(&url, &title, &favicon); 2472 drag_utils::SetURLAndDragImage(url, title, favicon, &data); 2473 supported_modes |= DROPEFFECT_LINK; 2474 UserMetrics::RecordAction(UserMetricsAction("Omnibox_DragURL"), 2475 model_->profile()); 2476 } else { 2477 supported_modes |= DROPEFFECT_MOVE; 2478 UserMetrics::RecordAction(UserMetricsAction("Omnibox_DragString"), 2479 model_->profile()); 2480 } 2481 2482 data.SetString(text_to_write); 2483 2484 scoped_refptr<ui::DragSource> drag_source(new ui::DragSource); 2485 DWORD dropped_mode; 2486 AutoReset<bool> auto_reset_in_drag(&in_drag_, true); 2487 if (DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data), 2488 drag_source, supported_modes, &dropped_mode) == 2489 DRAGDROP_S_DROP) { 2490 if ((dropped_mode == DROPEFFECT_MOVE) && (start_text == GetText())) { 2491 ScopedFreeze freeze(this, GetTextObjectModel()); 2492 OnBeforePossibleChange(); 2493 SetSelectionRange(sel); 2494 ReplaceSel(L"", true); 2495 OnAfterPossibleChange(); 2496 } 2497 // else case, not a move or it was a move and the drop was on us. 2498 // If the drop was on us, EditDropTarget took care of the move so that 2499 // we don't have to delete the text. 2500 possible_drag_ = false; 2501 } else { 2502 // Drag was canceled or failed. The mouse may still be down and 2503 // over us, in which case we need possible_drag_ to remain true so 2504 // that we don't forward mouse move events to the edit which will 2505 // start another drag. 2506 // 2507 // NOTE: we didn't use mouse capture during the mouse down as DoDragDrop 2508 // does its own capture. 2509 CPoint cursor_location; 2510 GetCursorPos(&cursor_location); 2511 2512 CRect client_rect; 2513 GetClientRect(&client_rect); 2514 2515 CPoint client_origin_on_screen(client_rect.left, client_rect.top); 2516 ClientToScreen(&client_origin_on_screen); 2517 client_rect.MoveToXY(client_origin_on_screen.x, 2518 client_origin_on_screen.y); 2519 possible_drag_ = (client_rect.PtInRect(cursor_location) && 2520 ((GetKeyState(VK_LBUTTON) != 0) || 2521 (GetKeyState(VK_MBUTTON) != 0) || 2522 (GetKeyState(VK_RBUTTON) != 0))); 2523 } 2524 2525 initiated_drag_ = true; 2526 tracking_click_[kLeft] = false; 2527 } 2528 2529 void AutocompleteEditViewWin::OnPossibleDrag(const CPoint& point) { 2530 if (possible_drag_) 2531 return; 2532 2533 click_point_[kLeft] = point; 2534 initiated_drag_ = false; 2535 2536 CHARRANGE selection; 2537 GetSel(selection); 2538 if (selection.cpMin != selection.cpMax) { 2539 const POINT min_sel_location(PosFromChar(selection.cpMin)); 2540 const POINT max_sel_location(PosFromChar(selection.cpMax)); 2541 // NOTE: we don't consider the y location here as we always pass a 2542 // y-coordinate in the middle to the default handler which always triggers 2543 // a drag regardless of the y-coordinate. 2544 possible_drag_ = (point.x >= min_sel_location.x) && 2545 (point.x < max_sel_location.x); 2546 } 2547 } 2548 2549 void AutocompleteEditViewWin::RepaintDropHighlight(int position) { 2550 if ((position != -1) && (position <= GetTextLength())) { 2551 const POINT min_loc(PosFromChar(position)); 2552 const RECT highlight_bounds = {min_loc.x - 1, font_y_adjustment_, 2553 min_loc.x + 2, font_.GetHeight() + font_y_adjustment_}; 2554 InvalidateRect(&highlight_bounds, false); 2555 } 2556 } 2557 2558 void AutocompleteEditViewWin::BuildContextMenu() { 2559 if (context_menu_contents_.get()) 2560 return; 2561 2562 context_menu_contents_.reset(new ui::SimpleMenuModel(this)); 2563 // Set up context menu. 2564 if (popup_window_mode_) { 2565 context_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY); 2566 } else { 2567 context_menu_contents_->AddItemWithStringId(IDS_UNDO, IDS_UNDO); 2568 context_menu_contents_->AddSeparator(); 2569 context_menu_contents_->AddItemWithStringId(IDC_CUT, IDS_CUT); 2570 context_menu_contents_->AddItemWithStringId(IDC_COPY, IDS_COPY); 2571 context_menu_contents_->AddItemWithStringId(IDC_PASTE, IDS_PASTE); 2572 // GetContextualLabel() will override this next label with the 2573 // IDS_PASTE_AND_SEARCH label as needed. 2574 context_menu_contents_->AddItemWithStringId(IDS_PASTE_AND_GO, 2575 IDS_PASTE_AND_GO); 2576 context_menu_contents_->AddSeparator(); 2577 context_menu_contents_->AddItemWithStringId(IDS_SELECT_ALL, IDS_SELECT_ALL); 2578 context_menu_contents_->AddSeparator(); 2579 context_menu_contents_->AddItemWithStringId(IDS_EDIT_SEARCH_ENGINES, 2580 IDS_EDIT_SEARCH_ENGINES); 2581 } 2582 context_menu_.reset(new views::Menu2(context_menu_contents_.get())); 2583 } 2584 2585 void AutocompleteEditViewWin::SelectAllIfNecessary(MouseButton button, 2586 const CPoint& point) { 2587 // When the user has clicked and released to give us focus, select all. 2588 if (tracking_click_[button] && 2589 !IsDrag(click_point_[button], point)) { 2590 // Select all in the reverse direction so as not to scroll the caret 2591 // into view and shift the contents jarringly. 2592 SelectAll(true); 2593 possible_drag_ = false; 2594 } 2595 } 2596 2597 void AutocompleteEditViewWin::TrackMousePosition(MouseButton button, 2598 const CPoint& point) { 2599 if (gaining_focus_.get()) { 2600 // This click is giving us focus, so we need to track how much the mouse 2601 // moves to see if it's a drag or just a click. Clicks should select all 2602 // the text. 2603 tracking_click_[button] = true; 2604 click_point_[button] = point; 2605 } 2606 } 2607 2608 int AutocompleteEditViewWin::GetHorizontalMargin() const { 2609 RECT rect; 2610 GetRect(&rect); 2611 RECT client_rect; 2612 GetClientRect(&client_rect); 2613 return (rect.left - client_rect.left) + (client_rect.right - rect.right); 2614 } 2615 2616 int AutocompleteEditViewWin::WidthNeededToDisplay( 2617 const string16& text) const { 2618 // Use font_.GetStringWidth() instead of 2619 // PosFromChar(location_entry_->GetTextLength()) because PosFromChar() is 2620 // apparently buggy. In both LTR UI and RTL UI with left-to-right layout, 2621 // PosFromChar(i) might return 0 when i is greater than 1. 2622 return font_.GetStringWidth(text) + GetHorizontalMargin(); 2623 } 2624 2625 bool AutocompleteEditViewWin::IsCaretAtEnd() const { 2626 long length = GetTextLength(); 2627 CHARRANGE sel; 2628 GetSelection(sel); 2629 return sel.cpMin == sel.cpMax && sel.cpMin == length; 2630 } 2631