
#include "win-dialog.h"

#include "win-main.h"
#include "gwindows-message-c.h"

#include "ggui/dialog.h"
#include "gcore/glaunch.h"

#include <commctrl.h>
#include <math.h>
#include <vfw.h>
#include <assert.h>

/****************************************************************************/

#define FIRSTCHAINID       100 // debut des indices des chaines
#define FIRSTSLIDERID      200 // debut des indices des sliders
#define FIRSTBUTTONID      300 // debut des indices des boutons 'more', 'logic' et 'file'
#define FIRSTCOMMANDID     400 // debut des indices des boutons de commande
#define FIRSTHELPID        500 // debut des indices des boutons helps

#define WIN_MENU_BUTTON_ID 1000
#define WIN_SLIDER_MAXIMUM 1000

#define IDIM 18
#define LDIM 25
const int LABELWIDTH = 200;
const int LABELHEIGHT = IDIM;
const int LABELFLAGS = WS_CHILD | WS_VISIBLE | WS_BORDER | SS_CENTER | SS_NOTIFY;
const int HORZPAD = 5;
const int ITEMHEIGHT = LDIM;
const int TEXTWIDTH = 300;
const int CHAINHEIGHT = IDIM;
const int CHAINFLAGS = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER;
const int BUTTONHEIGHT = 22;
const int BUTTONWIDTH = 100;
const int BUTTONFLAGS = WS_CHILD | WS_VISIBLE | WS_BORDER | BS_PUSHLIKE;
const int MOREWIDTH = 150;
const int FILECHAINWIDTH = 250;
const int HELPWIDTH = 50;
const int LOGICHEIGHT = IDIM;
const int LOGICFLAGS = WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX | BS_CENTER;
const int SLIDERCHAINWIDTH = 50;
const int SLIDERWIDTH = 250;
const int SLIDERHEIGHT = IDIM;
const int SLIDERFLAGS = WS_CHILD | WS_VISIBLE | TBS_NOTICKS | TBS_ENABLESELRANGE;
const int CHOICEHEIGHT = LDIM;
const int COMBOBOXFLAGS = WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_AUTOHSCROLL | CBS_DROPDOWN | CBS_HASSTRINGS;

/****************************************************************************/

static char win_window_title[TITLELENGTH];
static char win_helpfilename[HLPFILELNGTH];
static char win_main_command[COMMANDLENGTH];

static HFONT win_dialogfont = NULL;
static int SCB; // scrollbar needed
static SCROLLINFO si;
static int screenheight;
static int mainheight, viewportheight;
static HINSTANCE hInst; // current instance
static int mainwidth;
static int textwidth;
static int buttonwidth;
static int morewidth;
static int helpwidth;
static int filechainwidth;
static int sliderwidth;
static HWND toolBar;
static int yoffset = 0;
static HWND hWnd;
static HWND FrameWnd;

/****************************************************************************/

/* Windows widget implementation */
static void dialog_separator_add( dialog_info_t *dialog);
static void dialog_chain_add( dialog_info_t *dialog, chain_struct *chain);
static void dialog_slider_add( dialog_info_t *dialog, slider_struct *slider);
static void slider_info_set_value( widget_info_t *widget_info, double value);
static void logic_info_get_value( widget_info_t *widget_info);
static void logic_info_set_value( widget_info_t *widget_info, int value);
static void chain_info_get_value( widget_info_t *widget_info);
static void chain_info_set_value( widget_info_t *widget_info, const char *value);
static void choice_info_get_value( widget_info_t *widget_info);
static void choice_info_set_value( widget_info_t *widget_info, const char *value);
static void file_info_get_value( widget_info_t *widget_info);
static void file_info_set_value( widget_info_t *widget_info, const char *value);
static void dialog_show_add( dialog_info_t *dialog, show_struct *show);
static void dialog_logic_add( dialog_info_t *dialog, logic_struct *logic);
static void dialog_browser_add( dialog_info_t *dialog, file_struct *file);
static void dialog_choice_add( dialog_info_t *dialog, choice_struct *choice);
static void dialog_button_add( dialog_info_t *dialog, button_struct *button);
static void dialog_on_create_item( dialog_info_t *dialog, generic_struct *generic);
static void dialog_close( dialog_info_t *dialog);

/* Motif widget interface */
static widget_api_t win_widget_api = {
    NULL, /* dialog_info_create_group_box */
    dialog_separator_add,
    dialog_chain_add,
    chain_info_get_value,
    chain_info_set_value,
    dialog_slider_add,
    slider_info_set_value,
    dialog_show_add,
    dialog_logic_add,
    logic_info_get_value,
    logic_info_set_value,
    dialog_browser_add,
    file_info_get_value,
    file_info_set_value,
    dialog_choice_add,
    choice_info_get_value,
    choice_info_set_value,
    dialog_button_add,
    dialog_on_create_item,
    dialog_close,
};

/****************************************************************************/

typedef struct {
    widget_info_struct *widget_info;
    int y_offset;
    int id;
} win_dialog_item_info_t;

typedef struct {
    dialog_info_t _herits;
    widget_info_t *button_info;
    widget_info_t *help_button_info;
    int curheight;
    HWND parent_window;
    win_dialog_item_info_t *dialog_items[NSTRUCT];
    int nb_dialog_items;
} win_dialog_info_t;

typedef struct {
    HWND edit_window;
    WNDPROC original_edit_proc;
} win_edit_info_t;

typedef struct { // herits from widget_info_struct
    widget_t *generic;
    win_edit_info_t edit_info;
} win_edit_window_info_struct;
typedef void win_edit_window_info_t;

typedef struct { // herits from widget_info_struct
    choice_struct *choice;
    HWND choice_window;
    WNDPROC original_choice_proc;
} win_choice_info_t;

typedef struct { // herits from win_edit_window_info_struct
    slider_struct *slider;
    win_edit_info_t edit_info;
    HWND slider_window;
} win_slider_info_t;

typedef struct { // herits from widget_info_struct
    button_struct *button;
    win_dialog_info_t *dialog;
    HWND help_window;
    HWND help_edit;
} win_button_info_t;

typedef struct { // herits from widget_info_struct
    logic_struct *logic;
    HWND logic_window;
} win_logic_info_t;

typedef struct { // herits from win_edit_window_info_struct
    chain_struct *chain;
    win_edit_info_t edit_info;
} win_chain_info_t;

typedef struct { // herits from win_edit_window_info_struct
    file_struct *file;
    win_edit_info_t edit_info;
    long shell;
    long file_widget;
    long text_widget;
} win_file_info_t;

/****************************************************************************/

static win_dialog_item_info_t *win_dialog_item_info_new( widget_info_t *widget_info, int id, int y_offset)
{
    win_dialog_item_info_t *ret;
    ret = calloc( 1, sizeof( *ret));;
    ret->widget_info = widget_info;
    ret->id = id;
    ret->y_offset = y_offset;
    return ret;
}

static void win_dialog_item_info_delete( win_dialog_item_info_t *dialog_item)
{
    dialog_item->widget_info = NULL;
    free( dialog_item);
}

static win_dialog_info_t *win_dialog_info_new( win_button_info_t *button_info)
{
    win_dialog_info_t *ret;
    ret = calloc( 1, sizeof( *ret));;
    ret->button_info = button_info;
    ret->curheight = 0;
    ret->nb_dialog_items = 0;
    dialog_info_prepare( (dialog_info_t *)ret, button_info == NULL ? 0 : button_info->button->popup_window_id);
    return ret;
}

static void win_dialog_info_delete( win_dialog_info_t *win_dialog)
{
    int i;

    for (i = 0; i < win_dialog->nb_dialog_items; i++)
        win_dialog_item_info_delete( win_dialog->dialog_items[i]);

    free( win_dialog);
}

static win_choice_info_t *win_choice_info_new( choice_struct *choice)
{
    win_choice_info_t *ret;
    ret = widget_info_new( sizeof( *ret), choice);
    return ret;
}

static win_slider_info_t *win_slider_info_new( slider_struct *slider)
{
    win_slider_info_t *ret;
    ret = widget_info_new( sizeof( *ret), slider);
    return ret;
}

