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