/* dialog management for wineconsole
 * USER32 backend
 * Copyright (c) 2001, 2002 Eric Pouech
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "prsht.h"
#include "winecon_user.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(wineconsole);

struct dialog_info
{
    struct config_data  config;         /* configuration used for dialog box */
    struct inner_data*	data;	        /* pointer to current winecon info */
    HWND		hDlg;		/* handle to active propsheet */
    int			nFont;		/* number of font size in size LB */
    struct font_info
    {
        UINT                    height;
        UINT                    weight;
        WCHAR                   faceName[LF_FACESIZE];
    } 			*font;		/* array of nFont. index sync'ed with SIZE LB */
};

/******************************************************************
 *		WCUSER_OptionDlgProc
 *
 * Dialog prop for the option property sheet
 */
static INT_PTR WINAPI WCUSER_OptionDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    struct dialog_info*		di;
    unsigned 			idc;

    switch (msg)
    {
    case WM_INITDIALOG:
	di = (struct dialog_info*)((PROPSHEETPAGEA*)lParam)->lParam;
	di->hDlg = hDlg;
        SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)di);

        SendMessageW(GetDlgItem(hDlg,IDC_OPT_HIST_SIZE_UD), UDM_SETRANGE, 0, MAKELPARAM(500, 0));

	if (di->config.cursor_size <= 25)	idc = IDC_OPT_CURSOR_SMALL;
	else if (di->config.cursor_size <= 50)	idc = IDC_OPT_CURSOR_MEDIUM;
	else				        idc = IDC_OPT_CURSOR_LARGE;
        SendDlgItemMessageW(hDlg, idc, BM_SETCHECK, BST_CHECKED, 0);
	SetDlgItemInt(hDlg, IDC_OPT_HIST_SIZE, di->config.history_size,  FALSE);
        SendDlgItemMessageW(hDlg, IDC_OPT_HIST_NODOUBLE, BM_SETCHECK,
                            (di->config.history_nodup) ? BST_CHECKED : BST_UNCHECKED, 0);
        SendDlgItemMessageW(hDlg, IDC_OPT_CONF_CTRL, BM_SETCHECK,
                            (di->config.menu_mask & MK_CONTROL) ? BST_CHECKED : BST_UNCHECKED, 0);
        SendDlgItemMessageW(hDlg, IDC_OPT_CONF_SHIFT, BM_SETCHECK,
                            (di->config.menu_mask & MK_SHIFT) ? BST_CHECKED : BST_UNCHECKED, 0);
        SendDlgItemMessageW(hDlg, IDC_OPT_QUICK_EDIT, BM_SETCHECK,
                            (di->config.quick_edit) ? BST_CHECKED : BST_UNCHECKED, 0);
	return FALSE; /* because we set the focus */
    case WM_COMMAND:
	break;
    case WM_NOTIFY:
    {
	NMHDR*	nmhdr = (NMHDR*)lParam;
        DWORD   val;
        BOOL	done;

        di = (struct dialog_info*)GetWindowLongPtrW(hDlg, DWLP_USER);

	switch (nmhdr->code)
	{
	case PSN_SETACTIVE:
	    /* needed in propsheet to keep properly the selected radio button
	     * otherwise, the focus would be set to the first tab stop in the
	     * propsheet, which would always activate the first radio button
	     */
	    if (IsDlgButtonChecked(hDlg, IDC_OPT_CURSOR_SMALL) == BST_CHECKED)
		idc = IDC_OPT_CURSOR_SMALL;
	    else if (IsDlgButtonChecked(hDlg, IDC_OPT_CURSOR_MEDIUM) == BST_CHECKED)
		idc = IDC_OPT_CURSOR_MEDIUM;
	    else
		idc = IDC_OPT_CURSOR_LARGE;
            PostMessageW(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, idc), TRUE);
            di->hDlg = hDlg;
	    break;
	case PSN_APPLY:
	    if (IsDlgButtonChecked(hDlg, IDC_OPT_CURSOR_SMALL) == BST_CHECKED) val = 25;
	    else if (IsDlgButtonChecked(hDlg, IDC_OPT_CURSOR_MEDIUM) == BST_CHECKED) val = 50;
	    else val = 100;
            di->config.cursor_size = val;

 	    val = GetDlgItemInt(hDlg, IDC_OPT_HIST_SIZE, &done, FALSE);
	    if (done) di->config.history_size = val;

            val = (IsDlgButtonChecked(hDlg, IDC_OPT_HIST_NODOUBLE) & BST_CHECKED) != 0;
            di->config.history_nodup = val;

            val = 0;
            if (IsDlgButtonChecked(hDlg, IDC_OPT_CONF_CTRL) & BST_CHECKED)  val |= MK_CONTROL;
            if (IsDlgButtonChecked(hDlg, IDC_OPT_CONF_SHIFT) & BST_CHECKED) val |= MK_SHIFT;
            di->config.menu_mask = val;

            val = (IsDlgButtonChecked(hDlg, IDC_OPT_QUICK_EDIT) & BST_CHECKED) != 0;
            di->config.quick_edit = val;

            SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
	    return TRUE;
	default:
	    return FALSE;
	}
	break;
    }
    default:
	return FALSE;
    }
    return TRUE;
}