static win_button_info_t *win_button_info_new( button_struct *button)
{
    win_button_info_t *ret;
    ret = widget_info_new( sizeof( *ret), button);
    return ret;
}

static win_logic_info_t *win_logic_info_new( logic_struct *logic)
{
    win_logic_info_t *ret;
    ret = widget_info_new( sizeof( *ret), logic);
    return ret;
}

static win_chain_info_t *win_chain_info_new( chain_struct *chain)
{
    win_chain_info_t *ret;
    ret = widget_info_new( sizeof( *ret), chain);
    return ret;
}

static win_file_info_t *win_file_info_new( file_struct *file)
{
    win_file_info_t *ret;
    ret = widget_info_new( sizeof( *ret), file);
    return ret;
}

static void win_edit_window_set( win_edit_window_info_t *edit_window, const char *value)
{
    SetWindowText( ((win_edit_window_info_struct *)edit_window)->edit_info.edit_window, value);
}

static void win_edit_window_set_real( win_edit_window_info_t *edit_window, double value)
{
    char dum[80];

    sprintf( dum, "%g", value);
    win_edit_window_set( edit_window, dum);
}

static void win_edit_window_get( win_edit_window_info_t *edit_window, char *value, int size)
{
    GetWindowText( ((win_edit_window_info_struct *)edit_window)->edit_info.edit_window, (LPTSTR)value, size);
}

/****************************************************************************/

static widget_info_t *dialog_find_widget_info_from_id( win_dialog_info_t *win_dialog, int id)
{
    int i;

    for (i = 0; i < win_dialog->nb_dialog_items; i++) {
        if (win_dialog->dialog_items[i]->id == id)
            return win_dialog->dialog_items[i]->widget_info;
    }
    return NULL;
}

static void dialog_add_widget_info( win_dialog_info_t *win_dialog, widget_info_t *widget_info, int id)
{
    //if (id <= 0 || dialog_find_widget_info_from_id( win_dialog, id)) {
    //    assert( 0);
    //    return;
    //}
    win_dialog->dialog_items[win_dialog->nb_dialog_items++] = win_dialog_item_info_new( widget_info, id, win_dialog->curheight);
}

static widget_info_t *dialog_find_widget_info_from_y_offset( win_dialog_info_t *win_dialog, int y_offset)
{
    int i;

    for (i = 0; i < win_dialog->nb_dialog_items; i++) {
        if (win_dialog->dialog_items[i]->y_offset > y_offset)
            return win_dialog->dialog_items[i > 0 ? i - 1 : i]->widget_info;
    }
    return win_dialog->dialog_items[win_dialog->nb_dialog_items - 1]->widget_info;
}

static int win_widget_is_edit_window( widget_info_struct *widget_info)
{
    generic_struct *generic = widget_info->generic;
    return (generic->type == CHAIN && ((chain_struct *)generic)->editable
        || generic->type == SLIDER || generic->type == BROWSER);
}

static win_edit_window_info_struct *dialog_find_edit_window( win_dialog_info_t *win_dialog, win_edit_window_info_struct *edit_window, int forward)
{
    int i;
    int found = (edit_window == NULL);
    win_edit_window_info_struct *ret = NULL;

    for (i = 0; i < win_dialog->nb_dialog_items; i++) {
        widget_info_struct *widget_info = win_dialog->dialog_items[i]->widget_info;
        generic_struct *generic = widget_info->generic;
        if (widget_info == (void *)edit_window) {
            found = TRUE;
            if (!forward)
                break;
        } else if (win_widget_is_edit_window( widget_info)) {
            if (!forward || found)
                ret = (win_edit_window_info_struct *)widget_info;
            if (found)
                break;
        }
    }
    return ret;
}

static win_edit_window_info_struct *dialog_find_previous_edit_window( win_dialog_info_t *win_dialog, win_edit_window_info_struct *edit_window)
{
    return dialog_find_edit_window( win_dialog, edit_window, 0);
}

static win_edit_window_info_struct *dialog_find_next_edit_window( win_dialog_info_t *win_dialog, win_edit_window_info_struct *edit_window)
{
    return dialog_find_edit_window( win_dialog, edit_window, 1);
}

static win_edit_window_info_struct *dialog_find_first_edit_window( win_dialog_info_t *win_dialog)
{
    return dialog_find_edit_window( win_dialog, NULL, 1);
}

// Get widget vertical position
static int get_wy( win_dialog_info_t *win_dialog, int widget_height)
{
    return win_dialog->curheight + (ITEMHEIGHT - widget_height) / 2;
}

static void set_widget_font(HWND hwnd, HFONT hfont)
{
  SendMessage(hwnd, WM_SETFONT,
              (WPARAM) hfont,
              (LPARAM) MAKELPARAM(TRUE, 0));
}

static win_dialog_info_t *win_dialog_get_from_window( HWND hwnd)
{
	return (win_dialog_info_t *)GetWindowLong( hwnd, GWL_USERDATA);
}

static void chain_info_set( widget_info_t *widget_info, const char *value)
{
    win_chain_info_t *chain_info = (win_chain_info_t *)widget_info;

    strcpy( chain_info->chain->userchain, value);
}

static LRESULT APIENTRY EditSubclassProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
    win_edit_window_info_struct *edit_window;

    if (uMsg == WM_DESTROY)
        return 0;

    edit_window = (win_edit_window_info_struct *)GetWindowLong(hwnd, GWL_USERDATA);
    if (edit_window == NULL)
        return 0;

    switch (uMsg) {
    case WM_KEYDOWN:
        if (wParam == VK_UP || wParam == VK_DOWN) {
            HWND parent;
            POINT caretpos;
            win_dialog_info_t *win_dialog;
            win_edit_window_info_struct *new_edit_window;

            parent = GetParent(hwnd);
            GetCaretPos( &caretpos);
            MapWindowPoints( hwnd, parent, &caretpos, 1);

            win_dialog = win_dialog_get_from_window( parent);
            if (wParam == VK_UP) {
                new_edit_window = dialog_find_previous_edit_window( win_dialog, edit_window);
            } else {
                // wParam == VK_DOWN
                new_edit_window = dialog_find_next_edit_window( win_dialog, edit_window);
            }
            if (new_edit_window != NULL) {
                SetFocus( new_edit_window->edit_info.edit_window);
                if (SCB && win_dialog->button_info == NULL) {
                    // main window with scrollbars
                    GetCaretPos( &caretpos);
                    MapWindowPoints( new_edit_window->edit_info.edit_window, parent, &caretpos, 1);

                    if (wParam == VK_UP && caretpos.y < -yoffset) {
                        si.nPos -= ((-yoffset-caretpos.y+ITEMHEIGHT)/ITEMHEIGHT)*ITEMHEIGHT;
                        yoffset += ((-yoffset-caretpos.y+ITEMHEIGHT)/ITEMHEIGHT)*ITEMHEIGHT;
                        yoffset = min(0, yoffset);
                        si.nPos = max(0, si.nPos);

                        MoveWindow(hWnd, 0, yoffset, mainwidth, mainheight, 1);
                        si.fMask  = SIF_POS;
                        SetScrollInfo(FrameWnd, SB_VERT, &si, TRUE);
                    }
                    else if (wParam == VK_DOWN && caretpos.y + yoffset > viewportheight) {
                        si.nPos += ((caretpos.y-viewportheight+ITEMHEIGHT)/ITEMHEIGHT)*ITEMHEIGHT;
                        yoffset -= ((caretpos.y-viewportheight+ITEMHEIGHT)/ITEMHEIGHT)*ITEMHEIGHT;
                        yoffset = max(viewportheight - mainheight, yoffset);
                        si.nPos = min(si.nPos, mainheight-viewportheight);

                        MoveWindow(hWnd, 0, yoffset, mainwidth, mainheight, 1);
                        si.fMask  = SIF_POS;
                        SetScrollInfo(FrameWnd, SB_VERT, &si, TRUE);
                    }
                }
                return 0;
            }
        }
        break;

    case WM_KEYUP:
        if (((generic_struct*)edit_window->generic)->type == SLIDER) {
            static BOOL slider_info_set_pos( widget_info_t *widget_info, double value);
            char buffer[256];

			SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM) buffer);
            slider_info_set_pos( edit_window, atof( buffer));
        } else if (((generic_struct*)edit_window->generic)->type == CHAIN) {
            chain_info_get_value( edit_window);
        }
        update_other_widgets( &win_widget_api, edit_window->generic);
        break;
    }

    return CallWindowProc( edit_window->edit_info.original_edit_proc, hwnd, uMsg, wParam, lParam);
}

