| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h" | 5 #include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h" |
| 6 | 6 |
| 7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
| 8 #include <gdk/gdkkeysyms.h> | 8 #include <gdk/gdkkeysyms.h> |
| 9 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 command_updater_(command_updater), | 99 command_updater_(command_updater), |
| 100 popup_window_mode_(popup_window_mode), | 100 popup_window_mode_(popup_window_mode), |
| 101 scheme_security_level_(ToolbarModel::NORMAL), | 101 scheme_security_level_(ToolbarModel::NORMAL), |
| 102 mark_set_handler_id_(0), | 102 mark_set_handler_id_(0), |
| 103 button_1_pressed_(false), | 103 button_1_pressed_(false), |
| 104 text_selected_during_click_(false), | 104 text_selected_during_click_(false), |
| 105 text_view_focused_before_button_press_(false), | 105 text_view_focused_before_button_press_(false), |
| 106 #if !defined(TOOLKIT_VIEWS) | 106 #if !defined(TOOLKIT_VIEWS) |
| 107 theme_provider_(GtkThemeProvider::GetFrom(profile)), | 107 theme_provider_(GtkThemeProvider::GetFrom(profile)), |
| 108 #endif | 108 #endif |
| 109 enter_was_pressed_(false), |
| 109 tab_was_pressed_(false), | 110 tab_was_pressed_(false), |
| 110 paste_clipboard_requested_(false) { | 111 paste_clipboard_requested_(false) { |
| 111 model_->SetPopupModel(popup_view_->GetModel()); | 112 model_->SetPopupModel(popup_view_->GetModel()); |
| 112 } | 113 } |
| 113 | 114 |
| 114 AutocompleteEditViewGtk::~AutocompleteEditViewGtk() { | 115 AutocompleteEditViewGtk::~AutocompleteEditViewGtk() { |
| 115 NotificationService::current()->Notify( | 116 NotificationService::current()->Notify( |
| 116 NotificationType::AUTOCOMPLETE_EDIT_DESTROYED, | 117 NotificationType::AUTOCOMPLETE_EDIT_DESTROYED, |
| 117 Source<AutocompleteEditViewGtk>(this), | 118 Source<AutocompleteEditViewGtk>(this), |
| 118 NotificationService::NoDetails()); | 119 NotificationService::NoDetails()); |
| (...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 455 model_->on_paste_replacing_all(); | 456 model_->on_paste_replacing_all(); |
| 456 } | 457 } |
| 457 | 458 |
| 458 // Record our state. | 459 // Record our state. |
| 459 text_before_change_ = GetText(); | 460 text_before_change_ = GetText(); |
| 460 sel_before_change_ = GetSelection(); | 461 sel_before_change_ = GetSelection(); |
| 461 } | 462 } |
| 462 | 463 |
| 463 // TODO(deanm): This is mostly stolen from Windows, and will need some work. | 464 // TODO(deanm): This is mostly stolen from Windows, and will need some work. |
| 464 bool AutocompleteEditViewGtk::OnAfterPossibleChange() { | 465 bool AutocompleteEditViewGtk::OnAfterPossibleChange() { |
| 466 // If the change is caused by an Enter key press event, and the event was not |
| 467 // handled by IME, then it's an unexpected change and shall be reverted here. |
| 468 // {Start|Finish}UpdatingHighlightedText() are called here to prevent the |
| 469 // PRIMARY selection from being changed. |
| 470 if (enter_was_pressed_ && |
| 471 (char_inserted_ == '\n' || char_inserted_ == '\r')) { |
| 472 StartUpdatingHighlightedText(); |
| 473 SetTextAndSelectedRange(text_before_change_, sel_before_change_); |
| 474 FinishUpdatingHighlightedText(); |
| 475 return false; |
| 476 } |
| 477 |
| 465 CharRange new_sel = GetSelection(); | 478 CharRange new_sel = GetSelection(); |
| 466 int length = GetTextLength(); | 479 int length = GetTextLength(); |
| 467 bool selection_differs = (new_sel.cp_min != sel_before_change_.cp_min) || | 480 bool selection_differs = (new_sel.cp_min != sel_before_change_.cp_min) || |
| 468 (new_sel.cp_max != sel_before_change_.cp_max); | 481 (new_sel.cp_max != sel_before_change_.cp_max); |
| 469 bool at_end_of_edit = (new_sel.cp_min == length && new_sel.cp_max == length); | 482 bool at_end_of_edit = (new_sel.cp_min == length && new_sel.cp_max == length); |
| 470 | 483 |
| 471 // See if the text or selection have changed since OnBeforePossibleChange(). | 484 // See if the text or selection have changed since OnBeforePossibleChange(). |
| 472 std::wstring new_text(GetText()); | 485 std::wstring new_text(GetText()); |
| 473 bool text_differs = (new_text != text_before_change_); | 486 bool text_differs = (new_text != text_before_change_); |
| 474 | 487 |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 592 // - If there is only one character in inserted text, save it in | 605 // - If there is only one character in inserted text, save it in |
| 593 // char_inserted_. | 606 // char_inserted_. |
| 594 // - Filter out all new line and tab characters. | 607 // - Filter out all new line and tab characters. |
| 595 // | 608 // |
| 596 // So if |char_inserted_| equals '\n' after calling |text_view_|'s | 609 // So if |char_inserted_| equals '\n' after calling |text_view_|'s |
| 597 // default signal handler against an Enter key press event, then we know that | 610 // default signal handler against an Enter key press event, then we know that |
| 598 // the Enter key press event was handled by GtkTextView rather than IME, and | 611 // the Enter key press event was handled by GtkTextView rather than IME, and |
| 599 // can perform the special behavior for Enter key safely. | 612 // can perform the special behavior for Enter key safely. |
| 600 // | 613 // |
| 601 // Now the last thing is to prevent the content of omnibox from being changed | 614 // Now the last thing is to prevent the content of omnibox from being changed |
| 602 // by GtkTextView when Tab or Enter key is pressed. Because we can't prevent | 615 // by GtkTextView when Enter key is pressed. As OnBeforePossibleChange() and |
| 603 // it, we use a backup and restore trick: If Enter is pressed, backup the | 616 // OnAfterPossibleChange() will be called by GtkTextView before and after |
| 604 // content of omnibox before sending the key event to |text_view_|, and | 617 // changing the content, and the content is already saved in |
| 605 // then restore it afterwards if IME did not handle the event. | 618 // OnBeforePossibleChange(), so if the Enter key press event was not handled |
| 619 // by IME, it's easy to restore the content in OnAfterPossibleChange(), as if |
| 620 // it's not changed at all. |
| 606 | 621 |
| 607 GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget); | 622 GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget); |
| 608 | 623 |
| 609 bool enter_pressed = (event->keyval == GDK_Return || | 624 enter_was_pressed_ = (event->keyval == GDK_Return || |
| 610 event->keyval == GDK_ISO_Enter || | 625 event->keyval == GDK_ISO_Enter || |
| 611 event->keyval == GDK_KP_Enter); | 626 event->keyval == GDK_KP_Enter); |
| 612 | 627 |
| 613 gchar* original_text = NULL; | |
| 614 | |
| 615 // Enter key will have special behavior if it's not handled by IME. | |
| 616 // We need save the original content of |text_buffer_| and restore it when | |
| 617 // necessary, because GtkTextView might alter the content. | |
| 618 if (enter_pressed) { | |
| 619 GtkTextIter start, end; | |
| 620 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); | |
| 621 original_text = gtk_text_buffer_get_text(text_buffer_, &start, &end, FALSE); | |
| 622 // Reset |char_inserted_|, which may be set in the "insert-text" signal | |
| 623 // handler, so that we'll know if an Enter key event was handled by IME. | |
| 624 char_inserted_ = 0; | |
| 625 } | |
| 626 | |
| 627 // Set |tab_was_pressed_| to true if it's a Tab key press event, so that our | 628 // Set |tab_was_pressed_| to true if it's a Tab key press event, so that our |
| 628 // handler of "move-focus" signal can trigger Tab to search behavior when | 629 // handler of "move-focus" signal can trigger Tab to search behavior when |
| 629 // necessary. | 630 // necessary. |
| 630 tab_was_pressed_ = ((event->keyval == GDK_Tab || | 631 tab_was_pressed_ = ((event->keyval == GDK_Tab || |
| 631 event->keyval == GDK_ISO_Left_Tab || | 632 event->keyval == GDK_ISO_Left_Tab || |
| 632 event->keyval == GDK_KP_Tab) && | 633 event->keyval == GDK_KP_Tab) && |
| 633 !(event->state & GDK_CONTROL_MASK)); | 634 !(event->state & GDK_CONTROL_MASK)); |
| 634 | 635 |
| 636 // Reset |char_inserted_|, which may be set in the "insert-text" signal |
| 637 // handler, so that we'll know if an Enter key event was handled by IME. |
| 638 char_inserted_ = 0; |
| 639 |
| 635 // Reset |paste_clipboard_requested_| to make sure we won't misinterpret this | 640 // Reset |paste_clipboard_requested_| to make sure we won't misinterpret this |
| 636 // key input action as a paste action. | 641 // key input action as a paste action. |
| 637 paste_clipboard_requested_ = false; | 642 paste_clipboard_requested_ = false; |
| 638 | 643 |
| 639 // Call the default handler, so that IME can work as normal. | 644 // Call the default handler, so that IME can work as normal. |
| 640 // New line characters will be filtered out by our "insert-text" | 645 // New line characters will be filtered out by our "insert-text" |
| 641 // signal handler attached to |text_buffer_| object. | 646 // signal handler attached to |text_buffer_| object. |
| 642 gboolean result = klass->key_press_event(widget, event); | 647 gboolean result = klass->key_press_event(widget, event); |
| 643 | 648 |
| 644 // Set |tab_was_pressed_| to false, to make sure Tab to search behavior can | 649 // Set |tab_was_pressed_| to false, to make sure Tab to search behavior can |
| 645 // only be triggered by pressing Tab key. | 650 // only be triggered by pressing Tab key. |
| 646 tab_was_pressed_ = false; | 651 tab_was_pressed_ = false; |
| 647 | 652 |
| 648 if (enter_pressed && (char_inserted_ == '\n' || char_inserted_ == '\r')) { | 653 if (enter_was_pressed_ && |
| 654 (char_inserted_ == '\n' || char_inserted_ == '\r')) { |
| 649 bool alt_held = (event->state & GDK_MOD1_MASK); | 655 bool alt_held = (event->state & GDK_MOD1_MASK); |
| 650 // Revert the original text in case the text has been changed. | |
| 651 // Call gtk_text_buffer_{begin|end}_user_action() to make sure |model_| will | |
| 652 // be updated correctly. | |
| 653 // Note: SetUserText() does not work here, it'll reset the keyword state. | |
| 654 DCHECK(original_text); | |
| 655 gtk_text_buffer_begin_user_action(text_buffer_); | |
| 656 gtk_text_buffer_set_text(text_buffer_, original_text, -1); | |
| 657 gtk_text_buffer_end_user_action(text_buffer_); | |
| 658 model_->AcceptInput(alt_held ? NEW_FOREGROUND_TAB : CURRENT_TAB, false); | 656 model_->AcceptInput(alt_held ? NEW_FOREGROUND_TAB : CURRENT_TAB, false); |
| 659 result = TRUE; | 657 result = TRUE; |
| 660 } else if (!result && event->keyval == GDK_Escape && | 658 } else if (!result && event->keyval == GDK_Escape && |
| 661 (event->state & gtk_accelerator_get_default_mod_mask()) == 0) { | 659 (event->state & gtk_accelerator_get_default_mod_mask()) == 0) { |
| 662 // We can handle the Escape key if |text_view_| did not handle it. | 660 // We can handle the Escape key if |text_view_| did not handle it. |
| 663 // If it's not handled by us, then we need to propagate it up to the parent | 661 // If it's not handled by us, then we need to propagate it up to the parent |
| 664 // widgets, so that Escape accelerator can still work. | 662 // widgets, so that Escape accelerator can still work. |
| 665 result = model_->OnEscapeKeyPressed(); | 663 result = model_->OnEscapeKeyPressed(); |
| 666 } else if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) { | 664 } else if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) { |
| 667 // Omnibox2 can switch its contents while pressing a control key. To switch | 665 // Omnibox2 can switch its contents while pressing a control key. To switch |
| 668 // the contents of omnibox2, we notify the AutocompleteEditModel class when | 666 // the contents of omnibox2, we notify the AutocompleteEditModel class when |
| 669 // the control-key state is changed. | 667 // the control-key state is changed. |
| 670 model_->OnControlKeyChanged(true); | 668 model_->OnControlKeyChanged(true); |
| 671 } | 669 } |
| 672 | 670 |
| 673 if (original_text) | 671 // Set |enter_was_pressed_| to false, to make sure OnAfterPossibleChange() can |
| 674 g_free(original_text); | 672 // act as normal for changes made by other events. |
| 673 enter_was_pressed_ = false; |
| 675 | 674 |
| 676 // If the key event is not handled by |text_view_| or us, then we need to | 675 // If the key event is not handled by |text_view_| or us, then we need to |
| 677 // propagate the key event up to parent widgets by returning FALSE. | 676 // propagate the key event up to parent widgets by returning FALSE. |
| 678 // In this case we need to stop the signal emission explicitly to prevent the | 677 // In this case we need to stop the signal emission explicitly to prevent the |
| 679 // default "key-press-event" handler of |text_view_| from being called again. | 678 // default "key-press-event" handler of |text_view_| from being called again. |
| 680 if (!result) { | 679 if (!result) { |
| 681 static guint signal_id = | 680 static guint signal_id = |
| 682 g_signal_lookup("key-press-event", GTK_TYPE_WIDGET); | 681 g_signal_lookup("key-press-event", GTK_TYPE_WIDGET); |
| 683 g_signal_stop_emission(widget, signal_id, 0); | 682 g_signal_stop_emission(widget, signal_id, 0); |
| 684 } | 683 } |
| (...skipping 484 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1169 const std::string& selected_text) { | 1168 const std::string& selected_text) { |
| 1170 GtkClipboard* clipboard = | 1169 GtkClipboard* clipboard = |
| 1171 gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY); | 1170 gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY); |
| 1172 DCHECK(clipboard); | 1171 DCHECK(clipboard); |
| 1173 if (!clipboard) | 1172 if (!clipboard) |
| 1174 return; | 1173 return; |
| 1175 | 1174 |
| 1176 gtk_clipboard_set_text( | 1175 gtk_clipboard_set_text( |
| 1177 clipboard, selected_text.data(), selected_text.size()); | 1176 clipboard, selected_text.data(), selected_text.size()); |
| 1178 } | 1177 } |
| OLD | NEW |