/*
 * Copyright (c) 1995, 2003, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#ifdef HEADLESS
    #error This file should not be included in headless library
#endif

#include <Xm/VirtKeys.h>

#include "awt_p.h"
#include "java_awt_TextField.h"
#include "java_awt_Color.h"
#include "java_awt_AWTEvent.h"
#include "java_awt_Font.h"
#include "java_awt_Canvas.h"
#include "sun_awt_motif_MComponentPeer.h"
#include "sun_awt_motif_MCanvasPeer.h"
#include "sun_awt_motif_MTextFieldPeer.h"

#include "awt_Component.h"
#include "awt_TextField.h"

#include "multi_font.h"
#include <jni.h>
#include <jni_util.h>
#include <Xm/DropSMgr.h>
#include <Xm/TextFP.h>  /* Motif TextField private header. */


#define ECHO_BUFFER_LEN 1024

extern struct MComponentPeerIDs mComponentPeerIDs;
extern AwtGraphicsConfigDataPtr
    copyGraphicsConfigToPeer(JNIEnv *env, jobject this);
struct TextFieldIDs textFieldIDs;
struct MTextFieldPeerIDs mTextFieldPeerIDs;

/*
 * Class:     java_awt_TextField
 * Method:    initIDs
 * Signature: ()V
 */

/* This function gets called from the static initializer for TextField.java
   to initialize the fieldIDs for fields that may be accessed from C */

