1 // Copyright 2012 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_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_ 6 #define CHROME_BROWSER_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_ 7 8 #include <gtk/gtk.h> 9 10 #include <algorithm> 11 #include <string> 12 13 #include "base/basictypes.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/strings/string_util.h" 16 #include "chrome/browser/ui/omnibox/omnibox_view.h" 17 #include "chrome/browser/ui/toolbar/toolbar_model.h" 18 #include "content/public/browser/notification_observer.h" 19 #include "content/public/browser/notification_registrar.h" 20 #include "ui/base/gtk/gtk_signal.h" 21 #include "ui/base/gtk/gtk_signal_registrar.h" 22 #include "ui/base/gtk/owned_widget_gtk.h" 23 #include "ui/base/window_open_disposition.h" 24 #include "ui/gfx/rect.h" 25 26 class Browser; 27 class OmniboxPopupView; 28 class Profile; 29 30 namespace gfx { 31 class Font; 32 } 33 34 class GtkThemeService; 35 36 class OmniboxViewGtk : public OmniboxView, 37 public content::NotificationObserver { 38 public: 39 // Modeled like the Windows CHARRANGE. Represent a pair of cursor position 40 // offsets. Since GtkTextIters are invalid after the buffer is changed, we 41 // work in character offsets (not bytes). 42 struct CharRange { 43 CharRange() : cp_min(0), cp_max(0) { } 44 CharRange(int n, int x) : cp_min(n), cp_max(x) { } 45 46 // Returns the start/end of the selection. 47 int selection_min() const { return std::min(cp_min, cp_max); } 48 int selection_max() const { return std::max(cp_min, cp_max); } 49 50 // Work in integers to match the gint GTK APIs. 51 int cp_min; // For a selection: Represents the start. 52 int cp_max; // For a selection: Represents the end (insert position). 53 }; 54 55 // profile parameter is introduced for unittests which can not instantiate 56 // browser object and pass NULL to the browser parameter. 57 // In other use case, you should pass browser->profile() object as 58 // profile parameter. 59 OmniboxViewGtk(OmniboxEditController* controller, 60 ToolbarModel* toolbar_model, 61 Browser* browser, 62 Profile* profile, 63 CommandUpdater* command_updater, 64 bool popup_window_mode, 65 GtkWidget* location_bar); 66 virtual ~OmniboxViewGtk(); 67 68 // Initialize, create the underlying widgets, etc. 69 void Init(); 70 71 // OmniboxView: 72 virtual void SaveStateToTab(content::WebContents* tab) OVERRIDE; 73 virtual void Update( 74 const content::WebContents* tab_for_state_restoring) OVERRIDE; 75 virtual string16 GetText() const OVERRIDE; 76 virtual void SetWindowTextAndCaretPos(const string16& text, 77 size_t caret_pos, 78 bool update_popup, 79 bool notify_text_changed) OVERRIDE; 80 virtual void SetForcedQuery() OVERRIDE; 81 virtual bool IsSelectAll() const OVERRIDE; 82 virtual bool DeleteAtEndPressed() OVERRIDE; 83 virtual void GetSelectionBounds(string16::size_type* start, 84 string16::size_type* end) const OVERRIDE; 85 virtual void SelectAll(bool reversed) OVERRIDE; 86 virtual void UpdatePopup() OVERRIDE; 87 virtual void SetFocus() OVERRIDE; 88 virtual void ApplyCaretVisibility() OVERRIDE; 89 virtual void OnTemporaryTextMaybeChanged( 90 const string16& display_text, 91 bool save_original_selection, 92 bool notify_text_changed) OVERRIDE; 93 virtual bool OnInlineAutocompleteTextMaybeChanged( 94 const string16& display_text, size_t user_text_length) OVERRIDE; 95 virtual void OnRevertTemporaryText() OVERRIDE; 96 virtual void OnBeforePossibleChange() OVERRIDE; 97 virtual bool OnAfterPossibleChange() OVERRIDE; 98 virtual gfx::NativeView GetNativeView() const OVERRIDE; 99 virtual gfx::NativeView GetRelativeWindowForPopup() const OVERRIDE; 100 virtual void SetGrayTextAutocompletion(const string16& suggestion) OVERRIDE; 101 virtual string16 GetGrayTextAutocompletion() const OVERRIDE; 102 virtual int TextWidth() const OVERRIDE; 103 virtual bool IsImeComposing() const OVERRIDE; 104 105 // Overridden from content::NotificationObserver: 106 virtual void Observe(int type, 107 const content::NotificationSource& source, 108 const content::NotificationDetails& details) OVERRIDE; 109 110 // Sets the colors of the text view according to the theme. 111 void SetBaseColor(); 112 // Sets the colors of the gray text suggestion view according to the theme. 113 void UpdateGrayTextViewColors(); 114 115 // Returns the text view gtk widget. May return NULL if the widget 116 // has already been destroyed. 117 GtkWidget* text_view() { 118 return text_view_; 119 } 120 121 private: 122 friend class OmniboxViewGtkTest; 123 124 CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleBeginUserAction, 125 GtkTextBuffer*); 126 CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleEndUserAction, GtkTextBuffer*); 127 CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSet, GtkTextBuffer*, 128 GtkTextIter*, GtkTextMark*); 129 // As above, but called after the default handler. 130 CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSetAfter, GtkTextBuffer*, 131 GtkTextIter*, GtkTextMark*); 132 CHROMEG_CALLBACK_3(OmniboxViewGtk, void, HandleInsertText, GtkTextBuffer*, 133 GtkTextIter*, const gchar*, gint); 134 CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleKeymapDirectionChanged, 135 GdkKeymap*); 136 CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleDeleteRange, GtkTextBuffer*, 137 GtkTextIter*, GtkTextIter*); 138 // Unlike above HandleMarkSet and HandleMarkSetAfter, this handler will always 139 // be connected to the signal. 140 CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSetAlways, GtkTextBuffer*, 141 GtkTextIter*, GtkTextMark*); 142 143 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleKeyPress, GdkEventKey*); 144 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleKeyRelease, 145 GdkEventKey*); 146 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewButtonPress, 147 GdkEventButton*); 148 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewButtonRelease, 149 GdkEventButton*); 150 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewFocusIn, 151 GdkEventFocus*); 152 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewFocusOut, 153 GdkEventFocus*); 154 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleViewMoveFocus, 155 GtkDirectionType); 156 CHROMEGTK_CALLBACK_3(OmniboxViewGtk, void, HandleViewMoveCursor, 157 GtkMovementStep, gint, gboolean); 158 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleViewSizeRequest, 159 GtkRequisition*); 160 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandlePopulatePopup, GtkMenu*); 161 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleEditSearchEngines); 162 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePasteAndGo); 163 CHROMEGTK_CALLBACK_6(OmniboxViewGtk, void, HandleDragDataReceived, 164 GdkDragContext*, gint, gint, GtkSelectionData*, 165 guint, guint); 166 CHROMEGTK_CALLBACK_4(OmniboxViewGtk, void, HandleDragDataGet, 167 GdkDragContext*, GtkSelectionData*, guint, guint); 168 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleDragBegin, 169 GdkDragContext*); 170 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleDragEnd, 171 GdkDragContext*); 172 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleBackSpace); 173 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCopyClipboard); 174 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCopyURLClipboard); 175 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCutClipboard); 176 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePasteClipboard); 177 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleExposeEvent, 178 GdkEventExpose*); 179 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleWidgetDirectionChanged, 180 GtkTextDirection); 181 CHROMEGTK_CALLBACK_2(OmniboxViewGtk, void, HandleDeleteFromCursor, 182 GtkDeleteType, gint); 183 // We connect to this so we can determine our toplevel window, so we can 184 // listen to focus change events on it. 185 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleHierarchyChanged, 186 GtkWidget*); 187 CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandlePreEditChanged, 188 const gchar*); 189 // Undo/redo operations won't trigger "begin-user-action" and 190 // "end-user-action" signals, so we need to hook into "undo" and "redo" 191 // signals and call OnBeforePossibleChange()/OnAfterPossibleChange() by 192 // ourselves. 193 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleUndoRedo); 194 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleUndoRedoAfter); 195 196 CHROMEG_CALLBACK_1(OmniboxViewGtk, void, HandleWindowSetFocus, 197 GtkWindow*, GtkWidget*); 198 199 // Callback function called after context menu is closed. 200 CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePopupMenuDeactivate); 201 202 // Callback for the PRIMARY selection clipboard. 203 static void ClipboardGetSelectionThunk(GtkClipboard* clipboard, 204 GtkSelectionData* selection_data, 205 guint info, 206 gpointer object); 207 void ClipboardGetSelection(GtkClipboard* clipboard, 208 GtkSelectionData* selection_data, 209 guint info); 210 211 void HandleCopyOrCutClipboard(bool copy); 212 213 // OmniboxView overrides. 214 virtual int GetOmniboxTextLength() const OVERRIDE; 215 virtual void EmphasizeURLComponents() OVERRIDE; 216 217 // Common implementation for performing a drop on the edit view. 218 bool OnPerformDropImpl(const string16& text); 219 220 // Returns the font used in |text_view_|. 221 gfx::Font GetFont(); 222 223 // Take control of the PRIMARY selection clipboard with |text|. Use 224 // |text_buffer_| as the owner, so that this doesn't remove the selection on 225 // it. This makes use of the above callbacks. 226 void OwnPrimarySelection(const std::string& text); 227 228 // Gets the GTK_TEXT_WINDOW_WIDGET coordinates for |text_view_| that bound the 229 // given iters. 230 gfx::Rect WindowBoundsFromIters(GtkTextIter* iter1, GtkTextIter* iter2); 231 232 // Actual implementation of SelectAll(), but also provides control over 233 // whether the PRIMARY selection is set to the selected text (in SelectAll(), 234 // it isn't, but we want set the selection when the user clicks in the entry). 235 void SelectAllInternal(bool reversed, bool update_primary_selection); 236 237 // Get ready to update |text_buffer_|'s highlighting without making changes to 238 // the PRIMARY selection. Removes the clipboard from |text_buffer_| and 239 // blocks the "mark-set" signal handler. 240 void StartUpdatingHighlightedText(); 241 242 // Finish updating |text_buffer_|'s highlighting such that future changes will 243 // automatically update the PRIMARY selection. Undoes 244 // StartUpdatingHighlightedText()'s changes. 245 void FinishUpdatingHighlightedText(); 246 247 // Get the character indices of the current selection. This honors 248 // direction, cp_max is the insertion point, and cp_min is the bound. 249 CharRange GetSelection() const; 250 251 // Translate from character positions to iterators for the current buffer. 252 void ItersFromCharRange(const CharRange& range, 253 GtkTextIter* iter_min, 254 GtkTextIter* iter_max); 255 256 // Returns true if the caret is at the end of the content. 257 bool IsCaretAtEnd() const; 258 259 // Save |selected_text| as the PRIMARY X selection. Unlike 260 // OwnPrimarySelection(), this won't set an owner or use callbacks. 261 void SavePrimarySelection(const std::string& selected_text); 262 263 // Update the field with |text| and set the selection. 264 void SetTextAndSelectedRange(const string16& text, 265 const CharRange& range); 266 267 // Set the selection to |range|. 268 void SetSelectedRange(const CharRange& range); 269 270 // Adjust the text justification according to the text direction of the widget 271 // and |text_buffer_|'s content, to make sure the real text justification is 272 // always in sync with the UI language direction. 273 void AdjustTextJustification(); 274 275 // Get the text direction of |text_buffer_|'s content, by searching the first 276 // character that has a strong direction. 277 PangoDirection GetContentDirection(); 278 279 // Returns the selected text. 280 std::string GetSelectedText() const; 281 282 // If the selected text parses as a URL OwnPrimarySelection is invoked. 283 void UpdatePrimarySelectionIfValidURL(); 284 285 // Retrieves the first and last iterators in the |text_buffer_|, but excludes 286 // the anchor holding the |gray_text_view_| widget. 287 void GetTextBufferBounds(GtkTextIter* start, GtkTextIter* end) const; 288 289 // Validates an iterator in the |text_buffer_|, to make sure it doesn't go 290 // beyond the anchor for holding the |gray_text_view_| widget. 291 void ValidateTextBufferIter(GtkTextIter* iter) const; 292 293 // Adjusts vertical alignment of the |gray_text_view_| in the |text_view_|, to 294 // make sure they have the same baseline. 295 void AdjustVerticalAlignmentOfGrayTextView(); 296 297 // The Browser that contains this omnibox. 298 Browser* browser_; 299 300 // The widget we expose, used for vertically centering the real text edit, 301 // since the height will change based on the font / font size, etc. 302 ui::OwnedWidgetGtk alignment_; 303 304 // The actual text entry which will be owned by the alignment_. The 305 // reference will be set to NULL upon destruction to tell if the gtk 306 // widget tree has been destroyed. This is because gtk destroies child 307 // widgets if the parent (alignemtn_)'s refcount does not go down to 0. 308 GtkWidget* text_view_; 309 310 GtkTextTagTable* tag_table_; 311 GtkTextBuffer* text_buffer_; 312 GtkTextTag* faded_text_tag_; 313 GtkTextTag* secure_scheme_tag_; 314 GtkTextTag* security_error_scheme_tag_; 315 GtkTextTag* normal_text_tag_; 316 317 // Objects for the gray suggestion text view. 318 GtkTextTag* gray_text_anchor_tag_; 319 320 // A widget for displaying gray autocompletion text. It'll be attached to a 321 // child anchor in the |text_buffer_| object. 322 GtkWidget* gray_text_view_; 323 324 // A mark to split the content and the gray text anchor. Wherever the end 325 // iterator of the text buffer is required, the iterator to this mark should 326 // be used. 327 GtkTextMark* gray_text_mark_; 328 329 scoped_ptr<OmniboxPopupView> popup_view_; 330 331 // When true, the location bar view is read only and also is has a slightly 332 // different presentation (smaller font size). This is used for popups. 333 bool popup_window_mode_; 334 335 ToolbarModel::SecurityLevel security_level_; 336 337 // Selection at the point where the user started using the 338 // arrows to move around in the popup. 339 CharRange saved_temporary_selection_; 340 341 // Tracking state before and after a possible change. 342 string16 text_before_change_; 343 CharRange sel_before_change_; 344 345 // The most-recently-selected text from the entry that was copied to the 346 // clipboard. This is updated on-the-fly as the user selects text. This may 347 // differ from the actual selected text, such as when 'http://' is prefixed to 348 // the text. It is used in cases where we need to make the PRIMARY selection 349 // persist even after the user has unhighlighted the text in the view 350 // (e.g. when they highlight some text and then click to unhighlight it, we 351 // pass this string to SavePrimarySelection()). 352 std::string selected_text_; 353 354 std::string dragged_text_; 355 // When we own the X clipboard, this is the text for it. 356 std::string primary_selection_text_; 357 358 // IDs of the signal handlers for "mark-set" on |text_buffer_|. 359 gulong mark_set_handler_id_; 360 gulong mark_set_handler_id2_; 361 362 // Is the first mouse button currently down? When selection marks get moved, 363 // we use this to determine if the user was highlighting text with the mouse 364 // -- if so, we avoid selecting all the text on mouse-up. 365 bool button_1_pressed_; 366 367 // Supplies colors, et cetera. 368 GtkThemeService* theme_service_; 369 370 content::NotificationRegistrar registrar_; 371 372 // Indicates if Enter key was pressed. 373 // 374 // It's used in the key press handler to detect an Enter key press event 375 // during sync dispatch of "end-user-action" signal so that an unexpected 376 // change caused by the event can be ignored in OnAfterPossibleChange(). 377 bool enter_was_pressed_; 378 379 // Indicates if Tab key was pressed. 380 // 381 // It's only used in the key press handler to detect a Tab key press event 382 // during sync dispatch of "move-focus" signal. 383 bool tab_was_pressed_; 384 385 // Indicates if Shift key was pressed. 386 // Used in conjunction with the Tab key to determine if either traversal 387 // needs to move up the results or if the keyword needs to be cleared. 388 bool shift_was_pressed_; 389 390 // Indicates that user requested to paste clipboard. 391 // The actual paste clipboard action might be performed later if the 392 // clipboard is not empty. 393 bool paste_clipboard_requested_; 394 395 // Text to "Paste and go"; set by HandlePopulatePopup() and consumed by 396 // HandlePasteAndGo(). 397 string16 sanitized_text_for_paste_and_go_; 398 399 // Indicates if an Enter key press is inserted as text. 400 // It's used in the key press handler to determine if an Enter key event is 401 // handled by IME or not. 402 bool enter_was_inserted_; 403 404 // Indicates whether the IME changed the text. It's possible for the IME to 405 // handle a key event but not change the text contents (e.g., when pressing 406 // shift+del with no selection). 407 bool text_changed_; 408 409 // Contains the character range that should have a strikethrough (used for 410 // insecure schemes). If the range is size one or less, no strikethrough 411 // is needed. 412 CharRange strikethrough_; 413 414 // Indicates if the selected text is suggested text or not. If the selection 415 // is not suggested text, that means the user manually made the selection. 416 bool selection_suggested_; 417 418 // Was delete pressed? 419 bool delete_was_pressed_; 420 421 // Was the delete key pressed with an empty selection at the end of the edit? 422 bool delete_at_end_pressed_; 423 424 // Indicates if we are handling a key press event. 425 bool handling_key_press_; 426 427 // Indicates if omnibox's content maybe changed by a key press event, so that 428 // we need to call OnAfterPossibleChange() after handling the event. 429 // This flag should be set for changes directly caused by a key press event, 430 // including changes to content text, selection range and pre-edit string. 431 // Changes caused by function calls like SetUserText() should not affect this 432 // flag. 433 bool content_maybe_changed_by_key_press_; 434 435 // Set this flag to call UpdatePopup() in lost focus and need to update. 436 // Because context menu might take the focus, before setting the flag, check 437 // the focus with model_->has_focus(). 438 bool update_popup_without_focus_; 439 440 // On GTK 2.20+ |pre_edit_| and |pre_edit_size_before_change_| will be used. 441 const bool supports_pre_edit_; 442 443 // Stores the text being composed by the input method. 444 string16 pre_edit_; 445 446 // Tracking pre-edit state before and after a possible change. We don't need 447 // to track pre-edit_'s content, as it'll be treated as part of text content. 448 size_t pre_edit_size_before_change_; 449 450 // The view that is going to be focused next. Only valid while handling 451 // "focus-out" events. 452 GtkWidget* going_to_focus_; 453 454 ui::GtkSignalRegistrar signals_; 455 456 DISALLOW_COPY_AND_ASSIGN(OmniboxViewGtk); 457 }; 458 459 #endif // CHROME_BROWSER_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_ 460