/*
 * Copyright (c) 1995, 2004, 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 "awt_p.h"
#include "java_awt_Scrollbar.h"
#include "java_awt_event_MouseEvent.h"
#include "sun_awt_motif_MScrollbarPeer.h"
#include "sun_awt_motif_MComponentPeer.h"

#include "awt_Component.h"
#include "canvas.h"

#include <jni.h>
#include <jni_util.h>
#include "multi_font.h"


extern struct MComponentPeerIDs mComponentPeerIDs;
extern AwtGraphicsConfigDataPtr
    copyGraphicsConfigToPeer(JNIEnv *env, jobject this);

/* fieldIDs for java.awt.Scrollbar fields that may be accessed from C */
static struct ScrollbarIDs {
    jfieldID orientation;
    jfieldID visibleAmount;
    jfieldID lineIncrement;
    jfieldID pageIncrement;
    jfieldID value;
    jfieldID minimum;
    jfieldID maximum;
} targetIDs;

/* MScrollbarPeer callback methods */
static struct {
    jmethodID lineUp;
    jmethodID lineDown;
    jmethodID pageUp;
    jmethodID pageDown;
    jmethodID drag;
    jmethodID dragEnd;
    jmethodID warp;
} peerIDs;



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

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

JNIEXPORT void JNICALL
Java_java_awt_Scrollbar_initIDs(JNIEnv *env, jclass cls)
{
    targetIDs.orientation =
      (*env)->GetFieldID(env, cls, "orientation", "I");
    targetIDs.visibleAmount =
      (*env)->GetFieldID(env, cls, "visibleAmount", "I");
    targetIDs.lineIncrement =
      (*env)->GetFieldID(env, cls, "lineIncrement", "I");
    targetIDs.pageIncrement =
      (*env)->GetFieldID(env, cls, "pageIncrement", "I");
    targetIDs.value =
      (*env)->GetFieldID(env, cls, "value", "I");
    targetIDs.minimum =
      (*env)->GetFieldID(env, cls, "minimum", "I");
    targetIDs.maximum =
      (*env)->GetFieldID(env, cls, "maximum", "I");
}


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

/* This function gets called from the static initializer for
   MScrollbarPeer to initialize the JNI ids for fields and methods
   that may be accessed from C */

JNIEXPORT void JNICALL
Java_sun_awt_motif_MScrollbarPeer_initIDs(JNIEnv *env, jclass cls)
{
    peerIDs.lineUp =
        (*env)->GetMethodID(env, cls, "lineUp",   "(I)V");
    peerIDs.lineDown =
        (*env)->GetMethodID(env, cls, "lineDown", "(I)V");
    peerIDs.pageUp =
        (*env)->GetMethodID(env, cls, "pageUp",   "(I)V");
    peerIDs.pageDown =
        (*env)->GetMethodID(env, cls, "pageDown", "(I)V");
    peerIDs.drag =
        (*env)->GetMethodID(env, cls, "drag",     "(I)V");
    peerIDs.dragEnd =
        (*env)->GetMethodID(env, cls, "dragEnd",  "(I)V");
    peerIDs.warp =
        (*env)->GetMethodID(env, cls, "warp",     "(I)V");
}

/*
 * Call peer.jcallback(value)
 */
static void
DoJavaCallback(jobject peer, jmethodID jcallback, jint value)
{
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    (*env)->CallVoidMethod(env, peer, jcallback, value);
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
    }
}


static void /* XtCallbackProc */
decrementCallback(Widget w, jobject peer,
    XmScrollBarCallbackStruct *scroll)
{
    DASSERT(scroll->reason == XmCR_DECREMENT);
    DoJavaCallback(peer, peerIDs.lineUp, scroll->value);
}

static void /* XtCallbackProc */
incrementCallback(Widget w, jobject peer,
    XmScrollBarCallbackStruct *scroll)
{
    DASSERT(scroll->reason == XmCR_INCREMENT);
    DoJavaCallback(peer, peerIDs.lineDown, scroll->value);
}

static void /* XtCallbackProc */
pageDecrementCallback(Widget w, jobject peer,
    XmScrollBarCallbackStruct *scroll)
{
    DASSERT(scroll->reason == XmCR_PAGE_DECREMENT);
    DoJavaCallback(peer, peerIDs.pageUp, scroll->value);
}

static void /* XtCallbackProc */
pageIncrementCallback(Widget w, jobject peer,
    XmScrollBarCallbackStruct *scroll)
{
    DASSERT(scroll->reason == XmCR_PAGE_INCREMENT);
    DoJavaCallback(peer, peerIDs.pageDown, scroll->value);
}

