/* mswdlg - Low Level Dialog Objects for Microsoft Windows             */
/* XLISP-STAT 2.1 Copyright (c) 1990, 1991, by Luke Tierney            */
/* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz    */
/* You may give out copies of this software; for conditions see the    */
/* file COPYING included with this distribution.                       */

#include "dialogs.h"
#include "wxlisp.h"
#include "ledit.h"

/* external variables */
extern HWND hWndFrame, hWndClient;
extern HINSTANCE hInst;
extern HANDLE hAccel;
extern LVAL s_text_length;

/* static variables */
static char szXLSDlgClass[] = "XLSDialog";
static char szXLSMDIDlgClass[] = "XLSMDIDialog";
static char szXLSMDlgClass[] = "XLSModalDialog";
static BOOL InModalDialog = FALSE;
static LVAL ModalItem = NIL;
#ifdef WIN32
static WNDPROC pDefBtnProc = NULL;
static WNDPROC pDefEditTextProc = NULL;
static WNDPROC pDefScrollProc = NULL;
static WNDPROC pDefListProc = NULL;
#endif /* WIN32 */

/* function prototypes */
LRESULT CALLBACK XLSDlgProc(HWND, UINT, WPARAM, LONG);
LRESULT CALLBACK XLSMDIDlgProc(HWND, UINT, WPARAM, LONG);
LRESULT CALLBACK XLSMDlgProc(HWND, UINT, WPARAM, LONG);
static void InstallItem(HWND, LVAL);
static void InstallButtonItem(HWND, LVAL);
static void InstallToggleItem(HWND, LVAL);
static void InstallChoiceItem(HWND, LVAL);
static void InstallTextItem(HWND, LVAL);
static void InstallScrollItem(HWND, LVAL);
static void InstallListItem(HWND, LVAL);
static void SetClusterValue(HWND, int);
#ifdef WIN32
static void DialogDoDefaultButton(LVAL dialog);
static LRESULT CALLBACK CustomButtonProc(HWND hWnd,UINT message,WPARAM wPrm,LPARAM lPrm);
static LRESULT CALLBACK CustomEditTextProc(HWND hWnd,UINT message,WPARAM wPrm,LPARAM lPrm);
static LRESULT CALLBACK CustomScrollProc(HWND hWnd,UINT message,WPARAM wPrm,LPARAM lPrm);
static LRESULT CALLBACK CustomListProc(HWND hWnd,UINT message,WPARAM wPrm,LPARAM lPrm);
#endif /* WIN32 */

#ifdef WIN32
#  define DIALOG_FONT DEFAULT_GUI_FONT
#  define DIALOG_BACKGROUND COLOR_MENU
#  define AVE_CHAR_PAD 2
#  define CHOICE_WIDTH_FUZZ 6
#else
#  define DIALOG_FONT SYSTEM_FIXED_FONT
#  define DIALOG_BACKGROUND COLOR_WINDOW
#  define AVE_CHAR_PAD 0
#  define CHOICE_WIDTH_FUZZ 4
#endif
#define LIST_USE_LEADING FALSE  /**** inline this */
#define REAL_LIST_ITEM_PAD 0 /**** drop LIST_ITEM_PAD */
/**** need to rethink size calculations for non-fixed-width fonts */


/***********************************************************************/
/**                                                                   **/
/**                          Utility Functions                        **/
/**                                                                   **/
/***********************************************************************/

static void check_alloc(HANDLE p)
{
  if (! p) xlfail("allocation failed");
}

static void check_lock(void *p)
{
  if (! p) xlfail("lock failed");
}

static int FindItemType(LVAL item)
{
  if (consp(item)) return(ITEM_LIST);
  else if (button_item_p(item)) return(BUTTON_ITEM);
  else if (toggle_item_p(item)) return(TOGGLE_ITEM);
  else if (text_item_p(item)) return(TEXT_ITEM);
  else if (choice_item_p(item)) return(CHOICE_ITEM);
  else if (scroll_item_p(item)) return(SCROLL_ITEM);
  else if (list_item_p(item)) return(LIST_ITEM);
  else xlfail("item of unknown type");
  return(0);
}

static int count_hardware_items(LVAL items)
{
  LVAL temp;
  int n;

  if (! consp (items)) return(0);
  else {
    for (n = 0; consp(items); items = cdr(items)) {
      switch(FindItemType(car(items))) {
      case CHOICE_ITEM: 
	temp = slot_value(car(items), s_text);
	if (! consp(temp)) xlerror("not a list", temp);
	n += (int) llength(temp);
        break;
      case ITEM_LIST: n += count_hardware_items(car(items)); break;
      default: n += 1;
      }
    }
  }
  return(n);
}

static Point AvCharSize(BOOL leaded)
{
  Point pt;
  HDC hDC;
  TEXTMETRIC tm;

  hDC = GetDC(hWndFrame);
  SelectObject(hDC, GetStockObject(DIALOG_FONT));
  GetTextMetrics(hDC, &tm);
  pt.h = tm.tmAveCharWidth + AVE_CHAR_PAD; /* to correct font width problems */
  pt.v = tm.tmHeight;
  if (leaded) pt.v += tm.tmExternalLeading;
  ReleaseDC(hWndFrame, hDC);
  return(pt);
}

static Point text_size(char *s)
{
  char *bp;
  int w;
  Point sz;
  
  for (w = 0, sz.h = 0, sz.v = 0; *s != '\0'; s++) {
    for (bp = buf; *s != '\0' && *s != '\r' && *s != '\n'; s++, bp++)
      *bp = *s;
    *bp = '\0';
    w = strlen(buf);
    if (sz.h < w) sz.h = w;
    sz.v++;
    if (*s == '\0') break;
  }
  return(sz);
}

static void truncateListEntry(char *s)
{
  if (strlen(s) > MAX_ENTRY_LENGTH) {
    s = s + MAX_ENTRY_LENGTH - 3;
    s[0] = '.'; s[1] = '.'; s[2] = '.'; s[3] = '\0';
  }
}

static BOOL FindItemData(HWND theDialog,
			 int wParam,
			 DialogItemData *di)
{
  HANDLE hd;
  DialogData *dd;
  DialogItemData *did;
  int index, result;

  index = wParam - ITEM_INDEX_BASE;
  if (index < 0) return(FALSE);

  hd = GETDIALOGDATA(theDialog);
  dd = (DialogData *) GlobalLock(hd);
  check_lock(dd);
  did = GETITEMDATA(dd);
  if (index < dd->count) {
    *di = did[index];
    result = TRUE;
  }
  else result = FALSE;
  GlobalUnlock(hd);
  return(result);
}

