summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc
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/autocomplete/autocomplete_edit_view_gtk.cc
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/autocomplete/autocomplete_edit_view_gtk.cc')
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc2347
1 files changed, 0 insertions, 2347 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc
deleted file mode 100644
index 7bb87ce..0000000
--- a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc
+++ /dev/null
@@ -1,2347 +0,0 @@
-// 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/autocomplete/autocomplete_edit_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* kAutocompleteEditViewGtkKey = "__ACE_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 AutocompleteEditViewGtk::CharRange& selection_range)
- : selection_range(selection_range) {
- }
-
- // Range of selected text.
- AutocompleteEditViewGtk::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
-
-AutocompleteEditViewGtk::AutocompleteEditViewGtk(
- 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));
-}
-
-AutocompleteEditViewGtk::~AutocompleteEditViewGtk() {
- NotificationService::current()->Notify(
- NotificationType::AUTOCOMPLETE_EDIT_DESTROYED,
- Source<AutocompleteEditViewGtk>(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 AutocompleteEditViewGtk::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_), kAutocompleteEditViewGtkKey, 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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::SetFocus() {
- DCHECK(text_view_);
- gtk_widget_grab_focus(text_view_);
-}
-
-int AutocompleteEditViewGtk::WidthOfTextAfterCursor() {
- // Not used.
- return -1;
-}
-
-AutocompleteEditModel* AutocompleteEditViewGtk::model() {
- return model_.get();
-}
-
-const AutocompleteEditModel* AutocompleteEditViewGtk::model() const {
- return model_.get();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::IsEditingOrEmpty() const {
- return model_->user_input_in_progress() || (GetTextLength() == 0);
-}
-
-int AutocompleteEditViewGtk::GetIcon() const {
- return IsEditingOrEmpty() ?
- AutocompleteMatch::TypeToIcon(model_->CurrentTextType()) :
- toolbar_model_->GetIcon();
-}
-
-void AutocompleteEditViewGtk::SetUserText(const string16& text) {
- SetUserText(text, text, true);
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::SetWindowTextAndCaretPos(const string16& text,
- size_t caret_pos) {
- CharRange range(static_cast<int>(caret_pos), static_cast<int>(caret_pos));
- SetTextAndSelectedRange(text, range);
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::DeleteAtEndPressed() {
- return delete_at_end_pressed_;
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::RevertAll() {
- ClosePopup();
- model_->Revert();
- TextChanged();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::ClosePopup() {
- model_->StopAutocomplete();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::OnRevertTemporaryText() {
- StartUpdatingHighlightedText();
- SetSelectedRange(saved_temporary_selection_);
- FinishUpdatingHighlightedText();
- TextChanged();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::GetNativeView() const {
- return alignment_.get();
-}
-
-CommandUpdater* AutocompleteEditViewGtk::GetCommandUpdater() {
- return command_updater_;
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::GetInstantSuggestion() const {
- const gchar* suggestion = gtk_label_get_text(GTK_LABEL(instant_view_));
- return suggestion ? UTF8ToUTF16(suggestion) : string16();
-}
-
-int AutocompleteEditViewGtk::TextWidth() const {
- // TextWidth may be called after gtk widget tree is destroyed but
- // before AutocompleteEditViewGtk 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 AutocompleteEditViewGtk::IsImeComposing() const {
-#if GTK_CHECK_VERSION(2, 20, 0)
- return !preedit_.empty();
-#else
- return false;
-#endif
-}
-
-#if defined(TOOLKIT_VIEWS)
-views::View* AutocompleteEditViewGtk::AddToView(views::View* parent) {
- views::NativeViewHost* host = new views::NativeViewHost;
- parent->AddChildView(host);
- host->set_focus_view(parent);
- host->Attach(GetNativeView());
- return host;
-}
-
-int AutocompleteEditViewGtk::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* AutocompleteEditViewGtk::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;
- }
-
- AutocompleteEditViewGtk* autocomplete =
- new AutocompleteEditViewGtk(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 AutocompleteEditViewGtk::Observe(NotificationType type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- DCHECK(type == NotificationType::BROWSER_THEME_CHANGED);
-
- SetBaseColor();
-}
-
-void AutocompleteEditViewGtk::AnimationEnded(const ui::Animation* animation) {
- model_->CommitSuggestedText(false);
-}
-
-void AutocompleteEditViewGtk::AnimationProgressed(
- const ui::Animation* animation) {
- UpdateInstantViewColors();
-}
-
-void AutocompleteEditViewGtk::AnimationCanceled(
- const ui::Animation* animation) {
- UpdateInstantViewColors();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::HandleBeginUserAction(GtkTextBuffer* sender) {
- OnBeforePossibleChange();
-}
-
-void AutocompleteEditViewGtk::HandleEndUserAction(GtkTextBuffer* sender) {
- OnAfterPossibleChange();
-}
-
-gboolean AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::HandleEditSearchEngines(GtkWidget* sender) {
- command_updater_->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES);
-}
-
-void AutocompleteEditViewGtk::HandlePasteAndGo(GtkWidget* sender) {
- model_->PasteAndGo();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::HandleCopyClipboard(GtkWidget* sender) {
- HandleCopyOrCutClipboard(true);
-}
-
-void AutocompleteEditViewGtk::HandleCutClipboard(GtkWidget* sender) {
- HandleCopyOrCutClipboard(false);
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::OnPerformDropImpl(const string16& text) {
- if (model_->CanPasteAndGo(CollapseWhitespace(text, true))) {
- model_->PasteAndGo();
- return true;
- }
-
- return false;
-}
-
-gfx::Font AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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_);
-}
-
-AutocompleteEditViewGtk::CharRange
- AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::IsCaretAtEnd() const {
- const CharRange selection = GetSelection();
- return selection.cp_min == selection.cp_max &&
- selection.cp_min == GetTextLength();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::StopAnimation() {
- // Clear the animation delegate so we don't get an AnimationEnded() callback.
- instant_animation_->set_delegate(NULL);
- instant_animation_->Stop();
- UpdateInstantViewColors();
-}
-
-void AutocompleteEditViewGtk::TextChanged() {
- EmphasizeURLComponents();
- model_->OnChanged();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::HandleWidgetDirectionChanged(
- GtkWidget* sender, GtkTextDirection previous_direction) {
- AdjustTextJustification();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::HandleKeymapDirectionChanged(GdkKeymap* sender) {
- AdjustTextJustification();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::ClipboardGetSelectionThunk(
- GtkClipboard* clipboard,
- GtkSelectionData* selection_data,
- guint info,
- gpointer object) {
- AutocompleteEditViewGtk* edit_view =
- reinterpret_cast<AutocompleteEditViewGtk*>(
- g_object_get_data(G_OBJECT(object), kAutocompleteEditViewGtkKey));
- edit_view->ClipboardGetSelection(clipboard, selection_data, info);
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::HandleUndoRedo(GtkWidget* sender) {
- OnBeforePossibleChange();
-}
-
-void AutocompleteEditViewGtk::HandleUndoRedoAfter(GtkWidget* sender) {
- OnAfterPossibleChange();
-}
-
-void AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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 AutocompleteEditViewGtk::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);
-}