summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ui
diff options
context:
space:
mode:
authortfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-26 22:16:14 +0000
committertfarina@chromium.org <tfarina@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-26 22:16:14 +0000
commit7dcc272d07e2dc3fb5394439edc2975498c9ce98 (patch)
tree07b3c6a293d7d80c6e91899bfc19c00756c72cd8 /chrome/browser/ui
parent51c84ee35f03c61d35c30290d46df332c8ddeae5 (diff)
downloadchromium_src-7dcc272d07e2dc3fb5394439edc2975498c9ce98.zip
chromium_src-7dcc272d07e2dc3fb5394439edc2975498c9ce98.tar.gz
chromium_src-7dcc272d07e2dc3fb5394439edc2975498c9ce98.tar.bz2
gtk: Move AutocompleteEditViewGtk/autocomplete_edit_view_gtk.* to ui/gtk/omnibox directory.
- Rename AutocompleteEditViewGtk to OmniboxViewGtk. - Move autocomplete_edit_view_gtk.* to omnibox_view_gtk.* BUG=80186 TEST=None R=pkasting@chromium.org,evan@chromium.org Review URL: http://codereview.chromium.org/6905030 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83086 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui')
-rw-r--r--chrome/browser/ui/gtk/location_bar_view_gtk.cc18
-rw-r--r--chrome/browser/ui/gtk/location_bar_view_gtk.h6
-rw-r--r--chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc2346
-rw-r--r--chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h546
-rw-r--r--chrome/browser/ui/views/location_bar/location_bar_view.cc6
-rw-r--r--chrome/browser/ui/views/location_bar/location_bar_view.h2
6 files changed, 2907 insertions, 17 deletions
diff --git a/chrome/browser/ui/gtk/location_bar_view_gtk.cc b/chrome/browser/ui/gtk/location_bar_view_gtk.cc
index 500626f..0b73891 100644
--- a/chrome/browser/ui/gtk/location_bar_view_gtk.cc
+++ b/chrome/browser/ui/gtk/location_bar_view_gtk.cc
@@ -17,7 +17,6 @@
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/accessibility_events.h"
#include "chrome/browser/alternate_nav_url_fetcher.h"
-#include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h"
#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/defaults.h"
@@ -41,6 +40,7 @@
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/nine_box.h"
+#include "chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h"
#include "chrome/browser/ui/gtk/rounded_window.h"
#include "chrome/browser/ui/gtk/view_id_util.h"
#include "chrome/browser/ui/omnibox/location_bar_util.h"
@@ -184,7 +184,7 @@ LocationBarViewGtk::~LocationBarViewGtk() {
void LocationBarViewGtk::Init(bool popup_window_mode) {
popup_window_mode_ = popup_window_mode;
- // Create the widget first, so we can pass it to the AutocompleteEditViewGtk.
+ // Create the widget first, so we can pass it to the OmniboxViewGtk.
hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding));
gtk_container_set_border_width(GTK_CONTAINER(hbox_.get()), kHboxBorder);
// We will paint for the alignment, to paint the background and border.
@@ -193,13 +193,13 @@ void LocationBarViewGtk::Init(bool popup_window_mode) {
// the home button on/off.
gtk_widget_set_redraw_on_allocate(hbox_.get(), TRUE);
- // Now initialize the AutocompleteEditViewGtk.
- location_entry_.reset(new AutocompleteEditViewGtk(this,
- toolbar_model_,
- profile_,
- command_updater_,
- popup_window_mode_,
- hbox_.get()));
+ // Now initialize the OmniboxViewGtk.
+ location_entry_.reset(new OmniboxViewGtk(this,
+ toolbar_model_,
+ profile_,
+ command_updater_,
+ popup_window_mode_,
+ hbox_.get()));
location_entry_->Init();
g_signal_connect(hbox_.get(), "expose-event",
diff --git a/chrome/browser/ui/gtk/location_bar_view_gtk.h b/chrome/browser/ui/gtk/location_bar_view_gtk.h
index b07df3b..ec20f2d 100644
--- a/chrome/browser/ui/gtk/location_bar_view_gtk.h
+++ b/chrome/browser/ui/gtk/location_bar_view_gtk.h
@@ -16,7 +16,6 @@
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "chrome/browser/autocomplete/autocomplete_edit.h"
-#include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
#include "chrome/browser/extensions/image_loading_tracker.h"
#include "chrome/browser/first_run/first_run.h"
@@ -30,11 +29,12 @@
#include "content/common/notification_registrar.h"
#include "content/common/page_transition_types.h"
#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/animation/animation_delegate.h"
#include "ui/base/animation/slide_animation.h"
#include "ui/base/gtk/gtk_signal.h"
#include "webkit/glue/window_open_disposition.h"
-class AutocompleteEditViewGtk;
+class OmniboxViewGtk;
class Browser;
class CommandUpdater;
class ContentSettingImageModel;
@@ -403,7 +403,7 @@ class LocationBarViewGtk : public AutocompleteEditController,
GtkWidget* tab_to_search_hint_icon_;
GtkWidget* tab_to_search_hint_trailing_label_;
- scoped_ptr<AutocompleteEditViewGtk> location_entry_;
+ scoped_ptr<OmniboxViewGtk> location_entry_;
// Alignment used to wrap |location_entry_|.
GtkWidget* location_entry_alignment_;
diff --git a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
new file mode 100644
index 0000000..c2cb83a
--- /dev/null
+++ b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
@@ -0,0 +1,2346 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h"
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/autocomplete/autocomplete_edit.h"
+#include "chrome/browser/autocomplete/autocomplete_match.h"
+#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
+#include "chrome/browser/bookmarks/bookmark_node_data.h"
+#include "chrome/browser/command_updater.h"
+#include "chrome/browser/defaults.h"
+#include "chrome/browser/instant/instant_controller.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/ui/gtk/gtk_util.h"
+#include "chrome/browser/ui/gtk/view_id_util.h"
+#include "chrome/browser/ui/toolbar/toolbar_model.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/common/notification_service.h"
+#include "googleurl/src/gurl.h"
+#include "grit/generated_resources.h"
+#include "net/base/escape.h"
+#include "third_party/undoview/undo_view.h"
+#include "ui/base/animation/multi_animation.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/gtk_util.h"
+#include "ui/gfx/skia_utils_gtk.h"
+
+#if defined(TOOLKIT_VIEWS)
+#include "chrome/browser/autocomplete/autocomplete_edit_view_views.h"
+#include "chrome/browser/ui/views/autocomplete/autocomplete_popup_contents_view.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
+#include "views/controls/textfield/native_textfield_views.h"
+#include "views/events/event.h"
+#else
+#include "chrome/browser/autocomplete/autocomplete_popup_view_gtk.h"
+#include "chrome/browser/ui/gtk/gtk_theme_service.h"
+#include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
+#endif
+
+namespace {
+
+const gchar* kOmniboxViewGtkKey = "__OMNIBOX_VIEW_GTK__";
+
+const char kTextBaseColor[] = "#808080";
+const char kSecureSchemeColor[] = "#079500";
+const char kSecurityErrorSchemeColor[] = "#a20000";
+
+const double kStrikethroughStrokeRed = 162.0 / 256.0;
+const double kStrikethroughStrokeWidth = 2.0;
+
+size_t GetUTF8Offset(const string16& text, size_t text_offset) {
+ return UTF16ToUTF8(text.substr(0, text_offset)).size();
+}
+
+// A helper method for determining a valid drag operation given the allowed
+// operation. We prefer copy over link.
+int CopyOrLinkDragOperation(int drag_operation) {
+ if (drag_operation & ui::DragDropTypes::DRAG_COPY)
+ return ui::DragDropTypes::DRAG_COPY;
+ if (drag_operation & ui::DragDropTypes::DRAG_LINK)
+ return ui::DragDropTypes::DRAG_LINK;
+ return ui::DragDropTypes::DRAG_NONE;
+}
+
+// Stores GTK+-specific state so it can be restored after switching tabs.
+struct ViewState {
+ explicit ViewState(const OmniboxViewGtk::CharRange& selection_range)
+ : selection_range(selection_range) {
+ }
+
+ // Range of selected text.
+ OmniboxViewGtk::CharRange selection_range;
+};
+
+struct AutocompleteEditState {
+ AutocompleteEditState(const AutocompleteEditModel::State& model_state,
+ const ViewState& view_state)
+ : model_state(model_state),
+ view_state(view_state) {
+ }
+
+ const AutocompleteEditModel::State model_state;
+ const ViewState view_state;
+};
+
+// Returns a lazily initialized property bag accessor for saving our state in a
+// TabContents.
+PropertyAccessor<AutocompleteEditState>* GetStateAccessor() {
+ static PropertyAccessor<AutocompleteEditState> state;
+ return &state;
+}
+
+// Set up style properties to override the default GtkTextView; if a theme has
+// overridden some of these properties, an inner-line will be displayed inside
+// the fake GtkTextEntry.
+void SetEntryStyle() {
+ static bool style_was_set = false;
+
+ if (style_was_set)
+ return;
+ style_was_set = true;
+
+ gtk_rc_parse_string(
+ "style \"chrome-location-bar-entry\" {"
+ " xthickness = 0\n"
+ " ythickness = 0\n"
+ " GtkWidget::focus_padding = 0\n"
+ " GtkWidget::focus-line-width = 0\n"
+ " GtkWidget::interior_focus = 0\n"
+ " GtkWidget::internal-padding = 0\n"
+ " GtkContainer::border-width = 0\n"
+ "}\n"
+ "widget \"*chrome-location-bar-entry\" "
+ "style \"chrome-location-bar-entry\"");
+}
+
+// Copied from GTK+. Called when we lose the primary selection. This will clear
+// the selection in the text buffer.
+void ClipboardSelectionCleared(GtkClipboard* clipboard,
+ gpointer data) {
+ GtkTextIter insert;
+ GtkTextIter selection_bound;
+ GtkTextBuffer* buffer = GTK_TEXT_BUFFER(data);
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &insert,
+ gtk_text_buffer_get_insert(buffer));
+ gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound,
+ gtk_text_buffer_get_selection_bound(buffer));
+
+ if (!gtk_text_iter_equal(&insert, &selection_bound)) {
+ gtk_text_buffer_move_mark(buffer,
+ gtk_text_buffer_get_selection_bound(buffer),
+ &insert);
+ }
+}
+
+} // namespace
+
+OmniboxViewGtk::OmniboxViewGtk(
+ AutocompleteEditController* controller,
+ ToolbarModel* toolbar_model,
+ Profile* profile,
+ CommandUpdater* command_updater,
+ bool popup_window_mode,
+#if defined(TOOLKIT_VIEWS)
+ views::View* location_bar)
+#else
+ GtkWidget* location_bar)
+#endif
+ : text_view_(NULL),
+ tag_table_(NULL),
+ text_buffer_(NULL),
+ faded_text_tag_(NULL),
+ secure_scheme_tag_(NULL),
+ security_error_scheme_tag_(NULL),
+ normal_text_tag_(NULL),
+ instant_anchor_tag_(NULL),
+ instant_view_(NULL),
+ instant_mark_(NULL),
+ model_(new AutocompleteEditModel(this, controller, profile)),
+ controller_(controller),
+ toolbar_model_(toolbar_model),
+ command_updater_(command_updater),
+ popup_window_mode_(popup_window_mode),
+ security_level_(ToolbarModel::NONE),
+ mark_set_handler_id_(0),
+#if defined(OS_CHROMEOS)
+ button_1_pressed_(false),
+ text_selected_during_click_(false),
+ text_view_focused_before_button_press_(false),
+#endif
+#if defined(TOOLKIT_VIEWS)
+ location_bar_view_(location_bar),
+#else
+ theme_service_(GtkThemeService::GetFrom(profile)),
+#endif
+ enter_was_pressed_(false),
+ tab_was_pressed_(false),
+ paste_clipboard_requested_(false),
+ enter_was_inserted_(false),
+ selection_suggested_(false),
+ delete_was_pressed_(false),
+ delete_at_end_pressed_(false),
+ handling_key_press_(false),
+ content_maybe_changed_by_key_press_(false),
+ update_popup_without_focus_(false),
+#if GTK_CHECK_VERSION(2, 20, 0)
+ preedit_size_before_change_(0),
+#endif
+ going_to_focus_(NULL) {
+ popup_view_.reset(
+#if defined(TOOLKIT_VIEWS)
+ new AutocompletePopupContentsView
+#else
+ new AutocompletePopupViewGtk
+#endif
+ (GetFont(), this, model_.get(), profile, location_bar));
+}
+
+OmniboxViewGtk::~OmniboxViewGtk() {
+ NotificationService::current()->Notify(
+ NotificationType::AUTOCOMPLETE_EDIT_DESTROYED,
+ Source<OmniboxViewGtk>(this),
+ NotificationService::NoDetails());
+
+ // Explicitly teardown members which have a reference to us. Just to be safe
+ // we want them to be destroyed before destroying any other internal state.
+ popup_view_.reset();
+ model_.reset();
+
+ // We own our widget and TextView related objects.
+ if (alignment_.get()) { // Init() has been called.
+ alignment_.Destroy();
+ g_object_unref(text_buffer_);
+ g_object_unref(tag_table_);
+ // The tags we created are owned by the tag_table, and should be destroyed
+ // along with it. We don't hold our own reference to them.
+ }
+}
+
+void OmniboxViewGtk::Init() {
+ SetEntryStyle();
+
+ // The height of the text view is going to change based on the font used. We
+ // don't want to stretch the height, and we want it vertically centered.
+ alignment_.Own(gtk_alignment_new(0., 0.5, 1.0, 0.0));
+ gtk_widget_set_name(alignment_.get(),
+ "chrome-autocomplete-edit-view");
+
+ // The GtkTagTable and GtkTextBuffer are not initially unowned, so we have
+ // our own reference when we create them, and we own them. Adding them to
+ // the other objects adds a reference; it doesn't adopt them.
+ tag_table_ = gtk_text_tag_table_new();
+ text_buffer_ = gtk_text_buffer_new(tag_table_);
+ g_object_set_data(G_OBJECT(text_buffer_), kOmniboxViewGtkKey, this);
+
+ // We need to run this two handlers before undo manager's handlers, so that
+ // text iterators modified by these handlers can be passed down to undo
+ // manager's handlers.
+ g_signal_connect(text_buffer_, "delete-range",
+ G_CALLBACK(&HandleDeleteRangeThunk), this);
+ g_signal_connect(text_buffer_, "mark-set",
+ G_CALLBACK(&HandleMarkSetAlwaysThunk), this);
+
+ text_view_ = gtk_undo_view_new(text_buffer_);
+ if (popup_window_mode_)
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view_), false);
+
+ // One pixel left margin is necessary to make the cursor visible when UI
+ // language direction is LTR but |text_buffer_|'s content direction is RTL.
+ gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view_), 1);
+
+ // See SetEntryStyle() comments.
+ gtk_widget_set_name(text_view_, "chrome-location-bar-entry");
+
+ // The text view was floating. It will now be owned by the alignment.
+ gtk_container_add(GTK_CONTAINER(alignment_.get()), text_view_);
+
+ // Do not allow inserting tab characters when pressing Tab key, so that when
+ // Tab key is pressed, |text_view_| will emit "move-focus" signal, which will
+ // be intercepted by our own handler to trigger Tab to search feature when
+ // necessary.
+ gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(text_view_), FALSE);
+
+ faded_text_tag_ = gtk_text_buffer_create_tag(text_buffer_,
+ NULL, "foreground", kTextBaseColor, NULL);
+ secure_scheme_tag_ = gtk_text_buffer_create_tag(text_buffer_,
+ NULL, "foreground", kSecureSchemeColor, NULL);
+ security_error_scheme_tag_ = gtk_text_buffer_create_tag(text_buffer_,
+ NULL, "foreground", kSecurityErrorSchemeColor, NULL);
+ normal_text_tag_ = gtk_text_buffer_create_tag(text_buffer_,
+ NULL, "foreground", "#000000", NULL);
+
+ // NOTE: This code used to connect to "changed", however this was fired too
+ // often and during bad times (our own buffer changes?). It works out much
+ // better to listen to end-user-action, which should be fired whenever the
+ // user makes some sort of change to the buffer.
+ g_signal_connect(text_buffer_, "begin-user-action",
+ G_CALLBACK(&HandleBeginUserActionThunk), this);
+ g_signal_connect(text_buffer_, "end-user-action",
+ G_CALLBACK(&HandleEndUserActionThunk), this);
+ // We connect to key press and release for special handling of a few keys.
+ g_signal_connect(text_view_, "key-press-event",
+ G_CALLBACK(&HandleKeyPressThunk), this);
+ g_signal_connect(text_view_, "key-release-event",
+ G_CALLBACK(&HandleKeyReleaseThunk), this);
+ g_signal_connect(text_view_, "button-press-event",
+ G_CALLBACK(&HandleViewButtonPressThunk), this);
+ g_signal_connect(text_view_, "button-release-event",
+ G_CALLBACK(&HandleViewButtonReleaseThunk), this);
+ g_signal_connect(text_view_, "focus-in-event",
+ G_CALLBACK(&HandleViewFocusInThunk), this);
+ g_signal_connect(text_view_, "focus-out-event",
+ G_CALLBACK(&HandleViewFocusOutThunk), this);
+ // NOTE: The GtkTextView documentation asks you not to connect to this
+ // signal, but it is very convenient and clean for catching up/down.
+ g_signal_connect(text_view_, "move-cursor",
+ G_CALLBACK(&HandleViewMoveCursorThunk), this);
+ g_signal_connect(text_view_, "move-focus",
+ G_CALLBACK(&HandleViewMoveFocusThunk), this);
+ // Override the size request. We want to keep the original height request
+ // from the widget, since that's font dependent. We want to ignore the width
+ // so we don't force a minimum width based on the text length.
+ g_signal_connect(text_view_, "size-request",
+ G_CALLBACK(&HandleViewSizeRequestThunk), this);
+ g_signal_connect(text_view_, "populate-popup",
+ G_CALLBACK(&HandlePopulatePopupThunk), this);
+ mark_set_handler_id_ = g_signal_connect(
+ text_buffer_, "mark-set", G_CALLBACK(&HandleMarkSetThunk), this);
+ mark_set_handler_id2_ = g_signal_connect_after(
+ text_buffer_, "mark-set", G_CALLBACK(&HandleMarkSetAfterThunk), this);
+ g_signal_connect(text_view_, "drag-data-received",
+ G_CALLBACK(&HandleDragDataReceivedThunk), this);
+ // Override the text_view_'s default drag-data-get handler by calling our own
+ // version after the normal call has happened.
+ g_signal_connect_after(text_view_, "drag-data-get",
+ G_CALLBACK(&HandleDragDataGetThunk), this);
+ g_signal_connect(text_view_, "backspace",
+ G_CALLBACK(&HandleBackSpaceThunk), this);
+ g_signal_connect(text_view_, "copy-clipboard",
+ G_CALLBACK(&HandleCopyClipboardThunk), this);
+ g_signal_connect(text_view_, "cut-clipboard",
+ G_CALLBACK(&HandleCutClipboardThunk), this);
+ g_signal_connect(text_view_, "paste-clipboard",
+ G_CALLBACK(&HandlePasteClipboardThunk), this);
+ g_signal_connect_after(text_view_, "expose-event",
+ G_CALLBACK(&HandleExposeEventThunk), this);
+ g_signal_connect(text_view_, "direction-changed",
+ G_CALLBACK(&HandleWidgetDirectionChangedThunk), this);
+ g_signal_connect(text_view_, "delete-from-cursor",
+ G_CALLBACK(&HandleDeleteFromCursorThunk), this);
+ g_signal_connect(text_view_, "hierarchy-changed",
+ G_CALLBACK(&HandleHierarchyChangedThunk), this);
+#if GTK_CHECK_VERSION(2, 20, 0)
+ g_signal_connect(text_view_, "preedit-changed",
+ G_CALLBACK(&HandlePreeditChangedThunk), this);
+#endif
+ g_signal_connect(text_view_, "undo", G_CALLBACK(&HandleUndoRedoThunk), this);
+ g_signal_connect(text_view_, "redo", G_CALLBACK(&HandleUndoRedoThunk), this);
+ g_signal_connect_after(text_view_, "undo",
+ G_CALLBACK(&HandleUndoRedoAfterThunk), this);
+ g_signal_connect_after(text_view_, "redo",
+ G_CALLBACK(&HandleUndoRedoAfterThunk), this);
+ g_signal_connect(text_view_, "destroy",
+ G_CALLBACK(&gtk_widget_destroyed), &text_view_);
+
+ // Setup for the Instant suggestion text view.
+ // GtkLabel is used instead of GtkTextView to get transparent background.
+ instant_view_ = gtk_label_new(NULL);
+ gtk_widget_set_no_show_all(instant_view_, TRUE);
+ gtk_label_set_selectable(GTK_LABEL(instant_view_), TRUE);
+
+ GtkTextIter end_iter;
+ gtk_text_buffer_get_end_iter(text_buffer_, &end_iter);
+
+ // Insert a Zero Width Space character just before the instant anchor.
+ // It's a hack to workaround a bug of GtkTextView which can not align the
+ // preedit string and a child anchor correctly when there is no other content
+ // around the preedit string.
+ gtk_text_buffer_insert(text_buffer_, &end_iter, "\342\200\213", -1);
+ GtkTextChildAnchor* instant_anchor =
+ gtk_text_buffer_create_child_anchor(text_buffer_, &end_iter);
+
+ gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(text_view_),
+ instant_view_,
+ instant_anchor);
+
+ instant_anchor_tag_ = gtk_text_buffer_create_tag(text_buffer_, NULL, NULL);
+
+ GtkTextIter anchor_iter;
+ gtk_text_buffer_get_iter_at_child_anchor(text_buffer_, &anchor_iter,
+ instant_anchor);
+ gtk_text_buffer_apply_tag(text_buffer_, instant_anchor_tag_,
+ &anchor_iter, &end_iter);
+
+ GtkTextIter start_iter;
+ gtk_text_buffer_get_start_iter(text_buffer_, &start_iter);
+ instant_mark_ =
+ gtk_text_buffer_create_mark(text_buffer_, NULL, &start_iter, FALSE);
+
+ // Hooking up this handler after setting up above hacks for Instant view, so
+ // that we won't filter out the special ZWP mark itself.
+ g_signal_connect(text_buffer_, "insert-text",
+ G_CALLBACK(&HandleInsertTextThunk), this);
+
+ AdjustVerticalAlignmentOfInstantView();
+
+ ui::MultiAnimation::Parts parts;
+ parts.push_back(ui::MultiAnimation::Part(
+ InstantController::kAutoCommitPauseTimeMS, ui::Tween::ZERO));
+ parts.push_back(ui::MultiAnimation::Part(
+ InstantController::kAutoCommitFadeInTimeMS, ui::Tween::EASE_IN));
+ instant_animation_.reset(new ui::MultiAnimation(parts));
+ instant_animation_->set_continuous(false);
+
+#if !defined(TOOLKIT_VIEWS)
+ registrar_.Add(this,
+ NotificationType::BROWSER_THEME_CHANGED,
+ NotificationService::AllSources());
+ theme_service_->InitThemesFor(this);
+#else
+ // Manually invoke SetBaseColor() because TOOLKIT_VIEWS doesn't observe
+ // themes.
+ SetBaseColor();
+#endif
+
+ ViewIDUtil::SetID(GetNativeView(), VIEW_ID_AUTOCOMPLETE);
+}
+
+void OmniboxViewGtk::HandleHierarchyChanged(GtkWidget* sender,
+ GtkWidget* old_toplevel) {
+ GtkWindow* new_toplevel = platform_util::GetTopLevel(sender);
+ if (!new_toplevel)
+ return;
+
+ // Use |signals_| to make sure we don't get called back after destruction.
+ signals_.Connect(new_toplevel, "set-focus",
+ G_CALLBACK(&HandleWindowSetFocusThunk), this);
+}
+
+void OmniboxViewGtk::SetFocus() {
+ DCHECK(text_view_);
+ gtk_widget_grab_focus(text_view_);
+}
+
+int OmniboxViewGtk::WidthOfTextAfterCursor() {
+ // Not used.
+ return -1;
+}
+
+AutocompleteEditModel* OmniboxViewGtk::model() {
+ return model_.get();
+}
+
+const AutocompleteEditModel* OmniboxViewGtk::model() const {
+ return model_.get();
+}
+
+void OmniboxViewGtk::SaveStateToTab(TabContents* tab) {
+ DCHECK(tab);
+ // If any text has been selected, register it as the PRIMARY selection so it
+ // can still be pasted via middle-click after the text view is cleared.
+ if (!selected_text_.empty())
+ SavePrimarySelection(selected_text_);
+ // NOTE: GetStateForTabSwitch may affect GetSelection, so order is important.
+ AutocompleteEditModel::State model_state = model_->GetStateForTabSwitch();
+ GetStateAccessor()->SetProperty(
+ tab->property_bag(),
+ AutocompleteEditState(model_state, ViewState(GetSelection())));
+}
+
+void OmniboxViewGtk::Update(const TabContents* contents) {
+ // NOTE: We're getting the URL text here from the ToolbarModel.
+ bool visibly_changed_permanent_text =
+ model_->UpdatePermanentText(WideToUTF16Hack(toolbar_model_->GetText()));
+
+ ToolbarModel::SecurityLevel security_level =
+ toolbar_model_->GetSecurityLevel();
+ bool changed_security_level = (security_level != security_level_);
+ security_level_ = security_level;
+
+ if (contents) {
+ selected_text_.clear();
+ RevertAll();
+ const AutocompleteEditState* state =
+ GetStateAccessor()->GetProperty(contents->property_bag());
+ if (state) {
+ model_->RestoreState(state->model_state);
+
+ // Move the marks for the cursor and the other end of the selection to
+ // the previously-saved offsets (but preserve PRIMARY).
+ StartUpdatingHighlightedText();
+ SetSelectedRange(state->view_state.selection_range);
+ FinishUpdatingHighlightedText();
+ }
+ } else if (visibly_changed_permanent_text) {
+ RevertAll();
+ // TODO(deanm): There should be code to restore select all here.
+ } else if (changed_security_level) {
+ EmphasizeURLComponents();
+ }
+}
+
+void OmniboxViewGtk::OpenURL(const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition,
+ const GURL& alternate_nav_url,
+ size_t selected_line,
+ const string16& keyword) {
+ if (!url.is_valid())
+ return;
+
+ model_->OpenURL(url, disposition, transition, alternate_nav_url,
+ selected_line, keyword);
+}
+
+string16 OmniboxViewGtk::GetText() const {
+ GtkTextIter start, end;
+ GetTextBufferBounds(&start, &end);
+ gchar* utf8 = gtk_text_buffer_get_text(text_buffer_, &start, &end, false);
+ string16 out(UTF8ToUTF16(utf8));
+ g_free(utf8);
+
+#if GTK_CHECK_VERSION(2, 20, 0)
+ // We need to treat the text currently being composed by the input method as
+ // part of the text content, so that omnibox can work correctly in the middle
+ // of composition.
+ if (preedit_.size()) {
+ GtkTextMark* mark = gtk_text_buffer_get_insert(text_buffer_);
+ gtk_text_buffer_get_iter_at_mark(text_buffer_, &start, mark);
+ out.insert(gtk_text_iter_get_offset(&start), preedit_);
+ }
+#endif
+ return out;
+}
+
+bool OmniboxViewGtk::IsEditingOrEmpty() const {
+ return model_->user_input_in_progress() || (GetTextLength() == 0);
+}
+
+int OmniboxViewGtk::GetIcon() const {
+ return IsEditingOrEmpty() ?
+ AutocompleteMatch::TypeToIcon(model_->CurrentTextType()) :
+ toolbar_model_->GetIcon();
+}
+
+void OmniboxViewGtk::SetUserText(const string16& text) {
+ SetUserText(text, text, true);
+}
+
+void OmniboxViewGtk::SetUserText(const string16& text,
+ const string16& display_text,
+ bool update_popup) {
+ model_->SetUserText(text);
+ // TODO(deanm): something about selection / focus change here.
+ SetWindowTextAndCaretPos(display_text, display_text.length());
+ if (update_popup)
+ UpdatePopup();
+ TextChanged();
+}
+
+void OmniboxViewGtk::SetWindowTextAndCaretPos(const string16& text,
+ size_t caret_pos) {
+ CharRange range(static_cast<int>(caret_pos), static_cast<int>(caret_pos));
+ SetTextAndSelectedRange(text, range);
+}
+
+void OmniboxViewGtk::SetForcedQuery() {
+ const string16 current_text(GetText());
+ const size_t start = current_text.find_first_not_of(kWhitespaceUTF16);
+ if (start == string16::npos || (current_text[start] != '?')) {
+ SetUserText(ASCIIToUTF16("?"));
+ } else {
+ StartUpdatingHighlightedText();
+ SetSelectedRange(CharRange(current_text.size(), start + 1));
+ FinishUpdatingHighlightedText();
+ }
+}
+
+bool OmniboxViewGtk::IsSelectAll() {
+ GtkTextIter sel_start, sel_end;
+ gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end);
+
+ GtkTextIter start, end;
+ GetTextBufferBounds(&start, &end);
+
+ // Returns true if the |text_buffer_| is empty.
+ return gtk_text_iter_equal(&start, &sel_start) &&
+ gtk_text_iter_equal(&end, &sel_end);
+}
+
+bool OmniboxViewGtk::DeleteAtEndPressed() {
+ return delete_at_end_pressed_;
+}
+
+void OmniboxViewGtk::GetSelectionBounds(string16::size_type* start,
+ string16::size_type* end) {
+ CharRange selection = GetSelection();
+ *start = static_cast<size_t>(selection.cp_min);
+ *end = static_cast<size_t>(selection.cp_max);
+}
+
+void OmniboxViewGtk::SelectAll(bool reversed) {
+ // SelectAll() is invoked as a side effect of other actions (e.g. switching
+ // tabs or hitting Escape) in autocomplete_edit.cc, so we don't update the
+ // PRIMARY selection here.
+ SelectAllInternal(reversed, false);
+}
+
+void OmniboxViewGtk::RevertAll() {
+ ClosePopup();
+ model_->Revert();
+ TextChanged();
+}
+
+void OmniboxViewGtk::UpdatePopup() {
+ model_->SetInputInProgress(true);
+ if (!update_popup_without_focus_ && !model_->has_focus())
+ return;
+
+ // Don't inline autocomplete when the caret/selection isn't at the end of
+ // the text, or in the middle of composition.
+ CharRange sel = GetSelection();
+ bool no_inline_autocomplete =
+ std::max(sel.cp_max, sel.cp_min) < GetTextLength() || IsImeComposing();
+ model_->StartAutocomplete(sel.cp_min != sel.cp_max, no_inline_autocomplete);
+}
+
+void OmniboxViewGtk::ClosePopup() {
+ model_->StopAutocomplete();
+}
+
+void OmniboxViewGtk::OnTemporaryTextMaybeChanged(
+ const string16& display_text,
+ bool save_original_selection) {
+ if (save_original_selection)
+ saved_temporary_selection_ = GetSelection();
+
+ StartUpdatingHighlightedText();
+ SetWindowTextAndCaretPos(display_text, display_text.length());
+ FinishUpdatingHighlightedText();
+ TextChanged();
+}
+
+bool OmniboxViewGtk::OnInlineAutocompleteTextMaybeChanged(
+ const string16& display_text,
+ size_t user_text_length) {
+ if (display_text == GetText())
+ return false;
+
+ StartUpdatingHighlightedText();
+ CharRange range(display_text.size(), user_text_length);
+ SetTextAndSelectedRange(display_text, range);
+ FinishUpdatingHighlightedText();
+ TextChanged();
+ return true;
+}
+
+void OmniboxViewGtk::OnRevertTemporaryText() {
+ StartUpdatingHighlightedText();
+ SetSelectedRange(saved_temporary_selection_);
+ FinishUpdatingHighlightedText();
+ TextChanged();
+}
+
+void OmniboxViewGtk::OnBeforePossibleChange() {
+ // Record this paste, so we can do different behavior.
+ if (paste_clipboard_requested_) {
+ paste_clipboard_requested_ = false;
+ model_->on_paste();
+ }
+
+ // This method will be called in HandleKeyPress() method just before
+ // handling a key press event. So we should prevent it from being called
+ // when handling the key press event.
+ if (handling_key_press_)
+ return;
+
+ // Record our state.
+ text_before_change_ = GetText();
+ sel_before_change_ = GetSelection();
+#if GTK_CHECK_VERSION(2, 20, 0)
+ preedit_size_before_change_ = preedit_.size();
+#endif
+}
+
+// TODO(deanm): This is mostly stolen from Windows, and will need some work.
+bool OmniboxViewGtk::OnAfterPossibleChange() {
+ // This method will be called in HandleKeyPress() method just after
+ // handling a key press event. So we should prevent it from being called
+ // when handling the key press event.
+ if (handling_key_press_) {
+ content_maybe_changed_by_key_press_ = true;
+ return false;
+ }
+
+ // If the change is caused by an Enter key press event, and the event was not
+ // handled by IME, then it's an unexpected change and shall be reverted here.
+ // {Start|Finish}UpdatingHighlightedText() are called here to prevent the
+ // PRIMARY selection from being changed.
+ if (enter_was_pressed_ && enter_was_inserted_) {
+ StartUpdatingHighlightedText();
+ SetTextAndSelectedRange(text_before_change_, sel_before_change_);
+ FinishUpdatingHighlightedText();
+ return false;
+ }
+
+ const CharRange new_sel = GetSelection();
+ const int length = GetTextLength();
+ const bool selection_differs =
+ ((new_sel.cp_min != new_sel.cp_max) ||
+ (sel_before_change_.cp_min != sel_before_change_.cp_max)) &&
+ ((new_sel.cp_min != sel_before_change_.cp_min) ||
+ (new_sel.cp_max != sel_before_change_.cp_max));
+ const bool at_end_of_edit =
+ (new_sel.cp_min == length && new_sel.cp_max == length);
+
+ // See if the text or selection have changed since OnBeforePossibleChange().
+ const string16 new_text(GetText());
+ text_changed_ = (new_text != text_before_change_);
+#if GTK_CHECK_VERSION(2, 20, 0)
+ text_changed_ =
+ text_changed_ || (preedit_.size() != preedit_size_before_change_);
+#endif
+
+ if (text_changed_)
+ AdjustTextJustification();
+
+ // When the user has deleted text, we don't allow inline autocomplete. Make
+ // sure to not flag cases like selecting part of the text and then pasting
+ // (or typing) the prefix of that selection. (We detect these by making
+ // sure the caret, which should be after any insertion, hasn't moved
+ // forward of the old selection start.)
+ const bool just_deleted_text =
+ (text_before_change_.length() > new_text.length()) &&
+ (new_sel.cp_min <= std::min(sel_before_change_.cp_min,
+ sel_before_change_.cp_max));
+
+ delete_at_end_pressed_ = false;
+
+ const bool something_changed = model_->OnAfterPossibleChange(
+ new_text, new_sel.selection_min(), new_sel.selection_max(),
+ selection_differs, text_changed_, just_deleted_text,
+ !IsImeComposing());
+
+ // If only selection was changed, we don't need to call |controller_|'s
+ // OnChanged() method, which is called in TextChanged().
+ // But we still need to call EmphasizeURLComponents() to make sure the text
+ // attributes are updated correctly.
+ if (something_changed && text_changed_) {
+ TextChanged();
+ } else if (selection_differs) {
+ EmphasizeURLComponents();
+ } else if (delete_was_pressed_ && at_end_of_edit) {
+ delete_at_end_pressed_ = true;
+ model_->OnChanged();
+ }
+ delete_was_pressed_ = false;
+
+ return something_changed;
+}
+
+gfx::NativeView OmniboxViewGtk::GetNativeView() const {
+ return alignment_.get();
+}
+
+CommandUpdater* OmniboxViewGtk::GetCommandUpdater() {
+ return command_updater_;
+}
+
+void OmniboxViewGtk::SetInstantSuggestion(const string16& suggestion,
+ bool animate_to_complete) {
+ std::string suggestion_utf8 = UTF16ToUTF8(suggestion);
+
+ gtk_label_set_text(GTK_LABEL(instant_view_), suggestion_utf8.c_str());
+
+ StopAnimation();
+
+ if (suggestion.empty()) {
+ gtk_widget_hide(instant_view_);
+ return;
+ }
+ if (animate_to_complete
+#if GTK_CHECK_VERSION(2, 20, 0)
+ && preedit_.empty()
+#endif
+ ) {
+ instant_animation_->set_delegate(this);
+ instant_animation_->Start();
+ }
+
+ gtk_widget_show(instant_view_);
+ AdjustVerticalAlignmentOfInstantView();
+ UpdateInstantViewColors();
+}
+
+string16 OmniboxViewGtk::GetInstantSuggestion() const {
+ const gchar* suggestion = gtk_label_get_text(GTK_LABEL(instant_view_));
+ return suggestion ? UTF8ToUTF16(suggestion) : string16();
+}
+
+int OmniboxViewGtk::TextWidth() const {
+ // TextWidth may be called after gtk widget tree is destroyed but
+ // before OmniboxViewGtk gets deleted. This is a safe guard
+ // to avoid accessing |text_view_| that has already been destroyed.
+ // See crbug.com/70192.
+ if (!text_view_)
+ return 0;
+
+ int horizontal_border_size =
+ gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(text_view_),
+ GTK_TEXT_WINDOW_LEFT) +
+ gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(text_view_),
+ GTK_TEXT_WINDOW_RIGHT) +
+ gtk_text_view_get_left_margin(GTK_TEXT_VIEW(text_view_)) +
+ gtk_text_view_get_right_margin(GTK_TEXT_VIEW(text_view_));
+
+ GtkTextIter start, end;
+ GdkRectangle first_char_bounds, last_char_bounds;
+ gtk_text_buffer_get_start_iter(text_buffer_, &start);
+
+ // Use the real end iterator here to take the width of instant suggestion
+ // text into account, so that location bar can layout its children correctly.
+ gtk_text_buffer_get_end_iter(text_buffer_, &end);
+ gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_),
+ &start, &first_char_bounds);
+ gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_),
+ &end, &last_char_bounds);
+
+ gint first_char_start = first_char_bounds.x;
+ gint first_char_end = first_char_start + first_char_bounds.width;
+ gint last_char_start = last_char_bounds.x;
+ gint last_char_end = last_char_start + last_char_bounds.width;
+
+ // bounds width could be negative for RTL text.
+ if (first_char_start > first_char_end)
+ std::swap(first_char_start, first_char_end);
+ if (last_char_start > last_char_end)
+ std::swap(last_char_start, last_char_end);
+
+ gint text_width = first_char_start < last_char_start ?
+ last_char_end - first_char_start : first_char_end - last_char_start;
+
+ return text_width + horizontal_border_size;
+}
+
+bool OmniboxViewGtk::IsImeComposing() const {
+#if GTK_CHECK_VERSION(2, 20, 0)
+ return !preedit_.empty();
+#else
+ return false;
+#endif
+}
+
+#if defined(TOOLKIT_VIEWS)
+views::View* OmniboxViewGtk::AddToView(views::View* parent) {
+ views::NativeViewHost* host = new views::NativeViewHost;
+ parent->AddChildView(host);
+ host->set_focus_view(parent);
+ host->Attach(GetNativeView());
+ return host;
+}
+
+int OmniboxViewGtk::OnPerformDrop(
+ const views::DropTargetEvent& event) {
+ string16 text;
+ const ui::OSExchangeData& data = event.data();
+ if (data.HasURL()) {
+ GURL url;
+ string16 title;
+ if (data.GetURLAndTitle(&url, &title))
+ text = UTF8ToUTF16(url.spec());
+ } else {
+ string16 data_string;
+ if (data.GetString(&data_string))
+ text = CollapseWhitespace(data_string, true);
+ }
+
+ if (!text.empty() && OnPerformDropImpl(text))
+ return CopyOrLinkDragOperation(event.source_operations());
+
+ return ui::DragDropTypes::DRAG_NONE;
+}
+
+// static
+AutocompleteEditView* OmniboxViewGtk::Create(
+ AutocompleteEditController* controller,
+ ToolbarModel* toolbar_model,
+ Profile* profile,
+ CommandUpdater* command_updater,
+ bool popup_window_mode,
+ views::View* location_bar) {
+ if (views::NativeTextfieldViews::IsTextfieldViewsEnabled()) {
+ AutocompleteEditViewViews* autocomplete =
+ new AutocompleteEditViewViews(controller,
+ toolbar_model,
+ profile,
+ command_updater,
+ popup_window_mode,
+ location_bar);
+ autocomplete->Init();
+ return autocomplete;
+ }
+
+ OmniboxViewGtk* autocomplete = new OmniboxViewGtk(controller,
+ toolbar_model,
+ profile,
+ command_updater,
+ popup_window_mode,
+ location_bar);
+ autocomplete->Init();
+
+ // Make all the children of the widget visible. NOTE: this won't display
+ // anything, it just toggles the visible flag.
+ gtk_widget_show_all(autocomplete->GetNativeView());
+ // Hide the widget. NativeViewHostGtk will make it visible again as
+ // necessary.
+ gtk_widget_hide(autocomplete->GetNativeView());
+
+ return autocomplete;
+}
+#endif
+
+void OmniboxViewGtk::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::BROWSER_THEME_CHANGED);
+
+ SetBaseColor();
+}
+
+void OmniboxViewGtk::AnimationEnded(const ui::Animation* animation) {
+ model_->CommitSuggestedText(false);
+}
+
+void OmniboxViewGtk::AnimationProgressed(const ui::Animation* animation) {
+ UpdateInstantViewColors();
+}
+
+void OmniboxViewGtk::AnimationCanceled(const ui::Animation* animation) {
+ UpdateInstantViewColors();
+}
+
+void OmniboxViewGtk::SetBaseColor() {
+ DCHECK(text_view_);
+
+#if defined(TOOLKIT_VIEWS)
+ bool use_gtk = false;
+#else
+ bool use_gtk = theme_service_->UseGtkTheme();
+#endif
+ if (use_gtk) {
+ gtk_widget_modify_cursor(text_view_, NULL, NULL);
+ gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, NULL);
+ gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, NULL);
+ gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, NULL);
+ gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, NULL);
+ gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, NULL);
+
+ gtk_util::UndoForceFontSize(text_view_);
+ gtk_util::UndoForceFontSize(instant_view_);
+
+ // Grab the text colors out of the style and set our tags to use them.
+ GtkStyle* style = gtk_rc_get_style(text_view_);
+
+ // style may be unrealized at this point, so calculate the halfway point
+ // between text[] and base[] manually instead of just using text_aa[].
+ GdkColor average_color = gtk_util::AverageColors(
+ style->text[GTK_STATE_NORMAL], style->base[GTK_STATE_NORMAL]);
+
+ g_object_set(faded_text_tag_, "foreground-gdk", &average_color, NULL);
+ g_object_set(normal_text_tag_, "foreground-gdk",
+ &style->text[GTK_STATE_NORMAL], NULL);
+ } else {
+ const GdkColor* background_color_ptr;
+#if defined(TOOLKIT_VIEWS)
+ const GdkColor background_color = gfx::SkColorToGdkColor(
+ LocationBarView::GetColor(ToolbarModel::NONE,
+ LocationBarView::BACKGROUND));
+ background_color_ptr = &background_color;
+#else
+ background_color_ptr = &LocationBarViewGtk::kBackgroundColor;
+#endif
+ gtk_widget_modify_cursor(
+ text_view_, &gtk_util::kGdkBlack, &gtk_util::kGdkGray);
+ gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, background_color_ptr);
+
+#if !defined(TOOLKIT_VIEWS)
+ GdkColor c;
+ // Override the selected colors so we don't leak colors from the current
+ // gtk theme into the chrome-theme.
+ c = gfx::SkColorToGdkColor(
+ theme_service_->get_active_selection_bg_color());
+ gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, &c);
+
+ c = gfx::SkColorToGdkColor(
+ theme_service_->get_active_selection_fg_color());
+ gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, &c);
+
+ c = gfx::SkColorToGdkColor(
+ theme_service_->get_inactive_selection_bg_color());
+ gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, &c);
+
+ c = gfx::SkColorToGdkColor(
+ theme_service_->get_inactive_selection_fg_color());
+ gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, &c);
+#endif
+
+ // Until we switch to vector graphics, force the font size.
+ gtk_util::ForceFontSizePixels(text_view_, GetFont().GetFontSize());
+ gtk_util::ForceFontSizePixels(instant_view_, GetFont().GetFontSize());
+
+ g_object_set(faded_text_tag_, "foreground", kTextBaseColor, NULL);
+ g_object_set(normal_text_tag_, "foreground", "#000000", NULL);
+ }
+
+ AdjustVerticalAlignmentOfInstantView();
+ UpdateInstantViewColors();
+}
+
+void OmniboxViewGtk::UpdateInstantViewColors() {
+ SkColor selection_text, selection_bg;
+ GdkColor faded_text, normal_bg;
+
+#if defined(TOOLKIT_VIEWS)
+ bool use_gtk = false;
+#else
+ bool use_gtk = theme_service_->UseGtkTheme();
+#endif
+
+ if (use_gtk) {
+ GtkStyle* style = gtk_rc_get_style(instant_view_);
+
+ faded_text = gtk_util::AverageColors(
+ style->text[GTK_STATE_NORMAL], style->base[GTK_STATE_NORMAL]);
+ normal_bg = style->base[GTK_STATE_NORMAL];
+
+ selection_text = gfx::GdkColorToSkColor(style->text[GTK_STATE_SELECTED]);
+ selection_bg = gfx::GdkColorToSkColor(style->base[GTK_STATE_SELECTED]);
+ } else {
+ gdk_color_parse(kTextBaseColor, &faded_text);
+
+#if defined(TOOLKIT_VIEWS)
+ normal_bg = gfx::SkColorToGdkColor(
+ LocationBarView::GetColor(ToolbarModel::NONE,
+ LocationBarView::BACKGROUND));
+ selection_text = LocationBarView::GetColor(
+ ToolbarModel::NONE, LocationBarView::SELECTED_TEXT);
+
+ GtkStyle* style = gtk_rc_get_style(instant_view_);
+ selection_bg = gfx::GdkColorToSkColor(style->base[GTK_STATE_SELECTED]);
+#else
+ normal_bg = LocationBarViewGtk::kBackgroundColor;
+ selection_text =
+ theme_service_->get_active_selection_fg_color();
+ selection_bg =
+ theme_service_->get_active_selection_bg_color();
+#endif
+ }
+
+ double alpha = instant_animation_->is_animating() ?
+ instant_animation_->GetCurrentValue() : 0.0;
+ GdkColor text = gfx::SkColorToGdkColor(color_utils::AlphaBlend(
+ selection_text,
+ gfx::GdkColorToSkColor(faded_text),
+ alpha * 0xff));
+ GdkColor bg = gfx::SkColorToGdkColor(color_utils::AlphaBlend(
+ selection_bg,
+ gfx::GdkColorToSkColor(normal_bg),
+ alpha * 0xff));
+
+ if (alpha > 0.0) {
+ gtk_label_select_region(GTK_LABEL(instant_view_), 0, -1);
+ // ACTIVE is the state for text that is selected, but not focused.
+ gtk_widget_modify_text(instant_view_, GTK_STATE_ACTIVE, &text);
+ gtk_widget_modify_base(instant_view_, GTK_STATE_ACTIVE, &bg);
+ } else {
+ // When the text is unselected, fg is used for text color, the state
+ // is NORMAL, and the background is transparent.
+ gtk_widget_modify_fg(instant_view_, GTK_STATE_NORMAL, &text);
+ }
+}
+
+void OmniboxViewGtk::HandleBeginUserAction(GtkTextBuffer* sender) {
+ OnBeforePossibleChange();
+}
+
+void OmniboxViewGtk::HandleEndUserAction(GtkTextBuffer* sender) {
+ OnAfterPossibleChange();
+}
+
+gboolean OmniboxViewGtk::HandleKeyPress(GtkWidget* widget, GdkEventKey* event) {
+ // Background of this piece of complicated code:
+ // The omnibox supports several special behaviors which may be triggered by
+ // certain key events:
+ // Tab to search - triggered by Tab key
+ // Accept input - triggered by Enter key
+ // Revert input - triggered by Escape key
+ //
+ // Because we use a GtkTextView object |text_view_| for text input, we need
+ // send all key events to |text_view_| before handling them, to make sure
+ // IME works without any problem. So here, we intercept "key-press-event"
+ // signal of |text_view_| object and call its default handler to handle the
+ // key event first.
+ //
+ // Then if the key event is one of Tab, Enter and Escape, we need to trigger
+ // the corresponding special behavior if IME did not handle it.
+ // For Escape key, if the default signal handler returns FALSE, then we know
+ // it's not handled by IME.
+ //
+ // For Tab key, as "accepts-tab" property of |text_view_| is set to FALSE,
+ // if IME did not handle it then "move-focus" signal will be emitted by the
+ // default signal handler of |text_view_|. So we can intercept "move-focus"
+ // signal of |text_view_| to know if a Tab key press event was handled by IME,
+ // and trigger Tab to search behavior when necessary in the signal handler.
+ //
+ // But for Enter key, if IME did not handle the key event, the default signal
+ // handler will delete current selection range and insert '\n' and always
+ // return TRUE. We need to prevent |text_view_| from performing this default
+ // action if IME did not handle the key event, because we don't want the
+ // content of omnibox to be changed before triggering our special behavior.
+ // Otherwise our special behavior would not be performed correctly.
+ //
+ // But there is no way for us to prevent GtkTextView from handling the key
+ // event and performing built-in operation. So in order to achieve our goal,
+ // "insert-text" signal of |text_buffer_| object is intercepted, and
+ // following actions are done in the signal handler:
+ // - If there is only one character in inserted text, and it's '\n' or '\r',
+ // then set |enter_was_inserted_| to true.
+ // - Filter out all new line and tab characters.
+ //
+ // So if |enter_was_inserted_| is true after calling |text_view_|'s default
+ // signal handler against an Enter key press event, then we know that the
+ // Enter key press event was handled by GtkTextView rather than IME, and can
+ // perform the special behavior for Enter key safely.
+ //
+ // Now the last thing is to prevent the content of omnibox from being changed
+ // by GtkTextView when Enter key is pressed. As OnBeforePossibleChange() and
+ // OnAfterPossibleChange() will be called by GtkTextView before and after
+ // changing the content, and the content is already saved in
+ // OnBeforePossibleChange(), so if the Enter key press event was not handled
+ // by IME, it's easy to restore the content in OnAfterPossibleChange(), as if
+ // it's not changed at all.
+
+ GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget);
+
+ enter_was_pressed_ = event->keyval == GDK_Return ||
+ event->keyval == GDK_ISO_Enter ||
+ event->keyval == GDK_KP_Enter;
+
+ // Set |tab_was_pressed_| to true if it's a Tab key press event, so that our
+ // handler of "move-focus" signal can trigger Tab to search behavior when
+ // necessary.
+ tab_was_pressed_ = (event->keyval == GDK_Tab ||
+ event->keyval == GDK_ISO_Left_Tab ||
+ event->keyval == GDK_KP_Tab) &&
+ !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
+
+ delete_was_pressed_ = event->keyval == GDK_Delete ||
+ event->keyval == GDK_KP_Delete;
+
+ // Reset |enter_was_inserted_|, which may be set in the "insert-text" signal
+ // handler, so that we'll know if an Enter key event was handled by IME.
+ enter_was_inserted_ = false;
+
+ // Reset |paste_clipboard_requested_| to make sure we won't misinterpret this
+ // key input action as a paste action.
+ paste_clipboard_requested_ = false;
+
+ // Reset |text_changed_| before passing the key event on to the text view.
+ text_changed_ = false;
+
+ OnBeforePossibleChange();
+ handling_key_press_ = true;
+ content_maybe_changed_by_key_press_ = false;
+
+ // Call the default handler, so that IME can work as normal.
+ // New line characters will be filtered out by our "insert-text"
+ // signal handler attached to |text_buffer_| object.
+ gboolean result = klass->key_press_event(widget, event);
+
+ handling_key_press_ = false;
+ if (content_maybe_changed_by_key_press_)
+ OnAfterPossibleChange();
+
+ // Set |tab_was_pressed_| to false, to make sure Tab to search behavior can
+ // only be triggered by pressing Tab key.
+ tab_was_pressed_ = false;
+
+ if (enter_was_pressed_ && enter_was_inserted_) {
+ bool alt_held = (event->state & GDK_MOD1_MASK);
+ model_->AcceptInput(alt_held ? NEW_FOREGROUND_TAB : CURRENT_TAB, false);
+ result = TRUE;
+ } else if (!result && event->keyval == GDK_Escape &&
+ (event->state & gtk_accelerator_get_default_mod_mask()) == 0) {
+ // We can handle the Escape key if |text_view_| did not handle it.
+ // If it's not handled by us, then we need to propagate it up to the parent
+ // widgets, so that Escape accelerator can still work.
+ result = model_->OnEscapeKeyPressed();
+ } else if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) {
+ // Omnibox2 can switch its contents while pressing a control key. To switch
+ // the contents of omnibox2, we notify the AutocompleteEditModel class when
+ // the control-key state is changed.
+ model_->OnControlKeyChanged(true);
+ } else if (!text_changed_ && event->keyval == GDK_Delete &&
+ event->state & GDK_SHIFT_MASK) {
+ // If shift+del didn't change the text, we let this delete an entry from
+ // the popup. We can't check to see if the IME handled it because even if
+ // nothing is selected, the IME or the TextView still report handling it.
+ if (model_->popup_model()->IsOpen())
+ model_->popup_model()->TryDeletingCurrentItem();
+ }
+
+ // Set |enter_was_pressed_| to false, to make sure OnAfterPossibleChange() can
+ // act as normal for changes made by other events.
+ enter_was_pressed_ = false;
+
+ // If the key event is not handled by |text_view_| or us, then we need to
+ // propagate the key event up to parent widgets by returning FALSE.
+ // In this case we need to stop the signal emission explicitly to prevent the
+ // default "key-press-event" handler of |text_view_| from being called again.
+ if (!result) {
+ static guint signal_id =
+ g_signal_lookup("key-press-event", GTK_TYPE_WIDGET);
+ g_signal_stop_emission(widget, signal_id, 0);
+ }
+
+#if defined(TOOLKIT_VIEWS)
+ location_bar_view_->GetWidget()->NotifyAccessibilityEvent(
+ location_bar_view_, ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true);
+#endif
+
+ return result;
+}
+
+gboolean OmniboxViewGtk::HandleKeyRelease(GtkWidget* widget,
+ GdkEventKey* event) {
+ // Omnibox2 can switch its contents while pressing a control key. To switch
+ // the contents of omnibox2, we notify the AutocompleteEditModel class when
+ // the control-key state is changed.
+ if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) {
+ // Round trip to query the control state after the release. This allows
+ // you to release one control key while still holding another control key.
+ GdkDisplay* display = gdk_drawable_get_display(event->window);
+ GdkModifierType mod;
+ gdk_display_get_pointer(display, NULL, NULL, NULL, &mod);
+ if (!(mod & GDK_CONTROL_MASK))
+ model_->OnControlKeyChanged(false);
+ }
+
+ // Even though we handled the press ourselves, let GtkTextView handle the
+ // release. It shouldn't do anything particularly interesting, but it will
+ // handle the IME work for us.
+ return FALSE; // Propagate into GtkTextView.
+}
+
+gboolean OmniboxViewGtk::HandleViewButtonPress(GtkWidget* sender,
+ GdkEventButton* event) {
+ // We don't need to care about double and triple clicks.
+ if (event->type != GDK_BUTTON_PRESS)
+ return FALSE;
+
+ DCHECK(text_view_);
+
+ if (event->button == 1) {
+#if defined(OS_CHROMEOS)
+ // When the first button is pressed, track some stuff that will help us
+ // determine whether we should select all of the text when the button is
+ // released.
+ button_1_pressed_ = true;
+ text_view_focused_before_button_press_ = GTK_WIDGET_HAS_FOCUS(text_view_);
+ text_selected_during_click_ = false;
+#endif
+
+ // Button press event may change the selection, we need to record the change
+ // and report it to |model_| later when button is released.
+ OnBeforePossibleChange();
+ } else if (event->button == 2) {
+ // GtkTextView pastes PRIMARY selection with middle click.
+ // We can't call model_->on_paste_replacing_all() here, because the actual
+ // paste clipboard action may not be performed if the clipboard is empty.
+ paste_clipboard_requested_ = true;
+ }
+ return FALSE;
+}
+
+gboolean OmniboxViewGtk::HandleViewButtonRelease(GtkWidget* sender,
+ GdkEventButton* event) {
+ if (event->button != 1)
+ return FALSE;
+
+ DCHECK(text_view_);
+
+#if defined(OS_CHROMEOS)
+ button_1_pressed_ = false;
+#endif
+
+ // Call the GtkTextView default handler, ignoring the fact that it will
+ // likely have told us to stop propagating. We want to handle selection.
+ GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(text_view_);
+ klass->button_release_event(text_view_, event);
+
+#if defined(OS_CHROMEOS)
+ if (!text_view_focused_before_button_press_ && !text_selected_during_click_) {
+ // If this was a focusing click and the user didn't drag to highlight any
+ // text, select the full input and update the PRIMARY selection.
+ SelectAllInternal(false, true);
+
+ // So we told the buffer where the cursor should be, but make sure to tell
+ // the view so it can scroll it to be visible if needed.
+ // NOTE: This function doesn't seem to like a count of 0, looking at the
+ // code it will skip an important loop. Use -1 to achieve the same.
+ GtkTextIter start, end;
+ GetTextBufferBounds(&start, &end);
+ gtk_text_view_move_visually(GTK_TEXT_VIEW(text_view_), &start, -1);
+ }
+#endif
+
+ // Inform |model_| about possible text selection change.
+ OnAfterPossibleChange();
+
+ return TRUE; // Don't continue, we called the default handler already.
+}
+
+gboolean OmniboxViewGtk::HandleViewFocusIn(GtkWidget* sender,
+ GdkEventFocus* event) {
+ DCHECK(text_view_);
+ update_popup_without_focus_ = false;
+
+ GdkModifierType modifiers;
+ gdk_window_get_pointer(text_view_->window, NULL, NULL, &modifiers);
+ model_->OnSetFocus((modifiers & GDK_CONTROL_MASK) != 0);
+ controller_->OnSetFocus();
+ // TODO(deanm): Some keyword hit business, etc here.
+
+ g_signal_connect(
+ gdk_keymap_get_for_display(gtk_widget_get_display(text_view_)),
+ "direction-changed",
+ G_CALLBACK(&HandleKeymapDirectionChangedThunk), this);
+
+ AdjustTextJustification();
+
+ return FALSE; // Continue propagation.
+}
+
+gboolean OmniboxViewGtk::HandleViewFocusOut(GtkWidget* sender,
+ GdkEventFocus* event) {
+ DCHECK(text_view_);
+ GtkWidget* view_getting_focus = NULL;
+ GtkWindow* toplevel = platform_util::GetTopLevel(sender);
+ if (gtk_window_is_active(toplevel))
+ view_getting_focus = going_to_focus_;
+
+ // This must be invoked before ClosePopup.
+ model_->OnWillKillFocus(view_getting_focus);
+
+ // Close the popup.
+ ClosePopup();
+ // Tell the model to reset itself.
+ model_->OnKillFocus();
+ controller_->OnKillFocus();
+
+ g_signal_handlers_disconnect_by_func(
+ gdk_keymap_get_for_display(gtk_widget_get_display(text_view_)),
+ reinterpret_cast<gpointer>(&HandleKeymapDirectionChangedThunk), this);
+
+ return FALSE; // Pass the event on to the GtkTextView.
+}
+
+void OmniboxViewGtk::HandleViewMoveCursor(
+ GtkWidget* sender,
+ GtkMovementStep step,
+ gint count,
+ gboolean extend_selection) {
+ DCHECK(text_view_);
+ GtkTextIter sel_start, sel_end;
+ gboolean has_selection =
+ gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end);
+ bool handled = false;
+
+ if (step == GTK_MOVEMENT_VISUAL_POSITIONS && !extend_selection &&
+ (count == 1 || count == -1)) {
+ // We need to take the content direction into account when handling cursor
+ // movement, because the behavior of Left and Right key will be inverted if
+ // the direction is RTL. Although we should check the direction around the
+ // input caret, it's much simpler and good enough to check whole content's
+ // direction.
+ PangoDirection content_dir = GetContentDirection();
+ gint count_towards_end = content_dir == PANGO_DIRECTION_RTL ? -1 : 1;
+
+ // We want the GtkEntry behavior when you move the cursor while you have a
+ // selection. GtkTextView just drops the selection and moves the cursor,
+ // but instead we want to move the cursor to the appropiate end of the
+ // selection.
+ if (has_selection) {
+ // We have a selection and start / end are in ascending order.
+ // Cursor placement will remove the selection, so we need inform
+ // |model_| about this change by
+ // calling On{Before|After}PossibleChange() methods.
+ OnBeforePossibleChange();
+ gtk_text_buffer_place_cursor(
+ text_buffer_, count == count_towards_end ? &sel_end : &sel_start);
+ OnAfterPossibleChange();
+ handled = true;
+ } else if (count == count_towards_end && !IsCaretAtEnd()) {
+ handled = model_->CommitSuggestedText(true);
+ }
+ } else if (step == GTK_MOVEMENT_PAGES) { // Page up and down.
+ // Multiply by count for the direction (if we move too much that's ok).
+ model_->OnUpOrDownKeyPressed(model_->result().size() * count);
+ handled = true;
+ } else if (step == GTK_MOVEMENT_DISPLAY_LINES) { // Arrow up and down.
+ model_->OnUpOrDownKeyPressed(count);
+ handled = true;
+ }
+
+ if (!handled) {
+ // Cursor movement may change the selection, we need to record the change
+ // and report it to |model_|.
+ if (has_selection || extend_selection)
+ OnBeforePossibleChange();
+
+ // Propagate into GtkTextView
+ GtkTextViewClass* klass = GTK_TEXT_VIEW_GET_CLASS(text_view_);
+ klass->move_cursor(GTK_TEXT_VIEW(text_view_), step, count,
+ extend_selection);
+
+ if (has_selection || extend_selection)
+ OnAfterPossibleChange();
+ }
+
+ // move-cursor doesn't use a signal accumulator on the return value (it
+ // just ignores then), so we have to stop the propagation.
+ static guint signal_id = g_signal_lookup("move-cursor", GTK_TYPE_TEXT_VIEW);
+ g_signal_stop_emission(text_view_, signal_id, 0);
+}
+
+void OmniboxViewGtk::HandleViewSizeRequest(GtkWidget* sender,
+ GtkRequisition* req) {
+ // Don't force a minimum width, but use the font-relative height. This is a
+ // run-first handler, so the default handler was already called.
+ req->width = 1;
+}
+
+void OmniboxViewGtk::HandlePopupMenuDeactivate(GtkWidget* sender) {
+ // When the context menu appears, |text_view_|'s focus is lost. After an item
+ // is activated, the focus comes back to |text_view_|, but only after the
+ // check in UpdatePopup(). We set this flag to make UpdatePopup() aware that
+ // it will be receiving focus again.
+ if (!model_->has_focus())
+ update_popup_without_focus_ = true;
+}
+
+void OmniboxViewGtk::HandlePopulatePopup(GtkWidget* sender, GtkMenu* menu) {
+ GtkWidget* separator = gtk_separator_menu_item_new();
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator);
+ gtk_widget_show(separator);
+
+ // Search Engine menu item.
+ GtkWidget* search_engine_menuitem = gtk_menu_item_new_with_mnemonic(
+ gfx::ConvertAcceleratorsFromWindowsStyle(
+ l10n_util::GetStringUTF8(IDS_EDIT_SEARCH_ENGINES)).c_str());
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), search_engine_menuitem);
+ g_signal_connect(search_engine_menuitem, "activate",
+ G_CALLBACK(HandleEditSearchEnginesThunk), this);
+ gtk_widget_set_sensitive(search_engine_menuitem,
+ command_updater_->IsCommandEnabled(IDC_EDIT_SEARCH_ENGINES));
+ gtk_widget_show(search_engine_menuitem);
+
+ // We need to update the paste and go controller before we know what text
+ // to show. We could do this all asynchronously, but it would be elaborate
+ // because we'd have to account for multiple menus showing, getting called
+ // back after shutdown, and similar issues.
+ GtkClipboard* x_clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gchar* text = gtk_clipboard_wait_for_text(x_clipboard);
+ string16 text_wstr = UTF8ToUTF16(text ? text : "");
+ g_free(text);
+
+ // Paste and Go menu item.
+ GtkWidget* paste_go_menuitem = gtk_menu_item_new_with_mnemonic(
+ gfx::ConvertAcceleratorsFromWindowsStyle(
+ l10n_util::GetStringUTF8(model_->is_paste_and_search() ?
+ IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO)).c_str());
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), paste_go_menuitem);
+ g_signal_connect(paste_go_menuitem, "activate",
+ G_CALLBACK(HandlePasteAndGoThunk), this);
+ gtk_widget_set_sensitive(paste_go_menuitem,
+ model_->CanPasteAndGo(text_wstr));
+ gtk_widget_show(paste_go_menuitem);
+
+ g_signal_connect(menu, "deactivate",
+ G_CALLBACK(HandlePopupMenuDeactivateThunk), this);
+}
+
+void OmniboxViewGtk::HandleEditSearchEngines(GtkWidget* sender) {
+ command_updater_->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES);
+}
+
+void OmniboxViewGtk::HandlePasteAndGo(GtkWidget* sender) {
+ model_->PasteAndGo();
+}
+
+void OmniboxViewGtk::HandleMarkSet(GtkTextBuffer* buffer,
+ GtkTextIter* location,
+ GtkTextMark* mark) {
+ if (!text_buffer_ || buffer != text_buffer_)
+ return;
+
+ if (mark != gtk_text_buffer_get_insert(text_buffer_) &&
+ mark != gtk_text_buffer_get_selection_bound(text_buffer_)) {
+ return;
+ }
+
+ StopAnimation();
+
+ // If we are here, that means the user may be changing the selection
+ selection_suggested_ = false;
+
+ // Get the currently-selected text, if there is any.
+ std::string new_selected_text = GetSelectedText();
+
+#if defined(OS_CHROMEOS)
+ // If the user just selected some text with the mouse (or at least while the
+ // mouse button was down), make sure that we won't blow their selection away
+ // later by selecting all of the text when the button is released.
+ if (button_1_pressed_ && !new_selected_text.empty())
+ text_selected_during_click_ = true;
+#endif
+
+ // If we had some text selected earlier but it's no longer highlighted, we
+ // might need to save it now...
+ if (!selected_text_.empty() && new_selected_text.empty()) {
+ // ... but only if we currently own the selection. We want to manually
+ // update the selection when the text is unhighlighted because the user
+ // clicked in a blank area of the text view, but not when it's unhighlighted
+ // because another client or widget took the selection. (This handler gets
+ // called before the default handler, so as long as nobody else took the
+ // selection, the text buffer still owns it even if GTK is about to take it
+ // away in the default handler.)
+ GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+ if (gtk_clipboard_get_owner(clipboard) == G_OBJECT(text_buffer_))
+ SavePrimarySelection(selected_text_);
+ }
+
+ selected_text_ = new_selected_text;
+}
+
+// Override the primary selection the text buffer has set. This has to happen
+// after the default handler for the "mark-set" signal.
+void OmniboxViewGtk::HandleMarkSetAfter(GtkTextBuffer* buffer,
+ GtkTextIter* location,
+ GtkTextMark* mark) {
+ if (!text_buffer_ || buffer != text_buffer_)
+ return;
+
+ // We should only update primary selection when the user changes the selection
+ // range.
+ if (mark != gtk_text_buffer_get_insert(text_buffer_) &&
+ mark != gtk_text_buffer_get_selection_bound(text_buffer_)) {
+ return;
+ }
+
+ UpdatePrimarySelectionIfValidURL();
+}
+
+// Just use the default behavior for DnD, except if the drop can be a PasteAndGo
+// then override.
+void OmniboxViewGtk::HandleDragDataReceived(GtkWidget* sender,
+ GdkDragContext* context,
+ gint x,
+ gint y,
+ GtkSelectionData* selection_data,
+ guint target_type,
+ guint time) {
+ DCHECK(text_view_);
+
+ // Reset |paste_clipboard_requested_| to make sure we won't misinterpret this
+ // drop action as a paste action.
+ paste_clipboard_requested_ = false;
+
+ // Don't try to PasteAndGo on drops originating from this omnibox. However, do
+ // allow default behavior for such drags.
+ if (context->source_window == text_view_->window)
+ return;
+
+ guchar* text = gtk_selection_data_get_text(selection_data);
+ if (!text)
+ return;
+
+ string16 possible_url = UTF8ToUTF16(reinterpret_cast<char*>(text));
+ g_free(text);
+ if (OnPerformDropImpl(possible_url)) {
+ gtk_drag_finish(context, TRUE, TRUE, time);
+
+ static guint signal_id =
+ g_signal_lookup("drag-data-received", GTK_TYPE_WIDGET);
+ g_signal_stop_emission(text_view_, signal_id, 0);
+ }
+}
+
+void OmniboxViewGtk::HandleDragDataGet(GtkWidget* widget,
+ GdkDragContext* context,
+ GtkSelectionData* selection_data,
+ guint target_type,
+ guint time) {
+ DCHECK(text_view_);
+ // If GTK put the normal textual version of the selection in our drag data,
+ // put our doctored selection that might have the 'http://' prefix. Also, GTK
+ // is confused about signedness of its datatypes, leading to the weird switch
+ // statement (no set of casts fixes this).
+ switch (target_type) {
+ case GTK_TEXT_BUFFER_TARGET_INFO_TEXT: {
+ gtk_selection_data_set_text(selection_data, selected_text_.c_str(), -1);
+ }
+ }
+}
+
+void OmniboxViewGtk::HandleInsertText(GtkTextBuffer* buffer,
+ GtkTextIter* location,
+ const gchar* text,
+ gint len) {
+ std::string filtered_text;
+ filtered_text.reserve(len);
+
+ // Filter out new line and tab characters.
+ // |text| is guaranteed to be a valid UTF-8 string, so we don't need to
+ // validate it here.
+ //
+ // If there was only a single character, then it might be generated by a key
+ // event. In this case, we save the single character to help our
+ // "key-press-event" signal handler distinguish if an Enter key event is
+ // handled by IME or not.
+ if (len == 1 && (text[0] == '\n' || text[0] == '\r'))
+ enter_was_inserted_ = true;
+
+ const gchar* p = text;
+ while (*p && (p - text) < len) {
+ gunichar c = g_utf8_get_char(p);
+ const gchar* next = g_utf8_next_char(p);
+
+ // 0x200B is Zero Width Space, which is inserted just before the instant
+ // anchor for working around the GtkTextView's misalignment bug.
+ // This character might be captured and inserted into the content by undo
+ // manager, so we need to filter it out here.
+ if (c != L'\n' && c != L'\r' && c != L'\t' && c != 0x200B)
+ filtered_text.append(p, next);
+
+ p = next;
+ }
+
+ if (filtered_text.length()) {
+ // Avoid inserting the text after the instant anchor.
+ ValidateTextBufferIter(location);
+
+ // Call the default handler to insert filtered text.
+ GtkTextBufferClass* klass = GTK_TEXT_BUFFER_GET_CLASS(buffer);
+ klass->insert_text(buffer, location, filtered_text.data(),
+ static_cast<gint>(filtered_text.length()));
+ }
+
+ // Stop propagating the signal emission to prevent the default handler from
+ // being called again.
+ static guint signal_id = g_signal_lookup("insert-text", GTK_TYPE_TEXT_BUFFER);
+ g_signal_stop_emission(buffer, signal_id, 0);
+}
+
+void OmniboxViewGtk::HandleBackSpace(GtkWidget* sender) {
+ // Checks if it's currently in keyword search mode.
+ if (model_->is_keyword_hint() || model_->keyword().empty())
+ return; // Propgate into GtkTextView.
+
+ DCHECK(text_view_);
+
+ GtkTextIter sel_start, sel_end;
+ // Checks if there is some text selected.
+ if (gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end))
+ return; // Propgate into GtkTextView.
+
+ GtkTextIter start;
+ gtk_text_buffer_get_start_iter(text_buffer_, &start);
+
+ if (!gtk_text_iter_equal(&start, &sel_start))
+ return; // Propgate into GtkTextView.
+
+ // We're showing a keyword and the user pressed backspace at the beginning
+ // of the text. Delete the selected keyword.
+ model_->ClearKeyword(GetText());
+
+ // Stop propagating the signal emission into GtkTextView.
+ static guint signal_id = g_signal_lookup("backspace", GTK_TYPE_TEXT_VIEW);
+ g_signal_stop_emission(text_view_, signal_id, 0);
+}
+
+void OmniboxViewGtk::HandleViewMoveFocus(GtkWidget* widget,
+ GtkDirectionType direction) {
+ if (!tab_was_pressed_)
+ return;
+
+ // If special behavior is triggered, then stop the signal emission to
+ // prevent the focus from being moved.
+ bool handled = false;
+
+ // Trigger Tab to search behavior only when Tab key is pressed.
+ if (model_->is_keyword_hint())
+ handled = model_->AcceptKeyword();
+
+#if GTK_CHECK_VERSION(2, 20, 0)
+ if (!handled && !preedit_.empty())
+ handled = true;
+#endif
+
+ if (!handled && GTK_WIDGET_VISIBLE(instant_view_))
+ handled = model_->CommitSuggestedText(true);
+
+ if (!handled) {
+ if (!IsCaretAtEnd()) {
+ OnBeforePossibleChange();
+ PlaceCaretAt(GetTextLength());
+ OnAfterPossibleChange();
+ handled = true;
+ }
+ }
+
+ if (!handled)
+ handled = model_->AcceptCurrentInstantPreview();
+
+ if (handled) {
+ static guint signal_id = g_signal_lookup("move-focus", GTK_TYPE_WIDGET);
+ g_signal_stop_emission(widget, signal_id, 0);
+ }
+}
+
+void OmniboxViewGtk::HandleCopyClipboard(GtkWidget* sender) {
+ HandleCopyOrCutClipboard(true);
+}
+
+void OmniboxViewGtk::HandleCutClipboard(GtkWidget* sender) {
+ HandleCopyOrCutClipboard(false);
+}
+
+void OmniboxViewGtk::HandleCopyOrCutClipboard(bool copy) {
+ DCHECK(text_view_);
+
+ // On copy or cut, we manually update the PRIMARY selection to contain the
+ // highlighted text. This matches Firefox -- we highlight the URL but don't
+ // update PRIMARY on Ctrl-L, so Ctrl-L, Ctrl-C and then middle-click is a
+ // convenient way to paste the current URL somewhere.
+ if (!gtk_text_buffer_get_has_selection(text_buffer_))
+ return;
+
+ GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+ DCHECK(clipboard);
+ if (!clipboard)
+ return;
+
+ CharRange selection = GetSelection();
+ GURL url;
+ string16 text(UTF8ToUTF16(GetSelectedText()));
+ bool write_url;
+ model_->AdjustTextForCopy(selection.selection_min(), IsSelectAll(), &text,
+ &url, &write_url);
+
+ if (write_url) {
+ BookmarkNodeData data;
+ data.ReadFromTuple(url, text);
+ data.WriteToClipboard(NULL);
+
+ // Stop propagating the signal.
+ static guint copy_signal_id =
+ g_signal_lookup("copy-clipboard", GTK_TYPE_TEXT_VIEW);
+ static guint cut_signal_id =
+ g_signal_lookup("cut-clipboard", GTK_TYPE_TEXT_VIEW);
+ g_signal_stop_emission(text_view_,
+ copy ? copy_signal_id : cut_signal_id,
+ 0);
+
+ if (!copy && gtk_text_view_get_editable(GTK_TEXT_VIEW(text_view_)))
+ gtk_text_buffer_delete_selection(text_buffer_, true, true);
+ }
+
+ OwnPrimarySelection(UTF16ToUTF8(text));
+}
+
+bool OmniboxViewGtk::OnPerformDropImpl(const string16& text) {
+ if (model_->CanPasteAndGo(CollapseWhitespace(text, true))) {
+ model_->PasteAndGo();
+ return true;
+ }
+
+ return false;
+}
+
+gfx::Font OmniboxViewGtk::GetFont() {
+#if defined(TOOLKIT_VIEWS)
+ bool use_gtk = false;
+#else
+ bool use_gtk = theme_service_->UseGtkTheme();
+#endif
+
+ if (use_gtk) {
+ // If we haven't initialized the text view yet, just create a temporary one
+ // whose style we can grab.
+ GtkWidget* widget = text_view_ ? text_view_ : gtk_text_view_new();
+ GtkRcStyle* rc_style = gtk_widget_get_modifier_style(widget);
+ gfx::Font font((rc_style && rc_style->font_desc) ?
+ rc_style->font_desc :
+ widget->style->font_desc);
+ if (!text_view_)
+ g_object_unref(g_object_ref_sink(widget));
+
+ // Scaling the font down for popup windows doesn't help here, since we just
+ // use the normal unforced font size when using the GTK theme.
+ return font;
+ } else {
+ return gfx::Font(
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont).
+ GetFontName(),
+ popup_window_mode_ ?
+ browser_defaults::kAutocompleteEditFontPixelSizeInPopup :
+ browser_defaults::kAutocompleteEditFontPixelSize);
+ }
+}
+
+void OmniboxViewGtk::OwnPrimarySelection(const std::string& text) {
+ primary_selection_text_ = text;
+
+ GtkTargetList* list = gtk_target_list_new(NULL, 0);
+ gtk_target_list_add_text_targets(list, 0);
+ gint len;
+ GtkTargetEntry* entries = gtk_target_table_new_from_list(list, &len);
+
+ // When |text_buffer_| is destroyed, it will clear the clipboard, hence
+ // we needn't worry about calling gtk_clipboard_clear().
+ gtk_clipboard_set_with_owner(gtk_clipboard_get(GDK_SELECTION_PRIMARY),
+ entries, len,
+ ClipboardGetSelectionThunk,
+ ClipboardSelectionCleared,
+ G_OBJECT(text_buffer_));
+
+ gtk_target_list_unref(list);
+ gtk_target_table_free(entries, len);
+}
+
+void OmniboxViewGtk::HandlePasteClipboard(GtkWidget* sender) {
+ // We can't call model_->on_paste_replacing_all() here, because the actual
+ // paste clipboard action may not be performed if the clipboard is empty.
+ paste_clipboard_requested_ = true;
+}
+
+gfx::Rect OmniboxViewGtk::WindowBoundsFromIters(GtkTextIter* iter1,
+ GtkTextIter* iter2) {
+ GdkRectangle start_location, end_location;
+ GtkTextView* text_view = GTK_TEXT_VIEW(text_view_);
+ gtk_text_view_get_iter_location(text_view, iter1, &start_location);
+ gtk_text_view_get_iter_location(text_view, iter2, &end_location);
+
+ gint x1, x2, y1, y2;
+ gtk_text_view_buffer_to_window_coords(text_view, GTK_TEXT_WINDOW_WIDGET,
+ start_location.x, start_location.y,
+ &x1, &y1);
+ gtk_text_view_buffer_to_window_coords(text_view, GTK_TEXT_WINDOW_WIDGET,
+ end_location.x + end_location.width,
+ end_location.y + end_location.height,
+ &x2, &y2);
+
+ return gfx::Rect(x1, y1, x2 - x1, y2 - y1);
+}
+
+gboolean OmniboxViewGtk::HandleExposeEvent(GtkWidget* sender,
+ GdkEventExpose* expose) {
+ if (strikethrough_.cp_min >= strikethrough_.cp_max)
+ return FALSE;
+ DCHECK(text_view_);
+
+ gfx::Rect expose_rect(expose->area);
+
+ GtkTextIter iter_min, iter_max;
+ ItersFromCharRange(strikethrough_, &iter_min, &iter_max);
+ gfx::Rect strikethrough_rect = WindowBoundsFromIters(&iter_min, &iter_max);
+
+ if (!expose_rect.Intersects(strikethrough_rect))
+ return FALSE;
+
+ // Finally, draw.
+ cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(expose->window));
+ cairo_rectangle(cr, expose_rect.x(), expose_rect.y(),
+ expose_rect.width(), expose_rect.height());
+ cairo_clip(cr);
+
+ // TODO(estade): we probably shouldn't draw the strikethrough on selected
+ // text. I started to do this, but it was way more effort than it seemed
+ // worth.
+ strikethrough_rect.Inset(kStrikethroughStrokeWidth,
+ kStrikethroughStrokeWidth);
+ cairo_set_source_rgb(cr, kStrikethroughStrokeRed, 0.0, 0.0);
+ cairo_set_line_width(cr, kStrikethroughStrokeWidth);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
+ cairo_move_to(cr, strikethrough_rect.x(), strikethrough_rect.bottom());
+ cairo_line_to(cr, strikethrough_rect.right(), strikethrough_rect.y());
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+
+void OmniboxViewGtk::SelectAllInternal(bool reversed,
+ bool update_primary_selection) {
+ GtkTextIter start, end;
+ if (reversed) {
+ GetTextBufferBounds(&end, &start);
+ } else {
+ GetTextBufferBounds(&start, &end);
+ }
+ if (!update_primary_selection)
+ StartUpdatingHighlightedText();
+ gtk_text_buffer_select_range(text_buffer_, &start, &end);
+ if (!update_primary_selection)
+ FinishUpdatingHighlightedText();
+}
+
+void OmniboxViewGtk::StartUpdatingHighlightedText() {
+ if (GTK_WIDGET_REALIZED(text_view_)) {
+ GtkClipboard* clipboard =
+ gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY);
+ DCHECK(clipboard);
+ if (clipboard)
+ gtk_text_buffer_remove_selection_clipboard(text_buffer_, clipboard);
+ }
+ g_signal_handler_block(text_buffer_, mark_set_handler_id_);
+ g_signal_handler_block(text_buffer_, mark_set_handler_id2_);
+}
+
+void OmniboxViewGtk::FinishUpdatingHighlightedText() {
+ if (GTK_WIDGET_REALIZED(text_view_)) {
+ GtkClipboard* clipboard =
+ gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY);
+ DCHECK(clipboard);
+ if (clipboard)
+ gtk_text_buffer_add_selection_clipboard(text_buffer_, clipboard);
+ }
+ g_signal_handler_unblock(text_buffer_, mark_set_handler_id_);
+ g_signal_handler_unblock(text_buffer_, mark_set_handler_id2_);
+}
+
+OmniboxViewGtk::CharRange OmniboxViewGtk::GetSelection() const {
+ // You can not just use get_selection_bounds here, since the order will be
+ // ascending, and you don't know where the user's start and end of the
+ // selection was (if the selection was forwards or backwards). Get the
+ // actual marks so that we can preserve the selection direction.
+ GtkTextIter start, insert;
+ GtkTextMark* mark;
+
+ mark = gtk_text_buffer_get_selection_bound(text_buffer_);
+ gtk_text_buffer_get_iter_at_mark(text_buffer_, &start, mark);
+
+ mark = gtk_text_buffer_get_insert(text_buffer_);
+ gtk_text_buffer_get_iter_at_mark(text_buffer_, &insert, mark);
+
+ gint start_offset = gtk_text_iter_get_offset(&start);
+ gint end_offset = gtk_text_iter_get_offset(&insert);
+
+#if GTK_CHECK_VERSION(2, 20, 0)
+ // Nothing should be selected when we are in the middle of composition.
+ DCHECK(preedit_.empty() || start_offset == end_offset);
+ if (!preedit_.empty()) {
+ start_offset += preedit_.size();
+ end_offset += preedit_.size();
+ }
+#endif
+
+ return CharRange(start_offset, end_offset);
+}
+
+void OmniboxViewGtk::ItersFromCharRange(const CharRange& range,
+ GtkTextIter* iter_min,
+ GtkTextIter* iter_max) {
+ DCHECK(!IsImeComposing());
+ gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_min, range.cp_min);
+ gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_max, range.cp_max);
+}
+
+int OmniboxViewGtk::GetTextLength() const {
+ GtkTextIter end;
+ gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_);
+#if GTK_CHECK_VERSION(2, 20, 0)
+ // We need to count the length of the text being composed, because we treat
+ // it as part of the content in GetText().
+ return gtk_text_iter_get_offset(&end) + preedit_.size();
+#else
+ return gtk_text_iter_get_offset(&end);
+#endif
+}
+
+void OmniboxViewGtk::PlaceCaretAt(int pos) {
+ GtkTextIter cursor;
+ gtk_text_buffer_get_iter_at_offset(text_buffer_, &cursor, pos);
+ gtk_text_buffer_place_cursor(text_buffer_, &cursor);
+}
+
+bool OmniboxViewGtk::IsCaretAtEnd() const {
+ const CharRange selection = GetSelection();
+ return selection.cp_min == selection.cp_max &&
+ selection.cp_min == GetTextLength();
+}
+
+void OmniboxViewGtk::EmphasizeURLComponents() {
+#if GTK_CHECK_VERSION(2, 20, 0)
+ // We can't change the text style easily, if the preedit string (the text
+ // being composed by the input method) is not empty, which is not treated as
+ // a part of the text content inside GtkTextView. And it's ok to simply return
+ // in this case, as this method will be called again when the preedit string
+ // gets committed.
+ if (preedit_.size()) {
+ strikethrough_ = CharRange();
+ return;
+ }
+#endif
+ // See whether the contents are a URL with a non-empty host portion, which we
+ // should emphasize. To check for a URL, rather than using the type returned
+ // by Parse(), ask the model, which will check the desired page transition for
+ // this input. This can tell us whether an UNKNOWN input string is going to
+ // be treated as a search or a navigation, and is the same method the Paste
+ // And Go system uses.
+ url_parse::Component scheme, host;
+ string16 text(GetText());
+ AutocompleteInput::ParseForEmphasizeComponents(
+ text, model_->GetDesiredTLD(), &scheme, &host);
+ const bool emphasize = model_->CurrentTextIsURL() && (host.len > 0);
+
+ // Set the baseline emphasis.
+ GtkTextIter start, end;
+ GetTextBufferBounds(&start, &end);
+ gtk_text_buffer_remove_all_tags(text_buffer_, &start, &end);
+ if (emphasize) {
+ gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end);
+
+ // We've found a host name, give it more emphasis.
+ gtk_text_buffer_get_iter_at_line_index(text_buffer_, &start, 0,
+ GetUTF8Offset(text,
+ host.begin));
+ gtk_text_buffer_get_iter_at_line_index(text_buffer_, &end, 0,
+ GetUTF8Offset(text,
+ host.end()));
+
+ gtk_text_buffer_apply_tag(text_buffer_, normal_text_tag_, &start, &end);
+ } else {
+ gtk_text_buffer_apply_tag(text_buffer_, normal_text_tag_, &start, &end);
+ }
+
+ strikethrough_ = CharRange();
+ // Emphasize the scheme for security UI display purposes (if necessary).
+ if (!model_->user_input_in_progress() && scheme.is_nonempty() &&
+ (security_level_ != ToolbarModel::NONE)) {
+ CharRange scheme_range = CharRange(GetUTF8Offset(text, scheme.begin),
+ GetUTF8Offset(text, scheme.end()));
+ ItersFromCharRange(scheme_range, &start, &end);
+
+ if (security_level_ == ToolbarModel::SECURITY_ERROR) {
+ strikethrough_ = scheme_range;
+ // When we draw the strikethrough, we don't want to include the ':' at the
+ // end of the scheme.
+ strikethrough_.cp_max--;
+
+ gtk_text_buffer_apply_tag(text_buffer_, security_error_scheme_tag_,
+ &start, &end);
+ } else if (security_level_ == ToolbarModel::SECURITY_WARNING) {
+ gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end);
+ } else {
+ gtk_text_buffer_apply_tag(text_buffer_, secure_scheme_tag_, &start, &end);
+ }
+ }
+}
+
+void OmniboxViewGtk::StopAnimation() {
+ // Clear the animation delegate so we don't get an AnimationEnded() callback.
+ instant_animation_->set_delegate(NULL);
+ instant_animation_->Stop();
+ UpdateInstantViewColors();
+}
+
+void OmniboxViewGtk::TextChanged() {
+ EmphasizeURLComponents();
+ model_->OnChanged();
+}
+
+void OmniboxViewGtk::SavePrimarySelection(const std::string& selected_text) {
+ DCHECK(text_view_);
+
+ GtkClipboard* clipboard =
+ gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY);
+ DCHECK(clipboard);
+ if (!clipboard)
+ return;
+
+ gtk_clipboard_set_text(
+ clipboard, selected_text.data(), selected_text.size());
+}
+
+void OmniboxViewGtk::SetTextAndSelectedRange(const string16& text,
+ const CharRange& range) {
+ if (text != GetText()) {
+ std::string utf8 = UTF16ToUTF8(text);
+ gtk_text_buffer_set_text(text_buffer_, utf8.data(), utf8.length());
+ }
+ SetSelectedRange(range);
+ AdjustTextJustification();
+}
+
+void OmniboxViewGtk::SetSelectedRange(const CharRange& range) {
+ GtkTextIter insert, bound;
+ ItersFromCharRange(range, &bound, &insert);
+ gtk_text_buffer_select_range(text_buffer_, &insert, &bound);
+
+ // This should be set *after* setting the selection range, in case setting the
+ // selection triggers HandleMarkSet which sets |selection_suggested_| to
+ // false.
+ selection_suggested_ = true;
+}
+
+void OmniboxViewGtk::AdjustTextJustification() {
+ DCHECK(text_view_);
+
+ PangoDirection content_dir = GetContentDirection();
+
+ // Use keymap direction if content does not have strong direction.
+ // It matches the behavior of GtkTextView.
+ if (content_dir == PANGO_DIRECTION_NEUTRAL) {
+ content_dir = gdk_keymap_get_direction(
+ gdk_keymap_get_for_display(gtk_widget_get_display(text_view_)));
+ }
+
+ GtkTextDirection widget_dir = gtk_widget_get_direction(text_view_);
+
+ if ((widget_dir == GTK_TEXT_DIR_RTL && content_dir == PANGO_DIRECTION_LTR) ||
+ (widget_dir == GTK_TEXT_DIR_LTR && content_dir == PANGO_DIRECTION_RTL)) {
+ gtk_text_view_set_justification(GTK_TEXT_VIEW(text_view_),
+ GTK_JUSTIFY_RIGHT);
+ } else {
+ gtk_text_view_set_justification(GTK_TEXT_VIEW(text_view_),
+ GTK_JUSTIFY_LEFT);
+ }
+}
+
+PangoDirection OmniboxViewGtk::GetContentDirection() {
+ GtkTextIter iter;
+ gtk_text_buffer_get_start_iter(text_buffer_, &iter);
+
+ PangoDirection dir = PANGO_DIRECTION_NEUTRAL;
+ do {
+ dir = pango_unichar_direction(gtk_text_iter_get_char(&iter));
+ if (dir != PANGO_DIRECTION_NEUTRAL)
+ break;
+ } while (gtk_text_iter_forward_char(&iter));
+
+ return dir;
+}
+
+void OmniboxViewGtk::HandleWidgetDirectionChanged(
+ GtkWidget* sender,
+ GtkTextDirection previous_direction) {
+ AdjustTextJustification();
+}
+
+void OmniboxViewGtk::HandleDeleteFromCursor(GtkWidget* sender,
+ GtkDeleteType type,
+ gint count) {
+ // If the selected text was suggested for autocompletion, then erase those
+ // first and then let the default handler take over.
+ if (selection_suggested_) {
+ gtk_text_buffer_delete_selection(text_buffer_, true, true);
+ selection_suggested_ = false;
+ }
+}
+
+void OmniboxViewGtk::HandleKeymapDirectionChanged(GdkKeymap* sender) {
+ AdjustTextJustification();
+}
+
+void OmniboxViewGtk::HandleDeleteRange(GtkTextBuffer* buffer,
+ GtkTextIter* start,
+ GtkTextIter* end) {
+ // Prevent the user from deleting the instant anchor. We can't simply set the
+ // instant anchor readonly by applying a tag with "editable" = FALSE, because
+ // it'll prevent the insert caret from blinking.
+ ValidateTextBufferIter(start);
+ ValidateTextBufferIter(end);
+ if (!gtk_text_iter_compare(start, end)) {
+ static guint signal_id =
+ g_signal_lookup("delete-range", GTK_TYPE_TEXT_BUFFER);
+ g_signal_stop_emission(buffer, signal_id, 0);
+ }
+}
+
+void OmniboxViewGtk::HandleMarkSetAlways(GtkTextBuffer* buffer,
+ GtkTextIter* location,
+ GtkTextMark* mark) {
+ if (mark == instant_mark_ || !instant_mark_)
+ return;
+
+ GtkTextIter new_iter = *location;
+ ValidateTextBufferIter(&new_iter);
+
+ static guint signal_id = g_signal_lookup("mark-set", GTK_TYPE_TEXT_BUFFER);
+
+ // "mark-set" signal is actually emitted after the mark's location is already
+ // set, so if the location is beyond the instant anchor, we need to move the
+ // mark again, which will emit the signal again. In order to prevent other
+ // signal handlers from being called twice, we need to stop signal emission
+ // before moving the mark again.
+ if (gtk_text_iter_compare(&new_iter, location)) {
+ g_signal_stop_emission(buffer, signal_id, 0);
+ gtk_text_buffer_move_mark(buffer, mark, &new_iter);
+ return;
+ }
+
+ if (mark != gtk_text_buffer_get_insert(text_buffer_) &&
+ mark != gtk_text_buffer_get_selection_bound(text_buffer_)) {
+ return;
+ }
+
+ // See issue http://crbug.com/63860
+ GtkTextIter insert;
+ GtkTextIter selection_bound;
+ gtk_text_buffer_get_iter_at_mark(buffer, &insert,
+ gtk_text_buffer_get_insert(buffer));
+ gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound,
+ gtk_text_buffer_get_selection_bound(buffer));
+
+ GtkTextIter end;
+ gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_);
+
+ if (gtk_text_iter_compare(&insert, &end) > 0 ||
+ gtk_text_iter_compare(&selection_bound, &end) > 0) {
+ g_signal_stop_emission(buffer, signal_id, 0);
+ }
+}
+
+// static
+void OmniboxViewGtk::ClipboardGetSelectionThunk(
+ GtkClipboard* clipboard,
+ GtkSelectionData* selection_data,
+ guint info,
+ gpointer object) {
+ OmniboxViewGtk* edit_view =
+ reinterpret_cast<OmniboxViewGtk*>(
+ g_object_get_data(G_OBJECT(object), kOmniboxViewGtkKey));
+ edit_view->ClipboardGetSelection(clipboard, selection_data, info);
+}
+
+void OmniboxViewGtk::ClipboardGetSelection(GtkClipboard* clipboard,
+ GtkSelectionData* selection_data,
+ guint info) {
+ gtk_selection_data_set_text(selection_data, primary_selection_text_.c_str(),
+ primary_selection_text_.size());
+}
+
+std::string OmniboxViewGtk::GetSelectedText() const {
+ GtkTextIter start, end;
+ std::string result;
+ if (gtk_text_buffer_get_selection_bounds(text_buffer_, &start, &end)) {
+ gchar* text = gtk_text_iter_get_text(&start, &end);
+ size_t text_len = strlen(text);
+ if (text_len)
+ result = std::string(text, text_len);
+ g_free(text);
+ }
+ return result;
+}
+
+void OmniboxViewGtk::UpdatePrimarySelectionIfValidURL() {
+ string16 text = UTF8ToUTF16(GetSelectedText());
+
+ if (text.empty())
+ return;
+
+ // Use AdjustTextForCopy to make sure we prefix the text with 'http://'.
+ CharRange selection = GetSelection();
+ GURL url;
+ bool write_url;
+ model_->AdjustTextForCopy(selection.selection_min(), IsSelectAll(), &text,
+ &url, &write_url);
+ if (write_url) {
+ selected_text_ = UTF16ToUTF8(text);
+ OwnPrimarySelection(selected_text_);
+ }
+}
+
+#if GTK_CHECK_VERSION(2, 20, 0)
+void OmniboxViewGtk::HandlePreeditChanged(GtkWidget* sender,
+ const gchar* preedit) {
+ // GtkTextView won't fire "begin-user-action" and "end-user-action" signals
+ // when changing the preedit string, so we need to call
+ // OnBeforePossibleChange() and OnAfterPossibleChange() by ourselves.
+ OnBeforePossibleChange();
+ if (preedit && *preedit) {
+ // GtkTextView will only delete the selection range when committing the
+ // preedit string, which will cause very strange behavior, so we need to
+ // delete the selection range here explicitly. See http://crbug.com/18808.
+ if (preedit_.empty())
+ gtk_text_buffer_delete_selection(text_buffer_, false, true);
+ preedit_ = UTF8ToUTF16(preedit);
+ } else {
+ preedit_.clear();
+ }
+ OnAfterPossibleChange();
+}
+#endif
+
+void OmniboxViewGtk::HandleWindowSetFocus(GtkWindow* sender,
+ GtkWidget* focus) {
+ // This is actually a guess. If the focused widget changes in "focus-out"
+ // event handler, then the window will respect that and won't focus
+ // |focus|. I doubt that is likely to happen however.
+ going_to_focus_ = focus;
+}
+
+void OmniboxViewGtk::HandleUndoRedo(GtkWidget* sender) {
+ OnBeforePossibleChange();
+}
+
+void OmniboxViewGtk::HandleUndoRedoAfter(GtkWidget* sender) {
+ OnAfterPossibleChange();
+}
+
+void OmniboxViewGtk::GetTextBufferBounds(GtkTextIter* start,
+ GtkTextIter* end) const {
+ gtk_text_buffer_get_start_iter(text_buffer_, start);
+ gtk_text_buffer_get_iter_at_mark(text_buffer_, end, instant_mark_);
+}
+
+void OmniboxViewGtk::ValidateTextBufferIter(GtkTextIter* iter) const {
+ if (!instant_mark_)
+ return;
+
+ GtkTextIter end;
+ gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_);
+ if (gtk_text_iter_compare(iter, &end) > 0)
+ *iter = end;
+}
+
+void OmniboxViewGtk::AdjustVerticalAlignmentOfInstantView() {
+ // By default, GtkTextView layouts an anchored child widget just above the
+ // baseline, so we need to move the |instant_view_| down to make sure it
+ // has the same baseline as the |text_view_|.
+ PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(instant_view_));
+ int height;
+ pango_layout_get_size(layout, NULL, &height);
+ PangoLayoutIter* iter = pango_layout_get_iter(layout);
+ int baseline = pango_layout_iter_get_baseline(iter);
+ pango_layout_iter_free(iter);
+ g_object_set(instant_anchor_tag_, "rise", baseline - height, NULL);
+}
diff --git a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h
new file mode 100644
index 0000000..cbda4c7
--- /dev/null
+++ b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h
@@ -0,0 +1,546 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_
+#define CHROME_BROWSER_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/string_util.h"
+#include "chrome/browser/autocomplete/autocomplete_edit_view.h"
+#include "chrome/browser/ui/gtk/owned_widget_gtk.h"
+#include "chrome/browser/ui/toolbar/toolbar_model.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/page_transition_types.h"
+#include "ui/base/animation/animation_delegate.h"
+#include "ui/base/gtk/gtk_signal.h"
+#include "ui/base/gtk/gtk_signal_registrar.h"
+#include "ui/gfx/rect.h"
+#include "webkit/glue/window_open_disposition.h"
+
+class AutocompleteEditController;
+class AutocompleteEditModel;
+class AutocompletePopupView;
+class Profile;
+class TabContents;
+
+namespace gfx {
+class Font;
+}
+
+namespace ui {
+class MultiAnimation;
+}
+
+namespace views {
+class View;
+}
+
+#if !defined(TOOLKIT_VIEWS)
+class GtkThemeService;
+#endif
+
+class OmniboxViewGtk : public AutocompleteEditView,
+ public NotificationObserver,
+ public ui::AnimationDelegate {
+ public:
+ // Modeled like the Windows CHARRANGE. Represent a pair of cursor position
+ // offsets. Since GtkTextIters are invalid after the buffer is changed, we
+ // work in character offsets (not bytes).
+ struct CharRange {
+ CharRange() : cp_min(0), cp_max(0) { }
+ CharRange(int n, int x) : cp_min(n), cp_max(x) { }
+
+ // Returns the start/end of the selection.
+ int selection_min() const { return std::min(cp_min, cp_max); }
+ int selection_max() const { return std::max(cp_min, cp_max); }
+
+ // Work in integers to match the gint GTK APIs.
+ int cp_min; // For a selection: Represents the start.
+ int cp_max; // For a selection: Represents the end (insert position).
+ };
+
+ OmniboxViewGtk(AutocompleteEditController* controller,
+ ToolbarModel* toolbar_model,
+ Profile* profile,
+ CommandUpdater* command_updater,
+ bool popup_window_mode,
+#if defined(TOOLKIT_VIEWS)
+ views::View* location_bar
+#else
+ GtkWidget* location_bar
+#endif
+ );
+ virtual ~OmniboxViewGtk();
+
+ // Initialize, create the underlying widgets, etc.
+ void Init();
+ // Returns the width in pixels needed to display the text from one character
+ // before the caret to the end of the string. See comments in
+ // LocationBarView::Layout as to why this uses -1.
+ int WidthOfTextAfterCursor();
+
+ // Implement the AutocompleteEditView interface.
+ virtual AutocompleteEditModel* model();
+ virtual const AutocompleteEditModel* model() const;
+
+ virtual void SaveStateToTab(TabContents* tab);
+
+ virtual void Update(const TabContents* tab_for_state_restoring);
+
+ virtual void OpenURL(const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition,
+ const GURL& alternate_nav_url,
+ size_t selected_line,
+ const string16& keyword);
+
+ virtual string16 GetText() const;
+
+ virtual bool IsEditingOrEmpty() const;
+ virtual int GetIcon() const;
+
+ virtual void SetUserText(const string16& text);
+ virtual void SetUserText(const string16& text,
+ const string16& display_text,
+ bool update_popup);
+
+ virtual void SetWindowTextAndCaretPos(const string16& text,
+ size_t caret_pos);
+
+ virtual void SetForcedQuery();
+
+ virtual bool IsSelectAll();
+ virtual bool DeleteAtEndPressed();
+ virtual void GetSelectionBounds(string16::size_type* start,
+ string16::size_type* end);
+ virtual void SelectAll(bool reversed);
+ virtual void RevertAll();
+
+ virtual void UpdatePopup();
+ virtual void ClosePopup();
+
+ virtual void SetFocus();
+
+ virtual void OnTemporaryTextMaybeChanged(const string16& display_text,
+ bool save_original_selection);
+ virtual bool OnInlineAutocompleteTextMaybeChanged(
+ const string16& display_text, size_t user_text_length);
+ virtual void OnRevertTemporaryText();
+ virtual void OnBeforePossibleChange();
+ virtual bool OnAfterPossibleChange();
+ virtual gfx::NativeView GetNativeView() const;
+ virtual CommandUpdater* GetCommandUpdater();
+ virtual void SetInstantSuggestion(const string16& suggestion,
+ bool animate_to_complete);
+ virtual string16 GetInstantSuggestion() const;
+ virtual int TextWidth() const;
+ virtual bool IsImeComposing() const;
+
+#if defined(TOOLKIT_VIEWS)
+ virtual views::View* AddToView(views::View* parent);
+ virtual int OnPerformDrop(const views::DropTargetEvent& event);
+
+ // A factory method to create an AutocompleteEditView instance initialized for
+ // linux_views. This currently returns an instance of OmniboxViewGtk only,
+ // but AutocompleteEditViewViews will be added as an option when
+ // TextfieldViews is enabled.
+ static AutocompleteEditView* Create(AutocompleteEditController* controller,
+ ToolbarModel* toolbar_model,
+ Profile* profile,
+ CommandUpdater* command_updater,
+ bool popup_window_mode,
+ views::View* location_bar);
+#endif
+
+ // Overridden from NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Overridden from ui::AnimationDelegate.
+ virtual void AnimationEnded(const ui::Animation* animation);
+ virtual void AnimationProgressed(const ui::Animation* animation);
+ virtual void AnimationCanceled(const ui::Animation* animation);
+
+ // Sets the colors of the text view according to the theme.
+ void SetBaseColor();
+ // Sets the colors of the instant suggestion view according to the theme and
+ // the animation state.
+ void UpdateInstantViewColors();
+
+ // Returns the text view gtk widget. May return NULL if the widget
+ // has already been destroyed.
+ GtkWidget* text_view() {
+ return text_view_;
+ }
+
+ private:
+ CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleBeginUserAction,
+ GtkTextBuffer*);
+ CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleEndUserAction, GtkTextBuffer*);
+ CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSet, GtkTextBuffer*,
+ GtkTextIter*, GtkTextMark*);
+ // As above, but called after the default handler.
+ CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSetAfter, GtkTextBuffer*,
+ GtkTextIter*, GtkTextMark*);
+ CHROMEG_CALLBACK_3(OmniboxViewGtk, void, HandleInsertText, GtkTextBuffer*,
+ GtkTextIter*, const gchar*, gint);
+ CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleKeymapDirectionChanged,
+ GdkKeymap*);
+ CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleDeleteRange, GtkTextBuffer*,
+ GtkTextIter*, GtkTextIter*);
+ // Unlike above HandleMarkSet and HandleMarkSetAfter, this handler will always
+ // be connected to the signal.
+ CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSetAlways, GtkTextBuffer*,
+ GtkTextIter*, GtkTextMark*);
+
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleKeyPress, GdkEventKey*);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleKeyRelease,
+ GdkEventKey*);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewButtonPress,
+ GdkEventButton*);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewButtonRelease,
+ GdkEventButton*);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewFocusIn,
+ GdkEventFocus*);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewFocusOut,
+ GdkEventFocus*);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleViewMoveFocus,
+ GtkDirectionType);
+ CHROMEGTK_CALLBACK_3(OmniboxViewGtk, void, HandleViewMoveCursor,
+ GtkMovementStep, gint, gboolean);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleViewSizeRequest,
+ GtkRequisition*);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandlePopulatePopup, GtkMenu*);
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleEditSearchEngines);
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePasteAndGo);
+ CHROMEGTK_CALLBACK_6(OmniboxViewGtk, void, HandleDragDataReceived,
+ GdkDragContext*, gint, gint, GtkSelectionData*,
+ guint, guint);
+ CHROMEGTK_CALLBACK_4(OmniboxViewGtk, void, HandleDragDataGet,
+ GdkDragContext*, GtkSelectionData*, guint, guint);
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleBackSpace);
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCopyClipboard);
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCutClipboard);
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePasteClipboard);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleExposeEvent,
+ GdkEventExpose*);
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleWidgetDirectionChanged,
+ GtkTextDirection);
+ CHROMEGTK_CALLBACK_2(OmniboxViewGtk, void, HandleDeleteFromCursor,
+ GtkDeleteType, gint);
+ // We connect to this so we can determine our toplevel window, so we can
+ // listen to focus change events on it.
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleHierarchyChanged,
+ GtkWidget*);
+#if GTK_CHECK_VERSION(2, 20, 0)
+ CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandlePreeditChanged,
+ const gchar*);
+#endif
+ // Undo/redo operations won't trigger "begin-user-action" and
+ // "end-user-action" signals, so we need to hook into "undo" and "redo"
+ // signals and call OnBeforePossibleChange()/OnAfterPossibleChange() by
+ // ourselves.
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleUndoRedo);
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleUndoRedoAfter);
+
+ CHROMEG_CALLBACK_1(OmniboxViewGtk, void, HandleWindowSetFocus,
+ GtkWindow*, GtkWidget*);
+
+ // Callback function called after context menu is closed.
+ CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePopupMenuDeactivate);
+
+ // Callback for the PRIMARY selection clipboard.
+ static void ClipboardGetSelectionThunk(GtkClipboard* clipboard,
+ GtkSelectionData* selection_data,
+ guint info,
+ gpointer object);
+ void ClipboardGetSelection(GtkClipboard* clipboard,
+ GtkSelectionData* selection_data,
+ guint info);
+
+ void HandleCopyOrCutClipboard(bool copy);
+
+ // Common implementation for performing a drop on the edit view.
+ bool OnPerformDropImpl(const string16& text);
+
+ // Returns the font used in |text_view_|.
+ gfx::Font GetFont();
+
+ // Take control of the PRIMARY selection clipboard with |text|. Use
+ // |text_buffer_| as the owner, so that this doesn't remove the selection on
+ // it. This makes use of the above callbacks.
+ void OwnPrimarySelection(const std::string& text);
+
+ // Gets the GTK_TEXT_WINDOW_WIDGET coordinates for |text_view_| that bound the
+ // given iters.
+ gfx::Rect WindowBoundsFromIters(GtkTextIter* iter1, GtkTextIter* iter2);
+
+ // Actual implementation of SelectAll(), but also provides control over
+ // whether the PRIMARY selection is set to the selected text (in SelectAll(),
+ // it isn't, but we want set the selection when the user clicks in the entry).
+ void SelectAllInternal(bool reversed, bool update_primary_selection);
+
+ // Get ready to update |text_buffer_|'s highlighting without making changes to
+ // the PRIMARY selection. Removes the clipboard from |text_buffer_| and
+ // blocks the "mark-set" signal handler.
+ void StartUpdatingHighlightedText();
+
+ // Finish updating |text_buffer_|'s highlighting such that future changes will
+ // automatically update the PRIMARY selection. Undoes
+ // StartUpdatingHighlightedText()'s changes.
+ void FinishUpdatingHighlightedText();
+
+ // Get the character indices of the current selection. This honors
+ // direction, cp_max is the insertion point, and cp_min is the bound.
+ CharRange GetSelection() const;
+
+ // Translate from character positions to iterators for the current buffer.
+ void ItersFromCharRange(const CharRange& range,
+ GtkTextIter* iter_min,
+ GtkTextIter* iter_max);
+
+ // Return the number of characers in the current buffer.
+ int GetTextLength() const;
+
+ // Places the caret at the given position. This clears any selection.
+ void PlaceCaretAt(int pos);
+
+ // Returns true if the caret is at the end of the content.
+ bool IsCaretAtEnd() const;
+
+ // Try to parse the current text as a URL and colorize the components.
+ void EmphasizeURLComponents();
+
+ // Internally invoked whenever the text changes in some way.
+ void TextChanged();
+
+ // Save |selected_text| as the PRIMARY X selection. Unlike
+ // OwnPrimarySelection(), this won't set an owner or use callbacks.
+ void SavePrimarySelection(const std::string& selected_text);
+
+ // Update the field with |text| and set the selection.
+ void SetTextAndSelectedRange(const string16& text,
+ const CharRange& range);
+
+ // Set the selection to |range|.
+ void SetSelectedRange(const CharRange& range);
+
+ // Adjust the text justification according to the text direction of the widget
+ // and |text_buffer_|'s content, to make sure the real text justification is
+ // always in sync with the UI language direction.
+ void AdjustTextJustification();
+
+ // Get the text direction of |text_buffer_|'s content, by searching the first
+ // character that has a strong direction.
+ PangoDirection GetContentDirection();
+
+ // Returns the selected text.
+ std::string GetSelectedText() const;
+
+ // If the selected text parses as a URL OwnPrimarySelection is invoked.
+ void UpdatePrimarySelectionIfValidURL();
+
+ // Retrieves the first and last iterators in the |text_buffer_|, but excludes
+ // the anchor holding the |instant_view_| widget.
+ void GetTextBufferBounds(GtkTextIter* start, GtkTextIter* end) const;
+
+ // Validates an iterator in the |text_buffer_|, to make sure it doesn't go
+ // beyond the anchor for holding the |instant_view_| widget.
+ void ValidateTextBufferIter(GtkTextIter* iter) const;
+
+ // Adjusts vertical alignment of the |instant_view_| in the |text_view_|, to
+ // make sure they have the same baseline.
+ void AdjustVerticalAlignmentOfInstantView();
+
+ // Stop showing the instant suggest auto-commit animation.
+ void StopAnimation();
+
+ // The widget we expose, used for vertically centering the real text edit,
+ // since the height will change based on the font / font size, etc.
+ OwnedWidgetGtk alignment_;
+
+ // The actual text entry which will be owned by the alignment_. The
+ // reference will be set to NULL upon destruction to tell if the gtk
+ // widget tree has been destroyed. This is because gtk destroies child
+ // widgets if the parent (alignemtn_)'s refcount does not go down to 0.
+ GtkWidget* text_view_;
+
+ GtkTextTagTable* tag_table_;
+ GtkTextBuffer* text_buffer_;
+ GtkTextTag* faded_text_tag_;
+ GtkTextTag* secure_scheme_tag_;
+ GtkTextTag* security_error_scheme_tag_;
+ GtkTextTag* normal_text_tag_;
+
+ // Objects for the instant suggestion text view.
+ GtkTextTag* instant_anchor_tag_;
+
+ // A widget for displaying instant suggestion text. It'll be attached to a
+ // child anchor in the |text_buffer_| object.
+ GtkWidget* instant_view_;
+ // Animation from instant suggest (faded text) to autocomplete (selected
+ // text).
+ scoped_ptr<ui::MultiAnimation> instant_animation_;
+
+ // A mark to split the content and the instant anchor. Wherever the end
+ // iterator of the text buffer is required, the iterator to this mark should
+ // be used.
+ GtkTextMark* instant_mark_;
+
+ scoped_ptr<AutocompleteEditModel> model_;
+ scoped_ptr<AutocompletePopupView> popup_view_;
+ AutocompleteEditController* controller_;
+ ToolbarModel* toolbar_model_;
+
+ // The object that handles additional command functionality exposed on the
+ // edit, such as invoking the keyword editor.
+ CommandUpdater* command_updater_;
+
+ // When true, the location bar view is read only and also is has a slightly
+ // different presentation (smaller font size). This is used for popups.
+ bool popup_window_mode_;
+
+ ToolbarModel::SecurityLevel security_level_;
+
+ // Selection at the point where the user started using the
+ // arrows to move around in the popup.
+ CharRange saved_temporary_selection_;
+
+ // Tracking state before and after a possible change.
+ string16 text_before_change_;
+ CharRange sel_before_change_;
+
+ // The most-recently-selected text from the entry that was copied to the
+ // clipboard. This is updated on-the-fly as the user selects text. This may
+ // differ from the actual selected text, such as when 'http://' is prefixed to
+ // the text. It is used in cases where we need to make the PRIMARY selection
+ // persist even after the user has unhighlighted the text in the view
+ // (e.g. when they highlight some text and then click to unhighlight it, we
+ // pass this string to SavePrimarySelection()).
+ std::string selected_text_;
+
+ // When we own the X clipboard, this is the text for it.
+ std::string primary_selection_text_;
+
+ // IDs of the signal handlers for "mark-set" on |text_buffer_|.
+ gulong mark_set_handler_id_;
+ gulong mark_set_handler_id2_;
+
+#if defined(OS_CHROMEOS)
+ // The following variables are used to implement select-all-on-mouse-up, which
+ // is disabled in the standard Linux build due to poor interaction with the
+ // PRIMARY X selection.
+
+ // Is the first mouse button currently down? When selection marks get moved,
+ // we use this to determine if the user was highlighting text with the mouse
+ // -- if so, we avoid selecting all the text on mouse-up.
+ bool button_1_pressed_;
+
+ // Did the user change the selected text in the middle of the current click?
+ // If so, we don't select all of the text when the button is released -- we
+ // don't want to blow away their selection.
+ bool text_selected_during_click_;
+
+ // Was the text view already focused before the user clicked in it? We use
+ // this to figure out whether we should select all of the text when the button
+ // is released (we only do so if the view was initially unfocused).
+ bool text_view_focused_before_button_press_;
+#endif
+
+#if defined(TOOLKIT_VIEWS)
+ views::View* location_bar_view_;
+#else
+ // Supplies colors, et cetera.
+ GtkThemeService* theme_service_;
+
+ NotificationRegistrar registrar_;
+#endif
+
+ // Indicates if Enter key was pressed.
+ //
+ // It's used in the key press handler to detect an Enter key press event
+ // during sync dispatch of "end-user-action" signal so that an unexpected
+ // change caused by the event can be ignored in OnAfterPossibleChange().
+ bool enter_was_pressed_;
+
+ // Indicates if Tab key was pressed.
+ //
+ // It's only used in the key press handler to detect a Tab key press event
+ // during sync dispatch of "move-focus" signal.
+ bool tab_was_pressed_;
+
+ // Indicates that user requested to paste clipboard.
+ // The actual paste clipboard action might be performed later if the
+ // clipboard is not empty.
+ bool paste_clipboard_requested_;
+
+ // Indicates if an Enter key press is inserted as text.
+ // It's used in the key press handler to determine if an Enter key event is
+ // handled by IME or not.
+ bool enter_was_inserted_;
+
+ // Indicates whether the IME changed the text. It's possible for the IME to
+ // handle a key event but not change the text contents (e.g., when pressing
+ // shift+del with no selection).
+ bool text_changed_;
+
+ // Contains the character range that should have a strikethrough (used for
+ // insecure schemes). If the range is size one or less, no strikethrough
+ // is needed.
+ CharRange strikethrough_;
+
+ // Indicates if the selected text is suggested text or not. If the selection
+ // is not suggested text, that means the user manually made the selection.
+ bool selection_suggested_;
+
+ // Was delete pressed?
+ bool delete_was_pressed_;
+
+ // Was the delete key pressed with an empty selection at the end of the edit?
+ bool delete_at_end_pressed_;
+
+ // Indicates if we are handling a key press event.
+ bool handling_key_press_;
+
+ // Indicates if omnibox's content maybe changed by a key press event, so that
+ // we need to call OnAfterPossibleChange() after handling the event.
+ // This flag should be set for changes directly caused by a key press event,
+ // including changes to content text, selection range and preedit string.
+ // Changes caused by function calls like SetUserText() should not affect this
+ // flag.
+ bool content_maybe_changed_by_key_press_;
+
+ // Set this flag to call UpdatePopup() in lost focus and need to update.
+ // Because context menu might take the focus, before setting the flag, check
+ // the focus with model_->has_focus().
+ bool update_popup_without_focus_;
+
+#if GTK_CHECK_VERSION(2, 20, 0)
+ // Stores the text being composed by the input method.
+ string16 preedit_;
+
+ // Tracking preedit state before and after a possible change. We don't need to
+ // track preedit_'s content, as it'll be treated as part of text content.
+ size_t preedit_size_before_change_;
+#endif
+
+ // The view that is going to be focused next. Only valid while handling
+ // "focus-out" events.
+ GtkWidget* going_to_focus_;
+
+ ui::GtkSignalRegistrar signals_;
+
+ DISALLOW_COPY_AND_ASSIGN(OmniboxViewGtk);
+};
+
+#endif // CHROME_BROWSER_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index a3f4b82..77296d8 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -171,10 +171,8 @@ void LocationBarView::Init() {
GetWidget()->GetNativeView(), profile_, command_updater_,
mode_ == POPUP, this));
#else
- location_entry_.reset(
- AutocompleteEditViewGtk::Create(
- this, model_, profile_,
- command_updater_, mode_ == POPUP, this));
+ location_entry_.reset(OmniboxViewGtk::Create(this, model_, profile_,
+ command_updater_, mode_ == POPUP, this));
#endif
location_entry_view_ = location_entry_->AddToView(this);
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index 8017a6e..cd39837 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -26,7 +26,7 @@
#if defined(OS_WIN)
#include "chrome/browser/autocomplete/autocomplete_edit_view_win.h"
#elif defined(OS_LINUX)
-#include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h"
+#include "chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h"
#endif
class CommandUpdater;