[M108 Migration][Text Selection] Selection handles & Caret Selection
[platform/framework/web/chromium-efl.git] / tizen_src / chromium_impl / content / browser / selection / selection_handle_efl.cc
1 // Copyright 2013 Samsung Electronics. 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 "selection_handle_efl.h"
6
7 #include <Edje.h>
8 #include <Eina.h>
9 #include <Elementary.h>
10
11 #include "base/files/file_path.h"
12 #include "base/path_service.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
15 #include "content/browser/selection/selection_controller_efl.h"
16 #include "content/browser/web_contents/web_contents_impl.h"
17 #include "content/common/paths_efl.h"
18
19 namespace content {
20
21 // Size of the left/right margin which causes selection handles to be rotated
22 const int kReverseMargin = 32;
23 const char* kLeftHandlePath = "elm/entry/selection/block_handle_left";
24 const char* kRightHandlePath = "elm/entry/selection/block_handle_right";
25 const char* kInputHandlePath = "elm/entry/cursor_handle/default";
26
27 static const char* GetEdjeObjectGroupPath(SelectionHandleEfl::HandleType type) {
28   switch (type) {
29     case SelectionHandleEfl::HANDLE_TYPE_LEFT:
30       return kLeftHandlePath;
31     case SelectionHandleEfl::HANDLE_TYPE_RIGHT:
32       return kRightHandlePath;
33     case SelectionHandleEfl::HANDLE_TYPE_INPUT:
34       return kInputHandlePath;
35     default:
36       return NULL;
37   }
38 }
39
40 SelectionHandleEfl::SelectionHandleEfl(SelectionControllerEfl& controller,
41                                        HandleType type)
42     : controller_(controller),
43       pressed_(false),
44       is_top_(false),
45       handle_type_(type) {
46   Evas* evas = evas_object_evas_get(
47       controller_.rwhva()->offscreen_helper()->content_image());
48   handle_ = edje_object_add(evas);
49
50   base::FilePath edj_dir;
51   base::PathService::Get(PathsEfl::EDJE_RESOURCE_DIR, &edj_dir);
52   base::FilePath selection_handles_edj =
53       edj_dir.Append(FILE_PATH_LITERAL("SelectionHandles.edj"));
54
55   std::string theme_path = selection_handles_edj.AsUTF8Unsafe();
56   const char* group = GetEdjeObjectGroupPath(type);
57   if (edje_file_group_exists(theme_path.c_str(), group)) {
58     edje_object_file_set(handle_, theme_path.c_str(), group);
59   }
60
61   edje_object_signal_emit(handle_, "edje,focus,in", "edje");
62   edje_object_signal_emit(handle_, "elm,state,bottom", "elm");
63   Evas_Object* main_layout_ =
64       static_cast<WebContentsImpl*>(
65           controller_.rwhva()->offscreen_helper()->GetWebContents())
66           ->GetEflNativeView();
67   Evas_Object* smart_parent = evas_object_smart_parent_get(main_layout_);
68   evas_object_smart_member_add(handle_, smart_parent);
69   evas_object_propagate_events_set(handle_, false);
70
71   evas_object_event_callback_add(handle_, EVAS_CALLBACK_MOUSE_DOWN, OnMouseDown, this);
72   evas_object_event_callback_add(handle_, EVAS_CALLBACK_MOUSE_UP, OnMouseUp, this);
73 }
74
75 SelectionHandleEfl::~SelectionHandleEfl() {
76   evas_object_event_callback_del(handle_, EVAS_CALLBACK_MOUSE_DOWN, OnMouseDown);
77   evas_object_event_callback_del(handle_, EVAS_CALLBACK_MOUSE_UP, OnMouseUp);
78   evas_object_smart_member_del(handle_);
79   evas_object_del(handle_);
80 }
81
82 void SelectionHandleEfl::Show() {
83   evas_object_show(handle_);
84   evas_object_raise(handle_);
85 }
86
87 void SelectionHandleEfl::Hide() {
88   evas_object_hide(handle_);
89 }
90
91 bool SelectionHandleEfl::IsVisible() const {
92   int handle_x, handle_y;
93   evas_object_geometry_get(handle_, &handle_x, &handle_y, nullptr, nullptr);
94
95   auto view_rect = controller_.GetVisibleViewportRect();
96
97   return evas_object_visible_get(handle_) &&
98          view_rect.Contains(handle_x, handle_y);
99 }
100
101 void SelectionHandleEfl::Move(const gfx::Point& point) {
102   if (!pressed_)
103     ChangeObjectDirection(CalculateDirection(point));
104
105   if (handle_type_ == HANDLE_TYPE_INPUT)
106     MoveInputHandle(point);
107   else
108     MoveRangeHandle(point);
109 }
110
111 void SelectionHandleEfl::MoveInputHandle(const gfx::Point& point) {
112   CHECK(handle_type_ == HANDLE_TYPE_INPUT);
113   MoveObject(point);
114 }
115
116 void SelectionHandleEfl::MoveRangeHandle(const gfx::Point& point) {
117   CHECK(handle_type_ != HANDLE_TYPE_INPUT);
118   if (!pressed_)
119     MoveObject(point);
120 }
121
122 void SelectionHandleEfl::OnMouseDown(void* data, Evas*, Evas_Object*, void* event_info) {
123   Evas_Event_Mouse_Down* event = static_cast<Evas_Event_Mouse_Down*>(event_info);
124   SelectionHandleEfl* handle = static_cast<SelectionHandleEfl*>(data);
125   handle->pressed_ = true;
126   //Save the diff_point between touch point and base point of the handle
127   handle->diff_point_.SetPoint(event->canvas.x - handle->base_point_.x(),
128       event->canvas.y - handle->base_point_.y());
129
130   handle->controller_.HandleDragBeginNotification(handle);
131 }
132
133 void SelectionHandleEfl::UpdatePosition(const gfx::Point& position) {
134   current_touch_point_ = position;
135   ecore_job_add(UpdateMouseMove, this);
136 }
137
138 void SelectionHandleEfl::OnMouseUp(void* data, Evas*, Evas_Object*, void* /* event_info */) {
139   SelectionHandleEfl* handle = static_cast<SelectionHandleEfl*>(data);
140   handle->pressed_ = false;
141   handle->controller_.HandleDragEndNotification();
142 }
143
144 void SelectionHandleEfl::UpdateMouseMove(void* data) {
145   SelectionHandleEfl* handle = static_cast<SelectionHandleEfl*>(data);
146
147   // As this method is queued and then precessed, it might happen that
148   // it gets called once user has lifted up its finger.
149   // Bail out in such cases.
150   if (!handle->pressed_)
151     return;
152
153   int delta_x = handle->current_touch_point_.x() - handle->diff_point_.x();
154   int delta_y = handle->current_touch_point_.y() - handle->diff_point_.y();
155   handle->base_point_.SetPoint(delta_x, delta_y);
156
157   handle->controller_.HandleDragUpdateNotification(handle);
158 }
159
160 SelectionHandleEfl::HandleDirection SelectionHandleEfl::CalculateDirection(
161     const gfx::Point& point) const {
162   bool reverse_horizontally = false, reverse_vertically = false;
163   int handleHeight;
164   edje_object_part_geometry_get(handle_, "handle", 0, 0, 0, &handleHeight);
165
166   auto visible_viewport_rect = controller_.GetVisibleViewportRect();
167   gfx::Point conv_point(point.x() + visible_viewport_rect.x(),
168                         point.y() + visible_viewport_rect.y());
169
170   gfx::Rect reverse_direction_rect = gfx::Rect(
171       visible_viewport_rect.x() + kReverseMargin, visible_viewport_rect.y(),
172       visible_viewport_rect.width() - 2 * kReverseMargin,
173       visible_viewport_rect.height() - handleHeight);
174
175   if (!reverse_direction_rect.Contains(conv_point)) {
176     if (conv_point.x() <= reverse_direction_rect.x() ||
177         conv_point.x() >= reverse_direction_rect.right()) {
178       reverse_vertically = true;
179     }
180     if (conv_point.y() <= reverse_direction_rect.y() ||
181         conv_point.y() >= reverse_direction_rect.bottom()) {
182       reverse_horizontally = true;
183     }
184   }
185
186   if (handle_type_ != HANDLE_TYPE_INPUT) {
187     if (reverse_vertically) {
188       if (reverse_horizontally)
189         return DirectionTopReverse;
190       else
191         return DirectionBottomReverse;
192     } else {
193       if (reverse_horizontally)
194         return DirectionTopNormal;
195       else
196         return DirectionBottomNormal;
197     }
198   } else {
199     // Input handle can only be rotated horizontally
200     if (reverse_horizontally)
201       return DirectionTopNormal;
202     else
203       return DirectionBottomNormal;
204   }
205
206   NOTREACHED();
207   return DirectionBottomNormal;
208 }
209
210 void SelectionHandleEfl::ChangeObjectDirection(HandleDirection direction) {
211   TRACE_EVENT2("selection,efl", __PRETTY_FUNCTION__,
212                "handle type", handle_type_, "direction", direction);
213
214   is_top_ = (direction == DirectionTopNormal || direction == DirectionTopReverse);
215
216   switch (direction) {
217     case DirectionBottomNormal:
218       if (handle_type_ == HANDLE_TYPE_INPUT)
219         edje_object_signal_emit(handle_, "edje,cursor,handle,show", "edje");
220       else
221         edje_object_signal_emit(handle_, "elm,state,bottom", "elm");
222       break;
223     case DirectionBottomReverse:
224       if (handle_type_ == HANDLE_TYPE_INPUT)
225         edje_object_signal_emit(handle_, "edje,cursor,handle,show", "edje");
226       else
227         edje_object_signal_emit(handle_, "elm,state,bottom,reversed", "elm");
228       break;
229     case DirectionTopNormal:
230       if (handle_type_ == HANDLE_TYPE_INPUT)
231         edje_object_signal_emit(handle_, "edje,cursor,handle,top", "edje");
232       else
233         edje_object_signal_emit(handle_, "elm,state,top", "elm");
234       break;
235     case DirectionTopReverse:
236       if (handle_type_ == HANDLE_TYPE_INPUT)
237         edje_object_signal_emit(handle_, "edje,cursor,handle,top", "edje");
238       else
239         edje_object_signal_emit(handle_, "elm,state,top,reversed", "elm");
240       break;
241   }
242
243   switch (handle_type_) {
244     case HANDLE_TYPE_LEFT:
245       evas_object_smart_callback_call(
246           controller_.rwhva()->offscreen_helper()->ewk_view(),
247           "selection,handle,left,direction", &direction);
248       break;
249     case HANDLE_TYPE_RIGHT:
250       evas_object_smart_callback_call(
251           controller_.rwhva()->offscreen_helper()->ewk_view(),
252           "selection,handle,right,direction", &direction);
253       break;
254     case HANDLE_TYPE_INPUT:
255       evas_object_smart_callback_call(
256           controller_.rwhva()->offscreen_helper()->ewk_view(),
257           "selection,handle,large,direction", &direction);
258       break;
259   }
260 }
261
262 gfx::Rect SelectionHandleEfl::GetSelectionRect() const {
263   gfx::Rect rect;
264
265   switch(handle_type_) {
266   case HANDLE_TYPE_RIGHT:
267     return controller_.GetRightRect();
268   case HANDLE_TYPE_LEFT:
269     return controller_.GetLeftRect();
270   case HANDLE_TYPE_INPUT:
271     // no rect for this type of handle
272     break;
273   }
274
275   return gfx::Rect();
276 }
277
278 void SelectionHandleEfl::MoveObject(const gfx::Point& point, bool ignore_parent_view_offset) {
279   gfx::Rect view_bounds =
280       controller_.rwhva()->offscreen_helper()->GetViewBoundsInPix();
281   int handle_x = point.x() + view_bounds.x();
282   int handle_y = point.y() + view_bounds.y();
283
284   if (IsTop()) {
285     if (handle_type_ == HANDLE_TYPE_RIGHT)
286       handle_y -= controller_.GetRightRect().height();
287     else
288       handle_y -= controller_.GetLeftRect().height();
289   }
290
291   // Prevent selection handles from moving out of visible webview.
292   if (handle_type_ != HANDLE_TYPE_INPUT) {
293     const gfx::Rect& viewport_rect = controller_.GetVisibleViewportRect();
294     int viewport_rect_x = viewport_rect.x();
295     int viewport_rect_y = viewport_rect.y();
296     int viewport_rect_w = viewport_rect.width();
297     int viewport_rect_h = viewport_rect.height();
298
299     if (handle_y < viewport_rect_y)
300       handle_y = viewport_rect_y;
301     else if (handle_y > viewport_rect_h + viewport_rect_y)
302       handle_y = viewport_rect_h + viewport_rect_y;
303
304     if (handle_x < viewport_rect_x)
305       handle_x = viewport_rect_x;
306     else if (handle_x > viewport_rect_w + viewport_rect_x)
307       handle_x = viewport_rect_w + viewport_rect_x;
308   }
309
310   evas_object_move(handle_, handle_x, handle_y);
311 }
312
313 } // namespace content