/* OCaml-Win32
 * win32_control.c
 * Copyright (c) 2002-2004 by Harry Chomsky
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "win32.h"

/********************************************************************/
/* User Interface: Windows User Experience: Windows Shell: SDK: Windows Controls: General Control Information */

CAMLprim value get_com_ctl_version(value unit)
{
    CAMLparam1(unit);
    HINSTANCE comctl;
    DLLGETVERSIONPROC dll_get_version;
    DLLVERSIONINFO dvi;
    HRESULT hr;
    CAMLlocal1(val);

    comctl = LoadLibrary("comctl32.dll");
    if (!comctl)
        raise_win32_error(0);
    val = alloc_tuple(2);
    dll_get_version = (DLLGETVERSIONPROC)GetProcAddress(comctl, "DllGetVersion");
    if (dll_get_version) {
        ZeroMemory(&dvi, sizeof(dvi));
        dvi.cbSize = sizeof(dvi);
        hr = dll_get_version(&dvi);
        if (!SUCCEEDED(hr))
            raise_win32_error(0);
        Store_field(val, 0, Val_int(dvi.dwMajorVersion));
        Store_field(val, 1, Val_int(dvi.dwMinorVersion));
    } else {
        Store_field(val, 0, Val_int(4));
        Store_field(val, 1, Val_int(0));
    }
    FreeLibrary(comctl);
    CAMLreturn(val);
}

CAMLprim value get_effective_client_rect(value wnd, value ctrl_ids)
{
    int* info;
    int* p;
    RECT rect;

    info = malloc(2 * (list_length(ctrl_ids) + 1) * sizeof(int));
    p = info;
    for (; !Is_nil(ctrl_ids); ctrl_ids = Tail(ctrl_ids)) {
        *p++ = 1;
        *p++ = Int_val(Head(ctrl_ids));
    }
    *p++ = 0;
    *p++ = 0;
    GetEffectiveClientRect(Handle_val(wnd), &rect, info);
    free(info);
    return alloc_rect(&rect);
}

CAMLprim value init_common_controls(value unit)
{
    InitCommonControls();
    return Val_unit;
}

static DWORD init_common_control_options[] = {
    ICC_ANIMATE_CLASS,
    ICC_BAR_CLASSES,
    ICC_COOL_CLASSES,
    ICC_DATE_CLASSES,
    ICC_HOTKEY_CLASS,
    ICC_INTERNET_CLASSES,
    ICC_LISTVIEW_CLASSES,
    ICC_PAGESCROLLER_CLASS,
    ICC_PROGRESS_CLASS,
    ICC_TAB_CLASSES,
    ICC_TREEVIEW_CLASSES,
    ICC_UPDOWN_CLASS,
    ICC_USEREX_CLASSES,
    ICC_WIN95_CLASSES
};
Define_static_constants(init_common_control_options);

CAMLprim value init_common_controls_ex(value options)
{
    INITCOMMONCONTROLSEX icc;
    BOOL result;

    icc.dwSize = sizeof(icc);
    icc.dwICC = flags_val(options, init_common_control_options);
    result = InitCommonControlsEx(&icc);
    if (!result)
        raise_win32_error(0);
    return Val_unit;
}

CAMLprim value show_hide_menu_ctl(value wnd, value item, value menu, value item_ctrl_pairs)
{
    CAMLparam4(wnd, item, menu, item_ctrl_pairs);
    HWND hwnd;
    int* info;
    int* p;
    CAMLlocal1(pair);
    BOOL result;

    hwnd = Handle_val(wnd);
    info = malloc(2 * (list_length(item_ctrl_pairs) + 2) * sizeof(int));
    p = info;
    *p++ = 1;
    *p++ = (int)Handle_val(menu);
    for (; !Is_nil(item_ctrl_pairs); item_ctrl_pairs = Tail(item_ctrl_pairs)) {
        pair = Head(item_ctrl_pairs);
        *p++ = Int_val(Field(pair, 0));
        *p++ = Int_val(Field(pair, 1));
    }
    *p++ = 0;
    *p++ = 0;
    enter_blocking_section();
    result = ShowHideMenuCtl(hwnd, Int_val(item), info);
    leave_blocking_section();
    free(info);
    if (!result)
        raise_win32_error(0);
    CAMLreturn(Val_unit);
}

/* Messages */

