summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autocomplete
diff options
context:
space:
mode:
authorevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-01 21:56:25 +0000
committerevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-01 21:56:25 +0000
commit6ae0004ffeee18919063265714bac7608b9d860a (patch)
tree01ec067d8b44863ec1eff7229020b951f307b96f /chrome/browser/autocomplete
parentda195f278bc478068efa89613a96efba7df61588 (diff)
downloadchromium_src-6ae0004ffeee18919063265714bac7608b9d860a.zip
chromium_src-6ae0004ffeee18919063265714bac7608b9d860a.tar.gz
chromium_src-6ae0004ffeee18919063265714bac7608b9d860a.tar.bz2
Make Linux restore Omnibox contents on tab switch.
Tested as follows: Create a new window with two tabs. Type a bunch of 'a' characters into tab A's omnibox and a bunch of 'b's into tab B. Then, 1. Select tab A. Left-click in the omnibox and highlight a few characters in the middle. Left-click in the empty space to the right of the text to unhighlight it, then middle-click to make sure that the previously-highlighted text still gets pasted. 2. Repeat 1, but hit a key to remove the selection instead of clicking the mouse. The old highlighted text should still be available as the primary selection. 3. Highlight A's text as in 1. Select tab B and see that the omnibox is updated to B's string and highlighting is removed. Middle-click and confirm that the previously-highlighted text from A is pasted. 4. Select A and left-click in the middle of its string to position the cursor there. Click on tab B and then back on A to check that A's cursor position is restored. 5. Highlight text in A. Select tab B and then highlight text from a different window. Switch back to A and middle-click. The text from the different window, rather than A's previously-highlighted text, should be pasted. BUG=9225 Review URL: http://codereview.chromium.org/151006 Patch from Dan Erat <derat@google.com>. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19770 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/autocomplete')
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc140
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view_gtk.h54
2 files changed, 165 insertions, 29 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc
index 06647df..1ee5a15 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());
+}
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.h b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.h
index 70e32f3..97708d0 100644
--- a/chrome/browser/autocomplete/autocomplete_edit_view_gtk.h
+++ b/chrome/browser/autocomplete/autocomplete_edit_view_gtk.h
@@ -26,6 +26,18 @@ class TabContents;
class AutocompleteEditViewGtk : public AutocompleteEditView {
public:
+ // Modeled like the Windows CHARRANGE. Represent a pair of cursor position
+ // offsets. Since GtkTextIters are invalid after the buffer is changed, we
+ // work in character offsets (not bytes).
+ struct CharRange {
+ CharRange() : cp_min(0), cp_max(0) { }
+ CharRange(int n, int x) : cp_min(n), cp_max(x) { }
+
+ // Work in integers to match the gint GTK APIs.
+ int cp_min; // For a selection: Represents the start.
+ int cp_max; // For a selection: Represents the end (insert position).
+ };
+
AutocompleteEditViewGtk(AutocompleteEditController* controller,
ToolbarModel* toolbar_model,
Profile* profile,
@@ -86,18 +98,6 @@ class AutocompleteEditViewGtk : public AutocompleteEditView {
virtual bool OnAfterPossibleChange();
private:
- // Modeled like the Windows CHARRANGE. Represent a pair of cursor position
- // offsets. Since GtkTextIters are invalid after the buffer is changed, we
- // work in character offsets (not bytes).
- struct CharRange {
- CharRange() : cp_min(0), cp_max(0) { }
- CharRange(int n, int x) : cp_min(n), cp_max(x) { }
-
- // Work in integers to match the gint GTK APIs.
- int cp_min; // For a selection: Represents the start.
- int cp_max; // For a selection: Represents the end (insert position).
- };
-
// TODO(deanm): Would be nice to insulate the thunkers better, etc.
static void HandleBeginUserActionThunk(GtkTextBuffer* unused, gpointer self) {
reinterpret_cast<AutocompleteEditViewGtk*>(self)->HandleBeginUserAction();
@@ -201,6 +201,17 @@ class AutocompleteEditViewGtk : public AutocompleteEditView {
}
void HandlePasteAndGoReceivedText(const std::wstring& text);
+ static void HandleMarkSetThunk(GtkTextBuffer* buffer,
+ GtkTextIter* location,
+ GtkTextMark* mark,
+ gpointer self) {
+ reinterpret_cast<AutocompleteEditViewGtk*>(self)->
+ HandleMarkSet(buffer, location, mark);
+ }
+ void HandleMarkSet(GtkTextBuffer* buffer,
+ GtkTextIter* location,
+ GtkTextMark* mark);
+
// Get the character indices of the current selection. This honors
// direction, cp_max is the insertion point, and cp_min is the bound.
CharRange GetSelection();
@@ -219,6 +230,9 @@ class AutocompleteEditViewGtk : public AutocompleteEditView {
// Internally invoked whenever the text changes in some way.
void TextChanged();
+ // Save 'selected_text' as the PRIMARY X selection.
+ void SavePrimarySelection(const std::string& selected_text);
+
// The widget we expose, used for vertically centering the real text edit,
// since the height will change based on the font / font size, etc.
OwnedWidgetGtk alignment_;
@@ -233,11 +247,6 @@ class AutocompleteEditViewGtk : public AutocompleteEditView {
GtkTextTag* insecure_scheme_tag_;
GtkTextTag* black_text_tag_;
- // The primary selection clipboard for our text view widget. This is used
- // for working around some clipboard manager (klipper / glipper) bugs by
- // removing and adding back the clipboard around inline autocomplete.
- GtkClipboard* primary_clipboard_;
-
scoped_ptr<AutocompleteEditModel> model_;
scoped_ptr<AutocompletePopupViewGtk> popup_view_;
AutocompleteEditController* controller_;
@@ -258,6 +267,17 @@ class AutocompleteEditViewGtk : public AutocompleteEditView {
std::wstring text_before_change_;
CharRange sel_before_change_;
+ // The most-recently-selected text from the entry. This is updated on-the-fly
+ // as the user selects text. It is used in cases where we need to make the
+ // PRIMARY selection persist even after the user has unhighlighted the text in
+ // the view (e.g. when they highlight some text and then click to unhighlight
+ // it, we pass this string to SavePrimarySelection()).
+ std::string selected_text_;
+
+ // Has the current value of 'selected_text_' been saved as the PRIMARY
+ // selection?
+ bool selection_saved_;
+
DISALLOW_COPY_AND_ASSIGN(AutocompleteEditViewGtk);
};