Appendix C: Spinner for Windows

This appendix contains the source code for the Windows version of the Spinner natives in spinner.c, and the batch file and makefile for building the Windows Dynamic Link Library spinner.dll.

spinner.c

/*
 * (c) Copyright IBM Corp. 2000, 2001.
 * All Rights Reserved.
 */
 
#include <jni.h>
#include <windows.h>
#include <commctrl.h>
 
static DWORD tlsIndex = 0;
static jobject javaClass;
static jmethodID mid;
static WNDPROC oldProc;
 
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    JNIEnv *env = TlsGetValue(tlsIndex);
    if (env != NULL) {
        /* If an exception has already occurred,
         * allow the stack to unwind so that the
         * exception will be thrown in Java. */
        if ((*env)->ExceptionOccurred(env)) return 0;
        switch (msg) {
            case WM_VSCROLL:
                if ((wParam & 0xFFFF) == SB_THUMBPOSITION) {
                    ((*env)->CallStaticVoidMethod(env, javaClass, mid, lParam));
                    return (LRESULT) 0;
                }
                break;
        }
    }
    return CallWindowProc(oldProc, hwnd, msg, wParam, lParam); 
}

/*
 * Class:     spinner_Spinner
 * Method:    createControl
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_spinner_Spinner_createControl
    (JNIEnv *env, jclass that, jint hwndParent)
{
    HWND hwndText, hwndUpDown;
    if (tlsIndex == 0) {
        tlsIndex = TlsAlloc();
        if (tlsIndex == -1) return (jint) 0;
        javaClass = (*env)->NewGlobalRef(env, (jobject) that);
        mid = (*env)->GetStaticMethodID(env, (jobject) that, "widgetSelected", "(I)V");
        oldProc = (WNDPROC) GetWindowLong((HWND) hwndParent, GWL_WNDPROC);
    }
    TlsSetValue(tlsIndex, (LPVOID) env);

    hwndText = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        "EDIT",
        NULL,
        WS_CHILD | WS_VISIBLE | WS_TABSTOP,
        0, 0, 0, 0,
        (HWND) hwndParent,
        0,
        GetModuleHandle(NULL),
        NULL);
    if (hwndText == 0) return (jint) 0;
    hwndUpDown = CreateWindowEx(
        0,
        UPDOWN_CLASS,
        NULL,
        WS_CHILD | WS_VISIBLE | UDS_AUTOBUDDY | UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS,
        0, 0, 0, 0,
        (HWND) hwndParent,
        0,
        GetModuleHandle(NULL),
        NULL);
    if (hwndUpDown == 0) return (jint) 0;
    SetWindowLong((HWND) hwndParent, GWL_WNDPROC, (long) WindowProc);
    return (jint) hwndUpDown;
}

/*
 * Class:     spinner_Spinner
 * Method:    computeSize
 * Signature: (I[I)V
 */
JNIEXPORT void JNICALL Java_spinner_Spinner_computeSize
    (JNIEnv *env, jclass that, jint hwndUpDown, jintArray result) {
    int width, height;
    TEXTMETRIC tm;
    RECT rect;
    int comboHeight;
    int max, digits;
    UINT flags;
    char text[64];
    HWND hwndText = (HWND) SendMessage((HWND) hwndUpDown, UDM_GETBUDDY, 0, 0);
    HDC hDC = GetDC(hwndText);
    HFONT oldFont = 0;
    HFONT newFont = (HFONT) SendMessage(hwndText, WM_GETFONT, 0, 0);

    jint *result1 = NULL;
    result1 = (*env)->GetIntArrayElements(env, result, NULL);

    if (newFont != 0) oldFont = SelectObject(hDC, newFont);
    GetTextMetrics(hDC, &tm);
    comboHeight = GetSystemMetrics(SM_CYVSCROLL);
    height = (comboHeight > tm.tmHeight) ? comboHeight : tm.tmHeight;
    max = SendMessage((HWND) hwndUpDown, UDM_GETRANGE, 0, 0) & 0xFFFF;
    if (max > 0) {
        digits = 0;
        while (max > 0) {
            text[digits] = '0';
            max /= 10;
            digits++;
        }
        flags = DT_CALCRECT | DT_EDITCONTROL | DT_NOPREFIX;
        DrawText(hDC, (LPCTSTR) text, digits, (LPRECT) &rect, flags);
        width = rect.right - rect.left + 3;
    } else {
        width = 10;
    }
    if (newFont != 0) SelectObject(hDC, oldFont);
    ReleaseDC(hwndText, hDC);
    width += GetSystemMetrics(SM_CXVSCROLL);
    SendMessage(hwndText, EM_GETRECT, 0, (LPARAM) &rect);
    if (rect.top == 0) rect.top = 1; // windows bug fix
    width += (rect.left + 1) * 2;
    height += (rect.top + 1) * 2;

    result1 [0] = width;
    result1 [1] = height;
    (*env)->ReleaseIntArrayElements(env, result, result1, 0);
}

/*
 * Class:     spinner_Spinner
 * Method:    resizeControl
 * Signature: (IIIII)V
 */