/******************************************************************
 *		WCUSER_FontPreviewProc
 *
 * Window proc for font previewer in font property sheet
 */
static LRESULT WINAPI WCUSER_FontPreviewProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        SetWindowLongPtrW(hWnd, 0, 0);
        break;
    case WM_GETFONT:
        return GetWindowLongPtrW(hWnd, 0);
    case WM_SETFONT:
        SetWindowLongPtrW(hWnd, 0, wParam);
        if (LOWORD(lParam))
        {
            InvalidateRect(hWnd, NULL, TRUE);
            UpdateWindow(hWnd);
        }
        break;
    case WM_DESTROY:
        {
            HFONT hFont = (HFONT)GetWindowLongPtrW(hWnd, 0);
            if (hFont) DeleteObject(hFont);
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT	        ps;
            int		        size_idx;
            struct dialog_info*	di;
            HFONT	        hFont, hOldFont;

            di = (struct dialog_info*)GetWindowLongPtrW(GetParent(hWnd), DWLP_USER);
            BeginPaint(hWnd, &ps);

            size_idx = SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0, 0);

            hFont = (HFONT)GetWindowLongPtrW(hWnd, 0);
            if (hFont)
            {
                WCHAR ascii[] = {'A','S','C','I','I',':',' ','a','b','c','X','Y','Z','\0'};
                WCHAR buf[256];
                int   len;

                hOldFont = SelectObject(ps.hdc, hFont);
                SetBkColor(ps.hdc, WCUSER_ColorMap[GetWindowLongW(GetDlgItem(di->hDlg, IDC_FNT_COLOR_BK), 0)]);
                SetTextColor(ps.hdc, WCUSER_ColorMap[GetWindowLongW(GetDlgItem(di->hDlg, IDC_FNT_COLOR_FG), 0)]);
                len = LoadStringW(GetModuleHandleW(NULL), IDS_FNT_PREVIEW,
                                  buf, sizeof(buf) / sizeof(buf[0]));
                if (len)
                    TextOutW(ps.hdc, 0, 0, buf, len);
                TextOutW(ps.hdc, 0, di->font[size_idx].height, ascii,
                         sizeof(ascii)/sizeof(ascii[0]) - 1);
                SelectObject(ps.hdc, hOldFont);
            }
            EndPaint(hWnd, &ps);
        }
        break;
    default:
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }
    return 0L;
}

/******************************************************************
 *		WCUSER_ColorPreviewProc
 *
 * Window proc for color previewer in font property sheet
 */