static void win_on_create_window( win_dialog_info_t *win_dialog, widget_info_t *widget_info, HWND window)
{
    set_widget_font( window, win_dialogfont);
    if (widget_info != NULL) {
        LONG id;
        SetWindowLong( window, GWL_USERDATA, (LONG)widget_info);
        id = GetWindowLong( window, GWL_ID);
        dialog_add_widget_info( win_dialog, widget_info, id);
    }
}

static void win_on_create_readonly_edit_window( win_dialog_info_t *win_dialog, win_edit_window_info_t *edit_window, HWND window)
{
    win_on_create_window( win_dialog, edit_window, window);
    ((win_edit_window_info_struct *)edit_window)->edit_info.edit_window = window;
}

static void win_on_create_edit_window( win_dialog_info_t *win_dialog, win_edit_window_info_t *edit_window, HWND window)
{
    win_on_create_readonly_edit_window( win_dialog, edit_window, window);
    ((win_edit_window_info_struct *)edit_window)->edit_info.original_edit_proc = (WNDPROC)(intptr_t)SetWindowLong( window, GWL_WNDPROC, (LONG)(intptr_t)EditSubclassProc); // must be called after win_on_create_window
}

LRESULT APIENTRY ChoiceSubclassProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
    win_choice_info_t *choice_info;

    if (uMsg == WM_DESTROY)
        return 0;

    choice_info = (win_choice_info_t *)GetWindowLong( hwnd, GWL_USERDATA);
    if (choice_info == NULL)
        return 0;

    switch (uMsg) {
    case WM_COMMAND:
        choice_info_get_value( choice_info);
        update_other_widgets( &win_widget_api, choice_info->choice);
        break;
    }
    return CallWindowProc( choice_info->original_choice_proc, hwnd, uMsg, wParam, lParam);
}

#if 0
# a rendre generique avec slider_info->internal_maximum
#endif
static int slider_uservalue2value( slider_struct *slider)
{
    return (int)(WIN_SLIDER_MAXIMUM * (slider->uservalue - slider->min) / slider->width);
}

static double slider_value2uservalue( slider_struct *slider, int value)
{
    return slider->min + (slider->width / WIN_SLIDER_MAXIMUM) * value;
}

#if 0
# a rendre generique
#endif
static BOOL slider_info_set_pos( widget_info_t *widget_info, double value)
{
    win_slider_info_t *slider_info = (win_slider_info_t *)widget_info;
    slider_struct *slider;
    double corrected_value;
    double eps;

    slider = slider_info->slider;
    eps = slider->width / 1e5;
    corrected_value = MINI(MAXI(slider->min, value), slider->min + slider->width);
    if (fabs( corrected_value - value) > eps || fabs( corrected_value - slider->uservalue) > eps) {
        DWORD dwPos;
        slider->uservalue = (float)corrected_value;
        dwPos = (DWORD)slider_uservalue2value( slider);
        SendMessage( slider_info->slider_window, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)dwPos);
        return TRUE;
    }
    return FALSE;
}

static void win_destroy_window( HWND hwnd)
{
    if (hwnd == NULL)
        return;
    DestroyWindow( hwnd);
    SetWindowLong( hwnd, GWL_USERDATA, 0);
}

/****************************************************************************/
/* Widget API implementation for Windows ************************************/

static void slider_info_set_value( widget_info_t *widget_info, double value)
{
    win_slider_info_t *slider_info = (win_slider_info_t *)widget_info;
    if (slider_info_set_pos( slider_info, value))
        win_edit_window_set_real( slider_info, slider_info->slider->uservalue);
}

static void logic_info_get_value( widget_info_t *widget_info)
{
    win_logic_info_t *logic_info = (win_logic_info_t *)widget_info;
    LRESULT result;

    result = SendMessage( logic_info->logic_window, BM_GETCHECK, 0, 0);
    logic_info->logic->userlogic = result == BST_CHECKED ? 1 : 0;
}

static void logic_info_set_value( widget_info_t *widget_info, int value)
{
    win_logic_info_t *logic_info = (win_logic_info_t *)widget_info;

    logic_info->logic->userlogic = value;
    SendMessage( logic_info->logic_window, BM_SETCHECK, (WPARAM)(value ? BST_CHECKED : BST_UNCHECKED), (LPARAM)0);
    SetWindowText( logic_info->logic_window, (LPCTSTR)(value ? "Yes" : "No"));
}

static void chain_info_get_value( widget_info_t *widget_info)
{
    win_chain_info_t *chain_info = (win_chain_info_t *)widget_info;
    char buffer[CHAINLENGTH];

    win_edit_window_get( chain_info, buffer, sizeof( buffer));
    strncpy( chain_info->chain->userchain, buffer, CHAINLENGTH);
}

static void chain_info_set_value( widget_info_t *widget_info, const char *value)
{
    chain_info_set( widget_info, value);
    win_edit_window_set( widget_info, value);
}

static void choice_info_get_value( widget_info_t *widget_info)
{
    win_choice_info_t *choice_info = (win_choice_info_t *)widget_info;
    LRESULT index;
    choice_struct *choice;

    choice = choice_info->choice;
    index = SendMessage( choice_info->choice_window, CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
    if (index != CB_ERR) {
        choice_set_index( choice, index);
    } else {
        char tmp[CHAINLENGTH];
        GetWindowText( choice_info->choice_window, (LPTSTR)tmp, sizeof( tmp));
        strncpy( choice->userchoice, tmp, sizeof( tmp));
    }
}

static void choice_info_set_value( widget_info_t *widget_info, const char *value)
{
    win_choice_info_t *choice_info = (win_choice_info_t *)widget_info;
    choice_struct *choice;

    choice = choice_info->choice;
    strcpy( choice->userchoice, value);
    if (choice->mode) {
        int active_index = choice_get_index( choice);
        if (active_index >= 0)
            SendMessage( choice_info->choice_window, CB_SETCURSEL, (WPARAM)active_index, (LPARAM)0);
    } else {
        SetWindowText( choice_info->choice_window, value);
    }
}

static void file_info_get_value( widget_info_t *widget_info)
{
    win_file_info_t *file_info = (win_file_info_t *)widget_info;
    char buffer[CHAINLENGTH];

    win_edit_window_get( file_info, buffer, sizeof( buffer));
    strncpy( file_info->file->userchain, buffer, CHAINLENGTH);
}

static void file_info_set_value( widget_info_t *widget_info, const char *value)
{
    win_file_info_t *file_info = (win_file_info_t *)widget_info;

    strcpy( file_info->file->userchain, value);
    win_edit_window_set( file_info, value);
}

static void dialog_show_add( dialog_info_t *dialog, show_struct *show)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;
    HWND w;

    if (strlen(show->label) != 0)
        w = CreateWindow("STATIC", show->text,  LABELFLAGS,
        LABELWIDTH+HORZPAD, get_wy( win_dialog, LABELHEIGHT), textwidth, LABELHEIGHT,
        win_dialog->parent_window, (HMENU) NULL, hInst, (LPSTR) NULL);
    else
        w = CreateWindow("STATIC", show->text,  LABELFLAGS,
        0, get_wy( win_dialog, LABELHEIGHT), LABELWIDTH+HORZPAD+textwidth, LABELHEIGHT,
        win_dialog->parent_window, (HMENU) NULL, hInst, (LPSTR) NULL);
    win_on_create_window( win_dialog, NULL, w);
}