JNIEXPORT void JNICALL Java_spinner_Spinner_resizeControl
    (JNIEnv *env, jclass that, jint hwndUpDown, jint x, jint y, jint width, jint height)
{
    HWND hwndText = (HWND) SendMessage((HWND) hwndUpDown, UDM_GETBUDDY, 0, 0);
    UINT flags = SWP_NOZORDER | SWP_DRAWFRAME | SWP_NOACTIVATE;
    int upDownWidth = GetSystemMetrics(SM_CXVSCROLL);
    SetWindowPos(hwndText, (HWND) 0, x, y, width - upDownWidth + 2, height, flags);
    SetWindowPos((HWND) hwndUpDown, (HWND) 0, x + width - upDownWidth, y, upDownWidth, height, flags);               
}

/*
 * Class:     spinner_Spinner
 * Method:    setPosition
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_spinner_Spinner_setPosition
    (JNIEnv *env, jclass that, jint hwnd, jint position)
{
    SendMessage((HWND) hwnd, UDM_SETPOS, 0, position);
}

/*
 * Class:     spinner_Spinner
 * Method:    getPosition
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_spinner_Spinner_getPosition
    (JNIEnv *env, jclass that, jint hwnd)
{
    return (jint) SendMessage((HWND) hwnd, UDM_GETPOS, 0, 0) & 0xFFFF;
}

/*
 * Class:     spinner_Spinner
 * Method:    setMaximum
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_spinner_Spinner_setMaximum
    (JNIEnv *env, jclass that, jint hwnd, jint max)
{
    int min = SendMessage((HWND) hwnd, UDM_GETRANGE, 0, 0) >> 16;
    SendMessage((HWND) hwnd, UDM_SETRANGE, 0, (min << 16) | max);
}

/*
 * Class:     spinner_Spinner
 * Method:    getMaximum
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_spinner_Spinner_getMaximum
    (JNIEnv *env, jclass that, jint hwnd)
{
    return SendMessage((HWND) hwnd, UDM_GETRANGE, 0, 0) & 0xFFFF;
}

/*
 * Class:     spinner_Spinner
 * Method:    setMinimum
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_spinner_Spinner_setMinimum
    (JNIEnv *env, jclass that, jint hwnd, jint min)
{
    int max = SendMessage((HWND) hwnd, UDM_GETRANGE, 0, 0) & 0xFFFF;
    SendMessage((HWND) hwnd, UDM_SETRANGE, 0, (min << 16) | max);
}

/*
 * Class:     spinner_Spinner
 * Method:    getMinimum
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_spinner_Spinner_getMinimum
    (JNIEnv *env, jclass that, jint hwnd)
{
    return SendMessage((HWND) hwnd, UDM_GETRANGE, 0, 0) >> 16;
}

/*
 * Class:     spinner_Spinner
 * Method:    setFont
 * Signature: (II)V
 */
JNIEXPORT void JNICALL Java_spinner_Spinner_setFont
    (JNIEnv *env, jclass that, jint hwnd, jint hFont)
{
    HWND hwndText = (HWND) SendMessage((HWND) hwnd, UDM_GETBUDDY, 0, 0);
    SendMessage(hwndText, WM_SETFONT, hFont, 1);
}

/*
 * Class:     spinner_Spinner
 * Method:    setFocus
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_spinner_Spinner_setFocus
    (JNIEnv *env, jclass that, jint hwnd)
{
    HWND hwndText = (HWND) SendMessage((HWND) hwnd, UDM_GETBUDDY, 0, 0);
    SetFocus(hwndText);
}

build.bat

@echo off
 
IF NOT "%IVE_HOME%"=="" GOTO MAKE
 
set IVE_HOME=j:\teamswt\swt-builddir\ive\bin
set path=%IVE_HOME%;%path%
 
rem NOTE:
rem Initialize MSVC 6.0/MSSDK environment
call k:\dev\products\msvc60\vc98\bin\vcvars32.bat
set Mssdk=j:\teamswt\swt-builddir\mssdk
call %Mssdk%\setenv.bat
 
:MAKE
nmake makefile.mak

makefile.mak

# Makefile for module 'spinner.dll'
# assumes IVE_HOME is set in the environment from which nmake is run

!include <win32.mak>

DLLPREFIX=spinner
DLLNAME=$(DLLPREFIX).dll

CFLAGS=-c -W3 -G6 -GD -O1 -nologo -D_X86_=1 -D_WIN32 -D_WIN95 -D_WIN32_WINDOWS=0x0400 -D_MT -MT -DWIN32 -D_WIN32_DCOM /I$(IVE_HOME)\include /I.
LFLAGS=/INCREMENTAL:NO /PDB:NONE /RELEASE /NOLOGO /BASE:0x10000000 /DLL

LINK_LIBS=comctl32.lib user32.lib gdi32.lib

OBJS=$(DLLPREFIX).obj

all: $(OBJS) $(DLLPREFIX)

$(DLLPREFIX): $(OBJS)
    echo $(LINK_LIBS) >templrf
    echo $(LFLAGS) >>templrf
    echo -machine:IX86 >>templrf
    echo -subsystem:windows >>templrf
    echo -out:$(DLLNAME) >>templrf
    echo $(OBJS) >>templrf
    link @templrf
    del templrf

clean:
    del *.obj
    del *.dll
    del *.exp

.c.obj:
    cl $(CFLAGS) $*.c