static LRESULT WINAPI WCUSER_ColorPreviewProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_PAINT:
        {
            PAINTSTRUCT     ps;
            int             i, step;
            RECT            client, r;
            HBRUSH          hbr;

            BeginPaint(hWnd, &ps);
            GetClientRect(hWnd, &client);
            step = client.right / 8;

            for (i = 0; i < 16; i++)
            {
                r.top = (i / 8) * (client.bottom / 2);
                r.bottom = r.top + client.bottom / 2;
                r.left = (i & 7) * step;
                r.right = r.left + step;
                hbr = CreateSolidBrush(WCUSER_ColorMap[i]);
                FillRect(ps.hdc, &r, hbr);
                DeleteObject(hbr);
                if (GetWindowLongW(hWnd, 0) == i)
                {
                    HPEN        hOldPen;
                    int         i = 2;

                    hOldPen = SelectObject(ps.hdc, GetStockObject(WHITE_PEN));
                    r.right--; r.bottom--;
                    for (;;)
                    {
                        MoveToEx(ps.hdc, r.left, r.bottom, NULL);
                        LineTo(ps.hdc, r.left, r.top);
                        LineTo(ps.hdc, r.right, r.top);
                        SelectObject(ps.hdc, GetStockObject(BLACK_PEN));
                        LineTo(ps.hdc, r.right, r.bottom);
                        LineTo(ps.hdc, r.left, r.bottom);

                        if (--i == 0) break;
                        r.left++; r.top++; r.right--; r.bottom--;
                        SelectObject(ps.hdc, GetStockObject(WHITE_PEN));
                    }
                    SelectObject(ps.hdc, hOldPen);
                }
            }
            EndPaint(hWnd, &ps);
            break;
        }
    case WM_LBUTTONDOWN:
        {
            int             i, step;
            RECT            client;

            GetClientRect(hWnd, &client);
            step = client.right / 8;
            i = (HIWORD(lParam) >= client.bottom / 2) ? 8 : 0;
            i += LOWORD(lParam) / step;
            SetWindowLongW(hWnd, 0, i);
            InvalidateRect(GetDlgItem(GetParent(hWnd), IDC_FNT_PREVIEW), NULL, FALSE);
            InvalidateRect(hWnd, NULL, FALSE);
        }
        break;
    default:
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }
    return 0L;
}

/******************************************************************
 *		font_enum
 *
 * enumerates all the font names with at least one valid font
 */
static int CALLBACK font_enum_size2(const LOGFONTW* lf, const TEXTMETRICW* tm,
                                    DWORD FontType, LPARAM lParam)
{
    struct dialog_info*	di = (struct dialog_info*)lParam;

    WCUSER_DumpTextMetric(tm, FontType);
    if (WCUSER_ValidateFontMetric(di->data, tm, FontType))
    {
	di->nFont++;
    }

    return 1;
}

static int CALLBACK font_enum(const LOGFONTW* lf, const TEXTMETRICW* tm,
                              DWORD FontType, LPARAM lParam)
{
    struct dialog_info*	di = (struct dialog_info*)lParam;

    WCUSER_DumpLogFont("DlgFamily: ", lf, FontType);
    if (WCUSER_ValidateFont(di->data, lf))
    {
        if (FontType & RASTER_FONTTYPE)
        {
            di->nFont = 0;
            EnumFontFamiliesW(PRIVATE(di->data)->hMemDC, lf->lfFaceName, font_enum_size2, (LPARAM)di);
        }
        else
            di->nFont = 1;

        if (di->nFont)
	{
            SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_FONT, LB_ADDSTRING,
                                0, (LPARAM)lf->lfFaceName);
        }
    }

    return 1;
}

/******************************************************************
 *		font_enum_size
 *
 *
 */
