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
26class Browser;
27class OmniboxPopupView;
28class Profile;
29
30namespace gfx {
31class Font;
32}
33
34class GtkThemeService;
35
36class 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