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 "ui/views/touchui/touch_selection_controller_impl.h"
6
7#include "base/time/time.h"
8#include "ui/aura/client/cursor_client.h"
9#include "ui/aura/env.h"
10#include "ui/aura/window.h"
11#include "ui/base/resource/resource_bundle.h"
12#include "ui/gfx/canvas.h"
13#include "ui/gfx/image/image.h"
14#include "ui/gfx/path.h"
15#include "ui/gfx/rect.h"
16#include "ui/gfx/screen.h"
17#include "ui/gfx/size.h"
18#include "ui/resources/grit/ui_resources.h"
19#include "ui/strings/grit/ui_strings.h"
20#include "ui/views/widget/widget.h"
21#include "ui/wm/core/masked_window_targeter.h"
22#include "ui/wm/core/window_animations.h"
23
24namespace {
25
26// Constants defining the visual attributes of selection handles
27const int kSelectionHandleLineWidth = 1;
28const SkColor kSelectionHandleLineColor =
29    SkColorSetRGB(0x42, 0x81, 0xf4);
30
31// When a handle is dragged, the drag position reported to the client view is
32// offset vertically to represent the cursor position. This constant specifies
33// the offset in  pixels above the "O" (see pic below). This is required because
34// say if this is zero, that means the drag position we report is the point
35// right above the "O" or the bottom most point of the cursor "|". In that case,
36// a vertical movement of even one pixel will make the handle jump to the line
37// below it. So when the user just starts dragging, the handle will jump to the
38// next line if the user makes any vertical movement. It is correct but
39// looks/feels weird. So we have this non-zero offset to prevent this jumping.
40//
41// Editing handle widget showing the difference between the position of the
42// ET_GESTURE_SCROLL_UPDATE event and the drag position reported to the client:
43//                                  _____
44//                                 |  |<-|---- Drag position reported to client
45//                              _  |  O  |
46//          Vertical Padding __|   |   <-|---- ET_GESTURE_SCROLL_UPDATE position
47//                             |_  |_____|<--- Editing handle widget
48//
49//                                 | |
50//                                  T
51//                          Horizontal Padding
52//
53const int kSelectionHandleVerticalDragOffset = 5;
54
55// Padding around the selection handle defining the area that will be included
56// in the touch target to make dragging the handle easier (see pic above).
57const int kSelectionHandleHorizPadding = 10;
58const int kSelectionHandleVertPadding = 20;
59
60const int kContextMenuTimoutMs = 200;
61
62const int kSelectionHandleQuickFadeDurationMs = 50;
63
64// Minimum height for selection handle bar. If the bar height is going to be
65// less than this value, handle will not be shown.
66const int kSelectionHandleBarMinHeight = 5;
67// Maximum amount that selection handle bar can stick out of client view's
68// boundaries.
69const int kSelectionHandleBarBottomAllowance = 3;
70
71// Creates a widget to host SelectionHandleView.
72views::Widget* CreateTouchSelectionPopupWidget(
73    gfx::NativeView context,
74    views::WidgetDelegate* widget_delegate) {
75  views::Widget* widget = new views::Widget;
76  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
77  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
78  params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE;
79  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
80  params.parent = context;
81  params.delegate = widget_delegate;
82  widget->Init(params);
83  return widget;
84}
85
86gfx::Image* GetHandleImage() {
87  static gfx::Image* handle_image = NULL;
88  if (!handle_image) {
89    handle_image = &ui::ResourceBundle::GetSharedInstance().GetImageNamed(
90        IDR_TEXT_SELECTION_HANDLE);
91  }
92  return handle_image;
93}
94
95gfx::Size GetHandleImageSize() {
96  return GetHandleImage()->Size();
97}
98
99// Cannot use gfx::UnionRect since it does not work for empty rects.
100gfx::Rect Union(const gfx::Rect& r1, const gfx::Rect& r2) {
101  int rx = std::min(r1.x(), r2.x());
102  int ry = std::min(r1.y(), r2.y());
103  int rr = std::max(r1.right(), r2.right());
104  int rb = std::max(r1.bottom(), r2.bottom());
105
106  return gfx::Rect(rx, ry, rr - rx, rb - ry);
107}
108
109// Convenience methods to convert a |rect| from screen to the |client|'s
110// coordinate system and vice versa.
111// Note that this is not quite correct because it does not take into account
112// transforms such as rotation and scaling. This should be in TouchEditable.
113// TODO(varunjain): Fix this.
114gfx::Rect ConvertFromScreen(ui::TouchEditable* client, const gfx::Rect& rect) {
115  gfx::Point origin = rect.origin();
116  client->ConvertPointFromScreen(&origin);
117  return gfx::Rect(origin, rect.size());
118}
119gfx::Rect ConvertToScreen(ui::TouchEditable* client, const gfx::Rect& rect) {
120  gfx::Point origin = rect.origin();
121  client->ConvertPointToScreen(&origin);
122  return gfx::Rect(origin, rect.size());
123}
124
125}  // namespace
126
127namespace views {
128
129typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView;
130
131class TouchHandleWindowTargeter : public wm::MaskedWindowTargeter {
132 public:
133  TouchHandleWindowTargeter(aura::Window* window,
134                            EditingHandleView* handle_view);
135
136  virtual ~TouchHandleWindowTargeter() {}
137
138 private:
139  // wm::MaskedWindowTargeter:
140  virtual bool GetHitTestMask(aura::Window* window,
141                              gfx::Path* mask) const OVERRIDE;
142
143  EditingHandleView* handle_view_;
144
145  DISALLOW_COPY_AND_ASSIGN(TouchHandleWindowTargeter);
146};
147
148// A View that displays the text selection handle.
149class TouchSelectionControllerImpl::EditingHandleView
150    : public views::WidgetDelegateView {
151 public:
152  EditingHandleView(TouchSelectionControllerImpl* controller,
153                    gfx::NativeView context)
154      : controller_(controller),
155        drag_offset_(0),
156        draw_invisible_(false) {
157    widget_.reset(CreateTouchSelectionPopupWidget(context, this));
158    widget_->SetContentsView(this);
159
160    aura::Window* window = widget_->GetNativeWindow();
161    window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
162        new TouchHandleWindowTargeter(window, this)));
163
164    // We are owned by the TouchSelectionController.
165    set_owned_by_client();
166  }
167
168  virtual ~EditingHandleView() {
169    SetWidgetVisible(false, false);
170  }
171
172  // Overridden from views::WidgetDelegateView:
173  virtual bool WidgetHasHitTestMask() const OVERRIDE {
174    return true;
175  }
176
177  virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE {
178    gfx::Size image_size = GetHandleImageSize();
179    mask->addRect(SkIntToScalar(0), SkIntToScalar(selection_rect_.height()),
180        SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding,
181        SkIntToScalar(selection_rect_.height() + image_size.height() +
182            kSelectionHandleVertPadding));
183  }
184
185  virtual void DeleteDelegate() OVERRIDE {
186    // We are owned and deleted by TouchSelectionController.
187  }
188
189  // Overridden from views::View:
190  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
191    if (draw_invisible_)
192      return;
193    gfx::Size image_size = GetHandleImageSize();
194    int cursor_pos_x = image_size.width() / 2 - kSelectionHandleLineWidth +
195        kSelectionHandleHorizPadding;
196
197    // Draw the cursor line.
198    canvas->FillRect(
199        gfx::Rect(cursor_pos_x, 0,
200                  2 * kSelectionHandleLineWidth + 1, selection_rect_.height()),
201        kSelectionHandleLineColor);
202
203    // Draw the handle image.
204    canvas->DrawImageInt(*GetHandleImage()->ToImageSkia(),
205        kSelectionHandleHorizPadding, selection_rect_.height());
206  }
207
208  virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
209    event->SetHandled();
210    switch (event->type()) {
211      case ui::ET_GESTURE_SCROLL_BEGIN:
212        widget_->SetCapture(this);
213        controller_->SetDraggingHandle(this);
214        drag_offset_ = event->y() - selection_rect_.height() +
215            kSelectionHandleVerticalDragOffset;
216        break;
217      case ui::ET_GESTURE_SCROLL_UPDATE: {
218        gfx::Point drag_pos(event->location().x(),
219            event->location().y() - drag_offset_);
220        controller_->SelectionHandleDragged(drag_pos);
221        break;
222      }
223      case ui::ET_GESTURE_SCROLL_END:
224      case ui::ET_SCROLL_FLING_START:
225        widget_->ReleaseCapture();
226        controller_->SetDraggingHandle(NULL);
227        break;
228      default:
229        break;
230    }
231  }
232
233  virtual gfx::Size GetPreferredSize() const OVERRIDE {
234    gfx::Size image_size = GetHandleImageSize();
235    return gfx::Size(image_size.width() + 2 * kSelectionHandleHorizPadding,
236                     image_size.height() + selection_rect_.height() +
237                         kSelectionHandleVertPadding);
238  }
239
240  bool IsWidgetVisible() const {
241    return widget_->IsVisible();
242  }
243
244  void SetWidgetVisible(bool visible, bool quick) {
245    if (widget_->IsVisible() == visible)
246      return;
247    wm::SetWindowVisibilityAnimationDuration(
248        widget_->GetNativeView(),
249        base::TimeDelta::FromMilliseconds(
250            quick ? kSelectionHandleQuickFadeDurationMs : 0));
251    if (visible)
252      widget_->Show();
253    else
254      widget_->Hide();
255  }
256
257  void SetSelectionRectInScreen(const gfx::Rect& rect) {
258    gfx::Size image_size = GetHandleImageSize();
259    selection_rect_ = rect;
260    gfx::Rect widget_bounds(
261        rect.x() - image_size.width() / 2 - kSelectionHandleHorizPadding,
262        rect.y(),
263        image_size.width() + 2 * kSelectionHandleHorizPadding,
264        rect.height() + image_size.height() + kSelectionHandleVertPadding);
265    widget_->SetBounds(widget_bounds);
266  }
267
268  gfx::Point GetScreenPosition() {
269    return widget_->GetClientAreaBoundsInScreen().origin();
270  }
271
272  void SetDrawInvisible(bool draw_invisible) {
273    if (draw_invisible_ == draw_invisible)
274      return;
275    draw_invisible_ = draw_invisible;
276    SchedulePaint();
277  }
278
279  const gfx::Rect& selection_rect() const { return selection_rect_; }
280
281 private:
282  scoped_ptr<Widget> widget_;
283  TouchSelectionControllerImpl* controller_;
284  gfx::Rect selection_rect_;
285
286  // Vertical offset between the scroll event position and the drag position
287  // reported to the client view (see the ASCII figure at the top of the file
288  // and its description for more details).
289  int drag_offset_;
290
291  // If set to true, the handle will not draw anything, hence providing an empty
292  // widget. We need this because we may want to stop showing the handle while
293  // it is being dragged. Since it is being dragged, we cannot destroy the
294  // handle.
295  bool draw_invisible_;
296
297  DISALLOW_COPY_AND_ASSIGN(EditingHandleView);
298};
299
300TouchHandleWindowTargeter::TouchHandleWindowTargeter(
301    aura::Window* window,
302    EditingHandleView* handle_view)
303    : wm::MaskedWindowTargeter(window),
304      handle_view_(handle_view) {
305}
306
307bool TouchHandleWindowTargeter::GetHitTestMask(aura::Window* window,
308                                               gfx::Path* mask) const {
309  const gfx::Rect& selection_rect = handle_view_->selection_rect();
310  gfx::Size image_size = GetHandleImageSize();
311  mask->addRect(SkIntToScalar(0), SkIntToScalar(selection_rect.height()),
312      SkIntToScalar(image_size.width()) + 2 * kSelectionHandleHorizPadding,
313      SkIntToScalar(selection_rect.height() + image_size.height() +
314                    kSelectionHandleVertPadding));
315  return true;
316}
317
318TouchSelectionControllerImpl::TouchSelectionControllerImpl(
319    ui::TouchEditable* client_view)
320    : client_view_(client_view),
321      client_widget_(NULL),
322      selection_handle_1_(new EditingHandleView(this,
323                          client_view->GetNativeView())),
324      selection_handle_2_(new EditingHandleView(this,
325                          client_view->GetNativeView())),
326      cursor_handle_(new EditingHandleView(this,
327                     client_view->GetNativeView())),
328      context_menu_(NULL),
329      dragging_handle_(NULL) {
330  aura::Window* client_window = client_view_->GetNativeView();
331  client_window->AddObserver(this);
332  client_widget_ = Widget::GetTopLevelWidgetForNativeView(client_window);
333  if (client_widget_)
334    client_widget_->AddObserver(this);
335  aura::Env::GetInstance()->AddPreTargetHandler(this);
336}
337
338TouchSelectionControllerImpl::~TouchSelectionControllerImpl() {
339  HideContextMenu();
340  aura::Env::GetInstance()->RemovePreTargetHandler(this);
341  if (client_widget_)
342    client_widget_->RemoveObserver(this);
343  client_view_->GetNativeView()->RemoveObserver(this);
344}
345
346void TouchSelectionControllerImpl::SelectionChanged() {
347  gfx::Rect r1, r2;
348  client_view_->GetSelectionEndPoints(&r1, &r2);
349  gfx::Rect screen_rect_1 = ConvertToScreen(client_view_, r1);
350  gfx::Rect screen_rect_2 = ConvertToScreen(client_view_, r2);
351  gfx::Rect client_bounds = client_view_->GetBounds();
352  if (r1.y() < client_bounds.y())
353    r1.Inset(0, client_bounds.y() - r1.y(), 0, 0);
354  if (r2.y() < client_bounds.y())
355    r2.Inset(0, client_bounds.y() - r2.y(), 0, 0);
356  gfx::Rect screen_rect_1_clipped = ConvertToScreen(client_view_, r1);
357  gfx::Rect screen_rect_2_clipped = ConvertToScreen(client_view_, r2);
358  if (screen_rect_1_clipped == selection_end_point_1_clipped_ &&
359      screen_rect_2_clipped == selection_end_point_2_clipped_)
360    return;
361
362  selection_end_point_1_ = screen_rect_1;
363  selection_end_point_2_ = screen_rect_2;
364  selection_end_point_1_clipped_ = screen_rect_1_clipped;
365  selection_end_point_2_clipped_ = screen_rect_2_clipped;
366
367  if (client_view_->DrawsHandles()) {
368    UpdateContextMenu();
369    return;
370  }
371  if (dragging_handle_) {
372    // We need to reposition only the selection handle that is being dragged.
373    // The other handle stays the same. Also, the selection handle being dragged
374    // will always be at the end of selection, while the other handle will be at
375    // the start.
376    // If the new location of this handle is out of client view, its widget
377    // should not get hidden, since it should still receive touch events.
378    // Hence, we are not using |SetHandleSelectionRect()| method here.
379    dragging_handle_->SetSelectionRectInScreen(screen_rect_2_clipped);
380
381    // Temporary fix for selection handle going outside a window. On a webpage,
382    // the page should scroll if the selection handle is dragged outside the
383    // window. That does not happen currently. So we just hide the handle for
384    // now.
385    // TODO(varunjain): Fix this: crbug.com/269003
386    dragging_handle_->SetDrawInvisible(!ShouldShowHandleFor(r2));
387
388    if (dragging_handle_ != cursor_handle_.get()) {
389      // The non-dragging-handle might have recently become visible.
390      EditingHandleView* non_dragging_handle = selection_handle_1_.get();
391      if (dragging_handle_ == selection_handle_1_) {
392        non_dragging_handle = selection_handle_2_.get();
393        // if handle 1 is being dragged, it is corresponding to the end of
394        // selection and the other handle to the start of selection.
395        selection_end_point_1_ = screen_rect_2;
396        selection_end_point_2_ = screen_rect_1;
397        selection_end_point_1_clipped_ = screen_rect_2_clipped;
398        selection_end_point_2_clipped_ = screen_rect_1_clipped;
399      }
400      SetHandleSelectionRect(non_dragging_handle, r1, screen_rect_1_clipped);
401    }
402  } else {
403    UpdateContextMenu();
404
405    // Check if there is any selection at all.
406    if (screen_rect_1.origin() == screen_rect_2.origin()) {
407      selection_handle_1_->SetWidgetVisible(false, false);
408      selection_handle_2_->SetWidgetVisible(false, false);
409      SetHandleSelectionRect(cursor_handle_.get(), r1, screen_rect_1_clipped);
410      return;
411    }
412
413    cursor_handle_->SetWidgetVisible(false, false);
414    SetHandleSelectionRect(selection_handle_1_.get(), r1,
415                           screen_rect_1_clipped);
416    SetHandleSelectionRect(selection_handle_2_.get(), r2,
417                           screen_rect_2_clipped);
418  }
419}
420
421bool TouchSelectionControllerImpl::IsHandleDragInProgress() {
422  return !!dragging_handle_;
423}
424
425void TouchSelectionControllerImpl::HideHandles(bool quick) {
426  selection_handle_1_->SetWidgetVisible(false, quick);
427  selection_handle_2_->SetWidgetVisible(false, quick);
428  cursor_handle_->SetWidgetVisible(false, quick);
429}
430
431void TouchSelectionControllerImpl::SetDraggingHandle(
432    EditingHandleView* handle) {
433  dragging_handle_ = handle;
434  if (dragging_handle_)
435    HideContextMenu();
436  else
437    StartContextMenuTimer();
438}
439
440void TouchSelectionControllerImpl::SelectionHandleDragged(
441    const gfx::Point& drag_pos) {
442  DCHECK(dragging_handle_);
443  gfx::Point drag_pos_in_client = drag_pos;
444  ConvertPointToClientView(dragging_handle_, &drag_pos_in_client);
445
446  if (dragging_handle_ == cursor_handle_.get()) {
447    client_view_->MoveCaretTo(drag_pos_in_client);
448    return;
449  }
450
451  // Find the stationary selection handle.
452  gfx::Rect fixed_handle_rect = selection_end_point_1_;
453  if (selection_handle_1_ == dragging_handle_)
454    fixed_handle_rect = selection_end_point_2_;
455
456  // Find selection end points in client_view's coordinate system.
457  gfx::Point p2 = fixed_handle_rect.origin();
458  p2.Offset(0, fixed_handle_rect.height() / 2);
459  client_view_->ConvertPointFromScreen(&p2);
460
461  // Instruct client_view to select the region between p1 and p2. The position
462  // of |fixed_handle| is the start and that of |dragging_handle| is the end
463  // of selection.
464  client_view_->SelectRect(p2, drag_pos_in_client);
465}
466
467void TouchSelectionControllerImpl::ConvertPointToClientView(
468    EditingHandleView* source, gfx::Point* point) {
469  View::ConvertPointToScreen(source, point);
470  client_view_->ConvertPointFromScreen(point);
471}
472
473void TouchSelectionControllerImpl::SetHandleSelectionRect(
474    EditingHandleView* handle,
475    const gfx::Rect& rect,
476    const gfx::Rect& rect_in_screen) {
477  handle->SetWidgetVisible(ShouldShowHandleFor(rect), false);
478  if (handle->IsWidgetVisible())
479    handle->SetSelectionRectInScreen(rect_in_screen);
480}
481
482bool TouchSelectionControllerImpl::ShouldShowHandleFor(
483    const gfx::Rect& rect) const {
484  if (rect.height() < kSelectionHandleBarMinHeight)
485    return false;
486  gfx::Rect bounds = client_view_->GetBounds();
487  bounds.Inset(0, 0, 0, -kSelectionHandleBarBottomAllowance);
488  return bounds.Contains(rect);
489}
490
491bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
492  return client_view_->IsCommandIdEnabled(command_id);
493}
494
495void TouchSelectionControllerImpl::ExecuteCommand(int command_id,
496                                                  int event_flags) {
497  HideContextMenu();
498  client_view_->ExecuteCommand(command_id, event_flags);
499}
500
501void TouchSelectionControllerImpl::OpenContextMenu() {
502  // Context menu should appear centered on top of the selected region.
503  const gfx::Rect rect = context_menu_->GetAnchorRect();
504  const gfx::Point anchor(rect.CenterPoint().x(), rect.y());
505  HideContextMenu();
506  client_view_->OpenContextMenu(anchor);
507}
508
509void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) {
510  if (menu == context_menu_)
511    context_menu_ = NULL;
512}
513
514void TouchSelectionControllerImpl::OnAncestorWindowTransformed(
515    aura::Window* window,
516    aura::Window* ancestor) {
517  client_view_->DestroyTouchSelection();
518}
519
520void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) {
521  DCHECK_EQ(client_widget_, widget);
522  client_widget_ = NULL;
523}
524
525void TouchSelectionControllerImpl::OnWidgetBoundsChanged(
526    Widget* widget,
527    const gfx::Rect& new_bounds) {
528  DCHECK_EQ(client_widget_, widget);
529  SelectionChanged();
530}
531
532void TouchSelectionControllerImpl::OnKeyEvent(ui::KeyEvent* event) {
533  client_view_->DestroyTouchSelection();
534}
535
536void TouchSelectionControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
537  aura::client::CursorClient* cursor_client = aura::client::GetCursorClient(
538      client_view_->GetNativeView()->GetRootWindow());
539  if (!cursor_client || cursor_client->IsMouseEventsEnabled())
540    client_view_->DestroyTouchSelection();
541}
542
543void TouchSelectionControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
544  client_view_->DestroyTouchSelection();
545}
546
547void TouchSelectionControllerImpl::ContextMenuTimerFired() {
548  // Get selection end points in client_view's space.
549  gfx::Rect end_rect_1_in_screen;
550  gfx::Rect end_rect_2_in_screen;
551  if (cursor_handle_->IsWidgetVisible()) {
552    end_rect_1_in_screen = selection_end_point_1_clipped_;
553    end_rect_2_in_screen = end_rect_1_in_screen;
554  } else {
555    end_rect_1_in_screen = selection_end_point_1_clipped_;
556    end_rect_2_in_screen = selection_end_point_2_clipped_;
557  }
558
559  // Convert from screen to client.
560  gfx::Rect end_rect_1(ConvertFromScreen(client_view_, end_rect_1_in_screen));
561  gfx::Rect end_rect_2(ConvertFromScreen(client_view_, end_rect_2_in_screen));
562
563  // if selection is completely inside the view, we display the context menu
564  // in the middle of the end points on the top. Else, we show it above the
565  // visible handle. If no handle is visible, we do not show the menu.
566  gfx::Rect menu_anchor;
567  if (ShouldShowHandleFor(end_rect_1) &&
568      ShouldShowHandleFor(end_rect_2))
569    menu_anchor = Union(end_rect_1_in_screen,end_rect_2_in_screen);
570  else if (ShouldShowHandleFor(end_rect_1))
571    menu_anchor = end_rect_1_in_screen;
572  else if (ShouldShowHandleFor(end_rect_2))
573    menu_anchor = end_rect_2_in_screen;
574  else
575    return;
576
577  DCHECK(!context_menu_);
578  context_menu_ = TouchEditingMenuView::Create(this, menu_anchor,
579                                               GetHandleImageSize(),
580                                               client_view_->GetNativeView());
581}
582
583void TouchSelectionControllerImpl::StartContextMenuTimer() {
584  if (context_menu_timer_.IsRunning())
585    return;
586  context_menu_timer_.Start(
587      FROM_HERE,
588      base::TimeDelta::FromMilliseconds(kContextMenuTimoutMs),
589      this,
590      &TouchSelectionControllerImpl::ContextMenuTimerFired);
591}
592
593void TouchSelectionControllerImpl::UpdateContextMenu() {
594  // Hide context menu to be shown when the timer fires.
595  HideContextMenu();
596  StartContextMenuTimer();
597}
598
599void TouchSelectionControllerImpl::HideContextMenu() {
600  if (context_menu_)
601    context_menu_->Close();
602  context_menu_ = NULL;
603  context_menu_timer_.Stop();
604}
605
606gfx::NativeView TouchSelectionControllerImpl::GetCursorHandleNativeView() {
607  return cursor_handle_->GetWidget()->GetNativeView();
608}
609
610gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() {
611  return selection_handle_1_->GetScreenPosition();
612}
613
614gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() {
615  return selection_handle_2_->GetScreenPosition();
616}
617
618gfx::Point TouchSelectionControllerImpl::GetCursorHandlePosition() {
619  return cursor_handle_->GetScreenPosition();
620}
621
622bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() {
623  return selection_handle_1_->IsWidgetVisible();
624}
625
626bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() {
627  return selection_handle_2_->IsWidgetVisible();
628}
629
630bool TouchSelectionControllerImpl::IsCursorHandleVisible() {
631  return cursor_handle_->IsWidgetVisible();
632}
633
634}  // namespace views
635