• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 namespace {
25 
26 // Constants defining the visual attributes of selection handles
27 const int kSelectionHandleLineWidth = 1;
28 const 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 //
53 const 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).
57 const int kSelectionHandleHorizPadding = 10;
58 const int kSelectionHandleVertPadding = 20;
59 
60 const int kContextMenuTimoutMs = 200;
61 
62 const 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.
66 const int kSelectionHandleBarMinHeight = 5;
67 // Maximum amount that selection handle bar can stick out of client view's
68 // boundaries.
69 const int kSelectionHandleBarBottomAllowance = 3;
70 
71 // Creates a widget to host SelectionHandleView.
CreateTouchSelectionPopupWidget(gfx::NativeView context,views::WidgetDelegate * widget_delegate)72 views::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 
GetHandleImage()86 gfx::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 
GetHandleImageSize()95 gfx::Size GetHandleImageSize() {
96   return GetHandleImage()->Size();
97 }
98 
99 // Cannot use gfx::UnionRect since it does not work for empty rects.
Union(const gfx::Rect & r1,const gfx::Rect & r2)100 gfx::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.
ConvertFromScreen(ui::TouchEditable * client,const gfx::Rect & rect)114 gfx::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 }
ConvertToScreen(ui::TouchEditable * client,const gfx::Rect & rect)119 gfx::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 
127 namespace views {
128 
129 typedef TouchSelectionControllerImpl::EditingHandleView EditingHandleView;
130 
131 class TouchHandleWindowTargeter : public wm::MaskedWindowTargeter {
132  public:
133   TouchHandleWindowTargeter(aura::Window* window,
134                             EditingHandleView* handle_view);
135 
~TouchHandleWindowTargeter()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.
149 class TouchSelectionControllerImpl::EditingHandleView
150     : public views::WidgetDelegateView {
151  public:
EditingHandleView(TouchSelectionControllerImpl * controller,gfx::NativeView context)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 
~EditingHandleView()168   virtual ~EditingHandleView() {
169     SetWidgetVisible(false, false);
170   }
171 
172   // Overridden from views::WidgetDelegateView:
WidgetHasHitTestMask() const173   virtual bool WidgetHasHitTestMask() const OVERRIDE {
174     return true;
175   }
176 
GetWidgetHitTestMask(gfx::Path * mask) const177   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 
DeleteDelegate()185   virtual void DeleteDelegate() OVERRIDE {
186     // We are owned and deleted by TouchSelectionController.
187   }
188 
189   // Overridden from views::View:
OnPaint(gfx::Canvas * canvas)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 
OnGestureEvent(ui::GestureEvent * event)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 
GetPreferredSize() const233   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 
IsWidgetVisible() const240   bool IsWidgetVisible() const {
241     return widget_->IsVisible();
242   }
243 
SetWidgetVisible(bool visible,bool quick)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 
SetSelectionRectInScreen(const gfx::Rect & rect)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 
GetScreenPosition()268   gfx::Point GetScreenPosition() {
269     return widget_->GetClientAreaBoundsInScreen().origin();
270   }
271 
SetDrawInvisible(bool draw_invisible)272   void SetDrawInvisible(bool draw_invisible) {
273     if (draw_invisible_ == draw_invisible)
274       return;
275     draw_invisible_ = draw_invisible;
276     SchedulePaint();
277   }
278 
selection_rect() const279   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 
TouchHandleWindowTargeter(aura::Window * window,EditingHandleView * handle_view)300 TouchHandleWindowTargeter::TouchHandleWindowTargeter(
301     aura::Window* window,
302     EditingHandleView* handle_view)
303     : wm::MaskedWindowTargeter(window),
304       handle_view_(handle_view) {
305 }
306 
GetHitTestMask(aura::Window * window,gfx::Path * mask) const307 bool 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 
TouchSelectionControllerImpl(ui::TouchEditable * client_view)318 TouchSelectionControllerImpl::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 
~TouchSelectionControllerImpl()338 TouchSelectionControllerImpl::~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 
SelectionChanged()346 void 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 
IsHandleDragInProgress()421 bool TouchSelectionControllerImpl::IsHandleDragInProgress() {
422   return !!dragging_handle_;
423 }
424 
HideHandles(bool quick)425 void 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 
SetDraggingHandle(EditingHandleView * handle)431 void TouchSelectionControllerImpl::SetDraggingHandle(
432     EditingHandleView* handle) {
433   dragging_handle_ = handle;
434   if (dragging_handle_)
435     HideContextMenu();
436   else
437     StartContextMenuTimer();
438 }
439 
SelectionHandleDragged(const gfx::Point & drag_pos)440 void 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 
ConvertPointToClientView(EditingHandleView * source,gfx::Point * point)467 void TouchSelectionControllerImpl::ConvertPointToClientView(
468     EditingHandleView* source, gfx::Point* point) {
469   View::ConvertPointToScreen(source, point);
470   client_view_->ConvertPointFromScreen(point);
471 }
472 
SetHandleSelectionRect(EditingHandleView * handle,const gfx::Rect & rect,const gfx::Rect & rect_in_screen)473 void 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 
ShouldShowHandleFor(const gfx::Rect & rect) const482 bool 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 
IsCommandIdEnabled(int command_id) const491 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
492   return client_view_->IsCommandIdEnabled(command_id);
493 }
494 
ExecuteCommand(int command_id,int event_flags)495 void TouchSelectionControllerImpl::ExecuteCommand(int command_id,
496                                                   int event_flags) {
497   HideContextMenu();
498   client_view_->ExecuteCommand(command_id, event_flags);
499 }
500 
OpenContextMenu()501 void 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 
OnMenuClosed(TouchEditingMenuView * menu)509 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) {
510   if (menu == context_menu_)
511     context_menu_ = NULL;
512 }
513 
OnAncestorWindowTransformed(aura::Window * window,aura::Window * ancestor)514 void TouchSelectionControllerImpl::OnAncestorWindowTransformed(
515     aura::Window* window,
516     aura::Window* ancestor) {
517   client_view_->DestroyTouchSelection();
518 }
519 
OnWidgetClosing(Widget * widget)520 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) {
521   DCHECK_EQ(client_widget_, widget);
522   client_widget_ = NULL;
523 }
524 
OnWidgetBoundsChanged(Widget * widget,const gfx::Rect & new_bounds)525 void TouchSelectionControllerImpl::OnWidgetBoundsChanged(
526     Widget* widget,
527     const gfx::Rect& new_bounds) {
528   DCHECK_EQ(client_widget_, widget);
529   SelectionChanged();
530 }
531 
OnKeyEvent(ui::KeyEvent * event)532 void TouchSelectionControllerImpl::OnKeyEvent(ui::KeyEvent* event) {
533   client_view_->DestroyTouchSelection();
534 }
535 
OnMouseEvent(ui::MouseEvent * event)536 void 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 
OnScrollEvent(ui::ScrollEvent * event)543 void TouchSelectionControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
544   client_view_->DestroyTouchSelection();
545 }
546 
ContextMenuTimerFired()547 void 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 
StartContextMenuTimer()583 void 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 
UpdateContextMenu()593 void TouchSelectionControllerImpl::UpdateContextMenu() {
594   // Hide context menu to be shown when the timer fires.
595   HideContextMenu();
596   StartContextMenuTimer();
597 }
598 
HideContextMenu()599 void TouchSelectionControllerImpl::HideContextMenu() {
600   if (context_menu_)
601     context_menu_->Close();
602   context_menu_ = NULL;
603   context_menu_timer_.Stop();
604 }
605 
GetCursorHandleNativeView()606 gfx::NativeView TouchSelectionControllerImpl::GetCursorHandleNativeView() {
607   return cursor_handle_->GetWidget()->GetNativeView();
608 }
609 
GetSelectionHandle1Position()610 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() {
611   return selection_handle_1_->GetScreenPosition();
612 }
613 
GetSelectionHandle2Position()614 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() {
615   return selection_handle_2_->GetScreenPosition();
616 }
617 
GetCursorHandlePosition()618 gfx::Point TouchSelectionControllerImpl::GetCursorHandlePosition() {
619   return cursor_handle_->GetScreenPosition();
620 }
621 
IsSelectionHandle1Visible()622 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() {
623   return selection_handle_1_->IsWidgetVisible();
624 }
625 
IsSelectionHandle2Visible()626 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() {
627   return selection_handle_2_->IsWidgetVisible();
628 }
629 
IsCursorHandleVisible()630 bool TouchSelectionControllerImpl::IsCursorHandleVisible() {
631   return cursor_handle_->IsWidgetVisible();
632 }
633 
634 }  // namespace views
635