static void dialog_logic_add( dialog_info_t *dialog, logic_struct *logic)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;
    win_logic_info_t *logic_info;
    HWND w;

    logic_info = win_logic_info_new( logic);

    w = CreateWindow("BUTTON", (LPCTSTR)(logic->userlogic ? "Yes" : "No"), LOGICFLAGS,
        LABELWIDTH+HORZPAD, get_wy( win_dialog, LOGICHEIGHT), textwidth, LOGICHEIGHT,
        win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTBUTTONID+dialog->nb_items), hInst, (LPSTR) NULL);
    if (logic->userlogic)
        CheckDlgButton( win_dialog->parent_window, (FIRSTBUTTONID+dialog->nb_items), BST_CHECKED);
    logic_info->logic_window = w;
    win_on_create_window( win_dialog, logic_info, w);
}

static void dialog_browser_add( dialog_info_t *dialog, file_struct *file)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;
    win_file_info_t *file_info;
    HWND w;

    file_info = win_file_info_new( file);

    w = CreateWindow("EDIT", file->userchain,  CHAINFLAGS,
        LABELWIDTH+HORZPAD, get_wy( win_dialog, CHAINHEIGHT),
        filechainwidth-HORZPAD, CHAINHEIGHT,
        win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTCHAINID+dialog->nb_items), hInst, (LPSTR) NULL);
    win_on_create_edit_window( win_dialog, file_info, w);

    w = CreateWindow("BUTTON", "File", BUTTONFLAGS,
        LABELWIDTH+filechainwidth+HORZPAD, get_wy( win_dialog, BUTTONHEIGHT),
        helpwidth, BUTTONHEIGHT,
        win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTBUTTONID+dialog->nb_items), hInst, (LPSTR) NULL);
    win_on_create_window( win_dialog, file_info, w);
}

static void dialog_choice_add( dialog_info_t *dialog, choice_struct *choice)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;
    win_choice_info_t *choice_info;
    HWND w;
    int k;

    choice_info = win_choice_info_new( choice);

    if (choice->mode == 0) {
        w = CreateWindow("COMBOBOX", NULL, COMBOBOXFLAGS,
            LABELWIDTH+HORZPAD, get_wy( win_dialog, CHOICEHEIGHT), textwidth,
            choice->nchoices*CHOICEHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTCHAINID+dialog->nb_items), hInst, (LPSTR) NULL);
    } else {
        w = CreateWindow("COMBOBOX", NULL, COMBOBOXFLAGS | CBS_DROPDOWNLIST,
            LABELWIDTH+HORZPAD, get_wy( win_dialog, CHOICEHEIGHT), textwidth,
            choice->nchoices*CHOICEHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTCHAINID+dialog->nb_items), hInst, (LPSTR) NULL);
    }
    win_on_create_window( win_dialog, choice_info, w);
    choice_info->choice_window = w;
    choice_info->original_choice_proc = (WNDPROC)(intptr_t)SetWindowLong(w, GWL_WNDPROC, (LONG)(intptr_t)ChoiceSubclassProc);

    for (k = 0; k <choice->nchoices; k++)
        SendMessage(w, CB_ADDSTRING,
        (WPARAM) 0,
        (LPARAM) (LPCTSTR) choice->choices[k]);

    choice_info_set_value( choice_info, choice->userchoice);
}

static void dialog_button_add( dialog_info_t *dialog, button_struct *button)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;
    win_button_info_t *button_info;
    int h;
    HWND w;
    int id;

    id = button->popup_window_id;
    button_info = win_button_info_new( button);

    h = get_wy( win_dialog, BUTTONHEIGHT);
    w = CreateWindow("BUTTON", button->title, BUTTONFLAGS,
        LABELWIDTH+HORZPAD, h, buttonwidth, BUTTONHEIGHT,
        win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTCOMMANDID+id), hInst, (LPSTR) NULL);
    win_on_create_window( win_dialog, button_info, w);

    if (strlen( button->moretxt) > 1) {
        w = CreateWindow("BUTTON", button->moretxt, BUTTONFLAGS,
            LABELWIDTH+HORZPAD+buttonwidth, h, morewidth, BUTTONHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTBUTTONID+id), hInst, (LPSTR) NULL);
        win_on_create_window( win_dialog, button_info, w);
    }

    if (strlen( button->helptxt) > 1) {
        w = CreateWindow("BUTTON", "Help", BUTTONFLAGS,
            LABELWIDTH+HORZPAD+buttonwidth+morewidth, h, helpwidth, BUTTONHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTHELPID+id), hInst, (LPSTR) NULL);
        win_on_create_window( win_dialog, button_info, w);
    }
}

static void dialog_on_create_item( dialog_info_t *dialog, generic_struct *generic)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;

    if (generic->label[0]) {
        HWND w;

        w = CreateWindow( "STATIC", generic->label, LABELFLAGS,
            0, get_wy( win_dialog, LABELHEIGHT), LABELWIDTH, LABELHEIGHT,
            win_dialog->parent_window, (HMENU)NULL, hInst, (LPSTR)NULL);
        win_on_create_window( win_dialog, NULL, w);
    }
    dialog->nb_items++;
	win_dialog->curheight += ITEMHEIGHT;
}

static void dialog_close( dialog_info_t *dialog)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;
    if (GetWindowLong( win_dialog->parent_window, GWL_USERDATA)) {
        SetWindowLong( win_dialog->parent_window, GWL_USERDATA, 0);
        win_destroy_window( win_dialog->parent_window);
        PostQuitMessage( 0);
    }
}

static void dialog_separator_add( dialog_info_t *dialog)
{
    // curheight is increased in dialog_on_create_item
}

static void dialog_chain_add( dialog_info_t *dialog, chain_struct *chain)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;
    win_chain_info_t *chain_info;
    HWND w;

    chain_info = win_chain_info_new( chain);

    if (chain->editable) {
        w = CreateWindow("EDIT", chain->userchain,  CHAINFLAGS,
        LABELWIDTH+HORZPAD, get_wy( win_dialog, CHAINHEIGHT), textwidth, CHAINHEIGHT,
        win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTCHAINID+dialog->nb_items), hInst, (LPSTR) NULL);
        win_on_create_edit_window( win_dialog, chain_info, w);
    } else {
        w = CreateWindow("EDIT", chain->userchain,  CHAINFLAGS | ES_READONLY,
        LABELWIDTH+HORZPAD, get_wy( win_dialog, CHAINHEIGHT), textwidth, CHAINHEIGHT,
        win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTCHAINID+dialog->nb_items), hInst, (LPSTR) NULL);
        win_on_create_readonly_edit_window( win_dialog, chain_info, w);
    }
}

static void dialog_slider_add( dialog_info_t *dialog, slider_struct *slider)
{
    win_dialog_info_t *win_dialog = (win_dialog_info_t *)dialog;
    win_slider_info_t *slider_info;
    char dumstr[80];
    HWND w;

    slider_info = win_slider_info_new( slider);

    sprintf(dumstr, "%g", slider->uservalue);
    w = CreateWindow("EDIT", dumstr,  CHAINFLAGS,
        LABELWIDTH+HORZPAD, get_wy( win_dialog, CHAINHEIGHT), SLIDERCHAINWIDTH, CHAINHEIGHT,
        win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTCHAINID+dialog->nb_items), hInst, (LPSTR) NULL);
    win_on_create_edit_window( win_dialog, slider_info, w);

    w = CreateWindow(TRACKBAR_CLASS, (LPCTSTR) "Trackbar Control", SLIDERFLAGS,
        LABELWIDTH+HORZPAD+SLIDERCHAINWIDTH, get_wy( win_dialog, SLIDERHEIGHT), sliderwidth, SLIDERHEIGHT,
        win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTSLIDERID+dialog->nb_items), hInst, (LPSTR) NULL);
    SendMessage(w, TBM_SETRANGE,
        (WPARAM) TRUE,                   // redraw flag
        (LPARAM) MAKELONG(0, WIN_SLIDER_MAXIMUM));  // min. & max. positions
    SendMessage(w, TBM_SETPOS,
        (WPARAM) TRUE,                   // redraw flag
        (LPARAM) (WIN_SLIDER_MAXIMUM*(slider->uservalue-slider->min)/
        slider->width));
    slider_info->slider_window = w;
    win_on_create_window( win_dialog, slider_info, w);
}