CAMLprim value unpack_ccm_getunicodeformat(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

CAMLprim value pack_ccm_setunicodeformat(value sender, value unicode)
{
    return pack_message(CCM_SETUNICODEFORMAT, (WPARAM)Bool_val(unicode), 0);
}

CAMLprim value unpack_ccm_setunicodeformat(value result)
{
    return Val_bool(Int32_val(result) != 0);
}

/* Notifications */

CAMLprim value unpack_nmchar(value msg)
{
    CAMLparam1(msg);
    NMCHAR* nmc;
    CAMLlocal1(val);

    nmc = (NMCHAR*)Lparam(msg);
    val = alloc_tuple(3);
    Store_field(val, 0, Val_int(nmc->ch));
    Store_field(val, 1, Val_int(nmc->dwItemPrev));
    Store_field(val, 2, Val_int(nmc->dwItemNext));
    CAMLreturn(val);
}

CAMLprim value unpack_nmkey(value msg)
{
    CAMLparam1(msg);
    NMKEY* nmk;
    CAMLlocal2(val, v);

    nmk = (NMKEY*)Lparam(msg);
    val = alloc_tuple(2);
    v = alloc_virtualkeycode((BYTE)nmk->nVKey);
    Store_field(val, 0, v);
    v = alloc_keydata(nmk->uFlags);
    Store_field(val, 1, v);
    CAMLreturn(val);
}

CAMLprim value unpack_nmmouse(value msg)
{
    CAMLparam1(msg);
    NMMOUSE* nmm;
    CAMLlocal2(val, v);

    nmm = (NMMOUSE*)Lparam(msg);
    val = alloc_tuple(3);
    Store_field(val, 0, Val_int(nmm->dwItemSpec));
    v = copy_int32(nmm->dwItemData);
    Store_field(val, 1, v);
    v = alloc_point(nmm->pt);
    Store_field(val, 2, v);
    CAMLreturn(val);
}

CAMLprim value return_nm_nchittest(value result)
{
    if (Is_some(result))
        return copy_int32(Constant_val(Value_of(result), hit_test_results));
    else
        return copy_int32(0);
}

CAMLprim value return_nm_setcursor(value result)
{
    return copy_int32(Bool_val(result));
}

/* Custom draw */

static DWORD customdraw_draw_stages[] = {
    CDDS_POSTERASE,
    CDDS_POSTPAINT,
    CDDS_PREERASE,
    CDDS_PREPAINT
};
Define_static_constants(customdraw_draw_stages);

static DWORD customdraw_item_states[] = {
    CDIS_CHECKED,
    CDIS_DEFAULT,
    CDIS_DISABLED,
    CDIS_FOCUS,
    CDIS_GRAYED,
    CDIS_HOT,
    CDIS_INDETERMINATE,
    CDIS_MARKED,
    CDIS_SELECTED
};
Define_static_constants(customdraw_item_states);

CAMLprim value unpack_nmcustomdraw(value msg)
{
    CAMLparam1(msg);
    NMCUSTOMDRAW* nmcd;
    CAMLlocal2(val, v);

    nmcd = (NMCUSTOMDRAW*)Lparam(msg);
    val = alloc_tuple(6);
    v = alloc_tuple(2);
    if (nmcd->dwDrawStage & CDDS_SUBITEM)
        Store_field(v, 0, 2);
    else if (nmcd->dwDrawStage & CDDS_ITEM)
        Store_field(v, 0, 1);
    else
        Store_field(v, 0, 0);
    Store_field(v, 1, Val_constant(nmcd->dwDrawStage & 0xFFFF, customdraw_draw_stages));
    Store_field(val, 0, v);
    v = alloc_handle(nmcd->hdc);
    Store_field(val, 1, v);
    v = alloc_rect(&nmcd->rc);
    Store_field(val, 2, v);
    v = copy_int32(nmcd->dwItemSpec);
    Store_field(val, 3, v);
    v = Alloc_flags(nmcd->uItemState, customdraw_item_states);
    Store_field(val, 4, v);
    v = copy_int32(nmcd->lItemlParam);
    Store_field(val, 5, v);
    CAMLreturn(val);
}

static DWORD nm_customdraw_results[] = {
    CDRF_NEWFONT,
    CDRF_NOTIFYITEMDRAW,
    CDRF_NOTIFYPOSTERASE,
    CDRF_NOTIFYPOSTPAINT,
    CDRF_NOTIFYSUBITEMDRAW,
    CDRF_SKIPDEFAULT,
    TBCDRF_HILITEHOTTRACK,
    TBCDRF_NOEDGES,
    TBCDRF_NOETCHEDEFFECT,
    TBCDRF_NOMARK,
    TBCDRF_NOOFFSET,
    TVCDRF_NOIMAGES
};
Define_static_constants(nm_customdraw_results);

CAMLprim value return_nm_customdraw(value result)
{
    return copy_int32(flags_val(result, nm_customdraw_results));
}
