Revert "Revert "[CherryPick] Input Method upversion""
[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 TextSelection::TextSelection(EwkViewImpl* viewImpl)
42       : m_viewImpl(viewImpl)
43       , m_isTextSelectionDowned(false)
44       , m_isTextSelectionMode(false)
45       , m_moveAnimator(0)
46       , m_showTimer(0)
47 {
48     ASSERT(viewWidget);
49
50     const Eina_List* defaultThemeList = elm_theme_list_get(0);
51
52     const Eina_List* l;
53     void* theme;
54     EINA_LIST_FOREACH(defaultThemeList, l, theme) {
55         char* themePath = elm_theme_list_item_path_get((const char*)theme, 0);
56
57         if (themePath) {
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);
60
61             free(themePath);
62             break;
63         }
64     }
65
66     m_magnifier = new TextSelectionMagnifier(m_viewImpl);
67
68     evas_object_event_callback_add(m_viewImpl->view(), EVAS_CALLBACK_MOUSE_UP, onMouseUp, this);
69 }
70
71 TextSelection::~TextSelection()
72 {
73     if (m_leftHandle)
74         delete m_leftHandle;
75
76     if (m_rightHandle)
77         delete m_rightHandle;
78
79     delete m_magnifier;
80
81     evas_object_event_callback_del(m_viewImpl->view(), EVAS_CALLBACK_MOUSE_UP, onMouseUp);
82 }
83
84 void TextSelection::update()
85 {
86     EditorState editorState = m_viewImpl->page()->editorState();
87     if (editorState.updateEditorRectOnly)
88         return;
89
90     if (isTextSelectionMode()) {
91         if (!editorState.selectionIsRange) {
92             if (editorState.isContentEditable && !evas_object_focus_get(m_viewImpl->view())) {
93                 WebCore::IntRect caretRect;
94                 m_viewImpl->page()->getCaretPosition(caretRect);
95                 if (!caretRect.isEmpty())
96                     return;
97             } else {
98                 WebCore::IntRect leftRect;
99                 WebCore::IntRect rightRect;
100                 if (isTextSelectionDowned() || m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
101                     return;
102
103                 setIsTextSelectionMode(false);
104             }
105 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
106             m_viewImpl->page()->getTextStyleStateForSelection();
107 #endif
108         } else {
109             if (!isTextSelectionDowned() && !isTextSelectionHandleDowned()) {
110                 updateHandlers();
111                 showContextMenu();
112 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
113                 m_viewImpl->page()->getTextStyleStateForSelection();
114 #endif
115             }
116         }
117     }
118 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
119     else {
120         if (editorState.isContentEditable && !editorState.selectionIsRange) {
121             WebCore::IntRect caretRect;
122             m_viewImpl->page()->getCaretPosition(caretRect);
123             if (!caretRect.isEmpty()) {
124                 m_viewImpl->page()->getTextStyleStateForSelection();
125             }
126         }
127     }
128 #endif
129 }
130
131 void TextSelection::setIsTextSelectionMode(bool isTextSelectionMode)
132 {
133     if (!isAutomaticClearEnabled())
134         return;
135
136     if (!isTextSelectionMode) {
137         hide();
138         clear();
139     }
140
141     m_isTextSelectionMode = isTextSelectionMode;
142 }
143
144 void TextSelection::clear()
145 {
146     EditorState editorState = m_viewImpl->page()->editorState();
147     if (!editorState.selectionIsRange)
148         return;
149
150     m_viewImpl->page()->selectionRangeClear();
151 }
152
153 void TextSelection::hide()
154 {
155     hideHandlers();
156     hideMagnifier();
157     hideContextMenu();
158 }
159
160 void TextSelection::updateHandlers()
161 {
162     WebCore::IntRect leftRect, rightRect;
163     if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
164         return;
165
166     m_lastLeftHandleRect = leftRect;
167     m_lastRightHandleRect = rightRect;
168
169     AffineTransform toEvasTransform = m_viewImpl->transformToScene();
170     WebCore::IntPoint leftEvasPoint = toEvasTransform.mapPoint(leftRect.minXMaxYCorner());
171     WebCore::IntPoint rightEvasPoint = toEvasTransform.mapPoint(rightRect.maxXMaxYCorner());
172
173     EditorState editorState = m_viewImpl->page()->editorState();
174     if (editorState.isContentEditable) {
175         m_leftHandle->hide();
176         m_rightHandle->hide();
177
178         WebCore::IntRect editorRect = editorState.editorRect;
179         WebCore::IntPoint editorLeftEvasPoint = toEvasTransform.mapPoint(editorRect.location());
180         WebCore::IntPoint editorRightEvasPoint = toEvasTransform.mapPoint(editorRect.maxXMaxYCorner());
181         int webViewX, webViewY, webViewWidth, webViewHeight;
182
183         evas_object_geometry_get(m_viewImpl->view(), &webViewX, &webViewY, &webViewWidth, &webViewHeight);
184         if ((editorLeftEvasPoint.x() <= leftEvasPoint.x() && editorLeftEvasPoint.y() <= leftEvasPoint.y())
185             && (webViewX <= leftEvasPoint.x() && webViewY <= leftEvasPoint.y())) {
186                 m_leftHandle->move(leftEvasPoint);
187                 m_leftHandle->show();
188         }
189
190         if ((editorRightEvasPoint.x() >= rightEvasPoint.x() && editorRightEvasPoint.y() >= rightEvasPoint.y())
191             && ((webViewX + webViewWidth) >= rightEvasPoint.x() && (webViewY <= rightEvasPoint.y() && (webViewY + webViewHeight) >= rightEvasPoint.y()))) {
192             m_rightHandle->move(rightEvasPoint);
193             m_rightHandle->show();
194         }
195     } else {
196         m_leftHandle->move(leftEvasPoint);
197         m_leftHandle->show();
198
199         m_rightHandle->move(rightEvasPoint);
200         m_rightHandle->show();
201     }
202 }
203
204 void TextSelection::hideHandlers()
205 {
206     m_leftHandle->hide();
207     m_rightHandle->hide();
208 }
209
210 void TextSelection::showMagnifier()
211 {
212     m_magnifier->show();
213 }
214
215 void TextSelection::hideMagnifier()
216 {
217     m_magnifier->hide();
218 }
219
220 void TextSelection::updateMagnifier(const IntPoint& position)
221 {
222     m_magnifier->update(position);
223     m_magnifier->move(position);
224 }
225
226 void TextSelection::showContextMenu()
227 {
228     if (!isEnabled())
229         return;
230
231     EditorState editorState = m_viewImpl->page()->editorState();
232     if (!editorState.selectionIsRange && !editorState.isContentEditable)
233         return;
234
235     WebCore::IntPoint point;
236 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
237     bool isPresentInViewPort = false;
238 #endif
239     if (editorState.selectionIsRange) {
240         WebCore::IntRect leftRect, rightRect;
241         if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect))
242             return;
243
244 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
245         // Checking if this point is in viewport area. If the calcualated
246         // point/Left/Right point are in view port then draw else do not draw the
247         // context menu. Only draw the selection points.
248         FloatRect unscaledRect = FloatRect(m_viewImpl->pageClient->visibleContentRect());
249         unscaledRect.scale(1 / m_viewImpl->pageClient->scaleFactor());
250         IntRect viewportRect = enclosingIntRect(unscaledRect);
251
252         WebCore::IntPoint visiblePoint = leftRect.center();
253         if (viewportRect.contains(visiblePoint)) {
254             // First check That the modified points are present in view port
255             point = visiblePoint;
256             isPresentInViewPort = true;
257         } else if (viewportRect.contains(leftRect.location())) {
258             // else if the calculated point is not in the view port area the
259             // draw context menu at left point if visible
260             point = leftRect.location();
261             isPresentInViewPort = true;
262         } else if (viewportRect.contains(rightRect.maxXMinYCorner())) {
263             // else if the calculated point is not in the view port area the
264             // draw context menu at right point if visible
265             point = rightRect.maxXMinYCorner();
266             isPresentInViewPort = true;
267         }
268 #else
269         point = leftRect.center();
270 #endif
271     } else if (editorState.isContentEditable) {
272         WebCore::IntRect caretRect;
273         m_viewImpl->page()->getCaretPosition(caretRect);
274
275         if (caretRect.isEmpty())
276             return;
277
278         point = caretRect.center();
279 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
280         isPresentInViewPort = true;
281 #endif
282     }
283 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
284     if (!isPresentInViewPort)
285         return;
286 #endif
287
288     // show context menu if its in viewport else do not show the contextmenu
289
290     Ewk_View_Smart_Data* smartData = static_cast<Ewk_View_Smart_Data*>(evas_object_smart_data_get(m_viewImpl->view()));
291     if (!smartData || !smartData->api || !smartData->api->mouse_down || !smartData->api->mouse_up)
292         return;
293
294     point = m_viewImpl->transformToScene().mapPoint(point);
295     Evas* evas = evas_object_evas_get(m_viewImpl->view());
296
297     // send mouse down.
298     Evas_Event_Mouse_Down mouseDown;
299     mouseDown.button = 3;
300     mouseDown.output.x = mouseDown.canvas.x = point.x();
301     mouseDown.output.y = mouseDown.canvas.y = point.y();
302     mouseDown.data = 0;
303     mouseDown.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
304     mouseDown.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
305     mouseDown.flags = EVAS_BUTTON_NONE;
306     mouseDown.timestamp = ecore_time_get() * 1000;
307     mouseDown.event_flags = EVAS_EVENT_FLAG_NONE;
308     mouseDown.dev = 0;
309     smartData->api->mouse_down(smartData, &mouseDown);
310
311     // send mouse up.
312     Evas_Event_Mouse_Up mouseUp;
313     mouseUp.button = 3;
314     mouseUp.output.x = mouseUp.canvas.x = point.x();
315     mouseUp.output.y = mouseUp.canvas.y = point.y();
316     mouseUp.data = 0;
317     mouseUp.modifiers = const_cast<Evas_Modifier*>(evas_key_modifier_get(evas));
318     mouseUp.locks = const_cast<Evas_Lock*>(evas_key_lock_get(evas));
319     mouseUp.flags = EVAS_BUTTON_NONE;
320     mouseUp.timestamp = ecore_time_get() * 1000;
321     mouseUp.event_flags = EVAS_EVENT_FLAG_NONE;
322     mouseUp.dev = 0;
323     smartData->api->mouse_up(smartData, &mouseUp);
324 }
325
326 void TextSelection::hideContextMenu()
327 {
328     if (!isEnabled())
329         return;
330
331     m_viewImpl->page()->hideContextMenu();
332 }
333
334 void TextSelection::setLeftSelectionToEvasPoint(const IntPoint& evasPoint)
335 {
336     m_viewImpl->page()->setLeftSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
337     updateHandlers();
338 }
339
340 void TextSelection::setRightSelectionToEvasPoint(const IntPoint& evasPoint)
341 {
342     m_viewImpl->page()->setRightSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
343     updateHandlers();
344 }
345
346 // handle callbacks
347 void TextSelection::handleMouseDown(TextSelectionHandle* handle, const IntPoint& /*position*/)
348 {
349     WebCore::IntPoint basePosition;
350     EditorState editorState = m_viewImpl->page()->editorState();
351
352     if (editorState.selectionIsRange) {
353         WebCore::IntRect leftRect, rightRect;
354         if (!m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect)) {
355             clear();
356             return;
357         }
358
359         if (handle->isLeft()) {
360             basePosition.setX(leftRect.x());
361             basePosition.setY(leftRect.y() + (leftRect.height()/2));
362         } else {
363             basePosition.setX(rightRect.x() + rightRect.width());
364             basePosition.setY(rightRect.y() + (rightRect.height()/2));
365         }
366
367         handle->setBasePositionForMove(m_viewImpl->transformToScene().mapPoint(basePosition));
368     } else
369         return;
370
371     hideContextMenu();
372     updateMagnifier(handle->position());
373     showMagnifier();
374 }
375
376 void TextSelection::handleMouseMove(TextSelectionHandle* handle, const IntPoint& position)
377 {
378     if (handle->isLeft())
379         setLeftSelectionToEvasPoint(position);
380     else
381         setRightSelectionToEvasPoint(position);
382
383     updateMagnifier(handle->position());
384 }
385
386 void TextSelection::handleMouseUp(TextSelectionHandle* /* handle */, const IntPoint& /* position */)
387 {
388     hideMagnifier();
389     showContextMenu();
390 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
391     m_viewImpl->page()->getTextStyleStateForSelection();
392 #endif
393 }
394
395 bool TextSelection::isMagnifierVisible()
396 {
397     return m_magnifier->isVisible();
398 }
399
400 void TextSelection::updateHandlesAndContextMenu(bool isShow, bool isScrolling)
401 {
402     if (isTextSelectionDowned() && !isScrolling) {
403         showMagnifier();
404         return;
405     }
406
407     if (isShow) {
408         updateHandlers();
409         showContextMenu();
410     } else {
411         hideHandlers();
412         hideContextMenu();
413     }
414
415     if (isScrolling && isMagnifierVisible())
416         hideMagnifier();
417 }
418
419 void TextSelection::startMoveAnimator()
420 {
421     if (!isEnabled() || !isTextSelectionDowned())
422         return;
423
424     if (!m_moveAnimator)
425         m_moveAnimator = ecore_animator_add(moveAnimatorCallback, this);
426 }
427
428 void TextSelection::stopMoveAnimator()
429 {
430     if (m_moveAnimator) {
431         ecore_animator_del(m_moveAnimator);
432         m_moveAnimator = 0;
433     }
434 }
435
436 void TextSelection::onMouseUp(void* data, Evas*, Evas_Object*, void* eventInfo)
437 {
438     static_cast<TextSelection*>(data)->textSelectionUp(IntPoint());
439 }
440
441 Eina_Bool TextSelection::moveAnimatorCallback(void* data)
442 {
443     TextSelection* textSelection = static_cast<TextSelection*>(data);
444
445     Evas_Coord_Point point;
446     evas_pointer_canvas_xy_get(evas_object_evas_get(textSelection->m_viewImpl->view()), &point.x, &point.y);
447     textSelection->textSelectionMove(IntPoint(point.x, point.y));
448
449     return ECORE_CALLBACK_RENEW;
450 }
451
452 // 'return false' means text selection is not possible for point.
453 // 'return true' means text selection is possible.
454 bool TextSelection::textSelectionDown(const IntPoint& point, bool isStartedTextSelectionFromOutside)
455 {
456     // text selection should be ignored when longtap on handle from osp
457     if (!isEnabled() && isTextSelectionHandleDowned())
458         return false;
459
460     setIsTextSelectionMode(false);
461
462     IntPoint contentsPoint = m_viewImpl->transformFromScene().mapPoint(point);
463     bool result = m_viewImpl->page()->selectClosestWord(contentsPoint, isStartedTextSelectionFromOutside);
464     if (!result)
465         return false;
466
467     if (isTextSelectionMode()) {
468         hide();
469     } else {
470         setIsTextSelectionMode(true);
471     }
472     setIsTextSelectionDowned(true);
473
474     updateMagnifier(point);
475     showMagnifier();
476
477     startMoveAnimator();
478
479     return true;
480 }
481
482 void TextSelection::textSelectionMove(const IntPoint& point, bool isStartedTextSelectionFromOutside)
483 {
484     // text selection should be ignored when longtap on handle from osp
485     if (!isEnabled() && isTextSelectionHandleDowned())
486         return;
487
488     if (!isTextSelectionMode()) {
489         stopMoveAnimator();
490         return;
491     }
492
493     WebCore::IntPoint viewPoint = m_viewImpl->transformFromScene().mapPoint(point);
494     EditorState editorState = m_viewImpl->page()->editorState();
495     if (editorState.isContentEditable) {
496         IntRect mapRect = m_viewImpl->transformToScene().mapRect(editorState.editorRect);
497         if(mapRect.contains(point)) {
498             m_viewImpl->page()->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
499             updateMagnifier(point);
500         }
501     } else {
502         m_viewImpl->page()->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
503         updateMagnifier(point);
504     }
505     showMagnifier();
506 }
507
508 void TextSelection::textSelectionUp(const IntPoint& point, bool isStartedTextSelectionFromOutside)
509 {
510     // text selection should be ignored when longtap on handle from osp
511     if (!isEnabled() && isTextSelectionHandleDowned())
512         return;
513
514     stopMoveAnimator();
515
516     if (!isTextSelectionMode() || !isTextSelectionDowned())
517         return;
518
519     setIsTextSelectionDowned(false);
520     hideMagnifier();
521
522     EditorState editorState = m_viewImpl->page()->editorState();
523     if (editorState.selectionIsRange || editorState.isContentEditable) {
524         if (editorState.selectionIsRange)
525             updateHandlers();
526
527         showContextMenu();
528     } else if (!isStartedTextSelectionFromOutside)
529         setIsTextSelectionMode(false);
530
531 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
532     m_viewImpl->page()->getTextStyleStateForSelection();
533 #endif
534 }
535
536 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
537 TextSelectionHandle* TextSelection::getSelectedHandle(const IntPoint& position)
538 {
539     WebCore::IntRect leftHandleRect = m_leftHandle->getHandleRect();
540     if (leftHandleRect.contains(position))
541         return m_leftHandle;
542
543     WebCore::IntRect rightHandleRect = m_rightHandle->getHandleRect();
544     if (rightHandleRect.contains(position))
545         return m_rightHandle;
546
547     return 0;
548 }
549
550 void TextSelection::textSelectionHandleDown(const IntPoint& position)
551 {
552     TextSelectionHandle* selectedHandle = getSelectedHandle(position);
553     if (selectedHandle) {
554         selectedHandle->mouseDown(position);
555         if (selectedHandle->isMouseDowned())
556             return;
557     }
558 }
559
560 void TextSelection::textSelectionHandleMove(const IntPoint& position)
561 {
562     if (isTextSelectionHandleDowned()) {
563         TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
564         selectedHandle->mouseMove(position);
565     }
566 }
567
568 void TextSelection::textSelectionHandleUp()
569 {
570     if (isTextSelectionHandleDowned()) {
571         TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
572         selectedHandle->mouseUp();
573     }
574 }
575 #endif
576
577 bool TextSelection::isEnabled()
578 {
579     return ewk_settings_text_selection_enabled_get(ewk_view_settings_get(m_viewImpl->view()));
580 }
581
582 bool TextSelection::isAutomaticClearEnabled()
583 {
584     return ewk_settings_clear_text_selection_automatically_get(ewk_view_settings_get(m_viewImpl->view()));
585 }
586
587 void TextSelection::requestToShow()
588 {
589     if (m_showTimer)
590         ecore_timer_del(m_showTimer);
591     m_showTimer = ecore_timer_loop_add((double)200.0/1000.0, showTimerCallback, this);
592 }
593
594 Eina_Bool TextSelection::showTimerCallback(void* data)
595 {
596     TextSelection* textSelection = static_cast<TextSelection*>(data);
597     textSelection->showHandlesAndContextMenu();
598
599     return ECORE_CALLBACK_RENEW;
600 }
601
602 void TextSelection::showHandlesAndContextMenu()
603 {
604     WebCore::IntRect leftRect, rightRect;
605     if (m_viewImpl->page()->getSelectionHandlers(leftRect, rightRect)) {
606         if ((leftRect == m_lastLeftHandleRect) && (rightRect == m_lastRightHandleRect)) {
607             if (m_showTimer) {
608                 ecore_timer_del(m_showTimer);
609                 m_showTimer = 0;
610             }
611
612             updateHandlesAndContextMenu(true);
613         }
614
615         m_lastLeftHandleRect = leftRect;
616         m_lastRightHandleRect = rightRect;
617     }
618 }
619
620 } // namespace WebKit
621
622 #endif // TIZEN_WEBKIT2_TEXT_SELECTION