// Copyright (c) 2011 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_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_GTK_H_
#define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_GTK_H_
#pragma once
#include <gtk/gtk.h>
#include <algorithm>
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "chrome/browser/autocomplete/autocomplete_edit_view.h"
#include "chrome/browser/ui/gtk/owned_widget_gtk.h"
#include "chrome/browser/ui/toolbar/toolbar_model.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
#include "content/common/page_transition_types.h"
#include "ui/base/animation/animation_delegate.h"
#include "ui/base/gtk/gtk_signal.h"
#include "ui/base/gtk/gtk_signal_registrar.h"
#include "ui/gfx/rect.h"
#include "webkit/glue/window_open_disposition.h"
class AutocompleteEditController;
class AutocompleteEditModel;
class AutocompletePopupView;
class Profile;
class TabContents;
namespace gfx {
class Font;
}
namespace ui {
class MultiAnimation;
}
namespace views {
class View;
}
#if !defined(TOOLKIT_VIEWS)
class GtkThemeService;
#endif
class AutocompleteEditViewGtk : public AutocompleteEditView,
public NotificationObserver,
public ui::AnimationDelegate {
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).
};
AutocompleteEditViewGtk(AutocompleteEditController* controller,
ToolbarModel* toolbar_model,
Profile* profile,
CommandUpdater* command_updater,
bool popup_window_mode,
#if defined(TOOLKIT_VIEWS)
views::View* location_bar
#else
GtkWidget* location_bar
#endif
);
virtual ~AutocompleteEditViewGtk();
// Initialize, create the underlying widgets, etc.
void Init();
// Returns the width in pixels needed to display the text from one character
// before the caret to the end of the string. See comments in
// LocationBarView::Layout as to why this uses -1.
int WidthOfTextAfterCursor();
// Implement the AutocompleteEditView interface.
virtual AutocompleteEditModel* model();
virtual const AutocompleteEditModel* model() const;
virtual void SaveStateToTab(TabContents* tab);
virtual void Update(const TabContents* tab_for_state_restoring);
virtual void OpenURL(const GURL& url,
WindowOpenDisposition disposition,
PageTransition::Type transition,
const GURL& alternate_nav_url,
size_t selected_line,
const string16& keyword);
virtual string16 GetText() const;
virtual bool IsEditingOrEmpty() const;
virtual int GetIcon() const;
virtual void SetUserText(const string16& text);
virtual void SetUserText(const string16& text,
const string16& display_text,
bool update_popup);
virtual void SetWindowTextAndCaretPos(const string16& text,
size_t caret_pos);
virtual void SetForcedQuery();
virtual bool IsSelectAll();
virtual bool DeleteAtEndPressed();
virtual void GetSelectionBounds(string16::size_type* start,
string16::size_type* end);
virtual void SelectAll(bool reversed);
virtual void RevertAll();
virtual void UpdatePopup();
virtual void ClosePopup();
virtual void SetFocus();
virtual void OnTemporaryTextMaybeChanged(const string16& display_text,
bool save_original_selection);
virtual bool OnInlineAutocompleteTextMaybeChanged(
const string16& display_text, size_t user_text_length);
virtual void OnRevertTemporaryText();
virtual void OnBeforePossibleChange();
virtual bool OnAfterPossibleChange();
virtual gfx::NativeView GetNativeView() const;
virtual CommandUpdater* GetCommandUpdater();
virtual void SetInstantSuggestion(const string16& suggestion,
bool animate_to_complete);
virtual string16 GetInstantSuggestion() const;
virtual int TextWidth() const;
virtual bool IsImeComposing() const;
#if defined(TOOLKIT_VIEWS)
virtual views::View* AddToView(views::View* parent);
virtual int OnPerformDrop(const views::DropTargetEvent& event);
// A factory method to create an AutocompleteEditView instance initialized for
// linux_views. This currently returns an instance of
// AutocompleteEditViewGtk only, but AutocompleteEditViewViews will
// be added as an option when TextfieldViews is enabled.
static AutocompleteEditView* Create(AutocompleteEditController* controller,
ToolbarModel* toolbar_model,
Profile* profile,
CommandUpdater* command_updater,
bool popup_window_mode,
views::View* location_bar);
#endif
// Overridden from NotificationObserver:
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details);
// Overridden from ui::AnimationDelegate.
virtual void AnimationEnded(const ui::Animation* animation);
virtual void AnimationProgressed(const ui::Animation* animation);
virtual void AnimationCanceled(const ui::Animation* animation);
// Sets the colors of the text view according to the theme.
void SetBaseColor();
// Sets the colors of the instant suggestion view according to the theme and
// the animation state.
void UpdateInstantViewColors();
// Returns the text view gtk widget. May return NULL if the widget
// has already been destroyed.
GtkWidget* text_view() {
return text_view_;
}
private:
CHROMEG_CALLBACK_0(AutocompleteEditViewGtk, void, HandleBeginUserAction,
GtkTextBuffer*);
CHROMEG_CALLBACK_0(AutocompleteEditViewGtk, void, HandleEndUserAction,
GtkTextBuffer*);
CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleMarkSet,
GtkTextBuffer*, GtkTextIter*, GtkTextMark*);
// As above, but called after the default handler.
CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleMarkSetAfter,
GtkTextBuffer*, GtkTextIter*, GtkTextMark*);
CHROMEG_CALLBACK_3(AutocompleteEditViewGtk, void, HandleInsertText,
GtkTextBuffer*, GtkTextIter*, const gchar*, gint);
CHROMEG_CALLBACK_0(AutocompleteEditViewGtk, void,
HandleKeymapDirectionChanged, GdkKeymap*);
CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleDeleteRange,
GtkTextBuffer*, GtkTextIter*, GtkTextIter*);
// Unlike above HandleMarkSet and HandleMarkSetAfter, this handler will always
// be connected to the signal.
CHROMEG_CALLBACK_2(AutocompleteEditViewGtk, void, HandleMarkSetAlways,
GtkTextBuffer*, GtkTextIter*, GtkTextMark*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleKeyPress,
GdkEventKey*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleKeyRelease,
GdkEventKey*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleViewButtonPress,
GdkEventButton*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean,
HandleViewButtonRelease, GdkEventButton*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleViewFocusIn,
GdkEventFocus*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleViewFocusOut,
GdkEventFocus*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandleViewMoveFocus,
GtkDirectionType);
CHROMEGTK_CALLBACK_3(AutocompleteEditViewGtk, void, HandleViewMoveCursor,
GtkMovementStep, gint, gboolean);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandleViewSizeRequest,
GtkRequisition*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandlePopulatePopup,
GtkMenu*);
CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleEditSearchEngines);
CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandlePasteAndGo);
CHROMEGTK_CALLBACK_6(AutocompleteEditViewGtk, void, HandleDragDataReceived,
GdkDragContext*, gint, gint, GtkSelectionData*,
guint, guint);
CHROMEGTK_CALLBACK_4(AutocompleteEditViewGtk, void, HandleDragDataGet,
GdkDragContext*, GtkSelectionData*, guint, guint);
CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleBackSpace);
CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleCopyClipboard);
CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleCutClipboard);
CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandlePasteClipboard);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, gboolean, HandleExposeEvent,
GdkEventExpose*);
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void,
HandleWidgetDirectionChanged, GtkTextDirection);
CHROMEGTK_CALLBACK_2(AutocompleteEditViewGtk, 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(AutocompleteEditViewGtk, void, HandleHierarchyChanged,
GtkWidget*);
#if GTK_CHECK_VERSION(2, 20, 0)
CHROMEGTK_CALLBACK_1(AutocompleteEditViewGtk, void, HandlePreeditChanged,
const gchar*);
#endif
// 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(AutocompleteEditViewGtk, void, HandleUndoRedo);
CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, void, HandleUndoRedoAfter);
CHROMEG_CALLBACK_1(AutocompleteEditViewGtk, void, HandleWindowSetFocus,
GtkWindow*, GtkWidget*);
// Callback function called after context menu is closed.
CHROMEGTK_CALLBACK_0(AutocompleteEditViewGtk, 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);
// Common implementation for performing a drop on the edit view.
bool OnPerformDropImpl(const string16& text);
// 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);
// Return the number of characers in the current buffer.
int GetTextLength() const;
// Places the caret at the given position. This clears any selection.
void PlaceCaretAt(int pos);
// Returns true if the caret is at the end of the content.
bool IsCaretAtEnd() const;
// Try to parse the current text as a URL and colorize the components.
void EmphasizeURLComponents();
// Internally invoked whenever the text changes in some way.
void TextChanged();
// 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 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 |instant_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 |instant_view_| widget.
void ValidateTextBufferIter(GtkTextIter* iter) const;
// Adjusts vertical alignment of the |instant_view_| in the |text_view_|, to
// make sure they have the same baseline.
void AdjustVerticalAlignmentOfInstantView();
// Stop showing the instant suggest auto-commit animation.
void StopAnimation();
// The widget we expose, used for vertically centering the real text edit,
// since the height will change based on the font / font size, etc.
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 instant suggestion text view.
GtkTextTag* instant_anchor_tag_;
// A widget for displaying instant suggestion text. It'll be attached to a
// child anchor in the |text_buffer_| object.
GtkWidget* instant_view_;
// Animation from instant suggest (faded text) to autocomplete (selected
// text).
scoped_ptr<ui::MultiAnimation> instant_animation_;
// A mark to split the content and the instant anchor. Wherever the end
// iterator of the text buffer is required, the iterator to this mark should
// be used.
GtkTextMark* instant_mark_;
scoped_ptr<AutocompleteEditModel> model_;
scoped_ptr<AutocompletePopupView> popup_view_;
AutocompleteEditController* controller_;
ToolbarModel* toolbar_model_;
// The object that handles additional command functionality exposed on the
// edit, such as invoking the keyword editor.
CommandUpdater* command_updater_;
// 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.
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_;
// 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_;
#if defined(OS_CHROMEOS)
// The following variables are used to implement select-all-on-mouse-up, which
// is disabled in the standard Linux build due to poor interaction with the
// PRIMARY X selection.
// 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_;
// Did the user change the selected text in the middle of the current click?
// If so, we don't select all of the text when the button is released -- we
// don't want to blow away their selection.
bool text_selected_during_click_;
// Was the text view already focused before the user clicked in it? We use
// this to figure out whether we should select all of the text when the button
// is released (we only do so if the view was initially unfocused).
bool text_view_focused_before_button_press_;
#endif
#if defined(TOOLKIT_VIEWS)
views::View* location_bar_view_;
#else
// Supplies colors, et cetera.
GtkThemeService* theme_service_;
NotificationRegistrar registrar_;
#endif
// 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 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_;
// 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 preedit 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_;
#if GTK_CHECK_VERSION(2, 20, 0)
// Stores the text being composed by the input method.
string16 preedit_;
// Tracking preedit state before and after a possible change. We don't need to
// track preedit_'s content, as it'll be treated as part of text content.
size_t preedit_size_before_change_;
#endif
// The view that is going to be focused next. Only valid while handling
// "focus-out" events.
GtkWidget* going_to_focus_;
ui::GtkSignalRegistrar signals_;
DISALLOW_COPY_AND_ASSIGN(AutocompleteEditViewGtk);
};
#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_EDIT_VIEW_GTK_H_