JNIEXPORT void JNICALL
Java_java_awt_TextField_initIDs
  (JNIEnv *env, jclass cls)
{
    textFieldIDs.echoChar =
      (*env)->GetFieldID(env, cls, "echoChar", "C");
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    initIDs
 * Signature: ()V
 */

/* This function gets called from the static initializer for
   MTextFieldPeer.java to initialize the fieldIDs for fields that may
   be accessed from C */

JNIEXPORT void JNICALL
Java_sun_awt_motif_MTextFieldPeer_initIDs
  (JNIEnv *env, jclass cls)
{
    mTextFieldPeerIDs.firstChangeSkipped =
      (*env)->GetFieldID(env, cls, "firstChangeSkipped", "Z");
}

static void
echoChar(Widget text_w, XtPointer unused, XmTextVerifyCallbackStruct * cbs)
{
    size_t len;
    int32_t c;
    char *val;
    struct DPos *dp;
    int32_t ret;
    jobject globalRef;
    int32_t i, numbytes;

    struct TextFieldData *tdata;

    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    /*
     * Get the echoContextID from the globalRef which is stored in
     * the XmNuserData resource for the widget.
     */
    XtVaGetValues(text_w,XmNuserData,&globalRef,NULL);

    tdata = (struct TextFieldData *)
      (*env)->GetLongField(env,globalRef,mComponentPeerIDs.pData);

    ret = XFindContext(XtDisplay(text_w), (XID)text_w, tdata->echoContextID,
                       (XPointer *)&dp);
    if ((ret != 0) || (dp == NULL)) {
        /* no context found or DPos is NULL - shouldn't happen */
        return;
    }

    c = dp->echoC;
    val = (char *) (dp->data);

    len = strlen(val);
    if (cbs->text->ptr == NULL) {
        if (cbs->text->length == 0 && cbs->startPos == 0) {
            val[0] = '\0';
            return;
        } else if (cbs->startPos == (len - 1)) {
            /* handle deletion */
            cbs->endPos = strlen(val);
            val[cbs->startPos] = '\0';
            return;
        } else {
            /* disable deletes anywhere but at the end */
            cbs->doit = False;
            return;
        }
    }
    if (cbs->startPos != len) {
        /* disable "paste" or inserts into the middle */
        cbs->doit = False;
        return;
    }
    /* append the value typed in */
    if ((cbs->endPos + cbs->text->length) > ECHO_BUFFER_LEN) {
        val = realloc(val, cbs->endPos + cbs->text->length + 10);
    }
    strncat(val, cbs->text->ptr, cbs->text->length);
    val[cbs->endPos + cbs->text->length] = '\0';

    /* modify the output to be the echo character */
    for (len = 0, i = 0; len < cbs->text->length; i++) {
        /* Write one echo character for each multibyte character. */
        numbytes = mblen(cbs->text->ptr + len, cbs->text->length - len);
        cbs->text->ptr[i] = (char) c;
        len += numbytes;
    }
    cbs->text->length = i;
}

/*
 * Event handler used by both TextField/TextArea to correctly process
 * cut/copy/paste keys such that interaction with our own
 * clipboard mechanism will work properly.
 *
 * client_data is MTextFieldPeer instance
 */
void
Text_handlePaste(Widget w, XtPointer client_data, XEvent * event, Boolean * cont)
{
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    KeySym keysym;
    Modifiers mods;

    /* Any event handlers which take peer instance pointers as
     * client_data should check to ensure the widget has not been
     * marked as destroyed as a result of a dispose() call on the peer
     * (which can result in the peer instance pointer already haven
     * been gc'd by the time this event is processed)
     */
    if (event->type != KeyPress || w->core.being_destroyed) {
        return;
    }

    XtTranslateKeycode(event->xkey.display, (KeyCode) event->xkey.keycode,
                       event->xkey.state, &mods, &keysym);

    /* Should be a temporary fix for 4052132 if a cleaner fix is found later */
    if ((event->xkey.state & ControlMask) && (keysym == 'v' || keysym == 'V'))
        keysym = osfXK_Paste;
    if ((event->xkey.state & ShiftMask) && (keysym == osfXK_Insert))
        keysym = osfXK_Paste;

    switch (keysym) {
        case osfXK_Paste:
            /* If we own the selection, then paste the data directly */
            if (awtJNI_isSelectionOwner(env, "CLIPBOARD")) {
                JNU_CallMethodByName(env, NULL, (jobject) client_data,
                                     "pasteFromClipboard", "()V");
                if ((*env)->ExceptionOccurred(env)) {
                    (*env)->ExceptionDescribe(env);
                    (*env)->ExceptionClear(env);
                }
                *cont = FALSE;
            }
            break;

        case osfXK_Cut:
        case osfXK_Copy:
            /* For some reason if we own the selection, our loseSelection
             * callback is not automatically called on cut/paste from
             * text widgets.
             */
            if (awtJNI_isSelectionOwner(env, "CLIPBOARD")) {
                awtJNI_notifySelectionLost(env, "CLIPBOARD");
            }
            break;
        default:
            break;
    }
}

/*
 * client_data is MTextFieldPeer instance
 */
void
TextField_valueChanged(Widget w, XtPointer client_data, XtPointer call_data)
{
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    jboolean skipped;

    skipped = (*env)->GetBooleanField(env, (jobject) client_data,
                                      mTextFieldPeerIDs.firstChangeSkipped);
    if (!(*env)->ExceptionOccurred(env)) {
        if (skipped == JNI_FALSE) {
            (*env)->SetBooleanField(env, (jobject) client_data,
                                    mTextFieldPeerIDs.firstChangeSkipped,
                                    JNI_TRUE);
        } else {
            JNU_CallMethodByName(env, NULL, (jobject) client_data,
                                 "valueChanged", "()V");
        }
    }

    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
    }
}

/*
 * client_data is MTextFieldPeer instance
 */