static void /* XtCallbackProc */
dragCallback(Widget w, jobject peer,
    XmScrollBarCallbackStruct *scroll)
{
    DASSERT(scroll->reason == XmCR_DRAG);
    DoJavaCallback(peer, peerIDs.drag, scroll->value);
}

static void /* XtCallbackProc */
dragEndCallback(Widget w, jobject peer,
    XmScrollBarCallbackStruct *scroll)
{
    DASSERT(scroll->reason == XmCR_VALUE_CHANGED);
    DoJavaCallback(peer, peerIDs.dragEnd, scroll->value);
}

static void /* XtCallbackProc */
toTopCallback(Widget w, jobject peer,
    XmScrollBarCallbackStruct *scroll)
{
    DASSERT(scroll->reason == XmCR_TO_TOP);
    DoJavaCallback(peer, peerIDs.warp, scroll->value);
}

static void /* XtCallbackProc */
toBottomCallback(Widget w, jobject peer,
    XmScrollBarCallbackStruct *scroll)
{
    DASSERT(scroll->reason == XmCR_TO_BOTTOM);
    DoJavaCallback(peer, peerIDs.warp, scroll->value);
}


/*
 * Class:     sun_awt_motif_MScrollbarPeer
 * Method:    create
 * Signature: (Lsun/awt/motif/MComponentPeer;)V
 */
JNIEXPORT void JNICALL
Java_sun_awt_motif_MScrollbarPeer_create(JNIEnv *env, jobject this,
    jobject parent)
{
    Widget w;

    jobject target;
    XtPointer globalRef = (XtPointer) /* jobject */
        awtJNI_CreateAndSetGlobalRef(env, this);

    struct ComponentData *pdata; /* for parent     */
    struct ComponentData *sdata; /* for scrollbar */
    AwtGraphicsConfigDataPtr adata;

    int32_t value, visible, minimum, maximum;
    int32_t lineIncrement, pageIncrement;
    Pixel bg;

#define MAX_ARGC 20
    Arg args[MAX_ARGC];
    int32_t argc = 0;


    AWT_LOCK();

    if (JNU_IsNull(env, parent)) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }

    pdata = (struct ComponentData *)
        JNU_GetLongFieldAsPtr(env, parent, mComponentPeerIDs.pData);

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

    if (JNU_IsNull(env, target) || pdata == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }


    switch ((*env)->GetIntField(env, target, targetIDs.orientation)) {
      case java_awt_Scrollbar_HORIZONTAL:
          XtSetArg(args[argc], XmNorientation, XmHORIZONTAL);
          argc++;
          break;

      case java_awt_Scrollbar_VERTICAL:
          XtSetArg(args[argc], XmNorientation, XmVERTICAL);
          argc++;
          break;

      default:
          JNU_ThrowIllegalArgumentException(env, "bad scrollbar orientation");
          AWT_UNLOCK();
          return;
    }

    adata = copyGraphicsConfigToPeer(env, this);
    XtVaGetValues(pdata->widget, XmNbackground, &bg, NULL);

    visible = (int32_t) (*env)->GetIntField(env, target, targetIDs.visibleAmount);
    value   = (int32_t) (*env)->GetIntField(env, target, targetIDs.value);
    minimum = (int32_t) (*env)->GetIntField(env, target, targetIDs.minimum);
    maximum = (int32_t) (*env)->GetIntField(env, target, targetIDs.maximum);
    lineIncrement =
              (int32_t) (*env)->GetIntField(env, target, targetIDs.lineIncrement);
    pageIncrement =
              (int32_t) (*env)->GetIntField(env, target, targetIDs.pageIncrement);

    /*
     * Sanity check.  Scrollbar.setValues should have taken care.
     */
    DASSERT(maximum > minimum);
    DASSERT(visible <= maximum - minimum);
    DASSERT(visible >= 1);
    DASSERT(value >= minimum);
    DASSERT(value <= maximum - visible);

    XtSetArg(args[argc], XmNx,             0);                  argc++;
    XtSetArg(args[argc], XmNy,             0);                  argc++;
    XtSetArg(args[argc], XmNvalue,         value);              argc++;
    XtSetArg(args[argc], XmNsliderSize,    visible);            argc++;
    XtSetArg(args[argc], XmNminimum,       minimum);            argc++;
    XtSetArg(args[argc], XmNmaximum,       maximum);            argc++;
    XtSetArg(args[argc], XmNincrement,     lineIncrement);      argc++;
    XtSetArg(args[argc], XmNpageIncrement, pageIncrement);      argc++;
    XtSetArg(args[argc], XmNbackground,    bg);                 argc++;
    XtSetArg(args[argc], XmNrecomputeSize, False);              argc++;
    XtSetArg(args[argc], XmNuserData,      globalRef);          argc++;
    XtSetArg(args[argc], XmNscreen,
                             ScreenOfDisplay(awt_display,
                                 adata->awt_visInfo.screen));   argc++;

    DASSERT(argc <= MAX_ARGC);  /* sanity check */

    sdata = ZALLOC(ComponentData);
    if (sdata == NULL) {
        JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
        AWT_UNLOCK();
        return;
    }

    JNU_SetLongFieldFromPtr(env, this, mComponentPeerIDs.pData, sdata);

    sdata->widget = w =
        XmCreateScrollBar(pdata->widget, "scrollbar", args, argc);

    XtAddCallback(w, XmNdecrementCallback,
        (XtCallbackProc)decrementCallback, globalRef);
    XtAddCallback(w, XmNincrementCallback,
        (XtCallbackProc)incrementCallback, globalRef);
    XtAddCallback(w, XmNpageDecrementCallback,
        (XtCallbackProc)pageDecrementCallback, globalRef);
    XtAddCallback(w, XmNpageIncrementCallback,
        (XtCallbackProc)pageIncrementCallback, globalRef);
    XtAddCallback(w, XmNtoTopCallback,
        (XtCallbackProc)toTopCallback, globalRef);
    XtAddCallback(w, XmNtoBottomCallback,
        (XtCallbackProc)toBottomCallback, globalRef);
    XtAddCallback(w, XmNdragCallback,
        (XtCallbackProc)dragCallback, globalRef);
    XtAddCallback(w, XmNvalueChangedCallback,
        (XtCallbackProc)dragEndCallback, globalRef);

    /* Set up workaround for the continuous scrolling bug */
    XtAddEventHandler(w, ButtonReleaseMask, False,
        awt_motif_Scrollbar_ButtonReleaseHandler, NULL);

    /* Fix for 4955950. ButtonRelease & MotionNotify should be handled as well */
    XtAddEventHandler(w, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
                      False, awt_canvas_event_handler, globalRef);

    XtSetMappedWhenManaged(w, False);
    XtManageChild(w);

    AWT_UNLOCK();
}

