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 "EwkViewImpl.h"
33 #include "NativeWebMouseEvent.h"
35 #include <Elementary.h>
37 using namespace WebCore;
41 TextSelection::TextSelection(EwkViewImpl* viewImpl)
42 : m_viewImpl(viewImpl)
43 , m_isTextSelectionDowned(false)
44 , m_isTextSelectionMode(false)
47 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
53 const Eina_List* defaultThemeList = elm_theme_list_get(0);
57 EINA_LIST_FOREACH(defaultThemeList, l, theme) {
58 char* themePath = elm_theme_list_item_path_get((const char*)theme, 0);
61 m_leftHandle = new TextSelectionHandle(m_viewImpl->view(), themePath, "elm/entry/selection/block_handle_left", true, this);
62 m_rightHandle = new TextSelectionHandle(m_viewImpl->view(), themePath, "elm/entry/selection/block_handle_right", false, this);
69 m_magnifier = new TextSelectionMagnifier(m_viewImpl);
71 evas_object_event_callback_add(m_viewImpl->view(), EVAS_CALLBACK_MOUSE_UP, onMouseUp, this);
74 TextSelection::~TextSelection()
85 ecore_animator_del(m_moveAnimator);
90 ecore_timer_del(m_showTimer);
93 evas_object_event_callback_del(m_viewImpl->view(), EVAS_CALLBACK_MOUSE_UP, onMouseUp);
96 void TextSelection::update()
98 EditorState editorState = m_viewImpl->page()->editorState();
99 if (editorState.updateEditorRectOnly)
102 if (isTextSelectionMode()) {
103 if (!editorState.selectionIsRange) {
104 if (editorState.isContentEditable && !evas_object_focus_get(m_viewImpl->view())) {
105 WebCore::IntRect caretRect;
106 m_viewImpl->page()->getCaretPosition(caretRect);
107 if (!caretRect.isEmpty())
110 WebCore::IntRect leftRect;
111 WebCore::IntRect rightRect;
112 if (isTextSelectionDowned() || m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
115 setIsTextSelectionMode(false);
117 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
118 m_viewImpl->page()->getTextStyleStateForSelection();
121 if (!isTextSelectionDowned() && !isTextSelectionHandleDowned()) {
124 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
125 m_viewImpl->page()->getTextStyleStateForSelection();
130 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
132 if (editorState.isContentEditable && !editorState.selectionIsRange) {
133 WebCore::IntRect caretRect;
134 m_viewImpl->page()->getCaretPosition(caretRect);
135 if (!caretRect.isEmpty()) {
136 m_viewImpl->page()->getTextStyleStateForSelection();
143 void TextSelection::setIsTextSelectionMode(bool isTextSelectionMode)
145 if (!isAutomaticClearEnabled())
148 if (!isTextSelectionMode) {
151 m_leftHandle->setIsMouseDowned(false);
152 m_rightHandle->setIsMouseDowned(false);
155 m_isTextSelectionMode = isTextSelectionMode;
158 void TextSelection::clear()
160 EditorState editorState = m_viewImpl->page()->editorState();
161 if (!editorState.selectionIsRange)
164 m_viewImpl->page()->selectionRangeClear();
167 void TextSelection::hide()
174 void TextSelection::updateHandlers()
176 WebCore::IntRect leftRect, rightRect;
177 if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
180 m_lastLeftHandleRect = leftRect;
181 m_lastRightHandleRect = rightRect;
183 AffineTransform toEvasTransform = m_viewImpl->transformToScene();
184 WebCore::IntPoint leftEvasPoint = toEvasTransform.mapPoint(leftRect.minXMaxYCorner());
185 WebCore::IntPoint rightEvasPoint = toEvasTransform.mapPoint(rightRect.maxXMaxYCorner());
187 EditorState editorState = m_viewImpl->page()->editorState();
188 if (editorState.isContentEditable) {
189 m_leftHandle->hide();
190 m_rightHandle->hide();
192 WebCore::IntRect editorRect = editorState.editorRect;
193 WebCore::IntPoint editorLeftEvasPoint = toEvasTransform.mapPoint(editorRect.location());
194 WebCore::IntPoint editorRightEvasPoint = toEvasTransform.mapPoint(editorRect.maxXMaxYCorner());
195 int webViewX, webViewY, webViewWidth, webViewHeight;
197 evas_object_geometry_get(m_viewImpl->view(), &webViewX, &webViewY, &webViewWidth, &webViewHeight);
198 if ((editorLeftEvasPoint.x() <= leftEvasPoint.x() && editorLeftEvasPoint.y() <= leftEvasPoint.y())
199 && (webViewX <= leftEvasPoint.x() && webViewY <= leftEvasPoint.y())) {
200 m_leftHandle->move(leftEvasPoint);
201 m_leftHandle->show();
204 if ((editorRightEvasPoint.x() >= rightEvasPoint.x() && editorRightEvasPoint.y() >= rightEvasPoint.y())
205 && ((webViewX + webViewWidth) >= rightEvasPoint.x() && (webViewY <= rightEvasPoint.y() && (webViewY + webViewHeight) >= rightEvasPoint.y()))) {
206 m_rightHandle->move(rightEvasPoint);
207 m_rightHandle->show();
210 m_leftHandle->move(leftEvasPoint);
211 m_leftHandle->show();
213 m_rightHandle->move(rightEvasPoint);
214 m_rightHandle->show();
218 void TextSelection::hideHandlers()
220 m_leftHandle->hide();
221 m_rightHandle->hide();
224 void TextSelection::showMagnifier()
229 void TextSelection::hideMagnifier()
234 void TextSelection::updateMagnifier(const IntPoint& position)
236 m_magnifier->update(position);
237 m_magnifier->move(position);
240 void TextSelection::showContextMenu()
245 EditorState editorState = m_viewImpl->page()->editorState();
246 if (!editorState.selectionIsRange && !editorState.isContentEditable)
249 WebCore::IntPoint point;
250 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
251 bool isPresentInViewPort = false;
253 if (editorState.selectionIsRange) {
254 WebCore::IntRect leftRect, rightRect;
255 if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
258 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
259 // Checking if this point is in viewport area. If the calcualated
260 // point/Left/Right point are in view port then draw else do not draw the
261 // context menu. Only draw the selection points.
262 FloatRect unscaledRect = FloatRect(m_viewImpl->pageClient->visibleContentRect());
263 unscaledRect.scale(1 / m_viewImpl->pageClient->scaleFactor());
264 IntRect viewportRect = enclosingIntRect(unscaledRect);
266 WebCore::IntPoint visiblePoint = leftRect.center();
267 if (viewportRect.contains(visiblePoint)) {
268 // First check That the modified points are present in view port
269 point = visiblePoint;
270 isPresentInViewPort = true;
271 } else if (viewportRect.contains(leftRect.location())) {
272 // else if the calculated point is not in the view port area the
273 // draw context menu at left point if visible
274 point = leftRect.location();
275 isPresentInViewPort = true;
276 } else if (viewportRect.contains(rightRect.maxXMinYCorner())) {
277 // else if the calculated point is not in the view port area the
278 // draw context menu at right point if visible
279 point = rightRect.maxXMinYCorner();
280 isPresentInViewPort = true;
283 if (isPresentInViewPort && editorState.isContentEditable) {
284 // In case of single line editor box.
285 if (leftRect == rightRect) {
286 // draw context menu at center point of visible selection range in the editor box
287 IntRect editorRect = editorState.editorRect;
288 leftRect.intersect(editorRect);
289 if (!leftRect.isEmpty())
290 point = leftRect.center();
292 // not draw context menu if there is no visible selection range in the editor box
293 isPresentInViewPort = false;
298 point = leftRect.center();
300 } else if (editorState.isContentEditable) {
301 WebCore::IntRect caretRect;
302 m_viewImpl->page()->getCaretPosition(caretRect);
304 if (caretRect.isEmpty())
307 point = caretRect.center();
308 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
309 isPresentInViewPort = true;
312 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
313 if (!isPresentInViewPort)
317 // show context menu if its in viewport else do not show the contextmenu
319 Ewk_View_Smart_Data* smartData = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(m_viewImpl->view()));
320 if (!smartData || !smartData->api || !smartData->api->mouse_down || !smartData->api->mouse_up)
323 point = m_viewImpl->transformToScene().mapPoint(point);
324 Evas* evas = evas_object_evas_get(m_viewImpl->view());
327 Evas_Event_Mouse_Down mouseDown;
328 mouseDown.button = 3;
329 mouseDown.output.x = mouseDown.canvas.x = point.x();
330 mouseDown.output.y = mouseDown.canvas.y = point.y();
332 mouseDown.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
333 mouseDown.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
334 mouseDown.flags = EVAS_BUTTON_NONE;
335 mouseDown.timestamp = ecore_time_get() * 1000;
336 mouseDown.event_flags = EVAS_EVENT_FLAG_NONE;
338 smartData->api->mouse_down(smartData, &mouseDown);
341 Evas_Event_Mouse_Up mouseUp;
343 mouseUp.output.x = mouseUp.canvas.x = point.x();
344 mouseUp.output.y = mouseUp.canvas.y = point.y();
346 mouseUp.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
347 mouseUp.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
348 mouseUp.flags = EVAS_BUTTON_NONE;
349 mouseUp.timestamp = ecore_time_get() * 1000;
350 mouseUp.event_flags = EVAS_EVENT_FLAG_NONE;
352 smartData->api->mouse_up(smartData, &mouseUp);
355 void TextSelection::hideContextMenu()
360 m_viewImpl->page()->hideContextMenu();
363 void TextSelection::setLeftSelectionToEvasPoint(const IntPoint& evasPoint)
365 m_viewImpl->page()->setLeftSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
369 void TextSelection::setRightSelectionToEvasPoint(const IntPoint& evasPoint)
371 m_viewImpl->page()->setRightSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
376 void TextSelection::handleMouseDown(TextSelectionHandle* handle, const IntPoint& /*position*/)
378 WebCore::IntPoint basePosition;
379 EditorState editorState = m_viewImpl->page()->editorState();
381 if (editorState.selectionIsRange) {
382 WebCore::IntRect leftRect, rightRect;
383 if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect)) {
388 if (handle->isLeft()) {
389 basePosition.setX(leftRect.x());
390 basePosition.setY(leftRect.y() + (leftRect.height()/2));
392 basePosition.setX(rightRect.x() + rightRect.width());
393 basePosition.setY(rightRect.y() + (rightRect.height()/2));
396 handle->setBasePositionForMove(m_viewImpl->transformToScene().mapPoint(basePosition));
401 updateMagnifier(handle->position());
405 void TextSelection::handleMouseMove(TextSelectionHandle* handle, const IntPoint& position)
407 if (handle->isLeft())
408 setLeftSelectionToEvasPoint(position);
410 setRightSelectionToEvasPoint(position);
412 updateMagnifier(handle->position());
415 void TextSelection::handleMouseUp(TextSelectionHandle* /* handle */, const IntPoint& /* position */)
419 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
420 m_viewImpl->page()->getTextStyleStateForSelection();
424 bool TextSelection::isMagnifierVisible()
426 return m_magnifier->isVisible();
429 void TextSelection::updateHandlesAndContextMenu(bool isShow, bool isScrolling)
431 if (isTextSelectionDowned() && !isScrolling) {
437 #if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
438 if (m_viewImpl->pageClient->isClipboardWindowOpened())
441 if (m_viewImpl->gestureClient->isGestureWorking())
451 if (isScrolling && isMagnifierVisible())
455 void TextSelection::startMoveAnimator()
457 if (!isEnabled() || !isTextSelectionDowned())
461 m_moveAnimator = ecore_animator_add(moveAnimatorCallback, this);
464 void TextSelection::stopMoveAnimator()
466 if (m_moveAnimator) {
467 ecore_animator_del(m_moveAnimator);
472 void TextSelection::onMouseUp(void* data, Evas*, Evas_Object*, void* eventInfo)
474 static_cast<TextSelection*>(data)->textSelectionUp(IntPoint());
477 Eina_Bool TextSelection::moveAnimatorCallback(void* data)
479 TextSelection* textSelection = static_cast<TextSelection*>(data);
481 Evas_Coord_Point point;
482 evas_pointer_canvas_xy_get(evas_object_evas_get(textSelection->m_viewImpl->view()), &point.x, &point.y);
483 textSelection->textSelectionMove(IntPoint(point.x, point.y));
485 return ECORE_CALLBACK_RENEW;
488 // 'return false' means text selection is not possible for point.
489 // 'return true' means text selection is possible.
490 bool TextSelection::textSelectionDown(const IntPoint& point, bool isStartedTextSelectionFromOutside)
492 // text selection should be ignored when longtap on handle from osp
493 if (!isEnabled() && isTextSelectionHandleDowned())
496 #if ENABLE(TIZEN_ISF_PORT)
497 m_viewImpl->inputMethodContext()->resetIMFContext();
499 setIsTextSelectionMode(false);
501 IntPoint contentsPoint = m_viewImpl->transformFromScene().mapPoint(point);
502 bool result = m_viewImpl->page()->selectClosestWord(contentsPoint, isStartedTextSelectionFromOutside);
506 if (isTextSelectionMode()) {
509 setIsTextSelectionMode(true);
511 setIsTextSelectionDowned(true);
513 updateMagnifier(point);
521 static int s_textSelectionMargin = 5;
523 void TextSelection::textSelectionMove(const IntPoint& point, bool isStartedTextSelectionFromOutside)
525 // text selection should be ignored when longtap on handle from osp
526 if (!isEnabled() && isTextSelectionHandleDowned())
529 if (!isTextSelectionMode()) {
534 WebCore::IntPoint viewPoint;
535 EditorState editorState = m_viewImpl->page()->editorState();
536 if (editorState.isContentEditable) {
537 IntRect mapRect = m_viewImpl->transformToScene().mapRect(editorState.editorRect);
538 IntPoint updatedPoint = point;
539 bool scrolledY = false;
540 if (point.y() < mapRect.y()) {
541 updatedPoint.setY(mapRect.y() + s_textSelectionMargin);
542 if (m_viewImpl->page()->scrollContentByLine(point,WebCore::DirectionBackward)) {
544 updateMagnifier(updatedPoint);
546 } else if (point.y() > mapRect.maxY()) {
547 updatedPoint.setY(mapRect.maxY() - s_textSelectionMargin);
548 if (m_viewImpl->page()->scrollContentByLine(point,WebCore::DirectionForward)) {
550 updateMagnifier(updatedPoint);
554 bool scrolledX = false;
555 if (point.x() < mapRect.x()) {
556 updatedPoint.setX(mapRect.x() + s_textSelectionMargin);
557 if (m_viewImpl->page()->scrollContentByCharacter(point,WebCore::DirectionBackward)) {
559 updateMagnifier(updatedPoint);
561 } else if (point.x() > mapRect.maxX()) {
562 updatedPoint.setX(mapRect.maxX() - s_textSelectionMargin);
563 if (m_viewImpl->page()->scrollContentByCharacter(point,WebCore::DirectionForward)) {
565 updateMagnifier(updatedPoint);
569 if (!scrolledX && !scrolledY) {
570 viewPoint = m_viewImpl->transformFromScene().mapPoint(updatedPoint);
571 m_viewImpl->page()->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
572 updateMagnifier(updatedPoint);
575 viewPoint = m_viewImpl->transformFromScene().mapPoint(point);
576 m_viewImpl->page()->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
577 updateMagnifier(point);
582 void TextSelection::textSelectionUp(const IntPoint& point, bool isStartedTextSelectionFromOutside)
584 // text selection should be ignored when longtap on handle from osp
585 if (!isEnabled() && isTextSelectionHandleDowned())
590 if (!isTextSelectionMode() || !isTextSelectionDowned())
593 setIsTextSelectionDowned(false);
596 EditorState editorState = m_viewImpl->page()->editorState();
597 if (editorState.selectionIsRange || editorState.isContentEditable) {
598 if (editorState.selectionIsRange)
602 } else if (!isStartedTextSelectionFromOutside)
603 setIsTextSelectionMode(false);
605 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
606 m_viewImpl->page()->getTextStyleStateForSelection();
610 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
611 TextSelectionHandle* TextSelection::getSelectedHandle(const IntPoint& position)
613 WebCore::IntRect leftHandleRect = m_leftHandle->getHandleRect();
614 if (leftHandleRect.contains(position))
617 WebCore::IntRect rightHandleRect = m_rightHandle->getHandleRect();
618 if (rightHandleRect.contains(position))
619 return m_rightHandle;
624 void TextSelection::textSelectionHandleDown(const IntPoint& position)
626 m_selectedHandle = getSelectedHandle(position);
627 if (m_selectedHandle)
628 m_selectedHandle->mouseDown(position);
630 m_leftHandle->setIsMouseDowned(false);
631 m_rightHandle->setIsMouseDowned(false);
635 void TextSelection::textSelectionHandleMove(const IntPoint& position)
637 if (m_selectedHandle && isTextSelectionHandleDowned())
638 m_selectedHandle->mouseMove(position);
641 void TextSelection::textSelectionHandleUp()
643 if (m_selectedHandle && isTextSelectionHandleDowned()) {
644 m_selectedHandle->mouseUp();
645 m_selectedHandle = 0;
650 bool TextSelection::isEnabled()
652 return ewk_settings_text_selection_enabled_get(ewk_view_settings_get(m_viewImpl->view()));
655 bool TextSelection::isAutomaticClearEnabled()
657 return ewk_settings_clear_text_selection_automatically_get(ewk_view_settings_get(m_viewImpl->view()));
660 void TextSelection::requestToShow()
663 ecore_timer_del(m_showTimer);
664 m_showTimer = ecore_timer_loop_add((double)200.0/1000.0, showTimerCallback, this);
667 Eina_Bool TextSelection::showTimerCallback(void* data)
669 TextSelection* textSelection = static_cast<TextSelection*>(data);
670 textSelection->showHandlesAndContextMenu();
672 return ECORE_CALLBACK_RENEW;
675 void TextSelection::showHandlesAndContextMenu()
677 WebCore::IntRect leftRect, rightRect;
678 if (m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect)) {
679 if ((leftRect == m_lastLeftHandleRect) && (rightRect == m_lastRightHandleRect)) {
681 ecore_timer_del(m_showTimer);
685 updateHandlesAndContextMenu(true);
688 m_lastLeftHandleRect = leftRect;
689 m_lastRightHandleRect = rightRect;
693 } // namespace WebKit
695 #endif // TIZEN_WEBKIT2_TEXT_SELECTION