Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: chrome/browser/autocomplete/autocomplete_edit_view_gtk.cc

Issue 4202005: [Linux] Improve preedit string and Instant suggestion support in omnibox. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update according to review comment. Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/autocomplete/autocomplete_edit_view_gtk.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
143 const views::View* location_bar) 143 const views::View* location_bar)
144 #else 144 #else
145 GtkWidget* location_bar) 145 GtkWidget* location_bar)
146 #endif 146 #endif
147 : text_view_(NULL), 147 : text_view_(NULL),
148 tag_table_(NULL), 148 tag_table_(NULL),
149 text_buffer_(NULL), 149 text_buffer_(NULL),
150 faded_text_tag_(NULL), 150 faded_text_tag_(NULL),
151 secure_scheme_tag_(NULL), 151 secure_scheme_tag_(NULL),
152 security_error_scheme_tag_(NULL), 152 security_error_scheme_tag_(NULL),
153 normal_text_tag_(NULL),
154 instant_anchor_tag_(NULL),
155 instant_view_(NULL),
156 instant_mark_(NULL),
153 model_(new AutocompleteEditModel(this, controller, profile)), 157 model_(new AutocompleteEditModel(this, controller, profile)),
154 #if defined(TOOLKIT_VIEWS) 158 #if defined(TOOLKIT_VIEWS)
155 popup_view_(new AutocompletePopupContentsView( 159 popup_view_(new AutocompletePopupContentsView(
156 gfx::Font(), this, model_.get(), profile, location_bar)), 160 gfx::Font(), this, model_.get(), profile, location_bar)),
157 #else 161 #else
158 popup_view_(new AutocompletePopupViewGtk(this, model_.get(), profile, 162 popup_view_(new AutocompletePopupViewGtk(this, model_.get(), profile,
159 location_bar)), 163 location_bar)),
160 #endif 164 #endif
161 controller_(controller), 165 controller_(controller),
162 toolbar_model_(toolbar_model), 166 toolbar_model_(toolbar_model),
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
211 alignment_.Own(gtk_alignment_new(0., 0.5, 1.0, 0.0)); 215 alignment_.Own(gtk_alignment_new(0., 0.5, 1.0, 0.0));
212 gtk_widget_set_name(alignment_.get(), 216 gtk_widget_set_name(alignment_.get(),
213 "chrome-autocomplete-edit-view"); 217 "chrome-autocomplete-edit-view");
214 218
215 // The GtkTagTable and GtkTextBuffer are not initially unowned, so we have 219 // The GtkTagTable and GtkTextBuffer are not initially unowned, so we have
216 // our own reference when we create them, and we own them. Adding them to 220 // our own reference when we create them, and we own them. Adding them to
217 // the other objects adds a reference; it doesn't adopt them. 221 // the other objects adds a reference; it doesn't adopt them.
218 tag_table_ = gtk_text_tag_table_new(); 222 tag_table_ = gtk_text_tag_table_new();
219 text_buffer_ = gtk_text_buffer_new(tag_table_); 223 text_buffer_ = gtk_text_buffer_new(tag_table_);
220 g_object_set_data(G_OBJECT(text_buffer_), kAutocompleteEditViewGtkKey, this); 224 g_object_set_data(G_OBJECT(text_buffer_), kAutocompleteEditViewGtkKey, this);
225
226 // We need to run this two handlers before undo manager's handlers, so that
227 // text iterators modified by these handlers can be passed down to undo
228 // manager's handlers.
229 g_signal_connect(text_buffer_, "delete-range",
230 G_CALLBACK(&HandleDeleteRangeThunk), this);
231 g_signal_connect(text_buffer_, "mark-set",
232 G_CALLBACK(&HandleMarkSetAlwaysThunk), this);
233
221 text_view_ = gtk_undo_view_new(text_buffer_); 234 text_view_ = gtk_undo_view_new(text_buffer_);
222 if (popup_window_mode_) 235 if (popup_window_mode_)
223 gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view_), false); 236 gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view_), false);
224 237
225 // One pixel left margin is necessary to make the cursor visible when UI 238 // One pixel left margin is necessary to make the cursor visible when UI
226 // language direction is LTR but |text_buffer_|'s content direction is RTL. 239 // language direction is LTR but |text_buffer_|'s content direction is RTL.
227 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view_), 1); 240 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view_), 1);
228 241
229 // See SetEntryStyle() comments. 242 // See SetEntryStyle() comments.
230 gtk_widget_set_name(text_view_, "chrome-location-bar-entry"); 243 gtk_widget_set_name(text_view_, "chrome-location-bar-entry");
(...skipping 17 matching lines...) Expand all
248 NULL, "foreground", "#000000", NULL); 261 NULL, "foreground", "#000000", NULL);
249 262
250 // NOTE: This code used to connect to "changed", however this was fired too 263 // NOTE: This code used to connect to "changed", however this was fired too
251 // often and during bad times (our own buffer changes?). It works out much 264 // often and during bad times (our own buffer changes?). It works out much
252 // better to listen to end-user-action, which should be fired whenever the 265 // better to listen to end-user-action, which should be fired whenever the
253 // user makes some sort of change to the buffer. 266 // user makes some sort of change to the buffer.
254 g_signal_connect(text_buffer_, "begin-user-action", 267 g_signal_connect(text_buffer_, "begin-user-action",
255 G_CALLBACK(&HandleBeginUserActionThunk), this); 268 G_CALLBACK(&HandleBeginUserActionThunk), this);
256 g_signal_connect(text_buffer_, "end-user-action", 269 g_signal_connect(text_buffer_, "end-user-action",
257 G_CALLBACK(&HandleEndUserActionThunk), this); 270 G_CALLBACK(&HandleEndUserActionThunk), this);
258 g_signal_connect(text_buffer_, "insert-text",
259 G_CALLBACK(&HandleInsertTextThunk), this);
260 // We connect to key press and release for special handling of a few keys. 271 // We connect to key press and release for special handling of a few keys.
261 g_signal_connect(text_view_, "key-press-event", 272 g_signal_connect(text_view_, "key-press-event",
262 G_CALLBACK(&HandleKeyPressThunk), this); 273 G_CALLBACK(&HandleKeyPressThunk), this);
263 g_signal_connect(text_view_, "key-release-event", 274 g_signal_connect(text_view_, "key-release-event",
264 G_CALLBACK(&HandleKeyReleaseThunk), this); 275 G_CALLBACK(&HandleKeyReleaseThunk), this);
265 g_signal_connect(text_view_, "button-press-event", 276 g_signal_connect(text_view_, "button-press-event",
266 G_CALLBACK(&HandleViewButtonPressThunk), this); 277 G_CALLBACK(&HandleViewButtonPressThunk), this);
267 g_signal_connect(text_view_, "button-release-event", 278 g_signal_connect(text_view_, "button-release-event",
268 G_CALLBACK(&HandleViewButtonReleaseThunk), this); 279 G_CALLBACK(&HandleViewButtonReleaseThunk), this);
269 g_signal_connect(text_view_, "focus-in-event", 280 g_signal_connect(text_view_, "focus-in-event",
(...skipping 28 matching lines...) Expand all
298 g_signal_connect(text_view_, "paste-clipboard", 309 g_signal_connect(text_view_, "paste-clipboard",
299 G_CALLBACK(&HandlePasteClipboardThunk), this); 310 G_CALLBACK(&HandlePasteClipboardThunk), this);
300 g_signal_connect_after(text_view_, "expose-event", 311 g_signal_connect_after(text_view_, "expose-event",
301 G_CALLBACK(&HandleExposeEventThunk), this); 312 G_CALLBACK(&HandleExposeEventThunk), this);
302 g_signal_connect(text_view_, "direction-changed", 313 g_signal_connect(text_view_, "direction-changed",
303 G_CALLBACK(&HandleWidgetDirectionChangedThunk), this); 314 G_CALLBACK(&HandleWidgetDirectionChangedThunk), this);
304 g_signal_connect(text_view_, "delete-from-cursor", 315 g_signal_connect(text_view_, "delete-from-cursor",
305 G_CALLBACK(&HandleDeleteFromCursorThunk), this); 316 G_CALLBACK(&HandleDeleteFromCursorThunk), this);
306 g_signal_connect(text_view_, "hierarchy-changed", 317 g_signal_connect(text_view_, "hierarchy-changed",
307 G_CALLBACK(&HandleHierarchyChangedThunk), this); 318 G_CALLBACK(&HandleHierarchyChangedThunk), this);
308 #if GTK_CHECK_VERSION(2,20,0) 319 #if GTK_CHECK_VERSION(2, 20, 0)
309 g_signal_connect(text_view_, "preedit-changed", 320 g_signal_connect(text_view_, "preedit-changed",
310 G_CALLBACK(&HandlePreeditChangedThunk), this); 321 G_CALLBACK(&HandlePreeditChangedThunk), this);
311 #endif 322 #endif
323 g_signal_connect(text_view_, "undo", G_CALLBACK(&HandleUndoRedoThunk), this);
324 g_signal_connect(text_view_, "redo", G_CALLBACK(&HandleUndoRedoThunk), this);
325 g_signal_connect_after(text_view_, "undo",
326 G_CALLBACK(&HandleUndoRedoAfterThunk), this);
327 g_signal_connect_after(text_view_, "redo",
328 G_CALLBACK(&HandleUndoRedoAfterThunk), this);
312 329
313 // Setup for the Instant suggestion text view. 330 // Setup for the Instant suggestion text view.
314 instant_view_ = gtk_text_view_new(); 331 // GtkLabel is used instead of GtkTextView to get transparent background.
315 instant_buffer_ = gtk_text_view_get_buffer(GTK_TEXT_VIEW(instant_view_)); 332 instant_view_ = gtk_label_new(NULL);
316 gtk_text_view_add_child_in_window(GTK_TEXT_VIEW(text_view_), 333
334 GtkTextIter end_iter;
335 gtk_text_buffer_get_end_iter(text_buffer_, &end_iter);
336
337 // Insert a Zero Width Space character just before the instant anchor.
338 // It's a hack to workaround a bug of GtkTextView which can not align the
339 // preedit string and a child anchor correctly when there is no other content
340 // around the preedit string.
341 gtk_text_buffer_insert(text_buffer_, &end_iter, "\342\200\213", -1);
342 GtkTextChildAnchor* instant_anchor =
343 gtk_text_buffer_create_child_anchor(text_buffer_, &end_iter);
344
345 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(text_view_),
317 instant_view_, 346 instant_view_,
318 GTK_TEXT_WINDOW_WIDGET, 347 instant_anchor);
319 0, 0); 348
320 instant_text_tag_ = gtk_text_buffer_create_tag( 349 instant_anchor_tag_ = gtk_text_buffer_create_tag(text_buffer_, NULL, NULL);
321 instant_buffer_, NULL, "foreground", kTextBaseColor, NULL); 350
322 GTK_WIDGET_UNSET_FLAGS(instant_view_, GTK_CAN_FOCUS); 351 GtkTextIter anchor_iter;
323 g_signal_connect(instant_view_, "button-press-event", 352 gtk_text_buffer_get_iter_at_child_anchor(text_buffer_, &anchor_iter,
324 G_CALLBACK(&HandleInstantViewButtonPressThunk), this); 353 instant_anchor);
354 gtk_text_buffer_apply_tag(text_buffer_, instant_anchor_tag_,
355 &anchor_iter, &end_iter);
356
357 GtkTextIter start_iter;
358 gtk_text_buffer_get_start_iter(text_buffer_, &start_iter);
359 instant_mark_ =
360 gtk_text_buffer_create_mark(text_buffer_, NULL, &start_iter, FALSE);
361
362 // Hooking up this handler after setting up above hacks for Instant view, so
363 // that we won't filter out the special ZWP mark itself.
364 g_signal_connect(text_buffer_, "insert-text",
365 G_CALLBACK(&HandleInsertTextThunk), this);
366
367 AdjustVerticalAlignmentOfInstantView();
325 368
326 #if !defined(TOOLKIT_VIEWS) 369 #if !defined(TOOLKIT_VIEWS)
327 registrar_.Add(this, 370 registrar_.Add(this,
328 NotificationType::BROWSER_THEME_CHANGED, 371 NotificationType::BROWSER_THEME_CHANGED,
329 NotificationService::AllSources()); 372 NotificationService::AllSources());
330 theme_provider_->InitThemesFor(this); 373 theme_provider_->InitThemesFor(this);
331 #else 374 #else
332 // Manually invoke SetBaseColor() because TOOLKIT_VIEWS doesn't observe 375 // Manually invoke SetBaseColor() because TOOLKIT_VIEWS doesn't observe
333 // themes. 376 // themes.
334 SetBaseColor(); 377 SetBaseColor();
(...skipping 22 matching lines...) Expand all
357 gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(text_view_), 400 gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(text_view_),
358 GTK_TEXT_WINDOW_LEFT) + 401 GTK_TEXT_WINDOW_LEFT) +
359 gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(text_view_), 402 gtk_text_view_get_border_window_size(GTK_TEXT_VIEW(text_view_),
360 GTK_TEXT_WINDOW_RIGHT) + 403 GTK_TEXT_WINDOW_RIGHT) +
361 gtk_text_view_get_left_margin(GTK_TEXT_VIEW(text_view_)) + 404 gtk_text_view_get_left_margin(GTK_TEXT_VIEW(text_view_)) +
362 gtk_text_view_get_right_margin(GTK_TEXT_VIEW(text_view_)); 405 gtk_text_view_get_right_margin(GTK_TEXT_VIEW(text_view_));
363 406
364 GtkTextIter start, end; 407 GtkTextIter start, end;
365 GdkRectangle first_char_bounds, last_char_bounds; 408 GdkRectangle first_char_bounds, last_char_bounds;
366 gtk_text_buffer_get_start_iter(text_buffer_, &start); 409 gtk_text_buffer_get_start_iter(text_buffer_, &start);
410
411 // Use the real end iterator here to take the width of instant suggestion
412 // text into account, so that location bar can layout its children correctly.
367 gtk_text_buffer_get_end_iter(text_buffer_, &end); 413 gtk_text_buffer_get_end_iter(text_buffer_, &end);
368 gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_), 414 gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_),
369 &start, &first_char_bounds); 415 &start, &first_char_bounds);
370 gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_), 416 gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_),
371 &end, &last_char_bounds); 417 &end, &last_char_bounds);
372 return ((last_char_bounds.x > first_char_bounds.x) ? 418 return ((last_char_bounds.x > first_char_bounds.x) ?
373 (last_char_bounds.x + last_char_bounds.width - first_char_bounds.x) : 419 (last_char_bounds.x + last_char_bounds.width - first_char_bounds.x) :
374 (first_char_bounds.x - last_char_bounds.x + last_char_bounds.width)) + 420 (first_char_bounds.x - last_char_bounds.x + last_char_bounds.width)) +
375 horizontal_border_size; 421 horizontal_border_size;
376 } 422 }
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
438 size_t selected_line, 484 size_t selected_line,
439 const std::wstring& keyword) { 485 const std::wstring& keyword) {
440 if (!url.is_valid()) 486 if (!url.is_valid())
441 return; 487 return;
442 488
443 model_->OpenURL(url, disposition, transition, alternate_nav_url, 489 model_->OpenURL(url, disposition, transition, alternate_nav_url,
444 selected_line, keyword); 490 selected_line, keyword);
445 } 491 }
446 492
447 std::wstring AutocompleteEditViewGtk::GetText() const { 493 std::wstring AutocompleteEditViewGtk::GetText() const {
448 return GetTextFromBuffer(text_buffer_); 494 GtkTextIter start, end;
495 GetTextBufferBounds(&start, &end);
496 gchar* utf8 = gtk_text_buffer_get_text(text_buffer_, &start, &end, false);
497 std::wstring out(UTF8ToWide(utf8));
498 g_free(utf8);
499
500 #if GTK_CHECK_VERSION(2, 20, 0)
501 // We need to treat the text currently being composed by the input method as
502 // part of the text content, so that omnibox can work correctly in the middle
503 // of composition.
504 if (preedit_.size()) {
505 GtkTextMark* mark = gtk_text_buffer_get_insert(text_buffer_);
506 gtk_text_buffer_get_iter_at_mark(text_buffer_, &start, mark);
507 out.insert(gtk_text_iter_get_offset(&start), preedit_);
508 }
509 #endif
510 return out;
449 } 511 }
450 512
451 bool AutocompleteEditViewGtk::IsEditingOrEmpty() const { 513 bool AutocompleteEditViewGtk::IsEditingOrEmpty() const {
452 return model_->user_input_in_progress() || 514 return model_->user_input_in_progress() || (GetTextLength() == 0);
453 (gtk_text_buffer_get_char_count(text_buffer_) == 0);
454 } 515 }
455 516
456 int AutocompleteEditViewGtk::GetIcon() const { 517 int AutocompleteEditViewGtk::GetIcon() const {
457 return IsEditingOrEmpty() ? 518 return IsEditingOrEmpty() ?
458 AutocompleteMatch::TypeToIcon(model_->CurrentTextType()) : 519 AutocompleteMatch::TypeToIcon(model_->CurrentTextType()) :
459 toolbar_model_->GetIcon(); 520 toolbar_model_->GetIcon();
460 } 521 }
461 522
462 void AutocompleteEditViewGtk::SetUserText(const std::wstring& text) { 523 void AutocompleteEditViewGtk::SetUserText(const std::wstring& text) {
463 SetUserText(text, text, true); 524 SetUserText(text, text, true);
(...skipping 26 matching lines...) Expand all
490 SetSelectedRange(CharRange(current_text.size(), start + 1)); 551 SetSelectedRange(CharRange(current_text.size(), start + 1));
491 FinishUpdatingHighlightedText(); 552 FinishUpdatingHighlightedText();
492 } 553 }
493 } 554 }
494 555
495 bool AutocompleteEditViewGtk::IsSelectAll() { 556 bool AutocompleteEditViewGtk::IsSelectAll() {
496 GtkTextIter sel_start, sel_end; 557 GtkTextIter sel_start, sel_end;
497 gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end); 558 gtk_text_buffer_get_selection_bounds(text_buffer_, &sel_start, &sel_end);
498 559
499 GtkTextIter start, end; 560 GtkTextIter start, end;
500 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); 561 GetTextBufferBounds(&start, &end);
501 562
502 // Returns true if the |text_buffer_| is empty. 563 // Returns true if the |text_buffer_| is empty.
503 return gtk_text_iter_equal(&start, &sel_start) && 564 return gtk_text_iter_equal(&start, &sel_start) &&
504 gtk_text_iter_equal(&end, &sel_end); 565 gtk_text_iter_equal(&end, &sel_end);
505 } 566 }
506 567
507 void AutocompleteEditViewGtk::GetSelectionBounds(std::wstring::size_type* start, 568 void AutocompleteEditViewGtk::GetSelectionBounds(std::wstring::size_type* start,
508 std::wstring::size_type* end) { 569 std::wstring::size_type* end) {
509 CharRange selection = GetSelection(); 570 CharRange selection = GetSelection();
510 *start = static_cast<size_t>(selection.cp_min); 571 *start = static_cast<size_t>(selection.cp_min);
(...skipping 12 matching lines...) Expand all
523 model_->Revert(); 584 model_->Revert();
524 TextChanged(); 585 TextChanged();
525 } 586 }
526 587
527 void AutocompleteEditViewGtk::UpdatePopup() { 588 void AutocompleteEditViewGtk::UpdatePopup() {
528 model_->SetInputInProgress(true); 589 model_->SetInputInProgress(true);
529 if (!model_->has_focus()) 590 if (!model_->has_focus())
530 return; 591 return;
531 592
532 // Don't inline autocomplete when the caret/selection isn't at the end of 593 // Don't inline autocomplete when the caret/selection isn't at the end of
533 // the text. 594 // the text, or in the middle of composition.
534 CharRange sel = GetSelection(); 595 CharRange sel = GetSelection();
535 model_->StartAutocomplete(sel.cp_min != sel.cp_max, 596 bool no_inline_autocomplete =
536 std::max(sel.cp_max, sel.cp_min) < GetTextLength()); 597 std::max(sel.cp_max, sel.cp_min) < GetTextLength();
598 #if GTK_CHECK_VERSION(2, 20, 0)
599 no_inline_autocomplete = no_inline_autocomplete || preedit_.size();
600 #endif
601 model_->StartAutocomplete(sel.cp_min != sel.cp_max, no_inline_autocomplete);
537 } 602 }
538 603
539 void AutocompleteEditViewGtk::ClosePopup() { 604 void AutocompleteEditViewGtk::ClosePopup() {
540 if (popup_view_->GetModel()->IsOpen()) 605 if (popup_view_->GetModel()->IsOpen())
541 controller_->OnAutocompleteWillClosePopup(); 606 controller_->OnAutocompleteWillClosePopup();
542 607
543 popup_view_->GetModel()->StopAutocomplete(); 608 popup_view_->GetModel()->StopAutocomplete();
544 } 609 }
545 610
546 void AutocompleteEditViewGtk::OnTemporaryTextMaybeChanged( 611 void AutocompleteEditViewGtk::OnTemporaryTextMaybeChanged(
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
667 732
668 if (use_gtk) { 733 if (use_gtk) {
669 gtk_widget_modify_cursor(text_view_, NULL, NULL); 734 gtk_widget_modify_cursor(text_view_, NULL, NULL);
670 gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, NULL); 735 gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, NULL);
671 gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, NULL); 736 gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, NULL);
672 gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, NULL); 737 gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, NULL);
673 gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, NULL); 738 gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, NULL);
674 gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, NULL); 739 gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, NULL);
675 740
676 gtk_util::UndoForceFontSize(text_view_); 741 gtk_util::UndoForceFontSize(text_view_);
742 gtk_util::UndoForceFontSize(instant_view_);
677 743
678 // Grab the text colors out of the style and set our tags to use them. 744 // Grab the text colors out of the style and set our tags to use them.
679 GtkStyle* style = gtk_rc_get_style(text_view_); 745 GtkStyle* style = gtk_rc_get_style(text_view_);
680 746
681 // style may be unrealized at this point, so calculate the halfway point 747 // style may be unrealized at this point, so calculate the halfway point
682 // between text[] and base[] manually instead of just using text_aa[]. 748 // between text[] and base[] manually instead of just using text_aa[].
683 GdkColor average_color = gtk_util::AverageColors( 749 GdkColor average_color = gtk_util::AverageColors(
684 style->text[GTK_STATE_NORMAL], style->base[GTK_STATE_NORMAL]); 750 style->text[GTK_STATE_NORMAL], style->base[GTK_STATE_NORMAL]);
685 751
686 g_object_set(faded_text_tag_, "foreground-gdk", &average_color, NULL); 752 g_object_set(faded_text_tag_, "foreground-gdk", &average_color, NULL);
687 g_object_set(instant_text_tag_, "foreground-gdk", &average_color, NULL);
688 g_object_set(normal_text_tag_, "foreground-gdk", 753 g_object_set(normal_text_tag_, "foreground-gdk",
689 &style->text[GTK_STATE_NORMAL], NULL); 754 &style->text[GTK_STATE_NORMAL], NULL);
755
756 // GtkLabel uses fg color instead of text color.
757 gtk_widget_modify_fg(instant_view_, GTK_STATE_NORMAL, &average_color);
690 } else { 758 } else {
691 const GdkColor* background_color_ptr; 759 const GdkColor* background_color_ptr;
692 #if defined(TOOLKIT_VIEWS) 760 #if defined(TOOLKIT_VIEWS)
693 const GdkColor background_color = gfx::SkColorToGdkColor( 761 const GdkColor background_color = gfx::SkColorToGdkColor(
694 LocationBarView::GetColor(ToolbarModel::NONE, 762 LocationBarView::GetColor(ToolbarModel::NONE,
695 LocationBarView::BACKGROUND)); 763 LocationBarView::BACKGROUND));
696 background_color_ptr = &background_color; 764 background_color_ptr = &background_color;
697 #else 765 #else
698 background_color_ptr = &LocationBarViewGtk::kBackgroundColor; 766 background_color_ptr = &LocationBarViewGtk::kBackgroundColor;
699 #endif 767 #endif
700 gtk_widget_modify_cursor( 768 gtk_widget_modify_cursor(
701 text_view_, &gtk_util::kGdkBlack, &gtk_util::kGdkGray); 769 text_view_, &gtk_util::kGdkBlack, &gtk_util::kGdkGray);
702 gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, background_color_ptr); 770 gtk_widget_modify_base(text_view_, GTK_STATE_NORMAL, background_color_ptr);
703 771
772 GdkColor c;
704 #if !defined(TOOLKIT_VIEWS) 773 #if !defined(TOOLKIT_VIEWS)
705 // Override the selected colors so we don't leak colors from the current 774 // Override the selected colors so we don't leak colors from the current
706 // gtk theme into the chrome-theme. 775 // gtk theme into the chrome-theme.
707 GdkColor c;
708 c = gfx::SkColorToGdkColor( 776 c = gfx::SkColorToGdkColor(
709 theme_provider_->get_active_selection_bg_color()); 777 theme_provider_->get_active_selection_bg_color());
710 gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, &c); 778 gtk_widget_modify_base(text_view_, GTK_STATE_SELECTED, &c);
711 779
712 c = gfx::SkColorToGdkColor( 780 c = gfx::SkColorToGdkColor(
713 theme_provider_->get_active_selection_fg_color()); 781 theme_provider_->get_active_selection_fg_color());
714 gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, &c); 782 gtk_widget_modify_text(text_view_, GTK_STATE_SELECTED, &c);
715 783
716 c = gfx::SkColorToGdkColor( 784 c = gfx::SkColorToGdkColor(
717 theme_provider_->get_inactive_selection_bg_color()); 785 theme_provider_->get_inactive_selection_bg_color());
718 gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, &c); 786 gtk_widget_modify_base(text_view_, GTK_STATE_ACTIVE, &c);
719 787
720 c = gfx::SkColorToGdkColor( 788 c = gfx::SkColorToGdkColor(
721 theme_provider_->get_inactive_selection_fg_color()); 789 theme_provider_->get_inactive_selection_fg_color());
722 gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, &c); 790 gtk_widget_modify_text(text_view_, GTK_STATE_ACTIVE, &c);
723 #endif 791 #endif
724 792
793 gdk_color_parse(kTextBaseColor, &c);
794 gtk_widget_modify_fg(instant_view_, GTK_STATE_NORMAL, &c);
795
725 // Until we switch to vector graphics, force the font size. 796 // Until we switch to vector graphics, force the font size.
726 gtk_util::ForceFontSizePixels(text_view_, 797 gtk_util::ForceFontSizePixels(text_view_,
727 popup_window_mode_ ? 798 popup_window_mode_ ?
728 browser_defaults::kAutocompleteEditFontPixelSizeInPopup : 799 browser_defaults::kAutocompleteEditFontPixelSizeInPopup :
729 browser_defaults::kAutocompleteEditFontPixelSize); 800 browser_defaults::kAutocompleteEditFontPixelSize);
730 801
802 gtk_util::ForceFontSizePixels(instant_view_,
803 popup_window_mode_ ?
804 browser_defaults::kAutocompleteEditFontPixelSizeInPopup :
805 browser_defaults::kAutocompleteEditFontPixelSize);
806
731 g_object_set(faded_text_tag_, "foreground", kTextBaseColor, NULL); 807 g_object_set(faded_text_tag_, "foreground", kTextBaseColor, NULL);
732 g_object_set(instant_text_tag_, "foreground", kTextBaseColor, NULL);
733 g_object_set(normal_text_tag_, "foreground", "#000000", NULL); 808 g_object_set(normal_text_tag_, "foreground", "#000000", NULL);
734 } 809 }
810
811 AdjustVerticalAlignmentOfInstantView();
735 } 812 }
736 813
737 void AutocompleteEditViewGtk::HandleBeginUserAction(GtkTextBuffer* sender) { 814 void AutocompleteEditViewGtk::HandleBeginUserAction(GtkTextBuffer* sender) {
738 OnBeforePossibleChange(); 815 OnBeforePossibleChange();
739 } 816 }
740 817
741 void AutocompleteEditViewGtk::HandleEndUserAction(GtkTextBuffer* sender) { 818 void AutocompleteEditViewGtk::HandleEndUserAction(GtkTextBuffer* sender) {
742 OnAfterPossibleChange(); 819 OnAfterPossibleChange();
743 } 820 }
744 821
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after
939 if (!text_view_focused_before_button_press_ && !text_selected_during_click_) { 1016 if (!text_view_focused_before_button_press_ && !text_selected_during_click_) {
940 // If this was a focusing click and the user didn't drag to highlight any 1017 // If this was a focusing click and the user didn't drag to highlight any
941 // text, select the full input and update the PRIMARY selection. 1018 // text, select the full input and update the PRIMARY selection.
942 SelectAllInternal(false, true); 1019 SelectAllInternal(false, true);
943 1020
944 // So we told the buffer where the cursor should be, but make sure to tell 1021 // So we told the buffer where the cursor should be, but make sure to tell
945 // the view so it can scroll it to be visible if needed. 1022 // the view so it can scroll it to be visible if needed.
946 // NOTE: This function doesn't seem to like a count of 0, looking at the 1023 // NOTE: This function doesn't seem to like a count of 0, looking at the
947 // code it will skip an important loop. Use -1 to achieve the same. 1024 // code it will skip an important loop. Use -1 to achieve the same.
948 GtkTextIter start, end; 1025 GtkTextIter start, end;
949 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); 1026 GetTextBufferBounds(&start, &end);
950 gtk_text_view_move_visually(GTK_TEXT_VIEW(text_view_), &start, -1); 1027 gtk_text_view_move_visually(GTK_TEXT_VIEW(text_view_), &start, -1);
951 } 1028 }
952 #endif 1029 #endif
953 1030
954 // Inform |model_| about possible text selection change. 1031 // Inform |model_| about possible text selection change.
955 OnAfterPossibleChange(); 1032 OnAfterPossibleChange();
956 1033
957 return TRUE; // Don't continue, we called the default handler already. 1034 return TRUE; // Don't continue, we called the default handler already.
958 } 1035 }
959 1036
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
1018 // Cursor placement will remove the selection, so we need inform |model_| 1095 // Cursor placement will remove the selection, so we need inform |model_|
1019 // about this change by calling On{Before|After}PossibleChange() methods. 1096 // about this change by calling On{Before|After}PossibleChange() methods.
1020 OnBeforePossibleChange(); 1097 OnBeforePossibleChange();
1021 gtk_text_buffer_place_cursor(text_buffer_, 1098 gtk_text_buffer_place_cursor(text_buffer_,
1022 count == 1 ? &sel_end : &sel_start); 1099 count == 1 ? &sel_end : &sel_start);
1023 OnAfterPossibleChange(); 1100 OnAfterPossibleChange();
1024 } else if (count == 1 && !has_selection) { 1101 } else if (count == 1 && !has_selection) {
1025 gint cursor_pos; 1102 gint cursor_pos;
1026 g_object_get(G_OBJECT(text_buffer_), "cursor-position", &cursor_pos, 1103 g_object_get(G_OBJECT(text_buffer_), "cursor-position", &cursor_pos,
1027 NULL); 1104 NULL);
1028 if (cursor_pos == gtk_text_buffer_get_char_count(text_buffer_)) 1105 if (cursor_pos == GetTextLength())
1029 controller_->OnCommitSuggestedText(GetText()); 1106 controller_->OnCommitSuggestedText(GetText());
1030 else 1107 else
1031 handled = false; 1108 handled = false;
1032 } else { 1109 } else {
1033 handled = false; 1110 handled = false;
1034 } 1111 }
1035 } else if (step == GTK_MOVEMENT_PAGES) { // Page up and down. 1112 } else if (step == GTK_MOVEMENT_PAGES) { // Page up and down.
1036 // Multiply by count for the direction (if we move too much that's ok). 1113 // Multiply by count for the direction (if we move too much that's ok).
1037 model_->OnUpOrDownKeyPressed(model_->result().size() * count); 1114 model_->OnUpOrDownKeyPressed(model_->result().size() * count);
1038 } else if (step == GTK_MOVEMENT_DISPLAY_LINES) { // Arrow up and down. 1115 } else if (step == GTK_MOVEMENT_DISPLAY_LINES) { // Arrow up and down.
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
1196 g_signal_stop_emission(text_view_, signal_id, 0); 1273 g_signal_stop_emission(text_view_, signal_id, 0);
1197 } 1274 }
1198 } 1275 }
1199 1276
1200 void AutocompleteEditViewGtk::HandleInsertText( 1277 void AutocompleteEditViewGtk::HandleInsertText(
1201 GtkTextBuffer* buffer, GtkTextIter* location, const gchar* text, gint len) { 1278 GtkTextBuffer* buffer, GtkTextIter* location, const gchar* text, gint len) {
1202 std::string filtered_text; 1279 std::string filtered_text;
1203 filtered_text.reserve(len); 1280 filtered_text.reserve(len);
1204 1281
1205 // Filter out new line and tab characters. 1282 // Filter out new line and tab characters.
1206 // |text| is guaranteed to be a valid UTF-8 string, so it's safe here to 1283 // |text| is guaranteed to be a valid UTF-8 string, so we don't need to
1207 // filter byte by byte. 1284 // validate it here.
1208 // 1285 //
1209 // If there was only a single character, then it might be generated by a key 1286 // If there was only a single character, then it might be generated by a key
1210 // event. In this case, we save the single character to help our 1287 // event. In this case, we save the single character to help our
1211 // "key-press-event" signal handler distinguish if an Enter key event is 1288 // "key-press-event" signal handler distinguish if an Enter key event is
1212 // handled by IME or not. 1289 // handled by IME or not.
1213 if (len == 1 && (text[0] == '\n' || text[0] == '\r')) 1290 if (len == 1 && (text[0] == '\n' || text[0] == '\r'))
1214 enter_was_inserted_ = true; 1291 enter_was_inserted_ = true;
1215 1292
1216 for (gint i = 0; i < len; ++i) { 1293 const gchar* p = text;
1217 gchar c = text[i]; 1294 while(*p) {
1218 if (c == '\n' || c == '\r' || c == '\t') 1295 gunichar c = g_utf8_get_char(p);
1219 continue; 1296 const gchar* next = g_utf8_next_char(p);
1220 1297
1221 filtered_text.push_back(c); 1298 // 0x200B is Zero Width Space, which is inserted just before the instant
1299 // anchor for working around the GtkTextView's misalignment bug.
1300 // This character might be captured and inserted into the content by undo
1301 // manager, so we need to filter it out here.
1302 if (c != L'\n' && c != L'\r' && c != L'\t' && c != 0x200B)
1303 filtered_text.append(p, next);
1304
1305 p = next;
1222 } 1306 }
1223 1307
1224 if (filtered_text.length()) { 1308 if (filtered_text.length()) {
1309 // Avoid inserting the text after the instant anchor.
1310 ValidateTextBufferIter(location);
1311
1225 // Call the default handler to insert filtered text. 1312 // Call the default handler to insert filtered text.
1226 GtkTextBufferClass* klass = GTK_TEXT_BUFFER_GET_CLASS(buffer); 1313 GtkTextBufferClass* klass = GTK_TEXT_BUFFER_GET_CLASS(buffer);
1227 klass->insert_text(buffer, location, filtered_text.data(), 1314 klass->insert_text(buffer, location, filtered_text.data(),
1228 static_cast<gint>(filtered_text.length())); 1315 static_cast<gint>(filtered_text.length()));
1229 } 1316 }
1230 1317
1231 // Stop propagating the signal emission to prevent the default handler from 1318 // Stop propagating the signal emission to prevent the default handler from
1232 // being called again. 1319 // being called again.
1233 static guint signal_id = g_signal_lookup("insert-text", GTK_TYPE_TEXT_BUFFER); 1320 static guint signal_id = g_signal_lookup("insert-text", GTK_TYPE_TEXT_BUFFER);
1234 g_signal_stop_emission(buffer, signal_id, 0); 1321 g_signal_stop_emission(buffer, signal_id, 0);
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
1403 cairo_stroke(cr); 1490 cairo_stroke(cr);
1404 cairo_destroy(cr); 1491 cairo_destroy(cr);
1405 1492
1406 return FALSE; 1493 return FALSE;
1407 } 1494 }
1408 1495
1409 void AutocompleteEditViewGtk::SelectAllInternal(bool reversed, 1496 void AutocompleteEditViewGtk::SelectAllInternal(bool reversed,
1410 bool update_primary_selection) { 1497 bool update_primary_selection) {
1411 GtkTextIter start, end; 1498 GtkTextIter start, end;
1412 if (reversed) { 1499 if (reversed) {
1413 gtk_text_buffer_get_bounds(text_buffer_, &end, &start); 1500 GetTextBufferBounds(&end, &start);
1414 } else { 1501 } else {
1415 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); 1502 GetTextBufferBounds(&start, &end);
1416 } 1503 }
1417 if (!update_primary_selection) 1504 if (!update_primary_selection)
1418 StartUpdatingHighlightedText(); 1505 StartUpdatingHighlightedText();
1419 gtk_text_buffer_select_range(text_buffer_, &start, &end); 1506 gtk_text_buffer_select_range(text_buffer_, &start, &end);
1420 if (!update_primary_selection) 1507 if (!update_primary_selection)
1421 FinishUpdatingHighlightedText(); 1508 FinishUpdatingHighlightedText();
1422 } 1509 }
1423 1510
1424 void AutocompleteEditViewGtk::StartUpdatingHighlightedText() { 1511 void AutocompleteEditViewGtk::StartUpdatingHighlightedText() {
1425 if (GTK_WIDGET_REALIZED(text_view_)) { 1512 if (GTK_WIDGET_REALIZED(text_view_)) {
(...skipping 26 matching lines...) Expand all
1452 // actual marks so that we can preserve the selection direction. 1539 // actual marks so that we can preserve the selection direction.
1453 GtkTextIter start, insert; 1540 GtkTextIter start, insert;
1454 GtkTextMark* mark; 1541 GtkTextMark* mark;
1455 1542
1456 mark = gtk_text_buffer_get_selection_bound(text_buffer_); 1543 mark = gtk_text_buffer_get_selection_bound(text_buffer_);
1457 gtk_text_buffer_get_iter_at_mark(text_buffer_, &start, mark); 1544 gtk_text_buffer_get_iter_at_mark(text_buffer_, &start, mark);
1458 1545
1459 mark = gtk_text_buffer_get_insert(text_buffer_); 1546 mark = gtk_text_buffer_get_insert(text_buffer_);
1460 gtk_text_buffer_get_iter_at_mark(text_buffer_, &insert, mark); 1547 gtk_text_buffer_get_iter_at_mark(text_buffer_, &insert, mark);
1461 1548
1549 #if GTK_CHECK_VERSION(2, 20, 0)
1550 // Nothing should be selected when we are in the middle of composition.
1551 DCHECK(preedit_.empty() || gtk_text_iter_equal(&start, &insert));
1552 #endif
1553
1462 return CharRange(gtk_text_iter_get_offset(&start), 1554 return CharRange(gtk_text_iter_get_offset(&start),
1463 gtk_text_iter_get_offset(&insert)); 1555 gtk_text_iter_get_offset(&insert));
1464 } 1556 }
1465 1557
1466 void AutocompleteEditViewGtk::ItersFromCharRange(const CharRange& range, 1558 void AutocompleteEditViewGtk::ItersFromCharRange(const CharRange& range,
1467 GtkTextIter* iter_min, 1559 GtkTextIter* iter_min,
1468 GtkTextIter* iter_max) { 1560 GtkTextIter* iter_max) {
1469 gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_min, range.cp_min); 1561 gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_min, range.cp_min);
1470 gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_max, range.cp_max); 1562 gtk_text_buffer_get_iter_at_offset(text_buffer_, iter_max, range.cp_max);
1471 } 1563 }
1472 1564
1473 int AutocompleteEditViewGtk::GetTextLength() { 1565 int AutocompleteEditViewGtk::GetTextLength() const {
1474 GtkTextIter start, end; 1566 GtkTextIter end;
1475 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); 1567 gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_);
1568 #if GTK_CHECK_VERSION(2, 20, 0)
1569 // We need to count the length of the text being composed, because we treat
1570 // it as part of the content in GetText().
1571 return gtk_text_iter_get_offset(&end) + preedit_.size();
1572 #else
1476 return gtk_text_iter_get_offset(&end); 1573 return gtk_text_iter_get_offset(&end);
1477 } 1574 #endif
1478
1479 std::wstring AutocompleteEditViewGtk::GetTextFromBuffer(
1480 GtkTextBuffer* buffer) const {
1481 GtkTextIter start, end;
1482 gtk_text_buffer_get_bounds(buffer, &start, &end);
1483 gchar* utf8 = gtk_text_buffer_get_text(buffer, &start, &end, false);
1484 std::wstring out(UTF8ToWide(utf8));
1485 g_free(utf8);
1486 return out;
1487 } 1575 }
1488 1576
1489 void AutocompleteEditViewGtk::EmphasizeURLComponents() { 1577 void AutocompleteEditViewGtk::EmphasizeURLComponents() {
1578 #if GTK_CHECK_VERSION(2, 20, 0)
1579 // We can't change the text style easily, if the preedit string (the text
1580 // being composed by the input method) is not empty, which is not treated as
1581 // a part of the text content inside GtkTextView. And it's ok to simply return
1582 // in this case, as this method will be called again when the preedit string
1583 // gets committed.
1584 if (preedit_.size())
1585 return;
1586 #endif
1490 // See whether the contents are a URL with a non-empty host portion, which we 1587 // See whether the contents are a URL with a non-empty host portion, which we
1491 // should emphasize. To check for a URL, rather than using the type returned 1588 // should emphasize. To check for a URL, rather than using the type returned
1492 // by Parse(), ask the model, which will check the desired page transition for 1589 // by Parse(), ask the model, which will check the desired page transition for
1493 // this input. This can tell us whether an UNKNOWN input string is going to 1590 // this input. This can tell us whether an UNKNOWN input string is going to
1494 // be treated as a search or a navigation, and is the same method the Paste 1591 // be treated as a search or a navigation, and is the same method the Paste
1495 // And Go system uses. 1592 // And Go system uses.
1496 url_parse::Component scheme, host; 1593 url_parse::Component scheme, host;
1497 std::wstring text(GetText()); 1594 std::wstring text(GetText());
1498 AutocompleteInput::ParseForEmphasizeComponents( 1595 AutocompleteInput::ParseForEmphasizeComponents(
1499 text, model_->GetDesiredTLD(), &scheme, &host); 1596 text, model_->GetDesiredTLD(), &scheme, &host);
1500 const bool emphasize = model_->CurrentTextIsURL() && (host.len > 0); 1597 const bool emphasize = model_->CurrentTextIsURL() && (host.len > 0);
1501 1598
1502 // Set the baseline emphasis. 1599 // Set the baseline emphasis.
1503 GtkTextIter start, end; 1600 GtkTextIter start, end;
1504 gtk_text_buffer_get_bounds(text_buffer_, &start, &end); 1601 GetTextBufferBounds(&start, &end);
1505 gtk_text_buffer_remove_all_tags(text_buffer_, &start, &end); 1602 gtk_text_buffer_remove_all_tags(text_buffer_, &start, &end);
1506 if (emphasize) { 1603 if (emphasize) {
1507 gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end); 1604 gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end);
1508 1605
1509 // We've found a host name, give it more emphasis. 1606 // We've found a host name, give it more emphasis.
1510 gtk_text_buffer_get_iter_at_line_index(text_buffer_, &start, 0, 1607 gtk_text_buffer_get_iter_at_line_index(text_buffer_, &start, 0,
1511 GetUTF8Offset(text, 1608 GetUTF8Offset(text,
1512 host.begin)); 1609 host.begin));
1513 gtk_text_buffer_get_iter_at_line_index(text_buffer_, &end, 0, 1610 gtk_text_buffer_get_iter_at_line_index(text_buffer_, &end, 0,
1514 GetUTF8Offset(text, 1611 GetUTF8Offset(text,
(...skipping 23 matching lines...) Expand all
1538 } else if (security_level_ == ToolbarModel::SECURITY_WARNING) { 1635 } else if (security_level_ == ToolbarModel::SECURITY_WARNING) {
1539 gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end); 1636 gtk_text_buffer_apply_tag(text_buffer_, faded_text_tag_, &start, &end);
1540 } else { 1637 } else {
1541 gtk_text_buffer_apply_tag(text_buffer_, secure_scheme_tag_, &start, &end); 1638 gtk_text_buffer_apply_tag(text_buffer_, secure_scheme_tag_, &start, &end);
1542 } 1639 }
1543 } 1640 }
1544 } 1641 }
1545 1642
1546 void AutocompleteEditViewGtk::SetInstantSuggestion( 1643 void AutocompleteEditViewGtk::SetInstantSuggestion(
1547 const std::string& suggestion) { 1644 const std::string& suggestion) {
1645 gtk_label_set_text(GTK_LABEL(instant_view_), suggestion.c_str());
1548 if (suggestion.empty()) { 1646 if (suggestion.empty()) {
1549 gtk_widget_hide(instant_view_); 1647 gtk_widget_hide(instant_view_);
1550 } else { 1648 } else {
1551 gtk_text_buffer_set_text(instant_buffer_, suggestion.data(),
1552 suggestion.size());
1553 GtkTextIter start, end;
1554 gtk_text_buffer_get_bounds(instant_buffer_, &start, &end);
1555 gtk_text_buffer_apply_tag(instant_buffer_, instant_text_tag_, &start, &end);
1556 gtk_widget_show(instant_view_); 1649 gtk_widget_show(instant_view_);
1650 AdjustVerticalAlignmentOfInstantView();
1557 } 1651 }
1558 } 1652 }
1559 1653
1560 bool AutocompleteEditViewGtk::CommitInstantSuggestion() { 1654 bool AutocompleteEditViewGtk::CommitInstantSuggestion() {
1561 if (!GTK_WIDGET_VISIBLE(instant_view_)) 1655 const gchar* suggestion = gtk_label_get_text(GTK_LABEL(instant_view_));
1562 return false; 1656 if (!suggestion || !*suggestion)
1563
1564 std::wstring suggest_text = GetTextFromBuffer(instant_buffer_);
1565 if (suggest_text.empty())
1566 return false; 1657 return false;
1567 1658
1568 OnBeforePossibleChange(); 1659 OnBeforePossibleChange();
1569 SetUserText(GetText() + suggest_text); 1660 SetUserText(GetText() + UTF8ToWide(suggestion));
1570 OnAfterPossibleChange(); 1661 OnAfterPossibleChange();
1571 return true; 1662 return true;
1572 } 1663 }
1573 1664
1574 void AutocompleteEditViewGtk::TextChanged() { 1665 void AutocompleteEditViewGtk::TextChanged() {
1575 EmphasizeURLComponents(); 1666 EmphasizeURLComponents();
1576 controller_->OnChanged(); 1667 controller_->OnChanged();
1577
1578 // Move the instant suggestion to the end of the text.
1579 GtkTextIter start, end;
1580 gtk_text_buffer_get_bounds(text_buffer_, &start, &end);
1581 GdkRectangle rect;
1582 gtk_text_view_get_iter_location(GTK_TEXT_VIEW(text_view_), &end, &rect);
1583
1584 // +1 so the cursor will show.
1585 gtk_text_view_move_child(GTK_TEXT_VIEW(text_view_), instant_view_,
1586 rect.x + rect.width + 1, 0);
1587 } 1668 }
1588 1669
1589 void AutocompleteEditViewGtk::SavePrimarySelection( 1670 void AutocompleteEditViewGtk::SavePrimarySelection(
1590 const std::string& selected_text) { 1671 const std::string& selected_text) {
1591 GtkClipboard* clipboard = 1672 GtkClipboard* clipboard =
1592 gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY); 1673 gtk_widget_get_clipboard(text_view_, GDK_SELECTION_PRIMARY);
1593 DCHECK(clipboard); 1674 DCHECK(clipboard);
1594 if (!clipboard) 1675 if (!clipboard)
1595 return; 1676 return;
1596 1677
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
1665 if (selection_suggested_) { 1746 if (selection_suggested_) {
1666 gtk_text_buffer_delete_selection(text_buffer_, true, true); 1747 gtk_text_buffer_delete_selection(text_buffer_, true, true);
1667 selection_suggested_ = false; 1748 selection_suggested_ = false;
1668 } 1749 }
1669 } 1750 }
1670 1751
1671 void AutocompleteEditViewGtk::HandleKeymapDirectionChanged(GdkKeymap* sender) { 1752 void AutocompleteEditViewGtk::HandleKeymapDirectionChanged(GdkKeymap* sender) {
1672 AdjustTextJustification(); 1753 AdjustTextJustification();
1673 } 1754 }
1674 1755
1675 gboolean AutocompleteEditViewGtk::HandleInstantViewButtonPress( 1756 void AutocompleteEditViewGtk::HandleDeleteRange(GtkTextBuffer* buffer,
1676 GtkWidget* sender, 1757 GtkTextIter* start,
1677 GdkEventButton* event) { 1758 GtkTextIter* end) {
1678 // Clicking on the view should have no effect; just stop propagation of the 1759 // Prevent the user from deleting the instant anchor. We can't simply set the
1679 // event. 1760 // instant anchor readonly by applying a tag with "editable" = FALSE, because
1680 return TRUE; 1761 // it'll prevent the insert caret from blinking.
1762 ValidateTextBufferIter(start);
1763 ValidateTextBufferIter(end);
1764 if (!gtk_text_iter_compare(start, end)) {
1765 static guint signal_id =
1766 g_signal_lookup("delete-range", GTK_TYPE_TEXT_BUFFER);
1767 g_signal_stop_emission(buffer, signal_id, 0);
1768 }
1769 }
1770
1771 void AutocompleteEditViewGtk::HandleMarkSetAlways(GtkTextBuffer* buffer,
1772 GtkTextIter* location,
1773 GtkTextMark* mark) {
1774 if (mark == instant_mark_)
1775 return;
1776
1777 GtkTextIter new_iter = *location;
1778 ValidateTextBufferIter(&new_iter);
1779
1780 // "mark-set" signal is actually emitted after the mark's location is already
1781 // set, so if the location is beyond the instant anchor, we need to move the
1782 // mark again, which will emit the signal again. In order to prevent other
1783 // signal handlers from being called twice, we need to stop signal emission
1784 // before moving the mark again.
1785 if (gtk_text_iter_compare(&new_iter, location)) {
1786 static guint signal_id = g_signal_lookup("mark-set", GTK_TYPE_TEXT_BUFFER);
1787 g_signal_stop_emission(buffer, signal_id, 0);
1788 gtk_text_buffer_move_mark(buffer, mark, &new_iter);
1789 }
1681 } 1790 }
1682 1791
1683 // static 1792 // static
1684 void AutocompleteEditViewGtk::ClipboardGetSelectionThunk( 1793 void AutocompleteEditViewGtk::ClipboardGetSelectionThunk(
1685 GtkClipboard* clipboard, 1794 GtkClipboard* clipboard,
1686 GtkSelectionData* selection_data, 1795 GtkSelectionData* selection_data,
1687 guint info, 1796 guint info,
1688 gpointer object) { 1797 gpointer object) {
1689 AutocompleteEditViewGtk* edit_view = 1798 AutocompleteEditViewGtk* edit_view =
1690 reinterpret_cast<AutocompleteEditViewGtk*>( 1799 reinterpret_cast<AutocompleteEditViewGtk*>(
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1724 GURL url; 1833 GURL url;
1725 bool write_url; 1834 bool write_url;
1726 model_->AdjustTextForCopy(selection.selection_min(), IsSelectAll(), &text, 1835 model_->AdjustTextForCopy(selection.selection_min(), IsSelectAll(), &text,
1727 &url, &write_url); 1836 &url, &write_url);
1728 if (write_url) { 1837 if (write_url) {
1729 selected_text_ = WideToUTF8(text); 1838 selected_text_ = WideToUTF8(text);
1730 OwnPrimarySelection(selected_text_); 1839 OwnPrimarySelection(selected_text_);
1731 } 1840 }
1732 } 1841 }
1733 1842
1734 #if GTK_CHECK_VERSION(2,20,0) 1843 #if GTK_CHECK_VERSION(2, 20, 0)
1735 void AutocompleteEditViewGtk::HandlePreeditChanged(GtkWidget* sender, 1844 void AutocompleteEditViewGtk::HandlePreeditChanged(GtkWidget* sender,
1736 const gchar* preedit) { 1845 const gchar* preedit) {
1737 // GtkTextView won't fire "begin-user-action" and "end-user-action" signals 1846 // GtkTextView won't fire "begin-user-action" and "end-user-action" signals
1738 // when changing the preedit string, so we need to call 1847 // when changing the preedit string, so we need to call
1739 // OnBeforePossibleChange() and OnAfterPossibleChange() by ourselves. 1848 // OnBeforePossibleChange() and OnAfterPossibleChange() by ourselves.
1740 OnBeforePossibleChange(); 1849 OnBeforePossibleChange();
1741 if (preedit && *preedit) { 1850 if (preedit && *preedit) {
1742 // GtkTextView will only delete the selection range when committing the 1851 // GtkTextView will only delete the selection range when committing the
1743 // preedit string, which will cause very strange behavior, so we need to 1852 // preedit string, which will cause very strange behavior, so we need to
1744 // delete the selection range here explicitly. See http://crbug.com/18808. 1853 // delete the selection range here explicitly. See http://crbug.com/18808.
1745 if (preedit_.empty()) 1854 if (preedit_.empty())
1746 gtk_text_buffer_delete_selection(text_buffer_, false, true); 1855 gtk_text_buffer_delete_selection(text_buffer_, false, true);
1747 preedit_ = UTF8ToWide(preedit); 1856 preedit_ = UTF8ToWide(preedit);
1748 } else { 1857 } else {
1749 preedit_.clear(); 1858 preedit_.clear();
1750 } 1859 }
1751 OnAfterPossibleChange(); 1860 OnAfterPossibleChange();
1752 } 1861 }
1753 #endif 1862 #endif
1754 1863
1755 void AutocompleteEditViewGtk::HandleWindowSetFocus( 1864 void AutocompleteEditViewGtk::HandleWindowSetFocus(
1756 GtkWindow* sender, GtkWidget* focus) { 1865 GtkWindow* sender, GtkWidget* focus) {
1757 // This is actually a guess. If the focused widget changes in "focus-out" 1866 // This is actually a guess. If the focused widget changes in "focus-out"
1758 // event handler, then the window will respect that and won't focus 1867 // event handler, then the window will respect that and won't focus
1759 // |focus|. I doubt that is likely to happen however. 1868 // |focus|. I doubt that is likely to happen however.
1760 going_to_focus_ = focus; 1869 going_to_focus_ = focus;
1761 } 1870 }
1871
1872 void AutocompleteEditViewGtk::HandleUndoRedo(GtkWidget* sender) {
1873 OnBeforePossibleChange();
1874 }
1875
1876 void AutocompleteEditViewGtk::HandleUndoRedoAfter(GtkWidget* sender) {
1877 OnAfterPossibleChange();
1878 }
1879
1880 void AutocompleteEditViewGtk::GetTextBufferBounds(GtkTextIter* start,
1881 GtkTextIter* end) const {
1882 gtk_text_buffer_get_start_iter(text_buffer_, start);
1883 gtk_text_buffer_get_iter_at_mark(text_buffer_, end, instant_mark_);
1884 }
1885
1886 void AutocompleteEditViewGtk::ValidateTextBufferIter(GtkTextIter* iter) const {
1887 if (!instant_mark_)
1888 return;
1889
1890 GtkTextIter end;
1891 gtk_text_buffer_get_iter_at_mark(text_buffer_, &end, instant_mark_);
1892 if (gtk_text_iter_compare(iter, &end) > 0)
1893 *iter = end;
1894 }
1895
1896 void AutocompleteEditViewGtk::AdjustVerticalAlignmentOfInstantView() {
1897 // By default, GtkTextView layouts an anchored child widget just above the
1898 // baseline, so we need to move the |instant_view_| down to make sure it
1899 // has the same baseline as the |text_view_|.
1900 PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(instant_view_));
1901 int height;
1902 pango_layout_get_size(layout, NULL, &height);
1903 PangoLayoutIter* iter = pango_layout_get_iter(layout);
1904 int baseline = pango_layout_iter_get_baseline(iter);
1905 pango_layout_iter_free(iter);
1906 g_object_set(instant_anchor_tag_, "rise", baseline - height, NULL);
1907 }
OLDNEW
« no previous file with comments | « chrome/browser/autocomplete/autocomplete_edit_view_gtk.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698