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 |