Home | History | Annotate | Download | only in touchui
      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.
     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 
     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 
     95 gfx::Size GetHandleImageSize() {
     96   return GetHandleImage()->Size();
     97 }
     98 
     99 // Cannot use gfx::UnionRect since it does not work for empty rects.
    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.
    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 }
    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 
    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:
    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 
    300 TouchHandleWindowTargeter::TouchHandleWindowTargeter(
    301     aura::Window* window,
    302     EditingHandleView* handle_view)
    303     : wm::MaskedWindowTargeter(window),
    304       handle_view_(handle_view) {
    305 }
    306 
    307 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 
    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 
    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 
    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 
    421 bool TouchSelectionControllerImpl::IsHandleDragInProgress() {
    422   return !!dragging_handle_;
    423 }
    424 
    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 
    431 void TouchSelectionControllerImpl::SetDraggingHandle(
    432     EditingHandleView* handle) {
    433   dragging_handle_ = handle;
    434   if (dragging_handle_)
    435     HideContextMenu();
    436   else
    437     StartContextMenuTimer();
    438 }
    439 
    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 
    467 void TouchSelectionControllerImpl::ConvertPointToClientView(
    468     EditingHandleView* source, gfx::Point* point) {
    469   View::ConvertPointToScreen(source, point);
    470   client_view_->ConvertPointFromScreen(point);
    471 }
    472 
    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 
    482 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 
    491 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const {
    492   return client_view_->IsCommandIdEnabled(command_id);
    493 }
    494 
    495 void TouchSelectionControllerImpl::ExecuteCommand(int command_id,
    496                                                   int event_flags) {
    497   HideContextMenu();
    498   client_view_->ExecuteCommand(command_id, event_flags);
    499 }
    500 
    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 
    509 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) {
    510   if (menu == context_menu_)
    511     context_menu_ = NULL;
    512 }
    513 
    514 void TouchSelectionControllerImpl::OnAncestorWindowTransformed(
    515     aura::Window* window,
    516     aura::Window* ancestor) {
    517   client_view_->DestroyTouchSelection();
    518 }
    519 
    520 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) {
    521   DCHECK_EQ(client_widget_, widget);
    522   client_widget_ = NULL;
    523 }
    524 
    525 void TouchSelectionControllerImpl::OnWidgetBoundsChanged(
    526     Widget* widget,
    527     const gfx::Rect& new_bounds) {
    528   DCHECK_EQ(client_widget_, widget);
    529   SelectionChanged();
    530 }
    531 
    532 void TouchSelectionControllerImpl::OnKeyEvent(ui::KeyEvent* event) {
    533   client_view_->DestroyTouchSelection();
    534 }
    535 
    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 
    543 void TouchSelectionControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
    544   client_view_->DestroyTouchSelection();
    545 }
    546 
    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 
    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 
    593 void TouchSelectionControllerImpl::UpdateContextMenu() {
    594   // Hide context menu to be shown when the timer fires.
    595   HideContextMenu();
    596   StartContextMenuTimer();
    597 }
    598 
    599 void TouchSelectionControllerImpl::HideContextMenu() {
    600   if (context_menu_)
    601     context_menu_->Close();
    602   context_menu_ = NULL;
    603   context_menu_timer_.Stop();
    604 }
    605 
    606 gfx::NativeView TouchSelectionControllerImpl::GetCursorHandleNativeView() {
    607   return cursor_handle_->GetWidget()->GetNativeView();
    608 }
    609 
    610 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle1Position() {
    611   return selection_handle_1_->GetScreenPosition();
    612 }
    613 
    614 gfx::Point TouchSelectionControllerImpl::GetSelectionHandle2Position() {
    615   return selection_handle_2_->GetScreenPosition();
    616 }
    617 
    618 gfx::Point TouchSelectionControllerImpl::GetCursorHandlePosition() {
    619   return cursor_handle_->GetScreenPosition();
    620 }
    621 
    622 bool TouchSelectionControllerImpl::IsSelectionHandle1Visible() {
    623   return selection_handle_1_->IsWidgetVisible();
    624 }
    625 
    626 bool TouchSelectionControllerImpl::IsSelectionHandle2Visible() {
    627   return selection_handle_2_->IsWidgetVisible();
    628 }
    629 
    630 bool TouchSelectionControllerImpl::IsCursorHandleVisible() {
    631   return cursor_handle_->IsWidgetVisible();
    632 }
    633 
    634 }  // namespace views
    635