This source file includes following definitions.
- GetUTF8Offset
- SetEntryStyle
- ClipboardSelectionCleared
- GetPopupMenuIndexForStockLabel
- font_baseline_shift_
- Init
- HandleHierarchyChanged
- SetFocus
- ApplyCaretVisibility
- SaveStateToTab
- OnTabChanged
- Update
- GetText
- SetWindowTextAndCaretPos
- SetForcedQuery
- IsSelectAll
- DeleteAtEndPressed
- GetSelectionBounds
- SelectAll
- UpdatePopup
- OnTemporaryTextMaybeChanged
- OnInlineAutocompleteTextMaybeChanged
- OnInlineAutocompleteTextCleared
- OnRevertTemporaryText
- OnBeforePossibleChange
- OnAfterPossibleChange
- GetNativeView
- GetRelativeWindowForPopup
- SetGrayTextAutocompletion
- GetGrayTextAutocompletion
- GetTextWidth
- GetWidth
- IsImeComposing
- Observe
- UpdateGrayTextViewColors
- HandleBeginUserAction
- HandleEndUserAction
- HandleKeyPress
- HandleKeyRelease
- HandleViewButtonPress
- HandleViewButtonRelease
- HandleViewFocusIn
- HandleViewFocusOut
- HandleViewMoveCursor
- HandleViewSizeRequest
- HandlePopupMenuDeactivate
- HandlePopulatePopup
- HandlePasteAndGo
- HandleEditSearchEngines
- HandleShowURL
- HandleMarkSet
- HandleMarkSetAfter
- HandleDragDataReceived
- HandleDragDataGet
- HandleDragBegin
- HandleDragEnd
- HandleInsertText
- HandleBackSpace
- HandleViewMoveFocus
- HandleCopyClipboard
- HandleCutClipboard
- HandleCopyOrCutClipboard
- GetOmniboxTextLength
- EmphasizeURLComponents
- OnPerformDropImpl
- OnBrowserThemeChanged
- GetFont
- OwnPrimarySelection
- HandlePasteClipboard
- WindowBoundsFromIters
- HandleExposeEvent
- HandleExposeEventAfter
- SelectAllInternal
- StartUpdatingHighlightedText
- FinishUpdatingHighlightedText
- GetSelection
- ItersFromCharRange
- IsCaretAtEnd
- SavePrimarySelection
- SetTextAndSelectedRange
- SetSelectedRange
- AdjustTextJustification
- GetContentDirection
- HandleWidgetDirectionChanged
- HandleDeleteFromCursor
- HandleKeymapDirectionChanged
- HandleDeleteRange
- HandleMarkSetAlways
- ClipboardGetSelectionThunk
- ClipboardGetSelection
- GetSelectedText
- UpdatePrimarySelectionIfValidURL
- HandlePreEditChanged
- HandleWindowSetFocus
- HandleUndoRedo
- HandleUndoRedoAfter
- GetTextBufferBounds
- ValidateTextBufferIter
- AdjustVerticalAlignmentOfGrayTextView
#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();
}
struct ViewState {
explicit ViewState(const OmniboxViewGtk::CharRange& selection_range)
: selection_range(selection_range) {
}
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;
};
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\"");
}
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);
}
}
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;
}
}
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() {
popup_view_.reset();
if (alignment_.get()) {
alignment_.Destroy();
g_object_unref(text_buffer_);
g_object_unref(tag_table_);
}
}
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");
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);
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);
gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view_), 1);
gtk_widget_set_name(text_view_, "chrome-location-bar-entry");
gtk_container_add(GTK_CONTAINER(alignment_.get()), text_view_);
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);
g_signal_connect(text_buffer_, "begin-user-action",
G_CALLBACK(&HandleBeginUserActionThunk), this);
g_signal_connect(text_buffer_, "end-user-action",
G_CALLBACK(&HandleEndUserActionThunk), this);
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);
g_signal_connect(text_view_, "move-cursor",
G_CALLBACK(&HandleViewMoveCursorThunk), this);
g_signal_connect(text_view_, "move-focus",
G_CALLBACK(&HandleViewMoveFocusThunk), this);
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);
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_);
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);
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);
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;
signals_.Connect(new_toplevel, "set-focus",
G_CALLBACK(&HandleWindowSetFocusThunk), this);
}
void OmniboxViewGtk::SetFocus() {
DCHECK(text_view_);
gtk_widget_grab_focus(text_view_);
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 (!selected_text_.empty())
SavePrimarySelection(selected_text_);
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) {
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()) {
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_) {
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);
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) {
SelectAllInternal(reversed, false);
}
void OmniboxViewGtk::UpdatePopup() {
model()->SetInputInProgress(true);
if (!update_popup_without_focus_ && !model()->has_focus())
return;
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();
}
void OmniboxViewGtk::OnBeforePossibleChange() {
if (paste_clipboard_requested_) {
paste_clipboard_requested_ = false;
model()->OnPaste();
}
if (handling_key_press_)
return;
text_before_change_ = GetText();
sel_before_change_ = GetSelection();
if (supports_pre_edit_)
pre_edit_size_before_change_ = pre_edit_.size();
}
bool OmniboxViewGtk::OnAfterPossibleChange() {
if (handling_key_press_) {
content_maybe_changed_by_key_press_ = true;
return false;
}
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);
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();
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 (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 {
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);
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;
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) {
GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget);
enter_was_pressed_ = event->keyval == GDK_Return ||
event->keyval == GDK_ISO_Enter ||
event->keyval == GDK_KP_Enter;
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;
enter_was_inserted_ = false;
paste_clipboard_requested_ = false;
text_changed_ = false;
OnBeforePossibleChange();
handling_key_press_ = true;
content_maybe_changed_by_key_press_ = false;
gboolean result = klass->key_press_event(widget, event);
handling_key_press_ = false;
if (content_maybe_changed_by_key_press_)
OnAfterPossibleChange();
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) {
result = model()->OnEscapeKeyPressed();
} else if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) {
model()->OnControlKeyChanged(true);
} else if (!text_changed_ && event->keyval == GDK_Delete &&
event->state & GDK_SHIFT_MASK) {
if (model()->popup_model()->IsOpen())
model()->popup_model()->TryDeletingCurrentItem();
}
enter_was_pressed_ = false;
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) {
if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) {
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);
}
return FALSE;
}
gboolean OmniboxViewGtk::HandleViewButtonPress(GtkWidget* sender,
GdkEventButton* event) {
if (event->type != GDK_BUTTON_PRESS)
return FALSE;
DCHECK(text_view_);
if (event->button == 1 || event->button == 2)
model()->SetCaretVisibility(true);
if (event->button == 1) {
button_1_pressed_ = true;
OnBeforePossibleChange();
} else if (event->button == 2) {
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_);
GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(text_view_);
klass->button_release_event(text_view_, event);
if (button_1_was_pressed)
OnAfterPossibleChange();
return TRUE;
}
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();
g_signal_connect(
gdk_keymap_get_for_display(gtk_widget_get_display(text_view_)),
"direction-changed",
G_CALLBACK(&HandleKeymapDirectionChangedThunk), this);
AdjustTextJustification();
return FALSE;
}
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_;
model()->OnWillKillFocus(view_getting_focus);
CloseOmniboxPopup();
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;
}
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)) {
PangoDirection content_dir = GetContentDirection();
gint count_towards_end = content_dir == PANGO_DIRECTION_RTL ? -1 : 1;
if (has_selection) {
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) {
model()->OnUpOrDownKeyPressed(model()->result().size() * count);
handled = true;
} else if (step == GTK_MOVEMENT_DISPLAY_LINES) {
model()->OnUpOrDownKeyPressed(count);
handled = true;
}
if (!handled) {
if (has_selection || extend_selection)
OnBeforePossibleChange();
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();
}
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) {
req->width = 1;
}
void OmniboxViewGtk::HandlePopupMenuDeactivate(GtkWidget* sender) {
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);
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());
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);
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);
}
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;
}
selection_suggested_ = false;
std::string new_selected_text = GetSelectedText();
if (!selected_text_.empty() && new_selected_text.empty()) {
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;
}
void OmniboxViewGtk::HandleMarkSetAfter(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;
}
UpdatePrimarySelectionIfValidURL();
}
void OmniboxViewGtk::HandleDragDataReceived(GtkWidget* sender,
GdkDragContext* context,
gint x,
gint y,
GtkSelectionData* selection_data,
guint target_type,
guint time) {
DCHECK(text_view_);
paste_clipboard_requested_ = false;
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();
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;
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);
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);
if (c != 0x200B)
base::WriteUnicodeCharacter(c, &filtered_text);
}
if (model()->is_pasting()) {
filtered_text = base::CollapseWhitespace(filtered_text, true);
filtered_text = filtered_text.empty() ? base::ASCIIToUTF16(" ") :
StripJavascriptSchemas(filtered_text);
}
if (!filtered_text.empty()) {
ValidateTextBufferIter(location);
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()));
}
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) {
if (model()->is_keyword_hint() || model()->keyword().empty())
return;
DCHECK(text_view_);
GtkTextIter sel_start, sel_end;
if (gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end))
return;
GtkTextIter start;
gtk_text_buffer_get_start_iter(text_buffer_, &start);
if (!gtk_text_iter_equal(&start, &sel_start))
return;
model()->ClearKeyword(GetText());
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;
bool handled = false;
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_);
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);
if (write_url) {
BookmarkNodeData data;
data.ReadFromTuple(url, text);
data.WriteToClipboard(ui::CLIPBOARD_TYPE_COPY_PASTE);
SetSelectedRange(selection);
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_) {
return gtk_text_iter_get_offset(&end) + pre_edit_.size();
}
return gtk_text_iter_get_offset(&end);
}
void OmniboxViewGtk::EmphasizeURLComponents() {
if (supports_pre_edit_) {
if (pre_edit_.size()) {
strikethrough_ = CharRange();
return;
}
}
url_parse::Component scheme, host;
base::string16 text(GetText());
AutocompleteInput::ParseForEmphasizeComponents(text, &scheme, &host);
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) {
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();
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;
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_);
GtkStyle* style = gtk_rc_get_style(text_view_);
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;
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);
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);
}
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);
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) {
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) {
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_;
const gdouble new_scroll = std::max(shift, 0.);
const guint new_top_padding = std::max(0., -shift);
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;
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);
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 {
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_) {
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);
selection_suggested_ = true;
}
void OmniboxViewGtk::AdjustTextJustification() {
DCHECK(text_view_);
PangoDirection content_dir = GetContentDirection();
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 (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) {
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);
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;
}
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);
}
}
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;
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) {
OnBeforePossibleChange();
if (pre_edit && *pre_edit) {
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) {
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() {
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);
}