2 * Copyright (C) 2012 Samsung Electronics
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
28 #if ENABLE(TIZEN_WEBKIT2_TEXT_SELECTION)
29 #include "TextSelection.h"
31 #include "EditorState.h"
32 #include "NativeWebMouseEvent.h"
34 #include <Elementary.h>
36 using namespace WebCore;
40 TextSelection::TextSelection(Evas_Object* viewWidget, WebPageProxy* page, PageClientImpl* pageClient)
41 : m_object(viewWidget),
43 m_pageClient(pageClient),
44 m_isTextSelectionDowned(false),
45 m_isTextSelectionMode(false),
46 m_isTextSelectionEnable(true),
47 m_autoClearTextSelectionMode(true)
52 m_viewImpl = EwkViewImpl::fromEvasObject(m_object);
54 const Eina_List* defaultThemeList = elm_theme_list_get(0);
58 EINA_LIST_FOREACH(defaultThemeList, l, theme) {
59 char* themePath = elm_theme_list_item_path_get((const char*)theme, 0);
62 m_leftHandle = new TextSelectionHandle(m_object, themePath, "elm/entry/selection/block_handle_left", true, this);
63 m_rightHandle = new TextSelectionHandle(m_object, themePath, "elm/entry/selection/block_handle_right", false, this);
70 m_magnifier = new TextSelectionMagnifier(m_object, page, pageClient);
72 evas_object_event_callback_add(m_object, EVAS_CALLBACK_MOUSE_UP, onMouseUp, this);
75 TextSelection::~TextSelection()
85 evas_object_event_callback_del(m_object, EVAS_CALLBACK_MOUSE_UP, onMouseUp);
88 void TextSelection::update()
90 if (isTextSelectionMode() ) {
91 EditorState editorState = m_page->editorState();
92 if (!editorState.selectionIsRange) {
93 if (editorState.isContentEditable && !evas_object_focus_get(m_object)) {
94 WebCore::IntRect caretRect;
95 m_page->getCaretPosition(caretRect);
96 if (!caretRect.isEmpty())
99 WebCore::IntRect leftRect;
100 WebCore::IntRect rightRect;
101 if (isTextSelectionDowned() || m_page->getSelectionHandlers(leftRect, rightRect))
104 setIsTextSelectionMode(false);
106 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
107 m_page->getTextStyleStateForSelection();
110 if (!isTextSelectionDowned() && !isTextSelectionHandleDowned()) {
113 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
114 m_page->getTextStyleStateForSelection();
119 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
121 EditorState editorState = m_page->editorState();
122 if (editorState.isContentEditable && !editorState.selectionIsRange) {
123 WebCore::IntRect caretRect;
124 m_page->getCaretPosition(caretRect);
125 if (!caretRect.isEmpty()) {
126 m_page->getTextStyleStateForSelection();
133 void TextSelection::setIsTextSelectionMode(bool isTextSelectionMode)
135 if (!autoClearTextSelectionMode())
138 if (!isTextSelectionMode) {
143 m_isTextSelectionMode = isTextSelectionMode;
146 void TextSelection::clear()
148 EditorState editorState = m_page->editorState();
149 if (!editorState.selectionIsRange)
152 m_page->executeEditCommand("Unselect");
155 void TextSelection::hide()
162 void TextSelection::updateHandlers()
164 WebCore::IntRect leftRect, rightRect;
165 if (!m_page->getSelectionHandlers(leftRect, rightRect)) {
170 AffineTransform toEvasTransform = m_viewImpl->transformToScene();
171 WebCore::IntPoint leftEvasPoint = toEvasTransform.mapPoint(leftRect.minXMaxYCorner());
172 WebCore::IntPoint rightEvasPoint = toEvasTransform.mapPoint(rightRect.maxXMaxYCorner());
174 EditorState editorState = m_page->editorState();
175 if (editorState.isContentEditable) {
176 m_leftHandle->hide();
177 m_rightHandle->hide();
179 WebCore::IntRect editorRect = editorState.editorRect;
180 WebCore::IntPoint editorLeftEvasPoint = toEvasTransform.mapPoint(editorRect.location());
181 WebCore::IntPoint editorRightEvasPoint = toEvasTransform.mapPoint(editorRect.maxXMaxYCorner());
182 int webViewX, webViewY, webViewWidth, webViewHeight;
184 evas_object_geometry_get(m_object, &webViewX, &webViewY, &webViewWidth, &webViewHeight);
185 if ((editorLeftEvasPoint.x() <= leftEvasPoint.x() && editorLeftEvasPoint.y() <= leftEvasPoint.y())
186 && (webViewX <= leftEvasPoint.x() && webViewY <= leftEvasPoint.y())) {
187 m_leftHandle->move(leftEvasPoint);
188 m_leftHandle->show();
191 if ((editorRightEvasPoint.x() >= rightEvasPoint.x() && editorRightEvasPoint.y() >= rightEvasPoint.y())
192 && ((webViewX + webViewWidth) >= rightEvasPoint.x() && (webViewY <= rightEvasPoint.y() && (webViewY + webViewHeight) >= rightEvasPoint.y()))) {
193 m_rightHandle->move(rightEvasPoint);
194 m_rightHandle->show();
197 m_leftHandle->move(leftEvasPoint);
198 m_leftHandle->show();
200 m_rightHandle->move(rightEvasPoint);
201 m_rightHandle->show();
205 void TextSelection::hideHandlers()
207 m_leftHandle->hide();
208 m_rightHandle->hide();
211 void TextSelection::showMagnifier()
216 void TextSelection::hideMagnifier()
221 void TextSelection::updateMagnifier(const IntPoint& position)
223 m_magnifier->update(position);
224 m_magnifier->move(position);
227 void TextSelection::showContextMenu()
229 if (!m_isTextSelectionEnable)
232 EditorState editorState = m_page->editorState();
233 if (editorState.isInPasswordField)
236 if (!editorState.selectionIsRange && !editorState.isContentEditable)
239 WebCore::IntPoint point;
240 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
241 bool isPresentInViewPort = false;
243 if (editorState.selectionIsRange) {
244 WebCore::IntRect leftRect, rightRect;
245 if (!m_page->getSelectionHandlers(leftRect, rightRect)) {
249 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
250 // Checking if this point is in viewport area. If the calcualated
251 // point/Left/Right point are in view port then draw else do not draw the
252 // context menu. Only draw the selection points.
253 FloatRect unscaledRect = FloatRect(m_pageClient->visibleContentRect());
254 unscaledRect.scale(1 / m_pageClient->scaleFactor());
255 IntRect viewportRect = enclosingIntRect(unscaledRect);
257 WebCore::IntPoint visiblePoint = leftRect.center();
258 if (viewportRect.contains(visiblePoint)) {
259 // First check That the modified points are present in view port
260 point = visiblePoint;
261 isPresentInViewPort = true;
262 } else if (viewportRect.contains(leftRect.location())) {
263 // else if the calculated point is not in the view port area the
264 // draw context menu at left point if visible
265 point = leftRect.location();
266 isPresentInViewPort = true;
267 } else if (viewportRect.contains(rightRect.maxXMinYCorner())) {
268 // else if the calculated point is not in the view port area the
269 // draw context menu at right point if visible
270 point = rightRect.maxXMinYCorner();
271 isPresentInViewPort = true;
274 point = leftRect.center();
276 } else if (editorState.isContentEditable) {
277 WebCore::IntRect caretRect;
278 m_page->getCaretPosition(caretRect);
280 if (caretRect.isEmpty())
283 point = caretRect.center();
284 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
285 isPresentInViewPort = true;
288 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
289 if (!isPresentInViewPort)
293 // show context menu if its in viewport else do not show the contextmenu
295 Ewk_View_Smart_Data* smartData = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(m_object));
296 if (!smartData || !smartData->api || !smartData->api->mouse_down || !smartData->api->mouse_up)
299 point = m_viewImpl->transformToScene().mapPoint(point);
300 Evas* evas = evas_object_evas_get(m_object);
303 Evas_Event_Mouse_Down mouseDown;
304 mouseDown.button = 3;
305 mouseDown.output.x = mouseDown.canvas.x = point.x();
306 mouseDown.output.y = mouseDown.canvas.y = point.y();
308 mouseDown.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
309 mouseDown.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
310 mouseDown.flags = EVAS_BUTTON_NONE;
311 mouseDown.timestamp = ecore_time_get() * 1000;
312 mouseDown.event_flags = EVAS_EVENT_FLAG_NONE;
314 smartData->api->mouse_down(smartData, &mouseDown);
317 Evas_Event_Mouse_Up mouseUp;
319 mouseUp.output.x = mouseUp.canvas.x = point.x();
320 mouseUp.output.y = mouseUp.canvas.y = point.y();
322 mouseUp.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
323 mouseUp.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
324 mouseUp.flags = EVAS_BUTTON_NONE;
325 mouseUp.timestamp = ecore_time_get() * 1000;
326 mouseUp.event_flags = EVAS_EVENT_FLAG_NONE;
328 smartData->api->mouse_up(smartData, &mouseUp);
331 void TextSelection::hideContextMenu()
333 if (!m_isTextSelectionEnable)
336 m_page->hideContextMenu();
339 void TextSelection::setLeftSelectionToEvasPoint(const IntPoint& evasPoint)
341 m_page->setLeftSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
345 void TextSelection::setRightSelectionToEvasPoint(const IntPoint& evasPoint)
347 m_page->setRightSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
352 void TextSelection::handleMouseDown(TextSelectionHandle* handle, const IntPoint& /*position*/)
354 WebCore::IntPoint basePosition;
355 EditorState editorState = m_page->editorState();
357 if (editorState.selectionIsRange) {
358 WebCore::IntRect leftRect, rightRect;
359 if (!m_page->getSelectionHandlers(leftRect, rightRect)) {
364 if (handle->isLeft()) {
365 basePosition.setX(leftRect.x());
366 basePosition.setY(leftRect.y() + (leftRect.height()/2));
368 basePosition.setX(rightRect.x() + rightRect.width());
369 basePosition.setY(rightRect.y() + (rightRect.height()/2));
372 handle->setBasePositionForMove(m_viewImpl->transformToScene().mapPoint(basePosition));
377 updateMagnifier(handle->position());
381 void TextSelection::handleMouseMove(TextSelectionHandle* handle, const IntPoint& position)
383 if (handle->isLeft())
384 setLeftSelectionToEvasPoint(position);
386 setRightSelectionToEvasPoint(position);
388 updateMagnifier(handle->position());
391 void TextSelection::handleMouseUp(TextSelectionHandle* /* handle */, const IntPoint& /* position */)
395 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
396 m_page->getTextStyleStateForSelection();
400 bool TextSelection::isMagnifierVisible()
402 return m_magnifier->isVisible();
405 void TextSelection::updateHandlesAndContextMenu(bool isShow, bool isScrolling)
407 if (isTextSelectionDowned() && !isScrolling) {
420 if (isScrolling && isMagnifierVisible())
424 void TextSelection::startMoveAnimator()
426 if (!isTextSelectionEnable() || !isTextSelectionDowned())
430 m_moveAnimator = ecore_animator_add(moveAnimatorCallback, this);
433 void TextSelection::stopMoveAnimator()
435 if (m_moveAnimator) {
436 ecore_animator_del(m_moveAnimator);
441 void TextSelection::onMouseUp(void* data, Evas*, Evas_Object*, void* eventInfo)
443 static_cast<TextSelection*>(data)->textSelectionUp(IntPoint());
446 Eina_Bool TextSelection::moveAnimatorCallback(void* data)
448 TextSelection* textSelection = static_cast<TextSelection*>(data);
450 Evas_Coord_Point point;
451 evas_pointer_canvas_xy_get(evas_object_evas_get(textSelection->m_object), &point.x, &point.y);
452 textSelection->textSelectionMove(IntPoint(point.x, point.y));
454 return ECORE_CALLBACK_RENEW;
457 // 'return false' means text selection is not possible for point.
458 // 'return true' means text selection is possible, but it can be succeed or failed for some reason.
459 bool TextSelection::textSelectionDown(const IntPoint& point, bool isStartedTextSelectionFromOutside)
461 setIsTextSelectionMode(false);
463 IntPoint viewPoint = m_viewImpl->transformFromScene().mapPoint(point);
464 WebHitTestResult::Data hitTestResultData;
465 #if ENABLE(TIZEN_WEBKIT2_HIT_TEST)
466 hitTestResultData = m_pageClient->page()->hitTestResultAtPoint(viewPoint);
469 // If node under point has link, we can not process text selection.
470 if (!hitTestResultData.absoluteImageURL.isEmpty()
471 #if ENABLE(TIZEN_DRAG_SUPPORT)
472 || hitTestResultData.isDragSupport
474 || !hitTestResultData.absoluteLinkURL.isEmpty()
475 || !hitTestResultData.absoluteMediaURL.isEmpty())
478 // Process gesture_end(EWK_GESTURE_TAP) to activate the editing if node under point is editable.
479 if (hitTestResultData.context & WebHitTestResult::HitTestResultContextEditable) {
480 const Ewk_View_Smart_Data* smartData = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(m_pageClient->viewWidget()));
481 if (smartData->api && smartData->api->gesture_end) {
482 Ewk_Event_Gesture gestureEvent = {EWK_GESTURE_TAP, {point.x(), point.y()}, {0, 0}, 0, 1, ecore_time_get() * 1000};
483 smartData->api->gesture_end(const_cast<Ewk_View_Smart_Data*>(smartData), &gestureEvent);
487 bool result = m_page->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
491 if (isTextSelectionMode()) {
494 setIsTextSelectionMode(true);
496 setIsTextSelectionDowned(true);
498 updateMagnifier(point);
506 void TextSelection::textSelectionMove(const IntPoint& point, bool isStartedTextSelectionFromOutside)
508 if (!isTextSelectionMode()) {
513 WebCore::IntPoint viewPoint = m_viewImpl->transformFromScene().mapPoint(point);
514 m_page->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
516 updateMagnifier(point);
520 void TextSelection::textSelectionUp(const IntPoint& point, bool isStartedTextSelectionFromOutside)
524 if (!isTextSelectionMode())
527 setIsTextSelectionDowned(false);
530 EditorState editorState = m_page->editorState();
531 if (editorState.selectionIsRange || editorState.isContentEditable) {
532 if (editorState.selectionIsRange)
536 } else if (!isStartedTextSelectionFromOutside)
537 setIsTextSelectionMode(false);
539 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
540 m_page->getTextStyleStateForSelection();
544 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
545 TextSelectionHandle* TextSelection::getSelectedHandle(const IntPoint& position)
547 WebCore::IntRect leftHandleRect = m_leftHandle->getHandleRect();
548 if (leftHandleRect.contains(position))
551 WebCore::IntRect rightHandleRect = m_rightHandle->getHandleRect();
552 if (rightHandleRect.contains(position))
553 return m_rightHandle;
558 void TextSelection::textSelectionHandleDown(const IntPoint& position)
560 TextSelectionHandle* selectedHandle = getSelectedHandle(position);
561 if (selectedHandle) {
562 selectedHandle->mouseDown(position);
563 if (selectedHandle->isMouseDowned())
567 EditorState editorState = m_page->editorState();
568 if (editorState.isContentEditable)
569 setIsTextSelectionMode(false);
572 void TextSelection::textSelectionHandleMove(const IntPoint& position)
574 if (isTextSelectionHandleDowned()) {
575 TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
576 selectedHandle->mouseMove(position);
580 void TextSelection::textSelectionHandleUp()
582 if (isTextSelectionHandleDowned()) {
583 TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
584 selectedHandle->mouseUp();
589 } // namespace WebKit
591 #endif // TIZEN_WEBKIT2_TEXT_SELECTION