root/chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_
#define CHROME_BROWSER_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_

#include <gtk/gtk.h>

#include <algorithm>
#include <string>

#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "chrome/browser/ui/omnibox/omnibox_view.h"
#include "chrome/browser/ui/toolbar/toolbar_model.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "ui/base/gtk/gtk_signal.h"
#include "ui/base/gtk/gtk_signal_registrar.h"
#include "ui/base/gtk/owned_widget_gtk.h"
#include "ui/base/window_open_disposition.h"
#include "ui/gfx/rect.h"

class Browser;
class OmniboxPopupView;
class Profile;

namespace gfx {
class Font;
}

class GtkThemeService;

class OmniboxViewGtk : public OmniboxView,
                       public content::NotificationObserver {
 public:
  // Modeled like the Windows CHARRANGE.  Represent a pair of cursor position
  // offsets.  Since GtkTextIters are invalid after the buffer is changed, we
  // work in character offsets (not bytes).
  struct CharRange {
    CharRange() : cp_min(0), cp_max(0) { }
    CharRange(int n, int x) : cp_min(n), cp_max(x) { }

    // Returns the start/end of the selection.
    int selection_min() const { return std::min(cp_min, cp_max); }
    int selection_max() const { return std::max(cp_min, cp_max); }

    // Work in integers to match the gint GTK APIs.
    int cp_min;  // For a selection: Represents the start.
    int cp_max;  // For a selection: Represents the end (insert position).
  };

  // profile parameter is introduced for unittests which can not instantiate
  // browser object and pass NULL to the browser parameter.
  // In other use case, you should pass browser->profile() object as
  // profile parameter.
  OmniboxViewGtk(OmniboxEditController* controller,
                 Browser* browser,
                 Profile* profile,
                 CommandUpdater* command_updater,
                 bool popup_window_mode,
                 GtkWidget* location_bar);
  virtual ~OmniboxViewGtk();

  // Initialize, create the underlying widgets, etc.
  void Init();

  // OmniboxView:
  virtual void SaveStateToTab(content::WebContents* tab) OVERRIDE;
  virtual void OnTabChanged(const content::WebContents* web_contents) OVERRIDE;
  virtual void Update() OVERRIDE;
  virtual base::string16 GetText() const OVERRIDE;
  virtual void SetWindowTextAndCaretPos(const base::string16& text,
                                        size_t caret_pos,
                                        bool update_popup,
                                        bool notify_text_changed) OVERRIDE;
  virtual void SetForcedQuery() OVERRIDE;
  virtual bool IsSelectAll() const OVERRIDE;
  virtual bool DeleteAtEndPressed() OVERRIDE;
  virtual void GetSelectionBounds(
      base::string16::size_type* start,
      base::string16::size_type* end) const OVERRIDE;
  virtual void SelectAll(bool reversed) OVERRIDE;
  virtual void UpdatePopup() OVERRIDE;
  virtual void SetFocus() OVERRIDE;
  virtual void ApplyCaretVisibility() OVERRIDE;
  virtual void OnTemporaryTextMaybeChanged(
      const base::string16& display_text,
      bool save_original_selection,
      bool notify_text_changed) OVERRIDE;
  virtual bool OnInlineAutocompleteTextMaybeChanged(
      const base::string16& display_text, size_t user_text_length) OVERRIDE;
  virtual void OnInlineAutocompleteTextCleared() OVERRIDE;
  virtual void OnRevertTemporaryText() OVERRIDE;
  virtual void OnBeforePossibleChange() OVERRIDE;
  virtual bool OnAfterPossibleChange() OVERRIDE;
  virtual gfx::NativeView GetNativeView() const OVERRIDE;
  virtual gfx::NativeView GetRelativeWindowForPopup() const OVERRIDE;
  virtual void SetGrayTextAutocompletion(
      const base::string16& suggestion) OVERRIDE;
  virtual base::string16 GetGrayTextAutocompletion() const OVERRIDE;
  virtual int GetTextWidth() const OVERRIDE;
  virtual int GetWidth() const OVERRIDE;
  virtual bool IsImeComposing() const OVERRIDE;

  // Overridden from content::NotificationObserver:
  virtual void Observe(int type,
                       const content::NotificationSource& source,
                       const content::NotificationDetails& details) OVERRIDE;

  // Sets the colors of the gray text suggestion view according to the theme.
  void UpdateGrayTextViewColors();

  // Returns the text view gtk widget. May return NULL if the widget
  // has already been destroyed.
  GtkWidget* text_view() {
    return text_view_;
  }

 private:
  friend class OmniboxViewGtkTest;

  CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleBeginUserAction,
                     GtkTextBuffer*);
  CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleEndUserAction, GtkTextBuffer*);
  CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSet, GtkTextBuffer*,
                     GtkTextIter*, GtkTextMark*);
  // As above, but called after the default handler.
  CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSetAfter, GtkTextBuffer*,
                     GtkTextIter*, GtkTextMark*);
  CHROMEG_CALLBACK_3(OmniboxViewGtk, void, HandleInsertText, GtkTextBuffer*,
                     GtkTextIter*, const gchar*, gint);
  CHROMEG_CALLBACK_0(OmniboxViewGtk, void, HandleKeymapDirectionChanged,
                     GdkKeymap*);
  CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleDeleteRange, GtkTextBuffer*,
                     GtkTextIter*, GtkTextIter*);
  // Unlike above HandleMarkSet and HandleMarkSetAfter, this handler will always
  // be connected to the signal.
  CHROMEG_CALLBACK_2(OmniboxViewGtk, void, HandleMarkSetAlways, GtkTextBuffer*,
                     GtkTextIter*, GtkTextMark*);

  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleKeyPress, GdkEventKey*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleKeyRelease,
                       GdkEventKey*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewButtonPress,
                       GdkEventButton*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewButtonRelease,
                       GdkEventButton*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewFocusIn,
                       GdkEventFocus*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleViewFocusOut,
                       GdkEventFocus*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleViewMoveFocus,
                       GtkDirectionType);
  CHROMEGTK_CALLBACK_3(OmniboxViewGtk, void, HandleViewMoveCursor,
                       GtkMovementStep, gint, gboolean);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleViewSizeRequest,
                       GtkRequisition*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandlePopulatePopup, GtkMenu*);
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePasteAndGo);
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleShowURL);
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleEditSearchEngines);
  CHROMEGTK_CALLBACK_6(OmniboxViewGtk, void, HandleDragDataReceived,
                       GdkDragContext*, gint, gint, GtkSelectionData*,
                       guint, guint);
  CHROMEGTK_CALLBACK_4(OmniboxViewGtk, void, HandleDragDataGet,
                       GdkDragContext*, GtkSelectionData*, guint, guint);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleDragBegin,
                       GdkDragContext*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleDragEnd,
                       GdkDragContext*);
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleBackSpace);
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCopyClipboard);
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleCutClipboard);
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePasteClipboard);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleExposeEvent,
                       GdkEventExpose*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, gboolean, HandleExposeEventAfter,
                       GdkEventExpose*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleWidgetDirectionChanged,
                       GtkTextDirection);
  CHROMEGTK_CALLBACK_2(OmniboxViewGtk, void, HandleDeleteFromCursor,
                       GtkDeleteType, gint);
  // We connect to this so we can determine our toplevel window, so we can
  // listen to focus change events on it.
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandleHierarchyChanged,
                       GtkWidget*);
  CHROMEGTK_CALLBACK_1(OmniboxViewGtk, void, HandlePreEditChanged,
                       const gchar*);
  // Undo/redo operations won't trigger "begin-user-action" and
  // "end-user-action" signals, so we need to hook into "undo" and "redo"
  // signals and call OnBeforePossibleChange()/OnAfterPossibleChange() by
  // ourselves.
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleUndoRedo);
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandleUndoRedoAfter);

  CHROMEG_CALLBACK_1(OmniboxViewGtk, void, HandleWindowSetFocus,
                     GtkWindow*, GtkWidget*);

  // Callback function called after context menu is closed.
  CHROMEGTK_CALLBACK_0(OmniboxViewGtk, void, HandlePopupMenuDeactivate);

  // Callback for the PRIMARY selection clipboard.
  static void ClipboardGetSelectionThunk(GtkClipboard* clipboard,
                                         GtkSelectionData* selection_data,
                                         guint info,
                                         gpointer object);
  void ClipboardGetSelection(GtkClipboard* clipboard,
                             GtkSelectionData* selection_data,
                             guint info);

  void HandleCopyOrCutClipboard(bool copy);

  // OmniboxView overrides.
  virtual int GetOmniboxTextLength() const OVERRIDE;
  virtual void EmphasizeURLComponents() OVERRIDE;

  // Common implementation for performing a drop on the edit view.
  bool OnPerformDropImpl(const base::string16& text);

  // Sets the colors and font of the text view according to the theme.
  void OnBrowserThemeChanged();

  // Returns the font used in |text_view_|.
  gfx::Font GetFont();

  // Take control of the PRIMARY selection clipboard with |text|. Use
  // |text_buffer_| as the owner, so that this doesn't remove the selection on
  // it. This makes use of the above callbacks.
  void OwnPrimarySelection(const std::string& text);

  // Gets the GTK_TEXT_WINDOW_WIDGET coordinates for |text_view_| that bound the
  // given iters.
  gfx::Rect WindowBoundsFromIters(GtkTextIter* iter1, GtkTextIter* iter2);

  // Actual implementation of SelectAll(), but also provides control over
  // whether the PRIMARY selection is set to the selected text (in SelectAll(),
  // it isn't, but we want set the selection when the user clicks in the entry).
  void SelectAllInternal(bool reversed, bool update_primary_selection);

  // Get ready to update |text_buffer_|'s highlighting without making changes to
  // the PRIMARY selection.  Removes the clipboard from |text_buffer_| and
  // blocks the "mark-set" signal handler.
  void StartUpdatingHighlightedText();

  // Finish updating |text_buffer_|'s highlighting such that future changes will
  // automatically update the PRIMARY selection.  Undoes
  // StartUpdatingHighlightedText()'s changes.
  void FinishUpdatingHighlightedText();

  // Get the character indices of the current selection.  This honors
  // direction, cp_max is the insertion point, and cp_min is the bound.
  CharRange GetSelection() const;

  // Translate from character positions to iterators for the current buffer.
  void ItersFromCharRange(const CharRange& range,
                          GtkTextIter* iter_min,
                          GtkTextIter* iter_max);

  // Returns true if the caret is at the end of the content.
  bool IsCaretAtEnd() const;

  // Save |selected_text| as the PRIMARY X selection. Unlike
  // OwnPrimarySelection(), this won't set an owner or use callbacks.
  void SavePrimarySelection(const std::string& selected_text);

  // Update the field with |text| and set the selection.
  void SetTextAndSelectedRange(const base::string16& text,
                               const CharRange& range);

  // Set the selection to |range|.
  void SetSelectedRange(const CharRange& range);

  // Adjust the text justification according to the text direction of the widget
  // and |text_buffer_|'s content, to make sure the real text justification is
  // always in sync with the UI language direction.
  void AdjustTextJustification();

  // Get the text direction of |text_buffer_|'s content, by searching the first
  // character that has a strong direction.
  PangoDirection GetContentDirection();

  // Returns the selected text.
  std::string GetSelectedText() const;

  // If the selected text parses as a URL OwnPrimarySelection is invoked.
  void UpdatePrimarySelectionIfValidURL();

  // Retrieves the first and last iterators in the |text_buffer_|, but excludes
  // the anchor holding the |gray_text_view_| widget.
  void GetTextBufferBounds(GtkTextIter* start, GtkTextIter* end) const;

  // Validates an iterator in the |text_buffer_|, to make sure it doesn't go
  // beyond the anchor for holding the |gray_text_view_| widget.
  void ValidateTextBufferIter(GtkTextIter* iter) const;

  // Adjusts vertical alignment of the |gray_text_view_| in the |text_view_|, to
  // make sure they have the same baseline.
  void AdjustVerticalAlignmentOfGrayTextView();

  // The Browser that contains this omnibox.
  Browser* browser_;

  // The widget we expose, used for vertically centering the real text edit,
  // since the height will change based on the font / font size, etc.
  ui::OwnedWidgetGtk alignment_;

  // The actual text entry which will be owned by the alignment_.  The
  // reference will be set to NULL upon destruction to tell if the gtk
  // widget tree has been destroyed. This is because gtk destroies child
  // widgets if the parent (alignemtn_)'s refcount does not go down to 0.
  GtkWidget* text_view_;

  GtkTextTagTable* tag_table_;
  GtkTextBuffer* text_buffer_;
  GtkTextTag* faded_text_tag_;
  GtkTextTag* secure_scheme_tag_;
  GtkTextTag* security_error_scheme_tag_;
  GtkTextTag* normal_text_tag_;

  // Objects for the gray suggestion text view.
  GtkTextTag* gray_text_anchor_tag_;

  // A widget for displaying gray autocompletion text. It'll be attached to a
  // child anchor in the |text_buffer_| object.
  GtkWidget* gray_text_view_;

  // A mark to split the content and the gray text anchor. Wherever the end
  // iterator of the text buffer is required, the iterator to this mark should
  // be used.
  GtkTextMark* gray_text_mark_;

  scoped_ptr<OmniboxPopupView> popup_view_;

  // When true, the location bar view is read only and also is has a slightly
  // different presentation (smaller font size). This is used for popups.
  bool popup_window_mode_;

  ToolbarModel::SecurityLevel security_level_;

  // Selection at the point where the user started using the
  // arrows to move around in the popup.
  CharRange saved_temporary_selection_;

  // Tracking state before and after a possible change.
  base::string16 text_before_change_;
  CharRange sel_before_change_;

  // The most-recently-selected text from the entry that was copied to the
  // clipboard.  This is updated on-the-fly as the user selects text. This may
  // differ from the actual selected text, such as when 'http://' is prefixed to
  // the text.  It is used in cases where we need to make the PRIMARY selection
  // persist even after the user has unhighlighted the text in the view
  // (e.g. when they highlight some text and then click to unhighlight it, we
  // pass this string to SavePrimarySelection()).
  std::string selected_text_;

  std::string dragged_text_;
  // When we own the X clipboard, this is the text for it.
  std::string primary_selection_text_;

  // IDs of the signal handlers for "mark-set" on |text_buffer_|.
  gulong mark_set_handler_id_;
  gulong mark_set_handler_id2_;

  // Is the first mouse button currently down?  When selection marks get moved,
  // we use this to determine if the user was highlighting text with the mouse
  // -- if so, we avoid selecting all the text on mouse-up.
  bool button_1_pressed_;

  // Supplies colors, et cetera.
  GtkThemeService* theme_service_;

  content::NotificationRegistrar registrar_;

  // Indicates if Enter key was pressed.
  //
  // It's used in the key press handler to detect an Enter key press event
  // during sync dispatch of "end-user-action" signal so that an unexpected
  // change caused by the event can be ignored in OnAfterPossibleChange().
  bool enter_was_pressed_;

  // Indicates if Tab key was pressed.
  //
  // It's only used in the key press handler to detect a Tab key press event
  // during sync dispatch of "move-focus" signal.
  bool tab_was_pressed_;

  // Indicates if Shift key was pressed.
  // Used in conjunction with the Tab key to determine if either traversal
  // needs to move up the results or if the keyword needs to be cleared.
  bool shift_was_pressed_;

  // Indicates that user requested to paste clipboard.
  // The actual paste clipboard action might be performed later if the
  // clipboard is not empty.
  bool paste_clipboard_requested_;

  // Text to "Paste and go"; set by HandlePopulatePopup() and consumed by
  // HandlePasteAndGo().
  base::string16 sanitized_text_for_paste_and_go_;

  // Indicates if an Enter key press is inserted as text.
  // It's used in the key press handler to determine if an Enter key event is
  // handled by IME or not.
  bool enter_was_inserted_;

  // Indicates whether the IME changed the text.  It's possible for the IME to
  // handle a key event but not change the text contents (e.g., when pressing
  // shift+del with no selection).
  bool text_changed_;

  // Contains the character range that should have a strikethrough (used for
  // insecure schemes). If the range is size one or less, no strikethrough
  // is needed.
  CharRange strikethrough_;

  // Indicates if the selected text is suggested text or not. If the selection
  // is not suggested text, that means the user manually made the selection.
  bool selection_suggested_;

  // Was delete pressed?
  bool delete_was_pressed_;

  // Was the delete key pressed with an empty selection at the end of the edit?
  bool delete_at_end_pressed_;

  // Indicates if we are handling a key press event.
  bool handling_key_press_;

  // Indicates if omnibox's content maybe changed by a key press event, so that
  // we need to call OnAfterPossibleChange() after handling the event.
  // This flag should be set for changes directly caused by a key press event,
  // including changes to content text, selection range and pre-edit string.
  // Changes caused by function calls like SetUserText() should not affect this
  // flag.
  bool content_maybe_changed_by_key_press_;

  // Set this flag to call UpdatePopup() in lost focus and need to update.
  // Because context menu might take the focus, before setting the flag, check
  // the focus with model_->has_focus().
  bool update_popup_without_focus_;

  // On GTK 2.20+ |pre_edit_| and |pre_edit_size_before_change_| will be used.
  const bool supports_pre_edit_;

  // Stores the text being composed by the input method.
  base::string16 pre_edit_;

  // Tracking pre-edit state before and after a possible change. We don't need
  // to track pre-edit_'s content, as it'll be treated as part of text content.
  size_t pre_edit_size_before_change_;

  // The view that is going to be focused next. Only valid while handling
  // "focus-out" events.
  GtkWidget* going_to_focus_;

  ui::GtkSignalRegistrar signals_;

  // The baseline shift to be made to center the text.  Positive values move
  // the text upward.
  double font_baseline_shift_;

  DISALLOW_COPY_AND_ASSIGN(OmniboxViewGtk);
};

#endif  // CHROME_BROWSER_UI_GTK_OMNIBOX_OMNIBOX_VIEW_GTK_H_

/* [<][>][^][v][top][bottom][index][help] */