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