IME show properly after disappear the clipboard.
[framework/web/webkit-efl.git] / Source / WebKit2 / UIProcess / efl / InputMethodContextEfl.cpp
index cbb8209..d810116 100755 (executable)
@@ -34,6 +34,11 @@ InputMethodContextEfl::InputMethodContextEfl(EwkViewImpl* viewImpl, PassOwnPtr<E
     : m_viewImpl(viewImpl)
     , m_context(context)
     , m_focused(false)
+#if ENABLE(TIZEN_ISF_PORT)
+    , m_useInputMethod(false)
+    , m_state(ECORE_IMF_INPUT_PANEL_STATE_HIDE)
+    , m_inputPickerType(-1)
+#endif
 {
     ASSERT(context);
 #if ENABLE(TIZEN_ISF_PORT)
@@ -52,31 +57,33 @@ InputMethodContextEfl::~InputMethodContextEfl()
 void InputMethodContextEfl::onIMFInputPanelStateChanged(void* data, Ecore_IMF_Context*, int state)
 {
     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
-    if (!inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused)
-        return;
 
-    if (state == ECORE_IMF_INPUT_PANEL_STATE_HIDE)
+    inputMethodContext->setState(state);
+
+    if (state == ECORE_IMF_INPUT_PANEL_STATE_HIDE) {
+#if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
+        if (inputMethodContext->m_viewImpl->pageClient->isClipboardWindowOpened())
+            inputMethodContext->m_viewImpl->pageClient->closeClipboardWindow();
+#endif
         evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "editorclient,ime,closed", 0);
-    else if (state == ECORE_IMF_INPUT_PANEL_STATE_SHOW)
+    else if (state == ECORE_IMF_INPUT_PANEL_STATE_SHOW)
         evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "editorclient,ime,opened", 0);
 }
 
 void InputMethodContextEfl::onIMFInputPanelGeometryChanged(void* data, Ecore_IMF_Context*, int value)
 {
     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
-    if (!inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused)
-        return;
 
     Eina_Rectangle rect;
     ecore_imf_context_input_panel_geometry_get(inputMethodContext->m_context.get(), &rect.x, &rect.y, &rect.w, &rect.h);
     evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "inputmethod,changed", &rect);
+
+    inputMethodContext->setIMERect(IntRect(rect.x, rect.y, rect.w, rect.h));
 }
 
 void InputMethodContextEfl::onIMFCandidatePanelStateChanged(void* data, Ecore_IMF_Context*, int state)
 {
     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
-    if (!inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused)
-        return;
 
     if (state == ECORE_IMF_CANDIDATE_PANEL_SHOW)
         evas_object_smart_callback_call(inputMethodContext->m_viewImpl->view(), "editorclient,candidate,opened", 0);
@@ -87,8 +94,6 @@ void InputMethodContextEfl::onIMFCandidatePanelStateChanged(void* data, Ecore_IM
 void InputMethodContextEfl::onIMFCandidatePanelGeometryChanged(void* data, Ecore_IMF_Context*, int)
 {
     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
-    if (!inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused)
-        return;
 
     Eina_Rectangle rect;
     ecore_imf_context_candidate_panel_geometry_get(inputMethodContext->m_context.get(), &rect.x, &rect.y, &rect.w, &rect.h);
@@ -140,16 +145,121 @@ void InputMethodContextEfl::onIMFInputSequenceComplete(void* data, Ecore_IMF_Con
     if (!eventInfo || !inputMethodContext->m_focused)
         return;
 
-    inputMethodContext->m_viewImpl->pageClient->imContextCommitted(inputMethodContext->m_context.get(), static_cast<char*>(eventInfo));
+    inputMethodContext->m_viewImpl->page()->confirmComposition(String::fromUTF8(static_cast<char*>(eventInfo)));
+}
+
+#if ENABLE(TIZEN_WEBKIT2_SUPPORT_JAPANESE_IME)
+unsigned getUTF8CharacterIndex(const char* string, unsigned byteIndex)
+{
+    unsigned index = 0;
+    const char* end = string + byteIndex;
+
+    while (*string && string < end) {
+        unsigned offset;
+
+        if ((*string & 0x80) == 0x00)
+            offset = 1;
+        else if ((*string & 0xe0) == 0xc0)
+            offset = 2;
+        else if ((*string & 0xf0) == 0xe0)
+            offset = 3;
+        else if ((*string & 0xf8) == 0xf0)
+            offset = 4;
+        else if ((*string & 0xfc) == 0xf8)
+            offset = 5;
+        else if ((*string & 0xfe) == 0xfc)
+            offset = 6;
+        else
+            offset = 1;
+
+        ++index;
+        while (*string && offset--)
+            ++string;
+    }
+
+    return index;
 }
+#endif
 
-void InputMethodContextEfl::onIMFPreeditSequenceChanged(void* data, Ecore_IMF_Context*, void*)
+void InputMethodContextEfl::onIMFPreeditSequenceChanged(void* data, Ecore_IMF_Context* context, void*)
 {
     InputMethodContextEfl* inputMethodContext = static_cast<InputMethodContextEfl*>(data);
+
     if (!inputMethodContext->m_viewImpl->page()->focusedFrame() || !inputMethodContext->m_focused)
         return;
 
-    inputMethodContext->m_viewImpl->pageClient->imContextPreeditChanged(inputMethodContext->m_context.get());
+    WebPageProxy* page = inputMethodContext->m_viewImpl->page();
+    if (!page->focusedFrame())
+        return;
+
+    PageClientImpl* pageClient = inputMethodContext->m_viewImpl->pageClient.get();
+    IntRect caretRect;
+    page->getCaretPosition(caretRect);
+    caretRect.scale(pageClient->scaleFactor());
+
+    int viewX, viewY;
+    evas_object_geometry_get(inputMethodContext->m_viewImpl->view(), &viewX, &viewY, 0, 0);
+
+    int x = caretRect.x() - pageClient->scrollPosition().x() + viewX;
+    int y = caretRect.y() - pageClient->scrollPosition().y() + viewY;
+    int w = caretRect.width();
+    int h = caretRect.height();
+    ecore_imf_context_cursor_location_set(context, x, y, w, h);
+
+    char* buffer = 0;
+    Eina_List* preeditAttrs = 0;
+    int cursorPosition = 0;
+
+    ecore_imf_context_preedit_string_with_attributes_get(context, &buffer, &preeditAttrs, &cursorPosition);
+
+    String preeditString = String::fromUTF8(buffer);
+    Vector<CompositionUnderline> underlines;
+
+    if (preeditAttrs) {
+        void* item = 0;
+#if ENABLE(TIZEN_WEBKIT2_SUPPORT_JAPANESE_IME)
+        Eina_List* listIterator = 0;
+        EINA_LIST_FOREACH(preeditAttrs, listIterator, item) {
+            Ecore_IMF_Preedit_Attr* preeditAttr = static_cast<Ecore_IMF_Preedit_Attr*>(item);
+
+            unsigned startIndex = getUTF8CharacterIndex(buffer, preeditAttr->start_index);
+            unsigned endIndex = getUTF8CharacterIndex(buffer, preeditAttr->end_index);
+            switch (preeditAttr->preedit_type) {
+            case ECORE_IMF_PREEDIT_TYPE_SUB1:
+                underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), false));
+                break;
+            case ECORE_IMF_PREEDIT_TYPE_SUB2:
+            case ECORE_IMF_PREEDIT_TYPE_SUB3:
+                underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(255, 255, 255), false));
+                break;
+            case ECORE_IMF_PREEDIT_TYPE_SUB4:
+                underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(46, 168, 255), false));
+                break;
+            case ECORE_IMF_PREEDIT_TYPE_SUB5:
+                underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(153, 98, 195), false));
+                break;
+            case ECORE_IMF_PREEDIT_TYPE_SUB6:
+                underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(118, 222, 55), false));
+                break;
+            case ECORE_IMF_PREEDIT_TYPE_SUB7:
+                underlines.append(CompositionUnderline(startIndex, endIndex, Color(0, 0, 0), Color(153, 153, 153), false));
+                break;
+            default:
+                break;
+            }
+        }
+#endif
+        EINA_LIST_FREE(preeditAttrs, item)
+            free(item);
+    }
+
+    if (underlines.isEmpty())
+        underlines.append(CompositionUnderline(0, preeditString.length(), Color(0, 0, 0), false));
+
+    page->setComposition(preeditString, underlines, cursorPosition);
+
+    if (buffer)
+        free(buffer);
 }
 #else
 void InputMethodContextEfl::onIMFInputSequenceComplete(void* data, Ecore_IMF_Context*, void* eventInfo)