static int CALLBACK font_enum_size(const LOGFONTW* lf, const TEXTMETRICW* tm,
                                   DWORD FontType, LPARAM lParam)
{
    struct dialog_info*	di = (struct dialog_info*)lParam;
    WCHAR	        buf[32];
    static const WCHAR  fmt[] = {'%','l','d',0};

    WCUSER_DumpTextMetric(tm, FontType);
    if (di->nFont == 0 && !(FontType & RASTER_FONTTYPE))
    {
        static const int sizes[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
        int i;

        di->nFont = sizeof(sizes) / sizeof(sizes[0]);
        di->font = HeapAlloc(GetProcessHeap(), 0, di->nFont * sizeof(di->font[0]));
        for (i = 0; i < di->nFont; i++)
        {
            /* drop sizes where window size wouldn't fit on screen */
            if (sizes[i] * di->data->curcfg.win_height > GetSystemMetrics(SM_CYSCREEN))
            {
                di->nFont = i;
                break;
            }
            di->font[i].height = sizes[i];
            di->font[i].weight = 400;
            lstrcpyW(di->font[i].faceName, lf->lfFaceName);
            wsprintfW(buf, fmt, sizes[i]);
            SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_SIZE, LB_INSERTSTRING, i, (LPARAM)buf);
        }
        /* don't need to enumerate other */
        return 0;
    }

    if (WCUSER_ValidateFontMetric(di->data, tm, FontType))
    {
	int	idx;

	/* we want the string to be sorted with a numeric order, not a lexicographic...
	 * do the job by hand... get where to insert the new string
	 */
	for (idx = 0; idx < di->nFont && tm->tmHeight > di->font[idx].height; idx++);
        while (idx < di->nFont &&
               tm->tmHeight == di->font[idx].height &&
               tm->tmWeight > di->font[idx].weight)
            idx++;
        if (idx == di->nFont ||
            tm->tmHeight != di->font[idx].height ||
            tm->tmWeight < di->font[idx].weight)
        {
            /* here we need to add the new entry */
            wsprintfW(buf, fmt, tm->tmHeight);
            SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_SIZE, LB_INSERTSTRING, idx, (LPARAM)buf);

            /* now grow our arrays and insert the values at the same index than in the list box */
            if (di->nFont)
            {
                di->font = HeapReAlloc(GetProcessHeap(), 0, di->font, sizeof(*di->font) * (di->nFont + 1));
                if (idx != di->nFont)
                    memmove(&di->font[idx + 1], &di->font[idx], (di->nFont - idx) * sizeof(*di->font));
            }
            else
                di->font = HeapAlloc(GetProcessHeap(), 0, sizeof(*di->font));
            di->font[idx].height = tm->tmHeight;
            di->font[idx].weight = tm->tmWeight;
            lstrcpyW(di->font[idx].faceName, lf->lfFaceName);
            di->nFont++;
        }
    }
    return 1;
}

/******************************************************************
 *		select_font
 *
 *
 */
static BOOL  select_font(struct dialog_info* di)
{
    int		font_idx, size_idx;
    WCHAR	buf[256];
    WCHAR	fmt[128];
    DWORD_PTR   args[2];
    LOGFONTW    lf;
    HFONT       hFont, hOldFont;
    struct config_data config;

    font_idx = SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_FONT, LB_GETCURSEL, 0, 0);
    size_idx = SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0, 0);

    if (font_idx < 0 || size_idx < 0 || size_idx >= di->nFont)
	return FALSE;

    WCUSER_FillLogFont(&lf, di->font[size_idx].faceName,
                       di->font[size_idx].height, di->font[size_idx].weight);
    hFont = WCUSER_CopyFont(&config, di->data->hWnd, &lf, NULL);
    if (!hFont) return FALSE;

    if (config.cell_height != di->font[size_idx].height)
        WINE_TRACE("mismatched heights (%u<>%u)\n",
                   config.cell_height, di->font[size_idx].height);
    hOldFont = (HFONT)SendDlgItemMessageW(di->hDlg, IDC_FNT_PREVIEW, WM_GETFONT, 0, 0);

    SendDlgItemMessageW(di->hDlg, IDC_FNT_PREVIEW, WM_SETFONT, (WPARAM)hFont, TRUE);
    if (hOldFont) DeleteObject(hOldFont);

    LoadStringW(GetModuleHandleW(NULL), IDS_FNT_DISPLAY, fmt, sizeof(fmt) / sizeof(fmt[0]));
    args[0] = config.cell_width;
    args[1] = config.cell_height;
    FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
                   fmt, 0, 0, buf, sizeof(buf)/sizeof(*buf), (__ms_va_list*)args);

    SendDlgItemMessageW(di->hDlg, IDC_FNT_FONT_INFO, WM_SETTEXT, 0, (LPARAM)buf);

    return TRUE;
}

