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 "NativeWebMouseEvent.h"
33 #include "ewk_view.h"
34 #include <Elementary.h>
35
36 using namespace WebCore;
37
38 namespace WebKit {
39
40 TextSelection::TextSelection(Evas_Object* viewWidget, WebPageProxy* page, PageClientImpl* pageClient)
41       : m_object(viewWidget)
42       , m_page(page)
43       , m_pageClient(pageClient)
44       , m_isTextSelectionDowned(false)
45       , m_isTextSelectionMode(false)
46       , m_moveAnimator(0)
47       , m_showTimer(0)
48 {
49     ASSERT(viewWidget);
50
51     m_viewImpl = EwkViewImpl::fromEvasObject(m_object);
52
53     const Eina_List* defaultThemeList = elm_theme_list_get(0);
54
55     const Eina_List* l;
56     void* theme;
57     EINA_LIST_FOREACH(defaultThemeList, l, theme) {
58         char* themePath = elm_theme_list_item_path_get((const char*)theme, 0);
59
60         if (themePath) {
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);
63
64             free(themePath);
65             break;
66         }
67     }
68
69     m_magnifier = new TextSelectionMagnifier(m_object, page, pageClient);
70
71     evas_object_event_callback_add(m_object, EVAS_CALLBACK_MOUSE_UP, onMouseUp, this);
72 }
73
74 TextSelection::~TextSelection()
75 {
76     if (m_leftHandle)
77         delete m_leftHandle;
78
79     if (m_rightHandle)
80         delete m_rightHandle;
81
82     delete m_magnifier;
83
84     evas_object_event_callback_del(m_object, EVAS_CALLBACK_MOUSE_UP, onMouseUp);
85 }
86
87 void TextSelection::update()
88 {
89     EditorState editorState = m_page->editorState();
90     if (editorState.updateEditorRectOnly)
91         return;
92
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())
99                     return;
100             } else {
101                 WebCore::IntRect leftRect;
102                 WebCore::IntRect rightRect;
103                 if (isTextSelectionDowned() || m_page->getSelectionHandlers(leftRect, rightRect))
104                     return;
105
106                 setIsTextSelectionMode(false);
107             }
108 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
109             m_page->getTextStyleStateForSelection();
110 #endif
111         } else {
112             if (!isTextSelectionDowned() && !isTextSelectionHandleDowned()) {
113                 updateHandlers();
114                 showContextMenu();
115 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
116                 m_page->getTextStyleStateForSelection();
117 #endif
118             }
119         }
120     }
121 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
122     else {
123         if (editorState.isContentEditable && !editorState.selectionIsRange) {
124             WebCore::IntRect caretRect;
125             m_page->getCaretPosition(caretRect);
126             if (!caretRect.isEmpty()) {
127                 m_page->getTextStyleStateForSelection();
128             }
129         }
130     }
131 #endif
132 }
133
134 void TextSelection::setIsTextSelectionMode(bool isTextSelectionMode)
135 {
136     if (!isAutomaticClearEnabled())
137         return;
138
139     if (!isTextSelectionMode) {
140         hide();
141         clear();
142     }
143
144     m_isTextSelectionMode = isTextSelectionMode;
145 }
146
147 void TextSelection::clear()
148 {
149     EditorState editorState = m_page->editorState();
150     if (!editorState.selectionIsRange)
151         return;
152
153     m_page->selectionRangeClear();
154 }
155
156 void TextSelection::hide()
157 {
158     hideHandlers();
159     hideMagnifier();
160     hideContextMenu();
161 }
162
163 void TextSelection::updateHandlers()
164 {
165     WebCore::IntRect leftRect, rightRect;
166     if (!m_page->getSelectionHandlers(leftRect, rightRect))
167         return;
168
169     m_lastLeftHandleRect = leftRect;
170     m_lastRightHandleRect = rightRect;
171
172     AffineTransform toEvasTransform = m_viewImpl->transformToScene();
173     WebCore::IntPoint leftEvasPoint = toEvasTransform.mapPoint(leftRect.minXMaxYCorner());
174     WebCore::IntPoint rightEvasPoint = toEvasTransform.mapPoint(rightRect.maxXMaxYCorner());
175
176     EditorState editorState = m_page->editorState();
177     if (editorState.isContentEditable) {
178         m_leftHandle->hide();
179         m_rightHandle->hide();
180
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;
185
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();
191         }
192
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();
197         }
198     } else {
199         m_leftHandle->move(leftEvasPoint);
200         m_leftHandle->show();
201
202         m_rightHandle->move(rightEvasPoint);
203         m_rightHandle->show();
204     }
205 }
206
207 void TextSelection::hideHandlers()
208 {
209     m_leftHandle->hide();
210     m_rightHandle->hide();
211 }
212
213 void TextSelection::showMagnifier()
214 {
215     m_magnifier->show();
216 }
217
218 void TextSelection::hideMagnifier()
219 {
220     m_magnifier->hide();
221 }
222
223 void TextSelection::updateMagnifier(const IntPoint& position)
224 {
225     m_magnifier->update(position);
226     m_magnifier->move(position);
227 }
228
229 void TextSelection::showContextMenu()
230 {
231     if (!isEnabled())
232         return;
233
234     EditorState editorState = m_page->editorState();
235     if (!editorState.selectionIsRange && !editorState.isContentEditable)
236         return;
237
238     WebCore::IntPoint point;
239 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
240     bool isPresentInViewPort = false;
241 #endif
242     if (editorState.selectionIsRange) {
243         WebCore::IntRect leftRect, rightRect;
244         if (!m_page->getSelectionHandlers(leftRect, rightRect))
245             return;
246
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);
254
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;
270         }
271 #else
272         point = leftRect.center();
273 #endif
274     } else if (editorState.isContentEditable) {
275         WebCore::IntRect caretRect;
276         m_page->getCaretPosition(caretRect);
277
278         if (caretRect.isEmpty())
279             return;
280
281         point = caretRect.center();
282 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
283         isPresentInViewPort = true;
284 #endif
285     }
286 #if ENABLE(TIZEN_WEBKIT2_TILED_BACKING_STORE)
287     if (!isPresentInViewPort)
288         return;
289 #endif
290
291     // show context menu if its in viewport else do not show the contextmenu
292
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)
295         return;
296
297     point = m_viewImpl->transformToScene().mapPoint(point);
298     Evas* evas = evas_object_evas_get(m_object);
299
300     // send mouse down.
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();
305     mouseDown.data = 0;
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;
311     mouseDown.dev = 0;
312     smartData->api->mouse_down(smartData, &mouseDown);
313
314     // send mouse up.
315     Evas_Event_Mouse_Up mouseUp;
316     mouseUp.button = 3;
317     mouseUp.output.x = mouseUp.canvas.x = point.x();
318     mouseUp.output.y = mouseUp.canvas.y = point.y();
319     mouseUp.data = 0;
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;
325     mouseUp.dev = 0;
326     smartData->api->mouse_up(smartData, &mouseUp);
327 }
328
329 void TextSelection::hideContextMenu()
330 {
331     if (!isEnabled())
332         return;
333
334     m_page->hideContextMenu();
335 }
336
337 void TextSelection::setLeftSelectionToEvasPoint(const IntPoint& evasPoint)
338 {
339     m_page->setLeftSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
340     updateHandlers();
341 }
342
343 void TextSelection::setRightSelectionToEvasPoint(const IntPoint& evasPoint)
344 {
345     m_page->setRightSelection(m_viewImpl->transformFromScene().mapPoint(evasPoint));
346     updateHandlers();
347 }
348
349 // handle callbacks
350 void TextSelection::handleMouseDown(TextSelectionHandle* handle, const IntPoint& /*position*/)
351 {
352     WebCore::IntPoint basePosition;
353     EditorState editorState = m_page->editorState();
354
355     if (editorState.selectionIsRange) {
356         WebCore::IntRect leftRect, rightRect;
357         if (!m_page->getSelectionHandlers(leftRect, rightRect)) {
358             clear();
359             return;
360         }
361
362         if (handle->isLeft()) {
363             basePosition.setX(leftRect.x());
364             basePosition.setY(leftRect.y() + (leftRect.height()/2));
365         } else {
366             basePosition.setX(rightRect.x() + rightRect.width());
367             basePosition.setY(rightRect.y() + (rightRect.height()/2));
368         }
369
370         handle->setBasePositionForMove(m_viewImpl->transformToScene().mapPoint(basePosition));
371     } else
372         return;
373
374     hideContextMenu();
375     updateMagnifier(handle->position());
376     showMagnifier();
377 }
378
379 void TextSelection::handleMouseMove(TextSelectionHandle* handle, const IntPoint& position)
380 {
381     if (handle->isLeft())
382         setLeftSelectionToEvasPoint(position);
383     else
384         setRightSelectionToEvasPoint(position);
385
386     updateMagnifier(handle->position());
387 }
388
389 void TextSelection::handleMouseUp(TextSelectionHandle* /* handle */, const IntPoint& /* position */)
390 {
391     hideMagnifier();
392     showContextMenu();
393 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
394     m_page->getTextStyleStateForSelection();
395 #endif
396 }
397
398 bool TextSelection::isMagnifierVisible()
399 {
400     return m_magnifier->isVisible();
401 }
402
403 void TextSelection::updateHandlesAndContextMenu(bool isShow, bool isScrolling)
404 {
405     if (isTextSelectionDowned() && !isScrolling) {
406         showMagnifier();
407         return;
408     }
409
410     if (isShow) {
411         updateHandlers();
412         showContextMenu();
413     } else {
414         hideHandlers();
415         hideContextMenu();
416     }
417
418     if (isScrolling && isMagnifierVisible())
419         hideMagnifier();
420 }
421
422 void TextSelection::startMoveAnimator()
423 {
424     if (!isEnabled() || !isTextSelectionDowned())
425         return;
426
427     if (!m_moveAnimator)
428         m_moveAnimator = ecore_animator_add(moveAnimatorCallback, this);
429 }
430
431 void TextSelection::stopMoveAnimator()
432 {
433     if (m_moveAnimator) {
434         ecore_animator_del(m_moveAnimator);
435         m_moveAnimator = 0;
436     }
437 }
438
439 void TextSelection::onMouseUp(void* data, Evas*, Evas_Object*, void* eventInfo)
440 {
441     static_cast<TextSelection*>(data)->textSelectionUp(IntPoint());
442 }
443
444 Eina_Bool TextSelection::moveAnimatorCallback(void* data)
445 {
446     TextSelection* textSelection = static_cast<TextSelection*>(data);
447
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));
451
452     return ECORE_CALLBACK_RENEW;
453 }
454
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)
458 {
459     setIsTextSelectionMode(false);
460
461     IntPoint contentsPoint = m_viewImpl->transformFromScene().mapPoint(point);
462     bool result = m_page->selectClosestWord(contentsPoint, isStartedTextSelectionFromOutside);
463     if (!result)
464         return false;
465
466     if (isTextSelectionMode()) {
467         hide();
468     } else {
469         setIsTextSelectionMode(true);
470     }
471     setIsTextSelectionDowned(true);
472
473     updateMagnifier(point);
474     showMagnifier();
475
476     startMoveAnimator();
477
478     return true;
479 }
480
481 void TextSelection::textSelectionMove(const IntPoint& point, bool isStartedTextSelectionFromOutside)
482 {
483     if (!isTextSelectionMode()) {
484         stopMoveAnimator();
485         return;
486     }
487
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);
495         }
496     } else {
497         m_page->selectClosestWord(viewPoint, isStartedTextSelectionFromOutside);
498         updateMagnifier(point);
499     }
500     showMagnifier();
501 }
502
503 void TextSelection::textSelectionUp(const IntPoint& point, bool isStartedTextSelectionFromOutside)
504 {
505     stopMoveAnimator();
506
507     if (!isTextSelectionMode() || !isTextSelectionDowned())
508         return;
509
510     setIsTextSelectionDowned(false);
511     hideMagnifier();
512
513     EditorState editorState = m_page->editorState();
514     if (editorState.selectionIsRange || editorState.isContentEditable) {
515         if (editorState.selectionIsRange)
516             updateHandlers();
517
518         showContextMenu();
519     } else if (!isStartedTextSelectionFromOutside)
520         setIsTextSelectionMode(false);
521
522 #if ENABLE(TIZEN_WEBKIT2_GET_TEXT_STYLE_FOR_SELECTION)
523     m_page->getTextStyleStateForSelection();
524 #endif
525 }
526
527 #if ENABLE(TIZEN_WEBKIT2_FOR_MOVING_TEXT_SELECTION_HANDLE_FROM_OSP)
528 TextSelectionHandle* TextSelection::getSelectedHandle(const IntPoint& position)
529 {
530     WebCore::IntRect leftHandleRect = m_leftHandle->getHandleRect();
531     if (leftHandleRect.contains(position))
532         return m_leftHandle;
533
534     WebCore::IntRect rightHandleRect = m_rightHandle->getHandleRect();
535     if (rightHandleRect.contains(position))
536         return m_rightHandle;
537
538     return 0;
539 }
540
541 void TextSelection::textSelectionHandleDown(const IntPoint& position)
542 {
543     TextSelectionHandle* selectedHandle = getSelectedHandle(position);
544     if (selectedHandle) {
545         selectedHandle->mouseDown(position);
546         if (selectedHandle->isMouseDowned())
547             return;
548     }
549 }
550
551 void TextSelection::textSelectionHandleMove(const IntPoint& position)
552 {
553     if (isTextSelectionHandleDowned()) {
554         TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
555         selectedHandle->mouseMove(position);
556     }
557 }
558
559 void TextSelection::textSelectionHandleUp()
560 {
561     if (isTextSelectionHandleDowned()) {
562         TextSelectionHandle* selectedHandle = m_leftHandle->isMouseDowned() ? m_leftHandle : m_rightHandle;
563         selectedHandle->mouseUp();
564     }
565 }
566 #endif
567
568 bool TextSelection::isEnabled()
569 {
570     return ewk_settings_text_selection_enabled_get(ewk_view_settings_get(m_object));
571 }
572
573 bool TextSelection::isAutomaticClearEnabled()
574 {
575     return ewk_settings_clear_text_selection_automatically_get(ewk_view_settings_get(m_object));
576 }
577
578 void TextSelection::requestToShow()
579 {
580     if (m_showTimer)
581         ecore_timer_del(m_showTimer);
582     m_showTimer = ecore_timer_loop_add((double)200.0/1000.0, showTimerCallback, this);
583 }
584
585 Eina_Bool TextSelection::showTimerCallback(void* data)
586 {
587     TextSelection* textSelection = static_cast<TextSelection*>(data);
588     textSelection->showHandlesAndContextMenu();
589
590     return ECORE_CALLBACK_RENEW;
591 }
592
593 void TextSelection::showHandlesAndContextMenu()
594 {
595     WebCore::IntRect leftRect, rightRect;
596     if (m_page->getSelectionHandlers(leftRect, rightRect)) {
597         if ((leftRect == m_lastLeftHandleRect) && (rightRect == m_lastRightHandleRect)) {
598             if (m_showTimer) {
599                 ecore_timer_del(m_showTimer);
600                 m_showTimer = 0;
601             }
602
603             updateHandlesAndContextMenu(true);
604         }
605
606         m_lastLeftHandleRect = leftRect;
607         m_lastRightHandleRect = rightRect;
608     }
609 }
610
611 } // namespace WebKit
612
613 #endif // TIZEN_WEBKIT2_TEXT_SELECTION