@@ -211,6 +321,26 @@ void InputMethodContextEfl::handleKeyDownEvent(const Evas_Event_Key_Down* downEv
     *isFiltered = ecore_imf_context_filter_event(m_context.get(), ECORE_IMF_EVENT_KEY_DOWN, &inputMethodEvent);
 }
 
+#if ENABLE(TIZEN_ISF_PORT)
+void InputMethodContextEfl::updateTextInputState()
+{
+    const EditorState& editor = m_viewImpl->page()->editorState();
+    if (editor.shouldIgnoreCompositionSelectionChange)
+        return;
+
+    if (editor.isContentEditable && m_useInputMethod) {
+#if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
+        if (m_viewImpl->pageClient->isClipboardWindowOpened())
+            m_viewImpl->pageClient->closeClipboardWindow();
+#endif
+        showIMFContext(editor);
+    } else
+        hideIMFContext();
+
+    if (m_context)
+        ecore_imf_context_cursor_position_set(m_context.get(), editor.cursorPosition);
+}
+#else
 void InputMethodContextEfl::updateTextInputState()
 {
     if (!m_context)
@@ -237,6 +367,7 @@ void InputMethodContextEfl::updateTextInputState()
         ecore_imf_context_focus_out(m_context.get());
     }
 }
+#endif
 
 #if ENABLE(TIZEN_ISF_PORT)
 void InputMethodContextEfl::initializeIMFContext(Ecore_IMF_Context* context, Ecore_IMF_Input_Panel_Layout layout)