typedef struct {
    int command_id;
    button_struct *button;
} option_t;

#define MAX_OPTIONS 64
typedef struct {
    int command_id;
    option_t options[MAX_OPTIONS];
    int nb_options;
} option_menu_t;

#define MAX_OPTION_MENUS 32
static option_menu_t option_menus[MAX_OPTION_MENUS];
static int nb_option_menus = 0;

option_menu_t *find_option_menu( int command_id)
{
    int i;
    for (i = 0; i < nb_option_menus; i++)
        if (option_menus[i].command_id == command_id)
            return &option_menus[i];
    return NULL;
}

option_menu_t *create_option_menu( int command_id)
{
    option_menu_t *ret = &option_menus[nb_option_menus++];
    ret->command_id = command_id;
    ret->nb_options = 0;
    return ret;
}

void add_option_menu_item( option_menu_t *option_menu, int command_id, button_struct *button)
{
    option_t *o = &option_menu->options[option_menu->nb_options++];
    o->command_id = command_id;
    o->button = button;
}

option_t *find_option( option_menu_t *option_menu, int command_id)
{
    int i;
    for (i = 0; i < option_menu->nb_options; i++)
        if (option_menu->options[i].command_id == command_id)
            return &option_menu->options[i];
    return NULL;
}

option_t *find_option_recur( int command_id)
{
    int i;
    for (i = 0; i < nb_option_menus; i++) {
        int j;
        option_menu_t *option_menu = option_menus + i;
        for (j = 0; j < option_menu->nb_options; j++) {
            if (option_menu->options[j].command_id == command_id)
                return &option_menu->options[j];
        }
    }
    return NULL;
}

static HWND init_toolbar( win_dialog_info_t *dialog)
{
    TBBUTTON tbb[2 * (MAX_OPTIONS + MAX_OPTION_MENUS)];
    HWND hToolBar;
    int index;
    int command_id;
    option_menu_t *global_menu;
    option_menu_t *option_menu;
    void *iter;
    button_struct *b;

    //MoveWindow(dialog->parent_window, 0, 0, mainwidth + 500, mainheight, 1);

    /* Create command bar */
    global_menu = create_option_menu( -1);
    hToolBar = NULL;
    option_menu = NULL;
    index = 0;
    command_id = WIN_MENU_BUTTON_ID + 3;
    memset( tbb, 0, sizeof(tbb));
    iter = parse_menu_button_begin( );
    while ((b = parse_menu_button_next( iter)) != NULL) {
        if (hToolBar == NULL) {
            hToolBar = CreateWindowEx(WS_EX_CLIENTEDGE, TOOLBARCLASSNAME, NULL,
                WS_CHILD|WS_VISIBLE|TBSTYLE_LIST,
                0,0,0,0,dialog->parent_window,0,hInst,NULL);
            if(!hToolBar)
                break;
            SendMessage( hToolBar, TB_SETINDENT, 6, 0);
            SendMessage( hToolBar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
            SendMessage(hToolBar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON),0);
        }

        /* Add Buttons */
        if (b->group > 0) {
            option_menu = create_option_menu( command_id);
        }
        if (option_menu == NULL || b->group > 0) {
            tbb[index].iBitmap = I_IMAGENONE;
            tbb[index].fsState = TBSTATE_ENABLED;
            tbb[index].fsStyle = BTNS_SHOWTEXT|BTNS_AUTOSIZE;
            tbb[index].idCommand = command_id;
            if (option_menu != NULL)
                tbb[index].fsStyle |= BTNS_DROPDOWN;
            tbb[index].iString = SendMessage(hToolBar, TB_ADDSTRING, (WPARAM) 0,
                (LPARAM) (LPSTR) b->title);
            if (option_menu != NULL)
                SendMessage(hToolBar, TB_SETEXTENDEDSTYLE, tbb[index].idCommand, TBSTYLE_EX_DRAWDDARROWS);
            index++;
            tbb[index].fsStyle = BTNS_SEP;
            index++;
        }
        add_option_menu_item( option_menu != NULL ? option_menu : global_menu, command_id, b);
        command_id++;
        if (b->group < 0) {
            option_menu = NULL;
        }
    }

    if(!hToolBar)
        return NULL;

    SendMessage(hToolBar, TB_ADDBUTTONS, index, (LPARAM)tbb);

    {
        SIZE size;
        DWORD buttonsize;
        int toolbarheight;
        int border;
        buttonsize = SendMessage(hToolBar, TB_GETBUTTONSIZE, 0, (LPARAM)&size);
        toolbarheight = HIWORD( buttonsize);
        border = GetSystemMetrics( SM_CXFIXEDFRAME) + GetSystemMetrics( SM_CXBORDER);
        toolbarheight += 2 * border;
        // space for toolbar
        dialog->curheight += toolbarheight;
        mainheight += toolbarheight;
    }
    if (index) {
        RECT rect;
        int newmainwidth;

        SendMessage(hToolBar, TB_GETITEMRECT, index-1, (LPARAM)&rect);
        newmainwidth = rect.right;
        if (newmainwidth > mainwidth) {
            int diff = newmainwidth - mainwidth;
            textwidth += diff;
            buttonwidth += diff / 3;
            morewidth += diff / 3;
            helpwidth += diff / 3;
            filechainwidth += diff * 2 / 3;
            sliderwidth += diff;
            mainwidth = newmainwidth;
            MoveWindow(dialog->parent_window, 0, 0, mainwidth+5, mainheight, 1);
            SetWindowPos( hToolBar, NULL, 0, 0, rect.bottom - rect.top, mainwidth, SWP_NOZORDER | SWP_NOMOVE);
        }
    }

    UpdateWindow(hToolBar);

    return hToolBar;
}


static void win_dialog_create( win_dialog_info_t *win_dialog, HWND hwnd)
{
    HWND w;
    int id;
    win_button_info_t *button_info;

    textwidth = TEXTWIDTH;
    buttonwidth = BUTTONWIDTH;
    morewidth = MOREWIDTH;
    helpwidth = HELPWIDTH;
    filechainwidth = FILECHAINWIDTH;
    sliderwidth = SLIDERWIDTH;

    win_dialog->parent_window = hwnd;
    SetWindowLong( hwnd, GWL_USERDATA, (LONG)win_dialog);
    button_info = win_dialog->button_info;
    if (button_info == NULL) {
        // main window
        toolBar = init_toolbar( win_dialog);
        id = 0;
    } else {
        id = button_info->button->popup_window_id;
    }
    dialog_info_build( &win_widget_api, (dialog_info_t *)win_dialog, id);

	if (button_info != NULL) {
        w = CreateWindow("BUTTON", "Go",  BUTTONFLAGS,
            0, win_dialog->curheight, mainwidth/3, ITEMHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTCOMMANDID + id), hInst, (LPSTR) NULL);
        win_on_create_window( win_dialog, button_info, w);
        w = CreateWindow("BUTTON", "Dismiss",  BUTTONFLAGS,
            mainwidth/3, win_dialog->curheight, mainwidth/3, ITEMHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)IDCANCEL, hInst, (LPSTR) NULL);
        win_on_create_window( win_dialog, button_info, w);
        w = CreateWindow("BUTTON", "Help",  BUTTONFLAGS,
            2*mainwidth/3, win_dialog->curheight, mainwidth/3+1, ITEMHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)(FIRSTHELPID + id), hInst, (LPSTR) NULL);
        win_on_create_window( win_dialog, button_info, w);
	  // create helpfen