static BOOL FindItemObjectData(LVAL item, DialogItemData *di)
{
  LVAL dialog;
  HANDLE hd;
  DialogData *dd;
  DialogItemData *did;
  int i, found;

  dialog = slot_value(item, s_dialog);
  if (dialog == NIL || ! check_dialog_address(dialog)) return(FALSE);

  hd = GETDIALOGDATA(GETDIALOGADDRESS(dialog));
  dd = (DialogData *) GlobalLock(hd);
  check_lock(dd);
  did = GETITEMDATA(dd);
  for (i = 0, found = FALSE; i < dd->count && ! found; i++) {
     if (did[i].object == item) {
       *di = did[i];
      found = TRUE;
    }
  }
  GlobalUnlock(hd);
  return(found);
}

/***********************************************************************/
/**                                                                   **/
/**                    Internal Dialog Data Functions                 **/
/**                                                                   **/
/***********************************************************************/

static HANDLE MakeDialogData(LVAL dialog)
{
  int numItems;
  HANDLE hItems;
  DialogData *dd;

  numItems = count_hardware_items(slot_value(dialog, s_items));
  hItems = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
		       sizeof(DialogData) + numItems * sizeof(DialogItemData));
  check_alloc(hItems);
  dd = (DialogData *) GlobalLock(hItems);
  check_lock(dd);
  dd->count = 0;
  dd->object = dialog;
  dd->dflt = -1;
  GlobalUnlock(hItems);
  return(hItems);
}

static void FreeDialogData(HANDLE hItems)
{
  GlobalFree(hItems);
}

static void InstallItemList(HWND theDialog, LVAL items)
{
  for (; consp(items); items = cdr(items))
    if (consp(car(items))) InstallItemList(theDialog, car(items));
    else InstallItem(theDialog, car(items));
}

static void InstallItem(HWND theDialog, LVAL item)
{
  if (! dialog_item_p(item)) xlerror("not a dialog item", item);
  switch (FindItemType(item)) {
  case BUTTON_ITEM: InstallButtonItem(theDialog, item); break;
  case TOGGLE_ITEM: InstallToggleItem(theDialog, item); break;
  case CHOICE_ITEM: InstallChoiceItem(theDialog, item); break;
  case TEXT_ITEM:   InstallTextItem(theDialog, item);   break;
  case SCROLL_ITEM: InstallScrollItem(theDialog, item); break;
  case LIST_ITEM:   InstallListItem(theDialog, item);   break;
  default: xlfail("unkown item type");
  }
}

/***********************************************************************/
/**                                                                   **/
/**                Initialization and Callback Functions              **/
/**                                                                   **/
/***********************************************************************/

