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