/*
 * Class:     sun_awt_motif_MScrollbarPeer
 * Method:    pSetValues
 * Signature: (IIII)V
 */
JNIEXPORT void JNICALL
Java_sun_awt_motif_MScrollbarPeer_pSetValues(JNIEnv *env, jobject this,
    jint value, jint visible, jint minimum, jint maximum)
{
    struct ComponentData *sdata;

    AWT_LOCK();

    sdata = (struct ComponentData *)
        JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData);
    if (sdata == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    /* pass in visible for sliderSize since Motif will calculate the */
    /* slider's size for us. */
    XtVaSetValues(sdata->widget,
                  XmNminimum, minimum,
                  XmNmaximum, maximum,
                  XmNvalue, value,
                  XmNsliderSize, visible,
                  NULL);
    AWT_FLUSH_UNLOCK();
}


/*
 * Class:     sun_awt_motif_MScrollbarPeer
 * Method:    setLineIncrement
 * Signature: (I)V
 */
JNIEXPORT void JNICALL
Java_sun_awt_motif_MScrollbarPeer_setLineIncrement(JNIEnv *env, jobject this,
    jint value)
{
    struct ComponentData *sdata;

    AWT_LOCK();

    sdata = (struct ComponentData *)
        JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData);
    if (sdata == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    XtVaSetValues(sdata->widget,
                  XmNincrement, value,
                  NULL);
    AWT_FLUSH_UNLOCK();
}


/*
 * Class:     sun_awt_motif_MScrollbarPeer
 * Method:    setPageIncrement
 * Signature: (I)V
 */
JNIEXPORT void JNICALL
Java_sun_awt_motif_MScrollbarPeer_setPageIncrement(JNIEnv *env, jobject this,
    jint value)
{
    struct ComponentData *sdata;

    AWT_LOCK();

    sdata = (struct ComponentData *)
        JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.pData);
    if (sdata == NULL) {
        JNU_ThrowNullPointerException(env, "NullPointerException");
        AWT_UNLOCK();
        return;
    }
    XtVaSetValues(sdata->widget,
                  XmNpageIncrement, value,
                  NULL);
    AWT_FLUSH_UNLOCK();
}
