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)
51 m_viewImpl = EwkViewImpl::fromEvasObject(m_object);
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_object, themePath, "elm/entry/selection/block_handle_left", true, this);
62 m_rightHandle = new TextSelectionHandle(m_object, themePath, "elm/entry/selection/block_handle_right", false, this);
69 m_magnifier = new TextSelectionMagnifier(m_object, page, pageClient);
71 evas_object_event_callback_add(m_object, EVAS_CALLBACK_MOUSE_UP, onMouseUp, this);
74 TextSelection::~TextSelection()
84 evas_object_event_callback_del(m_object, EVAS_CALLBACK_MOUSE_UP, onMouseUp);
87 void TextSelection::update()
89 EditorState editorState = m_page->editorState();
90 if (editorState.updateEditorRectOnly)
93 if (isTextSelectionMode()) {
94 if (!editorState.selectionIsRange) {
95 if (editorState.isContentEditable && !evas_object_focus_get(m_object)) {
96 WebCore::IntRect caretRect;
97 m_page->getCaretPosition(caretRect);
98 if (!caretRect.isEmpty())
101 WebCore::IntRect leftRect;
102 WebCore::IntRect rightRect;
103 if (isTextSelectionDowned() || m_page->getSelectionHandlers(leftRect, rightRect))
106 setIsTextSelectionMode(false);
108 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
109 m_page->getTextStyleStateForSelection();
112 if (!isTextSelectionDowned() && !isTextSelectionHandleDowned()) {
115 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
116 m_page->getTextStyleStateForSelection();
121 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
123 if (editorState.isContentEditable && !editorState.selectionIsRange) {
124 WebCore::IntRect caretRect;
125 m_page->getCaretPosition(caretRect);
126 if (!caretRect.isEmpty()) {
127 m_page->getTextStyleStateForSelection();
134 void TextSelection::setIsTextSelectionMode(bool isTextSelectionMode)
136 if (!isAutomaticClearEnabled())
139 if (!isTextSelectionMode) {
144 m_isTextSelectionMode = isTextSelectionMode;
147 void TextSelection::clear()
149 EditorState editorState = m_page->editorState();
150 if (!editorState.selectionIsRange)
153 m_page->selectionRangeClear();
156 void TextSelection::hide()
163 void TextSelection::updateHandlers()
165 WebCore::IntRect leftRect, rightRect;
166 if (!m_page->getSelectionHandlers(leftRect, rightRect))
169 m_lastLeftHandleRect = leftRect;
170 m_lastRightHandleRect = rightRect;
172 AffineTransform toEvasTransform = m_viewImpl->transformToScene();
173 WebCore::IntPoint leftEvasPoint = toEvasTransform.mapPoint(leftRect.minXMaxYCorner());
174 WebCore::IntPoint rightEvasPoint = toEvasTransform.mapPoint(rightRect.maxXMaxYCorner());
176 EditorState editorState = m_page->editorState();
177 if (editorState.isContentEditable) {
178 m_leftHandle->hide();
179 m_rightHandle->hide();
181 WebCore::IntRect editorRect = editorState.editorRect;
182 WebCore::IntPoint editorLeftEvasPoint = toEvasTransform.mapPoint(editorRect.location());
183 WebCore::IntPoint editorRightEvasPoint = toEvasTransform.mapPoint(editorRect.maxXMaxYCorner());
184 int webViewX, webViewY, webViewWidth, webViewHeight;
186 evas_object_geometry_get(m_object, &webViewX, &webViewY, &webViewWidth, &webViewHeight);
187 if ((editorLeftEvasPoint.x() <= leftEvasPoint.x() && editorLeftEvasPoint.y() <= leftEvasPoint.y())
188 && (webViewX <= leftEvasPoint.x() && webViewY <= leftEvasPoint.y())) {
189 m_leftHandle->move(leftEvasPoint);
190 m_leftHandle->show();
193 if ((editorRightEvasPoint.x() >= rightEvasPoint.x() && editorRightEvasPoint.y() >= rightEvasPoint.y())
194 && ((webViewX + webViewWidth) >= rightEvasPoint.x() && (webViewY <= rightEvasPoint.y() && (webViewY + webViewHeight) >= rightEvasPoint.y()))) {
195 m_rightHandle->move(rightEvasPoint);
196 m_rightHandle->show();
199 m_leftHandle->move(leftEvasPoint);
200 m_leftHandle->show();
202 m_rightHandle->move(rightEvasPoint);
203 m_rightHandle->show();
207 void TextSelection::hideHandlers()
209 m_leftHandle->hide();
210 m_rightHandle->hide();
213 void TextSelection::showMagnifier()
218 void TextSelection::hideMagnifier()
223 void TextSelection::updateMagnifier(const IntPoint& position)
225 m_magnifier->update(position);
226 m_magnifier->move(position);
229 void TextSelection::showContextMenu()
234 EditorState editorState = m_page->editorState();
235 if (!editorState.selectionIsRange && !editorState.isContentEditable)
238 WebCore::IntPoint point;
239 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
240 bool isPresentInViewPort = false;
242 if (editorState.selectionIsRange) {
243 WebCore::IntRect leftRect, rightRect;
244 if (!m_page->getSelectionHandlers(leftRect, rightRect))
247 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
248 // Checking if this point is in viewport area. If the calcualated
249 // point/Left/Right point are in view port then draw else do not draw the
250 // context menu. Only draw the selection points.
251 FloatRect unscaledRect = FloatRect(m_pageClient->visibleContentRect());
252 unscaledRect.scale(1 / m_pageClient->scaleFactor());
253 IntRect viewportRect = enclosingIntRect(unscaledRect);
255 WebCore::IntPoint visiblePoint = leftRect.center();
256 if (viewportRect.contains(visiblePoint)) {
257 // First check That the modified points are present in view port
258 point = visiblePoint;
259 isPresentInViewPort = true;
260 } else if (viewportRect.contains(leftRect.location())) {
261 // else if the calculated point is not in the view port area the
262 // draw context menu at left point if visible
263 point = leftRect.location();
264 isPresentInViewPort = true;
265 } else if (viewportRect.contains(rightRect.maxXMinYCorner())) {
266 // else if the calculated point is not in the view port area the
267 // draw context menu at right point if visible
268 point = rightRect.maxXMinYCorner();
269 isPresentInViewPort = true;
272 point = leftRect.center();
274 } else if (editorState.isContentEditable) {
275 WebCore::IntRect caretRect;
276 m_page->getCaretPosition(caretRect);
278 if (caretRect.isEmpty())
281 point = caretRect.center();
282 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
283 isPresentInViewPort = true;
286 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
287 if (!isPresentInViewPort)
291 // show context menu if its in viewport else do not show the contextmenu
293 Ewk_View_Smart_Data* smartData = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(m_object));
294 if (!smartData || !smartData->api || !smartData->api->mouse_down || !smartData->api->mouse_up)
297 point = m_viewImpl->transformToScene().mapPoint(point);
298 Evas* evas = evas_object_evas_get(m_object);
301 Evas_Event_Mouse_Down mouseDown;
302 mouseDown.button = 3;
303 mouseDown.output.x = mouseDown.canvas.x = point.x();
304 mouseDown.output.y = mouseDown.canvas.y = point.y();
306 mouseDown.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
307 mouseDown.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
308 mouseDown.flags = EVAS_BUTTON_NONE;
309 mouseDown.timestamp = ecore_time_get() * 1000;
310 mouseDown.event_flags = EVAS_EVENT_FLAG_NONE;
312 smartData->api->mouse_down(smartData, &mouseDown);
315 Evas_Event_Mouse_Up mouseUp;
317 mouseUp.output.x = mouseUp.canvas.x = point.x();
318 mouseUp.output.y = mouseUp.canvas.y = point.y();
320 mouseUp.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
321 mouseUp.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
322 mouseUp.flags = EVAS_BUTTON_NONE;
323 mouseUp.timestamp = ecore_time_get() * 1000;
324 mouseUp.event_flags = EVAS_EVENT_FLAG_NONE;
326 smartData->api->mouse_up(smartData, &mouseUp);
329 void TextSelection::hideContextMenu()
334 m_page->hideContextMenu();
337 void TextSelection::setLeftSelectionToEvasPoint(const IntPoint& evasPoint)
339 m_page->setLeftSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
343 void TextSelection::setRightSelectionToEvasPoint(const IntPoint& evasPoint)
345 m_page->setRightSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
350 void TextSelection::handleMouseDown(TextSelectionHandle* handle, const IntPoint& /*position*/)
352 WebCore::IntPoint basePosition;
353 EditorState editorState = m_page->editorState();
355 if (editorState.selectionIsRange) {
356 WebCore::IntRect leftRect, rightRect;
357 if (!m_page->getSelectionHandlers(leftRect, rightRect)) {
362 if (handle->isLeft()) {
363 basePosition.setX(leftRect.x());
364 basePosition.setY(leftRect.y() + (leftRect.height()/2));
366 basePosition.setX(rightRect.x() + rightRect.width());
367 basePosition.setY(rightRect.y() + (rightRect.height()/2));
370 handle->setBasePositionForMove(m_viewImpl->transformToScene().mapPoint(basePosition));
375 updateMagnifier(handle->position());
379 void TextSelection::handleMouseMove(TextSelectionHandle* handle, const IntPoint& position)
381 if (handle->isLeft())
382 setLeftSelectionToEvasPoint(position);
384 setRightSelectionToEvasPoint(position);
386 updateMagnifier(handle->position());
389 void TextSelection::handleMouseUp(TextSelectionHandle* /* handle */, const IntPoint& /* position */)
393 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
394 m_page->getTextStyleStateForSelection();
398 bool TextSelection::isMagnifierVisible()
400 return m_magnifier->isVisible();
403 void TextSelection::updateHandlesAndContextMenu(bool isShow, bool isScrolling)
405 if (isTextSelectionDowned() && !isScrolling) {
418 if (isScrolling && isMagnifierVisible())
422 void TextSelection::startMoveAnimator()
424 if (!isEnabled() || !isTextSelectionDowned())
428 m_moveAnimator = ecore_animator_add(moveAnimatorCallback, this);
431 void TextSelection::stopMoveAnimator()
433 if (m_moveAnimator) {
434 ecore_animator_del(m_moveAnimator);
439 void TextSelection::onMouseUp(void* data, Evas*, Evas_Object*, void* eventInfo)
441 static_cast<TextSelection*>(data)->textSelectionUp(IntPoint());
444 Eina_Bool TextSelection::moveAnimatorCallback(void* data)
446 TextSelection* textSelection = static_cast<TextSelection*>(data);
448 Evas_Coord_Point point;
449 evas_pointer_canvas_xy_get(evas_object_evas_get(textSelection->m_object), &point.x, &point.y);
450 textSelection->textSelectionMove(IntPoint(point.x, point.y));
452 return ECORE_CALLBACK_RENEW;
455 // 'return false' means text selection is not possible for point.
456 // 'return true' means text selection is possible.
457 bool TextSelection::textSelectionDown(const IntPoint& point, bool isStartedTextSelectionFromOutside)
459 setIsTextSelectionMode(false);
461 IntPoint contentsPoint = m_viewImpl->transformFromScene().mapPoint(point);
462 bool result = m_page->selectClosestWord(contentsPoint, isStartedTextSelectionFromOutside);
466 if (isTextSelectionMode()) {
469 setIsTextSelectionMode(true);
471 setIsTextSelectionDowned(true);
473 updateMagnifier(point);
481 void TextSelection::textSelectionMove(const IntPoint& point, bool isStartedTextSelectionFromOutside)
483 if (!isTextSelectionMode()) {
488 WebCore::IntPoint viewPoint = m_viewImpl->transformFromScene().mapPoint(point);
489 EditorState editorState = m_page->editorState();
490 if (editorState.isContentEditable) {
491 IntRect mapRect = m_viewImpl->transformToScene().mapRect(editorState.editorRect);
492 if(mapRect.contains(point)) {
493 m_page->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
494 updateMagnifier(point);
497 m_page->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
498 updateMagnifier(point);
503 void TextSelection::textSelectionUp(const IntPoint& point, bool isStartedTextSelectionFromOutside)
507 if (!isTextSelectionMode() || !isTextSelectionDowned())
510 setIsTextSelectionDowned(false);
513 EditorState editorState = m_page->editorState();
514 if (editorState.selectionIsRange || editorState.isContentEditable) {
515 if (editorState.selectionIsRange)
519 } else if (!isStartedTextSelectionFromOutside)
520 setIsTextSelectionMode(false);
522 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
523 m_page->getTextStyleStateForSelection();
527 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
528 TextSelectionHandle* TextSelection::getSelectedHandle(const IntPoint& position)
530 WebCore::IntRect leftHandleRect = m_leftHandle->getHandleRect();
531 if (leftHandleRect.contains(position))
534 WebCore::IntRect rightHandleRect = m_rightHandle->getHandleRect();
535 if (rightHandleRect.contains(position))
536 return m_rightHandle;
541 void TextSelection::textSelectionHandleDown(const IntPoint& position)
543 TextSelectionHandle* selectedHandle = getSelectedHandle(position);
544 if (selectedHandle) {
545 selectedHandle->mouseDown(position);
546 if (selectedHandle->isMouseDowned())
551 void TextSelection::textSelectionHandleMove(const IntPoint& position)
553 if (isTextSelectionHandleDowned()) {
554 TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
555 selectedHandle->mouseMove(position);
559 void TextSelection::textSelectionHandleUp()
561 if (isTextSelectionHandleDowned()) {
562 TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
563 selectedHandle->mouseUp();
568 bool TextSelection::isEnabled()
570 return ewk_settings_text_selection_enabled_get(ewk_view_settings_get(m_object));
573 bool TextSelection::isAutomaticClearEnabled()
575 return ewk_settings_clear_text_selection_automatically_get(ewk_view_settings_get(m_object));
578 void TextSelection::requestToShow()
581 ecore_timer_del(m_showTimer);
582 m_showTimer = ecore_timer_loop_add((double)200.0/1000.0, showTimerCallback, this);
585 Eina_Bool TextSelection::showTimerCallback(void* data)
587 TextSelection* textSelection = static_cast<TextSelection*>(data);
588 textSelection->showHandlesAndContextMenu();
590 return ECORE_CALLBACK_RENEW;
593 void TextSelection::showHandlesAndContextMenu()
595 WebCore::IntRect leftRect, rightRect;
596 if (m_page->getSelectionHandlers(leftRect, rightRect)) {
597 if ((leftRect == m_lastLeftHandleRect) && (rightRect == m_lastRightHandleRect)) {
599 ecore_timer_del(m_showTimer);
603 updateHandlesAndContextMenu(true);
606 m_lastLeftHandleRect = leftRect;
607 m_lastRightHandleRect = rightRect;
611 } // namespace WebKit
613 #endif // TIZEN_WEBKIT2_TEXT_SELECTION