/******************************************************************
 *		fill_list_size
 *
 * fills the size list box according to selected family in font LB
 */
static BOOL  fill_list_size(struct dialog_info* di, BOOL doInit)
{
    int		idx;
    WCHAR	lfFaceName[LF_FACESIZE];

    idx = SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_FONT, LB_GETCURSEL, 0, 0);
    if (idx < 0) return FALSE;

    SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_FONT, LB_GETTEXT, idx, (LPARAM)lfFaceName);
    SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_SIZE, LB_RESETCONTENT, 0, 0);
    HeapFree(GetProcessHeap(), 0, di->font);
    di->nFont = 0;
    di->font = NULL;

    EnumFontFamiliesW(PRIVATE(di->data)->hMemDC, lfFaceName, font_enum_size, (LPARAM)di);

    if (doInit)
    {
        int     ref = -1;

	for (idx = 0; idx < di->nFont; idx++)
	{
            if (!lstrcmpW(di->font[idx].faceName, di->config.face_name) &&
                di->font[idx].height == di->config.cell_height &&
                di->font[idx].weight == di->config.font_weight)
            {
                if (ref == -1) ref = idx;
                else WINE_TRACE("Several matches found: ref=%d idx=%d\n", ref, idx);
            }
	}
	idx = (ref == -1) ? 0 : ref;
    }
    else
	idx = 0;
    SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_SIZE, LB_SETCURSEL, idx, 0);
    select_font(di);
    return TRUE;
}

/******************************************************************
 *		fill_list_font
 *
 * Fills the font LB
 */
static BOOL fill_list_font(struct dialog_info* di)
{
    SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_FONT, LB_RESETCONTENT, 0, 0);
    EnumFontFamiliesW(PRIVATE(di->data)->hMemDC, NULL, font_enum, (LPARAM)di);
    if (SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_FONT, LB_SELECTSTRING,
                            -1, (LPARAM)di->config.face_name) == LB_ERR)
        SendDlgItemMessageW(di->hDlg, IDC_FNT_LIST_FONT, LB_SETCURSEL, 0, 0);
    fill_list_size(di, TRUE);
    return TRUE;
}

/******************************************************************
 *		WCUSER_FontDlgProc
 *
 * Dialog proc for the Font property sheet
 */
static INT_PTR WINAPI WCUSER_FontDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    struct dialog_info*		di;

    switch (msg)
    {
    case WM_INITDIALOG:
	di = (struct dialog_info*)((PROPSHEETPAGEA*)lParam)->lParam;
	di->hDlg = hDlg;
        SetWindowLongPtrW(hDlg, DWLP_USER, (DWORD_PTR)di);
        /* remove dialog from this control, font will be reset when listboxes are filled */
        SendDlgItemMessageW(hDlg, IDC_FNT_PREVIEW, WM_SETFONT, 0, 0);
	fill_list_font(di);
        SetWindowLongW(GetDlgItem(hDlg, IDC_FNT_COLOR_BK), 0, (di->config.def_attr >> 4) & 0x0F);
        SetWindowLongW(GetDlgItem(hDlg, IDC_FNT_COLOR_FG), 0, di->config.def_attr & 0x0F);
	break;
    case WM_COMMAND:
        di = (struct dialog_info*)GetWindowLongPtrW(hDlg, DWLP_USER);
	switch (LOWORD(wParam))
	{
	case IDC_FNT_LIST_FONT:
	    if (HIWORD(wParam) == LBN_SELCHANGE)
	    {
		fill_list_size(di, FALSE);
	    }
	    break;
	case IDC_FNT_LIST_SIZE:
	    if (HIWORD(wParam) == LBN_SELCHANGE)
	    {
		select_font(di);
	    }
	    break;
	}
	break;
    case WM_NOTIFY:
    {
	NMHDR*	nmhdr = (NMHDR*)lParam;
        DWORD   val;

        di = (struct dialog_info*)GetWindowLongPtrW(hDlg, DWLP_USER);
	switch (nmhdr->code)
	{
        case PSN_SETACTIVE:
            di->hDlg = hDlg;
            break;
	case PSN_APPLY:
            val = SendDlgItemMessageW(hDlg, IDC_FNT_LIST_SIZE, LB_GETCURSEL, 0, 0);

	    if (val < di->nFont)
            {
                LOGFONTW lf;

                WCUSER_FillLogFont(&lf, di->font[val].faceName,
                                   di->font[val].height, di->font[val].weight);
                DeleteObject(WCUSER_CopyFont(&di->config,
                                             di->data->hWnd, &lf, NULL));
            }

            val = (GetWindowLongW(GetDlgItem(hDlg, IDC_FNT_COLOR_BK), 0) << 4) |
                   GetWindowLongW(GetDlgItem(hDlg, IDC_FNT_COLOR_FG), 0);
            di->config.def_attr = val;

            SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
	    return TRUE;
	default:
	    return FALSE;
	}
	break;
    }
    default:
	return FALSE;
    }
    return TRUE;
}

