1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_GTK_H_ 6 #define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_GTK_H_ 7 #pragma once 8 9 #include <gtk/gtk.h> 10 11 #include <algorithm> 12 #include <string> 13 14 #include "base/basictypes.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/string_util.h" 17 #include "chrome/browser/autocomplete/autocomplete_edit_view.h" 18 #include "chrome/browser/ui/gtk/owned_widget_gtk.h" 19 #include "chrome/browser/ui/toolbar/toolbar_model.h" 20 #include "content/common/notification_observer.h" 21 #include "content/common/notification_registrar.h" 22 #include "content/common/page_transition_types.h" 23 #include "ui/base/animation/animation_delegate.h" 24 #include "ui/base/gtk/gtk_signal.h" 25 #include "ui/base/gtk/gtk_signal_registrar.h" 26 #include "ui/gfx/rect.h" 27 #include "webkit/glue/window_open_disposition.h" 28 29 class AutocompleteEditController; 30 class AutocompleteEditModel; 31 class AutocompletePopupView; 32 class Profile; 33 class TabContents; 34 35 namespace gfx { 36 class Font; 37 } 38 39 namespace ui { 40 class MultiAnimation; 41 } 42 43 namespace views { 44 class View; 45 } 46 47 #if !defined(TOOLKIT_VIEWS) 48 class GtkThemeService; 49 #endif 50 51 class AutocompleteEditViewGtk : public AutocompleteEditView, 52 public NotificationObserver, 53 public ui::AnimationDelegate { 54 public: 55 // Modeled like the Windows CHARRANGE. Represent a pair of cursor position 56 // offsets. Since GtkTextIters are invalid after the buffer is changed, we 57 // work in character offsets (not bytes). 58 struct CharRange { 59 CharRange() : cp_min(0), cp_max(0) { } 60 CharRange(int n, int x) : cp_min(n), cp_max(x) { } 61 62 // Returns the start/end of the selection. 63 int selection_min() const { return std::min(cp_min, cp_max); } 64 int selection_max() const { return std::max(cp_min, cp_max); } 65 66 // Work in integers to match the gint GTK APIs. 67 int cp_min; // For a selection: Represents the start. 68 int cp_max; // For a selection: Represents the end (insert position). 69 }; 70 71 AutocompleteEditViewGtk(AutocompleteEditController* controller, 72 ToolbarModel* toolbar_model, 73 Profile* profile, 74 CommandUpdater* command_updater, 75 bool popup_window_mode, 76 #if defined(TOOLKIT_VIEWS) 77 views::View* location_bar 78 #else 79 GtkWidget* location_bar 80 #endif 81 ); 82 virtual ~AutocompleteEditViewGtk(); 83 84 // Initialize, create the underlying widgets, etc. 85 void Init(); 86 // Returns the width in pixels needed to display the text from one character 87 // before the caret to the end of the string. See comments in 88 // LocationBarView::Layout as to why this uses -1. 89 int WidthOfTextAfterCursor(); 90 91 // Implement the AutocompleteEditView interface. 92 virtual AutocompleteEditModel* model(); 93 virtual const AutocompleteEditModel* model() const; 94 95 virtual void SaveStateToTab(TabContents* tab); 96 97 virtual void Update(const TabContents* tab_for_state_restoring); 98 99 virtual void OpenURL(const GURL& url, 100 WindowOpenDisposition disposition, 101 PageTransition::Type transition, 102 const GURL& alternate_nav_url, 103 size_t selected_line, 104 const string16& keyword); 105 106 virtual string16 GetText() const; 107 108 virtual bool IsEditingOrEmpty() const; 109 virtual int GetIcon() const; 110 111 virtual void SetUserText(const string16& text); 112 virtual void SetUserText(const string16& text, 113 const string16& display_text, 114 bool update_popup); 115 116 virtual void SetWindowTextAndCaretPos(const string16& text, 117 size_t caret_pos); 118 119 virtual void SetForcedQuery(); 120 121 virtual bool IsSelectAll(); 122 virtual bool DeleteAtEndPressed(); 123 virtual void GetSelectionBounds(string16::size_type* start, 124 string16::size_type* end); 125 virtual void SelectAll(bool reversed); 126 virtual void RevertAll(); 127 128 virtual void UpdatePopup(); 129 virtual void ClosePopup(); 130 131 virtual void SetFocus(); 132 133 virtual void OnTemporaryTextMaybeChanged(const string16& display_text, 134 bool save_original_selection); 135 virtual bool OnInlineAutocompleteTextMaybeChanged( 136 const string16& display_text, size_t user_text_length); 137 virtual void OnRevertTemporaryText(); 138 virtual void OnBeforePossibleChange(); 139 virtual bool OnAfterPossibleChange(); 140 virtual gfx::NativeView GetNativeView() const; 141 virtual CommandUpdater* GetCommandUpdater(); 142 virtual void SetInstantSuggestion(const string16& suggestion, 143 bool animate_to_complete); 144 virtual string16 GetInstantSuggestion() const; 145 virtual int TextWidth() const; 146 virtual bool IsImeComposing() const; 147 148 #if defined(TOOLKIT_VIEWS) 149 virtual views::View* AddToView(views::View* parent); 150 virtual int OnPerformDrop(const views::DropTargetEvent& event); 151 152 // A factory method to create an AutocompleteEditView instance initialized for 153 // linux_views. This currently returns an instance of 154 // AutocompleteEditViewGtk only, but AutocompleteEditViewViews will 155 // be added as an option when TextfieldViews is enabled. 156 static AutocompleteEditView* Create(AutocompleteEditController* controller, 157 ToolbarModel* toolbar_model, 158 Profile* profile, 159 CommandUpdater* command_updater, 160 bool popup_window_mode, 161 views::View* location_bar); 162 #endif 163 164 // Overridden from NotificationObserver: 165 virtual void Observe(NotificationType type, 166 const NotificationSource& source, 167 const NotificationDetails& details); 168 169 // Overridden from ui::AnimationDelegate. 170 virtual void AnimationEnded(const ui::Animation* animation); 171 virtual void AnimationProgressed(const ui::Animation* animation); 172 virtual void AnimationCanceled(const ui::Animation* animation); 173 174 // Sets the colors of the text view according to the theme. 175 void SetBaseColor(); 176 // Sets the colors of the instant suggestion view according to the theme and 177 // the animation state. 178 void UpdateInstantViewColors(); 179 180 // Returns the text view gtk widget. May return NULL if the widget 181 // has already been destroyed. 182 GtkWidget* text_view() { 183 return text_view_; 184 } 185 186 private: 187 CHROMEG_CALLBACK_0(AutocompleteEditViewGtk, void, HandleBeginUserAction, 188 GtkTextBuffer*); 189 CHROMEG_CALLBACK_0(AutocompleteEditViewGtk, void, HandleEndUserAction, 190 GtkTextBuffer*); 191 CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleMarkSet, 192 GtkTextBuffer*, GtkTextIter*, GtkTextMark*); 193 // As above, but called after the default handler. 194 CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleMarkSetAfter, 195 GtkTextBuffer*, GtkTextIter*, GtkTextMark*); 196 CHROMEG_CALLBACK_3(AutocompleteEditViewGtk, void, HandleInsertText, 197 GtkTextBuffer*, GtkTextIter*, const gchar*, gint); 198 CHROMEG_CALLBACK_0(AutocompleteEditViewGtk, void, 199 HandleKeymapDirectionChanged, GdkKeymap*); 200 CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleDeleteRange, 201 GtkTextBuffer*, GtkTextIter*, GtkTextIter*); 202 // Unlike above HandleMarkSet and HandleMarkSetAfter, this handler will always 203 // be connected to the signal. 204 CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleMarkSetAlways, 205 GtkTextBuffer*, GtkTextIter*, GtkTextMark*); 206 207 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleKeyPress, 208 GdkEventKey*); 209 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleKeyRelease, 210 GdkEventKey*); 211 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleViewButtonPress, 212 GdkEventButton*); 213 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, 214 HandleViewButtonRelease, GdkEventButton*); 215 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleViewFocusIn, 216 GdkEventFocus*); 217 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleViewFocusOut, 218 GdkEventFocus*); 219 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandleViewMoveFocus, 220 GtkDirectionType); 221 CHROMEGTK_CALLBACK_3(AutocompleteEditViewGtk, void, HandleViewMoveCursor, 222 GtkMovementStep, gint, gboolean); 223 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandleViewSizeRequest, 224 GtkRequisition*); 225 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandlePopulatePopup, 226 GtkMenu*); 227 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleEditSearchEngines); 228 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandlePasteAndGo); 229 CHROMEGTK_CALLBACK_6(AutocompleteEditViewGtk, void, HandleDragDataReceived, 230 GdkDragContext*, gint, gint, GtkSelectionData*, 231 guint, guint); 232 CHROMEGTK_CALLBACK_4(AutocompleteEditViewGtk, void, HandleDragDataGet, 233 GdkDragContext*, GtkSelectionData*, guint, guint); 234 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleBackSpace); 235 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleCopyClipboard); 236 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleCutClipboard); 237 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandlePasteClipboard); 238 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleExposeEvent, 239 GdkEventExpose*); 240 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, 241 HandleWidgetDirectionChanged, GtkTextDirection); 242 CHROMEGTK_CALLBACK_2(AutocompleteEditViewGtk, void, 243 HandleDeleteFromCursor, GtkDeleteType, gint); 244 // We connect to this so we can determine our toplevel window, so we can 245 // listen to focus change events on it. 246 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandleHierarchyChanged, 247 GtkWidget*); 248 #if GTK_CHECK_VERSION(2, 20, 0) 249 CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandlePreeditChanged, 250 const gchar*); 251 #endif 252 // Undo/redo operations won't trigger "begin-user-action" and 253 // "end-user-action" signals, so we need to hook into "undo" and "redo" 254 // signals and call OnBeforePossibleChange()/OnAfterPossibleChange() by 255 // ourselves. 256 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleUndoRedo); 257 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleUndoRedoAfter); 258 259 CHROMEG_CALLBACK_1(AutocompleteEditViewGtk, void, HandleWindowSetFocus, 260 GtkWindow*, GtkWidget*); 261 262 // Callback function called after context menu is closed. 263 CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, 264 HandlePopupMenuDeactivate); 265 266 // Callback for the PRIMARY selection clipboard. 267 static void ClipboardGetSelectionThunk(GtkClipboard* clipboard, 268 GtkSelectionData* selection_data, 269 guint info, 270 gpointer object); 271 void ClipboardGetSelection(GtkClipboard* clipboard, 272 GtkSelectionData* selection_data, 273 guint info); 274 275 void HandleCopyOrCutClipboard(bool copy); 276 277 // Common implementation for performing a drop on the edit view. 278 bool OnPerformDropImpl(const string16& text); 279 280 // Returns the font used in |text_view_|. 281 gfx::Font GetFont(); 282 283 // Take control of the PRIMARY selection clipboard with |text|. Use 284 // |text_buffer_| as the owner, so that this doesn't remove the selection on 285 // it. This makes use of the above callbacks. 286 void OwnPrimarySelection(const std::string& text); 287 288 // Gets the GTK_TEXT_WINDOW_WIDGET coordinates for |text_view_| that bound the 289 // given iters. 290 gfx::Rect WindowBoundsFromIters(GtkTextIter* iter1, GtkTextIter* iter2); 291 292 // Actual implementation of SelectAll(), but also provides control over 293 // whether the PRIMARY selection is set to the selected text (in SelectAll(), 294 // it isn't, but we want set the selection when the user clicks in the entry). 295 void SelectAllInternal(bool reversed, bool update_primary_selection); 296 297 // Get ready to update |text_buffer_|'s highlighting without making changes to 298 // the PRIMARY selection. Removes the clipboard from |text_buffer_| and 299 // blocks the "mark-set" signal handler. 300 void StartUpdatingHighlightedText(); 301 302 // Finish updating |text_buffer_|'s highlighting such that future changes will 303 // automatically update the PRIMARY selection. Undoes 304 // StartUpdatingHighlightedText()'s changes. 305 void FinishUpdatingHighlightedText(); 306 307 // Get the character indices of the current selection. This honors 308 // direction, cp_max is the insertion point, and cp_min is the bound. 309 CharRange GetSelection() const; 310 311 // Translate from character positions to iterators for the current buffer. 312 void ItersFromCharRange(const CharRange& range, 313 GtkTextIter* iter_min, 314 GtkTextIter* iter_max); 315 316 // Return the number of characers in the current buffer. 317 int GetTextLength() const; 318 319 // Places the caret at the given position. This clears any selection. 320 void PlaceCaretAt(int pos); 321 322 // Returns true if the caret is at the end of the content. 323 bool IsCaretAtEnd() const; 324 325 // Try to parse the current text as a URL and colorize the components. 326 void EmphasizeURLComponents(); 327 328 // Internally invoked whenever the text changes in some way. 329 void TextChanged(); 330 331 // Save |selected_text| as the PRIMARY X selection. Unlike 332 // OwnPrimarySelection(), this won't set an owner or use callbacks. 333 void SavePrimarySelection(const std::string& selected_text); 334 335 // Update the field with |text| and set the selection. 336 void SetTextAndSelectedRange(const string16& text, 337 const CharRange& range); 338 339 // Set the selection to |range|. 340 void SetSelectedRange(const CharRange& range); 341 342 // Adjust the text justification according to the text direction of the widget 343 // and |text_buffer_|'s content, to make sure the real text justification is 344 // always in sync with the UI language direction. 345 void AdjustTextJustification(); 346 347 // Get the text direction of |text_buffer_|'s content, by searching the first 348 // character that has a strong direction. 349 PangoDirection GetContentDirection(); 350 351 // Returns the selected text. 352 std::string GetSelectedText() const; 353 354 // If the selected text parses as a URL OwnPrimarySelection is invoked. 355 void UpdatePrimarySelectionIfValidURL(); 356 357 // Retrieves the first and last iterators in the |text_buffer_|, but excludes 358 // the anchor holding the |instant_view_| widget. 359 void GetTextBufferBounds(GtkTextIter* start, GtkTextIter* end) const; 360 361 // Validates an iterator in the |text_buffer_|, to make sure it doesn't go 362 // beyond the anchor for holding the |instant_view_| widget. 363 void ValidateTextBufferIter(GtkTextIter* iter) const; 364 365 // Adjusts vertical alignment of the |instant_view_| in the |text_view_|, to 366 // make sure they have the same baseline. 367 void AdjustVerticalAlignmentOfInstantView(); 368 369 // Stop showing the instant suggest auto-commit animation. 370 void StopAnimation(); 371 372 // The widget we expose, used for vertically centering the real text edit, 373 // since the height will change based on the font / font size, etc. 374 OwnedWidgetGtk alignment_; 375 376 // The actual text entry which will be owned by the alignment_. The 377 // reference will be set to NULL upon destruction to tell if the gtk 378 // widget tree has been destroyed. This is because gtk destroies child 379 // widgets if the parent (alignemtn_)'s refcount does not go down to 0. 380 GtkWidget* text_view_; 381 382 GtkTextTagTable* tag_table_; 383 GtkTextBuffer* text_buffer_; 384 GtkTextTag* faded_text_tag_; 385 GtkTextTag* secure_scheme_tag_; 386 GtkTextTag* security_error_scheme_tag_; 387 GtkTextTag* normal_text_tag_; 388 389 // Objects for the instant suggestion text view. 390 GtkTextTag* instant_anchor_tag_; 391 392 // A widget for displaying instant suggestion text. It'll be attached to a 393 // child anchor in the |text_buffer_| object. 394 GtkWidget* instant_view_; 395 // Animation from instant suggest (faded text) to autocomplete (selected 396 // text). 397 scoped_ptr<ui::MultiAnimation> instant_animation_; 398 399 // A mark to split the content and the instant anchor. Wherever the end 400 // iterator of the text buffer is required, the iterator to this mark should 401 // be used. 402 GtkTextMark* instant_mark_; 403 404 scoped_ptr<AutocompleteEditModel> model_; 405 scoped_ptr<AutocompletePopupView> popup_view_; 406 AutocompleteEditController* controller_; 407 ToolbarModel* toolbar_model_; 408 409 // The object that handles additional command functionality exposed on the 410 // edit, such as invoking the keyword editor. 411 CommandUpdater* command_updater_; 412 413 // When true, the location bar view is read only and also is has a slightly 414 // different presentation (smaller font size). This is used for popups. 415 bool popup_window_mode_; 416 417 ToolbarModel::SecurityLevel security_level_; 418 419 // Selection at the point where the user started using the 420 // arrows to move around in the popup. 421 CharRange saved_temporary_selection_; 422 423 // Tracking state before and after a possible change. 424 string16 text_before_change_; 425 CharRange sel_before_change_; 426 427 // The most-recently-selected text from the entry that was copied to the 428 // clipboard. This is updated on-the-fly as the user selects text. This may 429 // differ from the actual selected text, such as when 'http://' is prefixed to 430 // the text. It is used in cases where we need to make the PRIMARY selection 431 // persist even after the user has unhighlighted the text in the view 432 // (e.g. when they highlight some text and then click to unhighlight it, we 433 // pass this string to SavePrimarySelection()). 434 std::string selected_text_; 435 436 // When we own the X clipboard, this is the text for it. 437 std::string primary_selection_text_; 438 439 // IDs of the signal handlers for "mark-set" on |text_buffer_|. 440 gulong mark_set_handler_id_; 441 gulong mark_set_handler_id2_; 442 443 #if defined(OS_CHROMEOS) 444 // The following variables are used to implement select-all-on-mouse-up, which 445 // is disabled in the standard Linux build due to poor interaction with the 446 // PRIMARY X selection. 447 448 // Is the first mouse button currently down? When selection marks get moved, 449 // we use this to determine if the user was highlighting text with the mouse 450 // -- if so, we avoid selecting all the text on mouse-up. 451 bool button_1_pressed_; 452 453 // Did the user change the selected text in the middle of the current click? 454 // If so, we don't select all of the text when the button is released -- we 455 // don't want to blow away their selection. 456 bool text_selected_during_click_; 457 458 // Was the text view already focused before the user clicked in it? We use 459 // this to figure out whether we should select all of the text when the button 460 // is released (we only do so if the view was initially unfocused). 461 bool text_view_focused_before_button_press_; 462 #endif 463 464 #if defined(TOOLKIT_VIEWS) 465 views::View* location_bar_view_; 466 #else 467 // Supplies colors, et cetera. 468 GtkThemeService* theme_service_; 469 470 NotificationRegistrar registrar_; 471 #endif 472 473 // Indicates if Enter key was pressed. 474 // 475 // It's used in the key press handler to detect an Enter key press event 476 // during sync dispatch of "end-user-action" signal so that an unexpected 477 // change caused by the event can be ignored in OnAfterPossibleChange(). 478 bool enter_was_pressed_; 479 480 // Indicates if Tab key was pressed. 481 // 482 // It's only used in the key press handler to detect a Tab key press event 483 // during sync dispatch of "move-focus" signal. 484 bool tab_was_pressed_; 485 486 // Indicates that user requested to paste clipboard. 487 // The actual paste clipboard action might be performed later if the 488 // clipboard is not empty. 489 bool paste_clipboard_requested_; 490 491 // Indicates if an Enter key press is inserted as text. 492 // It's used in the key press handler to determine if an Enter key event is 493 // handled by IME or not. 494 bool enter_was_inserted_; 495 496 // Indicates whether the IME changed the text. It's possible for the IME to 497 // handle a key event but not change the text contents (e.g., when pressing 498 // shift+del with no selection). 499 bool text_changed_; 500 501 // Contains the character range that should have a strikethrough (used for 502 // insecure schemes). If the range is size one or less, no strikethrough 503 // is needed. 504 CharRange strikethrough_; 505 506 // Indicates if the selected text is suggested text or not. If the selection 507 // is not suggested text, that means the user manually made the selection. 508 bool selection_suggested_; 509 510 // Was delete pressed? 511 bool delete_was_pressed_; 512 513 // Was the delete key pressed with an empty selection at the end of the edit? 514 bool delete_at_end_pressed_; 515 516 // Indicates if we are handling a key press event. 517 bool handling_key_press_; 518 519 // Indicates if omnibox's content maybe changed by a key press event, so that 520 // we need to call OnAfterPossibleChange() after handling the event. 521 // This flag should be set for changes directly caused by a key press event, 522 // including changes to content text, selection range and preedit string. 523 // Changes caused by function calls like SetUserText() should not affect this 524 // flag. 525 bool content_maybe_changed_by_key_press_; 526 527 // Set this flag to call UpdatePopup() in lost focus and need to update. 528 // Because context menu might take the focus, before setting the flag, check 529 // the focus with model_->has_focus(). 530 bool update_popup_without_focus_; 531 532 #if GTK_CHECK_VERSION(2, 20, 0) 533 // Stores the text being composed by the input method. 534 string16 preedit_; 535 536 // Tracking preedit state before and after a possible change. We don't need to 537 // track preedit_'s content, as it'll be treated as part of text content. 538 size_t preedit_size_before_change_; 539 #endif 540 541 // The view that is going to be focused next. Only valid while handling 542 // "focus-out" events. 543 GtkWidget* going_to_focus_; 544 545 ui::GtkSignalRegistrar signals_; 546 547 DISALLOW_COPY_AND_ASSIGN(AutocompleteEditViewGtk); 548 }; 549 550 #endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_GTK_H_ 551