Chromium Code Reviews| Index: chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc |
| =================================================================== |
| --- chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc (revision 23948) |
| +++ chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc (working copy) |
| @@ -100,12 +100,11 @@ |
| mark_set_handler_id_(0), |
| button_1_pressed_(false), |
| text_selected_during_click_(false), |
| - text_view_focused_before_button_press_(false) |
| + text_view_focused_before_button_press_(false), |
| #if !defined(TOOLKIT_VIEWS) |
| - , |
| - theme_provider_(GtkThemeProvider::GetFrom(profile)) |
| + theme_provider_(GtkThemeProvider::GetFrom(profile)), |
| #endif |
| - { |
| + tab_was_pressed_(false) { |
| model_->set_popup_model(popup_view_->GetModel()); |
| } |
| @@ -153,13 +152,11 @@ |
| // The text view was floating. It will now be owned by the alignment. |
| gtk_container_add(GTK_CONTAINER(alignment_.get()), text_view_); |
| - // Allows inserting tab characters when pressing tab key, to prevent |
| - // |text_view_| from moving focus. |
| - // Tab characters will be filtered out by our "insert-text" signal handler |
| - // attached to |text_buffer_| object. |
| - // Tab key events will be handled by our "key-press-event" signal handler for |
| - // tab to search feature. |
| - gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(text_view_), TRUE); |
| + // 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); |
| @@ -197,6 +194,8 @@ |
| // 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. |
| @@ -331,13 +330,18 @@ |
| void AutocompleteEditViewGtk::SetWindowTextAndCaretPos(const std::wstring& text, |
| size_t caret_pos) { |
| + CharRange range(static_cast<int>(caret_pos), static_cast<int>(caret_pos)); |
| + SetTextAndSelectedRange(text, range); |
| +} |
| + |
| +void AutocompleteEditViewGtk::SetTextAndSelectedRange(const std::wstring& text, |
| + const CharRange& range) { |
| std::string utf8 = WideToUTF8(text); |
| gtk_text_buffer_set_text(text_buffer_, utf8.data(), utf8.length()); |
| - EmphasizeURLComponents(); |
| - GtkTextIter cur_pos; |
| - gtk_text_buffer_get_iter_at_offset(text_buffer_, &cur_pos, caret_pos); |
| - gtk_text_buffer_place_cursor(text_buffer_, &cur_pos); |
| + GtkTextIter insert, bound; |
| + ItersFromCharRange(range, &insert, &bound); |
| + gtk_text_buffer_select_range(text_buffer_, &insert, &bound); |
| } |
| void AutocompleteEditViewGtk::SetForcedQuery() { |
| @@ -355,8 +359,15 @@ |
| } |
| bool AutocompleteEditViewGtk::IsSelectAll() { |
| - NOTIMPLEMENTED(); |
| - return false; |
| + GtkTextIter sel_start, sel_end; |
| + if (!gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end)) |
| + return false; |
| + |
| + GtkTextIter start, end; |
| + gtk_text_buffer_get_bounds(text_buffer_, &start, &end); |
| + |
| + return gtk_text_iter_equal(&start, &sel_start) && |
| + gtk_text_iter_equal(&end, &sel_end); |
| } |
| void AutocompleteEditViewGtk::SelectAll(bool reversed) { |
| @@ -392,8 +403,14 @@ |
| void AutocompleteEditViewGtk::OnTemporaryTextMaybeChanged( |
| const std::wstring& display_text, |
| bool save_original_selection) { |
| - // TODO(deanm): Ignoring save_original_selection here, etc. |
| + if (save_original_selection) { |
| + saved_temporary_selection_ = GetSelection(); |
| + saved_temporary_text_ = GetText(); |
| + } |
| + |
| + StartUpdatingHighlightedText(); |
| SetWindowTextAndCaretPos(display_text, display_text.length()); |
| + FinishUpdatingHighlightedText(); |
| TextChanged(); |
| } |
| @@ -418,7 +435,11 @@ |
| } |
| void AutocompleteEditViewGtk::OnRevertTemporaryText() { |
| - NOTIMPLEMENTED(); |
| + StartUpdatingHighlightedText(); |
| + SetTextAndSelectedRange(saved_temporary_text_, saved_temporary_selection_); |
| + FinishUpdatingHighlightedText(); |
| + saved_temporary_text_.clear(); |
| + TextChanged(); |
| } |
| void AutocompleteEditViewGtk::OnBeforePossibleChange() { |
| @@ -520,7 +541,7 @@ |
| gboolean AutocompleteEditViewGtk::HandleKeyPress(GtkWidget* widget, |
| GdkEventKey* event) { |
| // Background of this piece of complicated code: |
| - // The omnibox supports several special behavior which may be triggered by |
| + // 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 |
| @@ -532,114 +553,100 @@ |
| // 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 trigger |
| - // corresponding special behavior if IME did not handle it. |
| - // For Escape key if the default signal handler returns FALSE, then we know |
| + // 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. |
| // |
| - // But for Tab and Enter key, the default signal handler always returns TRUE, |
| - // and following operation will be performed by GtkTextView if IME did not |
| - // handle it: |
| - // Tab key - delete current selection range and insert '\t' |
| - // Enter key - delete current selection range and insert '\n' |
| + // For Tab key, as "accepts-tab" property of |text_view_| is set to FALSE, |
| + // if IME did not handle it then "move-focus" signal will be emitted by the |
| + // default signal handler of |text_view_|. So we can intercept "move-focus" |
| + // signal of |text_view_| to know if a Tab key press event was handled by IME, |
| + // and trigger Tab to search behavior when necessary in the signal handler. |
| // |
| - // We need dinstinguish if IME handled the key event or not, and avoid above |
| - // built-in operation 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 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 above built-in operation. So in order to achieve our |
| - // goal, "insert-text" signal of |text_buffer_| object is intercepted, and |
| + // 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, save it in |
| // char_inserted_. |
| // - Filter out all new line and tab characters. |
| // |
| - // So if char_inserted_ equals to '\t' after calling |text_view_|'s |
| - // default signal handler against a Tab key press event, then we can sure this |
| - // Tab key press event is handled by GtkTextView instead of IME. Then we can |
| - // perform the special behavior of Tab key safely. |
| + // So if |char_inserted_| equals '\n' 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. |
| // |
| - // Enter key press event can be treated in the same way. |
| - // |
| // Now the last thing is to prevent the content of omnibox from being changed |
| // by GtkTextView when Tab or Enter key is pressed. Because we can't prevent |
| - // it, we use backup and restore trick: If Tab or Enter is pressed, backup the |
| - // content of omnibox before sending the key event to |text_view_|, and then |
| - // restore it afterwards if IME did not handle the event. |
| + // it, we use a backup and restore trick: If Enter is pressed, backup the |
| + // content of omnibox before sending the key event to |text_view_|, and |
| + // then restore it afterwards if IME did not handle the event. |
| GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget); |
| - bool tab_pressed = ((event->keyval == GDK_Tab || |
| - event->keyval == GDK_ISO_Left_Tab || |
| - event->keyval == GDK_KP_Tab) && |
| - !(event->state & GDK_CONTROL_MASK)); |
| bool enter_pressed = (event->keyval == GDK_Return || |
| event->keyval == GDK_ISO_Enter || |
| event->keyval == GDK_KP_Enter); |
| - gchar* original_text = NULL; |
| + std::wstring original_text; |
| - // Tab and Enter key will have special behavior if it's not handled by IME. |
| + // Enter key will have special behavior if it's not handled by IME. |
| // We need save the original content of |text_buffer_| and restore it when |
| // necessary, because GtkTextView might alter the content. |
| - if (tab_pressed || enter_pressed) { |
| - GtkTextIter start, end; |
| - gtk_text_buffer_get_bounds(text_buffer_, &start, &end); |
| - original_text = gtk_text_buffer_get_text(text_buffer_, &start, &end, FALSE); |
| - |
| - // Reset these variable, which may be set in "insert-text" signal handler. |
| - // So that we can know if Tab or Enter key event is handled by IME or not. |
| + if (enter_pressed) { |
| + original_text = GetText(); |
| + // Reset |char_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. |
| char_inserted_ = 0; |
| } |
| + // 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)); |
| + |
| // Call the default handler, so that IME can work as normal. |
| - // New line and tab characters will be filtered out by our "insert-text" |
| + // 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); |
| - bool tab_inserted = (char_inserted_ == '\t'); |
| - bool new_line_inserted = (char_inserted_ == '\n' || char_inserted_ == '\r'); |
| - if ((tab_pressed && tab_inserted) || (enter_pressed && new_line_inserted)) { |
| - // Tab or Enter key is handled by GtkTextView, so we are sure that it is not |
| - // handled by IME. |
| - // Revert the original content in case it has been altered. |
| - // Call gtk_text_buffer_{begin|end}_user_action() to make sure |model_| will |
| - // be updated correctly. |
| - gtk_text_buffer_begin_user_action(text_buffer_); |
| - gtk_text_buffer_set_text(text_buffer_, original_text, -1); |
| - gtk_text_buffer_end_user_action(text_buffer_); |
| - } |
| + // 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 (original_text) |
| - g_free(original_text); |
| - |
| - if (tab_pressed && tab_inserted) { |
| - if (model_->is_keyword_hint() && !model_->keyword().empty()) { |
| - model_->AcceptKeyword(); |
| - } else { |
| - // Handle move focus by ourselves. |
| - static guint signal_id = g_signal_lookup("move-focus", GTK_TYPE_WIDGET); |
| - g_signal_emit(widget, signal_id, 0, (event->state & GDK_SHIFT_MASK) ? |
| - GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD); |
| - } |
| - result = TRUE; |
| - } else if (enter_pressed && new_line_inserted) { |
| + if (enter_pressed && (char_inserted_ == '\n' || char_inserted_ == '\r')) { |
| bool alt_held = (event->state & GDK_MOD1_MASK); |
| + // Revert the original text in case the text has been changed. |
| + SetUserText(original_text); |
| 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 Escape key if |text_view_| did not handle it. |
| - // If it's not handled by us, then we need propagate it upto parent |
| + // We can handle the Escape key if |text_view_| did not handle it. |
| + // If it's not handled by us, then we need to propagate it up to the parent |
| // widgets, so that Escape accelerator can still work. |
| result = model_->OnEscapeKeyPressed(); |
| + } else if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) { |
| + // Omnibox2 can switch its contents while pressing a control key. To switch |
| + // the contents of omnibox2, we notify the AutocompleteEditModel class when |
| + // the control-key state is changed. |
| + model_->OnControlKeyChanged(true); |
| } |
| - // If the key event is not handled by |text_view_| and us, then we need |
| + // 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 stop the signal emission explicitly to prevent the |
| + // 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 = |
| @@ -652,6 +659,13 @@ |
| gboolean AutocompleteEditViewGtk::HandleKeyRelease(GtkWidget* widget, |
| GdkEventKey* event) { |
| + if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) { |
| + // Omnibox2 can switch its contents while pressing a control key. To switch |
| + // the contents of omnibox2, we notify the AutocompleteEditModel class when |
| + // the control-key state is changed. |
| + model_->OnControlKeyChanged(false); |
|
Dean McNamee
2009/08/21 18:21:35
I am still not happy with this code, but I will le
|
| + } |
| + |
| // 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. |
| @@ -872,8 +886,8 @@ |
| // |
| // 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 a Tab or Enter key event |
| - // is handled by IME or not. |
| + // "key-press-event" signal handler distinguish if an Enter key event is |
| + // handled by IME or not. |
| if (len == 1) |
| char_inserted_ = text[0]; |
| @@ -923,6 +937,21 @@ |
| g_signal_stop_emission(text_view_, signal_id, 0); |
| } |
| +void AutocompleteEditViewGtk::HandleViewMoveFocus(GtkWidget* widget) { |
| + // Trigger Tab to search behavior only when Tab key is pressed. |
| + if (tab_was_pressed_ && model_->is_keyword_hint() && |
| + !model_->keyword().empty()) { |
| + model_->AcceptKeyword(); |
| + |
| + // If Tab to search behavior is triggered, then stop the signal emission to |
| + // prevent the focus from being moved. |
| + static guint signal_id = g_signal_lookup("move-focus", GTK_TYPE_WIDGET); |
| + g_signal_stop_emission(widget, signal_id, 0); |
| + } |
| + |
| + // Propagate the signal so that focus can be moved as normal. |
| +} |
| + |
| void AutocompleteEditViewGtk::HandleCopyClipboard() { |
| // On copy, we manually update the PRIMARY selection to contain the |
| // highlighted text. This matches Firefox -- we highlight the URL but don't |