| Index: chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc
|
| diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc
|
| index 06647df7e16595160f0dbfaa596e889341275149..1ee5a15327400d4307d27690136da4b433518f47 100644
|
| --- a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc
|
| +++ b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc
|
| @@ -34,6 +34,34 @@ size_t GetUTF8Offset(const std::wstring& wide_text, size_t wide_text_offset) {
|
| return WideToUTF8(wide_text.substr(0, wide_text_offset)).size();
|
| }
|
|
|
| +// Stores GTK+-specific state so it can be restored after switching tabs.
|
| +struct ViewState {
|
| + explicit ViewState(const AutocompleteEditViewGtk::CharRange& selection_range)
|
| + : selection_range(selection_range) {
|
| + }
|
| +
|
| + // Range of selected text.
|
| + AutocompleteEditViewGtk::CharRange selection_range;
|
| +};
|
| +
|
| +struct AutocompleteEditState {
|
| + AutocompleteEditState(const AutocompleteEditModel::State& model_state,
|
| + const ViewState& view_state)
|
| + : model_state(model_state),
|
| + view_state(view_state) {
|
| + }
|
| +
|
| + const AutocompleteEditModel::State model_state;
|
| + const ViewState view_state;
|
| +};
|
| +
|
| +// Returns a lazily initialized property bag accessor for saving our state in a
|
| +// TabContents.
|
| +PropertyAccessor<AutocompleteEditState>* GetStateAccessor() {
|
| + static PropertyAccessor<AutocompleteEditState> state;
|
| + return &state;
|
| +}
|
| +
|
| } // namespace
|
|
|
| AutocompleteEditViewGtk::AutocompleteEditViewGtk(
|
| @@ -48,7 +76,6 @@ AutocompleteEditViewGtk::AutocompleteEditViewGtk(
|
| base_tag_(NULL),
|
| secure_scheme_tag_(NULL),
|
| insecure_scheme_tag_(NULL),
|
| - primary_clipboard_(NULL),
|
| model_(new AutocompleteEditModel(this, controller, profile)),
|
| popup_view_(new AutocompletePopupViewGtk(this, model_.get(), profile,
|
| popup_positioner)),
|
| @@ -56,7 +83,8 @@ AutocompleteEditViewGtk::AutocompleteEditViewGtk(
|
| toolbar_model_(toolbar_model),
|
| command_updater_(command_updater),
|
| popup_window_mode_(false), // TODO(deanm)
|
| - scheme_security_level_(ToolbarModel::NORMAL) {
|
| + scheme_security_level_(ToolbarModel::NORMAL),
|
| + selection_saved_(false) {
|
| model_->set_popup_model(popup_view_->GetModel());
|
| }
|
|
|
| @@ -145,6 +173,8 @@ void AutocompleteEditViewGtk::Init() {
|
| G_CALLBACK(&HandleViewSizeRequestThunk), this);
|
| g_signal_connect(text_view_, "populate-popup",
|
| G_CALLBACK(&HandlePopulatePopupThunk), this);
|
| + g_signal_connect(text_buffer_, "mark-set",
|
| + G_CALLBACK(&HandleMarkSetThunk), this);
|
| }
|
|
|
| void AutocompleteEditViewGtk::SetFocus() {
|
| @@ -152,7 +182,18 @@ void AutocompleteEditViewGtk::SetFocus() {
|
| }
|
|
|
| void AutocompleteEditViewGtk::SaveStateToTab(TabContents* tab) {
|
| - NOTIMPLEMENTED();
|
| + DCHECK(tab);
|
| + GetStateAccessor()->SetProperty(
|
| + tab->property_bag(),
|
| + AutocompleteEditState(model_->GetStateForTabSwitch(),
|
| + ViewState(GetSelection())));
|
| +
|
| + // 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() && !selection_saved_) {
|
| + SavePrimarySelection(selected_text_);
|
| + selection_saved_ = true;
|
| + }
|
| }
|
|
|
| void AutocompleteEditViewGtk::Update(const TabContents* contents) {
|
| @@ -173,9 +214,27 @@ void AutocompleteEditViewGtk::Update(const TabContents* contents) {
|
| }
|
|
|
| if (contents) {
|
| + selected_text_.clear();
|
| + selection_saved_ = false;
|
| RevertAll();
|
| - // TODO(deanm): Tab switching. The Windows code puts some state in a
|
| - // PropertyBag on the tab contents, and restores state from there.
|
| + const AutocompleteEditState* state =
|
| + GetStateAccessor()->GetProperty(contents->property_bag());
|
| + if (state) {
|
| + model_->RestoreState(state->model_state);
|
| +
|
| + // Move the marks for the cursor and the other end of the selection to
|
| + // the previously-saved offsets.
|
| + GtkTextIter selection_iter, insert_iter;
|
| + ItersFromCharRange(
|
| + state->view_state.selection_range, &selection_iter, &insert_iter);
|
| + // TODO(derat): Restore the selection range instead of just the cursor
|
| + // ("insert") position. This in itself is trivial to do using
|
| + // gtk_text_buffer_select_range(), but then it also becomes necessary to
|
| + // invalidate hidden tabs' saved ranges when another tab or another app
|
| + // takes the selection, lest we incorrectly regrab a stale selection when
|
| + // a hidden tab is later shown.
|
| + gtk_text_buffer_place_cursor(text_buffer_, &insert_iter);
|
| + }
|
| } else if (visibly_changed_permanent_text) {
|
| RevertAll();
|
| // TODO(deanm): There should be code to restore select all here.
|
| @@ -295,17 +354,18 @@ bool AutocompleteEditViewGtk::OnInlineAutocompleteTextMaybeChanged(
|
| size_t user_text_length) {
|
| if (display_text == GetText())
|
| return false;
|
| -
|
| +
|
| // We need to get the clipboard while it's attached to the toplevel. The
|
| // easiest thing to do is just to lazily pull the clipboard here.
|
| - if (primary_clipboard_ == NULL) {
|
| - primary_clipboard_ = gtk_widget_get_clipboard(text_view_,
|
| - GDK_SELECTION_PRIMARY);
|
| - }
|
| + GtkClipboard* clipboard =
|
| + gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY);
|
| + DCHECK(clipboard);
|
| + if (!clipboard)
|
| + return true;
|
|
|
| // Remove the PRIMARY clipboard to avoid having "clipboard helpers" like
|
| // klipper and glipper race with / remove our inline autocomplete selection.
|
| - gtk_text_buffer_remove_selection_clipboard(text_buffer_, primary_clipboard_);
|
| + gtk_text_buffer_remove_selection_clipboard(text_buffer_, clipboard);
|
| SetWindowTextAndCaretPos(display_text, 0);
|
|
|
| // Select the part of the text that was inline autocompleted.
|
| @@ -316,7 +376,7 @@ bool AutocompleteEditViewGtk::OnInlineAutocompleteTextMaybeChanged(
|
|
|
| TextChanged();
|
| // Put the PRIMARY clipboard back, so that selection still somewhat works.
|
| - gtk_text_buffer_add_selection_clipboard(text_buffer_, primary_clipboard_);
|
| + gtk_text_buffer_add_selection_clipboard(text_buffer_, clipboard);
|
|
|
| return true;
|
| }
|
| @@ -449,6 +509,12 @@ gboolean AutocompleteEditViewGtk::HandleViewButtonPress(GdkEventButton* event) {
|
| if (GTK_WIDGET_HAS_FOCUS(text_view_))
|
| return FALSE; // Continue to propagate into the GtkTextView handler.
|
|
|
| + // We only want to select everything on left-click; otherwise we'll end up
|
| + // stealing the PRIMARY selection when the user middle-clicks to paste it
|
| + // here.
|
| + if (event->button != 1)
|
| + return FALSE;
|
| +
|
| // 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_);
|
| @@ -545,6 +611,44 @@ void AutocompleteEditViewGtk::HandlePasteAndGoReceivedText(
|
| model_->PasteAndGo();
|
| }
|
|
|
| +void AutocompleteEditViewGtk::HandleMarkSet(GtkTextBuffer* buffer,
|
| + GtkTextIter* location,
|
| + GtkTextMark* mark) {
|
| + if (!text_buffer_ || buffer != text_buffer_)
|
| + return;
|
| +
|
| + if (mark != gtk_text_buffer_get_insert(text_buffer_) &&
|
| + mark != gtk_text_buffer_get_selection_bound(text_buffer_)) {
|
| + return;
|
| + }
|
| +
|
| + // Is no text selected in the GtkTextView?
|
| + bool no_text_selected = false;
|
| +
|
| + // Get the currently-selected text, if there is any.
|
| + GtkTextIter start, end;
|
| + if (!gtk_text_buffer_get_selection_bounds(text_buffer_, &start, &end)) {
|
| + no_text_selected = true;
|
| + } else {
|
| + gchar* text = gtk_text_iter_get_text(&start, &end);
|
| + size_t text_len = strlen(text);
|
| + if (!text_len) {
|
| + no_text_selected = true;
|
| + } else {
|
| + selected_text_ = std::string(text, text_len);
|
| + selection_saved_ = false;
|
| + }
|
| + g_free(text);
|
| + }
|
| +
|
| + // If we have some previously-selected text but it's no longer highlighted
|
| + // and we haven't saved it as the selection yet, we save it now.
|
| + if (!selected_text_.empty() && no_text_selected && !selection_saved_) {
|
| + SavePrimarySelection(selected_text_);
|
| + selection_saved_ = true;
|
| + }
|
| +}
|
| +
|
| AutocompleteEditViewGtk::CharRange AutocompleteEditViewGtk::GetSelection() {
|
| // 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
|
| @@ -635,3 +739,15 @@ void AutocompleteEditViewGtk::TextChanged() {
|
| EmphasizeURLComponents();
|
| controller_->OnChanged();
|
| }
|
| +
|
| +void AutocompleteEditViewGtk::SavePrimarySelection(
|
| + const std::string& selected_text) {
|
| + 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());
|
| +}
|
|
|