Chromium Code Reviews| 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 496 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 507 void AutocompleteEditViewGtk::HandleBeginUserAction() { | 507 void AutocompleteEditViewGtk::HandleBeginUserAction() { |
| 508 OnBeforePossibleChange(); | 508 OnBeforePossibleChange(); |
| 509 } | 509 } |
| 510 | 510 |
| 511 void AutocompleteEditViewGtk::HandleEndUserAction() { | 511 void AutocompleteEditViewGtk::HandleEndUserAction() { |
| 512 OnAfterPossibleChange(); | 512 OnAfterPossibleChange(); |
| 513 } | 513 } |
| 514 | 514 |
| 515 gboolean AutocompleteEditViewGtk::HandleKeyPress(GtkWidget* widget, | 515 gboolean AutocompleteEditViewGtk::HandleKeyPress(GtkWidget* widget, |
| 516 GdkEventKey* event) { | 516 GdkEventKey* event) { |
| 517 // Background of this piece of complicated code: | |
| 518 // The omnibox supports several special behavior which may be triggered by | |
|
Daniel Erat
2009/08/13 23:36:38
s/behavior/behaviors/
| |
| 519 // certain key events: | |
| 520 // Tab to search - triggered by Tab key | |
| 521 // Accept input - triggered by Enter key | |
| 522 // Revert input - triggered by Escape key | |
| 523 // | |
| 524 // Because we use a GtkTextView object |text_view_| for text input, we need | |
| 525 // send all key events to |text_view_| before handling them, to make sure | |
| 526 // IME works without any problem. So here, we intercept "key-press-event" | |
| 527 // signal of |text_view_| object and call its default handler to handle the | |
| 528 // key event first. | |
| 529 // | |
| 530 // Then if the key event is one of Tab, Enter and Escape, we need trigger | |
|
Daniel Erat
2009/08/13 23:36:38
s/need trigger/need to trigger the/
| |
| 531 // corresponding special behavior if IME did not handle it. | |
| 532 // For Escape key if the default signal handler returns FALSE, then we know | |
| 533 // it's not handled by IME. | |
| 534 // | |
| 535 // But for Tab and Enter key, the default signal handler always returns TRUE, | |
| 536 // and following operation will be performed by GtkTextView if IME did not | |
| 537 // handle it: | |
| 538 // Tab key - delete current selection range and insert '\t' | |
| 539 // Enter key - delete current selection range and insert '\n' | |
| 540 // | |
| 541 // We need dinstinguish if IME handled the key event or not, and avoid above | |
| 542 // built-in operation if IME did not handle the key event. Because we don't | |
|
Daniel Erat
2009/08/13 23:36:38
change to "... and avoid the above built-in operat
| |
| 543 // want the content of omnibox to be changed before triggering our special | |
| 544 // behavior. Otherwise our special behavior would not be performed correctly. | |
| 545 // | |
| 546 // But there is no way for us to prevent GtkTextView from handling the key | |
| 547 // event and performing above built-in operation. So in order to achieve our | |
| 548 // goal, "insert-text" signal of |text_buffer_| object is intercepted, and | |
| 549 // following actions are done in the signal handler: | |
| 550 // - If there is only one character in inserted text, save it in | |
| 551 // char_inserted_. | |
| 552 // - Filter out all new line and tab characters. | |
| 553 // | |
| 554 // So if char_inserted_ equals to '\t' after calling |text_view_|'s | |
|
Daniel Erat
2009/08/13 23:36:38
s/equals to '\t'/equals '\t'/
| |
| 555 // default signal handler against a Tab key press event, then we can sure this | |
|
Daniel Erat
2009/08/13 23:36:38
switch to "then we know that the Tab key press eve
| |
| 556 // Tab key press event is handled by GtkTextView instead of IME. Then we can | |
| 557 // perform the special behavior of Tab key safely. | |
| 558 // | |
| 559 // Enter key press event can be treated in the same way. | |
| 560 // | |
| 561 // Now the last thing is to prevent the content of omnibox from being changed | |
| 562 // by GtkTextView when Tab or Enter key is pressed. Because we can't prevent | |
| 563 // it, we use backup and restore trick: If Tab or Enter is pressed, backup the | |
|
Daniel Erat
2009/08/13 23:36:38
s/we use backup/we use a backup/
| |
| 564 // content of omnibox before sending the key event to |text_view_|, and then | |
| 565 // restore it afterwards if IME did not handle the event. | |
| 566 | |
| 517 GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget); | 567 GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget); |
| 568 bool tab_pressed = ((event->keyval == GDK_Tab || | |
|
Daniel Erat
2009/08/13 23:36:38
insert a blank line before this line? it looks st
| |
| 569 event->keyval == GDK_ISO_Left_Tab || | |
| 570 event->keyval == GDK_KP_Tab) && | |
| 571 !(event->state & GDK_CONTROL_MASK)); | |
| 572 | |
| 573 bool enter_pressed = (event->keyval == GDK_Return || | |
| 574 event->keyval == GDK_ISO_Enter || | |
| 575 event->keyval == GDK_KP_Enter); | |
| 576 | |
| 577 gchar* original_text = NULL; | |
| 578 | |
| 579 // Tab and Enter key will have special behavior if it's not handled by IME. | |
| 580 // We need save the original content of |text_buffer_| and restore it when | |
| 581 // necessary, because GtkTextView might alter the content. | |
| 582 if (tab_pressed || enter_pressed) { | |
| 583 GtkTextIter start, end; | |
| 584 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); | |
| 585 original_text = gtk_text_buffer_get_text(text_buffer_, &start, &end, FALSE); | |
| 586 | |
| 587 // Reset these variable, which may be set in "insert-text" signal handler. | |
|
Daniel Erat
2009/08/13 23:36:38
change to "Reset |char_inserted_|, which may be se
| |
| 588 // So that we can know if Tab or Enter key event is handled by IME or not. | |
| 589 char_inserted_ = 0; | |
| 590 } | |
| 518 | 591 |
| 519 // Call the default handler, so that IME can work as normal. | 592 // Call the default handler, so that IME can work as normal. |
| 520 // New line and tab characters will be filtered out by our "insert-text" | 593 // New line and tab characters will be filtered out by our "insert-text" |
| 521 // signal handler attached to |text_buffer_| object. | 594 // signal handler attached to |text_buffer_| object. |
| 522 klass->key_press_event(widget, event); | 595 gboolean result = klass->key_press_event(widget, event); |
| 523 | 596 |
| 524 if ((event->keyval == GDK_Tab || event->keyval == GDK_ISO_Left_Tab || | 597 bool tab_inserted = (char_inserted_ == '\t'); |
| 525 event->keyval == GDK_KP_Tab) && !(event->state & GDK_CONTROL_MASK)) { | 598 bool new_line_inserted = (char_inserted_ == '\n' || char_inserted_ == '\r'); |
| 599 if ((tab_pressed && tab_inserted) || (enter_pressed && new_line_inserted)) { | |
| 600 // Tab or Enter key is handled by GtkTextView, so we are sure that it is not | |
| 601 // handled by IME. | |
| 602 // Revert the original content in case it has been altered. | |
| 603 // Call gtk_text_buffer_{begin|end}_user_action() to make sure |model_| will | |
| 604 // be updated correctly. | |
| 605 gtk_text_buffer_begin_user_action(text_buffer_); | |
| 606 gtk_text_buffer_set_text(text_buffer_, original_text, -1); | |
| 607 gtk_text_buffer_end_user_action(text_buffer_); | |
| 608 } | |
| 609 | |
| 610 if (original_text) | |
| 611 g_free(original_text); | |
| 612 | |
| 613 if (tab_pressed && tab_inserted) { | |
| 526 if (model_->is_keyword_hint() && !model_->keyword().empty()) { | 614 if (model_->is_keyword_hint() && !model_->keyword().empty()) { |
| 527 model_->AcceptKeyword(); | 615 model_->AcceptKeyword(); |
| 528 } else { | 616 } else { |
| 529 // Handle move focus by ourselves. | 617 // Handle move focus by ourselves. |
| 530 static guint signal_id = g_signal_lookup("move-focus", GTK_TYPE_WIDGET); | 618 static guint signal_id = g_signal_lookup("move-focus", GTK_TYPE_WIDGET); |
| 531 g_signal_emit(widget, signal_id, 0, (event->state & GDK_SHIFT_MASK) ? | 619 g_signal_emit(widget, signal_id, 0, (event->state & GDK_SHIFT_MASK) ? |
| 532 GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD); | 620 GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD); |
| 533 } | 621 } |
| 534 } else if (event->keyval == GDK_Return || | 622 result = TRUE; |
| 535 event->keyval == GDK_ISO_Enter || | 623 } else if (enter_pressed && new_line_inserted) { |
| 536 event->keyval == GDK_KP_Enter) { | |
| 537 bool alt_held = (event->state & GDK_MOD1_MASK); | 624 bool alt_held = (event->state & GDK_MOD1_MASK); |
| 538 model_->AcceptInput(alt_held ? NEW_FOREGROUND_TAB : CURRENT_TAB, false); | 625 model_->AcceptInput(alt_held ? NEW_FOREGROUND_TAB : CURRENT_TAB, false); |
| 539 } else if (event->keyval == GDK_Escape && | 626 result = TRUE; |
| 627 } else if (!result && event->keyval == GDK_Escape && | |
| 540 (event->state & gtk_accelerator_get_default_mod_mask()) == 0) { | 628 (event->state & gtk_accelerator_get_default_mod_mask()) == 0) { |
| 541 model_->OnEscapeKeyPressed(); | 629 // We can handle Escape key if |text_view_| did not handle it. |
|
Daniel Erat
2009/08/13 23:36:38
s/can handle Escape/can handle the Escape/
| |
| 630 // If it's not handled by us, then we need propagate it upto parent | |
|
Daniel Erat
2009/08/13 23:36:38
change to "... then we need to propagate it up to
| |
| 631 // widgets, so that Escape accelerator can still work. | |
| 632 result = model_->OnEscapeKeyPressed(); | |
| 542 } | 633 } |
| 543 | 634 |
| 544 // Stop propagating the event to prevent the default handler from being | 635 // If the key event is not handled by |text_view_| and us, then we need |
|
Daniel Erat
2009/08/13 23:36:38
s/and us/or us/
s/need/need to/
| |
| 545 // called again. | 636 // propagate the key event up to parent widgets by returning FALSE. |
| 546 return TRUE; | 637 // In this case we need stop the signal emission explicitly to prevent the |
|
Daniel Erat
2009/08/13 23:36:38
s/need stop/need to stop/
| |
| 638 // default "key-press-event" handler of |text_view_| from being called again. | |
| 639 if (!result) { | |
| 640 static guint signal_id = | |
| 641 g_signal_lookup("key-press-event", GTK_TYPE_WIDGET); | |
| 642 g_signal_stop_emission(widget, signal_id, 0); | |
| 643 } | |
| 644 | |
| 645 return result; | |
| 547 } | 646 } |
| 548 | 647 |
| 549 gboolean AutocompleteEditViewGtk::HandleKeyRelease(GtkWidget* widget, | 648 gboolean AutocompleteEditViewGtk::HandleKeyRelease(GtkWidget* widget, |
| 550 GdkEventKey* event) { | 649 GdkEventKey* event) { |
| 551 // Even though we handled the press ourselves, let GtkTextView handle the | 650 // Even though we handled the press ourselves, let GtkTextView handle the |
| 552 // release. It shouldn't do anything particularly interesting, but it will | 651 // release. It shouldn't do anything particularly interesting, but it will |
| 553 // handle the IME work for us. | 652 // handle the IME work for us. |
| 554 return FALSE; // Propagate into GtkTextView. | 653 return FALSE; // Propagate into GtkTextView. |
| 555 } | 654 } |
| 556 | 655 |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 747 } | 846 } |
| 748 | 847 |
| 749 void AutocompleteEditViewGtk::HandleInsertText( | 848 void AutocompleteEditViewGtk::HandleInsertText( |
| 750 GtkTextBuffer* buffer, GtkTextIter* location, const gchar* text, gint len) { | 849 GtkTextBuffer* buffer, GtkTextIter* location, const gchar* text, gint len) { |
| 751 std::string filtered_text; | 850 std::string filtered_text; |
| 752 filtered_text.reserve(len); | 851 filtered_text.reserve(len); |
| 753 | 852 |
| 754 // Filter out new line and tab characters. | 853 // Filter out new line and tab characters. |
| 755 // |text| is guaranteed to be a valid UTF-8 string, so it's safe here to | 854 // |text| is guaranteed to be a valid UTF-8 string, so it's safe here to |
| 756 // filter byte by byte. | 855 // filter byte by byte. |
| 856 // | |
| 857 // If there was only a single character, then it might be generated by a key | |
| 858 // event. In this case, we save the single character to help our | |
| 859 // "key-press-event" signal handler distinguish if a Tab or Enter key event | |
| 860 // is handled by IME or not. | |
| 861 if (len == 1) | |
| 862 char_inserted_ = text[0]; | |
| 863 | |
| 757 for (gint i = 0; i < len; ++i) { | 864 for (gint i = 0; i < len; ++i) { |
| 758 gchar c = text[i]; | 865 gchar c = text[i]; |
| 759 if (c == '\n' || c == '\r' || c == '\t') | 866 if (c == '\n' || c == '\r' || c == '\t') |
| 760 continue; | 867 continue; |
| 761 | 868 |
| 762 filtered_text.push_back(c); | 869 filtered_text.push_back(c); |
| 763 } | 870 } |
| 764 | 871 |
| 765 if (filtered_text.length()) { | 872 if (filtered_text.length()) { |
| 766 // Call the default handler to insert filtered text. | 873 // Call the default handler to insert filtered text. |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 928 const std::string& selected_text) { | 1035 const std::string& selected_text) { |
| 929 GtkClipboard* clipboard = | 1036 GtkClipboard* clipboard = |
| 930 gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY); | 1037 gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY); |
| 931 DCHECK(clipboard); | 1038 DCHECK(clipboard); |
| 932 if (!clipboard) | 1039 if (!clipboard) |
| 933 return; | 1040 return; |
| 934 | 1041 |
| 935 gtk_clipboard_set_text( | 1042 gtk_clipboard_set_text( |
| 936 clipboard, selected_text.data(), selected_text.size()); | 1043 clipboard, selected_text.data(), selected_text.size()); |
| 937 } | 1044 } |
| OLD | NEW |