/******************************************************************
 *		WCUSER_ConfigDlgProc
 *
 * Dialog proc for the config property sheet
 */
static INT_PTR WINAPI WCUSER_ConfigDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    struct dialog_info*		di;
    int                     nMaxUD = 2000;

    switch (msg)
    {
    case WM_INITDIALOG:
        di = (struct dialog_info*)((PROPSHEETPAGEA*)lParam)->lParam;
        di->hDlg = hDlg;

        SetWindowLongPtrW(hDlg, DWLP_USER, (DWORD_PTR)di);
        SetDlgItemInt(hDlg, IDC_CNF_SB_WIDTH,   di->config.sb_width,   FALSE);
        SetDlgItemInt(hDlg, IDC_CNF_SB_HEIGHT,  di->config.sb_height,  FALSE);
        SetDlgItemInt(hDlg, IDC_CNF_WIN_WIDTH,  di->config.win_width,  FALSE);
        SetDlgItemInt(hDlg, IDC_CNF_WIN_HEIGHT, di->config.win_height, FALSE);

        SendMessageW(GetDlgItem(hDlg,IDC_CNF_WIN_HEIGHT_UD), UDM_SETRANGE, 0, MAKELPARAM(nMaxUD, 0));
        SendMessageW(GetDlgItem(hDlg,IDC_CNF_WIN_WIDTH_UD), UDM_SETRANGE, 0, MAKELPARAM(nMaxUD, 0));
        SendMessageW(GetDlgItem(hDlg,IDC_CNF_SB_HEIGHT_UD), UDM_SETRANGE, 0, MAKELPARAM(nMaxUD, 0));
        SendMessageW(GetDlgItem(hDlg,IDC_CNF_SB_WIDTH_UD), UDM_SETRANGE, 0, MAKELPARAM(nMaxUD, 0));

        SendDlgItemMessageW(hDlg, IDC_CNF_CLOSE_EXIT, BM_SETCHECK,
                            (di->config.exit_on_die) ? BST_CHECKED : BST_UNCHECKED, 0);
        {
            static const WCHAR s1[] = {'W','i','n','3','2',0};
            static const WCHAR s2[] = {'E','m','a','c','s',0};

            SendDlgItemMessageW(hDlg, IDC_CNF_EDITION_MODE, CB_ADDSTRING, 0, (LPARAM)s1);
            SendDlgItemMessageW(hDlg, IDC_CNF_EDITION_MODE, CB_ADDSTRING, 0, (LPARAM)s2);
            SendDlgItemMessageW(hDlg, IDC_CNF_EDITION_MODE, CB_SETCURSEL, di->config.edition_mode, 0);
    	}

	break;
    case WM_COMMAND:
        di = (struct dialog_info*)GetWindowLongPtrW(hDlg, DWLP_USER);
	switch (LOWORD(wParam))
	{
	}
	break;
    case WM_NOTIFY:
    {
	NMHDR*	        nmhdr = (NMHDR*)lParam;
        int             win_w, win_h, sb_w, sb_h;
        BOOL            st1, st2;

        di = (struct dialog_info*)GetWindowLongPtrW(hDlg, DWLP_USER);
	switch (nmhdr->code)
	{
        case PSN_SETACTIVE:
            di->hDlg = hDlg;
            break;
	case PSN_APPLY:
            sb_w = GetDlgItemInt(hDlg, IDC_CNF_SB_WIDTH,  &st1, FALSE);
            sb_h = GetDlgItemInt(hDlg, IDC_CNF_SB_HEIGHT, &st2, FALSE);
            if (!st1 || ! st2)
            {
                SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, PSNRET_INVALID);
                return TRUE;
            }
            win_w = GetDlgItemInt(hDlg, IDC_CNF_WIN_WIDTH,  &st1, FALSE);
            win_h = GetDlgItemInt(hDlg, IDC_CNF_WIN_HEIGHT, &st2, FALSE);
            if (!st1 || !st2)
            {
                SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, PSNRET_INVALID);
                return TRUE;
            }
            if (win_w > sb_w || win_h > sb_h)
            {
                WCHAR   cap[256];
                WCHAR   txt[256];

                LoadStringW(GetModuleHandleW(NULL), IDS_DLG_TIT_ERROR,
                            cap, sizeof(cap) / sizeof(cap[0]));
                LoadStringW(GetModuleHandleW(NULL), IDS_DLG_ERR_SBWINSIZE,
                            txt, sizeof(txt) / sizeof(cap[0]));

                MessageBoxW(hDlg, txt, cap, MB_OK);
                SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, PSNRET_INVALID);
                return TRUE;
            }
            di->config.win_width  = win_w;
            di->config.win_height = win_h;
            di->config.sb_width  = sb_w;
            di->config.sb_height = sb_h;

            di->config.exit_on_die = IsDlgButtonChecked(hDlg, IDC_CNF_CLOSE_EXIT) ? 1 : 0;
            di->config.edition_mode = SendDlgItemMessageW(hDlg, IDC_CNF_EDITION_MODE,
                                                          CB_GETCURSEL, 0, 0);
            SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
	    return TRUE;
	default:
	    return FALSE;
	}
	break;
    }
    default:
	return FALSE;
    }
    return TRUE;
}

