touch_editable_impl_aura.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2013 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#include "content/browser/web_contents/touch_editable_impl_aura.h"
6
7#include "content/browser/renderer_host/render_widget_host_impl.h"
8#include "content/browser/renderer_host/render_widget_host_view_aura.h"
9#include "content/common/view_messages.h"
10#include "content/public/browser/render_widget_host.h"
11#include "grit/ui_strings.h"
12#include "ui/aura/client/activation_client.h"
13#include "ui/aura/client/screen_position_client.h"
14#include "ui/aura/root_window.h"
15#include "ui/aura/window.h"
16#include "ui/base/clipboard/clipboard.h"
17#include "ui/base/range/range.h"
18#include "ui/base/ui_base_switches_util.h"
19
20namespace content {
21
22////////////////////////////////////////////////////////////////////////////////
23// TouchEditableImplAura, public:
24
25TouchEditableImplAura::~TouchEditableImplAura() {
26  Cleanup();
27}
28
29// static
30TouchEditableImplAura* TouchEditableImplAura::Create() {
31  if (switches::IsTouchEditingEnabled())
32    return new TouchEditableImplAura();
33  return NULL;
34}
35
36void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura* view) {
37  if (rwhva_ == view)
38    return;
39
40  Cleanup();
41  if (!view)
42    return;
43
44  rwhva_ = view;
45  rwhva_->set_touch_editing_client(this);
46}
47
48void TouchEditableImplAura::UpdateEditingController() {
49  if (!rwhva_)
50    return;
51
52  // If touch editing handles were not visible, we bring them up only if
53  // there is non-zero selection on the page. And the current event is a
54  // gesture event (we dont want to show handles if the user is selecting
55  // using mouse or keyboard).
56  if (selection_gesture_in_process_ &&
57      selection_anchor_rect_ != selection_focus_rect_)
58    StartTouchEditing();
59
60  if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE ||
61      selection_anchor_rect_ != selection_focus_rect_) {
62    if (touch_selection_controller_)
63      touch_selection_controller_->SelectionChanged();
64  } else {
65    EndTouchEditing();
66  }
67}
68
69////////////////////////////////////////////////////////////////////////////////
70// TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient
71// implementation:
72
73void TouchEditableImplAura::StartTouchEditing() {
74  if (!touch_selection_controller_) {
75    touch_selection_controller_.reset(
76        ui::TouchSelectionController::create(this));
77  }
78  if (touch_selection_controller_)
79    touch_selection_controller_->SelectionChanged();
80}
81
82void TouchEditableImplAura::EndTouchEditing() {
83  if (touch_selection_controller_) {
84    if (touch_selection_controller_->IsHandleDragInProgress())
85      touch_selection_controller_->SelectionChanged();
86    else
87      touch_selection_controller_.reset();
88  }
89}
90
91void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect& anchor,
92                                                       const gfx::Rect& focus) {
93  selection_anchor_rect_ = anchor;
94  selection_focus_rect_ = focus;
95  UpdateEditingController();
96}
97
98void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) {
99  text_input_type_ = type;
100}
101
102bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
103  DCHECK(rwhva_);
104  if (event->IsTouchEvent())
105    return false;
106
107  if (!event->IsGestureEvent()) {
108    EndTouchEditing();
109    return false;
110  }
111
112  const ui::GestureEvent* gesture_event =
113      static_cast<const ui::GestureEvent*>(event);
114  switch (event->type()) {
115    case ui::ET_GESTURE_TAP:
116      if (gesture_event->details().tap_count() > 1)
117        selection_gesture_in_process_ = true;
118      // When the user taps, we want to show touch editing handles if user
119      // tapped on selected text.
120      if (selection_anchor_rect_ != selection_focus_rect_) {
121        // UnionRects only works for rects with non-zero width.
122        gfx::Rect anchor(selection_anchor_rect_.origin(),
123                         gfx::Size(1, selection_anchor_rect_.height()));
124        gfx::Rect focus(selection_focus_rect_.origin(),
125                        gfx::Size(1, selection_focus_rect_.height()));
126        gfx::Rect selection_rect = gfx::UnionRects(anchor, focus);
127        if (selection_rect.Contains(gesture_event->location())) {
128          StartTouchEditing();
129          return true;
130        }
131      }
132      break;
133    case ui::ET_GESTURE_LONG_PRESS:
134      selection_gesture_in_process_ = true;
135      break;
136    default:
137      break;
138  }
139  return false;
140}
141
142void TouchEditableImplAura::GestureEventAck(int gesture_event_type) {
143  DCHECK(rwhva_);
144  if (gesture_event_type == WebKit::WebInputEvent::GestureTap &&
145      text_input_type_ != ui::TEXT_INPUT_TYPE_NONE) {
146    StartTouchEditing();
147    if (touch_selection_controller_)
148      touch_selection_controller_->SelectionChanged();
149  }
150
151  if (gesture_event_type == WebKit::WebInputEvent::GestureLongPress ||
152      gesture_event_type == WebKit::WebInputEvent::GestureTap)
153    selection_gesture_in_process_ = false;
154}
155
156void TouchEditableImplAura::OnViewDestroyed() {
157  Cleanup();
158}
159
160////////////////////////////////////////////////////////////////////////////////
161// TouchEditableImplAura, ui::TouchEditable implementation:
162
163void TouchEditableImplAura::SelectRect(const gfx::Point& start,
164                                       const gfx::Point& end) {
165  if (!rwhva_)
166    return;
167
168  RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
169      rwhva_->GetRenderWidgetHost());
170  host->SelectRange(start, end);
171}
172
173void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) {
174  if (!rwhva_)
175    return;
176
177  RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
178      rwhva_->GetRenderWidgetHost());
179  host->MoveCaret(point);
180}
181
182void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1,
183                                                  gfx::Rect* p2) {
184  *p1 = selection_anchor_rect_;
185  *p2 = selection_focus_rect_;
186}
187
188gfx::Rect TouchEditableImplAura::GetBounds() {
189  return rwhva_ ? rwhva_->GetNativeView()->bounds() : gfx::Rect();
190}
191
192gfx::NativeView TouchEditableImplAura::GetNativeView() {
193  return rwhva_ ? rwhva_->GetNativeView()->GetRootWindow() : NULL;
194}
195
196void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) {
197  if (!rwhva_)
198    return;
199  aura::Window* window = rwhva_->GetNativeView();
200  aura::client::ScreenPositionClient* screen_position_client =
201      aura::client::GetScreenPositionClient(window->GetRootWindow());
202  if (screen_position_client)
203    screen_position_client->ConvertPointToScreen(window, point);
204}
205
206void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) {
207  if (!rwhva_)
208    return;
209  aura::Window* window = rwhva_->GetNativeView();
210  aura::client::ScreenPositionClient* screen_position_client =
211      aura::client::GetScreenPositionClient(window->GetRootWindow());
212  if (screen_position_client)
213    screen_position_client->ConvertPointFromScreen(window, point);
214}
215
216bool TouchEditableImplAura::DrawsHandles() {
217  return false;
218}
219
220void TouchEditableImplAura::OpenContextMenu(const gfx::Point anchor) {
221  if (!rwhva_)
222    return;
223  RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
224  host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID()));
225  EndTouchEditing();
226}
227
228bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const {
229  NOTREACHED();
230  return false;
231}
232
233bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const {
234  if (!rwhva_)
235    return false;
236  bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE;
237  ui::Range selection_range;
238  rwhva_->GetSelectionRange(&selection_range);
239  bool has_selection = !selection_range.is_empty();
240  switch (command_id) {
241    case IDS_APP_CUT:
242      return editable && has_selection;
243    case IDS_APP_COPY:
244      return has_selection;
245    case IDS_APP_PASTE: {
246      string16 result;
247      ui::Clipboard::GetForCurrentThread()->ReadText(
248          ui::Clipboard::BUFFER_STANDARD, &result);
249      return editable && !result.empty();
250    }
251    case IDS_APP_DELETE:
252      return editable && has_selection;
253    case IDS_APP_SELECT_ALL:
254      return true;
255    default:
256      return false;
257  }
258}
259
260bool TouchEditableImplAura::GetAcceleratorForCommandId(
261    int command_id,
262    ui::Accelerator* accelerator) {
263  return false;
264}
265
266void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) {
267  if (!rwhva_)
268    return;
269  RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
270  switch (command_id) {
271    case IDS_APP_CUT:
272      host->Cut();
273      break;
274    case IDS_APP_COPY:
275      host->Copy();
276      break;
277    case IDS_APP_PASTE:
278      host->Paste();
279      break;
280    case IDS_APP_DELETE:
281      host->Delete();
282      break;
283    case IDS_APP_SELECT_ALL:
284      host->SelectAll();
285      break;
286    default:
287      NOTREACHED();
288      break;
289  }
290  EndTouchEditing();
291}
292
293////////////////////////////////////////////////////////////////////////////////
294// TouchEditableImplAura, private:
295
296TouchEditableImplAura::TouchEditableImplAura()
297    : text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
298      rwhva_(NULL),
299      selection_gesture_in_process_(false) {
300}
301
302void TouchEditableImplAura::Cleanup() {
303  if (rwhva_) {
304    rwhva_->set_touch_editing_client(NULL);
305    rwhva_ = NULL;
306  }
307  touch_selection_controller_.reset();
308}
309
310}  // namespace content
311