1/*
2 * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include "awt.h"
27#include <imm.h>
28#include "awt_Component.h"
29#include "awt_InputTextInfor.h"
30
31#define WCHAR_SZ sizeof(WCHAR)
32#define DWORD_SZ sizeof(DWORD)
33
34// The start and end index of the result and composition in GCS_INDEX array.
35#define START_RESULTSTR 0
36#define END_RESULTSTR 3
37#define START_COMPSTR 4
38#define END_COMPSTR 8
39
40// The GCS_INDEX array is partitioned into 2 parts, one is result string related and the
41// other is composing string related.
42const DWORD AwtInputTextInfor::GCS_INDEX[9]= {GCS_RESULTSTR, GCS_RESULTREADSTR, GCS_RESULTCLAUSE,
43                                              GCS_RESULTREADCLAUSE, GCS_COMPSTR, GCS_COMPREADSTR,
44                                              GCS_COMPCLAUSE, GCS_COMPREADCLAUSE,GCS_COMPATTR};
45/* Default constructor */
46AwtInputTextInfor::AwtInputTextInfor() :
47    m_flags(0), m_cursorPosW(0), m_jtext(NULL), m_pResultTextInfor(NULL), \
48    m_cStrW(0), m_cReadStrW(0), m_cClauseW(0), m_cReadClauseW(0), m_cAttrW(0), \
49    m_lpStrW(NULL), m_lpReadStrW(NULL), m_lpClauseW(NULL), m_lpReadClauseW(NULL), m_lpAttrW(NULL)
50{}
51
52
53/* Retrieve the context data from the current IMC.
54   Params:
55   HIMC hIMC - the input method context, must NOT be NULL
56   LPARAMS flags - message param to WM_IME_COMPOSITION.
57   Returns 0 if success.
58*/
59int
60AwtInputTextInfor::GetContextData(HIMC hIMC, const LPARAM flags) {
61
62    DASSERT(hIMC != 0);
63
64    m_flags = flags;
65    // Based on different flags received, we use different GCS_XXX from the
66    // GCS_INDEX array.
67    int startIndex = 0, endIndex = 0;
68
69    if (flags & GCS_COMPSTR) {
70        startIndex = START_COMPSTR;
71        endIndex = END_COMPSTR;
72        /* For some window input method such as Chinese QuanPing, when the user
73         * commits some text, the IMM sends WM_IME_COMPOSITION with GCS_COMPSTR/GCS_RESULTSTR.
74         * So we have to extract the result string from IMC. For most of other cases,
75         * m_pResultTextInfor is NULL and this is why we choose to have a pointer as its member
76         * rather than having a list of the result string information.
77         */
78        if (flags & GCS_RESULTSTR) {
79            m_pResultTextInfor = new AwtInputTextInfor;
80            m_pResultTextInfor->GetContextData(hIMC, GCS_RESULTSTR);
81        }
82    } else if (flags & GCS_RESULTSTR) {
83        startIndex = START_RESULTSTR;
84        endIndex = END_RESULTSTR;
85    } else { // unknown flags.
86        return -1;
87    }
88
89    /* Get the data from the input context */
90    LONG   cbData[5] = {0};
91    LPVOID lpData[5] = {NULL};
92    for (int i = startIndex, j = 0; i <= endIndex; i++, j++) {
93        cbData[j] = ::ImmGetCompositionString(hIMC, GCS_INDEX[i], NULL, 0);
94        if (cbData[j] == 0) {
95            lpData[j] = NULL;
96        } else {
97            LPBYTE lpTemp = new BYTE[cbData[j]];
98            cbData[j] = ::ImmGetCompositionString(hIMC, GCS_INDEX[i], lpTemp, cbData[j]);
99            if (IMM_ERROR_GENERAL != cbData[j]) {
100                lpData[j] = (LPVOID)lpTemp;
101            } else {
102                lpData[j] = NULL;
103                return -1;
104            }
105        }
106    }
107
108    // Assign the context data
109    m_cStrW = cbData[0]/WCHAR_SZ;
110    m_lpStrW = (LPWSTR)lpData[0];
111
112    m_cReadStrW = cbData[1]/WCHAR_SZ;
113    m_lpReadStrW = (LPWSTR)lpData[1];
114
115    m_cClauseW = cbData[2]/DWORD_SZ - 1;
116    m_lpClauseW = (LPDWORD)lpData[2];
117
118    m_cReadClauseW = cbData[3]/DWORD_SZ - 1;
119    m_lpReadClauseW = (LPDWORD)lpData[3];
120
121    if (cbData[4] > 0) {
122        m_cAttrW = cbData[4];
123        m_lpAttrW = (LPBYTE)lpData[4];
124    }
125
126    // Get the cursor position
127    if (flags & GCS_COMPSTR) {
128        m_cursorPosW = ::ImmGetCompositionString(hIMC, GCS_CURSORPOS,
129                                                NULL, 0);
130    }
131
132    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
133    if (m_cStrW > 0) {
134        m_jtext = MakeJavaString(env, m_lpStrW, m_cStrW);
135        JNU_CHECK_EXCEPTION_RETURN(env, -1);
136    }
137
138    // Merge the string if necessary
139    if (m_pResultTextInfor != NULL) {
140        jstring jresultText = m_pResultTextInfor->GetText();
141        if (m_jtext != NULL && jresultText != NULL) {
142            jstring jMergedtext = (jstring)JNU_CallMethodByName(env, NULL, jresultText,
143                                                                "concat",
144                                                                "(Ljava/lang/String;)Ljava/lang/String;",
145                                                                m_jtext).l;
146            DASSERT(!safe_ExceptionOccurred(env));
147            DASSERT(jMergedtext != NULL);
148
149            env->DeleteLocalRef(m_jtext);
150            m_jtext = jMergedtext;
151        }
152        else if (m_jtext == NULL && jresultText != NULL) {
153            /* No composing text, assign the committed text to m_jtext */
154            m_jtext = (jstring)env->NewLocalRef(jresultText);
155        }
156    }
157
158    return 0;
159}
160
161/*
162 * Destructor
163 * free the pointer in the m_lpInfoStrW array
164 */
165AwtInputTextInfor::~AwtInputTextInfor() {
166
167    if (m_jtext) {
168        JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
169        env->DeleteLocalRef(m_jtext);
170        m_jtext = NULL;
171    }
172
173    delete [] m_lpStrW;
174    delete [] m_lpReadStrW;
175    delete [] m_lpClauseW;
176    delete [] m_lpReadClauseW;
177    delete [] m_lpAttrW;
178
179    if (m_pResultTextInfor) {
180        delete m_pResultTextInfor;
181        m_pResultTextInfor = NULL;
182    }
183}
184
185
186jstring AwtInputTextInfor::MakeJavaString(JNIEnv* env, LPWSTR lpStrW, int cStrW) {
187
188    if (env == NULL || lpStrW == NULL || cStrW == 0) {
189        return NULL;
190    } else {
191        return env->NewString(reinterpret_cast<jchar*>(lpStrW), cStrW);
192    }
193}
194
195//
196//  Convert Clause and Reading Information for DBCS string to that for Unicode string
197//  *lpBndClauseW and *lpReadingClauseW  must be deleted by caller.
198//
199int AwtInputTextInfor::GetClauseInfor(int*& lpBndClauseW, jstring*& lpReadingClauseW) {
200
201    if ( m_cStrW ==0 || m_cClauseW ==0 || m_cClauseW != m_cReadClauseW ||
202         m_lpClauseW == NULL || m_lpReadClauseW == NULL ||
203         m_lpClauseW[0] != 0 || m_lpClauseW[m_cClauseW] != (DWORD)m_cStrW ||
204         m_lpReadClauseW[0] != 0 || m_lpReadClauseW[m_cReadClauseW] != (DWORD)m_cReadStrW) {
205        lpBndClauseW = NULL;
206        lpReadingClauseW = NULL;
207        return 0;
208    }
209
210    int*    bndClauseW = NULL;
211    jstring* readingClauseW = NULL;
212
213    //Convert ANSI string caluse information to UNICODE string clause information.
214    try {
215        bndClauseW = new int[m_cClauseW + 1];
216        readingClauseW = new jstring[m_cClauseW];
217    } catch (std::bad_alloc&) {
218        lpBndClauseW = NULL;
219        lpReadingClauseW = NULL;
220        delete [] bndClauseW;
221        throw;
222    }
223
224    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
225
226    for ( int cls = 0; cls < m_cClauseW; cls++ ) {
227        bndClauseW[cls] = m_lpClauseW[cls];
228
229        if ( m_lpReadClauseW[cls + 1] <= (DWORD)m_cReadStrW ) {
230            LPWSTR lpHWStrW = m_lpReadStrW + m_lpReadClauseW[cls];
231            int cHWStrW = m_lpReadClauseW[cls+1] - m_lpReadClauseW[cls];
232
233            if (PRIMARYLANGID(AwtComponent::GetInputLanguage()) == LANG_JAPANESE) {
234                LCID lcJPN = MAKELCID(MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT),SORT_DEFAULT);
235                // Reading string is given in half width katakana in Japanese Windows
236                //  Convert it to full width katakana.
237                int cFWStrW = ::LCMapString(lcJPN, LCMAP_FULLWIDTH, lpHWStrW, cHWStrW, NULL, 0);
238                LPWSTR lpFWStrW;
239                try {
240                    lpFWStrW = new WCHAR[cFWStrW];
241                } catch (std::bad_alloc&) {
242                    lpBndClauseW = NULL;
243                    lpReadingClauseW = NULL;
244                    delete [] bndClauseW;
245                    delete [] readingClauseW;
246                    throw;
247                }
248
249                ::LCMapString(lcJPN, LCMAP_FULLWIDTH, lpHWStrW, cHWStrW, lpFWStrW, cFWStrW);
250                readingClauseW[cls] = MakeJavaString(env, lpFWStrW, cFWStrW);
251                delete [] lpFWStrW;
252            } else {
253                readingClauseW[cls] = MakeJavaString(env, lpHWStrW, cHWStrW);
254            }
255            if (env->ExceptionCheck()) {
256                lpBndClauseW = NULL;
257                lpReadingClauseW = NULL;
258                delete [] bndClauseW;
259                delete [] readingClauseW;
260                return 0;
261            }
262        }
263        else {
264            readingClauseW[cls] = NULL;
265        }
266    }
267
268    bndClauseW[m_cClauseW] = m_cStrW;
269
270    int retVal = 0;
271    int cCommittedStrW = GetCommittedTextLength();
272
273    /* The conditions to merge the clause information are described below:
274       Senario 1:
275       m_flags & GCS_RESULTSTR is true only, this case m_pResultTextInfor must be NULL.
276       No need to merge.
277
278       Senario 2:
279       m_flags & GCS_COMPSTR is true only, this case m_pResultTextInfor is also NULL.
280       No need to merge either.
281
282       Senario 3:
283       m_flags & GCS_COMPSTR and m_flags & GCS_RESULTSTR both yield to true, in this case
284       m_pResultTextInfor won't be NULL and if there is nothing to commit though, we don't
285       have to merge. Or if the current composing string size is 0, we don't have to merge either.
286
287       So in clusion, the three conditions not not merge are:
288       1. no committed string
289       2. m_pResultTextInfor points to NULL
290       3. the current string size is 0;
291
292       Same rule applies to merge the attribute information.
293    */
294    if (m_cStrW == 0 || cCommittedStrW == 0 ||
295        m_pResultTextInfor == NULL) {
296        lpBndClauseW = bndClauseW;
297        lpReadingClauseW = readingClauseW;
298        retVal = m_cClauseW;
299    } else { /* partial commit case */
300        int* bndResultClauseW = NULL;
301        jstring* readingResultClauseW = NULL;
302        int cResultClauseW = m_pResultTextInfor->GetClauseInfor(bndResultClauseW, readingResultClauseW);
303
304        // Concatenate Clause information.
305        int cMergedClauseW = m_cClauseW + cResultClauseW;
306        int* bndMergedClauseW = NULL;
307        jstring* readingMergedClauseW = NULL;
308        try {
309            bndMergedClauseW = new int[cMergedClauseW+1];
310            readingMergedClauseW = new jstring[cMergedClauseW];
311        } catch (std::bad_alloc&) {
312            delete [] bndMergedClauseW;
313            delete [] bndClauseW;
314            delete [] readingClauseW;
315            throw;
316        }
317
318        int i = 0;
319        if (cResultClauseW > 0 && bndResultClauseW && readingResultClauseW) {
320            for (; i < cResultClauseW; i++) {
321                bndMergedClauseW[i] = bndResultClauseW[i];
322                readingMergedClauseW[i] = readingResultClauseW[i];
323            }
324        }
325
326        if (m_cClauseW > 0 && bndClauseW && readingClauseW) {
327            for(int j = 0; j < m_cClauseW; j++, i++) {
328                bndMergedClauseW[i] = bndClauseW[j] + cCommittedStrW;
329                readingMergedClauseW[i] = readingClauseW[j];
330            }
331        }
332        delete [] bndClauseW;
333        delete [] readingClauseW;
334        bndMergedClauseW[cMergedClauseW] = m_cStrW + cCommittedStrW;
335        lpBndClauseW = bndMergedClauseW;
336        lpReadingClauseW = readingMergedClauseW;
337        retVal = cMergedClauseW;
338    }
339
340    return retVal;
341}
342
343//
344//  Convert Attribute Information for DBCS string to that for Unicode string
345//  *lpBndAttrW and *lpValAttrW  must be deleted by caller.
346//
347int AwtInputTextInfor::GetAttributeInfor(int*& lpBndAttrW, BYTE*& lpValAttrW) {
348    if (m_cStrW == 0 || m_cAttrW != m_cStrW) {
349        lpBndAttrW = NULL;
350        lpValAttrW = NULL;
351
352        return 0;
353    }
354
355    int* bndAttrW = NULL;
356    BYTE* valAttrW = NULL;
357
358    //Scan attribute byte array and make attribute run information.
359    try {
360        bndAttrW = new int[m_cAttrW + 1];
361        valAttrW = new BYTE[m_cAttrW];
362    } catch (std::bad_alloc&) {
363        lpBndAttrW = NULL;
364        lpValAttrW = NULL;
365        delete [] bndAttrW;
366        throw;
367    }
368
369    int cAttrWT = 0;
370    bndAttrW[0] = 0;
371    valAttrW[0] = m_lpAttrW[0];
372    /* remove duplicate attribute in the m_lpAttrW array. */
373    for ( int offW = 1; offW < m_cAttrW; offW++ ) {
374        if ( m_lpAttrW[offW] != valAttrW[cAttrWT]) {
375            cAttrWT++;
376            bndAttrW[cAttrWT] = offW;
377            valAttrW[cAttrWT] = m_lpAttrW[offW];
378        }
379    }
380    bndAttrW[++cAttrWT] =  m_cStrW;
381
382    int retVal = 0;
383
384    int cCommittedStrW = GetCommittedTextLength();
385    if (m_cStrW == 0 ||
386        cCommittedStrW == 0 || m_pResultTextInfor == NULL) {
387        lpBndAttrW = bndAttrW;
388        lpValAttrW = valAttrW;
389        retVal = cAttrWT;
390    } else {
391        int cMergedAttrW = 1 + cAttrWT;
392        int*    bndMergedAttrW = NULL;
393        BYTE*   valMergedAttrW = NULL;
394        try {
395            bndMergedAttrW = new int[cMergedAttrW+1];
396            valMergedAttrW = new BYTE[cMergedAttrW];
397        } catch (std::bad_alloc&) {
398            delete [] bndMergedAttrW;
399            delete [] bndAttrW;
400            delete [] valAttrW;
401            throw;
402        }
403        bndMergedAttrW[0] = 0;
404        valMergedAttrW[0] = ATTR_CONVERTED;
405        for (int j = 0; j < cAttrWT; j++) {
406            bndMergedAttrW[j+1] = bndAttrW[j]+cCommittedStrW;
407            valMergedAttrW[j+1] = valAttrW[j];
408        }
409        bndMergedAttrW[cMergedAttrW] = m_cStrW + cCommittedStrW;
410
411        delete [] bndAttrW;
412        delete [] valAttrW;
413        lpBndAttrW = bndMergedAttrW;
414        lpValAttrW = valMergedAttrW;
415        retVal = cMergedAttrW;
416    }
417
418    return retVal;
419}
420
421//
422// Returns the cursor position of the current composition.
423// returns 0 if the current mode is not GCS_COMPSTR
424//
425int AwtInputTextInfor::GetCursorPosition() const {
426    if (m_flags & GCS_COMPSTR) {
427        return m_cursorPosW;
428    } else {
429        return 0;
430    }
431}
432
433
434//
435// Returns the committed text length
436//
437int AwtInputTextInfor::GetCommittedTextLength() const {
438
439    if ((m_flags & GCS_COMPSTR) && m_pResultTextInfor) {
440        return m_pResultTextInfor->GetCommittedTextLength();
441    }
442
443    if (m_flags & GCS_RESULTSTR)
444        return m_cStrW;
445    else
446        return 0;
447}
448