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