autocomplete_edit_view_gtk.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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_gtk.h"
6
7#include <gtk/gtk.h>
8#include <gdk/gdkkeysyms.h>
9
10#include <algorithm>
11
12#include "app/l10n_util.h"
13#include "base/logging.h"
14#include "base/string_util.h"
15#include "base/utf_string_conversions.h"
16#include "chrome/app/chrome_command_ids.h"
17#include "chrome/browser/autocomplete/autocomplete_edit.h"
18#include "chrome/browser/autocomplete/autocomplete_match.h"
19#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
20#include "chrome/browser/bookmarks/bookmark_drag_data.h"
21#include "chrome/browser/browser_process.h"
22#include "chrome/browser/command_updater.h"
23#include "chrome/browser/defaults.h"
24#include "chrome/browser/gtk/gtk_util.h"
25#include "chrome/browser/gtk/view_id_util.h"
26#include "chrome/browser/platform_util.h"
27#include "chrome/browser/tab_contents/tab_contents.h"
28#include "chrome/browser/toolbar_model.h"
29#include "chrome/common/notification_service.h"
30#include "gfx/font.h"
31#include "gfx/gtk_util.h"
32#include "gfx/skia_utils_gtk.h"
33#include "googleurl/src/gurl.h"
34#include "grit/generated_resources.h"
35#include "net/base/escape.h"
36#include "third_party/undoview/undo_view.h"
37
38#if defined(TOOLKIT_VIEWS)
39#include "chrome/browser/views/autocomplete/autocomplete_popup_contents_view.h"
40#include "chrome/browser/views/location_bar/location_bar_view.h"
41#else
42#include "chrome/browser/autocomplete/autocomplete_popup_view_gtk.h"
43#include "chrome/browser/gtk/gtk_theme_provider.h"
44#include "chrome/browser/gtk/location_bar_view_gtk.h"
45#endif
46
47namespace {
48
49const gchar* kAutocompleteEditViewGtkKey = "__ACE_VIEW_GTK__";
50
51const char kTextBaseColor[] = "#808080";
52const char kSecureSchemeColor[] = "#079500";
53const char kSecurityErrorSchemeColor[] = "#a20000";
54
55const double kStrikethroughStrokeRed = 162.0 / 256.0;
56const double kStrikethroughStrokeWidth = 2.0;
57
58size_t GetUTF8Offset(const std::wstring& wide_text, size_t wide_text_offset) {
59  return WideToUTF8(wide_text.substr(0, wide_text_offset)).size();
60}
61
62// Stores GTK+-specific state so it can be restored after switching tabs.
63struct ViewState {
64  explicit ViewState(const AutocompleteEditViewGtk::CharRange& selection_range)
65      : selection_range(selection_range) {
66  }
67
68  // Range of selected text.
69  AutocompleteEditViewGtk::CharRange selection_range;
70};
71
72struct AutocompleteEditState {
73  AutocompleteEditState(const AutocompleteEditModel::State& model_state,
74                        const ViewState& view_state)
75      : model_state(model_state),
76        view_state(view_state) {
77  }
78
79  const AutocompleteEditModel::State model_state;
80  const ViewState view_state;
81};
82
83// Returns a lazily initialized property bag accessor for saving our state in a
84// TabContents.
85PropertyAccessor<AutocompleteEditState>* GetStateAccessor() {
86  static PropertyAccessor<AutocompleteEditState> state;
87  return &state;
88}
89
90// Set up style properties to override the default GtkTextView; if a theme has
91// overridden some of these properties, an inner-line will be displayed inside
92// the fake GtkTextEntry.
93void SetEntryStyle() {
94  static bool style_was_set = false;
95
96  if (style_was_set)
97    return;
98  style_was_set = true;
99
100  gtk_rc_parse_string(
101      "style \"chrome-location-bar-entry\" {"
102      "  xthickness = 0\n"
103      "  ythickness = 0\n"
104      "  GtkWidget::focus_padding = 0\n"
105      "  GtkWidget::focus-line-width = 0\n"
106      "  GtkWidget::interior_focus = 0\n"
107      "  GtkWidget::internal-padding = 0\n"
108      "  GtkContainer::border-width = 0\n"
109      "}\n"
110      "widget \"*chrome-location-bar-entry\" "
111      "style \"chrome-location-bar-entry\"");
112}
113
114// Copied from GTK+. Called when we lose the primary selection. This will clear
115// the selection in the text buffer.
116void ClipboardSelectionCleared(GtkClipboard* clipboard,
117                               gpointer data) {
118  GtkTextIter insert;
119  GtkTextIter selection_bound;
120  GtkTextBuffer* buffer = GTK_TEXT_BUFFER(data);
121
122  gtk_text_buffer_get_iter_at_mark(buffer, &insert,
123                                   gtk_text_buffer_get_insert(buffer));
124  gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound,
125                                   gtk_text_buffer_get_selection_bound(buffer));
126
127  if (!gtk_text_iter_equal(&insert, &selection_bound)) {
128    gtk_text_buffer_move_mark(buffer,
129                              gtk_text_buffer_get_selection_bound(buffer),
130                              &insert);
131  }
132}
133
134}  // namespace
135
136AutocompleteEditViewGtk::AutocompleteEditViewGtk(
137    AutocompleteEditController* controller,
138    ToolbarModel* toolbar_model,
139    Profile* profile,
140    CommandUpdater* command_updater,
141    bool popup_window_mode,
142#if defined(TOOLKIT_VIEWS)
143    const views::View* location_bar)
144#else
145    GtkWidget* location_bar)
146#endif
147    : text_view_(NULL),
148      tag_table_(NULL),
149      text_buffer_(NULL),
150      faded_text_tag_(NULL),
151      secure_scheme_tag_(NULL),
152      security_error_scheme_tag_(NULL),
153      normal_text_tag_(NULL),
154      instant_anchor_tag_(NULL),
155      instant_view_(NULL),
156      instant_mark_(NULL),
157      model_(new AutocompleteEditModel(this, controller, profile)),
158#if defined(TOOLKIT_VIEWS)
159      popup_view_(new AutocompletePopupContentsView(
160          gfx::Font(), this, model_.get(), profile, location_bar)),
161#else
162      popup_view_(new AutocompletePopupViewGtk(this, model_.get(), profile,
163                                               location_bar)),
164#endif
165      controller_(controller),
166      toolbar_model_(toolbar_model),
167      command_updater_(command_updater),
168      popup_window_mode_(popup_window_mode),
169      security_level_(ToolbarModel::NONE),
170      mark_set_handler_id_(0),
171#if defined(OS_CHROMEOS)
172      button_1_pressed_(false),
173      text_selected_during_click_(false),
174      text_view_focused_before_button_press_(false),
175#endif
176#if !defined(TOOLKIT_VIEWS)
177      theme_provider_(GtkThemeProvider::GetFrom(profile)),
178#endif
179      enter_was_pressed_(false),
180      tab_was_pressed_(false),
181      paste_clipboard_requested_(false),
182      enter_was_inserted_(false),
183      enable_tab_to_search_(true),
184      selection_suggested_(false),
185      going_to_focus_(NULL) {
186  model_->SetPopupModel(popup_view_->GetModel());
187}
188
189AutocompleteEditViewGtk::~AutocompleteEditViewGtk() {
190  NotificationService::current()->Notify(
191      NotificationType::AUTOCOMPLETE_EDIT_DESTROYED,
192      Source<AutocompleteEditViewGtk>(this),
193      NotificationService::NoDetails());
194
195  // Explicitly teardown members which have a reference to us.  Just to be safe
196  // we want them to be destroyed before destroying any other internal state.
197  popup_view_.reset();
198  model_.reset();
199
200  // We own our widget and TextView related objects.
201  if (alignment_.get()) {  // Init() has been called.
202    alignment_.Destroy();
203    g_object_unref(text_buffer_);
204    g_object_unref(tag_table_);
205    // The tags we created are owned by the tag_table, and should be destroyed
206    // along with it.  We don't hold our own reference to them.
207  }
208}
209
210void AutocompleteEditViewGtk::Init() {
211  SetEntryStyle();
212
213  // The height of the text view is going to change based on the font used.  We
214  // don't want to stretch the height, and we want it vertically centered.
215  alignment_.Own(gtk_alignment_new(0., 0.5, 1.0, 0.0));
216  gtk_widget_set_name(alignment_.get(),
217                      "chrome-autocomplete-edit-view");
218
219  // The GtkTagTable and GtkTextBuffer are not initially unowned, so we have
220  // our own reference when we create them, and we own them.  Adding them to
221  // the other objects adds a reference; it doesn't adopt them.
222  tag_table_ = gtk_text_tag_table_new();
223  text_buffer_ = gtk_text_buffer_new(tag_table_);
224  g_object_set_data(G_OBJECT(text_buffer_), kAutocompleteEditViewGtkKey, this);
225
226  // We need to run this two handlers before undo manager's handlers, so that
227  // text iterators modified by these handlers can be passed down to undo
228  // manager's handlers.
229  g_signal_connect(text_buffer_, "delete-range",
230                   G_CALLBACK(&HandleDeleteRangeThunk), this);
231  g_signal_connect(text_buffer_, "mark-set",
232                   G_CALLBACK(&HandleMarkSetAlwaysThunk), this);
233
234  text_view_ = gtk_undo_view_new(text_buffer_);
235  if (popup_window_mode_)
236    gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view_), false);
237
238  // One pixel left margin is necessary to make the cursor visible when UI
239  // language direction is LTR but |text_buffer_|'s content direction is RTL.
240  gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view_), 1);
241
242  // See SetEntryStyle() comments.
243  gtk_widget_set_name(text_view_, "chrome-location-bar-entry");
244
245  // The text view was floating.  It will now be owned by the alignment.
246  gtk_container_add(GTK_CONTAINER(alignment_.get()), text_view_);
247
248  // Do not allow inserting tab characters when pressing Tab key, so that when
249  // Tab key is pressed, |text_view_| will emit "move-focus" signal, which will
250  // be intercepted by our own handler to trigger Tab to search feature when
251  // necessary.
252  gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(text_view_), FALSE);
253
254  faded_text_tag_ = gtk_text_buffer_create_tag(text_buffer_,
255      NULL, "foreground", kTextBaseColor, NULL);
256  secure_scheme_tag_ = gtk_text_buffer_create_tag(text_buffer_,
257      NULL, "foreground", kSecureSchemeColor, NULL);
258  security_error_scheme_tag_ = gtk_text_buffer_create_tag(text_buffer_,
259      NULL, "foreground", kSecurityErrorSchemeColor, NULL);
260  normal_text_tag_ = gtk_text_buffer_create_tag(text_buffer_,
261      NULL, "foreground", "#000000", NULL);
262
263  // NOTE: This code used to connect to "changed", however this was fired too
264  // often and during bad times (our own buffer changes?).  It works out much
265  // better to listen to end-user-action, which should be fired whenever the
266  // user makes some sort of change to the buffer.
267  g_signal_connect(text_buffer_, "begin-user-action",
268                   G_CALLBACK(&HandleBeginUserActionThunk), this);
269  g_signal_connect(text_buffer_, "end-user-action",
270                   G_CALLBACK(&HandleEndUserActionThunk), this);
271  // We connect to key press and release for special handling of a few keys.
272  g_signal_connect(text_view_, "key-press-event",
273                   G_CALLBACK(&HandleKeyPressThunk), this);
274  g_signal_connect(text_view_, "key-release-event",
275                   G_CALLBACK(&HandleKeyReleaseThunk), this);
276  g_signal_connect(text_view_, "button-press-event",
277                   G_CALLBACK(&HandleViewButtonPressThunk), this);
278  g_signal_connect(text_view_, "button-release-event",
279                   G_CALLBACK(&HandleViewButtonReleaseThunk), this);
280  g_signal_connect(text_view_, "focus-in-event",
281                   G_CALLBACK(&HandleViewFocusInThunk), this);
282  g_signal_connect(text_view_, "focus-out-event",
283                   G_CALLBACK(&HandleViewFocusOutThunk), this);
284  // NOTE: The GtkTextView documentation asks you not to connect to this
285  // signal, but it is very convenient and clean for catching up/down.
286  g_signal_connect(text_view_, "move-cursor",
287                   G_CALLBACK(&HandleViewMoveCursorThunk), this);
288  g_signal_connect(text_view_, "move-focus",
289                   G_CALLBACK(&HandleViewMoveFocusThunk), this);
290  // Override the size request.  We want to keep the original height request
291  // from the widget, since that's font dependent.  We want to ignore the width
292  // so we don't force a minimum width based on the text length.
293  g_signal_connect(text_view_, "size-request",
294                   G_CALLBACK(&HandleViewSizeRequestThunk), this);
295  g_signal_connect(text_view_, "populate-popup",
296                   G_CALLBACK(&HandlePopulatePopupThunk), this);
297  mark_set_handler_id_ = g_signal_connect(
298      text_buffer_, "mark-set", G_CALLBACK(&HandleMarkSetThunk), this);
299  mark_set_handler_id2_ = g_signal_connect_after(
300      text_buffer_, "mark-set", G_CALLBACK(&HandleMarkSetAfterThunk), this);
301  g_signal_connect(text_view_, "drag-data-received",
302                   G_CALLBACK(&HandleDragDataReceivedThunk), this);
303  // Override the text_view_'s default drag-data-get handler by calling our own
304  // version after the normal call has happened.
305  g_signal_connect_after(text_view_, "drag-data-get",
306                   G_CALLBACK(&HandleDragDataGetThunk), this);
307  g_signal_connect(text_view_, "backspace",
308                   G_CALLBACK(&HandleBackSpaceThunk), this);
309  g_signal_connect(text_view_, "copy-clipboard",
310                   G_CALLBACK(&HandleCopyClipboardThunk), this);
311  g_signal_connect(text_view_, "cut-clipboard",
312                   G_CALLBACK(&HandleCutClipboardThunk), this);
313  g_signal_connect(text_view_, "paste-clipboard",
314                   G_CALLBACK(&HandlePasteClipboardThunk), this);
315  g_signal_connect_after(text_view_, "expose-event",
316                         G_CALLBACK(&HandleExposeEventThunk), this);
317  g_signal_connect(text_view_, "direction-changed",
318                   G_CALLBACK(&HandleWidgetDirectionChangedThunk), this);
319  g_signal_connect(text_view_, "delete-from-cursor",
320                   G_CALLBACK(&HandleDeleteFromCursorThunk), this);
321  g_signal_connect(text_view_, "hierarchy-changed",
322                   G_CALLBACK(&HandleHierarchyChangedThunk), this);
323#if GTK_CHECK_VERSION(2, 20, 0)
324  g_signal_connect(text_view_, "preedit-changed",
325                   G_CALLBACK(&HandlePreeditChangedThunk), this);
326#endif
327  g_signal_connect(text_view_, "undo", G_CALLBACK(&HandleUndoRedoThunk), this);
328  g_signal_connect(text_view_, "redo", G_CALLBACK(&HandleUndoRedoThunk), this);
329  g_signal_connect_after(text_view_, "undo",
330                         G_CALLBACK(&HandleUndoRedoAfterThunk), this);
331  g_signal_connect_after(text_view_, "redo",
332                         G_CALLBACK(&HandleUndoRedoAfterThunk), this);
333
334  // Setup for the Instant suggestion text view.
335  // GtkLabel is used instead of GtkTextView to get transparent background.
336  instant_view_ = gtk_label_new(NULL);
337
338  GtkTextIter end_iter;
339  gtk_text_buffer_get_end_iter(text_buffer_, &end_iter);
340
341  // Insert a Zero Width Space character just before the instant anchor.
342  // It's a hack to workaround a bug of GtkTextView which can not align the
343  // preedit string and a child anchor correctly when there is no other content
344  // around the preedit string.
345  gtk_text_buffer_insert(text_buffer_, &end_iter, "\342\200\213", -1);
346  GtkTextChildAnchor* instant_anchor =
347      gtk_text_buffer_create_child_anchor(text_buffer_, &end_iter);
348
349  gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(text_view_),
350                                    instant_view_,
351                                    instant_anchor);
352
353  instant_anchor_tag_ = gtk_text_buffer_create_tag(text_buffer_, NULL, NULL);
354
355  GtkTextIter anchor_iter;
356  gtk_text_buffer_get_iter_at_child_anchor(text_buffer_, &anchor_iter,
357                                           instant_anchor);
358  gtk_text_buffer_apply_tag(text_buffer_, instant_anchor_tag_,
359                            &anchor_iter, &end_iter);
360
361  GtkTextIter start_iter;
362  gtk_text_buffer_get_start_iter(text_buffer_, &start_iter);
363  instant_mark_ =
364      gtk_text_buffer_create_mark(text_buffer_, NULL, &start_iter, FALSE);
365
366  // Hooking up this handler after setting up above hacks for Instant view, so
367  // that we won't filter out the special ZWP mark itself.
368  g_signal_connect(text_buffer_, "insert-text",
369                   G_CALLBACK(&HandleInsertTextThunk), this);
370
371  AdjustVerticalAlignmentOfInstantView();
372
373#if !defined(TOOLKIT_VIEWS)
374  registrar_.Add(this,
375                 NotificationType::BROWSER_THEME_CHANGED,
376                 NotificationService::AllSources());
377  theme_provider_->InitThemesFor(this);
378#else
379  // Manually invoke SetBaseColor() because TOOLKIT_VIEWS doesn't observe
380  // themes.
381  SetBaseColor();
382#endif
383
384  ViewIDUtil::SetID(GetNativeView(), VIEW_ID_AUTOCOMPLETE);
385}
386
387void AutocompleteEditViewGtk::HandleHierarchyChanged(
388    GtkWidget* sender, GtkWidget* old_toplevel) {
389  GtkWindow* new_toplevel = platform_util::GetTopLevel(sender);
390  if (!new_toplevel)
391    return;
392
393  // Use |signals_| to make sure we don't get called back after destruction.
394  signals_.Connect(new_toplevel, "set-focus",
395                   G_CALLBACK(&HandleWindowSetFocusThunk), this);
396}
397
398void AutocompleteEditViewGtk::SetFocus() {
399  gtk_widget_grab_focus(text_view_);
400}
401
402int AutocompleteEditViewGtk::TextWidth() {
403  int horizontal_border_size =
404      gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(text_view_),
405                                           GTK_TEXT_WINDOW_LEFT) +
406      gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(text_view_),
407                                           GTK_TEXT_WINDOW_RIGHT) +
408      gtk_text_view_get_left_margin(GTK_TEXT_VIEW(text_view_)) +
409      gtk_text_view_get_right_margin(GTK_TEXT_VIEW(text_view_));
410
411  GtkTextIter start, end;
412  GdkRectangle first_char_bounds, last_char_bounds;
413  gtk_text_buffer_get_start_iter(text_buffer_, &start);
414
415  // Use the real end iterator here to take the width of instant suggestion
416  // text into account, so that location bar can layout its children correctly.
417  gtk_text_buffer_get_end_iter(text_buffer_, &end);
418  gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_),
419                                  &start, &first_char_bounds);
420  gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_),
421                                  &end, &last_char_bounds);
422  return ((last_char_bounds.x > first_char_bounds.x) ?
423          (last_char_bounds.x + last_char_bounds.width - first_char_bounds.x) :
424          (first_char_bounds.x - last_char_bounds.x + last_char_bounds.width)) +
425      horizontal_border_size;
426}
427
428int AutocompleteEditViewGtk::WidthOfTextAfterCursor() {
429  // Not used.
430  return -1;
431}
432
433gfx::Font AutocompleteEditViewGtk::GetFont() {
434  GtkRcStyle* rc_style = gtk_widget_get_modifier_style(text_view_);
435  return gfx::Font((rc_style && rc_style->font_desc) ?
436                   rc_style->font_desc : text_view_->style->font_desc);
437}
438
439void AutocompleteEditViewGtk::SaveStateToTab(TabContents* tab) {
440  DCHECK(tab);
441  // If any text has been selected, register it as the PRIMARY selection so it
442  // can still be pasted via middle-click after the text view is cleared.
443  if (!selected_text_.empty())
444    SavePrimarySelection(selected_text_);
445  // NOTE: GetStateForTabSwitch may affect GetSelection, so order is important.
446  AutocompleteEditModel::State model_state = model_->GetStateForTabSwitch();
447  GetStateAccessor()->SetProperty(
448      tab->property_bag(),
449      AutocompleteEditState(model_state, ViewState(GetSelection())));
450}
451
452void AutocompleteEditViewGtk::Update(const TabContents* contents) {
453  // NOTE: We're getting the URL text here from the ToolbarModel.
454  bool visibly_changed_permanent_text =
455      model_->UpdatePermanentText(toolbar_model_->GetText());
456
457  ToolbarModel::SecurityLevel security_level =
458        toolbar_model_->GetSecurityLevel();
459  bool changed_security_level = (security_level != security_level_);
460  security_level_ = security_level;
461
462  if (contents) {
463    selected_text_.clear();
464    RevertAll();
465    const AutocompleteEditState* state =
466        GetStateAccessor()->GetProperty(contents->property_bag());
467    if (state) {
468      model_->RestoreState(state->model_state);
469
470      // Move the marks for the cursor and the other end of the selection to
471      // the previously-saved offsets (but preserve PRIMARY).
472      StartUpdatingHighlightedText();
473      SetSelectedRange(state->view_state.selection_range);
474      FinishUpdatingHighlightedText();
475    }
476  } else if (visibly_changed_permanent_text) {
477    RevertAll();
478    // TODO(deanm): There should be code to restore select all here.
479  } else if (changed_security_level) {
480    EmphasizeURLComponents();
481  }
482}
483
484void AutocompleteEditViewGtk::OpenURL(const GURL& url,
485                                      WindowOpenDisposition disposition,
486                                      PageTransition::Type transition,
487                                      const GURL& alternate_nav_url,
488                                      size_t selected_line,
489                                      const std::wstring& keyword) {
490  if (!url.is_valid())
491    return;
492
493  model_->OpenURL(url, disposition, transition, alternate_nav_url,
494                  selected_line, keyword);
495}
496
497std::wstring AutocompleteEditViewGtk::GetText() const {
498  GtkTextIter start, end;
499  GetTextBufferBounds(&start, &end);
500  gchar* utf8 = gtk_text_buffer_get_text(text_buffer_, &start, &end, false);
501  std::wstring out(UTF8ToWide(utf8));
502  g_free(utf8);
503
504#if GTK_CHECK_VERSION(2, 20, 0)
505  // We need to treat the text currently being composed by the input method as
506  // part of the text content, so that omnibox can work correctly in the middle
507  // of composition.
508  if (preedit_.size()) {
509    GtkTextMark* mark = gtk_text_buffer_get_insert(text_buffer_);
510    gtk_text_buffer_get_iter_at_mark(text_buffer_, &start, mark);
511    out.insert(gtk_text_iter_get_offset(&start), preedit_);
512  }
513#endif
514  return out;
515}
516
517bool AutocompleteEditViewGtk::IsEditingOrEmpty() const {
518  return model_->user_input_in_progress() || (GetTextLength() == 0);
519}
520
521int AutocompleteEditViewGtk::GetIcon() const {
522  return IsEditingOrEmpty() ?
523      AutocompleteMatch::TypeToIcon(model_->CurrentTextType()) :
524      toolbar_model_->GetIcon();
525}
526
527void AutocompleteEditViewGtk::SetUserText(const std::wstring& text) {
528  SetUserText(text, text, true);
529}
530
531void AutocompleteEditViewGtk::SetUserText(const std::wstring& text,
532                                          const std::wstring& display_text,
533                                          bool update_popup) {
534  model_->SetUserText(text);
535  // TODO(deanm): something about selection / focus change here.
536  SetWindowTextAndCaretPos(display_text, display_text.length());
537  if (update_popup)
538    UpdatePopup();
539  TextChanged();
540}
541
542void AutocompleteEditViewGtk::SetWindowTextAndCaretPos(const std::wstring& text,
543                                                       size_t caret_pos) {
544  CharRange range(static_cast<int>(caret_pos), static_cast<int>(caret_pos));
545  SetTextAndSelectedRange(text, range);
546}
547
548void AutocompleteEditViewGtk::SetForcedQuery() {
549  const std::wstring current_text(GetText());
550  const size_t start = current_text.find_first_not_of(kWhitespaceWide);
551  if (start == std::wstring::npos || (current_text[start] != '?')) {
552    SetUserText(L"?");
553  } else {
554    StartUpdatingHighlightedText();
555    SetSelectedRange(CharRange(current_text.size(), start + 1));
556    FinishUpdatingHighlightedText();
557  }
558}
559
560bool AutocompleteEditViewGtk::IsSelectAll() {
561  GtkTextIter sel_start, sel_end;
562  gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end);
563
564  GtkTextIter start, end;
565  GetTextBufferBounds(&start, &end);
566
567  // Returns true if the |text_buffer_| is empty.
568  return gtk_text_iter_equal(&start, &sel_start) &&
569      gtk_text_iter_equal(&end, &sel_end);
570}
571
572void AutocompleteEditViewGtk::GetSelectionBounds(std::wstring::size_type* start,
573                                                 std::wstring::size_type* end) {
574  CharRange selection = GetSelection();
575  *start = static_cast<size_t>(selection.cp_min);
576  *end = static_cast<size_t>(selection.cp_max);
577}
578
579void AutocompleteEditViewGtk::SelectAll(bool reversed) {
580  // SelectAll() is invoked as a side effect of other actions (e.g.  switching
581  // tabs or hitting Escape) in autocomplete_edit.cc, so we don't update the
582  // PRIMARY selection here.
583  SelectAllInternal(reversed, false);
584}
585
586void AutocompleteEditViewGtk::RevertAll() {
587  ClosePopup();
588  model_->Revert();
589  TextChanged();
590}
591
592void AutocompleteEditViewGtk::UpdatePopup() {
593  model_->SetInputInProgress(true);
594  if (!model_->has_focus())
595    return;
596
597  // Don't inline autocomplete when the caret/selection isn't at the end of
598  // the text, or in the middle of composition.
599  CharRange sel = GetSelection();
600  bool no_inline_autocomplete =
601      std::max(sel.cp_max, sel.cp_min) < GetTextLength();
602#if GTK_CHECK_VERSION(2, 20, 0)
603  no_inline_autocomplete = no_inline_autocomplete || preedit_.size();
604#endif
605  model_->StartAutocomplete(sel.cp_min != sel.cp_max, no_inline_autocomplete);
606}
607
608void AutocompleteEditViewGtk::ClosePopup() {
609  if (popup_view_->GetModel()->IsOpen())
610    controller_->OnAutocompleteWillClosePopup();
611
612  popup_view_->GetModel()->StopAutocomplete();
613}
614
615void AutocompleteEditViewGtk::OnTemporaryTextMaybeChanged(
616    const std::wstring& display_text,
617    bool save_original_selection) {
618  if (save_original_selection)
619    saved_temporary_selection_ = GetSelection();
620
621  StartUpdatingHighlightedText();
622  SetWindowTextAndCaretPos(display_text, display_text.length());
623  FinishUpdatingHighlightedText();
624  TextChanged();
625}
626
627bool AutocompleteEditViewGtk::OnInlineAutocompleteTextMaybeChanged(
628    const std::wstring& display_text,
629    size_t user_text_length) {
630  if (display_text == GetText())
631    return false;
632
633  StartUpdatingHighlightedText();
634  CharRange range(display_text.size(), user_text_length);
635  SetTextAndSelectedRange(display_text, range);
636  FinishUpdatingHighlightedText();
637  TextChanged();
638  return true;
639}
640
641void AutocompleteEditViewGtk::OnRevertTemporaryText() {
642  StartUpdatingHighlightedText();
643  SetSelectedRange(saved_temporary_selection_);
644  FinishUpdatingHighlightedText();
645  TextChanged();
646}
647
648void AutocompleteEditViewGtk::OnBeforePossibleChange() {
649  // If this change is caused by a paste clipboard action and all text is
650  // selected, then call model_->on_paste_replacing_all() to prevent inline
651  // autocomplete.
652  if (paste_clipboard_requested_) {
653    paste_clipboard_requested_ = false;
654    if (IsSelectAll())
655      model_->on_paste_replacing_all();
656  }
657
658  // Record our state.
659  text_before_change_ = GetText();
660  sel_before_change_ = GetSelection();
661}
662
663// TODO(deanm): This is mostly stolen from Windows, and will need some work.
664bool AutocompleteEditViewGtk::OnAfterPossibleChange() {
665  // If the change is caused by an Enter key press event, and the event was not
666  // handled by IME, then it's an unexpected change and shall be reverted here.
667  // {Start|Finish}UpdatingHighlightedText() are called here to prevent the
668  // PRIMARY selection from being changed.
669  if (enter_was_pressed_ && enter_was_inserted_) {
670    StartUpdatingHighlightedText();
671    SetTextAndSelectedRange(text_before_change_, sel_before_change_);
672    FinishUpdatingHighlightedText();
673    return false;
674  }
675
676  CharRange new_sel = GetSelection();
677  int length = GetTextLength();
678  bool selection_differs = (new_sel.cp_min != sel_before_change_.cp_min) ||
679                           (new_sel.cp_max != sel_before_change_.cp_max);
680  bool at_end_of_edit = (new_sel.cp_min == length && new_sel.cp_max == length);
681
682  // See if the text or selection have changed since OnBeforePossibleChange().
683  std::wstring new_text(GetText());
684  text_changed_ = (new_text != text_before_change_);
685
686  if (text_changed_)
687    AdjustTextJustification();
688
689  // When the user has deleted text, we don't allow inline autocomplete.  Make
690  // sure to not flag cases like selecting part of the text and then pasting
691  // (or typing) the prefix of that selection.  (We detect these by making
692  // sure the caret, which should be after any insertion, hasn't moved
693  // forward of the old selection start.)
694  bool just_deleted_text =
695      (text_before_change_.length() > new_text.length()) &&
696      (new_sel.cp_min <= std::min(sel_before_change_.cp_min,
697                                 sel_before_change_.cp_max));
698
699  bool something_changed = model_->OnAfterPossibleChange(new_text,
700      selection_differs, text_changed_, just_deleted_text, at_end_of_edit);
701
702  // If only selection was changed, we don't need to call |controller_|'s
703  // OnChanged() method, which is called in TextChanged().
704  // But we still need to call EmphasizeURLComponents() to make sure the text
705  // attributes are updated correctly.
706  if (something_changed && text_changed_)
707    TextChanged();
708  else if (selection_differs)
709    EmphasizeURLComponents();
710
711  return something_changed;
712}
713
714gfx::NativeView AutocompleteEditViewGtk::GetNativeView() const {
715  return alignment_.get();
716}
717
718CommandUpdater* AutocompleteEditViewGtk::GetCommandUpdater() {
719  return command_updater_;
720}
721
722void AutocompleteEditViewGtk::Observe(NotificationType type,
723                                      const NotificationSource& source,
724                                      const NotificationDetails& details) {
725  DCHECK(type == NotificationType::BROWSER_THEME_CHANGED);
726
727  SetBaseColor();
728}
729
730void AutocompleteEditViewGtk::SetBaseColor() {
731#if defined(TOOLKIT_VIEWS)
732  bool use_gtk = false;
733#else
734  bool use_gtk = theme_provider_->UseGtkTheme();
735#endif
736
737  if (use_gtk) {
738    gtk_widget_modify_cursor(text_view_, NULL, NULL);
739    gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, NULL);
740    gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, NULL);
741    gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, NULL);
742    gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, NULL);
743    gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, NULL);
744
745    gtk_util::UndoForceFontSize(text_view_);
746    gtk_util::UndoForceFontSize(instant_view_);
747
748    // Grab the text colors out of the style and set our tags to use them.
749    GtkStyle* style = gtk_rc_get_style(text_view_);
750
751    // style may be unrealized at this point, so calculate the halfway point
752    // between text[] and base[] manually instead of just using text_aa[].
753    GdkColor average_color = gtk_util::AverageColors(
754        style->text[GTK_STATE_NORMAL], style->base[GTK_STATE_NORMAL]);
755
756    g_object_set(faded_text_tag_, "foreground-gdk", &average_color, NULL);
757    g_object_set(normal_text_tag_, "foreground-gdk",
758                 &style->text[GTK_STATE_NORMAL], NULL);
759
760    // GtkLabel uses fg color instead of text color.
761    gtk_widget_modify_fg(instant_view_, GTK_STATE_NORMAL, &average_color);
762  } else {
763    const GdkColor* background_color_ptr;
764#if defined(TOOLKIT_VIEWS)
765    const GdkColor background_color = gfx::SkColorToGdkColor(
766        LocationBarView::GetColor(ToolbarModel::NONE,
767                                  LocationBarView::BACKGROUND));
768    background_color_ptr = &background_color;
769#else
770    background_color_ptr = &LocationBarViewGtk::kBackgroundColor;
771#endif
772    gtk_widget_modify_cursor(
773        text_view_, &gtk_util::kGdkBlack, &gtk_util::kGdkGray);
774    gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, background_color_ptr);
775
776    GdkColor c;
777#if !defined(TOOLKIT_VIEWS)
778    // Override the selected colors so we don't leak colors from the current
779    // gtk theme into the chrome-theme.
780    c = gfx::SkColorToGdkColor(
781        theme_provider_->get_active_selection_bg_color());
782    gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, &c);
783
784    c = gfx::SkColorToGdkColor(
785        theme_provider_->get_active_selection_fg_color());
786    gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, &c);
787
788    c = gfx::SkColorToGdkColor(
789        theme_provider_->get_inactive_selection_bg_color());
790    gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, &c);
791
792    c = gfx::SkColorToGdkColor(
793        theme_provider_->get_inactive_selection_fg_color());
794    gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, &c);
795#endif
796
797    gdk_color_parse(kTextBaseColor, &c);
798    gtk_widget_modify_fg(instant_view_, GTK_STATE_NORMAL, &c);
799
800    // Until we switch to vector graphics, force the font size.
801    gtk_util::ForceFontSizePixels(text_view_,
802        popup_window_mode_ ?
803        browser_defaults::kAutocompleteEditFontPixelSizeInPopup :
804        browser_defaults::kAutocompleteEditFontPixelSize);
805
806    gtk_util::ForceFontSizePixels(instant_view_,
807        popup_window_mode_ ?
808        browser_defaults::kAutocompleteEditFontPixelSizeInPopup :
809        browser_defaults::kAutocompleteEditFontPixelSize);
810
811    g_object_set(faded_text_tag_, "foreground", kTextBaseColor, NULL);
812    g_object_set(normal_text_tag_, "foreground", "#000000", NULL);
813  }
814
815  AdjustVerticalAlignmentOfInstantView();
816}
817
818void AutocompleteEditViewGtk::HandleBeginUserAction(GtkTextBuffer* sender) {
819  OnBeforePossibleChange();
820}
821
822void AutocompleteEditViewGtk::HandleEndUserAction(GtkTextBuffer* sender) {
823  OnAfterPossibleChange();
824}
825
826gboolean AutocompleteEditViewGtk::HandleKeyPress(GtkWidget* widget,
827                                                 GdkEventKey* event) {
828  // Background of this piece of complicated code:
829  // The omnibox supports several special behaviors which may be triggered by
830  // certain key events:
831  // Tab to search - triggered by Tab key
832  // Accept input - triggered by Enter key
833  // Revert input - triggered by Escape key
834  //
835  // Because we use a GtkTextView object |text_view_| for text input, we need
836  // send all key events to |text_view_| before handling them, to make sure
837  // IME works without any problem. So here, we intercept "key-press-event"
838  // signal of |text_view_| object and call its default handler to handle the
839  // key event first.
840  //
841  // Then if the key event is one of Tab, Enter and Escape, we need to trigger
842  // the corresponding special behavior if IME did not handle it.
843  // For Escape key, if the default signal handler returns FALSE, then we know
844  // it's not handled by IME.
845  //
846  // For Tab key, as "accepts-tab" property of |text_view_| is set to FALSE,
847  // if IME did not handle it then "move-focus" signal will be emitted by the
848  // default signal handler of |text_view_|. So we can intercept "move-focus"
849  // signal of |text_view_| to know if a Tab key press event was handled by IME,
850  // and trigger Tab to search behavior when necessary in the signal handler.
851  //
852  // But for Enter key, if IME did not handle the key event, the default signal
853  // handler will delete current selection range and insert '\n' and always
854  // return TRUE. We need to prevent |text_view_| from performing this default
855  // action if IME did not handle the key event, because we don't want the
856  // content of omnibox to be changed before triggering our special behavior.
857  // Otherwise our special behavior would not be performed correctly.
858  //
859  // But there is no way for us to prevent GtkTextView from handling the key
860  // event and performing built-in operation. So in order to achieve our goal,
861  // "insert-text" signal of |text_buffer_| object is intercepted, and
862  // following actions are done in the signal handler:
863  // - If there is only one character in inserted text, and it's '\n' or '\r',
864  //   then set |enter_was_inserted_| to true.
865  // - Filter out all new line and tab characters.
866  //
867  // So if |enter_was_inserted_| is true after calling |text_view_|'s default
868  // signal handler against an Enter key press event, then we know that the
869  // Enter key press event was handled by GtkTextView rather than IME, and can
870  // perform the special behavior for Enter key safely.
871  //
872  // Now the last thing is to prevent the content of omnibox from being changed
873  // by GtkTextView when Enter key is pressed. As OnBeforePossibleChange() and
874  // OnAfterPossibleChange() will be called by GtkTextView before and after
875  // changing the content, and the content is already saved in
876  // OnBeforePossibleChange(), so if the Enter key press event was not handled
877  // by IME, it's easy to restore the content in OnAfterPossibleChange(), as if
878  // it's not changed at all.
879
880  GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget);
881
882  enter_was_pressed_ = (event->keyval == GDK_Return ||
883                        event->keyval == GDK_ISO_Enter ||
884                        event->keyval == GDK_KP_Enter);
885
886  // Set |tab_was_pressed_| to true if it's a Tab key press event, so that our
887  // handler of "move-focus" signal can trigger Tab to search behavior when
888  // necessary.
889  tab_was_pressed_ = ((event->keyval == GDK_Tab ||
890                       event->keyval == GDK_ISO_Left_Tab ||
891                       event->keyval == GDK_KP_Tab) &&
892                      !(event->state & GDK_CONTROL_MASK));
893
894  // Reset |enter_was_inserted_|, which may be set in the "insert-text" signal
895  // handler, so that we'll know if an Enter key event was handled by IME.
896  enter_was_inserted_ = false;
897
898  // Reset |paste_clipboard_requested_| to make sure we won't misinterpret this
899  // key input action as a paste action.
900  paste_clipboard_requested_ = false;
901
902  // Reset |text_changed_| before passing the key event on to the text view.
903  text_changed_ = false;
904
905  // Call the default handler, so that IME can work as normal.
906  // New line characters will be filtered out by our "insert-text"
907  // signal handler attached to |text_buffer_| object.
908  gboolean result = klass->key_press_event(widget, event);
909
910  // Set |tab_was_pressed_| to false, to make sure Tab to search behavior can
911  // only be triggered by pressing Tab key.
912  tab_was_pressed_ = false;
913
914  if (enter_was_pressed_ && enter_was_inserted_) {
915    bool alt_held = (event->state & GDK_MOD1_MASK);
916    model_->AcceptInput(alt_held ? NEW_FOREGROUND_TAB : CURRENT_TAB, false);
917    result = TRUE;
918  } else if (!result && event->keyval == GDK_Escape &&
919             (event->state & gtk_accelerator_get_default_mod_mask()) == 0) {
920    // We can handle the Escape key if |text_view_| did not handle it.
921    // If it's not handled by us, then we need to propagate it up to the parent
922    // widgets, so that Escape accelerator can still work.
923    result = model_->OnEscapeKeyPressed();
924  } else if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) {
925    // Omnibox2 can switch its contents while pressing a control key. To switch
926    // the contents of omnibox2, we notify the AutocompleteEditModel class when
927    // the control-key state is changed.
928    model_->OnControlKeyChanged(true);
929  } else if (!text_changed_ && event->keyval == GDK_Delete &&
930             event->state & GDK_SHIFT_MASK) {
931    // If shift+del didn't change the text, we let this delete an entry from
932    // the popup.  We can't check to see if the IME handled it because even if
933    // nothing is selected, the IME or the TextView still report handling it.
934    AutocompletePopupModel* popup_model = popup_view_->GetModel();
935    if (popup_model->IsOpen())
936      popup_model->TryDeletingCurrentItem();
937  }
938
939  // Set |enter_was_pressed_| to false, to make sure OnAfterPossibleChange() can
940  // act as normal for changes made by other events.
941  enter_was_pressed_ = false;
942
943  // If the key event is not handled by |text_view_| or us, then we need to
944  // propagate the key event up to parent widgets by returning FALSE.
945  // In this case we need to stop the signal emission explicitly to prevent the
946  // default "key-press-event" handler of |text_view_| from being called again.
947  if (!result) {
948    static guint signal_id =
949        g_signal_lookup("key-press-event", GTK_TYPE_WIDGET);
950    g_signal_stop_emission(widget, signal_id, 0);
951  }
952
953  return result;
954}
955
956gboolean AutocompleteEditViewGtk::HandleKeyRelease(GtkWidget* widget,
957                                                   GdkEventKey* event) {
958  // Omnibox2 can switch its contents while pressing a control key. To switch
959  // the contents of omnibox2, we notify the AutocompleteEditModel class when
960  // the control-key state is changed.
961  if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) {
962    // Round trip to query the control state after the release.  This allows
963    // you to release one control key while still holding another control key.
964    GdkDisplay* display = gdk_drawable_get_display(event->window);
965    GdkModifierType mod;
966    gdk_display_get_pointer(display, NULL, NULL, NULL, &mod);
967    if (!(mod & GDK_CONTROL_MASK))
968      model_->OnControlKeyChanged(false);
969  }
970
971  // Even though we handled the press ourselves, let GtkTextView handle the
972  // release.  It shouldn't do anything particularly interesting, but it will
973  // handle the IME work for us.
974  return FALSE;  // Propagate into GtkTextView.
975}
976
977gboolean AutocompleteEditViewGtk::HandleViewButtonPress(GtkWidget* sender,
978                                                        GdkEventButton* event) {
979  // We don't need to care about double and triple clicks.
980  if (event->type != GDK_BUTTON_PRESS)
981    return FALSE;
982
983  if (event->button == 1) {
984#if defined(OS_CHROMEOS)
985    // When the first button is pressed, track some stuff that will help us
986    // determine whether we should select all of the text when the button is
987    // released.
988    button_1_pressed_ = true;
989    text_view_focused_before_button_press_ = GTK_WIDGET_HAS_FOCUS(text_view_);
990    text_selected_during_click_ = false;
991#endif
992
993    // Button press event may change the selection, we need to record the change
994    // and report it to |model_| later when button is released.
995    OnBeforePossibleChange();
996  } else if (event->button == 2) {
997    // GtkTextView pastes PRIMARY selection with middle click.
998    // We can't call model_->on_paste_replacing_all() here, because the actual
999    // paste clipboard action may not be performed if the clipboard is empty.
1000    paste_clipboard_requested_ = true;
1001  }
1002  return FALSE;
1003}
1004
1005gboolean AutocompleteEditViewGtk::HandleViewButtonRelease(
1006    GtkWidget* sender, GdkEventButton* event) {
1007  if (event->button != 1)
1008    return FALSE;
1009
1010#if defined(OS_CHROMEOS)
1011  button_1_pressed_ = false;
1012#endif
1013
1014  // Call the GtkTextView default handler, ignoring the fact that it will
1015  // likely have told us to stop propagating.  We want to handle selection.
1016  GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(text_view_);
1017  klass->button_release_event(text_view_, event);
1018
1019#if defined(OS_CHROMEOS)
1020  if (!text_view_focused_before_button_press_ && !text_selected_during_click_) {
1021    // If this was a focusing click and the user didn't drag to highlight any
1022    // text, select the full input and update the PRIMARY selection.
1023    SelectAllInternal(false, true);
1024
1025    // So we told the buffer where the cursor should be, but make sure to tell
1026    // the view so it can scroll it to be visible if needed.
1027    // NOTE: This function doesn't seem to like a count of 0, looking at the
1028    // code it will skip an important loop.  Use -1 to achieve the same.
1029    GtkTextIter start, end;
1030    GetTextBufferBounds(&start, &end);
1031    gtk_text_view_move_visually(GTK_TEXT_VIEW(text_view_), &start, -1);
1032  }
1033#endif
1034
1035  // Inform |model_| about possible text selection change.
1036  OnAfterPossibleChange();
1037
1038  return TRUE;  // Don't continue, we called the default handler already.
1039}
1040
1041gboolean AutocompleteEditViewGtk::HandleViewFocusIn(GtkWidget* sender,
1042                                                    GdkEventFocus* event) {
1043  GdkModifierType modifiers;
1044  gdk_window_get_pointer(text_view_->window, NULL, NULL, &modifiers);
1045  model_->OnSetFocus((modifiers & GDK_CONTROL_MASK) != 0);
1046  controller_->OnSetFocus();
1047  // TODO(deanm): Some keyword hit business, etc here.
1048
1049  g_signal_connect(
1050      gdk_keymap_get_for_display(gtk_widget_get_display(text_view_)),
1051      "direction-changed",
1052      G_CALLBACK(&HandleKeymapDirectionChangedThunk), this);
1053
1054  AdjustTextJustification();
1055
1056  return FALSE;  // Continue propagation.
1057}
1058
1059gboolean AutocompleteEditViewGtk::HandleViewFocusOut(GtkWidget* sender,
1060                                                     GdkEventFocus* event) {
1061  GtkWidget* view_getting_focus = NULL;
1062  GtkWindow* toplevel = platform_util::GetTopLevel(sender);
1063  if (gtk_window_is_active(toplevel))
1064    view_getting_focus = going_to_focus_;
1065
1066  // This must be invoked before ClosePopup.
1067  controller_->OnAutocompleteLosingFocus(view_getting_focus);
1068
1069  // Close the popup.
1070  ClosePopup();
1071  // Tell the model to reset itself.
1072  model_->OnKillFocus();
1073  controller_->OnKillFocus();
1074
1075  g_signal_handlers_disconnect_by_func(
1076      gdk_keymap_get_for_display(gtk_widget_get_display(text_view_)),
1077      reinterpret_cast<gpointer>(&HandleKeymapDirectionChangedThunk), this);
1078
1079  return FALSE;  // Pass the event on to the GtkTextView.
1080}
1081
1082void AutocompleteEditViewGtk::HandleViewMoveCursor(
1083    GtkWidget* sender,
1084    GtkMovementStep step,
1085    gint count,
1086    gboolean extend_selection) {
1087  GtkTextIter sel_start, sel_end;
1088  gboolean has_selection =
1089      gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end);
1090
1091  bool handled = true;
1092
1093  // We want the GtkEntry behavior when you move the cursor while you have a
1094  // selection.  GtkTextView just drops the selection and moves the cursor, but
1095  // instead we want to move the cursor to the appropiate end of the selection.
1096  if (step == GTK_MOVEMENT_VISUAL_POSITIONS && !extend_selection) {
1097    if ((count == 1 || count == -1) && has_selection) {
1098      // We have a selection and start / end are in ascending order.
1099      // Cursor placement will remove the selection, so we need inform |model_|
1100      // about this change by calling On{Before|After}PossibleChange() methods.
1101      OnBeforePossibleChange();
1102      gtk_text_buffer_place_cursor(text_buffer_,
1103                                   count == 1 ? &sel_end : &sel_start);
1104      OnAfterPossibleChange();
1105    } else if (count == 1 && !has_selection) {
1106      gint cursor_pos;
1107      g_object_get(G_OBJECT(text_buffer_), "cursor-position", &cursor_pos,
1108                   NULL);
1109      if (cursor_pos == GetTextLength())
1110        controller_->OnCommitSuggestedText(GetText());
1111      else
1112        handled = false;
1113    } else {
1114      handled = false;
1115    }
1116  } else if (step == GTK_MOVEMENT_PAGES) {  // Page up and down.
1117    // Multiply by count for the direction (if we move too much that's ok).
1118    model_->OnUpOrDownKeyPressed(model_->result().size() * count);
1119  } else if (step == GTK_MOVEMENT_DISPLAY_LINES) {  // Arrow up and down.
1120    model_->OnUpOrDownKeyPressed(count);
1121  } else {
1122    handled = false;
1123  }
1124
1125  if (!handled) {
1126    // Cursor movement may change the selection, we need to record the change
1127    // and report it to |model_|.
1128    if (has_selection || extend_selection)
1129      OnBeforePossibleChange();
1130
1131    // Propagate into GtkTextView
1132    GtkTextViewClass* klass = GTK_TEXT_VIEW_GET_CLASS(text_view_);
1133    klass->move_cursor(GTK_TEXT_VIEW(text_view_), step, count,
1134                       extend_selection);
1135
1136    if (has_selection || extend_selection)
1137      OnAfterPossibleChange();
1138  }
1139
1140  // move-cursor doesn't use a signal accumulator on the return value (it
1141  // just ignores then), so we have to stop the propagation.
1142  static guint signal_id = g_signal_lookup("move-cursor", GTK_TYPE_TEXT_VIEW);
1143  g_signal_stop_emission(text_view_, signal_id, 0);
1144}
1145
1146void AutocompleteEditViewGtk::HandleViewSizeRequest(GtkWidget* sender,
1147                                                    GtkRequisition* req) {
1148  // Don't force a minimum width, but use the font-relative height.  This is a
1149  // run-first handler, so the default handler was already called.
1150  req->width = 1;
1151}
1152
1153void AutocompleteEditViewGtk::HandlePopulatePopup(GtkWidget* sender,
1154                                                  GtkMenu* menu) {
1155  GtkWidget* separator = gtk_separator_menu_item_new();
1156  gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator);
1157  gtk_widget_show(separator);
1158
1159  // Search Engine menu item.
1160  GtkWidget* search_engine_menuitem = gtk_menu_item_new_with_mnemonic(
1161      gfx::ConvertAcceleratorsFromWindowsStyle(
1162          l10n_util::GetStringUTF8(IDS_EDIT_SEARCH_ENGINES)).c_str());
1163  gtk_menu_shell_append(GTK_MENU_SHELL(menu), search_engine_menuitem);
1164  g_signal_connect(search_engine_menuitem, "activate",
1165                   G_CALLBACK(HandleEditSearchEnginesThunk), this);
1166  gtk_widget_set_sensitive(search_engine_menuitem,
1167      command_updater_->IsCommandEnabled(IDC_EDIT_SEARCH_ENGINES));
1168  gtk_widget_show(search_engine_menuitem);
1169
1170  // We need to update the paste and go controller before we know what text
1171  // to show. We could do this all asynchronously, but it would be elaborate
1172  // because we'd have to account for multiple menus showing, getting called
1173  // back after shutdown, and similar issues.
1174  GtkClipboard* x_clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
1175  gchar* text = gtk_clipboard_wait_for_text(x_clipboard);
1176  std::wstring text_wstr = UTF8ToWide(text);
1177  g_free(text);
1178
1179  // Paste and Go menu item.
1180  GtkWidget* paste_go_menuitem = gtk_menu_item_new_with_mnemonic(
1181      gfx::ConvertAcceleratorsFromWindowsStyle(
1182          l10n_util::GetStringUTF8(model_->is_paste_and_search() ?
1183              IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO)).c_str());
1184  gtk_menu_shell_append(GTK_MENU_SHELL(menu), paste_go_menuitem);
1185  g_signal_connect(paste_go_menuitem, "activate",
1186                   G_CALLBACK(HandlePasteAndGoThunk), this);
1187  gtk_widget_set_sensitive(paste_go_menuitem,
1188                           model_->CanPasteAndGo(text_wstr));
1189  gtk_widget_show(paste_go_menuitem);
1190}
1191
1192void AutocompleteEditViewGtk::HandleEditSearchEngines(GtkWidget* sender) {
1193  command_updater_->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES);
1194}
1195
1196void AutocompleteEditViewGtk::HandlePasteAndGo(GtkWidget* sender) {
1197  model_->PasteAndGo();
1198}
1199
1200void AutocompleteEditViewGtk::HandleMarkSet(GtkTextBuffer* buffer,
1201                                            GtkTextIter* location,
1202                                            GtkTextMark* mark) {
1203  if (!text_buffer_ || buffer != text_buffer_)
1204    return;
1205
1206  if (mark != gtk_text_buffer_get_insert(text_buffer_) &&
1207      mark != gtk_text_buffer_get_selection_bound(text_buffer_)) {
1208    return;
1209  }
1210
1211  // If we are here, that means the user may be changing the selection
1212  selection_suggested_ = false;
1213
1214  // Get the currently-selected text, if there is any.
1215  std::string new_selected_text = GetSelectedText();
1216
1217#if defined(OS_CHROMEOS)
1218  // If the user just selected some text with the mouse (or at least while the
1219  // mouse button was down), make sure that we won't blow their selection away
1220  // later by selecting all of the text when the button is released.
1221  if (button_1_pressed_ && !new_selected_text.empty())
1222    text_selected_during_click_ = true;
1223#endif
1224
1225  // If we had some text selected earlier but it's no longer highlighted, we
1226  // might need to save it now...
1227  if (!selected_text_.empty() && new_selected_text.empty()) {
1228    // ... but only if we currently own the selection.  We want to manually
1229    // update the selection when the text is unhighlighted because the user
1230    // clicked in a blank area of the text view, but not when it's unhighlighted
1231    // because another client or widget took the selection.  (This handler gets
1232    // called before the default handler, so as long as nobody else took the
1233    // selection, the text buffer still owns it even if GTK is about to take it
1234    // away in the default handler.)
1235    GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
1236    if (gtk_clipboard_get_owner(clipboard) == G_OBJECT(text_buffer_))
1237      SavePrimarySelection(selected_text_);
1238  }
1239
1240  selected_text_ = new_selected_text;
1241}
1242
1243// Override the primary selection the text buffer has set. This has to happen
1244// after the default handler for the "mark-set" signal.
1245void AutocompleteEditViewGtk::HandleMarkSetAfter(GtkTextBuffer* buffer,
1246                                                 GtkTextIter* location,
1247                                                 GtkTextMark* mark) {
1248  UpdatePrimarySelectionIfValidURL();
1249}
1250
1251// Just use the default behavior for DnD, except if the drop can be a PasteAndGo
1252// then override.
1253void AutocompleteEditViewGtk::HandleDragDataReceived(
1254    GtkWidget* sender, GdkDragContext* context, gint x, gint y,
1255    GtkSelectionData* selection_data, guint target_type, guint time) {
1256  // Reset |paste_clipboard_requested_| to make sure we won't misinterpret this
1257  // drop action as a paste action.
1258  paste_clipboard_requested_ = false;
1259
1260  // Don't try to PasteAndGo on drops originating from this omnibox. However, do
1261  // allow default behavior for such drags.
1262  if (context->source_window == text_view_->window)
1263    return;
1264
1265  guchar* text = gtk_selection_data_get_text(selection_data);
1266  if (!text)
1267    return;
1268
1269  std::wstring possible_url = UTF8ToWide(reinterpret_cast<char*>(text));
1270  g_free(text);
1271  if (model_->CanPasteAndGo(CollapseWhitespace(possible_url, true))) {
1272    model_->PasteAndGo();
1273    gtk_drag_finish(context, TRUE, TRUE, time);
1274
1275    static guint signal_id =
1276        g_signal_lookup("drag-data-received", GTK_TYPE_WIDGET);
1277    g_signal_stop_emission(text_view_, signal_id, 0);
1278  }
1279}
1280
1281void AutocompleteEditViewGtk::HandleDragDataGet(
1282    GtkWidget* widget,
1283    GdkDragContext* context,
1284    GtkSelectionData* selection_data,
1285    guint target_type,
1286    guint time) {
1287  // If GTK put the normal textual version of the selection in our drag data,
1288  // put our doctored selection that might have the 'http://' prefix. Also, GTK
1289  // is confused about signedness of its datatypes, leading to the weird switch
1290  // statement (no set of casts fixes this).
1291  switch (target_type) {
1292    case GTK_TEXT_BUFFER_TARGET_INFO_TEXT: {
1293      gtk_selection_data_set_text(selection_data, selected_text_.c_str(), -1);
1294    }
1295  }
1296}
1297
1298void AutocompleteEditViewGtk::HandleInsertText(
1299    GtkTextBuffer* buffer, GtkTextIter* location, const gchar* text, gint len) {
1300  std::string filtered_text;
1301  filtered_text.reserve(len);
1302
1303  // Filter out new line and tab characters.
1304  // |text| is guaranteed to be a valid UTF-8 string, so we don't need to
1305  // validate it here.
1306  //
1307  // If there was only a single character, then it might be generated by a key
1308  // event. In this case, we save the single character to help our
1309  // "key-press-event" signal handler distinguish if an Enter key event is
1310  // handled by IME or not.
1311  if (len == 1 && (text[0] == '\n' || text[0] == '\r'))
1312    enter_was_inserted_ = true;
1313
1314  const gchar* p = text;
1315  while(*p) {
1316    gunichar c = g_utf8_get_char(p);
1317    const gchar* next = g_utf8_next_char(p);
1318
1319    // 0x200B is Zero Width Space, which is inserted just before the instant
1320    // anchor for working around the GtkTextView's misalignment bug.
1321    // This character might be captured and inserted into the content by undo
1322    // manager, so we need to filter it out here.
1323    if (c != L'\n' && c != L'\r' && c != L'\t' && c != 0x200B)
1324      filtered_text.append(p, next);
1325
1326    p = next;
1327  }
1328
1329  if (filtered_text.length()) {
1330    // Avoid inserting the text after the instant anchor.
1331    ValidateTextBufferIter(location);
1332
1333    // Call the default handler to insert filtered text.
1334    GtkTextBufferClass* klass = GTK_TEXT_BUFFER_GET_CLASS(buffer);
1335    klass->insert_text(buffer, location, filtered_text.data(),
1336                       static_cast<gint>(filtered_text.length()));
1337  }
1338
1339  // Stop propagating the signal emission to prevent the default handler from
1340  // being called again.
1341  static guint signal_id = g_signal_lookup("insert-text", GTK_TYPE_TEXT_BUFFER);
1342  g_signal_stop_emission(buffer, signal_id, 0);
1343}
1344
1345void AutocompleteEditViewGtk::HandleBackSpace(GtkWidget* sender) {
1346  // Checks if it's currently in keyword search mode.
1347  if (model_->is_keyword_hint() || model_->keyword().empty())
1348    return;  // Propgate into GtkTextView.
1349
1350  GtkTextIter sel_start, sel_end;
1351  // Checks if there is some text selected.
1352  if (gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end))
1353    return;  // Propgate into GtkTextView.
1354
1355  GtkTextIter start;
1356  gtk_text_buffer_get_start_iter(text_buffer_, &start);
1357
1358  if (!gtk_text_iter_equal(&start, &sel_start))
1359    return;  // Propgate into GtkTextView.
1360
1361  // We're showing a keyword and the user pressed backspace at the beginning
1362  // of the text. Delete the selected keyword.
1363  model_->ClearKeyword(GetText());
1364
1365  // Stop propagating the signal emission into GtkTextView.
1366  static guint signal_id = g_signal_lookup("backspace", GTK_TYPE_TEXT_VIEW);
1367  g_signal_stop_emission(text_view_, signal_id, 0);
1368}
1369
1370void AutocompleteEditViewGtk::HandleViewMoveFocus(GtkWidget* widget,
1371                                                  GtkDirectionType direction) {
1372  // Trigger Tab to search behavior only when Tab key is pressed.
1373  if (tab_was_pressed_ && enable_tab_to_search_ &&
1374      model_->is_keyword_hint() && !model_->keyword().empty()) {
1375    model_->AcceptKeyword();
1376
1377    // If Tab to search behavior is triggered, then stop the signal emission to
1378    // prevent the focus from being moved.
1379    static guint signal_id = g_signal_lookup("move-focus", GTK_TYPE_WIDGET);
1380    g_signal_stop_emission(widget, signal_id, 0);
1381  }
1382
1383  // Propagate the signal so that focus can be moved as normal.
1384}
1385
1386void AutocompleteEditViewGtk::HandleCopyClipboard(GtkWidget* sender) {
1387  HandleCopyOrCutClipboard(true);
1388}
1389
1390void AutocompleteEditViewGtk::HandleCutClipboard(GtkWidget* sender) {
1391  HandleCopyOrCutClipboard(false);
1392}
1393
1394void AutocompleteEditViewGtk::HandleCopyOrCutClipboard(bool copy) {
1395  // On copy or cut, we manually update the PRIMARY selection to contain the
1396  // highlighted text.  This matches Firefox -- we highlight the URL but don't
1397  // update PRIMARY on Ctrl-L, so Ctrl-L, Ctrl-C and then middle-click is a
1398  // convenient way to paste the current URL somewhere.
1399  if (!gtk_text_buffer_get_has_selection(text_buffer_))
1400    return;
1401
1402  GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
1403  DCHECK(clipboard);
1404  if (!clipboard)
1405    return;
1406
1407  CharRange selection = GetSelection();
1408  GURL url;
1409  std::wstring text(UTF8ToWide(GetSelectedText()));
1410  bool write_url;
1411  model_->AdjustTextForCopy(selection.selection_min(), IsSelectAll(), &text,
1412                            &url, &write_url);
1413
1414  if (write_url) {
1415    string16 text16(WideToUTF16(text));
1416    BookmarkDragData data;
1417    data.ReadFromTuple(url, text16);
1418    data.WriteToClipboard(NULL);
1419
1420    // Stop propagating the signal.
1421    static guint copy_signal_id =
1422        g_signal_lookup("copy-clipboard", GTK_TYPE_TEXT_VIEW);
1423    static guint cut_signal_id =
1424        g_signal_lookup("cut-clipboard", GTK_TYPE_TEXT_VIEW);
1425    g_signal_stop_emission(text_view_,
1426                           copy ? copy_signal_id : cut_signal_id,
1427                           0);
1428
1429    if (!copy)
1430      gtk_text_buffer_delete_selection(text_buffer_, true, true);
1431  }
1432
1433  OwnPrimarySelection(WideToUTF8(text));
1434}
1435
1436void AutocompleteEditViewGtk::OwnPrimarySelection(const std::string& text) {
1437  primary_selection_text_ = text;
1438
1439  GtkTargetList* list = gtk_target_list_new(NULL, 0);
1440  gtk_target_list_add_text_targets(list, 0);
1441  gint len;
1442  GtkTargetEntry* entries = gtk_target_table_new_from_list(list, &len);
1443
1444  // When |text_buffer_| is destroyed, it will clear the clipboard, hence
1445  // we needn't worry about calling gtk_clipboard_clear().
1446  gtk_clipboard_set_with_owner(gtk_clipboard_get(GDK_SELECTION_PRIMARY),
1447                               entries, len,
1448                               ClipboardGetSelectionThunk,
1449                               ClipboardSelectionCleared,
1450                               G_OBJECT(text_buffer_));
1451
1452  gtk_target_list_unref(list);
1453  gtk_target_table_free(entries, len);
1454}
1455
1456void AutocompleteEditViewGtk::HandlePasteClipboard(GtkWidget* sender) {
1457  // We can't call model_->on_paste_replacing_all() here, because the actual
1458  // paste clipboard action may not be performed if the clipboard is empty.
1459  paste_clipboard_requested_ = true;
1460}
1461
1462gfx::Rect AutocompleteEditViewGtk::WindowBoundsFromIters(
1463    GtkTextIter* iter1, GtkTextIter* iter2) {
1464  GdkRectangle start_location, end_location;
1465  GtkTextView* text_view = GTK_TEXT_VIEW(text_view_);
1466  gtk_text_view_get_iter_location(text_view, iter1, &start_location);
1467  gtk_text_view_get_iter_location(text_view, iter2, &end_location);
1468
1469  gint x1, x2, y1, y2;
1470  gtk_text_view_buffer_to_window_coords(text_view, GTK_TEXT_WINDOW_WIDGET,
1471                                        start_location.x, start_location.y,
1472                                        &x1, &y1);
1473  gtk_text_view_buffer_to_window_coords(text_view, GTK_TEXT_WINDOW_WIDGET,
1474                                        end_location.x + end_location.width,
1475                                        end_location.y + end_location.height,
1476                                        &x2, &y2);
1477
1478  return gfx::Rect(x1, y1, x2 - x1, y2 - y1);
1479}
1480
1481gboolean AutocompleteEditViewGtk::HandleExposeEvent(GtkWidget* sender,
1482                                                    GdkEventExpose* expose) {
1483  if (strikethrough_.cp_min >= strikethrough_.cp_max)
1484    return FALSE;
1485
1486  gfx::Rect expose_rect(expose->area);
1487
1488  GtkTextIter iter_min, iter_max;
1489  ItersFromCharRange(strikethrough_, &iter_min, &iter_max);
1490  gfx::Rect strikethrough_rect = WindowBoundsFromIters(&iter_min, &iter_max);
1491
1492  if (!expose_rect.Intersects(strikethrough_rect))
1493    return FALSE;
1494
1495  // Finally, draw.
1496  cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(expose->window));
1497  cairo_rectangle(cr, expose_rect.x(), expose_rect.y(),
1498                      expose_rect.width(), expose_rect.height());
1499  cairo_clip(cr);
1500
1501  // TODO(estade): we probably shouldn't draw the strikethrough on selected
1502  // text. I started to do this, but it was way more effort than it seemed
1503  // worth.
1504  strikethrough_rect.Inset(kStrikethroughStrokeWidth,
1505                           kStrikethroughStrokeWidth);
1506  cairo_set_source_rgb(cr, kStrikethroughStrokeRed, 0.0, 0.0);
1507  cairo_set_line_width(cr, kStrikethroughStrokeWidth);
1508  cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1509  cairo_move_to(cr, strikethrough_rect.x(), strikethrough_rect.bottom());
1510  cairo_line_to(cr, strikethrough_rect.right(), strikethrough_rect.y());
1511  cairo_stroke(cr);
1512  cairo_destroy(cr);
1513
1514  return FALSE;
1515}
1516
1517void AutocompleteEditViewGtk::SelectAllInternal(bool reversed,
1518                                                bool update_primary_selection) {
1519  GtkTextIter start, end;
1520  if (reversed) {
1521    GetTextBufferBounds(&end, &start);
1522  } else {
1523    GetTextBufferBounds(&start, &end);
1524  }
1525  if (!update_primary_selection)
1526    StartUpdatingHighlightedText();
1527  gtk_text_buffer_select_range(text_buffer_, &start, &end);
1528  if (!update_primary_selection)
1529    FinishUpdatingHighlightedText();
1530}
1531
1532void AutocompleteEditViewGtk::StartUpdatingHighlightedText() {
1533  if (GTK_WIDGET_REALIZED(text_view_)) {
1534    GtkClipboard* clipboard =
1535        gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY);
1536    DCHECK(clipboard);
1537    if (clipboard)
1538      gtk_text_buffer_remove_selection_clipboard(text_buffer_, clipboard);
1539  }
1540  g_signal_handler_block(text_buffer_, mark_set_handler_id_);
1541  g_signal_handler_block(text_buffer_, mark_set_handler_id2_);
1542}
1543
1544void AutocompleteEditViewGtk::FinishUpdatingHighlightedText() {
1545  if (GTK_WIDGET_REALIZED(text_view_)) {
1546    GtkClipboard* clipboard =
1547        gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY);
1548    DCHECK(clipboard);
1549    if (clipboard)
1550      gtk_text_buffer_add_selection_clipboard(text_buffer_, clipboard);
1551  }
1552  g_signal_handler_unblock(text_buffer_, mark_set_handler_id_);
1553  g_signal_handler_unblock(text_buffer_, mark_set_handler_id2_);
1554}
1555
1556AutocompleteEditViewGtk::CharRange AutocompleteEditViewGtk::GetSelection() {
1557  // You can not just use get_selection_bounds here, since the order will be
1558  // ascending, and you don't know where the user's start and end of the
1559  // selection was (if the selection was forwards or backwards).  Get the
1560  // actual marks so that we can preserve the selection direction.
1561  GtkTextIter start, insert;
1562  GtkTextMark* mark;
1563
1564  mark = gtk_text_buffer_get_selection_bound(text_buffer_);
1565  gtk_text_buffer_get_iter_at_mark(text_buffer_, &start, mark);
1566
1567  mark = gtk_text_buffer_get_insert(text_buffer_);
1568  gtk_text_buffer_get_iter_at_mark(text_buffer_, &insert, mark);
1569
1570#if GTK_CHECK_VERSION(2, 20, 0)
1571  // Nothing should be selected when we are in the middle of composition.
1572  DCHECK(preedit_.empty() || gtk_text_iter_equal(&start, &insert));
1573#endif
1574
1575  return CharRange(gtk_text_iter_get_offset(&start),
1576                   gtk_text_iter_get_offset(&insert));
1577}
1578
1579void AutocompleteEditViewGtk::ItersFromCharRange(const CharRange& range,
1580                                                 GtkTextIter* iter_min,
1581                                                 GtkTextIter* iter_max) {
1582  gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_min, range.cp_min);
1583  gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_max, range.cp_max);
1584}
1585
1586int AutocompleteEditViewGtk::GetTextLength() const {
1587  GtkTextIter end;
1588  gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_);
1589#if GTK_CHECK_VERSION(2, 20, 0)
1590  // We need to count the length of the text being composed, because we treat
1591  // it as part of the content in GetText().
1592  return gtk_text_iter_get_offset(&end) + preedit_.size();
1593#else
1594  return gtk_text_iter_get_offset(&end);
1595#endif
1596}
1597
1598void AutocompleteEditViewGtk::EmphasizeURLComponents() {
1599#if GTK_CHECK_VERSION(2, 20, 0)
1600  // We can't change the text style easily, if the preedit string (the text
1601  // being composed by the input method) is not empty, which is not treated as
1602  // a part of the text content inside GtkTextView. And it's ok to simply return
1603  // in this case, as this method will be called again when the preedit string
1604  // gets committed.
1605  if (preedit_.size())
1606    return;
1607#endif
1608  // See whether the contents are a URL with a non-empty host portion, which we
1609  // should emphasize.  To check for a URL, rather than using the type returned
1610  // by Parse(), ask the model, which will check the desired page transition for
1611  // this input.  This can tell us whether an UNKNOWN input string is going to
1612  // be treated as a search or a navigation, and is the same method the Paste
1613  // And Go system uses.
1614  url_parse::Component scheme, host;
1615  std::wstring text(GetText());
1616  AutocompleteInput::ParseForEmphasizeComponents(
1617      text, model_->GetDesiredTLD(), &scheme, &host);
1618  const bool emphasize = model_->CurrentTextIsURL() && (host.len > 0);
1619
1620  // Set the baseline emphasis.
1621  GtkTextIter start, end;
1622  GetTextBufferBounds(&start, &end);
1623  gtk_text_buffer_remove_all_tags(text_buffer_, &start, &end);
1624  if (emphasize) {
1625    gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end);
1626
1627    // We've found a host name, give it more emphasis.
1628    gtk_text_buffer_get_iter_at_line_index(text_buffer_, &start, 0,
1629                                           GetUTF8Offset(text,
1630                                                         host.begin));
1631    gtk_text_buffer_get_iter_at_line_index(text_buffer_, &end, 0,
1632                                           GetUTF8Offset(text,
1633                                                         host.end()));
1634
1635    gtk_text_buffer_apply_tag(text_buffer_, normal_text_tag_, &start, &end);
1636  } else {
1637    gtk_text_buffer_apply_tag(text_buffer_, normal_text_tag_, &start, &end);
1638  }
1639
1640  strikethrough_ = CharRange();
1641  // Emphasize the scheme for security UI display purposes (if necessary).
1642  if (!model_->user_input_in_progress() && scheme.is_nonempty() &&
1643      (security_level_ != ToolbarModel::NONE)) {
1644    CharRange scheme_range = CharRange(GetUTF8Offset(text, scheme.begin),
1645                                       GetUTF8Offset(text, scheme.end()));
1646    ItersFromCharRange(scheme_range, &start, &end);
1647
1648    if (security_level_ == ToolbarModel::SECURITY_ERROR) {
1649      strikethrough_ = scheme_range;
1650      // When we draw the strikethrough, we don't want to include the ':' at the
1651      // end of the scheme.
1652      strikethrough_.cp_max--;
1653
1654      gtk_text_buffer_apply_tag(text_buffer_, security_error_scheme_tag_,
1655                                &start, &end);
1656    } else if (security_level_ == ToolbarModel::SECURITY_WARNING) {
1657      gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end);
1658    } else {
1659      gtk_text_buffer_apply_tag(text_buffer_, secure_scheme_tag_, &start, &end);
1660    }
1661  }
1662}
1663
1664void AutocompleteEditViewGtk::SetInstantSuggestion(
1665    const std::string& suggestion) {
1666  gtk_label_set_text(GTK_LABEL(instant_view_), suggestion.c_str());
1667  if (suggestion.empty()) {
1668    gtk_widget_hide(instant_view_);
1669  } else {
1670    gtk_widget_show(instant_view_);
1671    AdjustVerticalAlignmentOfInstantView();
1672  }
1673}
1674
1675bool AutocompleteEditViewGtk::CommitInstantSuggestion() {
1676  const gchar* suggestion = gtk_label_get_text(GTK_LABEL(instant_view_));
1677  if (!suggestion || !*suggestion)
1678    return false;
1679
1680  OnBeforePossibleChange();
1681  SetUserText(GetText() + UTF8ToWide(suggestion));
1682  OnAfterPossibleChange();
1683  return true;
1684}
1685
1686void AutocompleteEditViewGtk::TextChanged() {
1687  EmphasizeURLComponents();
1688  controller_->OnChanged();
1689}
1690
1691void AutocompleteEditViewGtk::SavePrimarySelection(
1692    const std::string& selected_text) {
1693  GtkClipboard* clipboard =
1694      gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY);
1695  DCHECK(clipboard);
1696  if (!clipboard)
1697    return;
1698
1699  gtk_clipboard_set_text(
1700      clipboard, selected_text.data(), selected_text.size());
1701}
1702
1703void AutocompleteEditViewGtk::SetTextAndSelectedRange(const std::wstring& text,
1704                                                      const CharRange& range) {
1705  std::string utf8 = WideToUTF8(text);
1706  gtk_text_buffer_set_text(text_buffer_, utf8.data(), utf8.length());
1707  SetSelectedRange(range);
1708  AdjustTextJustification();
1709}
1710
1711void AutocompleteEditViewGtk::SetSelectedRange(const CharRange& range) {
1712  GtkTextIter insert, bound;
1713  ItersFromCharRange(range, &bound, &insert);
1714  gtk_text_buffer_select_range(text_buffer_, &insert, &bound);
1715
1716  // This should be set *after* setting the selection range, in case setting the
1717  // selection triggers HandleMarkSet which sets |selection_suggested_| to
1718  // false.
1719  selection_suggested_ = true;
1720}
1721
1722void AutocompleteEditViewGtk::AdjustTextJustification() {
1723  PangoDirection content_dir = GetContentDirection();
1724
1725  // Use keymap direction if content does not have strong direction.
1726  // It matches the behavior of GtkTextView.
1727  if (content_dir == PANGO_DIRECTION_NEUTRAL) {
1728    content_dir = gdk_keymap_get_direction(
1729      gdk_keymap_get_for_display(gtk_widget_get_display(text_view_)));
1730  }
1731
1732  GtkTextDirection widget_dir = gtk_widget_get_direction(text_view_);
1733
1734  if ((widget_dir == GTK_TEXT_DIR_RTL && content_dir == PANGO_DIRECTION_LTR) ||
1735      (widget_dir == GTK_TEXT_DIR_LTR && content_dir == PANGO_DIRECTION_RTL)) {
1736    gtk_text_view_set_justification(GTK_TEXT_VIEW(text_view_),
1737                                    GTK_JUSTIFY_RIGHT);
1738  } else {
1739    gtk_text_view_set_justification(GTK_TEXT_VIEW(text_view_),
1740                                    GTK_JUSTIFY_LEFT);
1741  }
1742}
1743
1744PangoDirection AutocompleteEditViewGtk::GetContentDirection() {
1745  GtkTextIter iter;
1746  gtk_text_buffer_get_start_iter(text_buffer_, &iter);
1747
1748  PangoDirection dir = PANGO_DIRECTION_NEUTRAL;
1749  do {
1750    dir = pango_unichar_direction(gtk_text_iter_get_char(&iter));
1751    if (dir != PANGO_DIRECTION_NEUTRAL)
1752      break;
1753  } while (gtk_text_iter_forward_char(&iter));
1754
1755  return dir;
1756}
1757
1758void AutocompleteEditViewGtk::HandleWidgetDirectionChanged(
1759    GtkWidget* sender, GtkTextDirection previous_direction) {
1760  AdjustTextJustification();
1761}
1762
1763void AutocompleteEditViewGtk::HandleDeleteFromCursor(GtkWidget *sender,
1764    GtkDeleteType type, gint count) {
1765  // If the selected text was suggested for autocompletion, then erase those
1766  // first and then let the default handler take over.
1767  if (selection_suggested_) {
1768    gtk_text_buffer_delete_selection(text_buffer_, true, true);
1769    selection_suggested_ = false;
1770  }
1771}
1772
1773void AutocompleteEditViewGtk::HandleKeymapDirectionChanged(GdkKeymap* sender) {
1774  AdjustTextJustification();
1775}
1776
1777void AutocompleteEditViewGtk::HandleDeleteRange(GtkTextBuffer* buffer,
1778                                                GtkTextIter* start,
1779                                                GtkTextIter* end) {
1780  // Prevent the user from deleting the instant anchor. We can't simply set the
1781  // instant anchor readonly by applying a tag with "editable" = FALSE, because
1782  // it'll prevent the insert caret from blinking.
1783  ValidateTextBufferIter(start);
1784  ValidateTextBufferIter(end);
1785  if (!gtk_text_iter_compare(start, end)) {
1786    static guint signal_id =
1787        g_signal_lookup("delete-range", GTK_TYPE_TEXT_BUFFER);
1788    g_signal_stop_emission(buffer, signal_id, 0);
1789  }
1790}
1791
1792void AutocompleteEditViewGtk::HandleMarkSetAlways(GtkTextBuffer* buffer,
1793                                                  GtkTextIter* location,
1794                                                  GtkTextMark* mark) {
1795  if (mark == instant_mark_)
1796    return;
1797
1798  GtkTextIter new_iter = *location;
1799  ValidateTextBufferIter(&new_iter);
1800
1801  // "mark-set" signal is actually emitted after the mark's location is already
1802  // set, so if the location is beyond the instant anchor, we need to move the
1803  // mark again, which will emit the signal again. In order to prevent other
1804  // signal handlers from being called twice, we need to stop signal emission
1805  // before moving the mark again.
1806  if (gtk_text_iter_compare(&new_iter, location)) {
1807    static guint signal_id = g_signal_lookup("mark-set", GTK_TYPE_TEXT_BUFFER);
1808    g_signal_stop_emission(buffer, signal_id, 0);
1809    gtk_text_buffer_move_mark(buffer, mark, &new_iter);
1810  }
1811}
1812
1813// static
1814void AutocompleteEditViewGtk::ClipboardGetSelectionThunk(
1815    GtkClipboard* clipboard,
1816    GtkSelectionData* selection_data,
1817    guint info,
1818    gpointer object) {
1819  AutocompleteEditViewGtk* edit_view =
1820      reinterpret_cast<AutocompleteEditViewGtk*>(
1821          g_object_get_data(G_OBJECT(object), kAutocompleteEditViewGtkKey));
1822  edit_view->ClipboardGetSelection(clipboard, selection_data, info);
1823}
1824
1825void AutocompleteEditViewGtk::ClipboardGetSelection(
1826    GtkClipboard* clipboard,
1827    GtkSelectionData* selection_data,
1828    guint info) {
1829  gtk_selection_data_set_text(selection_data, primary_selection_text_.c_str(),
1830                              primary_selection_text_.size());
1831}
1832
1833std::string AutocompleteEditViewGtk::GetSelectedText() const {
1834  GtkTextIter start, end;
1835  std::string result;
1836  if (gtk_text_buffer_get_selection_bounds(text_buffer_, &start, &end)) {
1837    gchar* text = gtk_text_iter_get_text(&start, &end);
1838    size_t text_len = strlen(text);
1839    if (text_len)
1840      result = std::string(text, text_len);
1841    g_free(text);
1842  }
1843  return result;
1844}
1845
1846void AutocompleteEditViewGtk::UpdatePrimarySelectionIfValidURL() {
1847  std::wstring text = UTF8ToWide(GetSelectedText());
1848
1849  if (text.empty())
1850    return;
1851
1852  // Use AdjustTextForCopy to make sure we prefix the text with 'http://'.
1853  CharRange selection = GetSelection();
1854  GURL url;
1855  bool write_url;
1856  model_->AdjustTextForCopy(selection.selection_min(), IsSelectAll(), &text,
1857                            &url, &write_url);
1858  if (write_url) {
1859    selected_text_ = WideToUTF8(text);
1860    OwnPrimarySelection(selected_text_);
1861  }
1862}
1863
1864#if GTK_CHECK_VERSION(2, 20, 0)
1865void AutocompleteEditViewGtk::HandlePreeditChanged(GtkWidget* sender,
1866                                                   const gchar* preedit) {
1867  // GtkTextView won't fire "begin-user-action" and "end-user-action" signals
1868  // when changing the preedit string, so we need to call
1869  // OnBeforePossibleChange() and OnAfterPossibleChange() by ourselves.
1870  OnBeforePossibleChange();
1871  if (preedit && *preedit) {
1872    // GtkTextView will only delete the selection range when committing the
1873    // preedit string, which will cause very strange behavior, so we need to
1874    // delete the selection range here explicitly. See http://crbug.com/18808.
1875    if (preedit_.empty())
1876      gtk_text_buffer_delete_selection(text_buffer_, false, true);
1877    preedit_ = UTF8ToWide(preedit);
1878  } else {
1879    preedit_.clear();
1880  }
1881  OnAfterPossibleChange();
1882}
1883#endif
1884
1885void AutocompleteEditViewGtk::HandleWindowSetFocus(
1886    GtkWindow* sender, GtkWidget* focus) {
1887  // This is actually a guess. If the focused widget changes in "focus-out"
1888  // event handler, then the window will respect that and won't focus
1889  // |focus|. I doubt that is likely to happen however.
1890  going_to_focus_ = focus;
1891}
1892
1893void AutocompleteEditViewGtk::HandleUndoRedo(GtkWidget* sender) {
1894  OnBeforePossibleChange();
1895}
1896
1897void AutocompleteEditViewGtk::HandleUndoRedoAfter(GtkWidget* sender) {
1898  OnAfterPossibleChange();
1899}
1900
1901void AutocompleteEditViewGtk::GetTextBufferBounds(GtkTextIter* start,
1902                                                  GtkTextIter* end) const {
1903  gtk_text_buffer_get_start_iter(text_buffer_, start);
1904  gtk_text_buffer_get_iter_at_mark(text_buffer_, end, instant_mark_);
1905}
1906
1907void AutocompleteEditViewGtk::ValidateTextBufferIter(GtkTextIter* iter) const {
1908  if (!instant_mark_)
1909    return;
1910
1911  GtkTextIter end;
1912  gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_);
1913  if (gtk_text_iter_compare(iter, &end) > 0)
1914    *iter = end;
1915}
1916
1917void AutocompleteEditViewGtk::AdjustVerticalAlignmentOfInstantView() {
1918  // By default, GtkTextView layouts an anchored child widget just above the
1919  // baseline, so we need to move the |instant_view_| down to make sure it
1920  // has the same baseline as the |text_view_|.
1921  PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(instant_view_));
1922  int height;
1923  pango_layout_get_size(layout, NULL, &height);
1924  PangoLayoutIter* iter = pango_layout_get_iter(layout);
1925  int baseline = pango_layout_iter_get_baseline(iter);
1926  pango_layout_iter_free(iter);
1927  g_object_set(instant_anchor_tag_, "rise", baseline - height, NULL);
1928}
1929