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