BOOL InitApplDialogs(HANDLE hInstance)
{
  WNDCLASS wc;

  /* class structure for modeless dialog windows */
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = (WNDPROC) XLSDlgProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = sizeof(LONG) + sizeof(LONG);
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(hInstance, "WXLSIcon");
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (DIALOG_BACKGROUND + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = szXLSDlgClass;
  if (! RegisterClass(&wc)) return(FALSE);

  /* class structure for modeless MDI dialog windows */
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = (WNDPROC) XLSMDIDlgProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = sizeof(LONG) + sizeof(LONG);
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(hInstance, "WXLSIcon");
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (DIALOG_BACKGROUND + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = szXLSMDIDlgClass;
  if (! RegisterClass(&wc)) return(FALSE);

  /* class structure for modal dialog windows */
  wc.style = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc = (WNDPROC) XLSMDlgProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = sizeof(LONG) + sizeof(LONG);
  wc.hInstance = hInstance;
  wc.hIcon = NULL;
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (DIALOG_BACKGROUND + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = szXLSMDlgClass;
  if (! RegisterClass(&wc)) return(FALSE);
#ifdef WIN32
  //
  // these procs are used to pass the ENTER key on to the parent window to
  // mimic default button modal dialog behavior
  //
  GetClassInfo(hInstance, "button", &wc);
  pDefBtnProc = wc.lpfnWndProc;

  GetClassInfo(hInstance, "edit", &wc);
  pDefEditTextProc = wc.lpfnWndProc;

  GetClassInfo(hInstance, "scrollbar", &wc);
  pDefScrollProc = wc.lpfnWndProc;

  GetClassInfo(hInstance, "listbox", &wc);
  pDefListProc = wc.lpfnWndProc;
#endif /* WIN32 */
  return(TRUE);
}

int InitInstDialogs(HANDLE hInstance)
{
  return(TRUE);
}

static LRESULT ProcessDialogEvent(HWND hWnd,
  		                  UINT message,
			          WPARAM wPrm,
			          LONG lPrm,
                                  BOOL modeless,
				  BOOL mdidialog)
{

  LVAL dialog;
  DialogItemData di;
  int val, wid;

  switch(message) {
#ifdef WIN32
    /* Set the background color for WIN32 */
  case WM_CTLCOLORDLG:
  case WM_CTLCOLORSTATIC:
  case WM_CTLCOLORBTN:
    val = SetBkColor((HDC) wPrm, GetSysColor(DIALOG_BACKGROUND));
    SelectObject((HDC) wPrm, GetStockObject(DIALOG_FONT));
    return ((LRESULT) GetSysColorBrush(DIALOG_BACKGROUND));
#endif
  case WM_CREATE:
    if (mdidialog) {
      LPMDICREATESTRUCT mdic;
      mdic = (LPMDICREATESTRUCT) ((LPCREATESTRUCT) lPrm)->lpCreateParams;
      dialog = (LVAL) mdic->lParam;
    }
    else
      dialog = (LVAL) ((LPCREATESTRUCT) lPrm)->lpCreateParams;
    SETDIALOGOBJECT(hWnd, dialog);
    SETDIALOGDATA(hWnd, MakeDialogData(dialog));
    InstallItemList(hWnd, slot_value(dialog, s_items));
    DialogSetDefaultButton(dialog, slot_value(dialog, s_default_button));
    break;
  case WM_COMMAND:
    switch (GET_WM_COMMAND_ID(wPrm, lPrm)) {
    case IDC_SHOWWINDOW:
      if (modeless) {
	if (mdidialog) {
	  if (IsIconic(hWnd))
	    MDIRestoreWindow(hWndClient, hWnd);
	  MDIActivateWindow(hWndClient, hWnd);
	}
	else {
	  if (IsIconic(hWnd))
	    ShowWindow(hWnd,SW_RESTORE);
	}
        return(0);
      }
      break;
    case IDC_HIDEWINDOW:
      if (modeless) {
        if (! IsIconic(hWnd))
	  ShowWindow(hWnd, SW_MINIMIZE);
        return(0);
      }
      break;
    case IDC_DESTROY:
      if (mdidialog)
        MDIDestroyWindow(hWndClient, hWnd);
      else
        DestroyWindow(hWnd);
      return 0;
    default:
      if (FindItemData(hWnd, GET_WM_COMMAND_ID(wPrm, lPrm), &di)) {
	if (di.object && di.object != NIL) {
	  switch (di.type) {
	  case TOGGLE_ITEM:
	    val = ! Button_GetCheck(di.itemHandle);
	    Button_SetCheck(di.itemHandle, val);
	    set_slot_value(di.object, s_value, (val) ? s_true : NIL);
	    break;
	  case CHOICE_ITEM:
	    SetClusterValue(hWnd, di.itemNumber);
	    set_slot_value(di.object, s_value,
			   cvfixnum((FIXTYPE) di.itemNumber - di.clusterLeader));
	    break;
	  }
	  if (di.type == LIST_ITEM) {
	    switch (GET_WM_COMMAND_CMD(wPrm, lPrm)) {
	    case LBN_DBLCLK:
	      send_callback_message_1L(di.object, sk_do_action, s_true);
	      break;
	    case LBN_SELCHANGE:
	    case LBN_SELCANCEL:
	      send_callback_message(di.object, sk_do_action);
	      break;
	    }
	  }
	  else {
	    if (InModalDialog) ModalItem = di.object;
	    else send_callback_message(di.object, sk_do_action);
	  }
	}
	return(0);
      }
    }
    break;
  case WM_VSCROLL:
  case WM_HSCROLL:
    wid = GetWindowID(GET_WM_HSCROLL_HWND(wPrm, lPrm));
    switch (GET_WM_HSCROLL_CODE(wPrm, lPrm)) {
    case SB_PAGEDOWN:
    case SB_LINEDOWN:
    case SB_PAGEUP:
    case SB_LINEUP:
    case SB_TOP:
    case SB_BOTTOM:
      if (FindItemData(hWnd, wid, &di) && di.object && di.object != NIL) {
	int low, high, value, page;
	LVAL temp, item = di.object;

	temp = slot_value(item, s_min_value);
	low = fixp(temp) ? (int) getfixnum(temp) : SCROLL_MIN;
	temp = slot_value(item, s_max_value);
	high = fixp(temp) ? (int) getfixnum(temp) : SCROLL_MAX;
	temp = slot_value(item, s_value);
	value = (fixp(temp)) ? (int) getfixnum(temp) : low;
	temp = slot_value(item, s_page_increment);
	page = (fixp(temp)) ? (int) getfixnum(temp) : SCROLL_PAGE;
	switch (GET_WM_HSCROLL_CODE(wPrm, lPrm)) {
	case SB_PAGEDOWN: value += page; break;
	case SB_LINEDOWN: value++;       break;
	case SB_PAGEUP:   value -= page; break;
	case SB_LINEUP:   value--;       break;
	case SB_TOP:      value = low;   break;
	case SB_BOTTOM:   value = high;  break;
	}
	if (value < low) value = low;
	if (value > high) value = high;
	set_slot_value(item, s_value, cvfixnum((FIXTYPE) value));
	SetScrollPos(di.itemHandle, SB_CTL, value, TRUE);
	send_callback_message(item, sk_scroll_action);
      }
      return(0);
    case SB_THUMBPOSITION:
      val = GET_WM_HSCROLL_POS(wPrm, lPrm);
      if (FindItemData(hWnd, wid, &di) && di.object && di.object != NIL) {
	set_slot_value(di.object, s_value, cvfixnum((FIXTYPE) val));
	SetScrollPos(di.itemHandle, SB_CTL, val, TRUE);
	send_callback_message(di.object, sk_do_action);
      }
      return(0);
    }
    break;
#ifdef WIN32
  case WM_CHAR:
    switch (wPrm) {
    case VK_RETURN:
      // do the default button thing
      DialogDoDefaultButton(GETDIALOGOBJECT(hWnd));
      break;
    case VK_ESCAPE:
      DestroyWindow(hWnd);
      break;
    }
    break;
#endif /* WIN32 */
  case WM_QUERYENDSESSION:
  case WM_CLOSE:
    if (! InModalDialog)
      send_callback_message((LVAL) GETDIALOGOBJECT(hWnd), sk_close);
    return(0);
  case WM_DESTROY:
    FreeDialogData(GETDIALOGDATA(hWnd));
    if (mdidialog) return 0;
  }
  if (mdidialog)
    return(DefMDIChildProc(hWnd, message, wPrm, lPrm));
  else
    return(DefWindowProc(hWnd, message, wPrm, lPrm));
}

LRESULT CALLBACK XLSDlgProc(HWND hWnd, UINT message, WPARAM wPrm, LONG lPrm)
{
  return ProcessDialogEvent(hWnd, message, wPrm, lPrm, TRUE, FALSE);
}

LRESULT CALLBACK XLSMDIDlgProc(HWND hWnd, UINT message, WPARAM wPrm, LONG lPrm)
{
  return ProcessDialogEvent(hWnd, message, wPrm, lPrm, TRUE, TRUE);
}

LRESULT CALLBACK XLSMDlgProc(HWND hWnd, UINT message, WPARAM wPrm, LONG lPrm)
{
  return ProcessDialogEvent(hWnd, message, wPrm, lPrm, FALSE, FALSE);
}

void MSWResetDialogs(void)
{
  ModalItem = NIL;
  InModalDialog = FALSE;
}

/***********************************************************************/
/**                                                                   **/
/**                Allocation and Deallocation Functions              **/
/**                                                                   **/
/***********************************************************************/

void DialogAllocate(LVAL dialog)
{
  HWND theDialog;
  Point loc, size;
  char *title;
  BOOL goAway, modeless;
  DWORD flags;

  if (check_dialog_address(dialog)) DialogRemove(dialog);

  if (! stringp(slot_value(dialog, s_title)))
    xlerror("not a string", slot_value(dialog, s_title));
  title = (char *) getstring(slot_value(dialog, s_title));

  goAway = (slot_value(dialog, s_go_away) != NIL) ? TRUE : FALSE;
  modeless = (slot_value(dialog, s_type) != s_modal) ? TRUE : FALSE;

  loc = ListToPoint(slot_value(dialog, s_location));
  size = ListToPoint(slot_value(dialog, s_size));
  if (size.h < MIN_DLG_WIDTH) size.h = MIN_DLG_WIDTH;
  if (size.v < MIN_DLG_HEIGHT) size.v = MIN_DLG_HEIGHT;
  if (modeless) { // ### fix if manage to get proper frame
    /**** This is a quick hack.  It would probably be better to use a slot
	  to set the MDI/non-MDI option */
    LVAL topsym = xlenter("SYSTEM::*TOP-LEVEL-DIALOGS*");
    int mdidialog = (! boundp(topsym)) || null(getvalue(topsym));

    size.h += 2 * GetSystemMetrics(SM_CXBORDER);
    size.v += GetSystemMetrics(SM_CYBORDER);
    size.v += GetSystemMetrics(SM_CYCAPTION);

    // ### kill border so dlgframe works?
    flags = WS_POPUP | WS_DLGFRAME | WS_VISIBLE | WS_CAPTION;
    if (goAway) flags |= WS_SYSMENU;

    if (mdidialog) {
      MDICREATESTRUCT mdicreate;

      mdicreate.szClass = szXLSMDIDlgClass;
      mdicreate.szTitle = title;
      mdicreate.hOwner = hInst;
      mdicreate.x = loc.h;
      mdicreate.y = loc.v;
      mdicreate.cx = size.h;
      mdicreate.cy = size.v;
      mdicreate.style = WS_MINIMIZE;
      mdicreate.lParam = (LONG) dialog;

      theDialog = MDICreateWindow(hWndClient, &mdicreate);
      SetWindowStyle(theDialog, GetWindowStyle(theDialog)
		     & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
      MDIRestoreWindow(hWndClient, theDialog);
    }
    else {
      theDialog = CreateWindowEx(WS_EX_DLGMODALFRAME, szXLSDlgClass,
				 title, flags,
				 loc.h, loc.v, size.h, size.v,
				 NULL, NULL, hInst, (LPVOID) dialog);
    }
  }
  else {
    size.h += 2 * GetSystemMetrics(SM_CXDLGFRAME);
#ifdef WIN32
    size.v += GetSystemMetrics(SM_CYBORDER);
    size.v += GetSystemMetrics(SM_CYCAPTION);
    flags = WS_POPUP | WS_VISIBLE |WS_CAPTION;      //FC 7/20/99     
    theDialog = CreateWindowEx(WS_EX_DLGMODALFRAME, szXLSMDlgClass,
			       title, flags,
			       loc.h, loc.v, size.h, size.v,
			       NULL, NULL, hInst, (LPVOID) dialog);
#else
    size.v += 2 * GetSystemMetrics(SM_CYDLGFRAME);
    flags = WS_POPUP | WS_DLGFRAME | WS_VISIBLE;
    theDialog = CreateWindow(szXLSMDlgClass,
			     title, flags,
			     loc.h, loc.v, size.h, size.v,
			     hWndFrame, NULL, hInst, (LPVOID) dialog);
#endif
  }

  check_alloc(theDialog);
  set_dialog_address(theDialog, dialog);
}

void DialogRemove(LVAL dialog)
{
  if (check_dialog_address(dialog))
    XLSDestroyWindow(GETDIALOGADDRESS(dialog));
  if (objectp(dialog)) standard_hardware_clobber(dialog);
}

/***********************************************************************/
/**                                                                   **/
/**                       Dialog Item Functions                       **/
/**                                                                   **/
/***********************************************************************/

static void InstallBtnItem(HWND theDialog,
			   LVAL item,
			   DWORD flags,
			   int type,
			   DialogItemData *di)
{
  HANDLE hd;
  DialogItemData *data;
  DialogData *dialogData;
  HWND theItem;
  int itemIndex;
  char *text;
  Point loc, size;

  hd = GETDIALOGDATA(theDialog);
  dialogData = (DialogData *) GlobalLock(hd);
  check_lock(dialogData);

  itemIndex = (dialogData->count)++;

  if (! stringp(slot_value(item, s_text)))
    xlerror("not a string", slot_value(item, s_text));
  text = (char *) getstring(slot_value(item, s_text));

  loc = ListToPoint(slot_value(item, s_location));
  size = ListToPoint(slot_value(item, s_size));

  theItem = CreateWindow("button", text,
			 WS_CHILD | WS_VISIBLE | flags,
			 loc.h, loc.v, size.h, size.v,
			 (HWND) theDialog,
                         (HMENU) (itemIndex + ITEM_INDEX_BASE),
			 hInst, NULL);
  check_alloc(theItem);

  data = GETITEMDATA(dialogData);
  data[itemIndex].type = type;
  data[itemIndex].itemNumber = itemIndex;
  data[itemIndex].itemHandle = theItem;
  data[itemIndex].object = item;
  if (di) *di = data[itemIndex];
#ifdef WIN32
  SetWindowLong(theItem, GWL_WNDPROC, (LPARAM)CustomButtonProc);
#endif /* WIN32 */
  GlobalUnlock(hd);
}

static void InstallButtonItem(HWND theDialog, LVAL item)
{
  InstallBtnItem(theDialog, item, BS_PUSHBUTTON, BUTTON_ITEM, NULL);
}

#ifdef WIN32
static LRESULT CALLBACK CustomButtonProc(HWND hWnd, UINT message,
					 WPARAM wPrm, LPARAM lPrm)
{
  if ((message == WM_CHAR) && (wPrm == VK_RETURN)) {
    SendMessage(GetParent(hWnd), message, wPrm, lPrm);
    return 0;
  }
  return CallWindowProc(pDefBtnProc, hWnd, message, wPrm, lPrm);
}

static LRESULT CALLBACK CustomScrollProc(HWND hWnd, UINT message,
					 WPARAM wPrm, LPARAM lPrm)
{
  if ((message == WM_CHAR) && (wPrm == VK_RETURN)) {
    SendMessage(GetParent(hWnd), message, wPrm, lPrm);
    return 0;
  }
  return CallWindowProc(pDefScrollProc, hWnd, message, wPrm, lPrm);
}

static LRESULT CALLBACK CustomEditTextProc(HWND hWnd, UINT message,
					   WPARAM wPrm, LPARAM lPrm)
{
  if ((message == WM_CHAR) && (wPrm == VK_RETURN)) {
    SendMessage(GetParent(hWnd), message, wPrm, lPrm);
    return 0;
  }
  return CallWindowProc(pDefEditTextProc, hWnd, message, wPrm, lPrm);
}

static LRESULT CALLBACK CustomListProc(HWND hWnd, UINT message,
				       WPARAM wPrm, LPARAM lPrm)
{
  if ((message == WM_CHAR) && (wPrm == VK_RETURN)) {
    SendMessage(GetParent(hWnd), message, wPrm, lPrm);
    return 0;
  }
  return CallWindowProc(pDefListProc, hWnd, message, wPrm, lPrm);
}
#endif /* WIN32 */

void DialogButtonGetDefaultSize(LVAL item, int *pwidth, int *pheight)
{
  LVAL text;
  Point csz;
  int n;

  text = slot_value(item, s_text);
  if (! stringp(text)) xlerror("not a string", text);
  csz = AvCharSize(TRUE);
  n = strlen((char *) getstring(text));
  if (n < MIN_BUTTON_STRLEN) n = MIN_BUTTON_STRLEN;

  if (pwidth != NULL) *pwidth = (n + 2) * csz.h;
  if (pheight != NULL) *pheight = (7 * csz.v) / 4;
}

static void InstallToggleItem(HWND theDialog, LVAL item)
{
  DialogItemData di;

  InstallBtnItem(theDialog, item, BS_CHECKBOX, TOGGLE_ITEM, &di);
  Button_SetCheck(di.itemHandle,
                  (slot_value(item, s_value) != NIL) ? TRUE : FALSE);
}

void DialogToggleGetDefaultSize(LVAL item, int *pwidth, int *pheight)
{
  LVAL text;
  Point csz;
  int n;

  text = slot_value(item, s_text);
  if (! stringp(text)) xlerror("not a string", text);
  csz = AvCharSize(TRUE);
  n = strlen((char *) getstring(text));

  if (pwidth != NULL) *pwidth = (n + 4) * csz.h;
  if (pheight != NULL) *pheight = (3 * csz.v) / 2;
}

LVAL DialogToggleItemValue(item, set, value)
	LVAL item, value;
	int set;
{
  DialogItemData itemData;

  if (set) {
    set_slot_value(item, s_value, (value != NIL) ?  s_true : NIL);
    if (FindItemObjectData(item, &itemData)) {
      Button_SetCheck(itemData.itemHandle,
		      (value != NIL) ? TRUE : FALSE);
    }
  }
  return(slot_value(item, s_value));
}

static void InstallChoiceItem(HWND theDialog, LVAL item)
{
  LVAL titles, temp;
  DialogItemData *data;
  DialogData *dialogData;
  HWND theItem;
  int itemIndex, clusterLeader, clusterSize, initial;
  char *text;
  Point loc, size;
  HANDLE hd;

  titles = slot_value(item, s_text);
  if (! consp(titles)) xlerror("not a list", titles);

  loc = ListToPoint(slot_value(item, s_location));
  size = ListToPoint(slot_value(item, s_size));
  clusterSize = (int) llength(titles);
  size.v /= clusterSize;

  hd = GETDIALOGDATA(theDialog);
  dialogData = (DialogData *) GlobalLock(hd);
  check_lock(dialogData);

  clusterLeader = dialogData->count;
  for (; consp(titles); titles = cdr(titles)) {
    if (! stringp(car(titles))) xlerror("not a string", car(titles));
    text = (char *) getstring(car(titles));

    itemIndex = (dialogData->count)++;
    theItem = CreateWindow("button", text,
			   WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON,
			   loc.h, loc.v, size.h, size.v,
			   (HWND) theDialog,
  		          (HMENU) (itemIndex + ITEM_INDEX_BASE),
			   hInst, NULL);
    check_alloc(theItem);
    loc.v += size.v;

    data = GETITEMDATA(dialogData);
    data[itemIndex].type = CHOICE_ITEM;
    data[itemIndex].itemNumber = itemIndex;
    data[itemIndex].itemHandle = theItem;
    data[itemIndex].object = item;
    data[itemIndex].clusterLeader = clusterLeader;
    data[itemIndex].clusterSize = clusterSize;
#ifdef WIN32
    SetWindowLong(theItem, GWL_WNDPROC, (LPARAM)CustomButtonProc);
#endif /* WIN32 */
  }
  temp = slot_value(item, s_value);
  initial = (fixp(temp)) ? (int) getfixnum(temp) : 0;
  if (initial < 0 || initial >= clusterSize) initial = 0;
  initial += clusterLeader;
  Button_SetCheck(data[initial].itemHandle, 1);
  GlobalUnlock(hd);
}

void DialogChoiceGetDefaultSize(LVAL item, int *pwidth, int *pheight)
{
  Point csz;
  LVAL text = slot_value(item, s_text);
  int n, k, h;
  
  csz = AvCharSize(TRUE);
  for (h = 0, n = 0; consp(text); text = cdr(text)) {
    k = strlen((char *) getstring(car(text)));
    if (n < k) n = k;
    h += (3 * csz.v) / 2;
  }
  if (pwidth != NULL) *pwidth = (n + CHOICE_WIDTH_FUZZ) * csz.h;
  if (pheight != NULL) *pheight = h;
}

static void SetClusterValue(HWND theDialog, int index)
{
  int i, n, leader;
  HANDLE hd;
  DialogData *dialogData;
  DialogItemData *data;

  if (index < 0) return;

  hd = GETDIALOGDATA(theDialog);
  dialogData = (DialogData *) GlobalLock(hd);
  check_lock(dialogData);

  if (index < dialogData->count) {
    data = GETITEMDATA(dialogData);
    leader = data[index].clusterLeader;
    n = data[index].clusterSize + leader;

    for (i = leader; i < n; i++)
      Button_SetCheck(data[i].itemHandle, 0);
    Button_SetCheck(data[index].itemHandle, 1);
  }

  GlobalUnlock(hd);
}

LVAL DialogChoiceItemValue(LVAL item, int set, int value)
{
  DialogItemData itemData;
  LVAL dialog, textlist;

  if (set) {
    textlist = slot_value(item, s_text);
    if (! consp(textlist) || value < 0 || value >= llength(textlist))
      xlfail("Value out of range");
    set_slot_value(item, s_value, cvfixnum((FIXTYPE) value));
    if (FindItemObjectData(item, &itemData)) {
      if (value < 0 || value >= itemData.clusterSize)
	xlfail("value out of range");
      dialog = slot_value(item, s_dialog);
      SetClusterValue(GETDIALOGADDRESS(dialog),
		      itemData.clusterLeader + value);
    }
  }
  return(slot_value(item, s_value));
}

static void InstallTextItem(HWND theDialog, LVAL item)
{
  HANDLE hd;
  DialogItemData *data;
  DialogData *dialogData;
  HWND theItem;
  int editable, itemIndex;
  char *text;
  Point loc, size;
  
  if (! stringp(slot_value(item, s_text)))
    xlerror("not a string", slot_value(item, s_text));
  text = (char *) getstring(slot_value(item, s_text));
  loc = ListToPoint(slot_value(item, s_location));
  size = ListToPoint(slot_value(item, s_size));
  editable = (slot_value(item, s_editable) != NIL) ? TRUE : FALSE;

  hd = GETDIALOGDATA(theDialog);
  dialogData = (DialogData *) GlobalLock(hd);
  check_lock(dialogData);
  itemIndex = (dialogData->count)++;

  strcpy(buf, text);
  if (editable) {
    theItem = CreateWindow("edit", buf, WS_CHILD | WS_VISIBLE | WS_BORDER,
			   loc.h, loc.v, size.h, size.v,
			   (HWND) theDialog,
                           (HMENU) (itemIndex + ITEM_INDEX_BASE),
			   hInst, NULL);
  }
  else {
    theItem = CreateWindow("static", buf, WS_CHILD | WS_VISIBLE,
			   loc.h, loc.v, size.h, size.v,
			   (HWND) theDialog,
                           (HMENU) (itemIndex + ITEM_INDEX_BASE),
			   hInst, NULL);
  }
  check_alloc(theItem);
#ifdef WIN32  
  if (editable) {
   SetWindowLong(theItem, GWL_WNDPROC, (LPARAM)CustomEditTextProc);
  }
#endif /* WIN32 */
  data = GETITEMDATA(dialogData);
  data[itemIndex].type = TEXT_ITEM;
  data[itemIndex].itemNumber = itemIndex;
  data[itemIndex].itemHandle = theItem;
  data[itemIndex].object = item;
  GlobalUnlock(hd);
}

void DialogTextGetDefaultSize(LVAL item, int *pwidth, int *pheight)
{
  Point csz, tsz;
  LVAL text = slot_value(item, s_text);
  LVAL text_length = slot_value(item, s_text_length);

  tsz.h = 0; tsz.v = 0;
  csz = AvCharSize(TRUE);
  if (stringp(text)) {
    tsz = text_size((char *) getstring(text));
  }
  if (fixp(text_length)) {
    if (tsz.h < getfixnum(text_length)) tsz.h = (int) getfixnum(text_length);
    if (tsz.v < 1) tsz.v = 1;
  }
  if (slot_value(item, s_editable) != NIL) {
    if (pwidth != NULL) *pwidth = csz.h * tsz.h + EDIT_TEXT_PAD;
    if (pheight != NULL) *pheight = csz.v * tsz.v + EDIT_TEXT_PAD;
  }
  else {
    if (pwidth != NULL) *pwidth = csz.h * tsz.h + STATIC_TEXT_PAD;
    if (pheight != NULL) *pheight = csz.v * tsz.v + STATIC_TEXT_PAD;
  }
}

LVAL DialogTextItemText(LVAL item, int set, char *text)
{
  DialogItemData di;
  int n;

  if (set) set_slot_value(item, s_text, cvstring(text));
  if (FindItemObjectData(item, &di)) {
    if (set) {
      strcpy(buf, text);
      SetWindowText(di.itemHandle, buf);
    }
    n = GetWindowText(di.itemHandle, buf, STRMAX);
    buf[n] = '\0';
    set_slot_value(item, s_text, cvstring(buf));
  }
  return(slot_value(item, s_text));
}

static void InstallScrollItem(HWND theDialog, LVAL item)
{
  HANDLE hd;
  DialogItemData *data;
  DialogData *dialogData;
  HWND theItem;
  int low, high, value;
  int itemIndex;
  Point loc, size;
  LVAL temp;
  DWORD flags;
  
  loc = ListToPoint(slot_value(item, s_location));
  size = ListToPoint(slot_value(item, s_size));
  flags = (size.h > size.v) ? SBS_HORZ : SBS_VERT;

  temp = slot_value(item, s_min_value);
  low = fixp(temp) ? (int) getfixnum(temp) : SCROLL_MIN;
  temp = slot_value(item, s_max_value);
  high = fixp(temp) ? (int) getfixnum(temp) : SCROLL_MAX;
  temp = slot_value(item, s_value);
  value = (fixp(temp)) ? (int) getfixnum(temp) : low;

  hd = GETDIALOGDATA(theDialog);
  dialogData = (DialogData *) GlobalLock(hd);
  check_lock(dialogData);

  itemIndex = (dialogData->count)++;
  theItem = CreateWindow("scrollbar", NULL,
			 WS_CHILD | WS_VISIBLE | WS_TABSTOP | flags,
			 loc.h, loc.v, size.h, size.v,
			 (HWND) theDialog,
                         (HMENU) (itemIndex + ITEM_INDEX_BASE),
			 hInst, NULL);
  check_alloc(theItem);
  SetScrollRange(theItem, SB_CTL, low, high, FALSE);
  SetScrollPos(theItem, SB_CTL, value, TRUE);

  data = GETITEMDATA(dialogData);
  data[itemIndex].type = SCROLL_ITEM;
  data[itemIndex].itemNumber = itemIndex;
  data[itemIndex].itemHandle = theItem;
  data[itemIndex].object = item;
  GlobalUnlock(hd);
#ifdef WIN32
  SetWindowLong(theItem, GWL_WNDPROC, (LPARAM)CustomScrollProc);
#endif /* WIN32 */
}

void DialogScrollGetDefaultSize(LVAL item, int *pwidth, int *pheight)
{
  int h, w;

  h = GetSystemMetrics(SM_CYHSCROLL);
  w = SCROLL_WIDTH; // ### 10 * h
  if (pwidth != NULL) *pwidth = w;
  if (pheight != NULL) *pheight = h;
}

static LVAL scroll_item_value(LVAL item, int set, int value, int which)
{
  LVAL slot, temp;
  DialogItemData di;
  int low, high;

  switch (which) {
  case 'V': slot = s_value;     break;
  case 'H': slot = s_max_value; break;
  case 'L': slot = s_min_value; break;
  }

  if (set) {
    if (FindItemObjectData(item, &di)) {
      switch (which) {
      case 'V':
	SetScrollPos(di.itemHandle, SB_CTL, value, TRUE);
	break;
      case 'H':
	temp = slot_value(item, s_min_value);
	low = fixp(temp) ? (int) getfixnum(temp) : SCROLL_MIN;
	SetScrollRange(di.itemHandle, SB_CTL, low, value, TRUE);
	break;
      case 'L':
	temp = slot_value(item, s_max_value);
	high = fixp(temp) ? (int) getfixnum(temp) : SCROLL_MAX;
	SetScrollRange(di.itemHandle, SB_CTL, value, high, TRUE);
	break;
      }
    }
    set_slot_value(item, slot, cvfixnum((FIXTYPE) value));
    temp = slot_value(item, s_min_value);
    low = fixp(temp) ? (int) getfixnum(temp) : SCROLL_MIN;
    temp = slot_value(item, s_max_value);
    high = fixp(temp) ? (int) getfixnum(temp) : SCROLL_MAX;
    temp = slot_value(item, s_value);
    value = (fixp(temp)) ? (int) getfixnum(temp) : low;
    if (value < low || value > high)
      set_slot_value(item, s_value, cvfixnum((FIXTYPE) low));
  }
  return(slot_value(item, slot));
}

LVAL DialogScrollItemValue(LVAL item, int set, int value)
{
  return(scroll_item_value(item, set, value, 'V'));
}

LVAL DialogScrollItemMax(LVAL item, int set, int value)
{
  return(scroll_item_value(item, set, value, 'H'));
}

LVAL DialogScrollItemMin(LVAL item, int set, int value)
{
  return(scroll_item_value(item, set, value, 'L'));
}

static Point ListItemDims(LVAL item)
{
  LVAL listData = slot_value(item, s_list_data);
  Point sz;

  if (seqp(listData)) {
    sz.v = (short) seqlen(listData);
    sz.h = 1;
  }
  else if (matrixp(listData)) {
    sz.v = numrows(listData);
    sz.h = numcols(listData);
  }
  else xlerror("this form of data is not yet supported", listData);
  return(sz);
}

static void InstallListItem(HWND theDialog, LVAL item)
{
  HANDLE hd;
  DialogItemData *data;
  DialogData *dialogData;
  HWND theItem;
  int itemIndex, columns, n, m, i, j, k;
  Point loc, size, csz, lsz;
  LVAL listData, next, temp;
  BOOL vscroll, hscroll;
  DWORD flags;
  char *s;

  csz = AvCharSize(LIST_USE_LEADING);
  csz.v += REAL_LIST_ITEM_PAD;
  loc = ListToPoint(slot_value(item, s_location));
  size = ListToPoint(slot_value(item, s_size));

  xlsave1(listData);
  listData = slot_value(item, s_list_data);
  lsz = ListItemDims(item);
  n = lsz.v;
  m = lsz.h;
  temp = slot_value(item, s_columns);
  if (! fixp(temp) || getfixnum(temp) < 1) columns = 1;
  else columns = (int) getfixnum(temp);

  hscroll = (columns < m) ? TRUE : FALSE;
  vscroll = (n * csz.v > size.v - ((hscroll) ? GetSystemMetrics(SM_CYHSCROLL) : 0)) ? TRUE : FALSE;
  flags = WS_CHILD | WS_VISIBLE | WS_BORDER | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT;
  if (hscroll) flags |= WS_HSCROLL;
  if (vscroll) flags |= WS_VSCROLL;
  if (m > 1) flags |= LBS_MULTICOLUMN;

  hd = GETDIALOGDATA(theDialog);
  dialogData = (DialogData *) GlobalLock(hd);
  check_lock(dialogData);

  itemIndex = (dialogData->count)++;

  theItem = CreateWindow("listbox", NULL, flags,
			 loc.h, loc.v, size.h, size.v,
			 (HWND) theDialog,
                         (HMENU) (itemIndex + ITEM_INDEX_BASE),
                         hInst, NULL);
  check_alloc(theItem);
  SendMessage(theItem,WM_SETFONT,(WPARAM) GetStockObject(DIALOG_FONT),0);

  ListBox_SetColumnWidth(theItem, LIST_COL_CHARS * csz.h);
  if (darrayp(listData))
    listData = getdarraydata(listData);
  else if (listp(listData)) listData = coerce_to_tvec(listData, s_true);
  SetWindowRedraw(theItem, FALSE);
  for (j = 0; j < m; j++) {
    for (i = 0, k = j; i < n; i++, k += m) {
      next = gettvecelement(listData, k);
      s = (stringp(next)) ? (char *) getstring(next) : "";
      strcpy(buf, s);
      truncateListEntry(buf);
      if (ListBox_AddString(theItem, buf) < 0)
	xlfail("list allocation failed");
    }
  }
  SetWindowRedraw(theItem, TRUE);

  data = GETITEMDATA(dialogData);
  data[itemIndex].type = LIST_ITEM;
  data[itemIndex].itemNumber = itemIndex;
  data[itemIndex].itemHandle = theItem;
  data[itemIndex].object = item;
  GlobalUnlock(hd);
  xlpop();
#ifdef WIN32
  SetWindowLong(theItem, GWL_WNDPROC, (LPARAM)CustomListProc);
#endif /* WIN32 */
}

void DialogListGetDefaultSize(LVAL item, int *pwidth, int *pheight)
{
  LVAL columns = slot_value(item, s_columns);
  LVAL data = slot_value(item, s_list_data);
  Point csz, sz;

  csz = AvCharSize(LIST_USE_LEADING);
  sz.h = LIST_COL_CHARS * (short) getfixnum(columns);
  if (seqp(data))
    sz.v = (short) seqlen(data);
  else if (matrixp(data))
    sz.v = (int) numrows(data);

  csz.v += REAL_LIST_ITEM_PAD;
  sz.h *= csz.h;
  sz.v *= csz.v;
  if (sz.v > csz.v * MAX_LIST_ROWS) {
    sz.v = csz.v * MAX_LIST_ROWS;
    sz.h += GetSystemMetrics(SM_CXVSCROLL);
  }
  if (matrixp(data) && numrows(data) > getfixnum(columns))
    sz.v += GetSystemMetrics(SM_CYHSCROLL);
  if (pwidth != NULL) *pwidth = sz.h;
  if (pheight != NULL) *pheight = sz.v;
}

LVAL DialogListItemSelection(LVAL item, int set, LVAL index)
{
  LVAL result;
  DialogItemData di;
  Point lsz, cell;
  int i;
  BOOL twodim;

  lsz = ListItemDims(item);

  if (FindItemObjectData(item, &di)) {
    if (set) {
      if (index == NIL) i = -1;
      else if (fixp(index)) i = (int) getfixnum(index);
      else if (consp(index)) {
	cell = ListToPoint(index);
	i = cell.h + cell.v * lsz.h;
      }
      else xlbadtype(index);
      if (i < 0 || i >= lsz.h * lsz.v) i = -1;
      (void) ListBox_SetCurSel(di.itemHandle, i);
    }
    twodim = matrixp(slot_value(item, s_list_data)) ? TRUE : FALSE;
    i = (int) ListBox_GetCurSel(di.itemHandle);
    if (i == LB_ERR || lsz.h <= 0) result = NIL;
    else if (twodim) {
      cell.h = i % lsz.h;
      cell.v = i / lsz.h;
      result = integer_list_2(cell.h, cell.v);
    }
    else result = cvfixnum((FIXTYPE) i);
  }
  else result = NIL;

  return(result);
}

void DialogListItemSetText(LVAL item, LVAL index, char *text)
{
  DialogItemData di;
  Point lsz, cell;
  int i, j;

  lsz = ListItemDims(item);
  if (FindItemObjectData(item, &di)) {
    if (fixp(index)) i = (int) getfixnum(index);
    else if (consp(index)) {
      cell = ListToPoint(index);
      i = cell.h + cell.v * lsz.h;
    }
    else xlbadtype(index);
    if (i >= 0 && i < lsz.h * lsz.v) {
      j = ListBox_GetCurSel(di.itemHandle);
      (void) ListBox_DeleteString(di.itemHandle, i);
      (void) ListBox_InsertString(di.itemHandle, i, text);
      (void) ListBox_SetCurSel(di.itemHandle, j);
    }
  }
}

LVAL DialogGetModalItem(LVAL dialog)
{
  HWND theDialog;
  LVAL item, oldModalItem;
  BOOL oldInModalDialog;
  MSG msg;

  theDialog = GETDIALOGADDRESS(dialog);
  if (theDialog == NULL) xlfail("the dialog is not visible");

  oldModalItem = ModalItem;
  oldInModalDialog = InModalDialog;
  ModalItem = NIL;
  InModalDialog = TRUE;

  while (ModalItem == NIL && GetMessage(&msg, /*theDialog*/NULL, 0, 0)) {
    if(! TranslateMDISysAccel(hWndClient, &msg) &&
       ! TranslateAccelerator(hWndFrame, hAccel, &msg)) {
      TTYFlushOutput();
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      SetActiveWindow(theDialog);
    }
  }

  item = ModalItem;
  ModalItem = oldModalItem;
  InModalDialog = oldInModalDialog;

  if (item == NIL) item = slot_value(dialog, s_default_button);
  return(item);
}

void DialogSetDefaultButton(LVAL dialog, LVAL item)
{
  HANDLE hd;
  DialogData *dd;
  DialogItemData di;
  HWND theDialog;

  if (item != NIL && ! button_item_p(item))
    xlerror("not a button item", item);

  set_slot_value(dialog, s_default_button, item);
    
  theDialog = GETDIALOGADDRESS(dialog);
  if (theDialog != NULL) {
    hd = GETDIALOGDATA(theDialog);
    dd = (DialogData *) GlobalLock(hd);
    check_lock(dd);

    if (FindItemData(theDialog, ITEM_INDEX_BASE + dd->dflt, &di)) {
      dd->dflt = -1;
      Button_SetStyle(di.itemHandle, BS_PUSHBUTTON, TRUE);
    }
    if (item != NIL && FindItemObjectData(item, &di)) {
      dd->dflt = di.itemNumber;
      Button_SetStyle(di.itemHandle, BS_DEFPUSHBUTTON, TRUE);
      SetFocus(di.itemHandle);
    }
    GlobalUnlock(hd);
  }
}

#ifdef WIN32
static void DialogDoDefaultButton(LVAL dialog)
{
  HANDLE hd;
  DialogData *dd;
  DialogItemData di;
  HWND theDialog;
  LVAL item;

  item = slot_value(dialog, s_default_button);
    
  if (item != NIL && ! button_item_p(item))
    xlerror("not a button item", item);

  theDialog = GETDIALOGADDRESS(dialog);
  if (theDialog != NULL) {
    hd = GETDIALOGDATA(theDialog);
    dd = (DialogData *) GlobalLock(hd);
    check_lock(dd);

    if (FindItemData(theDialog, ITEM_INDEX_BASE + dd->dflt, &di))
      dd->dflt = -1;
    if (item != NIL && FindItemObjectData(item, &di)) {
      HWND hwndDefault = di.itemHandle;
      if (IsWindow(hwndDefault))
	PostMessage(hwndDefault, BM_CLICK, 0, 0);
    }
    GlobalUnlock(hd);
  }
}
#endif /* WIN32 */

#ifdef DODO
Add keyboard support:
subclass all items to handle tabs
handle return as hitting default item
#endif DODO


syntax highlighted by Code2HTML, v. 0.9.1