@@ -253,30 +384,37 @@ void InputMethodContextEfl::initializeIMFContext(Ecore_IMF_Context* context, Eco
     ecore_imf_context_input_panel_layout_set(m_context.get(), layout);
 }
 
-void InputMethodContextEfl::setType(const String& type)
+void InputMethodContextEfl::setUseInputMethod(bool use)
+{
+    m_useInputMethod = use;
+    updateTextInputState();
+}
+
+Ecore_IMF_Input_Panel_Layout InputMethodContextEfl::layoutType(const String& type)
 {
-    Ecore_IMF_Input_Panel_Layout layout;
     if (type == "number")
-        layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBER;
+        return ECORE_IMF_INPUT_PANEL_LAYOUT_NUMBER;
     else if (type == "email")
-        layout = ECORE_IMF_INPUT_PANEL_LAYOUT_EMAIL;
+        return ECORE_IMF_INPUT_PANEL_LAYOUT_EMAIL;
     else if (type == "url")
-        layout = ECORE_IMF_INPUT_PANEL_LAYOUT_URL;
+        return ECORE_IMF_INPUT_PANEL_LAYOUT_URL;
     else if (type == "tel")
-        layout = ECORE_IMF_INPUT_PANEL_LAYOUT_PHONENUMBER;
+        return ECORE_IMF_INPUT_PANEL_LAYOUT_PHONENUMBER;
     else if (type == "password")
-        layout = ECORE_IMF_INPUT_PANEL_LAYOUT_PASSWORD;
+        return ECORE_IMF_INPUT_PANEL_LAYOUT_PASSWORD;
     else
-        layout = ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL;
+        return ECORE_IMF_INPUT_PANEL_LAYOUT_NORMAL;
+}
 
+void InputMethodContextEfl::setIMFContext(Ecore_IMF_Input_Panel_Layout layout, const String& type)
+{
     if (m_contextList.contains(layout)) {
         revertIMFContext();
         m_context = m_contextList.take(layout);
     } else if (!m_context || ecore_imf_context_input_panel_layout_get(m_context.get()) != layout) {
         OwnPtr<Ecore_IMF_Context> context = createIMFContext(evas_object_evas_get(m_viewImpl->view()));
-        if (!m_context)
-            return;
-        revertIMFContext();
+        if (m_context)
+            revertIMFContext();
         m_context = context.release();
         initializeIMFContext(m_context.get(), layout);
     }
@@ -292,33 +430,6 @@ void InputMethodContextEfl::setType(const String& type)
         ecore_imf_context_autocapital_type_set(m_context.get(), ECORE_IMF_AUTOCAPITAL_TYPE_NONE);
 }
 