static void
TextField_action(Widget w, XtPointer client_data, XmAnyCallbackStruct * s)
{
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    ConvertEventTimeAndModifiers converted;

    awt_util_convertEventTimeAndModifiers(s->event, &converted);

    JNU_CallMethodByName(env, NULL, (jobject) client_data, "action", "(JI)V",
                         converted.when, converted.modifiers);
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
    }
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    pCreate
 * Signature: (Lsun/awt/motif/MComponentPeer;)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_pCreate
  (JNIEnv *env, jobject this, jobject parent)
{
    struct ComponentData *wdata;
    struct TextFieldData *tdata;

    jobject globalRef = awtJNI_CreateAndSetGlobalRef(env, this);
    AwtGraphicsConfigDataPtr adata;

    AWT_LOCK();

    adata = copyGraphicsConfigToPeer(env, this);

    if (JNU_IsNull(env, parent)) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    wdata = (struct ComponentData *)
        JNU_GetLongFieldAsPtr(env,parent,mComponentPeerIDs.pData);
    if (wdata == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    tdata = ZALLOC(TextFieldData);
    if (tdata == NULL) {
        JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
        AWT_UNLOCK();
        return;
    }
    JNU_SetLongFieldFromPtr(env,this,mComponentPeerIDs.pData,tdata);

    tdata->comp.widget = XtVaCreateManagedWidget("textfield",
                                                 xmTextFieldWidgetClass,
                                                 wdata->widget,
                                                 XmNrecomputeSize, False,
                                                 XmNhighlightThickness, 1,
                                                 XmNshadowThickness, 2,
                                                 XmNuserData, (XtPointer) globalRef,
                                                 XmNscreen,
                                                 ScreenOfDisplay(awt_display,
                                                   adata->awt_visInfo.screen),
                                                 XmNfontList, getMotifFontList(),
                                                 NULL);
    tdata->echoContextIDInit = FALSE;

    XtSetMappedWhenManaged(tdata->comp.widget, False);
    XtAddCallback(tdata->comp.widget,
                  XmNactivateCallback,
                  (XtCallbackProc) TextField_action,
                  (XtPointer) globalRef);
    XtAddCallback(tdata->comp.widget,
                  XmNvalueChangedCallback,
                  (XtCallbackProc) TextField_valueChanged,
                  (XtPointer) globalRef);
    XtInsertEventHandler(tdata->comp.widget,
                         KeyPressMask,
                         False, Text_handlePaste, (XtPointer) globalRef,
                         XtListHead);
    /*
     * Fix for BugTraq ID 4349615.
     * Unregister Motif drop site to prevent it from crash
     * when dropping java objects.
     */
    XmDropSiteUnregister(tdata->comp.widget);

    AWT_UNLOCK();
}

/*
 * Class     sun_awt_motif_MTextFieldPeer
 * Method:    pSetEditable
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_pSetEditable
  (JNIEnv *env, jobject this, jboolean editable)
{
    struct TextFieldData *tdata;

    AWT_LOCK();
    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    XtVaSetValues(tdata->comp.widget,
                  XmNeditable, (editable ? True : False),
                  XmNcursorPositionVisible, (editable ? True : False),
                  NULL);
    AWT_FLUSH_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    select
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_select
  (JNIEnv *env, jobject this, jint start, jint end)
{
    struct TextFieldData *tdata;

    AWT_LOCK();

    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    XmTextSetSelection(tdata->comp.widget, (XmTextPosition) start, (XmTextPosition) end, 0);
    AWT_FLUSH_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    getSelectionStart
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_sun_awt_motif_MTextFieldPeer_getSelectionStart
  (JNIEnv *env, jobject this)
{
    struct TextFieldData *tdata;
    XmTextPosition start, end, pos;

    AWT_LOCK();

    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return 0;
    }
    if (XmTextGetSelectionPosition(tdata->comp.widget, &start, &end) &&
                                                (start != end)) {
        pos = start;
    } else {
        pos = XmTextGetInsertionPosition(tdata->comp.widget);
    }
    AWT_UNLOCK();

    return (jint) pos;
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    getSelectionEnd
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_sun_awt_motif_MTextFieldPeer_getSelectionEnd
  (JNIEnv *env, jobject this)
{
    struct TextFieldData *tdata;
    XmTextPosition start, end, pos;

    AWT_LOCK();

    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return 0;
    }
    if (XmTextGetSelectionPosition(tdata->comp.widget, &start, &end) &&
                                                 (start != end)) {
        pos = end;
    } else {
        pos = XmTextGetInsertionPosition(tdata->comp.widget);
    }
    AWT_UNLOCK();

    return (jint) pos;
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    setText
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_setText
  (JNIEnv *env, jobject this, jstring l)
{
    struct TextFieldData *tdata;
    char *cl;
    jobject target;

    AWT_LOCK();

    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    if (JNU_IsNull(env, l)) {
        cl = "";
    } else {
        /*
         * Note: Motif TextField widgets do not support multi-font
         * compound strings.
         */
        cl = (char *) JNU_GetStringPlatformChars(env, l, NULL);
    }

    /* Fix for bug 4084454 : setText appears in clear */
    target = (*env)->GetObjectField(env, this, mComponentPeerIDs.target);
    if ((*env)->GetCharField(env, target, textFieldIDs.echoChar) != 0) {
        XtVaSetValues(tdata->comp.widget,
                      XmNvalue, "", NULL);
        XmTextFieldInsert(tdata->comp.widget,0,cl);
        XmTextSetInsertionPosition(tdata->comp.widget,
                                   (XmTextPosition) strlen(cl));
    }
    else {
        XtVaSetValues(tdata->comp.widget,
                      XmNvalue, cl,
                      NULL);
    }
    /*
     * Fix for BugTraq Id 4185654 - TextField.setText(<String>) incorrect justification
     * Comment out the next line.
     */
    /* XmTextSetInsertionPosition(tdata->comp.widget,
     *                            (XmTextPosition) strlen(cl));
     */

    if (cl != NULL && cl != "") {
        JNU_ReleaseStringPlatformChars(env, l, cl);
    }
    AWT_FLUSH_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    insertReplaceText
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_insertReplaceText
  (JNIEnv *env, jobject this, jstring l)
{
    struct TextFieldData *tdata;
    char *cl;
    XmTextPosition start, end;

    AWT_LOCK();

    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);
    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }

    if (JNU_IsNull(env, l)) {
        cl = "";
    } else {
        /*
         * Note: Motif TextField widgets do not support multi-font
         * compound strings.
         */
        cl = (char *) JNU_GetStringPlatformChars(env, l, NULL);
    }

    if (!XmTextGetSelectionPosition(tdata->comp.widget, &start, &end)) {
        start = end = XmTextGetInsertionPosition(tdata->comp.widget);
    }
    XmTextReplace(tdata->comp.widget, start, end, cl);

    if (cl != NULL && cl != "") {
        JNU_ReleaseStringPlatformChars(env, l, cl);
    }
    AWT_FLUSH_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    preDispose
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_preDispose
  (JNIEnv *env, jobject this)
{
    struct TextFieldData *tdata;
    struct DPos *dp;
    jobject target;
    int32_t ret;

    AWT_LOCK();

    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    target = (*env)->GetObjectField(env, this, mComponentPeerIDs.target);

    if ((*env)->GetCharField(env, target, textFieldIDs.echoChar) != 0) {
        ret = XFindContext(XtDisplay(tdata->comp.widget), (XID)(tdata->comp.widget),
                           tdata->echoContextID, (XPointer *)&dp);
        if ((ret == 0) && dp != NULL) {

            /* Remove the X context associated with this textfield's
             * echo character. BugId #4225734
             */
            XDeleteContext(XtDisplay(tdata->comp.widget),
                           (XID)(tdata->comp.widget),
                           tdata->echoContextID);

            tdata->echoContextIDInit = FALSE;

            /* Free up the space allocated for the echo character data. */
            if (dp->data) {
                free(dp->data);
            }
            free(dp);
        }
    }

    AWT_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    getText
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_sun_awt_motif_MTextFieldPeer_getText
  (JNIEnv *env, jobject this)
{
    struct TextFieldData *tdata;
    char *val;
    struct DPos *dp;
    jobject target;
    int32_t ret;
    jstring returnVal;

    AWT_LOCK();
    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return NULL;
    }

    target = (*env)->GetObjectField(env, this, mComponentPeerIDs.target);

    if ((*env)->GetCharField(env, target, textFieldIDs.echoChar) != 0) {
        ret = XFindContext(XtDisplay(tdata->comp.widget), (XID)tdata->comp.widget,
                           tdata->echoContextID, (XPointer *)&dp);
        if ((ret == 0) && (dp != NULL)) {
            val = (char *)(dp->data);
        } else {
            val = "";
        }
    } else {
        XtVaGetValues(tdata->comp.widget, XmNvalue, &val, NULL);
    }
    AWT_UNLOCK();

    returnVal = JNU_NewStringPlatform(env, (const char *) val);
    if ((*env)->GetCharField(env, target, textFieldIDs.echoChar) == 0) {
        free(val);
    }
    return returnVal;
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    setEchoChar
 * Signature: (C)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_setEchoChar
  (JNIEnv *env, jobject this, jchar c)
{
    char *val;
    char *cval;
    struct TextFieldData *tdata;
    struct DPos *dp;
    int32_t i;
    size_t len;
    int32_t ret;

    AWT_LOCK();
    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);
    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }

    XtVaGetValues(tdata->comp.widget,
                  XmNvalue, &cval,
                  NULL);

    DASSERT(c != 0 || tdata->echoContextIDInit);

    if (!tdata->echoContextIDInit) {
        tdata->echoContextID = XUniqueContext();
        tdata->echoContextIDInit = TRUE;
    }
    ret = XFindContext(XtDisplay(tdata->comp.widget), (XID)(tdata->comp.widget),
                       tdata->echoContextID, (XPointer *)&dp);
    /*
     * Fix for BugTraq ID 4307281.
     * Special case for setting echo char to 0:
     *  - remove the callback and X context associated with echo character;
     *  - restore the original text.
     */
    if (c == 0) {
        XtRemoveCallback(tdata->comp.widget, XmNmodifyVerifyCallback,
                         (XtCallbackProc) echoChar, NULL);
        if (ret == 0 && dp != NULL) {

            /* Remove the X context associated with echo character. */
            XDeleteContext(XtDisplay(tdata->comp.widget),
                           (XID)(tdata->comp.widget),
                           tdata->echoContextID);

            tdata->echoContextIDInit = FALSE;

            /* Restore the original text. */
            if (dp->data != NULL) {
                val = (char *)(dp->data);
            } else {
                val = "";
            }
            XtVaSetValues(tdata->comp.widget,
                          XmNvalue, val,
                          NULL);

            /* Free up the space allocated for the echo character data. */
            if (dp->data) {
                free(dp->data);
            }
            free(dp);
        }
        AWT_UNLOCK();
        return;
    }
    if (ret != 0) {
        dp = NULL;
    }

    if (dp != NULL) {
        /* Fix bug 4124697: cannot change setEchoChar twice on Motif */
        XtRemoveCallback(tdata->comp.widget, XmNmodifyVerifyCallback,
                        (XtCallbackProc) echoChar, NULL);
    } else {
        if ((int32_t) strlen(cval) > ECHO_BUFFER_LEN) {
            val = (char *) malloc(strlen(cval) + 1);
        } else {
            val = (char *) malloc(ECHO_BUFFER_LEN + 1);
        }
        if (val == NULL) {
            JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
            AWT_UNLOCK();
            return;
        }
        if (cval != NULL) {
            strcpy(val, cval);
        } else {
            *val = '\0';
        }
        dp = (struct DPos *) malloc(sizeof(struct DPos));

        dp->x = -1;
        dp->data = (void *) val;
    }

    dp->echoC = c;
    len = strlen(cval);
    for (i = 0; i < len; i++) {
        cval[i] = (char) (c);
    }
    XtVaSetValues(tdata->comp.widget,
                  XmNvalue, cval,
                  NULL);

    ret = XSaveContext(XtDisplay(tdata->comp.widget), (XID)tdata->comp.widget,
                       tdata->echoContextID, (XPointer)dp);
    if (ret == 0) {
        XtAddCallback(tdata->comp.widget, XmNmodifyVerifyCallback,
                      (XtCallbackProc) echoChar, NULL);
    }
    AWT_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    setFont
 * Signature: (Ljava/awt/Font;)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_setFont
  (JNIEnv *env, jobject this, jobject f)
{
    struct TextFieldData *tdata;
    struct FontData *fdata;
    XmFontListEntry fontentry;
    XmFontList fontlist;
    char *err;

    AWT_LOCK();
    if (JNU_IsNull(env, f)) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    fdata = awtJNI_GetFontData(env, f, &err);
    if (fdata == NULL) {
        JNU_ThrowInternalError(env, err);
        AWT_UNLOCK();
        return;
    }
    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);
    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    if (awtJNI_IsMultiFont(env, f)) {
        if (fdata->xfs == NULL) {
            fdata->xfs = awtJNI_MakeFontSet(env, f);
        }
        if (fdata->xfs != NULL) {
            fontentry = XmFontListEntryCreate("labelFont",
                                              XmFONT_IS_FONTSET,
                                              (XtPointer) (fdata->xfs));
            fontlist = XmFontListAppendEntry(NULL, fontentry);
            /*
             * Some versions of motif have a bug in
             * XmFontListEntryFree() which causes it to free more than it
             * should.  Use XtFree() instead.  See O'Reilly's
             * Motif Reference Manual for more information.
             */
            XmFontListEntryFree(&fontentry);
        } else {
            fontlist = XmFontListCreate(fdata->xfont, "labelFont");
        }
    } else {
        fontlist = XmFontListCreate(fdata->xfont, "labelFont");
    }

    if (fontlist != NULL) {
        XtVaSetValues(tdata->comp.widget, XmNfontList, fontlist, NULL);
        XmFontListFree(fontlist);
    } else {
        JNU_ThrowNullPointerException(env, "NullPointerException");
    }
    AWT_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    setCaretPosition
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_sun_awt_motif_MTextFieldPeer_setCaretPosition
  (JNIEnv *env, jobject this, jint pos)
{
    struct TextFieldData *tdata;

    AWT_LOCK();

    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    XmTextSetInsertionPosition(tdata->comp.widget, (XmTextPosition) pos);

    AWT_FLUSH_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MTextFieldPeer
 * Method:    getCaretPosition
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_sun_awt_motif_MTextFieldPeer_getCaretPosition
  (JNIEnv *env, jobject this)
{
    struct TextFieldData *tdata;
    XmTextPosition pos;

    AWT_LOCK();

    tdata = (struct TextFieldData *)
        JNU_GetLongFieldAsPtr(env,this,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return 0;
    }
    pos = XmTextGetInsertionPosition(tdata->comp.widget);
    AWT_UNLOCK();

    return (jint) pos;
}


/*  To be fully implemented in a future release
 *
 * Class:     sun_awt_windows_MTextFieldPeer
 * Method:    getIndexAtPoint
 * Signature: (Ljava/awt/Point;)I
 *
JNIEXPORT jint JNICALL
Java_sun_awt_motif_MTextFieldPeer_getIndexAtPoint(JNIEnv *env, jobject self,
 jint x, jint y)
{
    struct ComponentData *tdata;
    XmTextPosition pos;

    AWT_LOCK();

    tdata = (struct ComponentData *)
        JNU_GetLongFieldAsPtr(env,self,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->comp.widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return -1;
    }
    pos = XmTextFieldXYToPos(tdata->widget, x, y);
    AWT_UNLOCK();

    return (jint) pos;
}
*/

/*  To be fully implemented in a future release
 *
 * Class:     sun_awt_windows_MTextFieldPeer
 * Method:    getCharacterBounds
 * Signature: (I)Ljava/awt/Rectangle;
 *
JNIEXPORT jobject JNICALL
Java_sun_awt_motif_MTextFieldPeer_getCharacterBounds(JNIEnv *env, jobject self, jint i)
{
#define TextF_FontAscent(tfg)                   (((XmTextFieldWidget)(tfg)) -> \
                                           text.font_ascent)
#define TextF_FontDescent(tfg)                  (((XmTextFieldWidget)(tfg)) -> \
                                           text.font_descent)

    struct ComponentData *tdata;
    jobject rect=NULL;
    Position x=0, y=0;
    Position next_x=0, next_y=0;
    int32_t w=0, h=0;

    AWT_LOCK();

    tdata = (struct ComponentData *)
        JNU_GetLongFieldAsPtr(env,self,mComponentPeerIDs.pData);

    if (tdata == NULL || tdata->widget == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return (jobject) NULL;
    }

    XmTextFieldPosToXY(tdata->widget, i, &x, &y);
    y -= TextF_FontAscent(tdata->widget);
    XmTextFieldPosToXY(tdata->widget, i+1, &next_x, &next_y);
    w = next_x - x;
    h = TextF_FontAscent(tdata->widget) + TextF_FontDescent(tdata->widget);

    AWT_UNLOCK();

    if (w>0) {
        jclass clazz;
        jmethodID mid;

        clazz = (*env)->FindClass(env, "java/awt/Rectangle");
        mid = (*env)->GetMethodID(env, clazz, "<init>", "(IIII)V");
        if (mid != NULL) {
            rect = (*env)->NewObject(env, clazz, mid, x, y, w, h);
            if ((*env)->ExceptionOccurred(env)) {
                return NULL;
            }
        }
    }
    return rect;
}
*/
