| Index: chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
|
| diff --git a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc b/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
|
| deleted file mode 100644
|
| index 781d2b50e72537b83a7b58287443a402adc46274..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.cc
|
| +++ /dev/null
|
| @@ -1,2194 +0,0 @@
|
| -// Copyright 2012 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/metrics/histogram.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/utf_string_conversion_utils.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| -#include "chrome/app/chrome_command_ids.h"
|
| -#include "chrome/browser/autocomplete/autocomplete_input.h"
|
| -#include "chrome/browser/autocomplete/autocomplete_match.h"
|
| -#include "chrome/browser/bookmarks/bookmark_node_data.h"
|
| -#include "chrome/browser/chrome_notification_types.h"
|
| -#include "chrome/browser/command_updater.h"
|
| -#include "chrome/browser/defaults.h"
|
| -#include "chrome/browser/platform_util.h"
|
| -#include "chrome/browser/search/search.h"
|
| -#include "chrome/browser/ui/browser.h"
|
| -#include "chrome/browser/ui/gtk/gtk_theme_service.h"
|
| -#include "chrome/browser/ui/gtk/gtk_util.h"
|
| -#include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
|
| -#include "chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h"
|
| -#include "chrome/browser/ui/gtk/view_id_util.h"
|
| -#include "chrome/browser/ui/omnibox/omnibox_edit_controller.h"
|
| -#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
|
| -#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
|
| -#include "chrome/browser/ui/tabs/tab_strip_model.h"
|
| -#include "chrome/browser/ui/toolbar/toolbar_model.h"
|
| -#include "content/public/browser/notification_source.h"
|
| -#include "content/public/browser/web_contents.h"
|
| -#include "extensions/common/constants.h"
|
| -#include "grit/generated_resources.h"
|
| -#include "net/base/escape.h"
|
| -#include "third_party/undoview/undo_view.h"
|
| -#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
|
| -#include "ui/base/dragdrop/drag_drop_types.h"
|
| -#include "ui/base/dragdrop/gtk_dnd_util.h"
|
| -#include "ui/base/gtk/gtk_hig_constants.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_compat.h"
|
| -#include "ui/gfx/skia_utils_gtk.h"
|
| -#include "url/gurl.h"
|
| -
|
| -using content::WebContents;
|
| -
|
| -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 base::string16& text, size_t text_offset) {
|
| - return base::UTF16ToUTF8(text.substr(0, text_offset)).size();
|
| -}
|
| -
|
| -// 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;
|
| -};
|
| -
|
| -const char kAutocompleteEditStateKey[] = "AutocompleteEditState";
|
| -
|
| -struct AutocompleteEditState : public base::SupportsUserData::Data {
|
| - AutocompleteEditState(const OmniboxEditModel::State& model_state,
|
| - const ViewState& view_state)
|
| - : model_state(model_state),
|
| - view_state(view_state) {
|
| - }
|
| - virtual ~AutocompleteEditState() {}
|
| -
|
| - const OmniboxEditModel::State model_state;
|
| - const ViewState view_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);
|
| - }
|
| -}
|
| -
|
| -// Returns the |menu| item whose label matches |label|.
|
| -guint GetPopupMenuIndexForStockLabel(const char* label, GtkMenu* menu) {
|
| - GList* list = gtk_container_get_children(GTK_CONTAINER(menu));
|
| - guint index = 1;
|
| - for (GList* item = list; item != NULL; item = item->next, ++index) {
|
| - if (GTK_IS_IMAGE_MENU_ITEM(item->data)) {
|
| - gboolean is_stock = gtk_image_menu_item_get_use_stock(
|
| - GTK_IMAGE_MENU_ITEM(item->data));
|
| - if (is_stock) {
|
| - std::string menu_item_label =
|
| - gtk_menu_item_get_label(GTK_MENU_ITEM(item->data));
|
| - if (menu_item_label == label)
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - g_list_free(list);
|
| - return index;
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -OmniboxViewGtk::OmniboxViewGtk(OmniboxEditController* controller,
|
| - Browser* browser,
|
| - Profile* profile,
|
| - CommandUpdater* command_updater,
|
| - bool popup_window_mode,
|
| - GtkWidget* location_bar)
|
| - : OmniboxView(profile, controller, command_updater),
|
| - browser_(browser),
|
| - 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),
|
| - gray_text_anchor_tag_(NULL),
|
| - gray_text_view_(NULL),
|
| - gray_text_mark_(NULL),
|
| - popup_window_mode_(popup_window_mode),
|
| - security_level_(ToolbarModel::NONE),
|
| - mark_set_handler_id_(0),
|
| - button_1_pressed_(false),
|
| - theme_service_(GtkThemeService::GetFrom(profile)),
|
| - 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),
|
| - supports_pre_edit_(!gtk_check_version(2, 20, 0)),
|
| - pre_edit_size_before_change_(0),
|
| - going_to_focus_(NULL),
|
| - font_baseline_shift_(0) {
|
| - OmniboxPopupViewGtk* view = new OmniboxPopupViewGtk(
|
| - GetFont(), this, model(), location_bar);
|
| - view->Init();
|
| - popup_view_.reset(view);
|
| -}
|
| -
|
| -OmniboxViewGtk::~OmniboxViewGtk() {
|
| - // 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();
|
| -
|
| - // 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();
|
| -
|
| - alignment_.Own(gtk_alignment_new(0.0, 0.0, 1.0, 1.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_after(text_view_, "drag-begin",
|
| - G_CALLBACK(&HandleDragBeginThunk), this);
|
| - g_signal_connect_after(text_view_, "drag-end",
|
| - G_CALLBACK(&HandleDragEndThunk), 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(text_view_, "expose-event",
|
| - G_CALLBACK(&HandleExposeEventThunk), this);
|
| - g_signal_connect_after(text_view_, "expose-event",
|
| - G_CALLBACK(&HandleExposeEventAfterThunk), 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 (supports_pre_edit_) {
|
| - g_signal_connect(text_view_, "preedit-changed",
|
| - G_CALLBACK(&HandlePreEditChangedThunk), this);
|
| - }
|
| - 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(>k_widget_destroyed), &text_view_);
|
| -
|
| - // Setup for the gray suggestion text view.
|
| - // GtkLabel is used instead of GtkTextView to get transparent background.
|
| - gray_text_view_ = gtk_label_new(NULL);
|
| - gtk_widget_set_no_show_all(gray_text_view_, TRUE);
|
| - gtk_label_set_selectable(GTK_LABEL(gray_text_view_), TRUE);
|
| -
|
| - GtkTextIter end_iter;
|
| - gtk_text_buffer_get_end_iter(text_buffer_, &end_iter);
|
| -
|
| - // Insert a Zero Width Space character just before the gray text anchor.
|
| - // It's a hack to workaround a bug of GtkTextView which can not align the
|
| - // pre-edit string and a child anchor correctly when there is no other content
|
| - // around the pre-edit string.
|
| - gtk_text_buffer_insert(text_buffer_, &end_iter, "\342\200\213", -1);
|
| - GtkTextChildAnchor* gray_text_anchor =
|
| - gtk_text_buffer_create_child_anchor(text_buffer_, &end_iter);
|
| -
|
| - gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(text_view_),
|
| - gray_text_view_,
|
| - gray_text_anchor);
|
| -
|
| - gray_text_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,
|
| - gray_text_anchor);
|
| - gtk_text_buffer_apply_tag(text_buffer_, gray_text_anchor_tag_,
|
| - &anchor_iter, &end_iter);
|
| -
|
| - GtkTextIter start_iter;
|
| - gtk_text_buffer_get_start_iter(text_buffer_, &start_iter);
|
| - gray_text_mark_ =
|
| - gtk_text_buffer_create_mark(text_buffer_, NULL, &start_iter, FALSE);
|
| -
|
| - // Hooking up this handler after setting up above hacks for gray text view, so
|
| - // that we won't filter out the special ZWP mark itself.
|
| - g_signal_connect(text_buffer_, "insert-text",
|
| - G_CALLBACK(&HandleInsertTextThunk), this);
|
| -
|
| - AdjustVerticalAlignmentOfGrayTextView();
|
| -
|
| - registrar_.Add(this,
|
| - chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
|
| - content::Source<ThemeService>(theme_service_));
|
| - theme_service_->InitThemesFor(this);
|
| -
|
| - ViewIDUtil::SetID(GetNativeView(), VIEW_ID_OMNIBOX);
|
| -}
|
| -
|
| -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_);
|
| - // Restore caret visibility if focus is explicitly requested. This is
|
| - // necessary because if we already have invisible focus, the RequestFocus()
|
| - // call above will short-circuit, preventing us from reaching
|
| - // OmniboxEditModel::OnSetFocus(), which handles restoring visibility when the
|
| - // omnibox regains focus after losing focus.
|
| - model()->SetCaretVisibility(true);
|
| -}
|
| -
|
| -void OmniboxViewGtk::ApplyCaretVisibility() {
|
| - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text_view_),
|
| - model()->is_caret_visible());
|
| -}
|
| -
|
| -void OmniboxViewGtk::SaveStateToTab(WebContents* 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.
|
| - OmniboxEditModel::State model_state = model()->GetStateForTabSwitch();
|
| - tab->SetUserData(
|
| - kAutocompleteEditStateKey,
|
| - new AutocompleteEditState(model_state, ViewState(GetSelection())));
|
| -}
|
| -
|
| -void OmniboxViewGtk::OnTabChanged(const WebContents* web_contents) {
|
| - security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
|
| - selected_text_.clear();
|
| -
|
| - const AutocompleteEditState* state = static_cast<AutocompleteEditState*>(
|
| - web_contents->GetUserData(&kAutocompleteEditStateKey));
|
| - model()->RestoreState(state ? &state->model_state : NULL);
|
| - if (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();
|
| - }
|
| -}
|
| -
|
| -void OmniboxViewGtk::Update() {
|
| - const ToolbarModel::SecurityLevel old_security_level = security_level_;
|
| - security_level_ = controller()->GetToolbarModel()->GetSecurityLevel(false);
|
| - if (model()->UpdatePermanentText()) {
|
| - // Something visibly changed. Re-enable URL replacement.
|
| - controller()->GetToolbarModel()->set_url_replacement_enabled(true);
|
| - model()->UpdatePermanentText();
|
| -
|
| - RevertAll();
|
| - } else if (old_security_level != security_level_) {
|
| - EmphasizeURLComponents();
|
| - }
|
| -}
|
| -
|
| -base::string16 OmniboxViewGtk::GetText() const {
|
| - GtkTextIter start, end;
|
| - GetTextBufferBounds(&start, &end);
|
| - gchar* utf8 = gtk_text_buffer_get_text(text_buffer_, &start, &end, false);
|
| - base::string16 out(base::UTF8ToUTF16(utf8));
|
| - g_free(utf8);
|
| -
|
| - if (supports_pre_edit_) {
|
| - // 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 (pre_edit_.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), pre_edit_);
|
| - }
|
| - }
|
| - return out;
|
| -}
|
| -
|
| -void OmniboxViewGtk::SetWindowTextAndCaretPos(const base::string16& text,
|
| - size_t caret_pos,
|
| - bool update_popup,
|
| - bool notify_text_changed) {
|
| - CharRange range(static_cast<int>(caret_pos), static_cast<int>(caret_pos));
|
| - SetTextAndSelectedRange(text, range);
|
| -
|
| - if (update_popup)
|
| - UpdatePopup();
|
| -
|
| - if (notify_text_changed)
|
| - TextChanged();
|
| -}
|
| -
|
| -void OmniboxViewGtk::SetForcedQuery() {
|
| - const base::string16 current_text(GetText());
|
| - const size_t start = current_text.find_first_not_of(base::kWhitespaceUTF16);
|
| - if (start == base::string16::npos || (current_text[start] != '?')) {
|
| - SetUserText(base::ASCIIToUTF16("?"));
|
| - } else {
|
| - StartUpdatingHighlightedText();
|
| - SetSelectedRange(CharRange(current_text.size(), start + 1));
|
| - FinishUpdatingHighlightedText();
|
| - }
|
| -}
|
| -
|
| -bool OmniboxViewGtk::IsSelectAll() const {
|
| - 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(base::string16::size_type* start,
|
| - base::string16::size_type* end) const {
|
| - 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::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) < GetOmniboxTextLength() ||
|
| - IsImeComposing();
|
| - model()->StartAutocomplete(sel.cp_min != sel.cp_max, no_inline_autocomplete);
|
| -}
|
| -
|
| -void OmniboxViewGtk::OnTemporaryTextMaybeChanged(
|
| - const base::string16& display_text,
|
| - bool save_original_selection,
|
| - bool notify_text_changed) {
|
| - if (save_original_selection)
|
| - saved_temporary_selection_ = GetSelection();
|
| -
|
| - StartUpdatingHighlightedText();
|
| - SetWindowTextAndCaretPos(display_text, display_text.length(), false, false);
|
| - FinishUpdatingHighlightedText();
|
| - if (notify_text_changed)
|
| - TextChanged();
|
| -}
|
| -
|
| -bool OmniboxViewGtk::OnInlineAutocompleteTextMaybeChanged(
|
| - const base::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::OnInlineAutocompleteTextCleared() {
|
| -}
|
| -
|
| -void OmniboxViewGtk::OnRevertTemporaryText() {
|
| - StartUpdatingHighlightedText();
|
| - SetSelectedRange(saved_temporary_selection_);
|
| - FinishUpdatingHighlightedText();
|
| - // We got here because the user hit the Escape key. We explicitly don't call
|
| - // TextChanged(), since OmniboxPopupModel::ResetToDefaultMatch() has already
|
| - // been called by now, and it would've called TextChanged() if it was
|
| - // warranted.
|
| -}
|
| -
|
| -void OmniboxViewGtk::OnBeforePossibleChange() {
|
| - // Record this paste, so we can do different behavior.
|
| - if (paste_clipboard_requested_) {
|
| - paste_clipboard_requested_ = false;
|
| - model()->OnPaste();
|
| - }
|
| -
|
| - // 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 (supports_pre_edit_)
|
| - pre_edit_size_before_change_ = pre_edit_.size();
|
| -}
|
| -
|
| -// 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 = GetOmniboxTextLength();
|
| - 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 base::string16 new_text(GetText());
|
| - text_changed_ = (new_text != text_before_change_) || (supports_pre_edit_ &&
|
| - (pre_edit_.size() != pre_edit_size_before_change_));
|
| -
|
| - 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(
|
| - text_before_change_, 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 the 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();
|
| -}
|
| -
|
| -gfx::NativeView OmniboxViewGtk::GetRelativeWindowForPopup() const {
|
| - GtkWidget* toplevel = gtk_widget_get_toplevel(GetNativeView());
|
| - DCHECK(gtk_widget_is_toplevel(toplevel));
|
| - return toplevel;
|
| -}
|
| -
|
| -void OmniboxViewGtk::SetGrayTextAutocompletion(
|
| - const base::string16& suggestion) {
|
| - std::string suggestion_utf8 = base::UTF16ToUTF8(suggestion);
|
| -
|
| - gtk_label_set_text(GTK_LABEL(gray_text_view_), suggestion_utf8.c_str());
|
| -
|
| - if (suggestion.empty()) {
|
| - gtk_widget_hide(gray_text_view_);
|
| - return;
|
| - }
|
| -
|
| - gtk_widget_show(gray_text_view_);
|
| - AdjustVerticalAlignmentOfGrayTextView();
|
| - UpdateGrayTextViewColors();
|
| -}
|
| -
|
| -base::string16 OmniboxViewGtk::GetGrayTextAutocompletion() const {
|
| - const gchar* suggestion = gtk_label_get_text(GTK_LABEL(gray_text_view_));
|
| - return suggestion ? base::UTF8ToUTF16(suggestion) : base::string16();
|
| -}
|
| -
|
| -int OmniboxViewGtk::GetTextWidth() 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 gray 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;
|
| -}
|
| -
|
| -int OmniboxViewGtk::GetWidth() const {
|
| - GtkAllocation allocation;
|
| - gtk_widget_get_allocation(text_view_, &allocation);
|
| - return allocation.width;
|
| -}
|
| -
|
| -bool OmniboxViewGtk::IsImeComposing() const {
|
| - return supports_pre_edit_ && !pre_edit_.empty();
|
| -}
|
| -
|
| -void OmniboxViewGtk::Observe(int type,
|
| - const content::NotificationSource& source,
|
| - const content::NotificationDetails& details) {
|
| - DCHECK(type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED);
|
| -
|
| - OnBrowserThemeChanged();
|
| -}
|
| -
|
| -void OmniboxViewGtk::UpdateGrayTextViewColors() {
|
| - GdkColor faded_text;
|
| - if (theme_service_->UsingNativeTheme()) {
|
| - GtkStyle* style = gtk_rc_get_style(gray_text_view_);
|
| - faded_text = gtk_util::AverageColors(
|
| - style->text[GTK_STATE_NORMAL], style->base[GTK_STATE_NORMAL]);
|
| - } else {
|
| - gdk_color_parse(kTextBaseColor, &faded_text);
|
| - }
|
| - gtk_widget_modify_fg(gray_text_view_, GTK_STATE_NORMAL, &faded_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 or result traversal 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);
|
| -
|
| - shift_was_pressed_ = event->state & 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 OmniboxEditModel 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);
|
| - }
|
| -
|
| - 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 OmniboxEditModel 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_window_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_);
|
| -
|
| - // Restore caret visibility whenever the user clicks in the omnibox in a way
|
| - // that would give it focus. We must handle this case separately here because
|
| - // if the omnibox currently has invisible focus, the mouse event won't trigger
|
| - // either SetFocus() or OmniboxEditModel::OnSetFocus().
|
| - if (event->button == 1 || event->button == 2)
|
| - model()->SetCaretVisibility(true);
|
| -
|
| - if (event->button == 1) {
|
| - button_1_pressed_ = true;
|
| -
|
| - // 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;
|
| -
|
| - bool button_1_was_pressed = button_1_pressed_;
|
| - button_1_pressed_ = false;
|
| -
|
| - DCHECK(text_view_);
|
| -
|
| - // 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);
|
| -
|
| - // Inform model() about possible text selection change. We may get a button
|
| - // release with no press (e.g. if the user clicks in the omnibox to dismiss a
|
| - // bubble).
|
| - if (button_1_was_pressed)
|
| - 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;
|
| - GdkWindow* gdk_window = gtk_widget_get_window(text_view_);
|
| - gdk_window_get_pointer(gdk_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.
|
| - CloseOmniboxPopup();
|
| - // Tell the model to reset itself.
|
| - model()->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();
|
| - }
|
| - } 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);
|
| -
|
| - // Paste and Go menu item.
|
| - GtkClipboard* x_clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
|
| - gchar* text = gtk_clipboard_wait_for_text(x_clipboard);
|
| - sanitized_text_for_paste_and_go_ = text ?
|
| - StripJavascriptSchemas(
|
| - base::CollapseWhitespace(base::UTF8ToUTF16(text), true)) :
|
| - base::string16();
|
| - g_free(text);
|
| - GtkWidget* paste_and_go_menuitem = gtk_menu_item_new_with_mnemonic(
|
| - ui::ConvertAcceleratorsFromWindowsStyle(l10n_util::GetStringUTF8(
|
| - model()->IsPasteAndSearch(sanitized_text_for_paste_and_go_) ?
|
| - IDS_PASTE_AND_SEARCH : IDS_PASTE_AND_GO)).c_str());
|
| - // Detect the stock Paste menu item by searching for the stock label
|
| - // GTK_STOCK_PASTE. If we don't find it, the Paste and Go item will be
|
| - // appended at the end of the popup menu.
|
| - gtk_menu_shell_insert(GTK_MENU_SHELL(menu), paste_and_go_menuitem,
|
| - GetPopupMenuIndexForStockLabel(GTK_STOCK_PASTE, menu));
|
| - g_signal_connect(paste_and_go_menuitem, "activate",
|
| - G_CALLBACK(HandlePasteAndGoThunk), this);
|
| - gtk_widget_set_sensitive(
|
| - paste_and_go_menuitem,
|
| - model()->CanPasteAndGo(sanitized_text_for_paste_and_go_));
|
| - gtk_widget_show(paste_and_go_menuitem);
|
| -
|
| - // Show URL menu item.
|
| - if (chrome::IsQueryExtractionEnabled()) {
|
| - GtkWidget* show_url_menuitem = gtk_menu_item_new_with_mnemonic(
|
| - ui::ConvertAcceleratorsFromWindowsStyle(
|
| - l10n_util::GetStringUTF8(IDS_SHOW_URL)).c_str());
|
| - gtk_menu_shell_append(GTK_MENU_SHELL(menu), show_url_menuitem);
|
| - g_signal_connect(show_url_menuitem, "activate",
|
| - G_CALLBACK(HandleShowURLThunk), this);
|
| - gtk_widget_set_sensitive(
|
| - show_url_menuitem,
|
| - controller()->GetToolbarModel()->WouldReplaceURL());
|
| - gtk_widget_show(show_url_menuitem);
|
| - }
|
| -
|
| - // Edit Search Engines menu item.
|
| - GtkWidget* edit_search_engines_menuitem = gtk_menu_item_new_with_mnemonic(
|
| - ui::ConvertAcceleratorsFromWindowsStyle(
|
| - l10n_util::GetStringUTF8(IDS_EDIT_SEARCH_ENGINES)).c_str());
|
| - gtk_menu_shell_append(GTK_MENU_SHELL(menu), edit_search_engines_menuitem);
|
| - g_signal_connect(edit_search_engines_menuitem, "activate",
|
| - G_CALLBACK(HandleEditSearchEnginesThunk), this);
|
| - gtk_widget_set_sensitive(
|
| - edit_search_engines_menuitem,
|
| - command_updater()->IsCommandEnabled(IDC_EDIT_SEARCH_ENGINES));
|
| - gtk_widget_show(edit_search_engines_menuitem);
|
| -
|
| - g_signal_connect(menu, "deactivate",
|
| - G_CALLBACK(HandlePopupMenuDeactivateThunk), this);
|
| -}
|
| -
|
| -void OmniboxViewGtk::HandlePasteAndGo(GtkWidget* sender) {
|
| - model()->PasteAndGo(sanitized_text_for_paste_and_go_);
|
| -}
|
| -
|
| -void OmniboxViewGtk::HandleEditSearchEngines(GtkWidget* sender) {
|
| - command_updater()->ExecuteCommand(IDC_EDIT_SEARCH_ENGINES);
|
| -}
|
| -
|
| -void OmniboxViewGtk::HandleShowURL(GtkWidget* sender) {
|
| - ShowURL();
|
| -}
|
| -
|
| -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;
|
| - }
|
| -
|
| - // 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 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 (gdk_drag_context_get_source_window(context) ==
|
| - gtk_widget_get_window(text_view_))
|
| - return;
|
| -
|
| - guchar* text = gtk_selection_data_get_text(selection_data);
|
| - if (!text)
|
| - return;
|
| -
|
| - base::string16 possible_url =
|
| - base::UTF8ToUTF16(reinterpret_cast<char*>(text));
|
| - g_free(text);
|
| - if (OnPerformDropImpl(possible_url)) {
|
| - gtk_drag_finish(context, TRUE, FALSE, 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_);
|
| -
|
| - switch (target_type) {
|
| - case GTK_TEXT_BUFFER_TARGET_INFO_TEXT: {
|
| - gtk_selection_data_set_text(selection_data, dragged_text_.c_str(), -1);
|
| - break;
|
| - }
|
| - case ui::CHROME_NAMED_URL: {
|
| - WebContents* current_tab = controller()->GetWebContents();
|
| - base::string16 tab_title = current_tab->GetTitle();
|
| - // Pass an empty string if user has edited the URL.
|
| - if (current_tab->GetURL().spec() != dragged_text_)
|
| - tab_title = base::string16();
|
| - ui::WriteURLWithName(selection_data, GURL(dragged_text_),
|
| - tab_title, target_type);
|
| - break;
|
| - }
|
| - }
|
| -}
|
| -
|
| -void OmniboxViewGtk::HandleDragBegin(GtkWidget* widget,
|
| - GdkDragContext* context) {
|
| - base::string16 text = base::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_ = base::UTF16ToUTF8(text);
|
| - GtkTargetList* copy_targets =
|
| - gtk_text_buffer_get_copy_target_list(text_buffer_);
|
| - gtk_target_list_add(copy_targets,
|
| - ui::GetAtomForTarget(ui::CHROME_NAMED_URL),
|
| - GTK_TARGET_SAME_APP, ui::CHROME_NAMED_URL);
|
| - }
|
| - dragged_text_ = selected_text_;
|
| -}
|
| -
|
| -void OmniboxViewGtk::HandleDragEnd(GtkWidget* widget,
|
| - GdkDragContext* context) {
|
| - GdkAtom atom = ui::GetAtomForTarget(ui::CHROME_NAMED_URL);
|
| - GtkTargetList* copy_targets =
|
| - gtk_text_buffer_get_copy_target_list(text_buffer_);
|
| - gtk_target_list_remove(copy_targets, atom);
|
| - dragged_text_.clear();
|
| -}
|
| -
|
| -void OmniboxViewGtk::HandleInsertText(GtkTextBuffer* buffer,
|
| - GtkTextIter* location,
|
| - const gchar* text,
|
| - gint len) {
|
| - base::string16 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;
|
| -
|
| - for (const gchar* p = text; *p && (p - text) < len;
|
| - p = g_utf8_next_char(p)) {
|
| - gunichar c = g_utf8_get_char(p);
|
| -
|
| - // 0x200B is Zero Width Space, which is inserted just before the gray text
|
| - // 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 != 0x200B)
|
| - base::WriteUnicodeCharacter(c, &filtered_text);
|
| - }
|
| -
|
| - if (model()->is_pasting()) {
|
| - // If the user is pasting all-whitespace, paste a single space
|
| - // rather than nothing, since pasting nothing feels broken.
|
| - filtered_text = base::CollapseWhitespace(filtered_text, true);
|
| - filtered_text = filtered_text.empty() ? base::ASCIIToUTF16(" ") :
|
| - StripJavascriptSchemas(filtered_text);
|
| - }
|
| -
|
| - if (!filtered_text.empty()) {
|
| - // Avoid inserting the text after the gray text anchor.
|
| - ValidateTextBufferIter(location);
|
| -
|
| - // Call the default handler to insert filtered text.
|
| - GtkTextBufferClass* klass = GTK_TEXT_BUFFER_GET_CLASS(buffer);
|
| - std::string utf8_text = base::UTF16ToUTF8(filtered_text);
|
| - klass->insert_text(buffer, location, utf8_text.data(),
|
| - static_cast<gint>(utf8_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() && !shift_was_pressed_) {
|
| - handled = model()->AcceptKeyword(ENTERED_KEYWORD_MODE_VIA_TAB);
|
| - } else if (model()->popup_model()->IsOpen()) {
|
| - if (shift_was_pressed_ &&
|
| - model()->popup_model()->selected_line_state() ==
|
| - OmniboxPopupModel::KEYWORD)
|
| - model()->ClearKeyword(GetText());
|
| - else
|
| - model()->OnUpOrDownKeyPressed(shift_was_pressed_ ? -1 : 1);
|
| -
|
| - handled = true;
|
| - }
|
| -
|
| - if (supports_pre_edit_ && !handled && !pre_edit_.empty())
|
| - handled = true;
|
| -
|
| - if (!handled && gtk_widget_get_visible(gray_text_view_))
|
| - handled = model()->CommitSuggestedText();
|
| -
|
| - 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);
|
| -
|
| - CharRange selection = GetSelection();
|
| - GURL url;
|
| - base::string16 text(base::UTF8ToUTF16(GetSelectedText()));
|
| - bool write_url;
|
| - model()->AdjustTextForCopy(selection.selection_min(), IsSelectAll(), &text,
|
| - &url, &write_url);
|
| -
|
| - if (IsSelectAll())
|
| - UMA_HISTOGRAM_COUNTS(OmniboxEditModel::kCutOrCopyAllTextHistogram, 1);
|
| -
|
| - // On other platforms we write |text| to the clipboard regardless of
|
| - // |write_url|. We don't need to do that here because we fall through to
|
| - // the default signal handlers.
|
| - if (write_url) {
|
| - BookmarkNodeData data;
|
| - data.ReadFromTuple(url, text);
|
| - data.WriteToClipboard(ui::CLIPBOARD_TYPE_COPY_PASTE);
|
| - SetSelectedRange(selection);
|
| -
|
| - // 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(base::UTF16ToUTF8(text));
|
| -}
|
| -
|
| -int OmniboxViewGtk::GetOmniboxTextLength() const {
|
| - GtkTextIter end;
|
| - gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, gray_text_mark_);
|
| - if (supports_pre_edit_) {
|
| - // 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) + pre_edit_.size();
|
| - }
|
| - return gtk_text_iter_get_offset(&end);
|
| -}
|
| -
|
| -void OmniboxViewGtk::EmphasizeURLComponents() {
|
| - if (supports_pre_edit_) {
|
| - // We can't change the text style easily, if the pre-edit 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
|
| - // pre-edit string gets committed.
|
| - if (pre_edit_.size()) {
|
| - strikethrough_ = CharRange();
|
| - return;
|
| - }
|
| - }
|
| - // 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;
|
| - base::string16 text(GetText());
|
| - AutocompleteInput::ParseForEmphasizeComponents(text, &scheme, &host);
|
| -
|
| - // Set the baseline emphasis.
|
| - GtkTextIter start, end;
|
| - GetTextBufferBounds(&start, &end);
|
| - gtk_text_buffer_remove_all_tags(text_buffer_, &start, &end);
|
| - bool grey_out_url = text.substr(scheme.begin, scheme.len) ==
|
| - base::UTF8ToUTF16(extensions::kExtensionScheme);
|
| - bool grey_base = model()->CurrentTextIsURL() &&
|
| - (host.is_nonempty() || grey_out_url);
|
| - gtk_text_buffer_apply_tag(
|
| - text_buffer_, grey_base ? faded_text_tag_ : normal_text_tag_ , &start,
|
| - &end);
|
| -
|
| - if (grey_base && !grey_out_url) {
|
| - // 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);
|
| - }
|
| -
|
| - strikethrough_ = CharRange();
|
| - // Emphasize the scheme for security UI display purposes (if necessary).
|
| - if (!model()->user_input_in_progress() && model()->CurrentTextIsURL() &&
|
| - 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);
|
| - }
|
| - }
|
| -}
|
| -
|
| -bool OmniboxViewGtk::OnPerformDropImpl(const base::string16& text) {
|
| - base::string16 sanitized_string(StripJavascriptSchemas(
|
| - base::CollapseWhitespace(text, true)));
|
| - if (model()->CanPasteAndGo(sanitized_string)) {
|
| - model()->PasteAndGo(sanitized_string);
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -void OmniboxViewGtk::OnBrowserThemeChanged() {
|
| - DCHECK(text_view_);
|
| -
|
| - bool use_gtk = theme_service_->UsingNativeTheme();
|
| - 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(gray_text_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 =
|
| - &LocationBarViewGtk::kBackgroundColor;
|
| - gtk_widget_modify_cursor(text_view_, &ui::kGdkBlack, &ui::kGdkGray);
|
| - gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, background_color_ptr);
|
| -
|
| - 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);
|
| -
|
| - // Until we switch to vector graphics, force the font size.
|
| - const gfx::Font& font = GetFont();
|
| - gtk_util::ForceFontSizePixels(text_view_, font.GetFontSize());
|
| - gtk_util::ForceFontSizePixels(gray_text_view_, font.GetFontSize());
|
| -
|
| - g_object_set(faded_text_tag_, "foreground", kTextBaseColor, NULL);
|
| - g_object_set(normal_text_tag_, "foreground", "#000000", NULL);
|
| - }
|
| -
|
| - const gfx::Font& font = GetFont();
|
| - const int cap_height = font.GetCapHeight();
|
| - const int internal_leading = font.GetBaseline() - cap_height;
|
| - font_baseline_shift_ =
|
| - (font.GetHeight() - cap_height) / 2.0 - internal_leading;
|
| -
|
| - AdjustVerticalAlignmentOfGrayTextView();
|
| - UpdateGrayTextViewColors();
|
| -}
|
| -
|
| -gfx::Font OmniboxViewGtk::GetFont() {
|
| - if (!theme_service_->UsingNativeTheme()) {
|
| - return gfx::Font(
|
| - ui::ResourceBundle::GetSharedInstance().GetFont(
|
| - ui::ResourceBundle::BaseFont).GetFontName(),
|
| - browser_defaults::kOmniboxFontPixelSize);
|
| - }
|
| -
|
| - // 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();
|
| - GtkStyle* gtk_style = gtk_widget_get_style(widget);
|
| - GtkRcStyle* rc_style = gtk_widget_get_modifier_style(widget);
|
| - gfx::Font font(
|
| - (rc_style && rc_style->font_desc) ?
|
| - rc_style->font_desc : gtk_style->font_desc);
|
| - if (!text_view_)
|
| - g_object_unref(g_object_ref_sink(widget));
|
| - return font;
|
| -}
|
| -
|
| -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* event) {
|
| - // Adjust vertical alignment of |sender| before we start rendering it.
|
| - // GtkTextView is a multi-line text edit control and scrollable. Thus we
|
| - // have to adjust the amount of scroll in order to put the content text at
|
| - // the center of Omnibox.
|
| -
|
| - GtkTextView* text_view = GTK_TEXT_VIEW(sender);
|
| - GtkTextIter iter;
|
| - gtk_text_view_get_iter_at_location(text_view, &iter, 0, 0);
|
| - gint line_height = 0;
|
| - gtk_text_view_get_line_yrange(text_view, &iter, NULL, &line_height);
|
| -
|
| - GtkAllocation allocation;
|
| - gtk_widget_get_allocation(alignment_.get(), &allocation);
|
| -
|
| - const double shift =
|
| - (line_height - allocation.height) / 2.0 + font_baseline_shift_;
|
| -
|
| - // If the desired shift is positive (upwards), we can scroll the control to
|
| - // accomplish it; but if the desired shift is downwards, we need to add extra
|
| - // padding atop the control.
|
| - const gdouble new_scroll = std::max(shift, 0.);
|
| - const guint new_top_padding = std::max(0., -shift);
|
| -
|
| - // Changing the amount of scroll may fire another "expose-event", so avoid
|
| - // unnecessary change.
|
| - GtkAdjustment* adjustment = gtk_text_view_get_vadjustment(text_view);
|
| - if (new_scroll != gtk_adjustment_get_value(adjustment))
|
| - gtk_adjustment_set_value(adjustment, new_scroll);
|
| - guint top_padding = 0;
|
| - guint bottom_padding = 0;
|
| - guint left_padding = 0;
|
| - guint right_padding = 0;
|
| - gtk_alignment_get_padding(GTK_ALIGNMENT(alignment_.get()), &top_padding,
|
| - &bottom_padding, &left_padding, &right_padding);
|
| - if (new_top_padding != top_padding)
|
| - gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_.get()), new_top_padding,
|
| - bottom_padding, left_padding, right_padding);
|
| -
|
| - return FALSE;
|
| -}
|
| -
|
| -gboolean OmniboxViewGtk::HandleExposeEventAfter(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_get_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_get_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 (supports_pre_edit_) {
|
| - // Nothing should be selected when we are in the middle of composition.
|
| - DCHECK(pre_edit_.empty() || start_offset == end_offset);
|
| - if (!pre_edit_.empty()) {
|
| - start_offset += pre_edit_.size();
|
| - end_offset += pre_edit_.size();
|
| - }
|
| - }
|
| -
|
| - 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);
|
| -}
|
| -
|
| -bool OmniboxViewGtk::IsCaretAtEnd() const {
|
| - const CharRange selection = GetSelection();
|
| - return selection.cp_min == selection.cp_max &&
|
| - selection.cp_min == GetOmniboxTextLength();
|
| -}
|
| -
|
| -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 base::string16& text,
|
| - const CharRange& range) {
|
| - if (text != GetText()) {
|
| - std::string utf8 = base::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 gray text anchor. We can't simply set
|
| - // the gray text 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 == gray_text_mark_ || !gray_text_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 gray text 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, gray_text_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* omnibox_view =
|
| - reinterpret_cast<OmniboxViewGtk*>(
|
| - g_object_get_data(G_OBJECT(object), kOmniboxViewGtkKey));
|
| - omnibox_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() {
|
| - base::string16 text = base::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_ = base::UTF16ToUTF8(text);
|
| - OwnPrimarySelection(selected_text_);
|
| - }
|
| -}
|
| -
|
| -void OmniboxViewGtk::HandlePreEditChanged(GtkWidget* sender,
|
| - const gchar* pre_edit) {
|
| - // GtkTextView won't fire "begin-user-action" and "end-user-action" signals
|
| - // when changing the pre-edit string, so we need to call
|
| - // OnBeforePossibleChange() and OnAfterPossibleChange() by ourselves.
|
| - OnBeforePossibleChange();
|
| - if (pre_edit && *pre_edit) {
|
| - // GtkTextView will only delete the selection range when committing the
|
| - // pre-edit string, which will cause very strange behavior, so we need to
|
| - // delete the selection range here explicitly. See http://crbug.com/18808.
|
| - if (pre_edit_.empty())
|
| - gtk_text_buffer_delete_selection(text_buffer_, false, true);
|
| - pre_edit_ = base::UTF8ToUTF16(pre_edit);
|
| - } else {
|
| - pre_edit_.clear();
|
| - }
|
| - OnAfterPossibleChange();
|
| -}
|
| -
|
| -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, gray_text_mark_);
|
| -}
|
| -
|
| -void OmniboxViewGtk::ValidateTextBufferIter(GtkTextIter* iter) const {
|
| - if (!gray_text_mark_)
|
| - return;
|
| -
|
| - GtkTextIter end;
|
| - gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, gray_text_mark_);
|
| - if (gtk_text_iter_compare(iter, &end) > 0)
|
| - *iter = end;
|
| -}
|
| -
|
| -void OmniboxViewGtk::AdjustVerticalAlignmentOfGrayTextView() {
|
| - // By default, GtkTextView layouts an anchored child widget just above the
|
| - // baseline, so we need to move the |gray_text_view_| down to make sure it
|
| - // has the same baseline as the |text_view_|.
|
| - PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(gray_text_view_));
|
| - int height;
|
| - pango_layout_get_size(layout, NULL, &height);
|
| - int baseline = pango_layout_get_baseline(layout);
|
| - g_object_set(gray_text_anchor_tag_, "rise", baseline - height, NULL);
|
| -}
|
|
|