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