Text selection is updated by timer when webview's geometry is changed.
[framework/web/webkit-efl.git] / Source / WebKit2 / UIProcess / API / efl / tizen / TextSelection.cpp
1 /*
2  * Copyright (C) 2012 Samsung Electronics
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(TIZEN_WEBKIT2_TEXT_SELECTION)
29 #include "TextSelection.h"
30
31 #include "EditorState.h"
32 #include "EwkViewImpl.h"
33 #include "NativeWebMouseEvent.h"
34 #include "ewk_view.h"
35 #include <Elementary.h>
36
37 using namespace WebCore;
38
39 namespace WebKit {
40
41 const int zIndexDefault = -999;
42
43 TextSelection::TextSelection(EwkViewImpl* viewImpl)
44       : m_viewImpl(viewImpl)
45       , m_isTextSelectionDowned(false)
46       , m_isTextSelectionMode(false)
47       , m_moveAnimator(0)
48       , m_showTimer(0)
49 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
50       , m_selectedHandle(0)
51 #endif
52       , m_handleMovingDirection(HandleMovingDirectionNormal)
53       , m_isSelectionInScrollingMode(false)
54       , m_isAutoWordSelectionScheduled(false)
55 {
56     ASSERT(viewWidget);
57
58     const Eina_List* defaultThemeList = elm_theme_list_get(0);
59
60     const Eina_List* l;
61     void* theme;
62     EINA_LIST_FOREACH(defaultThemeList, l, theme) {
63         char* themePath = elm_theme_list_item_path_get((const char*)theme, 0);
64
65         if (themePath) {
66             m_leftHandle = new TextSelectionHandle(m_viewImpl->view(), themePath, "elm/entry/selection/block_handle_left", true, this);
67             m_rightHandle = new TextSelectionHandle(m_viewImpl->view(), themePath, "elm/entry/selection/block_handle_right", false, this);
68
69             free(themePath);
70             break;
71         }
72     }
73
74     m_magnifier = new TextSelectionMagnifier(m_viewImpl);
75     m_zIndex.append(zIndexDefault);
76     m_zIndex.append(zIndexDefault);
77     m_zIndex.append(zIndexDefault);
78     evas_object_event_callback_add(m_viewImpl->view(), EVAS_CALLBACK_MOUSE_UP, onMouseUp, this);
79 }
80
81 TextSelection::~TextSelection()
82 {
83     if (m_leftHandle)
84         delete m_leftHandle;
85
86     if (m_rightHandle)
87         delete m_rightHandle;
88
89     delete m_magnifier;
90
91     if (m_moveAnimator) {
92         ecore_animator_del(m_moveAnimator);
93         m_moveAnimator = 0;
94     }
95
96     if (m_showTimer) {
97         ecore_timer_del(m_showTimer);
98         m_showTimer = 0;
99     }
100     evas_object_event_callback_del(m_viewImpl->view(), EVAS_CALLBACK_MOUSE_UP, onMouseUp);
101 }
102
103 void TextSelection::update()
104 {
105     EditorState editorState = m_viewImpl->page()->editorState();
106     if (editorState.updateEditorRectOnly)
107         return;
108
109 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
110     if (!editorState.shouldIgnoreCompositionSelectionChange && ewk_settings_text_style_state_enabled_get(ewk_view_settings_get(m_viewImpl->view())))
111         informTextStyleState();
112 #endif
113
114     if (!editorState.shouldIgnoreCompositionSelectionChange && !editorState.hasComposition && m_isAutoWordSelectionScheduled)
115         setAutoWordSelection(m_scheduledAutoWordSelectionPosition);
116
117     if (isTextSelectionMode()) {
118         if (!editorState.selectionIsRange) {
119             if (editorState.isContentEditable && !evas_object_focus_get(m_viewImpl->view())) {
120                 const EditorState& editor = m_viewImpl->page()->editorState();
121                 IntRect caretRect;
122                 if (!editor.selectionIsNone && !editor.selectionIsRange)
123                     caretRect = editor.selectionRect;
124
125                 if (!caretRect.isEmpty()) {
126                     setIsTextSelectionMode(false);
127                     return;
128                 }
129             } else {
130                 WebCore::IntRect leftRect;
131                 WebCore::IntRect rightRect;
132                 int selectionDirection = LToR;
133                 if (isTextSelectionDowned() || m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect, selectionDirection))
134                     return;
135
136                 setIsTextSelectionMode(false);
137             }
138         } else {
139             if (!isTextSelectionDowned() && !isTextSelectionHandleDowned()) {
140                 updateHandlers();
141                 showContextMenu();
142             }
143         }
144         return;
145     }
146     // Auto selection happened from the JS through Editor command and update called from
147     // WebPageProxy::editorStateChanged(), so need to update the selection handler and context
148     // menu because focused node is currently active.
149     if (editorState.isContentEditable && !editorState.shouldIgnoreCompositionSelectionChange && editorState.selectionIsRange && !editorState.selectionRect.isEmpty() && m_viewImpl->page()
150         && m_viewImpl->page()->isLoadingFinished()) {
151         setIsTextSelectionMode(true);
152         updateHandlers();
153         showContextMenu();
154     }
155 }
156
157 void TextSelection::setIsTextSelectionMode(bool isTextSelectionMode)
158 {
159     if (!isTextSelectionMode) {
160         hide();
161         clear();
162         initHandlesMouseDownedStatus();
163         setIsTextSelectionDowned(false);
164     }
165
166     m_isTextSelectionMode = isTextSelectionMode;
167 }
168
169 void TextSelection::clear()
170 {
171     EditorState editorState = m_viewImpl->page()->editorState();
172     if (!editorState.selectionIsRange)
173         return;
174
175     m_zIndex[0] = zIndexDefault;
176     m_zIndex[1] = zIndexDefault;
177     m_zIndex[2] = zIndexDefault;
178     m_viewImpl->page()->selectionRangeClear();
179 }
180
181 void TextSelection::hide()
182 {
183     hideHandlers();
184     hideMagnifier();
185     hideContextMenu();
186 }
187
188 // int handle represents 0 - Left handle 1 - Right handle 2 - ContextMenu
189 bool TextSelection::shouldShowObject(WebCore::IntPoint handlePoint, int handle)
190 {
191     WebHitTestResult::Data hitrestatpoint;
192     hitrestatpoint = m_viewImpl->page()->hitTestResultAtPoint(m_viewImpl->transformFromScene().mapPoint(handlePoint), WebHitTestResult::HitTestModeNodeData);
193     if (m_zIndex[handle] < hitrestatpoint.nodeData.zIndex && m_zIndex[handle] != zIndexDefault)
194         return false;
195
196     if (m_zIndex[handle] > hitrestatpoint.nodeData.zIndex)
197         m_zIndex[handle] = zIndexDefault;
198     else
199         m_zIndex[handle] = hitrestatpoint.nodeData.zIndex;
200
201     return true;
202 }
203
204 void TextSelection::updateHandlers()
205 {
206     WebCore::IntRect leftRect, rightRect;
207     int selectionDirection = LToR;
208     if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect, selectionDirection))
209         return;
210
211     m_lastLeftHandleRect = m_viewImpl->transformToScene().mapRect(leftRect);
212     m_lastRightHandleRect = m_viewImpl->transformToScene().mapRect(rightRect);
213
214     AffineTransform toEvasTransform = m_viewImpl->transformToScene();
215     WebCore::IntPoint leftEvasPoint ;
216     WebCore::IntPoint rightEvasPoint ;
217
218     // Selection Direction 1 -> L2R, Selection is Left to Right
219     if (selectionDirection == LToR) {
220         leftEvasPoint = toEvasTransform.mapPoint(leftRect.minXMaxYCorner());
221         rightEvasPoint = toEvasTransform.mapPoint(rightRect.maxXMaxYCorner());
222     }
223     // Selection Direction 0 -> R2L, Selection is Right to Left
224     else {
225         leftEvasPoint = toEvasTransform.mapPoint(leftRect.maxXMaxYCorner());
226         rightEvasPoint = toEvasTransform.mapPoint(rightRect.minXMaxYCorner());
227     }
228     TextSelectionHandle* shownLeftHandle = m_leftHandle;
229     TextSelectionHandle* shownRightHandle = m_rightHandle;
230
231     if (m_handleMovingDirection == HandleMovingDirectionReverse) {
232         shownLeftHandle = m_rightHandle;
233         shownRightHandle = m_leftHandle;
234     }
235
236     EditorState editorState = m_viewImpl->page()->editorState();
237     if (editorState.isContentEditable) {
238         shownLeftHandle->hide();
239         shownRightHandle->hide();
240
241         WebCore::IntRect editorRect = editorState.editorRect;
242         WebCore::IntPoint editorLeftEvasPoint = toEvasTransform.mapPoint(editorRect.location());
243         WebCore::IntPoint editorRightEvasPoint = toEvasTransform.mapPoint(editorRect.maxXMaxYCorner());
244         int webViewX, webViewY, webViewWidth, webViewHeight;
245
246         evas_object_geometry_get(m_viewImpl->view(), &webViewX, &webViewY, &webViewWidth, &webViewHeight);
247         if ((editorLeftEvasPoint.x() <= leftEvasPoint.x() && editorLeftEvasPoint.y() <= leftEvasPoint.y())
248             && (webViewX <= leftEvasPoint.x() && webViewY <= leftEvasPoint.y())) {
249             if (shouldShowObject(leftEvasPoint, 0)) {
250                 shownLeftHandle->move(leftEvasPoint, m_viewImpl->transformToScene().mapRect(leftRect), (selectionDirection == LToR?true:false));
251                 shownLeftHandle->show();
252             }
253         }
254
255         if ((editorRightEvasPoint.x() >= rightEvasPoint.x() && editorRightEvasPoint.y() >= rightEvasPoint.y())
256             && ((webViewX + webViewWidth) >= rightEvasPoint.x() && (webViewY <= rightEvasPoint.y() && (webViewY + webViewHeight) >= rightEvasPoint.y()))) {
257             if (shouldShowObject(rightEvasPoint, 1)) {
258                 shownRightHandle->move(rightEvasPoint, m_viewImpl->transformToScene().mapRect(rightRect), (selectionDirection == LToR?true:false));
259                 shownRightHandle->show();
260             }
261         }
262     } else {
263         if (shouldShowObject(leftEvasPoint, 0)) {
264             shownLeftHandle->move(leftEvasPoint, m_viewImpl->transformToScene().mapRect(leftRect), (selectionDirection == LToR?true:false));
265             shownLeftHandle->show();
266         }
267         if (shouldShowObject(rightEvasPoint, 1)) {
268             shownRightHandle->move(rightEvasPoint, m_viewImpl->transformToScene().mapRect(rightRect), (selectionDirection == LToR?true:false));
269             shownRightHandle->show();
270         }
271     }
272 }
273
274 void TextSelection::hideHandlers()
275 {
276     m_leftHandle->hide();
277     m_rightHandle->hide();
278 }
279
280 void TextSelection::showMagnifier()
281 {
282     m_magnifier->show();
283 }
284
285 void TextSelection::hideMagnifier()
286 {
287     m_magnifier->hide();
288 }
289
290 void TextSelection::updateMagnifier(const IntPoint& position)
291 {
292     m_magnifier->update(position);
293     m_magnifier->move(position);
294 }
295
296 void TextSelection::showContextMenu()
297 {
298     if (!isEnabled())
299         return;
300
301     EditorState editorState = m_viewImpl->page()->editorState();
302     if (!editorState.selectionIsRange && !editorState.isContentEditable)
303         return;
304
305     WebCore::IntPoint point;
306 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
307     bool isPresentInViewPort = false;
308 #endif
309     if (editorState.selectionIsRange) {
310         WebCore::IntRect leftRect, rightRect;
311         int selectionDirection = LToR;
312         if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect, selectionDirection))
313             return;
314
315 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
316         // Checking if this point is in viewport area. If the calcualated
317         // point/Left/Right point are in view port then draw else do not draw the
318         // context menu. Only draw the selection points.
319         FloatRect unscaledRect = FloatRect(m_viewImpl->pageClient->visibleContentRect());
320         unscaledRect.scale(1 / m_viewImpl->pageClient->scaleFactor());
321         IntRect viewportRect = enclosingIntRect(unscaledRect);
322
323         WebCore::IntPoint visiblePoint = leftRect.center();
324         if (viewportRect.contains(visiblePoint)) {
325             // First check That the modified points are present in view port
326             point = visiblePoint;
327             isPresentInViewPort = true;
328         } else if (viewportRect.contains(leftRect.location())) {
329             // else if the calculated point is not in the view port area the
330             // draw context menu at left point if visible
331             point = leftRect.location();
332             isPresentInViewPort = true;
333         } else if (viewportRect.contains(rightRect.maxXMinYCorner())) {
334             // else if the calculated point is not in the view port area the
335             // draw context menu at right point if visible
336             point = rightRect.maxXMinYCorner();
337             isPresentInViewPort = true;
338         }
339
340         if (isPresentInViewPort && editorState.isContentEditable) {
341             // In case of single line editor box.
342             if (leftRect == rightRect) {
343                 // draw context menu at center point of visible selection range in the editor box
344                 IntRect editorRect = editorState.editorRect;
345                 leftRect.intersect(editorRect);
346                 if (!leftRect.isEmpty())
347                     point = leftRect.center();
348                 else {
349                     // not draw context menu if there is no visible selection range in the editor box
350                     isPresentInViewPort = false;
351                 }
352             }
353         }
354 #else
355         point = leftRect.center();
356 #endif
357     } else if (editorState.isContentEditable) {
358         const EditorState& editor = m_viewImpl->page()->editorState();
359         IntRect caretRect;
360         if (!editor.selectionIsNone && !editor.selectionIsRange)
361             caretRect = editor.selectionRect;
362
363         if (caretRect.isEmpty())
364             return;
365
366         point = caretRect.center();
367 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
368         isPresentInViewPort = true;
369 #endif
370     }
371 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
372     if (!isPresentInViewPort)
373         return;
374 #endif
375
376     // show context menu if its in viewport else do not show the contextmenu
377     Ewk_View_Smart_Data* smartData = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(m_viewImpl->view()));
378     if (!smartData || !smartData->api || !smartData->api->mouse_down || !smartData->api->mouse_up)
379         return;
380
381     point = m_viewImpl->transformToScene().mapPoint(point);
382     Evas* evas = evas_object_evas_get(m_viewImpl->view());
383
384     if (editorState.selectionIsRange && !shouldShowObject(point,2))
385         return;
386
387     // send mouse down.
388     Evas_Event_Mouse_Down mouseDown;
389     mouseDown.button = 3;
390     mouseDown.output.x = mouseDown.canvas.x = point.x();
391     mouseDown.output.y = mouseDown.canvas.y = point.y();
392     mouseDown.data = 0;
393     mouseDown.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
394     mouseDown.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
395     mouseDown.flags = EVAS_BUTTON_NONE;
396     mouseDown.timestamp = ecore_time_get() * 1000;
397     mouseDown.event_flags = EVAS_EVENT_FLAG_NONE;
398     mouseDown.dev = 0;
399     smartData->api->mouse_down(smartData, &mouseDown);
400
401     // send mouse up.
402     Evas_Event_Mouse_Up mouseUp;
403     mouseUp.button = 3;
404     mouseUp.output.x = mouseUp.canvas.x = point.x();
405     mouseUp.output.y = mouseUp.canvas.y = point.y();
406     mouseUp.data = 0;
407     mouseUp.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
408     mouseUp.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
409     mouseUp.flags = EVAS_BUTTON_NONE;
410     mouseUp.timestamp = ecore_time_get() * 1000;
411     mouseUp.event_flags = EVAS_EVENT_FLAG_NONE;
412     mouseUp.dev = 0;
413     smartData->api->mouse_up(smartData, &mouseUp);
414 }
415
416 void TextSelection::hideContextMenu()
417 {
418     if (!isEnabled())
419         return;
420
421     m_viewImpl->page()->hideContextMenu();
422 }
423
424 void TextSelection::scrollContentWithSelectionIfRequired(const IntPoint& evasPoint)
425 {
426     // Content should get scroll irrespective whether content is editable or not.
427     setSelectionScrollingStatus(true);
428     IntRect unscaledVisibleContentRect = m_viewImpl->pageClient->visibleContentRect();
429     const int scrollClipValue = 50;
430     if (evasPoint.y() >= (unscaledVisibleContentRect.height() - scrollClipValue))
431         m_viewImpl->pageClient->scrollBy(IntSize(0, m_rightHandle->getHandleRect().height()));
432     else if (evasPoint.y() <= scrollClipValue) {
433         IntSize scrollSize(0, m_rightHandle->getHandleRect().height());
434         scrollSize.scale(-1);
435         m_viewImpl->pageClient->scrollBy(scrollSize);
436     }
437
438     if (evasPoint.x() >= (unscaledVisibleContentRect.width() - scrollClipValue))
439         m_viewImpl->pageClient->scrollBy(IntSize(m_rightHandle->getHandleRect().width(),0));
440     else if (evasPoint.x() <= scrollClipValue) {
441         IntSize scrollSize(m_rightHandle->getHandleRect().width(), 0);
442         scrollSize.scale(-1);
443         m_viewImpl->pageClient->scrollBy(scrollSize);
444     }
445     setSelectionScrollingStatus(false);
446 }
447
448 void TextSelection::setLeftSelectionToEvasPoint(const IntPoint& evasPoint)
449 {
450     scrollContentWithSelectionIfRequired(evasPoint);
451     int result = m_viewImpl->page()->setLeftSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint), m_handleMovingDirection);
452     if (result)
453         m_handleMovingDirection = result;
454     updateHandlers();
455 }
456
457 void TextSelection::setRightSelectionToEvasPoint(const IntPoint& evasPoint)
458 {
459     scrollContentWithSelectionIfRequired(evasPoint);
460     int result = m_viewImpl->page()->setRightSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint), m_handleMovingDirection);
461     if (result)
462         m_handleMovingDirection = result;
463     updateHandlers();
464 }
465
466 // handle callbacks
467 void TextSelection::handleMouseDown(TextSelectionHandle* handle, const IntPoint& /*position*/)
468 {
469     WebCore::IntPoint basePosition;
470     EditorState editorState = m_viewImpl->page()->editorState();
471
472     if (editorState.selectionIsRange) {
473         WebCore::IntRect leftRect, rightRect;
474         int selectionDirection = LToR, adjustValue = 2;
475         if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect, selectionDirection)) {
476             clear();
477             return;
478         }
479         (editorState.isOnlyImageSelection == true)?adjustValue = 1:adjustValue = 2;
480         if (handle->isLeft()) {
481             if (selectionDirection == LToR) {
482                 basePosition.setX(leftRect.x());
483                 basePosition.setY(leftRect.y() + (leftRect.height()/adjustValue));
484             } else {
485                 basePosition.setX(leftRect.x() + leftRect.width());
486                 basePosition.setY(leftRect.y() + (leftRect.height()/adjustValue));
487             }
488         } else {
489             if (selectionDirection == LToR) {
490                 basePosition.setX(rightRect.x() + rightRect.width());
491                 basePosition.setY(rightRect.y() + (rightRect.height()/adjustValue));
492             } else {
493                 basePosition.setX(rightRect.x());
494                 basePosition.setY(rightRect.y() + (rightRect.height()/adjustValue));
495             }
496         }
497         handle->setBasePositionForMove(m_viewImpl->transformToScene().mapPoint(basePosition));
498     } else
499         return;
500
501     hideContextMenu();
502     updateMagnifier(handle->position());
503     showMagnifier();
504 }
505
506 void TextSelection::handleMouseMove(TextSelectionHandle* handle, const IntPoint& position)
507 {
508     if (handle->isLeft())
509         setLeftSelectionToEvasPoint(position);
510     else
511         setRightSelectionToEvasPoint(position);
512
513     updateMagnifier(handle->position());
514 }
515
516 void TextSelection::handleMouseUp(TextSelectionHandle* /* handle */, const IntPoint& /* position */)
517 {
518     m_handleMovingDirection = HandleMovingDirectionNormal;
519
520     hideMagnifier();
521     updateHandlers();
522     showContextMenu();
523 }
524
525 bool TextSelection::isMagnifierVisible()
526 {
527     return m_magnifier->isVisible();
528 }
529
530 void TextSelection::updateHandlesAndContextMenu(bool isShow, bool isScrolling)
531 {
532     if (getSelectionScrollingStatus())
533         return;
534
535     if (isTextSelectionDowned() && !isScrolling) {
536         showMagnifier();
537         return;
538     }
539
540     if (m_viewImpl->gestureClient->isGestureWorking()) {
541         EditorState editorState = m_viewImpl->page()->editorState();
542         if (!editorState.selectionIsRange && editorState.isContentEditable)
543             setIsTextSelectionMode(false);
544     }
545
546     if (isShow) {
547 #if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
548         if (m_viewImpl->pageClient->isClipboardWindowOpened())
549             return;
550 #endif
551         if (m_viewImpl->gestureClient->isGestureWorking())
552             return;
553
554         if (m_showTimer)
555             return;
556
557         updateHandlers();
558         showContextMenu();
559     } else {
560         hideHandlers();
561         hideContextMenu();
562     }
563
564     if (isScrolling && isMagnifierVisible())
565         hideMagnifier();
566 }
567
568 void TextSelection::startMoveAnimator()
569 {
570     if (!isEnabled() || !isTextSelectionDowned())
571         return;
572
573     if (!m_moveAnimator)
574         m_moveAnimator = ecore_animator_add(moveAnimatorCallback, this);
575 }
576
577 void TextSelection::stopMoveAnimator()
578 {
579     if (m_moveAnimator) {
580         ecore_animator_del(m_moveAnimator);
581         m_moveAnimator = 0;
582     }
583 }
584
585 void TextSelection::onMouseUp(void* data, Evas*, Evas_Object*, void* eventInfo)
586 {
587     static_cast<TextSelection*>(data)->textSelectionUp(IntPoint());
588 }
589
590 Eina_Bool TextSelection::moveAnimatorCallback(void* data)
591 {
592     TextSelection* textSelection = static_cast<TextSelection*>(data);
593
594     Evas_Coord_Point point;
595     evas_pointer_canvas_xy_get(evas_object_evas_get(textSelection->m_viewImpl->view()), &point.x, &point.y);
596     textSelection->textSelectionMove(IntPoint(point.x, point.y));
597
598     return ECORE_CALLBACK_RENEW;
599 }
600
601 // 'return false' means text selection is not possible for point.
602 // 'return true' means text selection is possible.
603 bool TextSelection::textSelectionDown(const IntPoint& point)
604 {
605     // text selection should be ignored when longtap on handle from osp
606     if (!isEnabled() && isTextSelectionHandleDowned())
607         return false;
608
609 #if ENABLE(TIZEN_ISF_PORT)
610     m_viewImpl->inputMethodContext()->resetIMFContext();
611 #endif
612     setIsTextSelectionMode(false);
613
614     IntPoint contentsPoint = m_viewImpl->transformFromScene().mapPoint(point);
615     bool result = m_viewImpl->page()->selectClosestWord(contentsPoint, false);
616     if (!result)
617         return false;
618
619     if (isTextSelectionMode()) {
620         hide();
621     } else {
622         setIsTextSelectionMode(true);
623     }
624     setIsTextSelectionDowned(true);
625
626     updateMagnifier(point);
627     showMagnifier();
628
629     startMoveAnimator();
630
631     return true;
632 }
633
634 static int s_textSelectionMargin = 5;
635
636 void TextSelection::textSelectionMove(const IntPoint& point)
637 {
638     // text selection should be ignored when longtap on handle from osp
639     if (!isEnabled() && isTextSelectionHandleDowned())
640         return;
641
642     if (!isTextSelectionMode()) {
643         stopMoveAnimator();
644         return;
645     }
646
647     WebCore::IntPoint viewPoint;
648     EditorState editorState = m_viewImpl->page()->editorState();
649     bool isInEditablePicker = false;
650
651 #if ENABLE(TIZEN_INPUT_TAG_EXTENSION)
652     if (editorState.isContentEditable) {
653         if (editorState.inputMethodHints == "date"
654             || editorState.inputMethodHints == "datetime"
655             || editorState.inputMethodHints == "datetime-local"
656             || editorState.inputMethodHints == "month"
657             || editorState.inputMethodHints == "time"
658             || editorState.inputMethodHints == "week")
659             isInEditablePicker = true;
660     }
661 #endif
662
663     if (editorState.isContentEditable && !isInEditablePicker) {
664         IntRect mapRect = m_viewImpl->transformToScene().mapRect(editorState.editorRect);
665         IntPoint updatedPoint = point;
666         bool scrolledY = false;
667         if (point.y() < mapRect.y()) {
668             updatedPoint.setY(mapRect.y() + s_textSelectionMargin);
669             if (m_viewImpl->page()->scrollContentByLine(point,WebCore::DirectionBackward)) {
670                 scrolledY = true;
671                 updateMagnifier(updatedPoint);
672             }
673         } else if (point.y() > mapRect.maxY()) {
674             updatedPoint.setY(mapRect.maxY() - s_textSelectionMargin);
675             if (m_viewImpl->page()->scrollContentByLine(point,WebCore::DirectionForward)) {
676                 scrolledY = true;
677                 updateMagnifier(updatedPoint);
678             }
679         }
680
681         bool scrolledX = false;
682         if (point.x() < mapRect.x()) {
683             updatedPoint.setX(mapRect.x() + s_textSelectionMargin);
684             if (m_viewImpl->page()->scrollContentByCharacter(point,WebCore::DirectionBackward)) {
685                 scrolledX = true;
686                 updateMagnifier(updatedPoint);
687             }
688         } else if (point.x() > mapRect.maxX()) {
689             updatedPoint.setX(mapRect.maxX() - s_textSelectionMargin);
690             if (m_viewImpl->page()->scrollContentByCharacter(point,WebCore::DirectionForward)) {
691                 scrolledX = true;
692                 updateMagnifier(updatedPoint);
693             }
694         }
695
696         if (!scrolledX && !scrolledY) {
697             viewPoint = m_viewImpl->transformFromScene().mapPoint(updatedPoint);
698             m_viewImpl->page()->selectClosestWord(viewPoint, false);
699             updateMagnifier(updatedPoint);
700         }
701     } else {
702         viewPoint = m_viewImpl->transformFromScene().mapPoint(point);
703         m_viewImpl->page()->selectClosestWord(viewPoint, false);
704         updateMagnifier(point);
705     }
706     showMagnifier();
707 }
708
709 void TextSelection::textSelectionUp(const IntPoint& point, bool isStartedTextSelectionFromOutside)
710 {
711     // text selection should be ignored when longtap on handle from osp
712     if (!isEnabled() && isTextSelectionHandleDowned())
713         return;
714
715     stopMoveAnimator();
716
717     if (!isTextSelectionMode() || !isTextSelectionDowned())
718         return;
719
720     setIsTextSelectionDowned(false);
721     hideMagnifier();
722
723     EditorState editorState = m_viewImpl->page()->editorState();
724     if (editorState.selectionIsRange || editorState.isContentEditable) {
725         if (editorState.selectionIsRange)
726             updateHandlers();
727
728         showContextMenu();
729     } else if (!isStartedTextSelectionFromOutside)
730         setIsTextSelectionMode(false);
731 }
732
733 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
734 TextSelectionHandle* TextSelection::getSelectedHandle(const IntPoint& position)
735 {
736     WebCore::IntRect leftHandleRect = m_leftHandle->getHandleRect();
737     if (!leftHandleRect.isEmpty() && leftHandleRect.contains(position))
738         return m_leftHandle;
739
740     WebCore::IntRect rightHandleRect = m_rightHandle->getHandleRect();
741     if (!rightHandleRect.isEmpty() && rightHandleRect.contains(position))
742         return m_rightHandle;
743
744     return 0;
745 }
746
747 void TextSelection::textSelectionHandleDown(const IntPoint& position)
748 {
749     m_selectedHandle = getSelectedHandle(position);
750     if (m_selectedHandle)
751         m_selectedHandle->mouseDown(position);
752     else
753         initHandlesMouseDownedStatus();
754 }
755
756 void TextSelection::textSelectionHandleMove(const IntPoint& position)
757 {
758     if (m_selectedHandle && isTextSelectionHandleDowned())
759         m_selectedHandle->mouseMove(position);
760 }
761
762 void TextSelection::textSelectionHandleUp()
763 {
764     if (m_selectedHandle && isTextSelectionHandleDowned()) {
765         m_selectedHandle->mouseUp();
766         m_selectedHandle = 0;
767     }
768 }
769 #endif
770
771 bool TextSelection::isEnabled()
772 {
773     return ewk_settings_text_selection_enabled_get(ewk_view_settings_get(m_viewImpl->view()));
774 }
775
776 bool TextSelection::isAutomaticClearEnabled()
777 {
778     return ewk_settings_clear_text_selection_automatically_get(ewk_view_settings_get(m_viewImpl->view()));
779 }
780
781 void TextSelection::requestToShow()
782 {
783     EditorState editorState = m_viewImpl->page()->editorState();
784     if (!editorState.selectionIsRange && editorState.isContentEditable) {
785         setIsTextSelectionMode(false);
786         return;
787     }
788
789     if (m_showTimer)
790         ecore_timer_del(m_showTimer);
791     m_showTimer = ecore_timer_loop_add((double)200.0/1000.0, showTimerCallback, this);
792 }
793
794 Eina_Bool TextSelection::showTimerCallback(void* data)
795 {
796     TextSelection* textSelection = static_cast<TextSelection*>(data);
797     textSelection->showHandlesAndContextMenu();
798
799     return ECORE_CALLBACK_RENEW;
800 }
801
802 void TextSelection::showHandlesAndContextMenu()
803 {
804     WebCore::IntRect leftRect, rightRect;
805     int selectionDirection = LToR;
806     if (m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect, selectionDirection)) {
807         leftRect = m_viewImpl->transformToScene().mapRect(leftRect);
808         rightRect = m_viewImpl->transformToScene().mapRect(rightRect);
809         if ((leftRect == m_lastLeftHandleRect) && (rightRect == m_lastRightHandleRect)) {
810             if (m_showTimer) {
811                 ecore_timer_del(m_showTimer);
812                 m_showTimer = 0;
813             }
814
815             updateHandlesAndContextMenu(true);
816         }
817
818         m_lastLeftHandleRect = leftRect;
819         m_lastRightHandleRect = rightRect;
820     }
821 }
822
823 void TextSelection::initHandlesMouseDownedStatus()
824 {
825     m_leftHandle->setIsMouseDowned(false);
826     m_rightHandle->setIsMouseDowned(false);
827 }
828
829 void TextSelection::changeContextMenuPosition(WebCore::IntPoint& position)
830 {
831     if (m_leftHandle->isTop()) {
832         IntRect handleRect = m_leftHandle->getHandleRect();
833         position.setY(position.y() - handleRect.height());
834     }
835 }
836
837 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
838 void TextSelection::informTextStyleState()
839 {
840     WebCore::IntPoint startPoint, endPoint;
841     WebCore::IntRect leftRect, rightRect;
842     int selectionDirection = LToR;
843
844     const EditorState& editor = m_viewImpl->page()->editorState();
845     IntRect caretRect;
846     if (!editor.selectionIsNone && !editor.selectionIsRange)
847         caretRect = editor.selectionRect;
848
849     if (!caretRect.isEmpty()) {
850         startPoint.setX(caretRect.x());
851         startPoint.setY(caretRect.y() + caretRect.height());
852
853         endPoint.setX(caretRect.x() + caretRect.width());
854         endPoint.setY(caretRect.y() + caretRect.height());
855     }
856     else if (m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect, selectionDirection)) {
857         startPoint.setX(leftRect.x());
858         startPoint.setY(leftRect.y() + leftRect.height());
859
860         endPoint.setX(rightRect.x() + rightRect.width());
861         endPoint.setY(rightRect.y() + rightRect.height());
862     }
863
864     AffineTransform toEvasTransform = m_viewImpl->transformToScene();
865     WebCore::IntPoint startEvasPoint = toEvasTransform.mapPoint(startPoint);
866     WebCore::IntPoint endEvasPoint = toEvasTransform.mapPoint(endPoint);
867
868     ewkViewTextStyleState(m_viewImpl->view(), startEvasPoint, endEvasPoint);
869 }
870 #endif
871
872 void TextSelection::selectWordAutomatically(const IntPoint& point)
873 {
874     EditorState editorState = m_viewImpl->page()->editorState();
875     if (editorState.hasComposition) {
876         setAutoWordSelectionScheduled(true, point);
877         m_viewImpl->inputMethodContext()->resetIMFContext();
878         return;
879     }
880
881     setAutoWordSelection(point);
882 }
883
884 void TextSelection::setAutoWordSelectionScheduled(bool scheduled, const IntPoint& position)
885 {
886     m_isAutoWordSelectionScheduled = scheduled;
887     m_scheduledAutoWordSelectionPosition = position;
888 }
889
890 void TextSelection::setAutoWordSelection(const IntPoint& point)
891 {
892     IntPoint contentsPoint = m_viewImpl->transformFromScene().mapPoint(point);
893
894     setAutoWordSelectionScheduled(false, IntPoint(0, 0));
895     setIsTextSelectionMode(false);
896
897     bool result = m_viewImpl->page()->selectClosestWord(contentsPoint, true);
898     if (!result)
899         return;
900
901     setIsTextSelectionMode(true);
902
903     updateHandlers();
904     showContextMenu();
905 }
906 } // namespace WebKit
907
908 #endif // TIZEN_WEBKIT2_TEXT_SELECTION