#ifdef BUTTONS_ON_LAST_LINE
    } else {
#ifdef INSERT_SEPARATOR_BEFORE_LAST_LINE
        win_dialog->curheight+=ITEMHEIGHT;
#endif
        w = CreateWindow("BUTTON", "GO",  BUTTONFLAGS,
            0, win_dialog->curheight, mainwidth/3, ITEMHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)WIN_MENU_BUTTON_ID, hInst, (LPSTR) NULL);
        win_on_create_window( win_dialog, NULL, w);
        w = CreateWindow("BUTTON", "ABORT",  BUTTONFLAGS,
            mainwidth/3, win_dialog->curheight, mainwidth/3, ITEMHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)WIN_MENU_BUTTON_ID + 1, hInst, (LPSTR) NULL);
        win_on_create_window( win_dialog, NULL, w);
        w = CreateWindow("BUTTON", "HELP",  BUTTONFLAGS,
            2*mainwidth/3, win_dialog->curheight, mainwidth/3+1, ITEMHEIGHT,
            win_dialog->parent_window, (HMENU)(intptr_t)WIN_MENU_BUTTON_ID + 2, hInst, (LPSTR) NULL);
        win_on_create_window( win_dialog, NULL, w);
#endif
	}
}

static void win_dialog_set_initial_focus( win_dialog_info_t *win_dialog)
{
    win_edit_window_info_struct *first_edit_window;

    first_edit_window = dialog_find_first_edit_window( win_dialog);
    if (first_edit_window != NULL)
        SetFocus( first_edit_window->edit_info.edit_window);
}

/* Pops the Floating Windows for menus hidden behind buttons in the Main menu */
static void win_create_option_popup( win_button_info_t *button_info)
{
    HWND w;

    if (button_info->dialog == NULL) {
        RECT rect;

        mainwidth = LABELWIDTH+HORZPAD+TEXTWIDTH;

        button_info->dialog = win_dialog_info_new( button_info);

        rect.left = 0;
        rect.right = mainwidth;
        rect.top = 0;
        rect.bottom = ITEMHEIGHT * (button_info->dialog->_herits.nb_items + 1); // 1 for row of buttons
        AdjustWindowRect(&rect, WS_CAPTION, 0/* Mo Menu */);

        w = CreateWindowEx( WS_EX_CONTEXTHELP,
            TEXT("dialog"), TEXT(button_info->button->moretxt), WS_SYSMENU | WS_CAPTION,
            CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
            NULL, NULL, hInst, NULL);

        win_dialog_create( button_info->dialog, w);
    } else {
        w = button_info->dialog->parent_window;
    }
	ShowWindow( w, SW_SHOWNORMAL);
    SetForegroundWindow( w);
    win_dialog_set_initial_focus( button_info->dialog);
}

static void win_dialog_clean( widget_info_struct *widget_info)
{
    if (widget_info->generic->type == BUTTON) {
        win_button_info_t *button_info = (win_button_info_t *)widget_info;
        win_destroy_window( button_info->help_window);
        if (button_info->dialog != NULL) {
            win_destroy_window( button_info->dialog->parent_window);
            win_dialog_info_delete( button_info->dialog);
        }
    } else if (win_widget_is_edit_window( widget_info)) {
        win_edit_window_info_struct *edit_window = (win_edit_window_info_struct *)widget_info;            
        SetWindowLong( edit_window->edit_info.edit_window, GWL_WNDPROC, (LONG)edit_window->edit_info.original_edit_proc);
    } else if (widget_info->generic->type == CHOICE) {
        win_choice_info_t *choice_info = (win_choice_info_t *)widget_info;
        SetWindowLong( choice_info->choice_window, GWL_WNDPROC, (LONG)choice_info->original_choice_proc);
    }
}

void on_end_dialog( void)
{
    widget_info_close( win_dialog_clean);
    nb_option_menus = 0;
}

void on_open_dialog( const char *helpfilename)
{
}

static void close_dialog( win_dialog_info_t *win_dialog, WORD id)
{
    char return_command[COMMANDLENGTH];

    if (id == WIN_MENU_BUTTON_ID) {
        strcpy( return_command, win_main_command);
    } else if (id >= WIN_MENU_BUTTON_ID + 3) {
        option_t *option = find_option_recur( id);
        strcpy( return_command, option->button->command);
    } else {
        return_command[0] = '\0';
    }

    if (on_close_dialog( &win_widget_api, (dialog_info_t *)win_dialog, return_command, id == WIN_MENU_BUTTON_ID + 1 ? GGUI_ABORT : GGUI_OK) == -1)
        MessageBeep( MB_ICONHAND);
}

static void close_button_dialog( win_dialog_info_t *win_dialog, button_struct *button)
{
    if (on_close_dialog( &win_widget_api, (dialog_info_t *)win_dialog, button->command, widget_get_index( button)) == -1)
        MessageBeep( MB_ICONHAND);
}

static void display_help( win_dialog_info_t *win_dialog, char *help_file, char *variable_name, win_button_info_t *button_info)
{
    static char text[32768];
    int nrows, ncols;

    if (button_info == NULL || button_info->help_window == NULL) {
        static win_button_info_t s_button_info;

        if (button_info == NULL) {
            if (win_dialog->help_button_info == NULL) {
                win_dialog->help_button_info = &s_button_info;
                s_button_info.dialog = NULL;
                s_button_info.help_window = NULL; /* because thread has been killed */
            }
            button_info = win_dialog->help_button_info;
        }
        if (button_info->help_window == NULL)
            button_info->help_window = CreateWindowEx(WS_EX_TOOLWINDOW, TEXT("helpdialog"),
                (LPCTSTR)(variable_name != NULL ? variable_name : "Help"),
                WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                600, 300, (HWND)NULL, (HMENU)NULL, hInst, button_info);

        gdialog_build_help( help_file, variable_name, text, &ncols, &nrows);

    	SendMessage( button_info->help_edit, WM_SETTEXT, 0, (LPARAM)text);
    }
    ShowWindow( button_info->help_window, SW_SHOWNORMAL);
    SetForegroundWindow( button_info->help_window);
}

BOOL DoNotify( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#define lpnm   ((LPNMHDR)lParam)

    RECT      rc;
    TPMPARAMS tpm;
    HMENU     hPopupMenu = NULL;
    BOOL      bRet = FALSE;
    option_menu_t *option_menu;
    int i;
    LPNMTOOLBAR lpnmTB;

    switch (lpnm->code){
    case TBN_DROPDOWN:
        lpnmTB = (LPNMTOOLBAR)lParam;
        option_menu = find_option_menu( lpnmTB->iItem);
        SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT,
            (WPARAM)lpnmTB->iItem, (LPARAM)&rc);

        MapWindowPoints(lpnmTB->hdr.hwndFrom,
            HWND_DESKTOP, (LPPOINT)&rc, 2);

        tpm.cbSize = sizeof(TPMPARAMS);
        tpm.rcExclude = rc;

        hPopupMenu = CreatePopupMenu();
        for (i = 0; i < option_menu->nb_options; i++) {
            option_t *option = &option_menu->options[i];
            AppendMenu( hPopupMenu, MFT_STRING, option->command_id, option->button->title);
        }

        bRet = TrackPopupMenuEx(hPopupMenu,
            TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_VERTICAL|TPM_RETURNCMD,
            rc.left, rc.bottom, hwnd, &tpm);

        if (bRet) {
            option_t *option = find_option( option_menu, bRet);
            TBBUTTONINFO tbinfo;
            ZeroMemory(&tbinfo, sizeof(TBBUTTONINFO));
            tbinfo.cbSize = sizeof(TBBUTTONINFO);
            tbinfo.dwMask = TBIF_TEXT | TBIF_COMMAND;
            tbinfo.idCommand = option->command_id;
            tbinfo.pszText = option->button->title;
            SendMessage(toolBar, TB_SETBUTTONINFO, option_menu->command_id, (LPARAM)&tbinfo);
            option_menu->command_id = option->command_id;
            //UpdateWindow(toolBar);

            SendMessage( hwnd, WM_COMMAND, bRet, (LPARAM)NULL);
        }

        DestroyMenu(hPopupMenu);
        return (FALSE);
    }
    return FALSE;
}

