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)
50 const Eina_List* defaultThemeList = elm_theme_list_get(0);
54 EINA_LIST_FOREACH(defaultThemeList, l, theme) {
55 char* themePath = elm_theme_list_item_path_get((const char*)theme, 0);
58 m_leftHandle = new TextSelectionHandle(m_viewImpl->view(), themePath, "elm/entry/selection/block_handle_left", true, this);
59 m_rightHandle = new TextSelectionHandle(m_viewImpl->view(), themePath, "elm/entry/selection/block_handle_right", false, this);
66 m_magnifier = new TextSelectionMagnifier(m_viewImpl);
68 evas_object_event_callback_add(m_viewImpl->view(), EVAS_CALLBACK_MOUSE_UP, onMouseUp, this);
71 TextSelection::~TextSelection()
82 ecore_animator_del(m_moveAnimator);
87 ecore_timer_del(m_showTimer);
90 evas_object_event_callback_del(m_viewImpl->view(), EVAS_CALLBACK_MOUSE_UP, onMouseUp);
93 void TextSelection::update()
95 EditorState editorState = m_viewImpl->page()->editorState();
96 if (editorState.updateEditorRectOnly)
99 if (isTextSelectionMode()) {
100 if (!editorState.selectionIsRange) {
101 if (editorState.isContentEditable && !evas_object_focus_get(m_viewImpl->view())) {
102 WebCore::IntRect caretRect;
103 m_viewImpl->page()->getCaretPosition(caretRect);
104 if (!caretRect.isEmpty())
107 WebCore::IntRect leftRect;
108 WebCore::IntRect rightRect;
109 if (isTextSelectionDowned() || m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
112 setIsTextSelectionMode(false);
114 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
115 m_viewImpl->page()->getTextStyleStateForSelection();
118 if (!isTextSelectionDowned() && !isTextSelectionHandleDowned()) {
121 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
122 m_viewImpl->page()->getTextStyleStateForSelection();
127 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
129 if (editorState.isContentEditable && !editorState.selectionIsRange) {
130 WebCore::IntRect caretRect;
131 m_viewImpl->page()->getCaretPosition(caretRect);
132 if (!caretRect.isEmpty()) {
133 m_viewImpl->page()->getTextStyleStateForSelection();
140 void TextSelection::setIsTextSelectionMode(bool isTextSelectionMode)
142 if (!isAutomaticClearEnabled())
145 if (!isTextSelectionMode) {
150 m_isTextSelectionMode = isTextSelectionMode;
153 void TextSelection::clear()
155 EditorState editorState = m_viewImpl->page()->editorState();
156 if (!editorState.selectionIsRange)
159 m_viewImpl->page()->selectionRangeClear();
162 void TextSelection::hide()
169 void TextSelection::updateHandlers()
171 WebCore::IntRect leftRect, rightRect;
172 if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
175 m_lastLeftHandleRect = leftRect;
176 m_lastRightHandleRect = rightRect;
178 AffineTransform toEvasTransform = m_viewImpl->transformToScene();
179 WebCore::IntPoint leftEvasPoint = toEvasTransform.mapPoint(leftRect.minXMaxYCorner());
180 WebCore::IntPoint rightEvasPoint = toEvasTransform.mapPoint(rightRect.maxXMaxYCorner());
182 EditorState editorState = m_viewImpl->page()->editorState();
183 if (editorState.isContentEditable) {
184 m_leftHandle->hide();
185 m_rightHandle->hide();
187 WebCore::IntRect editorRect = editorState.editorRect;
188 WebCore::IntPoint editorLeftEvasPoint = toEvasTransform.mapPoint(editorRect.location());
189 WebCore::IntPoint editorRightEvasPoint = toEvasTransform.mapPoint(editorRect.maxXMaxYCorner());
190 int webViewX, webViewY, webViewWidth, webViewHeight;
192 evas_object_geometry_get(m_viewImpl->view(), &webViewX, &webViewY, &webViewWidth, &webViewHeight);
193 if ((editorLeftEvasPoint.x() <= leftEvasPoint.x() && editorLeftEvasPoint.y() <= leftEvasPoint.y())
194 && (webViewX <= leftEvasPoint.x() && webViewY <= leftEvasPoint.y())) {
195 m_leftHandle->move(leftEvasPoint);
196 m_leftHandle->show();
199 if ((editorRightEvasPoint.x() >= rightEvasPoint.x() && editorRightEvasPoint.y() >= rightEvasPoint.y())
200 && ((webViewX + webViewWidth) >= rightEvasPoint.x() && (webViewY <= rightEvasPoint.y() && (webViewY + webViewHeight) >= rightEvasPoint.y()))) {
201 m_rightHandle->move(rightEvasPoint);
202 m_rightHandle->show();
205 m_leftHandle->move(leftEvasPoint);
206 m_leftHandle->show();
208 m_rightHandle->move(rightEvasPoint);
209 m_rightHandle->show();
213 void TextSelection::hideHandlers()
215 m_leftHandle->hide();
216 m_rightHandle->hide();
219 void TextSelection::showMagnifier()
224 void TextSelection::hideMagnifier()
229 void TextSelection::updateMagnifier(const IntPoint& position)
231 m_magnifier->update(position);
232 m_magnifier->move(position);
235 void TextSelection::showContextMenu()
240 EditorState editorState = m_viewImpl->page()->editorState();
241 if (!editorState.selectionIsRange && !editorState.isContentEditable)
244 WebCore::IntPoint point;
245 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
246 bool isPresentInViewPort = false;
248 if (editorState.selectionIsRange) {
249 WebCore::IntRect leftRect, rightRect;
250 if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
253 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
254 // Checking if this point is in viewport area. If the calcualated
255 // point/Left/Right point are in view port then draw else do not draw the
256 // context menu. Only draw the selection points.
257 FloatRect unscaledRect = FloatRect(m_viewImpl->pageClient->visibleContentRect());
258 unscaledRect.scale(1 / m_viewImpl->pageClient->scaleFactor());
259 IntRect viewportRect = enclosingIntRect(unscaledRect);
261 WebCore::IntPoint visiblePoint = leftRect.center();
262 if (viewportRect.contains(visiblePoint)) {
263 // First check That the modified points are present in view port
264 point = visiblePoint;
265 isPresentInViewPort = true;
266 } else if (viewportRect.contains(leftRect.location())) {
267 // else if the calculated point is not in the view port area the
268 // draw context menu at left point if visible
269 point = leftRect.location();
270 isPresentInViewPort = true;
271 } else if (viewportRect.contains(rightRect.maxXMinYCorner())) {
272 // else if the calculated point is not in the view port area the
273 // draw context menu at right point if visible
274 point = rightRect.maxXMinYCorner();
275 isPresentInViewPort = true;
278 point = leftRect.center();
280 } else if (editorState.isContentEditable) {
281 WebCore::IntRect caretRect;
282 m_viewImpl->page()->getCaretPosition(caretRect);
284 if (caretRect.isEmpty())
287 point = caretRect.center();
288 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
289 isPresentInViewPort = true;
292 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
293 if (!isPresentInViewPort)
297 // show context menu if its in viewport else do not show the contextmenu
299 Ewk_View_Smart_Data* smartData = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(m_viewImpl->view()));
300 if (!smartData || !smartData->api || !smartData->api->mouse_down || !smartData->api->mouse_up)
303 point = m_viewImpl->transformToScene().mapPoint(point);
304 Evas* evas = evas_object_evas_get(m_viewImpl->view());
307 Evas_Event_Mouse_Down mouseDown;
308 mouseDown.button = 3;
309 mouseDown.output.x = mouseDown.canvas.x = point.x();
310 mouseDown.output.y = mouseDown.canvas.y = point.y();
312 mouseDown.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
313 mouseDown.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
314 mouseDown.flags = EVAS_BUTTON_NONE;
315 mouseDown.timestamp = ecore_time_get() * 1000;
316 mouseDown.event_flags = EVAS_EVENT_FLAG_NONE;
318 smartData->api->mouse_down(smartData, &mouseDown);
321 Evas_Event_Mouse_Up mouseUp;
323 mouseUp.output.x = mouseUp.canvas.x = point.x();
324 mouseUp.output.y = mouseUp.canvas.y = point.y();
326 mouseUp.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
327 mouseUp.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
328 mouseUp.flags = EVAS_BUTTON_NONE;
329 mouseUp.timestamp = ecore_time_get() * 1000;
330 mouseUp.event_flags = EVAS_EVENT_FLAG_NONE;
332 smartData->api->mouse_up(smartData, &mouseUp);
335 void TextSelection::hideContextMenu()
340 m_viewImpl->page()->hideContextMenu();
343 void TextSelection::setLeftSelectionToEvasPoint(const IntPoint& evasPoint)
345 m_viewImpl->page()->setLeftSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
349 void TextSelection::setRightSelectionToEvasPoint(const IntPoint& evasPoint)
351 m_viewImpl->page()->setRightSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
356 void TextSelection::handleMouseDown(TextSelectionHandle* handle, const IntPoint& /*position*/)
358 WebCore::IntPoint basePosition;
359 EditorState editorState = m_viewImpl->page()->editorState();
361 if (editorState.selectionIsRange) {
362 WebCore::IntRect leftRect, rightRect;
363 if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect)) {
368 if (handle->isLeft()) {
369 basePosition.setX(leftRect.x());
370 basePosition.setY(leftRect.y() + (leftRect.height()/2));
372 basePosition.setX(rightRect.x() + rightRect.width());
373 basePosition.setY(rightRect.y() + (rightRect.height()/2));
376 handle->setBasePositionForMove(m_viewImpl->transformToScene().mapPoint(basePosition));
381 updateMagnifier(handle->position());
385 void TextSelection::handleMouseMove(TextSelectionHandle* handle, const IntPoint& position)
387 if (handle->isLeft())
388 setLeftSelectionToEvasPoint(position);
390 setRightSelectionToEvasPoint(position);
392 updateMagnifier(handle->position());
395 void TextSelection::handleMouseUp(TextSelectionHandle* /* handle */, const IntPoint& /* position */)
399 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
400 m_viewImpl->page()->getTextStyleStateForSelection();
404 bool TextSelection::isMagnifierVisible()
406 return m_magnifier->isVisible();
409 void TextSelection::updateHandlesAndContextMenu(bool isShow, bool isScrolling)
411 if (isTextSelectionDowned() && !isScrolling) {
417 if (m_viewImpl->gestureClient->isGestureWorking())
427 if (isScrolling && isMagnifierVisible())
431 void TextSelection::startMoveAnimator()
433 if (!isEnabled() || !isTextSelectionDowned())
437 m_moveAnimator = ecore_animator_add(moveAnimatorCallback, this);
440 void TextSelection::stopMoveAnimator()
442 if (m_moveAnimator) {
443 ecore_animator_del(m_moveAnimator);
448 void TextSelection::onMouseUp(void* data, Evas*, Evas_Object*, void* eventInfo)
450 static_cast<TextSelection*>(data)->textSelectionUp(IntPoint());
453 Eina_Bool TextSelection::moveAnimatorCallback(void* data)
455 TextSelection* textSelection = static_cast<TextSelection*>(data);
457 Evas_Coord_Point point;
458 evas_pointer_canvas_xy_get(evas_object_evas_get(textSelection->m_viewImpl->view()), &point.x, &point.y);
459 textSelection->textSelectionMove(IntPoint(point.x, point.y));
461 return ECORE_CALLBACK_RENEW;
464 // 'return false' means text selection is not possible for point.
465 // 'return true' means text selection is possible.
466 bool TextSelection::textSelectionDown(const IntPoint& point, bool isStartedTextSelectionFromOutside)
468 // text selection should be ignored when longtap on handle from osp
469 if (!isEnabled() && isTextSelectionHandleDowned())
472 #if ENABLE(TIZEN_ISF_PORT)
473 m_viewImpl->inputMethodContext()->resetIMFContext();
475 setIsTextSelectionMode(false);
477 IntPoint contentsPoint = m_viewImpl->transformFromScene().mapPoint(point);
478 bool result = m_viewImpl->page()->selectClosestWord(contentsPoint, isStartedTextSelectionFromOutside);
482 if (isTextSelectionMode()) {
485 setIsTextSelectionMode(true);
487 setIsTextSelectionDowned(true);
489 updateMagnifier(point);
497 void TextSelection::textSelectionMove(const IntPoint& point, bool isStartedTextSelectionFromOutside)
499 // text selection should be ignored when longtap on handle from osp
500 if (!isEnabled() && isTextSelectionHandleDowned())
503 if (!isTextSelectionMode()) {
508 WebCore::IntPoint viewPoint;
509 EditorState editorState = m_viewImpl->page()->editorState();
510 if (editorState.isContentEditable) {
511 IntRect mapRect = m_viewImpl->transformToScene().mapRect(editorState.editorRect);
512 IntPoint updatedPoint = point;
513 if ((point.y() < mapRect.y()) || (point.y() > ((mapRect.y()) + (mapRect.height()))))
514 updatedPoint.setY((mapRect.y()) + ((mapRect.height())/2) );
516 if (point.x() < mapRect.x()) {
517 updatedPoint.setX(mapRect.x());
518 if (m_viewImpl->page()->scrollContentByCharacter(point,WebCore::DirectionBackward))
519 updateMagnifier(updatedPoint);
520 } else if (point.x() > ((mapRect.x()) + (mapRect.width()))) {
521 updatedPoint.setX((mapRect.x()) + (mapRect.width()));
522 if (m_viewImpl->page()->scrollContentByCharacter(point,WebCore::DirectionForward))
523 updateMagnifier(updatedPoint);
525 viewPoint = m_viewImpl->transformFromScene().mapPoint(updatedPoint);
526 m_viewImpl->page()->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
527 updateMagnifier(updatedPoint);
530 viewPoint = m_viewImpl->transformFromScene().mapPoint(point);
531 m_viewImpl->page()->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
532 updateMagnifier(point);
537 void TextSelection::textSelectionUp(const IntPoint& point, bool isStartedTextSelectionFromOutside)
539 // text selection should be ignored when longtap on handle from osp
540 if (!isEnabled() && isTextSelectionHandleDowned())
545 if (!isTextSelectionMode() || !isTextSelectionDowned())
548 setIsTextSelectionDowned(false);
551 EditorState editorState = m_viewImpl->page()->editorState();
552 if (editorState.selectionIsRange || editorState.isContentEditable) {
553 if (editorState.selectionIsRange)
557 } else if (!isStartedTextSelectionFromOutside)
558 setIsTextSelectionMode(false);
560 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
561 m_viewImpl->page()->getTextStyleStateForSelection();
565 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
566 TextSelectionHandle* TextSelection::getSelectedHandle(const IntPoint& position)
568 WebCore::IntRect leftHandleRect = m_leftHandle->getHandleRect();
569 if (leftHandleRect.contains(position))
572 WebCore::IntRect rightHandleRect = m_rightHandle->getHandleRect();
573 if (rightHandleRect.contains(position))
574 return m_rightHandle;
579 void TextSelection::textSelectionHandleDown(const IntPoint& position)
581 TextSelectionHandle* selectedHandle = getSelectedHandle(position);
582 if (selectedHandle) {
583 selectedHandle->mouseDown(position);
584 if (selectedHandle->isMouseDowned())
589 void TextSelection::textSelectionHandleMove(const IntPoint& position)
591 if (isTextSelectionHandleDowned()) {
592 TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
593 selectedHandle->mouseMove(position);
597 void TextSelection::textSelectionHandleUp()
599 if (isTextSelectionHandleDowned()) {
600 TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
601 selectedHandle->mouseUp();
606 bool TextSelection::isEnabled()
608 return ewk_settings_text_selection_enabled_get(ewk_view_settings_get(m_viewImpl->view()));
611 bool TextSelection::isAutomaticClearEnabled()
613 return ewk_settings_clear_text_selection_automatically_get(ewk_view_settings_get(m_viewImpl->view()));
616 void TextSelection::requestToShow()
619 ecore_timer_del(m_showTimer);
620 m_showTimer = ecore_timer_loop_add((double)200.0/1000.0, showTimerCallback, this);
623 Eina_Bool TextSelection::showTimerCallback(void* data)
625 TextSelection* textSelection = static_cast<TextSelection*>(data);
626 textSelection->showHandlesAndContextMenu();
628 return ECORE_CALLBACK_RENEW;
631 void TextSelection::showHandlesAndContextMenu()
633 WebCore::IntRect leftRect, rightRect;
634 if (m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect)) {
635 if ((leftRect == m_lastLeftHandleRect) && (rightRect == m_lastRightHandleRect)) {
637 ecore_timer_del(m_showTimer);
641 updateHandlesAndContextMenu(true);
644 m_lastLeftHandleRect = leftRect;
645 m_lastRightHandleRect = rightRect;
649 } // namespace WebKit
651 #endif // TIZEN_WEBKIT2_TEXT_SELECTION