-void InputMethodContextEfl::showIMFContext()
-{
-    if (!m_context)
-        return;
-
-    ecore_imf_context_focus_in(m_context.get());
-    ecore_imf_context_input_panel_show(m_context.get());
-    ecore_imf_context_cursor_position_set(m_context.get(), m_viewImpl->page()->getCursorOffset());
-}
-
-void InputMethodContextEfl::setIMFContextCursorPosition(int position)
-{
-    if (!m_context)
-        return;
-
-    ecore_imf_context_cursor_position_set(m_context.get(), position);
-}
-
-void InputMethodContextEfl::setIMData(const String& data)
-{
-    if (!m_context)
-        return;
-
-    CString utf8Data = data.utf8();
-    ecore_imf_context_input_panel_imdata_set(m_context.get(), utf8Data.data(), utf8Data.length());
-}
-
 bool InputMethodContextEfl::isShow()
 {
     return (m_context && m_focused && ecore_imf_context_input_panel_state_get(m_context.get()) != ECORE_IMF_INPUT_PANEL_STATE_HIDE);
@@ -331,6 +442,11 @@ Ecore_IMF_Autocapital_Type InputMethodContextEfl::autoCapitalType()
 
 void InputMethodContextEfl::onFocusIn()
 {
+    if (m_inputPickerType >= 0) {
+        showInputPicker(m_viewImpl->page()->editorState());
+        return;
+    }
+
     if (!m_context || !m_focused)
         return;
 
@@ -340,6 +456,13 @@ void InputMethodContextEfl::onFocusIn()
 
 void InputMethodContextEfl::onFocusOut()
 {
+#if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
+    if (m_state != ECORE_IMF_INPUT_PANEL_STATE_SHOW) {
+        if (m_viewImpl->pageClient->isClipboardWindowOpened())
+            m_viewImpl->pageClient->closeClipboardWindow();
+    }
+#endif
+
     if (!m_context || !m_focused)
         return;
 
@@ -353,7 +476,8 @@ void InputMethodContextEfl::revertIMFContext()
         return;
 
     PassOwnPtr<Ecore_IMF_Context> imfContext = m_context.release();
-    m_contextList.add(ecore_imf_context_input_panel_layout_get(imfContext.get()), imfContext);
+    int layout = ecore_imf_context_input_panel_layout_get(imfContext.get());
+    m_contextList.add(layout, imfContext);
 }
 
 void InputMethodContextEfl::resetIMFContext()
@@ -362,17 +486,111 @@ void InputMethodContextEfl::resetIMFContext()
         return;
 
     ecore_imf_context_reset(m_context.get());
+}
 
-    if (ecore_imf_context_input_panel_state_get(m_context.get()) == ECORE_IMF_INPUT_PANEL_STATE_HIDE)
-        revertIMFContext();
+void InputMethodContextEfl::showIMFContext(const EditorState& editor)
+{
+    Ecore_IMF_Input_Panel_Layout layout = layoutType(editor.inputMethodHints);
+
+    if (m_context && m_state != ECORE_IMF_INPUT_PANEL_STATE_HIDE && layout == ecore_imf_context_input_panel_layout_get(m_context.get()))
+        return;
+
+    Ewk_Settings* settings = ewk_view_settings_get(m_viewImpl->view());
+    bool defaultKeypadEnabled = ewk_settings_default_keypad_enabled_get(settings);
+
+#if ENABLE(TIZEN_INPUT_TAG_EXTENSION)
+    if (editor.inputMethodHints == "date")
+        m_inputPickerType = EWK_INPUT_TYPE_DATE;
+    else if (editor.inputMethodHints == "datetime")
+        m_inputPickerType = EWK_INPUT_TYPE_DATETIME;
+    else if (editor.inputMethodHints == "datetime-local")
+        m_inputPickerType = EWK_INPUT_TYPE_DATETIMELOCAL;
+    else if (editor.inputMethodHints == "month")
+        m_inputPickerType = EWK_INPUT_TYPE_MONTH;
+    else if (editor.inputMethodHints == "time")
+        m_inputPickerType = EWK_INPUT_TYPE_TIME;
+    else if (editor.inputMethodHints == "week")
+        m_inputPickerType = EWK_INPUT_TYPE_WEEK;
+    else
+        m_inputPickerType = -1;
+
+    if (m_inputPickerType >= 0) {
+        showInputPicker(editor);
+        return;
+    }
+
+#if ENABLE(TIZEN_DATALIST_ELEMENT)
+    Vector<String> optionList = m_viewImpl->page()->getFocusedInputElementDataList();
+    if (optionList.size() > 0) {
+        if (editor.selectionIsRange || !evas_object_focus_get(m_viewImpl->view()))
+            return;
+
+        if (editor.inputMethodHints == "tel")
+            ewkViewDataListShowRequest(m_viewImpl->view(), EWK_INPUT_TYPE_TELEPHONE, optionList);
+        else if (editor.inputMethodHints == "number")
+            ewkViewDataListShowRequest(m_viewImpl->view(), EWK_INPUT_TYPE_NUMBER, optionList);
+        else if (editor.inputMethodHints == "email")
+            ewkViewDataListShowRequest(m_viewImpl->view(), EWK_INPUT_TYPE_EMAIL, optionList);
+        else if (editor.inputMethodHints == "url")
+            ewkViewDataListShowRequest(m_viewImpl->view(), EWK_INPUT_TYPE_URL, optionList);
+        else
+            ewkViewDataListShowRequest(m_viewImpl->view(), EWK_INPUT_TYPE_TEXT, optionList);
+
+        return;
+    }
+#endif
+#endif // ENABLE(TIZEN_INPUT_TAG_EXTENSION)
+
+    bool hasFocus = evas_object_focus_get(m_viewImpl->view());
+
+    if (!defaultKeypadEnabled) {
+        if (hasFocus) {
+            Eina_Rectangle dummyRectForCustomKeypadCallback;
+            memset(&dummyRectForCustomKeypadCallback, 0, sizeof(Eina_Rectangle));
+            evas_object_smart_callback_call(m_viewImpl->view(), "inputmethod,changed", &dummyRectForCustomKeypadCallback);
+        }
+        return;
+    }
+
+    setIMFContext(layout, editor.inputMethodHints);
+
+    if (!hasFocus) {
+        m_focused = true;
+        return;
+    }
+
+    ecore_imf_context_reset(m_context.get());
+    ecore_imf_context_focus_in(m_context.get());
+    ecore_imf_context_input_panel_show(m_context.get());
+
+    // input field zoom for external keyboard
+    ewk_view_focused_node_adjust(m_viewImpl->view(), EINA_TRUE);
+
+    m_focused = true;
+    m_state = ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW;
 }
 
 void InputMethodContextEfl::hideIMFContext()
 {
-    if (!m_context)
+#if ENABLE(TIZEN_WEBKIT2_CONTEXT_MENU_CLIPBOARD)
+    if (m_state != ECORE_IMF_INPUT_PANEL_STATE_SHOW) {
+        if (m_viewImpl->pageClient->isClipboardWindowOpened())
+            m_viewImpl->pageClient->closeClipboardWindow();
+    }
+#endif
+
+    m_inputPickerType = -1;
+
+    if (!m_context || !m_focused)
         return;
 
-    if (ecore_imf_context_input_panel_state_get(m_context.get()) != ECORE_IMF_INPUT_PANEL_STATE_HIDE) {
+    if (m_viewImpl->page()->editorState().hasComposition)
+        m_viewImpl->page()->cancelComposition();
+
+    m_focused = false;
+
+    if (ecore_imf_context_input_panel_state_get(m_context.get()) != ECORE_IMF_INPUT_PANEL_STATE_HIDE
+        && evas_object_focus_get(m_viewImpl->view())) {
         ecore_imf_context_reset(m_context.get());
         ecore_imf_context_input_panel_hide(m_context.get());
         ecore_imf_context_focus_out(m_context.get());
@@ -385,6 +603,26 @@ void InputMethodContextEfl::destroyIMFContextList()
 {
     m_contextList.clear();
 }
+
+#if ENABLE(TIZEN_INPUT_TAG_EXTENSION)
+void InputMethodContextEfl::showInputPicker(const EditorState& editorState)
+{
+    if (editorState.selectionIsRange || !evas_object_focus_get(m_viewImpl->view()))
+        return;
+
+    ewkViewInputPickerRequest(m_viewImpl->view(), static_cast<Ewk_Input_Type>(m_inputPickerType), editorState.surroundingText);
+    m_inputPickerType = -1;
+}
+#endif
+
+bool InputMethodContextEfl::isIMEPostion(int x, int y)
+{
+    if (m_state == ECORE_IMF_INPUT_PANEL_STATE_SHOW)
+        return m_imeRect.contains(x, y);
+
+    return false;
+}
+
 #endif
 
 }