static void win_create_fileselbox( win_file_info_t *file_info, HWND hwnd)
{
    OPENFILENAME ofn;       // common dialog box structure
    char buffer[MAX_PATH];
    char filter[MAX_PATH];
    char *all_files = "All files";
    size_t l;
    file_struct *file;

    file = file_info->file;

    GetWindowText( file_info->edit_info.edit_window, (LPTSTR)buffer, sizeof( buffer));

    // Initialize OPENFILENAME
    ZeroMemory(&ofn, sizeof(OPENFILENAME));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hwnd;
    ofn.lpstrFile = buffer;
    ofn.nMaxFile = sizeof(buffer);
    l = strlen( file->filter);
    if (l > 0) {
	l++;
	strcpy( filter, file->filter);
	strcpy( filter + l, file->filter);
    }
    strcpy( filter + 2 * l, all_files);
    strncpy( filter + 2 * l + strlen(all_files) + 1, "*.*", 5); /* 5 for 2 null terminated character */
    ofn.lpstrFilter = filter;
    //"All\0*.*\0Text\0*.TXT\0";
    ofn.lpstrTitle = file->label;
    ofn.nFilterIndex = 1;
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 0;
    ofn.lpstrInitialDir = NULL;
    ofn.Flags = OFN_PATHMUSTEXIST;

    // Display the Open dialog box.

    if (GetOpenFileNamePreview(&ofn)==TRUE) {
        if ((strstr(ofn.lpstrFile, ".") == 0) && (strlen(ofn.lpstrFilter) != 0))
            sprintf(buffer, "%s.%s", ofn.lpstrFile, ofn.lpstrFilter);
        else
            sprintf(buffer, ofn.lpstrFile);
        file_info_set_value( file_info, buffer);
        update_other_widgets( &win_widget_api, file_info->file);
    }
}

static void win_logic_update( win_logic_info_t *logic_info)
{
    logic_info_get_value( logic_info);
    logic_info_set_value( logic_info, logic_info->logic->userlogic);
    update_other_widgets( &win_widget_api, logic_info->logic);
}

LRESULT APIENTRY MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    POINT pt;
    WORD idcom;
    DWORD dwPos;
    win_slider_info_t *slider_info;
    int NewPos;
    static int PrevPos = -1;
    win_dialog_info_t *win_dialog;
    widget_info_struct *widget_info;

    switch (message) {
    case WM_NOTIFY:
        DoNotify( hwnd, message, wParam, lParam);
        break;

    case WM_DESTROY:
        win_dialog = win_dialog_get_from_window( hwnd);
        if (win_dialog != NULL && win_dialog->button_info == NULL)
            // main window
            close_dialog( win_dialog, WIN_MENU_BUTTON_ID + 1);
        break;

    case WM_CREATE:
        break;

    case WM_HSCROLL:
        dwPos = (DWORD)SendMessage( (HWND)lParam, TBM_GETPOS, 0, 0);
        slider_info = (win_slider_info_t *)GetWindowLong( (HWND)lParam, GWL_USERDATA);
        slider_info->slider->uservalue = (float)slider_value2uservalue( slider_info->slider, dwPos);
        win_edit_window_set_real( slider_info, slider_info->slider->uservalue);
        update_other_widgets( &win_widget_api, slider_info->slider);
        break;

        // Gestion de la scrollbar dans le cas d'une fenetre plus grande que l'ecran
    case WM_VSCROLL:
        switch(LOWORD(wParam)) {
        case SB_PAGEUP:
            yoffset+=ITEMHEIGHT;
            si.nPos-=ITEMHEIGHT;

            yoffset=min(0, yoffset);
            si.nPos = max(0, si.nPos);
            //printf("Page Up %d\n", si.nPos);

            MoveWindow(hWnd, 0, yoffset, mainwidth, mainheight, 1);

            si.fMask  = SIF_POS;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            break;
        case SB_PAGEDOWN:
            yoffset -= ITEMHEIGHT;
            si.nPos += ITEMHEIGHT;

            yoffset = max(viewportheight - mainheight, yoffset);

            si.nPos = min(si.nPos, mainheight-viewportheight);
            //printf("Page Down %d\n", si.nPos);

            MoveWindow(hWnd, 0, yoffset, mainwidth, mainheight, 1);

            si.fMask  = SIF_POS;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            break;
        case SB_LINEUP:
            yoffset+=ITEMHEIGHT;
            si.nPos-=ITEMHEIGHT;

            yoffset=min(0, yoffset);
            si.nPos = max(0, si.nPos);
            //printf("Line Up %d\n", si.nPos);

            MoveWindow(hWnd, 0, yoffset, mainwidth, mainheight, 1);

            si.fMask  = SIF_POS;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            break;
        case SB_LINEDOWN:
            yoffset -= ITEMHEIGHT;
            si.nPos += ITEMHEIGHT;

            yoffset = max(viewportheight - mainheight, yoffset);

            si.nPos = min(si.nPos, mainheight-viewportheight);
            //printf("Line Down %d\n", si.nPos);

            MoveWindow(hWnd, 0, yoffset, mainwidth, mainheight, 1);

            si.fMask  = SIF_POS;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            break;
        case SB_THUMBPOSITION:
            NewPos = HIWORD(wParam);
            yoffset = -(NewPos/ITEMHEIGHT)*ITEMHEIGHT;
            //printf("Thumb %d NewPos %d\n", NewPos, yoffset);

            MoveWindow(hWnd, 0, yoffset, mainwidth, mainheight, 1);

            si.nPos = NewPos;
            si.fMask  = SIF_POS;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            break;

        case SB_THUMBTRACK:
            NewPos = HIWORD(wParam);
            if ((NewPos/ITEMHEIGHT)*ITEMHEIGHT != (PrevPos/ITEMHEIGHT)*ITEMHEIGHT) {
                yoffset = -(NewPos/ITEMHEIGHT)*ITEMHEIGHT;
                PrevPos = NewPos;
                //printf("Default %d\n", yoffset);
                MoveWindow(hWnd, 0, yoffset, mainwidth, mainheight, 1);

                si.nPos = NewPos;
                si.fMask  = SIF_POS;
                SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
            }

            break;
        default:
            break;
        }
        break;

    case WM_HELP:
        win_dialog = win_dialog_get_from_window( hwnd);
        GetCursorPos(&pt);
        ScreenToClient(hwnd, &pt);
        widget_info = dialog_find_widget_info_from_y_offset( win_dialog, pt.y);
        if (win_dialog->button_info == NULL) {
            // main window
            display_help( win_dialog, win_helpfilename, widget_info->generic->variable, NULL);
        } else {
            display_help( win_dialog, ((win_button_info_t *)win_dialog->button_info)->button->helptxt, widget_info->generic->variable, NULL);
        }
        break;

    case WM_COMMAND:
        win_dialog = win_dialog_get_from_window( hwnd);
        if (win_dialog == NULL)
            return DefWindowProc(hwnd, message, wParam, lParam);

        idcom = LOWORD(wParam);
        if (idcom == WIN_MENU_BUTTON_ID + 2) {
            display_help( win_dialog, win_helpfilename, NULL, NULL);
        }
        else if (idcom >= WIN_MENU_BUTTON_ID) {
            close_dialog( win_dialog, idcom);
        }
        else if (idcom == IDCANCEL) {
            ShowWindow(hwnd, SW_HIDE);
        }
        // par ordre de plage d'indice decroissants
        else if (idcom >= FIRSTHELPID) { // id d'un bouton help d'une fenetre optionnelle ou principale
            win_button_info_t *button_info = dialog_find_widget_info_from_id( win_dialog, idcom);
            display_help( win_dialog, button_info->button->helptxt, NULL, button_info);
        }
        else if (idcom >= FIRSTCOMMANDID) {
            win_button_info_t *button_info = dialog_find_widget_info_from_id( win_dialog, idcom);
            close_button_dialog( button_info->dialog, button_info->button);
        }
        else if (idcom >= FIRSTBUTTONID) { // boutons file, logic et more
            widget_info = dialog_find_widget_info_from_id( win_dialog, idcom);

            if (widget_info->generic->type == BROWSER) {
                win_create_fileselbox( (win_file_info_t *)widget_info, hwnd);
            } else if (widget_info->generic->type == LOGIC) {
                win_logic_update( (win_logic_info_t *)widget_info);
            } else if (widget_info->generic->type == BUTTON) {
                // More button pressed
                win_create_option_popup( (win_button_info_t *)widget_info);
            }
        }
        else if (idcom >= FIRSTCHAINID) {
            widget_info = dialog_find_widget_info_from_id( win_dialog, idcom);
            if (widget_info != NULL)
                update_other_widgets( &win_widget_api, widget_info->generic);
        }
        else {
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
        break;

    case WM_SYSCOMMAND:
        if (wParam == SC_CLOSE) {
            win_dialog_info_t *win_dialog = win_dialog_get_from_window( hwnd);
            if (win_dialog->button_info != NULL) {
                // popup window
                ShowWindow(hwnd, SW_HIDE);
                return 0;
            }
        }
        return (DefWindowProc(hwnd, message, wParam, lParam));
        break;

    default:
        return (DefWindowProc(hwnd, message, wParam, lParam));
        break;
    }
  return 0;
}

