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