Modify execution condition of input field zoom
[framework/web/webkit-efl.git] / Source / WebKit2 / UIProcess / efl / InputMethodContextEfl.cpp
1 /*
2    Copyright (C) 2011 Samsung Electronics
3    Copyright (C) 2012 Intel Corporation. All rights reserved.
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14
15     You should have received a copy of the GNU Library General Public License
16     along with this library; see the file COPYING.LIB.  If not, write to
17     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18     Boston, MA 02110-1301, USA.
19 */
20
21 #include "config.h"
22 #include "InputMethodContextEfl.h"
23
24 #include "EwkViewImpl.h"
25 #include "WebPageProxy.h"
26 #include <Ecore_Evas.h>
27 #include <Ecore_IMF_Evas.h>
28
29 using namespace WebCore;
30
31 namespace WebKit {
32
33 #if ENABLE(TIZEN_ISF_PORT)
34 const unsigned InputMethodContextEfl::maxContextSize = 10;
35 Ecore_X_Atom InputMethodContextEfl::s_externalKeyboardProperty = 0;
36 bool InputMethodContextEfl::s_shouldUseExternalKeyboard = false;
37 bool InputMethodContextEfl::s_isSystemKeypadShow = false;
38
39 void InputMethodContextEfl::initializeExternalKeyboard()
40 {
41     s_externalKeyboardProperty = ecore_x_atom_get("X External Keyboard Exist");
42     if (!s_externalKeyboardProperty)
43         return;
44
45     Ecore_X_Window rootWin = ecore_x_window_root_first_get();
46     ecore_x_event_mask_set(rootWin, ECORE_X_EVENT_MASK_WINDOW_PROPERTY);
47
48     ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY, windowPropertyChanged, 0);
49
50     unsigned int value;
51     if (ecore_x_window_prop_card32_get(rootWin, s_externalKeyboardProperty, &value, 1) <= 0)
52         return;
53
54     s_shouldUseExternalKeyboard = value;
55
56     if (ecore_x_e_virtual_keyboard_state_get(rootWin) == ECORE_X_VIRTUAL_KEYBOARD_STATE_ON)
57         s_isSystemKeypadShow = true;
58 }
59
60 Eina_Bool InputMethodContextEfl::windowPropertyChanged(void*, int, void* event)
61 {
62     Ecore_X_Event_Window_Property* propertyEvent = static_cast<Ecore_X_Event_Window_Property*>(event);
63
64     if (propertyEvent->atom == s_externalKeyboardProperty) {
65         unsigned int value;
66         if (ecore_x_window_prop_card32_get(propertyEvent->win, s_externalKeyboardProperty, &value, 1) <= 0 || s_shouldUseExternalKeyboard == value)
67             return ECORE_CALLBACK_PASS_ON;
68
69         s_shouldUseExternalKeyboard = value;
70
71 #if ENABLE(TIZEN_FOCUS_UI)
72         if (!s_shouldUseExternalKeyboard) {
73             Vector<RefPtr<WebPageProxy> > pages;
74             EwkViewImpl::pages(pages);
75
76             for (size_t i = 0; i < pages.size(); ++i)
77                 pages[i]->setFocusUIEnabled(false);
78         }
79 #endif
80     } else if (propertyEvent->atom == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE) {
81         if (ecore_x_e_virtual_keyboard_state_get(propertyEvent->win) == ECORE_X_VIRTUAL_KEYBOARD_STATE_ON)
82             s_isSystemKeypadShow = true;
83         else
84             s_isSystemKeypadShow = false;
85     }
86
87     return ECORE_CALLBACK_PASS_ON;
88 }
89 #endif
90
91 InputMethodContextEfl::InputMethodContextEfl(EwkViewImpl* viewImpl, PassOwnPtr<Ecore_IMF_Context> context)
92     : m_viewImpl(viewImpl)
93     , m_context(context)
94     , m_focused(false)
95 #if ENABLE(TIZEN_ISF_PORT)
96     , m_contextID(0)
97     , m_state(ECORE_IMF_INPUT_PANEL_STATE_HIDE)
98     , m_inputPickerType(-1)
99     , m_doNotHandleFakeKeyEvent(false)
100     , m_fakeKeyEventTimer(this, &InputMethodContextEfl::fakeKeyEventTimerFired)
101     , m_approximateCursorPosition(0)
102     , m_isLastKeyEventFiltered(false)
103 #endif
104 {
105 #if ENABLE(TIZEN_ISF_PORT)
106     if (!s_externalKeyboardProperty)
107         initializeExternalKeyboard();
108 #else
109     ASSERT(context);
110     ecore_imf_context_event_callback_add(m_context.get(), ECORE_IMF_CALLBACK_PREEDIT_CHANGED, onIMFPreeditSequenceChanged, this);
111     ecore_imf_context_event_callback_add(m_context.get(), ECORE_IMF_CALLBACK_COMMIT, onIMFInputSequenceComplete, this);
112 #endif
113 }
114
115 InputMethodContextEfl::~InputMethodContextEfl()
116 {
117 }
118
119 #if ENABLE(TIZEN_ISF_PORT)
120 void InputMethodContextEfl::onIMFInputPanelStateChanged(void* data, Ecore_IMF_Context*, int state)
121 {
122     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
123
124     inputMethodContext->setState(state);
125
126     if (state == ECORE_IMF_INPUT_PANEL_STATE_HIDE) {
127 #if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
128         if (!s_shouldUseExternalKeyboard && inputMethodContext->m_viewImpl->pageClient->isClipboardWindowOpened())
129             inputMethodContext->m_viewImpl->pageClient->closeClipboardWindow();
130 #endif
131
132         if (inputMethodContext->m_viewImpl->page()->editorState().hasComposition)
133             inputMethodContext->resetIMFContext();
134
135         evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "editorclient,ime,closed", 0);
136     } else if (state == ECORE_IMF_INPUT_PANEL_STATE_SHOW)
137         evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "editorclient,ime,opened", 0);
138 }
139
140 void InputMethodContextEfl::onIMFInputPanelGeometryChanged(void* data, Ecore_IMF_Context*, int)
141 {
142     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
143     if (!inputMethodContext->m_context)
144         return;
145
146     Eina_Rectangle rect;
147     ecore_imf_context_input_panel_geometry_get(inputMethodContext->m_context.get(), &rect.x, &rect.y, &rect.w, &rect.h);
148     evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "inputmethod,changed", &rect);
149
150     inputMethodContext->setIMERect(IntRect(rect.x, rect.y, rect.w, rect.h));
151 }
152
153 void InputMethodContextEfl::onIMFCandidatePanelStateChanged(void* data, Ecore_IMF_Context*, int state)
154 {
155     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
156
157     if (state == ECORE_IMF_CANDIDATE_PANEL_SHOW)
158         evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "editorclient,candidate,opened", 0);
159     else
160         evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "editorclient,candidate,closed", 0);
161 }
162
163 void InputMethodContextEfl::onIMFCandidatePanelGeometryChanged(void* data, Ecore_IMF_Context*, int)
164 {
165     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
166     if (!inputMethodContext->m_context)
167         return;
168
169     Eina_Rectangle rect;
170     ecore_imf_context_candidate_panel_geometry_get(inputMethodContext->m_context.get(), &rect.x, &rect.y, &rect.w, &rect.h);
171     evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "editorclient,candidate,changed", &rect);
172 }
173
174 Eina_Bool InputMethodContextEfl::onIMFRetrieveSurrounding(void* data, Ecore_IMF_Context*, char** text, int* offset)
175 {
176     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
177     if (!inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused || (!text && !offset))
178         return false;
179
180     const EditorState& editor = inputMethodContext->m_viewImpl->page()->editorState();
181
182     if (text) {
183         CString utf8Text;
184         if (!inputMethodContext->m_approximateSurroundingText.isNull())
185             utf8Text = inputMethodContext->m_approximateSurroundingText.utf8();
186         else
187             utf8Text = editor.surroundingText.utf8();
188         size_t length = utf8Text.length();
189
190         *text = static_cast<char*>(malloc((length + 1) * sizeof(char)));
191         if (!(*text))
192             return false;
193
194         if (length)
195             strncpy(*text, utf8Text.data(), length);
196         (*text)[length] = 0;
197     }
198
199     if (offset) {
200         if (!inputMethodContext->m_approximateSurroundingText.isNull())
201             *offset = inputMethodContext->m_approximateCursorPosition;
202         else
203             *offset = editor.cursorPosition;
204     }
205
206     return true;
207 }
208
209 void InputMethodContextEfl::onIMFDeleteSurrounding(void* data, Ecore_IMF_Context*, void* eventInfo)
210 {
211     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
212     if (!eventInfo || !inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused)
213         return;
214
215     inputMethodContext->requestFakeKeyEvent();
216
217     Ecore_IMF_Event_Delete_Surrounding* event = static_cast<Ecore_IMF_Event_Delete_Surrounding*>(eventInfo);
218     inputMethodContext->m_viewImpl->page()->deleteSurroundingText(event->offset, event->n_chars);
219
220     inputMethodContext->updateApproximateText(String(), event->offset, event->n_chars);
221 }
222
223 void InputMethodContextEfl::onIMFInputSequenceComplete(void* data, Ecore_IMF_Context*, void* eventInfo)
224 {
225     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
226     if (!eventInfo || !inputMethodContext->m_focused)
227         return;
228
229     inputMethodContext->requestFakeKeyEvent();
230
231     String completeString = String::fromUTF8(static_cast<char*>(eventInfo));
232     inputMethodContext->m_viewImpl->page()->confirmComposition(completeString);
233
234     inputMethodContext->updateApproximateText(completeString, 0, 0);
235 }
236
237 #if ENABLE(TIZEN_WEBKIT2_SUPPORT_JAPANESE_IME)
238 unsigned getUTF8CharacterIndex(const char* string, unsigned byteIndex)
239 {
240     unsigned index = 0;
241     const char* end = string + byteIndex;
242
243     while (*string && string < end) {
244         unsigned offset;
245
246         if ((*string & 0x80) == 0x00)
247             offset = 1;
248         else if ((*string & 0xe0) == 0xc0)
249             offset = 2;
250         else if ((*string & 0xf0) == 0xe0)
251             offset = 3;
252         else if ((*string & 0xf8) == 0xf0)
253             offset = 4;
254         else if ((*string & 0xfc) == 0xf8)
255             offset = 5;
256         else if ((*string & 0xfe) == 0xfc)
257             offset = 6;
258         else
259             offset = 1;
260
261         ++index;
262         while (*string && offset--)
263             ++string;
264     }
265
266     return index;
267 }
268 #endif
269
270 void InputMethodContextEfl::onIMFPreeditSequenceChanged(void* data, Ecore_IMF_Context* context, void*)
271 {
272     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
273
274     if (!inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused)
275         return;
276
277     WebPageProxy* page = inputMethodContext->m_viewImpl->page();
278     if (!page->focusedFrame())
279         return;
280
281     inputMethodContext->requestFakeKeyEvent();
282
283     const EditorState& editor = inputMethodContext->m_viewImpl->page()->editorState();
284     PageClientImpl* pageClient = inputMethodContext->m_viewImpl->pageClient.get();
285     IntRect caretRect;
286     if (!editor.selectionIsNone && !editor.selectionIsRange) {
287         caretRect = editor.selectionRect;
288         caretRect.scale(pageClient->scaleFactor());
289     }
290
291     int viewX, viewY;
292     evas_object_geometry_get(inputMethodContext->m_viewImpl->view(), &viewX, &viewY, 0, 0);
293
294     int x = caretRect.x() - pageClient->scrollPosition().x() + viewX;
295     int y = caretRect.y() - pageClient->scrollPosition().y() + viewY;
296     int w = caretRect.width();
297     int h = caretRect.height();
298     ecore_imf_context_cursor_location_set(context, x, y, w, h);
299
300     char* buffer = 0;
301     Eina_List* preeditAttrs = 0;
302     int cursorPosition = 0;
303
304     ecore_imf_context_preedit_string_with_attributes_get(context, &buffer, &preeditAttrs, &cursorPosition);
305
306     String preeditString = String::fromUTF8(buffer);
307     Vector<CompositionUnderline> underlines;
308
309     if (preeditAttrs) {
310         void* item = 0;
311 #if ENABLE(TIZEN_WEBKIT2_SUPPORT_JAPANESE_IME)
312         Eina_List* listIterator = 0;
313         EINA_LIST_FOREACH(preeditAttrs, listIterator, item) {
314             Ecore_IMF_Preedit_Attr* preeditAttr = static_cast<Ecore_IMF_Preedit_Attr*>(item);
315
316             unsigned startIndex = getUTF8CharacterIndex(buffer, preeditAttr->start_index);
317             unsigned endIndex = getUTF8CharacterIndex(buffer, preeditAttr->end_index);
318             switch (preeditAttr->preedit_type) {
319             case ECORE_IMF_PREEDIT_TYPE_SUB1:
320                 underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), false));
321                 break;
322             case ECORE_IMF_PREEDIT_TYPE_SUB2:
323             case ECORE_IMF_PREEDIT_TYPE_SUB3:
324                 underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(255, 255, 255), false));
325                 break;
326             case ECORE_IMF_PREEDIT_TYPE_SUB4:
327                 underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(46, 168, 255), false));
328                 break;
329             case ECORE_IMF_PREEDIT_TYPE_SUB5:
330                 underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(153, 98, 195), false));
331                 break;
332             case ECORE_IMF_PREEDIT_TYPE_SUB6:
333                 underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(118, 222, 55), false));
334                 break;
335             case ECORE_IMF_PREEDIT_TYPE_SUB7:
336                 underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(153, 153, 153), false));
337                 break;
338             default:
339                 break;
340             }
341         }
342 #endif
343         EINA_LIST_FREE(preeditAttrs, item)
344             free(item);
345     }
346
347     if (underlines.isEmpty())
348         underlines.append(CompositionUnderline(0, preeditString.length(), Color(0, 0, 0), false));
349
350     page->setComposition(preeditString, underlines, cursorPosition);
351
352     if (buffer)
353         free(buffer);
354
355     inputMethodContext->updateApproximateText(preeditString, 0, 0);
356 }
357 #else
358 void InputMethodContextEfl::onIMFInputSequenceComplete(void* data, Ecore_IMF_Context*, void* eventInfo)
359 {
360     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
361     if (!eventInfo || !inputMethodContext->m_focused)
362         return;
363
364     inputMethodContext->m_viewImpl->page()->confirmComposition(String::fromUTF8(static_cast<char*>(eventInfo)));
365 }
366
367 void InputMethodContextEfl::onIMFPreeditSequenceChanged(void* data, Ecore_IMF_Context* context, void*)
368 {
369     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
370
371     if (!inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused)
372         return;
373
374     char* buffer = 0;
375     ecore_imf_context_preedit_string_get(context, &buffer, 0);
376     if (!buffer)
377         return;
378
379     String preeditString = String::fromUTF8(buffer);
380     free(buffer);
381     Vector<CompositionUnderline> underlines;
382     underlines.append(CompositionUnderline(0, preeditString.length(), Color(0, 0, 0), false));
383     inputMethodContext->m_viewImpl->page()->setComposition(preeditString, underlines, 0);
384 }
385 #endif
386
387 PassOwnPtr<Ecore_IMF_Context> InputMethodContextEfl::createIMFContext(Evas* canvas)
388 {
389     const char* defaultContextID = ecore_imf_context_default_id_get();
390     if (!defaultContextID)
391         return nullptr;
392
393     OwnPtr<Ecore_IMF_Context> imfContext = adoptPtr(ecore_imf_context_add(defaultContextID));
394     if (!imfContext)
395         return nullptr;
396
397     Ecore_Evas* ecoreEvas = ecore_evas_ecore_evas_get(canvas);
398     ecore_imf_context_client_window_set(imfContext.get(), reinterpret_cast<void*>(ecore_evas_window_get(ecoreEvas)));
399     ecore_imf_context_client_canvas_set(imfContext.get(), canvas);
400
401     return imfContext.release();
402 }
403
404 void InputMethodContextEfl::handleMouseUpEvent(const Evas_Event_Mouse_Up*)
405 {
406 #if ENABLE(TIZEN_ISF_PORT)
407     if (!m_context)
408         return;
409
410     resetIMFContext();
411 #else
412     ecore_imf_context_reset(m_context.get());
413 #endif
414 }
415
416 void InputMethodContextEfl::handleKeyDownEvent(const Evas_Event_Key_Down* downEvent, bool* isFiltered)
417 {
418 #if ENABLE(TIZEN_ISF_PORT)
419     if (!m_context)
420         return;
421
422     const EditorState& state = m_viewImpl->page()->editorState();
423
424     if (!m_isLastKeyEventFiltered && !m_approximateSurroundingText.isNull() && !state.hasComposition)
425         ecore_imf_context_cursor_position_set(m_context.get(), m_approximateCursorPosition);
426
427     if (m_fakeKeyEventTimer.isActive())
428         m_fakeKeyEventTimer.stop();
429     else
430         m_viewImpl->page()->prepareKeyDownEvent();
431
432     m_doNotHandleFakeKeyEvent = true;
433 #endif
434
435     Ecore_IMF_Event inputMethodEvent;
436     ecore_imf_evas_event_key_down_wrap(const_cast<Evas_Event_Key_Down*>(downEvent), &inputMethodEvent.key_down);
437
438     *isFiltered = ecore_imf_context_filter_event(m_context.get(), ECORE_IMF_EVENT_KEY_DOWN, &inputMethodEvent);
439
440 #if ENABLE(TIZEN_ISF_PORT)
441     m_doNotHandleFakeKeyEvent = false;
442     m_isLastKeyEventFiltered = *isFiltered;
443
444     if (!*isFiltered) {
445         if (!strcmp(downEvent->key, "BackSpace")) {
446             if (state.cursorPosition > 0)
447                 updateApproximateText(String(), state.cursorPosition - 1, 1);
448         } else if (!strcmp(downEvent->key, "Delete")) {
449             if (state.cursorPosition < state.surroundingText.length())
450                 updateApproximateText(String(), state.cursorPosition, 1);
451         } else
452             updateApproximateText(String::fromUTF8(downEvent->compose), 0, 0);
453     }
454 #endif
455 }
456
457 #if ENABLE(TIZEN_ISF_PORT)
458 void InputMethodContextEfl::updateTextInputState()
459 {
460     const EditorState& editor = m_viewImpl->page()->editorState();
461     if (editor.shouldIgnoreCompositionSelectionChange || editor.updateEditorRectOnly)
462         return;
463
464     if (editor.isContentEditable)
465         showIMFContext(editor);
466     else
467         hideIMFContext();
468
469     m_approximateSurroundingText = String();
470
471     if (m_context && !editor.hasComposition)
472         ecore_imf_context_cursor_position_set(m_context.get(), editor.cursorPosition);
473 }
474
475 void InputMethodContextEfl::updateTextInputStateByUserAction(bool setFocus)
476 {
477     const EditorState& editor = m_viewImpl->page()->editorState();
478
479     if (editor.isContentEditable) {
480         if (setFocus)
481             evas_object_focus_set(m_viewImpl->view(), true);
482
483         showIMFContext(editor, true);
484     } else
485         hideIMFContext();
486 }
487 #else
488 void InputMethodContextEfl::updateTextInputState()
489 {
490     if (!m_context)
491         return;
492
493     const EditorState& editor = m_viewImpl->page()->editorState();
494
495     if (editor.isContentEditable) {
496         if (m_focused)
497             return;
498
499         ecore_imf_context_reset(m_context.get());
500         ecore_imf_context_focus_in(m_context.get());
501         m_focused = true;
502     } else {
503         if (!m_focused)
504             return;
505
506         if (editor.hasComposition)
507             m_viewImpl->page()->cancelComposition();
508
509         m_focused = false;
510         ecore_imf_context_reset(m_context.get());
511         ecore_imf_context_focus_out(m_context.get());
512     }
513 }
514 #endif
515
516 #if ENABLE(TIZEN_ISF_PORT)
517 void InputMethodContextEfl::initializeIMFContext(Ecore_IMF_Context* context, Ecore_IMF_Input_Panel_Layout layout, int layoutVariation, Ecore_IMF_Input_Panel_Return_Key_Type returnKeyType)
518 {
519     ecore_imf_context_input_panel_enabled_set(context, false);
520     ecore_imf_context_input_panel_event_callback_add(context, ECORE_IMF_INPUT_PANEL_STATE_EVENT, onIMFInputPanelStateChanged, this);
521     ecore_imf_context_input_panel_event_callback_add(context, ECORE_IMF_INPUT_PANEL_GEOMETRY_EVENT, onIMFInputPanelGeometryChanged, this);
522     ecore_imf_context_input_panel_event_callback_add(context, ECORE_IMF_CANDIDATE_PANEL_STATE_EVENT, onIMFCandidatePanelStateChanged, this);
523     ecore_imf_context_input_panel_event_callback_add(context, ECORE_IMF_CANDIDATE_PANEL_GEOMETRY_EVENT, onIMFCandidatePanelGeometryChanged, this);
524     ecore_imf_context_retrieve_surrounding_callback_set(context, onIMFRetrieveSurrounding, this);
525     ecore_imf_context_event_callback_add(context, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, onIMFDeleteSurrounding, this);
526     ecore_imf_context_event_callback_add(context, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, onIMFPreeditSequenceChanged, this);
527     ecore_imf_context_event_callback_add(context, ECORE_IMF_CALLBACK_COMMIT, onIMFInputSequenceComplete, this);
528
529     ecore_imf_context_input_panel_layout_set(context, layout);
530     if (layoutVariation >= 0)
531         ecore_imf_context_input_panel_layout_variation_set(context, layoutVariation);
532     ecore_imf_context_input_panel_return_key_type_set(context, returnKeyType);
533 }
534
535 PassOwnPtr<Ecore_IMF_Context> InputMethodContextEfl::takeContext(uintptr_t contextID)
536 {
537     size_t i = m_contextList.size();
538     while (i > 0) {
539         --i;
540         if (m_contextList[i].first == contextID) {
541             PassOwnPtr<Ecore_IMF_Context> context = m_contextList[i].second.release();
542             m_contextList.remove(i);
543             return context;
544         }
545     }
546
547     return PassOwnPtr<Ecore_IMF_Context>();
548 }
549
550 void InputMethodContextEfl::setIMFContext(const EditorState& editor)
551 {
552     const String& type = editor.inputMethodHints;
553     Ecore_IMF_Input_Panel_Layout layout;
554     int layoutVariation = -1;
555     Ecore_IMF_Input_Panel_Return_Key_Type returnKeyType = ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DEFAULT;
556
557     if (type == "number") {
558         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY;
559         layoutVariation = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY_VARIATION_NORMAL;
560     } else if (type == "signedNumber") {
561         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY;
562         layoutVariation = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY_VARIATION_SIGNED;
563     } else if (type == "decimalNumber") {
564         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY;
565         layoutVariation = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY_VARIATION_DECIMAL;
566     } else if (type == "signedDecimalNumber") {
567         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY;
568         layoutVariation = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY_VARIATION_SIGNED_AND_DECIMAL;
569     } else if (type == "email")
570         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_EMAIL;
571     else if (type == "url")
572         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_URL;
573     else if (type == "tel")
574         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_PHONENUMBER;
575     else if (type == "password")
576         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_PASSWORD;
577     else {
578         layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL;
579         if (type == "search")
580             returnKeyType = ECORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_SEARCH;
581     }
582
583     OwnPtr<Ecore_IMF_Context> context;
584     if (m_contextID == editor.inputMethodContextID)
585         context = m_context.release();
586     else
587         context = takeContext(editor.inputMethodContextID);
588
589     revertIMFContext();
590
591     if (!context) {
592         context = createIMFContext(evas_object_evas_get(m_viewImpl->view()));
593         if (!context)
594             return;
595         initializeIMFContext(context.get(), layout, layoutVariation, returnKeyType);
596     }
597
598     m_context = context.release();
599     m_contextID = editor.inputMethodContextID;
600
601     if (type == "password" || type == "plugin")
602         ecore_imf_context_prediction_allow_set(m_context.get(), false);
603     else
604         ecore_imf_context_prediction_allow_set(m_context.get(), true);
605
606     if (type.isEmpty() || type == "textarea")
607         ecore_imf_context_autocapital_type_set(m_context.get(), ECORE_IMF_AUTOCAPITAL_TYPE_SENTENCE);
608     else
609         ecore_imf_context_autocapital_type_set(m_context.get(), ECORE_IMF_AUTOCAPITAL_TYPE_NONE);
610 }
611
612 bool InputMethodContextEfl::isShow()
613 {
614     return (m_context && m_focused && ecore_imf_context_input_panel_state_get(m_context.get()) != ECORE_IMF_INPUT_PANEL_STATE_HIDE);
615 }
616
617 Ecore_IMF_Autocapital_Type InputMethodContextEfl::autoCapitalType()
618 {
619     return (m_context ? ecore_imf_context_autocapital_type_get(m_context.get()) : ECORE_IMF_AUTOCAPITAL_TYPE_NONE);
620 }
621
622 void InputMethodContextEfl::onFocusIn()
623 {
624     if (m_inputPickerType >= 0) {
625         showInputPicker(m_viewImpl->page()->editorState());
626         return;
627     }
628
629     if (!m_context || !m_focused)
630         return;
631
632     ecore_imf_context_focus_in(m_context.get());
633     ecore_imf_context_input_panel_show(m_context.get());
634     if (!isSystemKeypadShow())
635         setState(ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW);
636 }
637
638 void InputMethodContextEfl::onFocusOut()
639 {
640 #if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
641     if (m_state != ECORE_IMF_INPUT_PANEL_STATE_SHOW) {
642         if (m_viewImpl->pageClient->isClipboardWindowOpened())
643             m_viewImpl->pageClient->closeClipboardWindow();
644     }
645 #endif
646
647 #if ENABLE(TIZEN_FOCUS_UI)
648     m_viewImpl->page()->setFocusUIEnabled(false);
649 #endif
650
651     if (!m_context || !m_focused)
652         return;
653
654     ecore_imf_context_input_panel_hide(m_context.get());
655     ecore_imf_context_focus_out(m_context.get());
656 }
657
658 void InputMethodContextEfl::revertIMFContext()
659 {
660     if (!m_context)
661         return;
662
663     if (m_contextList.size() >= maxContextSize)
664         m_contextList.remove(0);
665
666     PassOwnPtr<Ecore_IMF_Context> imfContext = m_context.release();
667     m_contextList.append(std::make_pair(m_contextID, imfContext));
668     m_contextID = 0;
669 }
670
671 void InputMethodContextEfl::resetIMFContext()
672 {
673     if (!m_context)
674         return;
675
676     m_doNotHandleFakeKeyEvent = true;
677     ecore_imf_context_reset(m_context.get());
678     m_doNotHandleFakeKeyEvent = false;
679 }
680
681 void InputMethodContextEfl::showIMFContext(const EditorState& editor, bool isUserAction)
682 {
683     Ewk_Settings* settings = ewk_view_settings_get(m_viewImpl->view());
684     bool isContextTransition = (m_contextID && m_contextID != editor.inputMethodContextID);
685
686     if (!isUserAction && !isContextTransition) {
687         if (!ewk_settings_uses_keypad_without_user_action_get(settings) || (m_focused && m_contextID == editor.inputMethodContextID))
688             return;
689     }
690
691 #if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
692     if (m_viewImpl->pageClient->isClipboardWindowOpened())
693         return;
694 #endif
695
696     if (m_contextID != editor.inputMethodContextID)
697         hideIMFContext();
698
699     m_focused = true;
700
701 #if ENABLE(TIZEN_INPUT_TAG_EXTENSION)
702     if (editor.inputMethodHints == "date")
703         m_inputPickerType = EWK_INPUT_TYPE_DATE;
704     else if (editor.inputMethodHints == "datetime")
705         m_inputPickerType = EWK_INPUT_TYPE_DATETIME;
706     else if (editor.inputMethodHints == "datetime-local")
707         m_inputPickerType = EWK_INPUT_TYPE_DATETIMELOCAL;
708     else if (editor.inputMethodHints == "month")
709         m_inputPickerType = EWK_INPUT_TYPE_MONTH;
710     else if (editor.inputMethodHints == "time")
711         m_inputPickerType = EWK_INPUT_TYPE_TIME;
712     else if (editor.inputMethodHints == "week")
713         m_inputPickerType = EWK_INPUT_TYPE_WEEK;
714     else
715         m_inputPickerType = -1;
716
717     if (m_inputPickerType >= 0) {
718         showInputPicker(editor);
719         m_contextID = editor.inputMethodContextID;
720
721         return;
722     }
723 #endif // ENABLE(TIZEN_INPUT_TAG_EXTENSION)
724
725     bool hasFocus = evas_object_focus_get(m_viewImpl->view());
726
727     if (!ewk_settings_default_keypad_enabled_get(settings)) {
728         if (hasFocus) {
729             Eina_Rectangle dummyRectForCustomKeypadCallback;
730             memset(&dummyRectForCustomKeypadCallback, 0, sizeof(Eina_Rectangle));
731             evas_object_smart_callback_call(m_viewImpl->view(), "inputmethod,changed", &dummyRectForCustomKeypadCallback);
732         }
733         return;
734     }
735
736     setIMFContext(editor);
737     if (!m_context)
738         return;
739
740     if (!hasFocus)
741         return;
742
743      // input field zoom for external keyboard
744     if (s_shouldUseExternalKeyboard)
745         ewk_view_focused_node_adjust(m_viewImpl->view(), EINA_TRUE);
746     else {
747         if (isShow())
748             ewk_view_focused_node_adjust(m_viewImpl->view());
749     }
750
751     resetIMFContext();
752     ecore_imf_context_focus_in(m_context.get());
753     ecore_imf_context_input_panel_show(m_context.get());
754     setState(ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW);
755 }
756
757 void InputMethodContextEfl::hideIMFContext()
758 {
759 #if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
760     if (m_state != ECORE_IMF_INPUT_PANEL_STATE_SHOW) {
761         if (m_viewImpl->pageClient->isClipboardWindowOpened())
762             m_viewImpl->pageClient->closeClipboardWindow();
763     }
764 #endif
765
766     m_inputPickerType = -1;
767
768     if (!m_context || !m_focused)
769         return;
770
771     if (evas_object_focus_get(m_viewImpl->view())) {
772         resetIMFContext();
773         ecore_imf_context_input_panel_hide(m_context.get());
774         ecore_imf_context_focus_out(m_context.get());
775     }
776
777     m_focused = false;
778     revertIMFContext();
779 }
780
781 void InputMethodContextEfl::destroyIMFContextList()
782 {
783     m_contextList.clear();
784 }
785
786 #if ENABLE(TIZEN_INPUT_TAG_EXTENSION)
787 void InputMethodContextEfl::showInputPicker(const EditorState& editorState)
788 {
789     if (editorState.selectionIsRange || !evas_object_focus_get(m_viewImpl->view()))
790         return;
791
792     ewkViewInputPickerRequest(m_viewImpl->view(), static_cast<Ewk_Input_Type>(m_inputPickerType), editorState.surroundingText);
793     m_inputPickerType = -1;
794 }
795 #endif
796
797 bool InputMethodContextEfl::isIMEPostion(int x, int y)
798 {
799     if (m_state == ECORE_IMF_INPUT_PANEL_STATE_SHOW)
800         return m_imeRect.contains(x, y);
801
802     return false;
803 }
804
805 void InputMethodContextEfl::removeIMFContext(uintptr_t contextID)
806 {
807     if (m_contextID == contextID)
808         hideIMFContext();
809
810     takeContext(contextID);
811 }
812
813 void InputMethodContextEfl::requestFakeKeyEvent()
814 {
815     if (m_doNotHandleFakeKeyEvent || m_fakeKeyEventTimer.isActive())
816         return;
817
818     m_fakeKeyEventTimer.startOneShot(0);
819     m_viewImpl->page()->prepareKeyDownEvent();
820 }
821
822 void InputMethodContextEfl::fakeKeyEventTimerFired(WebCore::Timer<InputMethodContextEfl>*)
823 {
824     UChar ch = 0;
825     if (!m_approximateSurroundingText.isNull()) {
826         if (!m_approximateCursorPosition)
827             ch = m_approximateSurroundingText[0];
828         else if (m_approximateCursorPosition > m_approximateSurroundingText.length())
829             ch = m_approximateSurroundingText[m_approximateSurroundingText.length() - 1];
830         else
831             ch = m_approximateSurroundingText[m_approximateCursorPosition - 1];
832     }
833
834     String string;
835     if (Unicode::isSeparatorSpace(ch))
836         string = "space";
837     else if (Unicode::isPrintableChar(ch))
838         string.append(ch);
839     else
840         string = "Shift_L";
841
842     CString stringForEvent = string.utf8();
843     const char* data = stringForEvent.data();
844
845     Evas_Event_Key_Down downEvent;
846     memset(&downEvent, 0, sizeof(Evas_Event_Key_Down));
847     downEvent.key = data;
848     downEvent.string = data;
849     NativeWebKeyboardEvent nativeEvent(&downEvent, true);
850     nativeEvent.setInputMethodContextID(m_viewImpl->page()->editorState().inputMethodContextID);
851     m_viewImpl->page()->handleKeyboardEvent(nativeEvent);
852
853     Evas_Event_Key_Up upEvent;
854     memset(&upEvent, 0, sizeof(Evas_Event_Key_Up));
855     upEvent.key = data;
856     upEvent.string = data;
857     m_viewImpl->page()->handleKeyboardEvent(NativeWebKeyboardEvent(&upEvent));
858 }
859
860 void InputMethodContextEfl::updateApproximateText(const String& text, unsigned removePosition, unsigned removeLength)
861 {
862     if (m_approximateSurroundingText.isNull()) {
863         const EditorState& state = m_viewImpl->page()->editorState();
864
865         m_approximateSurroundingText = state.surroundingText;
866         if (m_approximateSurroundingText.isNull())
867             m_approximateSurroundingText = emptyString();
868         m_approximateCursorPosition = state.cursorPosition;
869     }
870
871     if (!text.isNull()) {
872         m_approximateSurroundingText.insert(text, m_approximateCursorPosition);
873         m_approximateCursorPosition += text.length();
874     } else {
875         m_approximateSurroundingText.remove(removePosition, removeLength);
876         if (m_approximateCursorPosition > removePosition && m_approximateCursorPosition <= removePosition + removeLength)
877             m_approximateCursorPosition = removePosition;
878     }
879 }
880
881 bool InputMethodContextEfl::recalcFilterEvent(const Ecore_IMF_Event* event)
882 {
883     if (!m_context)
884         return false;
885
886     m_doNotHandleFakeKeyEvent = true;
887     bool isFiltered = ecore_imf_context_filter_event(m_context.get(), ECORE_IMF_EVENT_KEY_DOWN, const_cast<Ecore_IMF_Event*>(event));
888     m_doNotHandleFakeKeyEvent = false;
889
890     return isFiltered;
891 }
892 #endif // #if ENABLE(TIZEN_ISF_PORT)
893
894 }