static void win_update_variable( sic_widget_def_t *widget, void *data)
{
    widget_update_variable( &win_widget_api, widget);
}

void create_widgets( )
{
    MSG    msg;
    HMENU hMenu;
    RECT rect;
    int border;
    win_dialog_info_t *main_dialog;
    int nb_items;

    InitCommonControls(); /* Necessary for TRACKBAR class */

    widget_info_open( );

    on_run_dialog( win_update_variable, NULL);

    main_dialog = win_dialog_info_new( NULL);
    nb_items = main_dialog->_herits.nb_items;

    mainwidth = LABELWIDTH+HORZPAD+TEXTWIDTH;
    mainheight = ITEMHEIGHT * (nb_items
#ifdef BUTTONS_ON_LAST_LINE
#ifdef INSERT_SEPARATOR_BEFORE_LAST_LINE
        + 2
#else
        + 1
#endif
#endif
        );

#ifdef BUTTONS_ON_LAST_LINE
    hMenu = NULL;
#else
    hMenu = LoadMenu(hInst, (LPCTSTR) "dialog");
#endif

    rect.left = 0;
    rect.right = mainwidth;
    rect.top = 0;
    rect.bottom = mainheight;
    // on reajuste rect aux dimensions de la viewport
    AdjustWindowRect(&rect, WS_CAPTION, hMenu != NULL /* Menu present*/);
    if (rect.bottom - rect.top > screenheight) {
        rect.bottom -= rect.bottom - rect.top - screenheight;
        SCB = 1;
    } else {
        SCB = 0;
    }

    if (SCB) {
        FrameWnd = CreateWindowEx(
            WS_EX_CONTEXTHELP, TEXT("dialog"), TEXT(win_window_title),
            WS_SYSMENU | WS_CAPTION | WS_VSCROLL,
            CW_USEDEFAULT, CW_USEDEFAULT,
            rect.right - rect.left + GetSystemMetrics( SM_CXVSCROLL),
            rect.bottom - rect.top + 32, /* toolbar estimation */
            NULL, hMenu, hInst, NULL);

        GetClientRect(FrameWnd, &rect);
        viewportheight = rect.bottom;
        //printf("Client height %d\n", viewportheight);

        border = 0;

        hWnd = CreateWindowEx(
            0, TEXT("dialog"), NULL,
            WS_CHILD | WS_VISIBLE,
            0, 0, mainwidth, mainheight,
            (HWND)FrameWnd, NULL, hInst, NULL);

        si.cbSize = sizeof(si);
        si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
        si.nMin   = 0;
        si.nMax   = mainheight - viewportheight;  // SG: subtract 100
        si.nPage  = 1;
        si.nPos   = 0;
        SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
    }
    else {
        border = GetSystemMetrics( SM_CXFIXEDFRAME) + GetSystemMetrics( SM_CXBORDER);
        mainheight = rect.bottom - rect.top;

        hWnd = CreateWindowEx(
            WS_EX_CONTEXTHELP, TEXT("dialog"), TEXT(win_window_title),
            WS_SYSMENU | WS_CAPTION,
            CW_USEDEFAULT, CW_USEDEFAULT, mainwidth + 2 * border, mainheight,
            NULL, hMenu, hInst, NULL);

        GetClientRect(hWnd, &rect);
        viewportheight = rect.bottom;
    }

    /* printf("wind %d\n", hWnd);*/
    if (!hWnd) {
        gwindows_c_message(seve_e, "WIN-DIALOG", "Create window failed");
        return;
    }

    if (win_dialogfont == NULL) {
        win_dialogfont = CreateFont(14, 0, 0, 0, 800, FALSE, FALSE, FALSE,
            ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
            DEFAULT_QUALITY, DEFAULT_PITCH, "Arial");
    }

    win_dialog_create( main_dialog, hWnd);
    SetWindowPos( hWnd, 0, 0, 0, mainwidth + 2 * border, mainheight, SWP_NOZORDER | SWP_NOMOVE);

    if (SCB) {
        ShowWindow(FrameWnd, SW_SHOWNORMAL);
        /*UpdateWindow(hWnd);*/
        SetForegroundWindow(FrameWnd);
    }
    else {
        ShowWindow(hWnd, SW_SHOWNORMAL);
        /*UpdateWindow(hWnd);*/
        SetForegroundWindow(hWnd);
    }
    win_dialog_set_initial_focus( main_dialog);

    sic_post_widget_created( );

    /* Loop getting messages and dispatching them. */
    while (GetMessage( &msg,NULL, 0, 0)) {
        TranslateMessage( &msg);
        DispatchMessage( &msg);
    }

    on_end_dialog( );
    win_dialog_info_delete( main_dialog);
}

int run_dialog_args( int argc, char **argv)
{
    int nb_widgets;

    nb_widgets = sic_open_widget_board( );

    sic_get_widget_global_infos( win_window_title, win_helpfilename, win_main_command);

    sic_close_widget_board( );

    create_widgets( );

    return 0;
}

int run_dialog( void)
{
     return run_dialog_args( 0, NULL);
}

LRESULT APIENTRY HelpWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    win_button_info_t *button_info;

    button_info = (win_button_info_t *)GetWindowLong( hwnd, GWL_USERDATA);

    switch (message) {
    case WM_CREATE:
        button_info = ((LPCREATESTRUCT)lParam)->lpCreateParams;
        button_info->help_edit = CreateWindow("RICHEDIT", NULL,
            WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER |
            ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY,
            0, 0, 0, 0, /* set size in WM_SIZE message  */
            hwnd, (HMENU)NULL, hInst, NULL);
        SetWindowLong( hwnd, GWL_USERDATA, (LONG)button_info);
        break;
    case WM_DESTROY:
        button_info->help_window = NULL;
        break;
    case WM_SIZE:
        MoveWindow( button_info->help_edit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
        break;
    case WM_CLOSE:
        if (button_info->dialog != NULL)
            ShowWindow( hwnd, SW_HIDE);
        else
            DestroyWindow( hwnd);
        break;
    default:
        return (DefWindowProc( hwnd, message, wParam, lParam));
        break;
    }
    return 0;
}

void init_dialog( HANDLE hInstance)
{
    WNDCLASS  wc;
    HDC hdcScreen;

    hInst = hInstance;

    wc.style = 0;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(LTGRAY_BRUSH);
    wc.lpszMenuName = NULL;

    wc.lpfnWndProc = (WNDPROC)MainWndProc;
    wc.lpszClassName = TEXT("dialog");
    if (!RegisterClass(&wc)) MyErrorHandler("RegisterClass", "DllMain");

    wc.lpfnWndProc = (WNDPROC)HelpWndProc;
    wc.lpszClassName = TEXT("helpdialog");
    if (!RegisterClass(&wc)) MyErrorHandler("RegisterClass", "DllMain");

    hdcScreen = CreateDC("DISPLAY", (LPCSTR) NULL,
        (LPCSTR) NULL, (CONST DEVMODE *) NULL);

    screenheight = GetDeviceCaps(hdcScreen, VERTRES);
    //printf("screenwidth %d screenheight %d\n", screenwidth, screenheight);
    DeleteDC(hdcScreen);

    set_dialog_handler( run_dialog);
    set_on_end_dialog_handler( on_end_dialog);
    set_on_open_dialog_handler( on_open_dialog);

    LoadLibrary("RICHED32.DLL");
}