/******************************************************************
 *		WCUSER_SaveDlgProc
 *
 *      Dialog Procedure for choosing how to handle modification to the
 * console settings.
 */
static INT_PTR WINAPI WCUSER_SaveDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
        SendDlgItemMessageW(hDlg, IDC_SAV_SESSION, BM_SETCHECK, BST_CHECKED, 0);
	break;
    case WM_COMMAND:
	switch (LOWORD(wParam))
	{
        case IDOK:
            EndDialog(hDlg,
                      (IsDlgButtonChecked(hDlg, IDC_SAV_SAVE) == BST_CHECKED) ?
                      IDC_SAV_SAVE : IDC_SAV_SESSION);
            break;
        case IDCANCEL:
            EndDialog(hDlg, IDCANCEL); break;
	}
        break;
    default:
        return FALSE;
    }
    return TRUE;
}

/******************************************************************
 *		WCUSER_GetProperties
 *
 * Runs the dialog box to set up the wineconsole options
 */
BOOL WCUSER_GetProperties(struct inner_data* data, BOOL current)
{
    HPROPSHEETPAGE   psPage[3];
    PROPSHEETPAGEW   psp;
    PROPSHEETHEADERW psHead;
    WCHAR            buff[256];
    WNDCLASSW        wndclass;
    static const WCHAR szFntPreview[] = {'W','i','n','e','C','o','n','F','o','n','t','P','r','e','v','i','e','w',0};
    static const WCHAR szColorPreview[] = {'W','i','n','e','C','o','n','C','o','l','o','r','P','r','e','v','i','e','w',0};
    struct dialog_info	di;
    struct config_data  defcfg;
    struct config_data* refcfg;
    BOOL                save, modify_session;

    InitCommonControls();

    di.data = data;
    if (current)
    {
        refcfg = &data->curcfg;
        save = FALSE;
    }
    else
    {
        WINECON_RegLoad(NULL, refcfg = &defcfg);
        save = TRUE;
    }
    di.config = *refcfg;
    di.nFont = 0;
    di.font = NULL;

    modify_session = FALSE;

    wndclass.style         = 0;
    wndclass.lpfnWndProc   = WCUSER_FontPreviewProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = sizeof (DWORD_PTR); /* for hFont */
    wndclass.hInstance     = GetModuleHandleW(NULL);
    wndclass.hIcon         = 0;
    wndclass.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
    wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = szFntPreview;
    RegisterClassW(&wndclass);

    wndclass.style         = 0;
    wndclass.lpfnWndProc   = WCUSER_ColorPreviewProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = sizeof(DWORD);
    wndclass.hInstance     = GetModuleHandleW(NULL);
    wndclass.hIcon         = 0;
    wndclass.hCursor       = LoadCursorW(0, (LPCWSTR)IDC_ARROW);
    wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = szColorPreview;
    RegisterClassW(&wndclass);

    memset(&psp, 0, sizeof(psp));
    psp.dwSize = sizeof(psp);
    psp.dwFlags = 0;
    psp.hInstance = wndclass.hInstance;
    psp.lParam = (LPARAM)&di;

    psp.u.pszTemplate = MAKEINTRESOURCEW(IDD_OPTION);
    psp.pfnDlgProc = WCUSER_OptionDlgProc;
    psPage[0] = CreatePropertySheetPageW(&psp);

    psp.u.pszTemplate = MAKEINTRESOURCEW(IDD_FONT);
    psp.pfnDlgProc = WCUSER_FontDlgProc;
    psPage[1] = CreatePropertySheetPageW(&psp);

    psp.u.pszTemplate = MAKEINTRESOURCEW(IDD_CONFIG);
    psp.pfnDlgProc = WCUSER_ConfigDlgProc;
    psPage[2] = CreatePropertySheetPageW(&psp);

    memset(&psHead, 0, sizeof(psHead));
    psHead.dwSize = sizeof(psHead);

    if (!LoadStringW(GetModuleHandleW(NULL),
                     (current) ? IDS_DLG_TIT_CURRENT : IDS_DLG_TIT_DEFAULT,
                     buff, sizeof(buff) / sizeof(buff[0])))
    {
        buff[0] = 'S';
        buff[1] = 'e';
        buff[2] = 't';
        buff[3] = 'u';
        buff[4] = 'p';
        buff[5] = '\0';
    }

    psHead.pszCaption = buff;
    psHead.nPages = 3;
    psHead.hwndParent = data->hWnd;
    psHead.u3.phpage = psPage;
    psHead.dwFlags = PSH_NOAPPLYNOW;

    WINECON_DumpConfig("init", refcfg);

    PropertySheetW(&psHead);

    if (memcmp(refcfg, &di.config, sizeof(*refcfg)) == 0)
        return TRUE;

    WINECON_DumpConfig("ref", refcfg);
    WINECON_DumpConfig("cur", &di.config);
    if (refcfg == &data->curcfg)
    {
        switch (DialogBoxW(GetModuleHandleW(NULL), MAKEINTRESOURCEW(IDD_SAVE_SETTINGS),
                           data->hWnd, WCUSER_SaveDlgProc))
        {
        case IDC_SAV_SAVE:      save = TRUE; modify_session = TRUE; break;
        case IDC_SAV_SESSION:   modify_session = TRUE; break;
        case IDCANCEL:          break;
        default: WINE_ERR("ooch\n");
        }
    }

    if (modify_session) WINECON_SetConfig(data, &di.config);
    if (save)           WINECON_RegSave(&di.config);

    return TRUE;
}