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