| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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_, >k_util::kGdkBlack, >k_util::kGdkGray); | 769 text_view_, >k_util::kGdkBlack, >k_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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |