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 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 93 popup_positioner)), | 93 popup_positioner)), |
| 94 controller_(controller), | 94 controller_(controller), |
| 95 toolbar_model_(toolbar_model), | 95 toolbar_model_(toolbar_model), |
| 96 command_updater_(command_updater), | 96 command_updater_(command_updater), |
| 97 popup_window_mode_(popup_window_mode), | 97 popup_window_mode_(popup_window_mode), |
| 98 scheme_security_level_(ToolbarModel::NORMAL), | 98 scheme_security_level_(ToolbarModel::NORMAL), |
| 99 selection_saved_(false), | 99 selection_saved_(false), |
| 100 mark_set_handler_id_(0), | 100 mark_set_handler_id_(0), |
| 101 button_1_pressed_(false), | 101 button_1_pressed_(false), |
| 102 text_selected_during_click_(false), | 102 text_selected_during_click_(false), |
| 103 text_view_focused_before_button_press_(false) | 103 text_view_focused_before_button_press_(false), |
| 104 #if !defined(TOOLKIT_VIEWS) | 104 #if !defined(TOOLKIT_VIEWS) |
| 105 , | 105 theme_provider_(GtkThemeProvider::GetFrom(profile)), |
| 106 theme_provider_(GtkThemeProvider::GetFrom(profile)) | |
| 107 #endif | 106 #endif |
| 108 { | 107 tab_was_pressed_(false) { |
| 109 model_->set_popup_model(popup_view_->GetModel()); | 108 model_->set_popup_model(popup_view_->GetModel()); |
| 110 } | 109 } |
| 111 | 110 |
| 112 AutocompleteEditViewGtk::~AutocompleteEditViewGtk() { | 111 AutocompleteEditViewGtk::~AutocompleteEditViewGtk() { |
| 113 NotificationService::current()->Notify( | 112 NotificationService::current()->Notify( |
| 114 NotificationType::AUTOCOMPLETE_EDIT_DESTROYED, | 113 NotificationType::AUTOCOMPLETE_EDIT_DESTROYED, |
| 115 Source<AutocompleteEditViewGtk>(this), | 114 Source<AutocompleteEditViewGtk>(this), |
| 116 NotificationService::NoDetails()); | 115 NotificationService::NoDetails()); |
| 117 | 116 |
| 118 // Explicitly teardown members which have a reference to us. Just to be safe | 117 // Explicitly teardown members which have a reference to us. Just to be safe |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 146 | 145 |
| 147 // Until we switch to vector graphics, force the font size. | 146 // Until we switch to vector graphics, force the font size. |
| 148 gtk_util::ForceFontSizePixels(text_view_, | 147 gtk_util::ForceFontSizePixels(text_view_, |
| 149 popup_window_mode_ ? | 148 popup_window_mode_ ? |
| 150 browser_defaults::kAutocompleteEditFontPixelSizeInPopup : | 149 browser_defaults::kAutocompleteEditFontPixelSizeInPopup : |
| 151 browser_defaults::kAutocompleteEditFontPixelSize); | 150 browser_defaults::kAutocompleteEditFontPixelSize); |
| 152 | 151 |
| 153 // The text view was floating. It will now be owned by the alignment. | 152 // The text view was floating. It will now be owned by the alignment. |
| 154 gtk_container_add(GTK_CONTAINER(alignment_.get()), text_view_); | 153 gtk_container_add(GTK_CONTAINER(alignment_.get()), text_view_); |
| 155 | 154 |
| 156 // Allows inserting tab characters when pressing tab key, to prevent | 155 // Do not allow inserting tab characters when pressing Tab key, so that when |
| 157 // |text_view_| from moving focus. | 156 // Tab key is pressed, |text_view_| will emit "move-focus" signal, which will |
| 158 // Tab characters will be filtered out by our "insert-text" signal handler | 157 // be intercepted by our own handler to trigger Tab to search feature when |
| 159 // attached to |text_buffer_| object. | 158 // necessary. |
| 160 // Tab key events will be handled by our "key-press-event" signal handler for | 159 gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(text_view_), FALSE); |
| 161 // tab to search feature. | |
| 162 gtk_text_view_set_accepts_tab(GTK_TEXT_VIEW(text_view_), TRUE); | |
| 163 | 160 |
| 164 faded_text_tag_ = gtk_text_buffer_create_tag(text_buffer_, | 161 faded_text_tag_ = gtk_text_buffer_create_tag(text_buffer_, |
| 165 NULL, "foreground", kTextBaseColor, NULL); | 162 NULL, "foreground", kTextBaseColor, NULL); |
| 166 secure_scheme_tag_ = gtk_text_buffer_create_tag(text_buffer_, | 163 secure_scheme_tag_ = gtk_text_buffer_create_tag(text_buffer_, |
| 167 NULL, "foreground", kSecureSchemeColor, NULL); | 164 NULL, "foreground", kSecureSchemeColor, NULL); |
| 168 insecure_scheme_tag_ = gtk_text_buffer_create_tag(text_buffer_, | 165 insecure_scheme_tag_ = gtk_text_buffer_create_tag(text_buffer_, |
| 169 NULL, "foreground", kInsecureSchemeColor, NULL); | 166 NULL, "foreground", kInsecureSchemeColor, NULL); |
| 170 normal_text_tag_ = gtk_text_buffer_create_tag(text_buffer_, | 167 normal_text_tag_ = gtk_text_buffer_create_tag(text_buffer_, |
| 171 NULL, "foreground", "#000000", NULL); | 168 NULL, "foreground", "#000000", NULL); |
| 172 | 169 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 190 g_signal_connect(text_view_, "button-release-event", | 187 g_signal_connect(text_view_, "button-release-event", |
| 191 G_CALLBACK(&HandleViewButtonReleaseThunk), this); | 188 G_CALLBACK(&HandleViewButtonReleaseThunk), this); |
| 192 g_signal_connect(text_view_, "focus-in-event", | 189 g_signal_connect(text_view_, "focus-in-event", |
| 193 G_CALLBACK(&HandleViewFocusInThunk), this); | 190 G_CALLBACK(&HandleViewFocusInThunk), this); |
| 194 g_signal_connect(text_view_, "focus-out-event", | 191 g_signal_connect(text_view_, "focus-out-event", |
| 195 G_CALLBACK(&HandleViewFocusOutThunk), this); | 192 G_CALLBACK(&HandleViewFocusOutThunk), this); |
| 196 // NOTE: The GtkTextView documentation asks you not to connect to this | 193 // NOTE: The GtkTextView documentation asks you not to connect to this |
| 197 // signal, but it is very convenient and clean for catching up/down. | 194 // signal, but it is very convenient and clean for catching up/down. |
| 198 g_signal_connect(text_view_, "move-cursor", | 195 g_signal_connect(text_view_, "move-cursor", |
| 199 G_CALLBACK(&HandleViewMoveCursorThunk), this); | 196 G_CALLBACK(&HandleViewMoveCursorThunk), this); |
| 197 g_signal_connect(text_view_, "move-focus", | |
| 198 G_CALLBACK(&HandleViewMoveFocusThunk), this); | |
| 200 // Override the size request. We want to keep the original height request | 199 // Override the size request. We want to keep the original height request |
| 201 // from the widget, since that's font dependent. We want to ignore the width | 200 // from the widget, since that's font dependent. We want to ignore the width |
| 202 // so we don't force a minimum width based on the text length. | 201 // so we don't force a minimum width based on the text length. |
| 203 g_signal_connect(text_view_, "size-request", | 202 g_signal_connect(text_view_, "size-request", |
| 204 G_CALLBACK(&HandleViewSizeRequestThunk), this); | 203 G_CALLBACK(&HandleViewSizeRequestThunk), this); |
| 205 g_signal_connect(text_view_, "populate-popup", | 204 g_signal_connect(text_view_, "populate-popup", |
| 206 G_CALLBACK(&HandlePopulatePopupThunk), this); | 205 G_CALLBACK(&HandlePopulatePopupThunk), this); |
| 207 mark_set_handler_id_ = g_signal_connect( | 206 mark_set_handler_id_ = g_signal_connect( |
| 208 text_buffer_, "mark-set", G_CALLBACK(&HandleMarkSetThunk), this); | 207 text_buffer_, "mark-set", G_CALLBACK(&HandleMarkSetThunk), this); |
| 209 g_signal_connect(text_view_, "drag-data-received", | 208 g_signal_connect(text_view_, "drag-data-received", |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 324 model_->SetUserText(text); | 323 model_->SetUserText(text); |
| 325 // TODO(deanm): something about selection / focus change here. | 324 // TODO(deanm): something about selection / focus change here. |
| 326 SetWindowTextAndCaretPos(display_text, display_text.length()); | 325 SetWindowTextAndCaretPos(display_text, display_text.length()); |
| 327 if (update_popup) | 326 if (update_popup) |
| 328 UpdatePopup(); | 327 UpdatePopup(); |
| 329 TextChanged(); | 328 TextChanged(); |
| 330 } | 329 } |
| 331 | 330 |
| 332 void AutocompleteEditViewGtk::SetWindowTextAndCaretPos(const std::wstring& text, | 331 void AutocompleteEditViewGtk::SetWindowTextAndCaretPos(const std::wstring& text, |
| 333 size_t caret_pos) { | 332 size_t caret_pos) { |
| 333 CharRange range(static_cast<int>(caret_pos), static_cast<int>(caret_pos)); | |
| 334 SetTextAndSelectedRange(text, range); | |
| 335 } | |
| 336 | |
| 337 void AutocompleteEditViewGtk::SetTextAndSelectedRange(const std::wstring& text, | |
| 338 const CharRange& range) { | |
| 334 std::string utf8 = WideToUTF8(text); | 339 std::string utf8 = WideToUTF8(text); |
| 335 gtk_text_buffer_set_text(text_buffer_, utf8.data(), utf8.length()); | 340 gtk_text_buffer_set_text(text_buffer_, utf8.data(), utf8.length()); |
| 336 EmphasizeURLComponents(); | |
| 337 | 341 |
| 338 GtkTextIter cur_pos; | 342 GtkTextIter insert, bound; |
| 339 gtk_text_buffer_get_iter_at_offset(text_buffer_, &cur_pos, caret_pos); | 343 ItersFromCharRange(range, &insert, &bound); |
| 340 gtk_text_buffer_place_cursor(text_buffer_, &cur_pos); | 344 gtk_text_buffer_select_range(text_buffer_, &insert, &bound); |
| 341 } | 345 } |
| 342 | 346 |
| 343 void AutocompleteEditViewGtk::SetForcedQuery() { | 347 void AutocompleteEditViewGtk::SetForcedQuery() { |
| 344 const std::wstring current_text(GetText()); | 348 const std::wstring current_text(GetText()); |
| 345 if (current_text.empty() || (current_text[0] != '?')) { | 349 if (current_text.empty() || (current_text[0] != '?')) { |
| 346 SetUserText(L"?"); | 350 SetUserText(L"?"); |
| 347 } else { | 351 } else { |
| 348 GtkTextIter start, end; | 352 GtkTextIter start, end; |
| 349 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); | 353 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); |
| 350 gtk_text_buffer_get_iter_at_offset(text_buffer_, &start, 1); | 354 gtk_text_buffer_get_iter_at_offset(text_buffer_, &start, 1); |
| 351 StartUpdatingHighlightedText(); | 355 StartUpdatingHighlightedText(); |
| 352 gtk_text_buffer_select_range(text_buffer_, &start, &end); | 356 gtk_text_buffer_select_range(text_buffer_, &start, &end); |
| 353 FinishUpdatingHighlightedText(); | 357 FinishUpdatingHighlightedText(); |
| 354 } | 358 } |
| 355 } | 359 } |
| 356 | 360 |
| 357 bool AutocompleteEditViewGtk::IsSelectAll() { | 361 bool AutocompleteEditViewGtk::IsSelectAll() { |
| 358 NOTIMPLEMENTED(); | 362 GtkTextIter sel_start, sel_end; |
| 359 return false; | 363 if (!gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end)) |
| 364 return false; | |
| 365 | |
| 366 GtkTextIter start, end; | |
| 367 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); | |
| 368 | |
| 369 return gtk_text_iter_equal(&start, &sel_start) && | |
| 370 gtk_text_iter_equal(&end, &sel_end); | |
| 360 } | 371 } |
| 361 | 372 |
| 362 void AutocompleteEditViewGtk::SelectAll(bool reversed) { | 373 void AutocompleteEditViewGtk::SelectAll(bool reversed) { |
| 363 // SelectAll() is invoked as a side effect of other actions (e.g. switching | 374 // SelectAll() is invoked as a side effect of other actions (e.g. switching |
| 364 // tabs or hitting Escape) in autocomplete_edit.cc, so we don't update the | 375 // tabs or hitting Escape) in autocomplete_edit.cc, so we don't update the |
| 365 // PRIMARY selection here. | 376 // PRIMARY selection here. |
| 366 // TODO(derat): But this is also called by LocationBarView::FocusLocation() -- | 377 // TODO(derat): But this is also called by LocationBarView::FocusLocation() -- |
| 367 // should the X selection be updated when the user hits Ctrl-L? | 378 // should the X selection be updated when the user hits Ctrl-L? |
| 368 SelectAllInternal(reversed, false); | 379 SelectAllInternal(reversed, false); |
| 369 } | 380 } |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 385 model_->StartAutocomplete(sel.cp_max < GetTextLength()); | 396 model_->StartAutocomplete(sel.cp_max < GetTextLength()); |
| 386 } | 397 } |
| 387 | 398 |
| 388 void AutocompleteEditViewGtk::ClosePopup() { | 399 void AutocompleteEditViewGtk::ClosePopup() { |
| 389 popup_view_->GetModel()->StopAutocomplete(); | 400 popup_view_->GetModel()->StopAutocomplete(); |
| 390 } | 401 } |
| 391 | 402 |
| 392 void AutocompleteEditViewGtk::OnTemporaryTextMaybeChanged( | 403 void AutocompleteEditViewGtk::OnTemporaryTextMaybeChanged( |
| 393 const std::wstring& display_text, | 404 const std::wstring& display_text, |
| 394 bool save_original_selection) { | 405 bool save_original_selection) { |
| 395 // TODO(deanm): Ignoring save_original_selection here, etc. | 406 if (save_original_selection) { |
| 407 saved_temporary_selection_ = GetSelection(); | |
| 408 saved_temporary_text_ = GetText(); | |
| 409 } | |
| 410 | |
| 411 StartUpdatingHighlightedText(); | |
| 396 SetWindowTextAndCaretPos(display_text, display_text.length()); | 412 SetWindowTextAndCaretPos(display_text, display_text.length()); |
| 413 FinishUpdatingHighlightedText(); | |
| 397 TextChanged(); | 414 TextChanged(); |
| 398 } | 415 } |
| 399 | 416 |
| 400 bool AutocompleteEditViewGtk::OnInlineAutocompleteTextMaybeChanged( | 417 bool AutocompleteEditViewGtk::OnInlineAutocompleteTextMaybeChanged( |
| 401 const std::wstring& display_text, | 418 const std::wstring& display_text, |
| 402 size_t user_text_length) { | 419 size_t user_text_length) { |
| 403 if (display_text == GetText()) | 420 if (display_text == GetText()) |
| 404 return false; | 421 return false; |
| 405 | 422 |
| 406 StartUpdatingHighlightedText(); | 423 StartUpdatingHighlightedText(); |
| 407 SetWindowTextAndCaretPos(display_text, 0); | 424 SetWindowTextAndCaretPos(display_text, 0); |
| 408 | 425 |
| 409 // Select the part of the text that was inline autocompleted. | 426 // Select the part of the text that was inline autocompleted. |
| 410 GtkTextIter bound, insert; | 427 GtkTextIter bound, insert; |
| 411 gtk_text_buffer_get_bounds(text_buffer_, &insert, &bound); | 428 gtk_text_buffer_get_bounds(text_buffer_, &insert, &bound); |
| 412 gtk_text_buffer_get_iter_at_offset(text_buffer_, &insert, user_text_length); | 429 gtk_text_buffer_get_iter_at_offset(text_buffer_, &insert, user_text_length); |
| 413 gtk_text_buffer_select_range(text_buffer_, &insert, &bound); | 430 gtk_text_buffer_select_range(text_buffer_, &insert, &bound); |
| 414 | 431 |
| 415 FinishUpdatingHighlightedText(); | 432 FinishUpdatingHighlightedText(); |
| 416 TextChanged(); | 433 TextChanged(); |
| 417 return true; | 434 return true; |
| 418 } | 435 } |
| 419 | 436 |
| 420 void AutocompleteEditViewGtk::OnRevertTemporaryText() { | 437 void AutocompleteEditViewGtk::OnRevertTemporaryText() { |
| 421 NOTIMPLEMENTED(); | 438 StartUpdatingHighlightedText(); |
| 439 SetTextAndSelectedRange(saved_temporary_text_, saved_temporary_selection_); | |
| 440 FinishUpdatingHighlightedText(); | |
| 441 saved_temporary_text_.clear(); | |
| 442 TextChanged(); | |
| 422 } | 443 } |
| 423 | 444 |
| 424 void AutocompleteEditViewGtk::OnBeforePossibleChange() { | 445 void AutocompleteEditViewGtk::OnBeforePossibleChange() { |
| 425 // Record our state. | 446 // Record our state. |
| 426 text_before_change_ = GetText(); | 447 text_before_change_ = GetText(); |
| 427 sel_before_change_ = GetSelection(); | 448 sel_before_change_ = GetSelection(); |
| 428 } | 449 } |
| 429 | 450 |
| 430 // TODO(deanm): This is mostly stolen from Windows, and will need some work. | 451 // TODO(deanm): This is mostly stolen from Windows, and will need some work. |
| 431 bool AutocompleteEditViewGtk::OnAfterPossibleChange() { | 452 bool AutocompleteEditViewGtk::OnAfterPossibleChange() { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 513 OnBeforePossibleChange(); | 534 OnBeforePossibleChange(); |
| 514 } | 535 } |
| 515 | 536 |
| 516 void AutocompleteEditViewGtk::HandleEndUserAction() { | 537 void AutocompleteEditViewGtk::HandleEndUserAction() { |
| 517 OnAfterPossibleChange(); | 538 OnAfterPossibleChange(); |
| 518 } | 539 } |
| 519 | 540 |
| 520 gboolean AutocompleteEditViewGtk::HandleKeyPress(GtkWidget* widget, | 541 gboolean AutocompleteEditViewGtk::HandleKeyPress(GtkWidget* widget, |
| 521 GdkEventKey* event) { | 542 GdkEventKey* event) { |
| 522 // Background of this piece of complicated code: | 543 // Background of this piece of complicated code: |
| 523 // The omnibox supports several special behavior which may be triggered by | 544 // The omnibox supports several special behaviors which may be triggered by |
| 524 // certain key events: | 545 // certain key events: |
| 525 // Tab to search - triggered by Tab key | 546 // Tab to search - triggered by Tab key |
| 526 // Accept input - triggered by Enter key | 547 // Accept input - triggered by Enter key |
| 527 // Revert input - triggered by Escape key | 548 // Revert input - triggered by Escape key |
| 528 // | 549 // |
| 529 // Because we use a GtkTextView object |text_view_| for text input, we need | 550 // Because we use a GtkTextView object |text_view_| for text input, we need |
| 530 // send all key events to |text_view_| before handling them, to make sure | 551 // send all key events to |text_view_| before handling them, to make sure |
| 531 // IME works without any problem. So here, we intercept "key-press-event" | 552 // IME works without any problem. So here, we intercept "key-press-event" |
| 532 // signal of |text_view_| object and call its default handler to handle the | 553 // signal of |text_view_| object and call its default handler to handle the |
| 533 // key event first. | 554 // key event first. |
| 534 // | 555 // |
| 535 // Then if the key event is one of Tab, Enter and Escape, we need trigger | 556 // Then if the key event is one of Tab, Enter and Escape, we need to trigger |
| 536 // corresponding special behavior if IME did not handle it. | 557 // the corresponding special behavior if IME did not handle it. |
| 537 // For Escape key if the default signal handler returns FALSE, then we know | 558 // For Escape key, if the default signal handler returns FALSE, then we know |
| 538 // it's not handled by IME. | 559 // it's not handled by IME. |
| 539 // | 560 // |
| 540 // But for Tab and Enter key, the default signal handler always returns TRUE, | 561 // For Tab key, as "accepts-tab" property of |text_view_| is set to FALSE, |
| 541 // and following operation will be performed by GtkTextView if IME did not | 562 // if IME did not handle it then "move-focus" signal will be emitted by the |
| 542 // handle it: | 563 // default signal handler of |text_view_|. So we can intercept "move-focus" |
| 543 // Tab key - delete current selection range and insert '\t' | 564 // signal of |text_view_| to know if a Tab key press event was handled by IME, |
| 544 // Enter key - delete current selection range and insert '\n' | 565 // and trigger Tab to search behavior when necessary in the signal handler. |
| 545 // | 566 // |
| 546 // We need dinstinguish if IME handled the key event or not, and avoid above | 567 // But for Enter key, if IME did not handle the key event, the default signal |
| 547 // built-in operation if IME did not handle the key event. Because we don't | 568 // handler will delete current selection range and insert '\n' and always |
| 548 // want the content of omnibox to be changed before triggering our special | 569 // return TRUE. We need to prevent |text_view_| from performing this default |
| 549 // behavior. Otherwise our special behavior would not be performed correctly. | 570 // action if IME did not handle the key event, because we don't want the |
| 571 // content of omnibox to be changed before triggering our special behavior. | |
| 572 // Otherwise our special behavior would not be performed correctly. | |
| 550 // | 573 // |
| 551 // But there is no way for us to prevent GtkTextView from handling the key | 574 // But there is no way for us to prevent GtkTextView from handling the key |
| 552 // event and performing above built-in operation. So in order to achieve our | 575 // event and performing built-in operation. So in order to achieve our goal, |
| 553 // goal, "insert-text" signal of |text_buffer_| object is intercepted, and | 576 // "insert-text" signal of |text_buffer_| object is intercepted, and |
| 554 // following actions are done in the signal handler: | 577 // following actions are done in the signal handler: |
| 555 // - If there is only one character in inserted text, save it in | 578 // - If there is only one character in inserted text, save it in |
| 556 // char_inserted_. | 579 // char_inserted_. |
| 557 // - Filter out all new line and tab characters. | 580 // - Filter out all new line and tab characters. |
| 558 // | 581 // |
| 559 // So if char_inserted_ equals to '\t' after calling |text_view_|'s | 582 // So if |char_inserted_| equals '\n' after calling |text_view_|'s |
| 560 // default signal handler against a Tab key press event, then we can sure this | 583 // default signal handler against an Enter key press event, then we know that |
| 561 // Tab key press event is handled by GtkTextView instead of IME. Then we can | 584 // the Enter key press event was handled by GtkTextView rather than IME, and |
| 562 // perform the special behavior of Tab key safely. | 585 // can perform the special behavior for Enter key safely. |
| 563 // | |
| 564 // Enter key press event can be treated in the same way. | |
| 565 // | 586 // |
| 566 // Now the last thing is to prevent the content of omnibox from being changed | 587 // Now the last thing is to prevent the content of omnibox from being changed |
| 567 // by GtkTextView when Tab or Enter key is pressed. Because we can't prevent | 588 // by GtkTextView when Tab or Enter key is pressed. Because we can't prevent |
| 568 // it, we use backup and restore trick: If Tab or Enter is pressed, backup the | 589 // it, we use a backup and restore trick: If Enter is pressed, backup the |
| 569 // content of omnibox before sending the key event to |text_view_|, and then | 590 // content of omnibox before sending the key event to |text_view_|, and |
| 570 // restore it afterwards if IME did not handle the event. | 591 // then restore it afterwards if IME did not handle the event. |
| 571 | 592 |
| 572 GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget); | 593 GtkWidgetClass* klass = GTK_WIDGET_GET_CLASS(widget); |
| 573 bool tab_pressed = ((event->keyval == GDK_Tab || | |
| 574 event->keyval == GDK_ISO_Left_Tab || | |
| 575 event->keyval == GDK_KP_Tab) && | |
| 576 !(event->state & GDK_CONTROL_MASK)); | |
| 577 | 594 |
| 578 bool enter_pressed = (event->keyval == GDK_Return || | 595 bool enter_pressed = (event->keyval == GDK_Return || |
| 579 event->keyval == GDK_ISO_Enter || | 596 event->keyval == GDK_ISO_Enter || |
| 580 event->keyval == GDK_KP_Enter); | 597 event->keyval == GDK_KP_Enter); |
| 581 | 598 |
| 582 gchar* original_text = NULL; | 599 std::wstring original_text; |
| 583 | 600 |
| 584 // Tab and Enter key will have special behavior if it's not handled by IME. | 601 // Enter key will have special behavior if it's not handled by IME. |
| 585 // We need save the original content of |text_buffer_| and restore it when | 602 // We need save the original content of |text_buffer_| and restore it when |
| 586 // necessary, because GtkTextView might alter the content. | 603 // necessary, because GtkTextView might alter the content. |
| 587 if (tab_pressed || enter_pressed) { | 604 if (enter_pressed) { |
| 588 GtkTextIter start, end; | 605 original_text = GetText(); |
| 589 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); | 606 // Reset |char_inserted_|, which may be set in the "insert-text" signal |
| 590 original_text = gtk_text_buffer_get_text(text_buffer_, &start, &end, FALSE); | 607 // handler, so that we'll know if an Enter key event was handled by IME. |
| 591 | |
| 592 // Reset these variable, which may be set in "insert-text" signal handler. | |
| 593 // So that we can know if Tab or Enter key event is handled by IME or not. | |
| 594 char_inserted_ = 0; | 608 char_inserted_ = 0; |
| 595 } | 609 } |
| 596 | 610 |
| 611 // Set |tab_was_pressed_| to true if it's a Tab key press event, so that our | |
| 612 // handler of "move-focus" signal can trigger Tab to search behavior when | |
| 613 // necessary. | |
| 614 tab_was_pressed_ = ((event->keyval == GDK_Tab || | |
| 615 event->keyval == GDK_ISO_Left_Tab || | |
| 616 event->keyval == GDK_KP_Tab) && | |
| 617 !(event->state & GDK_CONTROL_MASK)); | |
| 618 | |
| 597 // Call the default handler, so that IME can work as normal. | 619 // Call the default handler, so that IME can work as normal. |
| 598 // New line and tab characters will be filtered out by our "insert-text" | 620 // New line characters will be filtered out by our "insert-text" |
| 599 // signal handler attached to |text_buffer_| object. | 621 // signal handler attached to |text_buffer_| object. |
| 600 gboolean result = klass->key_press_event(widget, event); | 622 gboolean result = klass->key_press_event(widget, event); |
| 601 | 623 |
| 602 bool tab_inserted = (char_inserted_ == '\t'); | 624 // Set |tab_was_pressed_| to false, to make sure Tab to search behavior can |
| 603 bool new_line_inserted = (char_inserted_ == '\n' || char_inserted_ == '\r'); | 625 // only be triggered by pressing Tab key. |
| 604 if ((tab_pressed && tab_inserted) || (enter_pressed && new_line_inserted)) { | 626 tab_was_pressed_ = false; |
| 605 // Tab or Enter key is handled by GtkTextView, so we are sure that it is not | |
| 606 // handled by IME. | |
| 607 // Revert the original content in case it has been altered. | |
| 608 // Call gtk_text_buffer_{begin|end}_user_action() to make sure |model_| will | |
| 609 // be updated correctly. | |
| 610 gtk_text_buffer_begin_user_action(text_buffer_); | |
| 611 gtk_text_buffer_set_text(text_buffer_, original_text, -1); | |
| 612 gtk_text_buffer_end_user_action(text_buffer_); | |
| 613 } | |
| 614 | 627 |
| 615 if (original_text) | 628 if (enter_pressed && (char_inserted_ == '\n' || char_inserted_ == '\r')) { |
| 616 g_free(original_text); | |
| 617 | |
| 618 if (tab_pressed && tab_inserted) { | |
| 619 if (model_->is_keyword_hint() && !model_->keyword().empty()) { | |
| 620 model_->AcceptKeyword(); | |
| 621 } else { | |
| 622 // Handle move focus by ourselves. | |
| 623 static guint signal_id = g_signal_lookup("move-focus", GTK_TYPE_WIDGET); | |
| 624 g_signal_emit(widget, signal_id, 0, (event->state & GDK_SHIFT_MASK) ? | |
| 625 GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD); | |
| 626 } | |
| 627 result = TRUE; | |
| 628 } else if (enter_pressed && new_line_inserted) { | |
| 629 bool alt_held = (event->state & GDK_MOD1_MASK); | 629 bool alt_held = (event->state & GDK_MOD1_MASK); |
| 630 // Revert the original text in case the text has been changed. | |
| 631 SetUserText(original_text); | |
| 630 model_->AcceptInput(alt_held ? NEW_FOREGROUND_TAB : CURRENT_TAB, false); | 632 model_->AcceptInput(alt_held ? NEW_FOREGROUND_TAB : CURRENT_TAB, false); |
| 631 result = TRUE; | 633 result = TRUE; |
| 632 } else if (!result && event->keyval == GDK_Escape && | 634 } else if (!result && event->keyval == GDK_Escape && |
| 633 (event->state & gtk_accelerator_get_default_mod_mask()) == 0) { | 635 (event->state & gtk_accelerator_get_default_mod_mask()) == 0) { |
| 634 // We can handle Escape key if |text_view_| did not handle it. | 636 // We can handle the Escape key if |text_view_| did not handle it. |
| 635 // If it's not handled by us, then we need propagate it upto parent | 637 // If it's not handled by us, then we need to propagate it up to the parent |
| 636 // widgets, so that Escape accelerator can still work. | 638 // widgets, so that Escape accelerator can still work. |
| 637 result = model_->OnEscapeKeyPressed(); | 639 result = model_->OnEscapeKeyPressed(); |
| 640 } else if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) { | |
| 641 // Omnibox2 can switch its contents while pressing a control key. To switch | |
| 642 // the contents of omnibox2, we notify the AutocompleteEditModel class when | |
| 643 // the control-key state is changed. | |
| 644 model_->OnControlKeyChanged(true); | |
| 638 } | 645 } |
| 639 | 646 |
| 640 // If the key event is not handled by |text_view_| and us, then we need | 647 // If the key event is not handled by |text_view_| or us, then we need to |
| 641 // propagate the key event up to parent widgets by returning FALSE. | 648 // propagate the key event up to parent widgets by returning FALSE. |
| 642 // In this case we need stop the signal emission explicitly to prevent the | 649 // In this case we need to stop the signal emission explicitly to prevent the |
| 643 // default "key-press-event" handler of |text_view_| from being called again. | 650 // default "key-press-event" handler of |text_view_| from being called again. |
| 644 if (!result) { | 651 if (!result) { |
| 645 static guint signal_id = | 652 static guint signal_id = |
| 646 g_signal_lookup("key-press-event", GTK_TYPE_WIDGET); | 653 g_signal_lookup("key-press-event", GTK_TYPE_WIDGET); |
| 647 g_signal_stop_emission(widget, signal_id, 0); | 654 g_signal_stop_emission(widget, signal_id, 0); |
| 648 } | 655 } |
| 649 | 656 |
| 650 return result; | 657 return result; |
| 651 } | 658 } |
| 652 | 659 |
| 653 gboolean AutocompleteEditViewGtk::HandleKeyRelease(GtkWidget* widget, | 660 gboolean AutocompleteEditViewGtk::HandleKeyRelease(GtkWidget* widget, |
| 654 GdkEventKey* event) { | 661 GdkEventKey* event) { |
| 662 if (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R) { | |
| 663 // Omnibox2 can switch its contents while pressing a control key. To switch | |
| 664 // the contents of omnibox2, we notify the AutocompleteEditModel class when | |
| 665 // the control-key state is changed. | |
| 666 model_->OnControlKeyChanged(false); | |
|
Dean McNamee
2009/08/21 18:21:35
I am still not happy with this code, but I will le
| |
| 667 } | |
| 668 | |
| 655 // Even though we handled the press ourselves, let GtkTextView handle the | 669 // Even though we handled the press ourselves, let GtkTextView handle the |
| 656 // release. It shouldn't do anything particularly interesting, but it will | 670 // release. It shouldn't do anything particularly interesting, but it will |
| 657 // handle the IME work for us. | 671 // handle the IME work for us. |
| 658 return FALSE; // Propagate into GtkTextView. | 672 return FALSE; // Propagate into GtkTextView. |
| 659 } | 673 } |
| 660 | 674 |
| 661 gboolean AutocompleteEditViewGtk::HandleViewButtonPress(GdkEventButton* event) { | 675 gboolean AutocompleteEditViewGtk::HandleViewButtonPress(GdkEventButton* event) { |
| 662 if (event->button == 1) { | 676 if (event->button == 1) { |
| 663 // When the first button is pressed, track some stuff that will help us | 677 // When the first button is pressed, track some stuff that will help us |
| 664 // determine whether we should select all of the text when the button is | 678 // determine whether we should select all of the text when the button is |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 865 GtkTextBuffer* buffer, GtkTextIter* location, const gchar* text, gint len) { | 879 GtkTextBuffer* buffer, GtkTextIter* location, const gchar* text, gint len) { |
| 866 std::string filtered_text; | 880 std::string filtered_text; |
| 867 filtered_text.reserve(len); | 881 filtered_text.reserve(len); |
| 868 | 882 |
| 869 // Filter out new line and tab characters. | 883 // Filter out new line and tab characters. |
| 870 // |text| is guaranteed to be a valid UTF-8 string, so it's safe here to | 884 // |text| is guaranteed to be a valid UTF-8 string, so it's safe here to |
| 871 // filter byte by byte. | 885 // filter byte by byte. |
| 872 // | 886 // |
| 873 // If there was only a single character, then it might be generated by a key | 887 // If there was only a single character, then it might be generated by a key |
| 874 // event. In this case, we save the single character to help our | 888 // event. In this case, we save the single character to help our |
| 875 // "key-press-event" signal handler distinguish if a Tab or Enter key event | 889 // "key-press-event" signal handler distinguish if an Enter key event is |
| 876 // is handled by IME or not. | 890 // handled by IME or not. |
| 877 if (len == 1) | 891 if (len == 1) |
| 878 char_inserted_ = text[0]; | 892 char_inserted_ = text[0]; |
| 879 | 893 |
| 880 for (gint i = 0; i < len; ++i) { | 894 for (gint i = 0; i < len; ++i) { |
| 881 gchar c = text[i]; | 895 gchar c = text[i]; |
| 882 if (c == '\n' || c == '\r' || c == '\t') | 896 if (c == '\n' || c == '\r' || c == '\t') |
| 883 continue; | 897 continue; |
| 884 | 898 |
| 885 filtered_text.push_back(c); | 899 filtered_text.push_back(c); |
| 886 } | 900 } |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 916 | 930 |
| 917 // We're showing a keyword and the user pressed backspace at the beginning | 931 // We're showing a keyword and the user pressed backspace at the beginning |
| 918 // of the text. Delete the selected keyword. | 932 // of the text. Delete the selected keyword. |
| 919 model_->ClearKeyword(GetText()); | 933 model_->ClearKeyword(GetText()); |
| 920 | 934 |
| 921 // Stop propagating the signal emission into GtkTextView. | 935 // Stop propagating the signal emission into GtkTextView. |
| 922 static guint signal_id = g_signal_lookup("backspace", GTK_TYPE_TEXT_VIEW); | 936 static guint signal_id = g_signal_lookup("backspace", GTK_TYPE_TEXT_VIEW); |
| 923 g_signal_stop_emission(text_view_, signal_id, 0); | 937 g_signal_stop_emission(text_view_, signal_id, 0); |
| 924 } | 938 } |
| 925 | 939 |
| 940 void AutocompleteEditViewGtk::HandleViewMoveFocus(GtkWidget* widget) { | |
| 941 // Trigger Tab to search behavior only when Tab key is pressed. | |
| 942 if (tab_was_pressed_ && model_->is_keyword_hint() && | |
| 943 !model_->keyword().empty()) { | |
| 944 model_->AcceptKeyword(); | |
| 945 | |
| 946 // If Tab to search behavior is triggered, then stop the signal emission to | |
| 947 // prevent the focus from being moved. | |
| 948 static guint signal_id = g_signal_lookup("move-focus", GTK_TYPE_WIDGET); | |
| 949 g_signal_stop_emission(widget, signal_id, 0); | |
| 950 } | |
| 951 | |
| 952 // Propagate the signal so that focus can be moved as normal. | |
| 953 } | |
| 954 | |
| 926 void AutocompleteEditViewGtk::HandleCopyClipboard() { | 955 void AutocompleteEditViewGtk::HandleCopyClipboard() { |
| 927 // On copy, we manually update the PRIMARY selection to contain the | 956 // On copy, we manually update the PRIMARY selection to contain the |
| 928 // highlighted text. This matches Firefox -- we highlight the URL but don't | 957 // highlighted text. This matches Firefox -- we highlight the URL but don't |
| 929 // update PRIMARY on Ctrl-L, so Ctrl-L, Ctrl-C and then middle-click is a | 958 // update PRIMARY on Ctrl-L, so Ctrl-L, Ctrl-C and then middle-click is a |
| 930 // convenient way to paste the current URL somewhere. | 959 // convenient way to paste the current URL somewhere. |
| 931 if (!gtk_text_buffer_get_has_selection(text_buffer_)) | 960 if (!gtk_text_buffer_get_has_selection(text_buffer_)) |
| 932 return; | 961 return; |
| 933 | 962 |
| 934 GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); | 963 GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); |
| 935 DCHECK(clipboard); | 964 DCHECK(clipboard); |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1076 const std::string& selected_text) { | 1105 const std::string& selected_text) { |
| 1077 GtkClipboard* clipboard = | 1106 GtkClipboard* clipboard = |
| 1078 gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY); | 1107 gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY); |
| 1079 DCHECK(clipboard); | 1108 DCHECK(clipboard); |
| 1080 if (!clipboard) | 1109 if (!clipboard) |
| 1081 return; | 1110 return; |
| 1082 | 1111 |
| 1083 gtk_clipboard_set_text( | 1112 gtk_clipboard_set_text( |
| 1084 clipboard, selected_text.data(), selected_text.size()); | 1113 clipboard, selected_text.data(), selected_text.size()); |
| 1085 } | 1114 } |
| OLD | NEW |