/*
 *     XaoS, a fast portable realtime fractal zoomer 
 *                  Copyright (C) 1996 by
 *
 *      Jan Hubicka          (hubicka@paru.cas.cz)
 *      Thomas Marsh         (tmarsh@austin.ibm.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#undef _EFENCE_
#include <config.h>
#ifdef _plan9_
#include <u.h>
#include <libc.h>
#include <ctype.h>
#else
#include <aconfig.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#ifndef _MAC
#include <sys/stat.h>
#endif
#endif
#include <fconfig.h>
#ifndef _plan9_
#include <assert.h>
#endif
#include <filter.h>
#include <fractal.h>
#include <ui_helper.h>
#include <ui.h>
#include <xmenu.h>
#include <grlib.h>
#include "uiint.h"
#include <xldio.h>
#include <misc-f.h>

#ifdef HAVE_GETTEXT
#include <libintl.h>
#else
#define gettext(STRING) STRING
#endif

struct dialogitem;
struct dialogtype
{
  void (*build) (struct dialogitem * item, CONST menudialog * entry);
  int (*key) (struct dialogitem * item, int key);
  void (*mouse) (struct dialogitem * item, int x, int y, int buttons,
		 int flags);
  void (*destroy) (struct dialogitem * item, dialogparam * param);
  void (*draw) (struct dialogitem * item);
  void (*unselect) (struct dialogitem * item);
};
struct dialogitem
{
  int y;
  int width, width1, height;
  CONST menudialog *dialog;
  void *data;
  CONST struct dialogtype *type;
};
static struct opendialog
{
  int x, y, width, height;
  int half;
  int nitems;
  CONST menudialog *dialog;
  int mousereleased;
  int mousegrab;
  CONST menuitem *item;
  int current;
  struct dialogitem *items;
  struct uih_window *window;
}
dialog;

static dialogparam *qparam;
static CONST menuitem *qitem;

int dialogvisible;

int yesnodialogvisible;
static struct yesnodialog
{
  int width;
  int questionwidth;
  int mousereleased;
  char *question;
  void (*handler) (int yes);
  int selected;
  int pressed;
  struct uih_window *window;
}
yesnodialog;

// These 3 definitions are no longer used:
static CONST char *CONST oktext = "OK";
static CONST char *CONST canceltext = "Cancel";
static CONST char *CONST helptext = "Help";

static int okwidth;
static int cancelwidth;
#define SELECTED(item) ((item-dialog.items)==dialog.current)
static void
NEXT (void)
{
  dialog.items[dialog.current].type->unselect (dialog.items + dialog.current);
  dialog.current = (dialog.current + 1) % dialog.nitems;
  uih->display = 1;
}
static void
PREV (void)
{
  dialog.items[dialog.current].type->unselect (dialog.items + dialog.current);
  dialog.current = dialog.current ? (dialog.current - 1) : dialog.nitems - 1;
  uih->display = 1;
}

struct okdata
{
  int pressed;
  int selected;
};

CONST char *CONST yestext = "Yes";
CONST char *CONST notext = "No";
#define YESNOX ((uih->image->width-yesnodialog.width)/2)
#define YESNOHEIGHT (2*BUTTONHEIGHT+2*BORDERHEIGHT)
#define YESNOY ((uih->image->height-YESNOHEIGHT)/2)
void
ui_drawbutton (CONST char *text, int pressed, int selected, int x1, int x2,
	       int y)
{
  int width = xtextwidth (uih->font, text);
  /*printf("%s %i %i\n",text,pressed,selected); */
  if (uih->palette->type & BITMAPS)
    {
      uih_drawborder (uih, x1, y, x2 - x1, BUTTONHEIGHT,
		      (pressed != 0
		       || selected != 0) * BORDER_PRESSED | BORDER_LIGHT);
      xprint (uih->image, uih->font, (x1 + x2 - width) / 2 + pressed,
	      y + BORDERHEIGHT + pressed, text, selected
	      || pressed ? BGCOLOR (uih) : FGCOLOR (uih), BGCOLOR (uih),
	      TEXT_PRESSED);
    }
  else
    {
      uih_drawborder (uih, x1, y, x2 - x1, BUTTONHEIGHT,
		      (pressed != 0) * BORDER_PRESSED | BORDER_LIGHT);
      xprint (uih->image, uih->font, (x1 + x2 - width) / 2 + pressed,
	      y + BORDERHEIGHT + pressed, text,
	      selected ? SELCOLOR (uih) : FGCOLOR (uih), BGCOLOR (uih),
	      /*TEXT_PRESSED */ 0);
    }
}
static void
ui_yesnopos (struct uih_context *c, int *x, int *y, int *w, int *h,
	     void *data)
{
#ifdef _plan9_
#define filevisible 0
#endif
  if (filevisible || helpvisible)
    {
      *x = *y = *w = *h = 0;
      return;
    }
  *w = yesnodialog.width;
  *h = YESNOHEIGHT;
  *x = YESNOX;
  *y = YESNOY;
}
static void
ui_drawyesno (struct uih_context *c, void *data)
{
  xprint (uih->image, uih->font,
	  YESNOX + (yesnodialog.width - yesnodialog.questionwidth) / 2,
	  YESNOY + BORDERHEIGHT, yesnodialog.question, FGCOLOR (uih),
	  BGCOLOR (uih), 0);
  ui_drawbutton (yestext, yesnodialog.pressed == 0, yesnodialog.selected == 0,
		 YESNOX + BORDERWIDTH + 1,
		 YESNOX + (yesnodialog.width) / 2 - 1,
		 YESNOY + BUTTONHEIGHT + BORDERHEIGHT);
  ui_drawbutton (notext, yesnodialog.pressed == 1, yesnodialog.selected == 1,
		 YESNOX + (yesnodialog.width) / 2 + 1,
		 YESNOX + yesnodialog.width - BORDERWIDTH - 1,
		 YESNOY + BUTTONHEIGHT + BORDERHEIGHT);
}
static void
ui_closeyesno (int success)
{
  if (!yesnodialogvisible)
    return;
  free (yesnodialog.question);
  yesnodialog.handler (success);
  yesnodialogvisible = 0;
  uih_removew (uih, yesnodialog.window);
  uih->display = 1;
}

void
ui_buildyesno (CONST char *question, void (*handler) (int yes))
{
  if (yesnodialogvisible)
    ui_closeyesno (0);
  yesnodialogvisible = 1;
  yesnodialog.questionwidth = xtextwidth (uih->font, question);
  yesnodialog.question = mystrdup (question);
  yesnodialog.mousereleased = 0;
  yesnodialog.width =
    xtextwidth (uih->font, yestext) + xtextwidth (uih->font,
						  notext) + 8 * BORDERWIDTH +
    2;
  if (yesnodialog.width < yesnodialog.questionwidth)
    yesnodialog.width = yesnodialog.questionwidth;
  yesnodialog.width += 2 * BORDERWIDTH;
  yesnodialog.handler = handler;
  yesnodialog.selected = 0;
  yesnodialog.pressed = -1;
  yesnodialog.window =
    uih_registerw (uih, ui_yesnopos, ui_drawyesno, NULL, DRAWBORDER);
  uih->display = 1;
}
static int
ui_keyyesno (int key)
{
  if (!yesnodialogvisible)
    return 0;
  switch (key)
    {
    case UIKEY_LEFT:
      yesnodialog.selected ^= 1;
      uih->display = 1;
      return 1;
    case UIKEY_UP:
      return 1;
    case UIKEY_RIGHT:
    case UIKEY_TAB:
      yesnodialog.selected ^= 1;
      uih->display = 1;
      return 1;
    case UIKEY_DOWN:
      return 1;
    case 13:
    case '\n':
      ui_closeyesno (!yesnodialog.selected);
      return 1;
    case UIKEY_ESC:
      ui_closeyesno (0);
      return 1;
    }
  return 1;
}
static int
ui_mouseyesno (int x, int y, int buttons, int flags)
{
  int mouseat = 0;
  if (!yesnodialogvisible)
    return 0;
  if (!yesnodialog.mousereleased && (flags & MOUSE_RELEASE))
    {
      yesnodialog.mousereleased = 1;
      return 1;
    }
  if (!yesnodialog.mousereleased && (flags & MOUSE_DRAG))
    {
      return 1;
    }
  yesnodialog.mousereleased = 1;
  if (x < YESNOX || y < YESNOY || x > YESNOX + yesnodialog.width
      || y > YESNOY + YESNOHEIGHT)
    {
      if (flags & MOUSE_PRESS)
	{
	  ui_closeyesno (0);
	}
      else
	{
	  if (yesnodialog.pressed != -1)
	    uih->display = 1;
	  yesnodialog.pressed = -1;
	}
      return 1;
    }
  if (x > YESNOX + yesnodialog.width / 2)
    mouseat = 1;
  if (flags & MOUSE_DRAG)
    {
      if (yesnodialog.pressed != mouseat)
	uih->display = 1;
      if (yesnodialog.selected != mouseat)
	uih->display = 1;
      yesnodialog.selected = mouseat;
      yesnodialog.pressed = mouseat;
    }
  else
    {
      if ((flags & MOUSE_MOVE) && yesnodialog.selected != mouseat)
	uih->display = 1, yesnodialog.selected = mouseat;
      if (yesnodialog.pressed != -1)
	uih->display = 1;
      yesnodialog.pressed = -1;
    }
  if (flags & MOUSE_RELEASE)
    {
      ui_closeyesno (!mouseat);
    }
  return 1;
}

static void
ui_buildok (struct dialogitem *item, CONST menudialog * entry)
{
  struct okdata *ok;
  item->height = BUTTONHEIGHT;
  okwidth = xtextwidth (uih->font, gettext ("OK"));
  cancelwidth = xtextwidth (uih->font, gettext ("Cancel"));
  item->width = okwidth + 2 * BORDERWIDTH + 2;
  item->width1 = cancelwidth + 2 * BORDERWIDTH + 2;
  if (item->width < item->width1)
    item->width = item->width1;
  if (item->width > item->width1)
    item->width1 = item->width;
  item->data = ok = (struct okdata *) malloc (sizeof (struct okdata));
  ok->pressed = -1;
  ok->selected = 0;
}
static void
ui_destroyok (struct dialogitem *item, dialogparam * param)
{
  free (item->data);
}
static int
ui_keyok (struct dialogitem *item, int key)
{
  struct okdata *ok = (struct okdata *) item->data;
  switch (key)
    {
    case UIKEY_LEFT:
      if (ok->selected >= 1)
	{
	  ok->selected--;
	  uih->display = 1;
	  return 1;
	}
    case UIKEY_UP:
      PREV ();
      return 1;
    case UIKEY_RIGHT:
    case UIKEY_TAB:
      if (ok->selected < 2)
	{
	  ok->selected++;
	  uih->display = 1;
	  return 1;
	}
    case UIKEY_DOWN:
      NEXT ();
      return 1;
    case 13:
    case '\n':
      if (ok->selected <= 1)
	ui_closedialog (!ok->selected);
      else
	ui_help (dialog.item->shortname);
      return 1;
    }
  return 0;
}
static void
ui_mouseok (struct dialogitem *item, int x, int y, int buttons, int flags)
{
  struct okdata *ok = (struct okdata *) item->data;
  int mouseat = 0;
  if (x > dialog.x + dialog.width / 3)
    mouseat = 1;
  if (x > dialog.x + 2 * dialog.width / 3)
    mouseat = 2;
  if (flags & MOUSE_DRAG)
    {
      if (ok->pressed != mouseat)
	uih->display = 1;
      if (ok->selected != mouseat)
	uih->display = 1;
      ok->selected = mouseat;
      ok->pressed = mouseat;
    }
  else
    {
      if ((flags & MOUSE_MOVE) && ok->selected != mouseat)
	uih->display = 1, ok->selected = mouseat;
      if (ok->pressed != -1)
	uih->display = 1;
      ok->pressed = -1;
    }
  if (flags & MOUSE_RELEASE)
    {
      if (mouseat < 2)
	ui_closedialog (!mouseat);
      else
	ui_help (dialog.item->shortname);
    }
}
static void
ui_drawok (struct dialogitem *item)
{
  struct okdata *ok = (struct okdata *) item->data;
  ui_drawbutton (gettext ("OK"), ok->pressed == 0, SELECTED (item)
		 && ok->selected == 0, dialog.x + BORDERWIDTH + 1,
		 dialog.x + (dialog.width) / 3 - 1, item->y);
  ui_drawbutton (gettext ("Cancel"), ok->pressed == 1, SELECTED (item)
		 && ok->selected == 1, dialog.x + (dialog.width) / 3 + 1,
		 dialog.x + 2 * dialog.width / 3 - BORDERWIDTH, item->y);
  ui_drawbutton (gettext ("Help"), ok->pressed == 2, SELECTED (item)
		 && ok->selected == 2, dialog.x + 2 * (dialog.width) / 3,
		 dialog.x + dialog.width - BORDERWIDTH - 1, item->y);
}
static void
ui_unselectok (struct dialogitem *item)
{
  struct okdata *ok = (struct okdata *) item->data;
  ok->pressed = -1;
  ok->selected = 0;
  uih->display = 1;
}
CONST static struct dialogtype okdialog = {
  ui_buildok,
  ui_keyok,
  ui_mouseok,
  ui_destroyok,
  ui_drawok,
  ui_unselectok
};
void
ui_updatetext (struct ui_textdata *d)
{
  int again = 1;
  int i;
  int wi;
  int len = (int) strlen (d->text);
  if (d->start >= len)
    d->start = 0;
  if (d->cursor > len)
    d->cursor = len;
  if (d->cursor < d->start)
    d->start = d->cursor;
  do
    {
      wi = 0;
      for (i = 0; d->text[d->start + i]; i++)
	{
	  if (d->start + i == d->cursor)
	    d->cursorpos = wi;
	  wi += xtextcharw (uih->font, d->taxt[d->start + i]);
	  if (wi >= d->width)
	    {
	      break;
	    }
	}
      if (d->start + i == d->cursor && wi < d->width)
	d->cursorpos = wi;
      if (d->start + i < d->cursor)
	d->start++;
      else
	again = 0;
    }
  while (again);
  d->ndisplayed = i;
  while (again);
}
struct ui_textdata *
ui_opentext (int x, int y, int width, CONST char *def)
{
  struct ui_textdata *d = (struct ui_textdata *) malloc (sizeof (*d));
  char *text;
  int size = 100;
  if ((int) strlen (def) > size)
    size = (int) strlen (def) * 2;
  d->x = x;
  d->y = y;
  d->width = width;
  text = (char *) malloc (size);
  strcpy (text, def);
  d->text = text;
  d->cursor = 0;
  d->cursorpos = 0;
  d->start = 0;
  d->ndisplayed = 0;
  d->clear = 1;
  d->size = size;
  ui_updatetext (d);
  return (d);
}

void
ui_drawtext (struct ui_textdata *d, int active)
{
  char *c = (char *) malloc (d->ndisplayed + 2);
  strncpy (c, d->text + d->start, d->ndisplayed);
  c[d->ndisplayed] = 0;
  xprint (uih->image, uih->font, d->x, d->y, c,
	  (uih->palette->type & BITMAPS) ? BGCOLOR (uih) : ((active
							     && d->clear) ?
							    SELCOLOR (uih) :
							    FGCOLOR (uih)),
	  BGCOLOR (uih), (uih->palette->type & BITMAPS) ? TEXT_PRESSED : 0);
  if (active)
    {
      xdrawcursor (uih->image, d->x + d->cursorpos, d->y,
		   (uih->
		    palette->type & BITMAPS) ? BGCOLOR (uih) : SELCOLOR (uih),
		   xtextheight (uih->font));
    }
  free (c);
}

void
ui_textmouse (struct ui_textdata *d, int x, int y)
{
  if (y > d->y && y < d->y + xtextheight (uih->font) && x > d->x)
    {
      int w = 0;
      int i;
      int xp = d->x;
      for (i = 0; i < d->ndisplayed + 1 && xp - w / 2 < x; i++)
	{
	  w = xtextcharw (uih->font, d->text[i + d->start]);
	  xp += w;
	}
      d->cursor = i + d->start - 1;
      if (d->cursor < 0)
	d->cursor = 0;
      d->clear = 0;
      ui_updatetext (d);
      uih->display = 1;
    }
}
void
ui_closetext (struct ui_textdata *d)
{
  free (d->text);
  free (d);
}

int
ui_textkey (struct ui_textdata *d, int key)
{
  switch (key)
    {
    case UIKEY_LEFT:
      if (d->clear)
	d->clear = 0;
      if (d->cursor)
	d->cursor--;
      else
	return 0;
      ui_updatetext (d);
      uih->display = 1;
      return 1;
    case UIKEY_RIGHT:
      if (d->clear)
	d->clear = 0;
      if (d->cursor < (int) strlen (d->text))
	d->cursor++;
      else
	return 0;
      ui_updatetext (d);
      uih->display = 1;
      return 1;
    case UIKEY_HOME:
      if (d->clear)
	d->clear = 0;
      d->cursor = 0;
      ui_updatetext (d);
      uih->display = 1;
      return 1;
    case UIKEY_END:
      if (d->clear)
	d->clear = 0;
      d->cursor = (int) strlen (d->text);
      ui_updatetext (d);
      uih->display = 1;
      return 1;
    case UIKEY_BACKSPACE:
      if (d->clear)
	d->text[0] = 0, d->clear = 0;
      else if (d->cursor)
	{
	  int len, i;
	  len = (int) strlen (d->text);
	  for (i = d->cursor; i <= len; i++)
	    {
	      d->text[i - 1] = d->text[i];
	    }
	  d->cursor--;
	}
      ui_updatetext (d);
      uih->display = 1;
      return 1;
    }
  if (isprint (key))
    {
      int i;
      int len;
      if (d->clear)
	d->text[0] = 0, d->clear = 0;
      if ((len = (int) strlen (d->text)) > d->size - 2)
	{
	  d->text = (char *) realloc (d->text, d->size * 2);
	}
      for (i = len; i >= d->cursor; i--)
	{
	  d->text[i + 1] = d->text[i];
	}
      d->text[d->cursor] = key;
      d->cursor++;
      ui_updatetext (d);
      uih->display = 1;
      return 1;
    }
  return 0;
}
static void
ui_buildstring (struct dialogitem *item, CONST menudialog * entry)
{
  item->height = BUTTONHEIGHT;
  item->width = xtextwidth (uih->font, item->dialog->question);
  item->width1 = xtextcharw (uih->font, 'w') * 20;
  item->data = ui_opentext (0, 0, 2043, item->dialog->defstr);
}
static void
ui_destroystring (struct dialogitem *item, dialogparam * param)
{
  struct ui_textdata *text = (struct ui_textdata *) item->data;
  param->dstring = mystrdup (text->text);
  ui_closetext (text);
}
static void
ui_drawquestion (struct dialogitem *item)
{
  if (uih->palette->type & BITMAPS)
    {
      if (SELECTED (item))
	xrectangle (uih->image, dialog.x + BORDERWIDTH, item->y,
		    dialog.half - dialog.x - 2 * BORDERWIDTH, BUTTONHEIGHT,
		    FGCOLOR (uih));
      xprint (uih->image, uih->font, dialog.half - item->width,
	      item->y + BORDERHEIGHT, item->dialog->question,
	      SELECTED (item) ? BGCOLOR (uih) : FGCOLOR (uih), BGCOLOR (uih),
	      TEXT_PRESSED);
    }
  else
    {
      xprint (uih->image, uih->font, dialog.half - item->width,
	      item->y + BORDERHEIGHT, item->dialog->question,
	      SELECTED (item) ? SELCOLOR (uih) : FGCOLOR (uih), BGCOLOR (uih),
	      0);
    }
}
static void
ui_drawstring (struct dialogitem *item)
{
  struct ui_textdata *text = (struct ui_textdata *) item->data;
  if (text->width == 2043)
    text->x = dialog.half + BORDERWIDTH, text->width =
      dialog.width + dialog.x - dialog.half - 2 * BORDERWIDTH, text->y =
      item->y + BORDERHEIGHT, ui_updatetext (text);
  uih_drawborder (uih, dialog.half, item->y,
		  dialog.width - dialog.half + dialog.x - BORDERWIDTH,
		  BUTTONHEIGHT, BORDER_PRESSED | BORDER_LIGHT);
  ui_drawtext (text, SELECTED (item));
  ui_drawquestion (item);
}
static int
ui_keystring (struct dialogitem *item, int key)
{
  struct ui_textdata *text = (struct ui_textdata *) item->data;
  return (ui_textkey (text, key));
}
static void
ui_mousestring (struct dialogitem *item, int x, int y, int buttons, int flags)
{
  struct ui_textdata *text = (struct ui_textdata *) item->data;
  if (flags & MOUSE_DRAG)
    {
      ui_textmouse (text, x, y);
    }
}
static void
ui_unselectstring (struct dialogitem *item)
{
}
CONST static struct dialogtype stringdialog = {
  ui_buildstring,
  ui_keystring,
  ui_mousestring,
  ui_destroystring,
  ui_drawstring,
  ui_unselectstring
};
static void
ui_buildint (struct dialogitem *item, CONST menudialog * entry)
{
  char s[50];
  item->height = BUTTONHEIGHT;
  item->width = xtextwidth (uih->font, item->dialog->question);
  item->width1 = xtextcharw (uih->font, 'w') * 5;
  sprintf (s, "%i", item->dialog->defint);
  item->data = ui_opentext (0, 0, 2043, s);
}
static void
ui_destroyint (struct dialogitem *item, dialogparam * param)
{
  struct ui_textdata *text = (struct ui_textdata *) item->data;
  param->dint = (int) atol (text->text);
  ui_closetext (text);
}
CONST static struct dialogtype intdialog = {
  ui_buildint,
  ui_keystring,
  ui_mousestring,
  ui_destroyint,
  ui_drawstring,
  ui_unselectstring
};
static CONST char *
ui_getextension (CONST char *ch)
{
  int i = 0;
  while (ch[i])
    {
      if (ch[i] == '*')
	return (ch + i + 1);
      i++;
    }
  return ch + i;
}

number_t
ui_getfloat (CONST char *c)
{
#ifdef HAVE_LONG_DOUBLE
  long double param;
#else
  double param;
#endif
#ifdef HAVE_LONG_DOUBLE
#ifndef USE_ATOLD
#ifdef USE_XLDIO
  param = x_strtold (c, NULL);
  if (0)
#else
  if (sscanf (c, "%LG", &param) == 0)
#endif
#else
  param = _atold (c);
  if (0)
#endif
    {
#else
  if (sscanf (c, "%lG", &param) == 0)
    {
#endif
      return 0;
    }
  return (param);
}

#define BROWSEWIDTH /*(2*BORDERWIDTH+xtextcharw(uih->font,'B'))*/BUTTONHEIGHT
struct ui_filedata
{
  struct ui_textdata *text;
  int active;
  int pressed;
};

static struct dialogitem *curritem;
static void
filecallback (CONST char *name, int succ)
{
  struct ui_filedata *text = (struct ui_filedata *) curritem->data;
  if (succ)
    {
      ui_closetext (text->text);
      uih->display = 1;
      text->text = ui_opentext (0, 0, 2043, name);
      dialog.mousereleased = 0;
    }
  if (dialog.nitems == 2)
    ui_closedialog (succ);
}
static void
ui_buildfile (struct dialogitem *item, CONST menudialog * entry)
{
  char str[256];
  struct ui_filedata *data = (struct ui_filedata *) malloc (sizeof (*data));
  int i = 0;
  item->height = BUTTONHEIGHT;
  item->width = xtextwidth (uih->font, item->dialog->question);
  item->width1 = xtextcharw (uih->font, 'w') * 20;
  while (item->dialog->defstr[i] != '*')
    str[i] = item->dialog->defstr[i], i++;
  str[i] = 0;
  item->data = data;
  if (entry->type == DIALOG_OFILE)
    data->text =
      ui_opentext (0, 0, 2043,
		   ui_getfile (str, ui_getextension (item->dialog->defstr)));
  else
    data->text = ui_opentext (0, 0, 2043, item->dialog->defstr);
  data->active = 0;
  data->pressed = 0;
#ifndef _plan9_
  if (dialog.nitems == 2)
    {
      curritem = item;
      ui_buildfilesel (data->text->text, "", filecallback);
    }
#endif
}
static void
ui_destroyfile (struct dialogitem *item, dialogparam * param)
{
  struct ui_filedata *text = (struct ui_filedata *) item->data;
#ifndef _plan9_
  if (filevisible)
    ui_closefilesel (0);
#endif
  param->dpath = mystrdup (text->text->text);
  ui_closetext (text->text);
  free (text);
}
static void
ui_drawfile (struct dialogitem *item)
{
  struct ui_filedata *data = (struct ui_filedata *) item->data;
  int wholesize = dialog.width + dialog.x - dialog.half - 2 * BORDERWIDTH;
  if (data->text->width == 2043)
    {
      data->text->x = dialog.half + BORDERWIDTH,
	data->text->width = wholesize - BROWSEWIDTH - 2 * BORDERWIDTH,
	data->text->y = item->y + BORDERHEIGHT;
      ui_updatetext (data->text);
    }
  uih_drawborder (uih, dialog.half, item->y, wholesize - BROWSEWIDTH,
		  BUTTONHEIGHT, BORDER_PRESSED | BORDER_LIGHT);
  ui_drawtext (data->text, SELECTED (item) && !data->active);
  xprint (uih->image, uih->font, dialog.half - item->width,
	  item->y + BORDERHEIGHT, item->dialog->question,
	  SELECTED (item) ? SELCOLOR (uih) : FGCOLOR (uih), BGCOLOR (uih), 0);
  ui_drawquestion (item);
  ui_drawbutton ("B", data->pressed && SELECTED (item), SELECTED (item)
		 && data->active,
		 dialog.x + dialog.width - BROWSEWIDTH - BORDERWIDTH,
		 dialog.x + dialog.width - BORDERWIDTH, item->y);
}
static int
ui_keyfile (struct dialogitem *item, int key)
{
  struct ui_filedata *text = (struct ui_filedata *) item->data;
  int i = 0;
  if (!text->active)
    i = ui_textkey (text->text, key);
  if (!i)
    {
      if (key == '\t' || key == UIKEY_RIGHT)
	{
	  text->active++;
	  if (text->active > 1)
	    {
	      text->active = 0;
	      return 0;
	    }
	  uih->display = 1;
	  return 1;
	}
      if (key == UIKEY_LEFT)
	{
	  text->active--;
	  if (text->active < 0)
	    {
	      text->active = 1;
	      return 0;
	    }
	  return 1;
	}
#ifndef _plan9_
      if ((key == 13 || key == '\n') && text->active)
	{
	  curritem = item;
	  ui_buildfilesel (text->text->text, "", filecallback);
	  return 1;
	}
#endif
    }
  return (i);
}
static void
ui_mousefile (struct dialogitem *item, int x, int y, int buttons, int flags)
{
  struct ui_filedata *text = (struct ui_filedata *) item->data;
  int i;
  if (flags & MOUSE_MOVE)
    {
      if (x < dialog.x + dialog.width - BORDERWIDTH - BROWSEWIDTH)
	i = 0;
      else
	i = 1;
      if (text->active != i)
	text->active = i, uih->display = 1;
    }
#ifndef _plan9_
  if ((flags & MOUSE_RELEASE) && text->pressed)
    {
      text->pressed = 0;
      uih->display = 1;
      curritem = item;
      ui_buildfilesel (text->text->text, "", filecallback);
      return;
    }
#endif
  if (flags & MOUSE_DRAG)
    {
      if (x < dialog.x + dialog.width - BORDERWIDTH - BROWSEWIDTH)
	{
	  text->active = 0, ui_textmouse (text->text, x, y);
	  if (text->pressed)
	    text->pressed = 0, uih->display = 1;
	}
      else if (!text->pressed)
	{
	  text->pressed = 1;
	  uih->display = 1;
	}
    }
}
static void
ui_unselectfile (struct dialogitem *item)
{
  struct ui_filedata *text = (struct ui_filedata *) item->data;
  if (text->active)
    text->active = 0, uih->display = 1;
  if (text->pressed)
    text->pressed = 0, uih->display = 1;
}
CONST static struct dialogtype filedialog = {
  ui_buildfile,
  ui_keyfile,
  ui_mousefile,
  ui_destroyfile,
  ui_drawfile,
  ui_unselectfile
};
static void
ui_buildfloat (struct dialogitem *item, CONST menudialog * entry)
{
  char s[50];
  item->height = BUTTONHEIGHT;
  item->width = xtextwidth (uih->font, item->dialog->question);
  item->width1 = xtextcharw (uih->font, 'w') * 10;
  sprintf (s, "%g", (double) item->dialog->deffloat);
  item->data = ui_opentext (0, 0, 2043, s);
}
static void
ui_destroyfloat (struct dialogitem *item, dialogparam * param)
{
  struct ui_textdata *text = (struct ui_textdata *) item->data;
  param->number = ui_getfloat (text->text);
  ui_closetext (text);
}
CONST static struct dialogtype floatdialog = {
  ui_buildfloat,
  ui_keystring,
  ui_mousestring,
  ui_destroyfloat,
  ui_drawstring,
  ui_unselectstring
};
struct ui_coorddata
{
  struct ui_textdata *text[2];
  int active;
};
static void
ui_buildcoord (struct dialogitem *item, CONST menudialog * entry)
{
  char s[50];
  struct ui_coorddata *data = (struct ui_coorddata *) malloc (sizeof (*data));
  item->height = BUTTONHEIGHT;
  item->width = xtextwidth (uih->font, item->dialog->question);
  item->width1 = xtextcharw (uih->font, 'w') * 20;
  item->data = data;
  data->active = 0;
  sprintf (s, "%g", (double) item->dialog->deffloat);
  data->text[0] = ui_opentext (0, 0, 2043, s);
  sprintf (s, "%g", (double) item->dialog->deffloat2);
  data->text[1] = ui_opentext (0, 0, 2043, s);
}
static void
ui_destroycoord (struct dialogitem *item, dialogparam * param)
{
  struct ui_coorddata *data = (struct ui_coorddata *) item->data;
  param->dcoord[0] = ui_getfloat (data->text[0]->text);
  param->dcoord[1] = ui_getfloat (data->text[1]->text);
  ui_closetext (data->text[0]);
  ui_closetext (data->text[1]);
  free (data);
}

#define SPACESIZE xtextwidth(uih->font,"+")
#define ENDSIZE xtextwidth(uih->font,"i")
static void
ui_drawcoord (struct dialogitem *item)
{
  struct ui_coorddata *data = (struct ui_coorddata *) item->data;
  int wholesize =
    dialog.width + dialog.x - dialog.half - 2 * BORDERWIDTH - ENDSIZE;
  int half = (wholesize - SPACESIZE) / 2;
  if (data->text[0]->width == 2043)
    {
      data->text[0]->x = dialog.half + BORDERWIDTH,
	data->text[0]->width = half - BORDERWIDTH,
	data->text[0]->y = item->y + BORDERHEIGHT;
      data->text[1]->x = dialog.half + half + SPACESIZE + BORDERWIDTH,
	data->text[1]->width = half - BORDERWIDTH,
	data->text[1]->y = item->y + BORDERHEIGHT;
      ui_updatetext (data->text[0]);
      ui_updatetext (data->text[1]);
    }
  uih_drawborder (uih, dialog.half, item->y, half, BUTTONHEIGHT,
		  BORDER_PRESSED | BORDER_LIGHT);
  uih_drawborder (uih, dialog.half + half + SPACESIZE, item->y, half,
		  BUTTONHEIGHT, BORDER_PRESSED | BORDER_LIGHT);
  ui_drawtext (data->text[0], SELECTED (item) && !data->active);
  ui_drawtext (data->text[1], SELECTED (item) && data->active);
  xprint (uih->image, uih->font, dialog.half + half, item->y + BORDERHEIGHT,
	  "+", FGCOLOR (uih), BGCOLOR (uih), 0);
  xprint (uih->image, uih->font,
	  dialog.x + dialog.width - BORDERWIDTH - ENDSIZE,
	  item->y + BORDERHEIGHT, "i", FGCOLOR (uih), BGCOLOR (uih), 0);
  ui_drawquestion (item);
}
static int
ui_keycoord (struct dialogitem *item, int key)
{
  struct ui_coorddata *text = (struct ui_coorddata *) item->data;
  int i = ui_textkey (text->text[text->active], key);
  if (!i)
    {
      if (key == '\t' || key == UIKEY_RIGHT)
	{
	  text->active++;
	  if (text->active > 1)
	    {
	      text->active = 0;
	      return 0;
	    }
	  uih->display = 1;
	  if (key == UIKEY_RIGHT)
	    {
	      text->text[1]->cursor = 0;
	      ui_updatetext (text->text[1]);
	      uih->display = 1;
	    }
	  return 1;
	}
      if (key == UIKEY_LEFT)
	{
	  text->active--;
	  if (text->active < 0)
	    {
	      text->active = 1;
	      return 0;
	    }
	  text->text[0]->cursor = (int) strlen (text->text[0]->text);
	  ui_updatetext (text->text[0]);
	  uih->display = 1;
	  return 1;
	}
    }
  return (i);
}
static void
ui_mousecoord (struct dialogitem *item, int x, int y, int buttons, int flags)
{
  struct ui_coorddata *text = (struct ui_coorddata *) item->data;
  int i;
  if (flags & MOUSE_MOVE)
    {
      if (x < text->text[1]->x)
	i = 0;
      else
	i = 1;
      if (text->active != i)
	text->active = i, uih->display = 1;
    }
  if (flags & MOUSE_DRAG)
    {
      if (x < text->text[1]->x)
	text->active = 0, ui_textmouse (text->text[0], x, y);
      else
	text->active = 1, ui_textmouse (text->text[1], x, y);
    }
}
static void
ui_unselectcoord (struct dialogitem *item)
{
  struct ui_coorddata *text = (struct ui_coorddata *) item->data;
  text->active = 0;
}
CONST static struct dialogtype coorddialog = {
  ui_buildcoord,
  ui_keycoord,
  ui_mousecoord,
  ui_destroycoord,
  ui_drawcoord,
  ui_unselectcoord
};
struct ui_choicedata
{
  CONST char **texts;
  int selected;
  int n;

  struct uih_window *menu;
  int x, y, width, height;
  int active;
};
static void
ui_choicemenupos (struct uih_context *uih, int *x, int *y, int *width,
		  int *height, void *data)
{
  struct ui_choicedata *choice = (struct ui_choicedata *) data;
  if (filevisible || helpvisible)
    {
      *x = *y = *width = *height = 0;
      return;
    }
  *x = choice->x;
  *y = choice->y;
  *width = choice->width;
  *height = choice->height;
}
static void
ui_drawchoicemenu (uih_context * uih, void *data)
{
  struct ui_choicedata *choice = (struct ui_choicedata *) data;
  int i;
  for (i = 0; i < choice->n; i++)
    {
      xprint (uih->image, uih->font, choice->x + BORDERWIDTH,
	      choice->y + BORDERHEIGHT + i * xtextheight (uih->font),
	      choice->texts[i],
	      i == choice->active ? SELCOLOR (uih) : FGCOLOR (uih),
	      BGCOLOR (uih), 0);
    }
}
static void
ui_buildchoicemenu (struct uih_context *uih, struct ui_choicedata *choice,
		    int x, int y, int width)
{
  if (choice->menu != NULL)
    return;
  choice->width = width + 2 * BORDERWIDTH;
  choice->x = x;
  choice->height = xtextheight (uih->font) * choice->n + 2 * BORDERHEIGHT;
  choice->active = choice->selected;
  choice->y = y - choice->active * xtextheight (uih->font);
  dialog.mousegrab = 1;
  if (choice->x + choice->width > uih->image->width)
    choice->x = uih->image->width - choice->width;
  if (choice->y + choice->height > uih->image->height)
    choice->y = uih->image->height - choice->height;
  if (choice->x < 0)
    choice->x = 0;
  if (choice->y < 0)
    choice->y = 0;
  choice->menu =
    uih_registerw (uih, ui_choicemenupos, ui_drawchoicemenu, choice,
		   DRAWBORDER);
  uih->display = 1;
}
static void
ui_closechoicemenu (struct uih_context *uih, struct ui_choicedata *choice)
{
  if (choice->menu == NULL)
    return;
  uih_removew (uih, choice->menu);
  choice->menu = NULL;
  uih->display = 1;
  dialog.mousegrab = 0;
  dialog.mousereleased = 0;
}
static void
ui_buildchoice (struct dialogitem *item, CONST menudialog * entry)
{
  int i;
  struct ui_choicedata *data =
    (struct ui_choicedata *) malloc (sizeof (*data));
  item->height = BUTTONHEIGHT;
  item->width = xtextwidth (uih->font, item->dialog->question);
  item->width1 = 0;
  data->menu = NULL;

  data->texts = (CONST char **) entry->defstr;
  for (i = 0; data->texts[i] != NULL; i++)
    {
      int w = xtextwidth (uih->font, data->texts[i]);
      if (w > item->width1)
	item->width1 = w;
    }
  item->width1 += 2 * BORDERWIDTH;
  data->n = i;
  data->selected = entry->defint;
  item->data = data;
}
static void
ui_destroychoice (struct dialogitem *item, dialogparam * param)
{
  struct ui_choicedata *data = (struct ui_choicedata *) item->data;
  param->dint = data->selected;
  ui_closechoicemenu (uih, data);
  free (data);
}
static void
ui_drawchoice (struct dialogitem *item)
{
  struct ui_choicedata *data = (struct ui_choicedata *) item->data;
  uih_drawborder (uih, dialog.half, item->y, item->width1,
		  BUTTONHEIGHT | BORDER_LIGHT, 0);
  xprint (uih->image, uih->font, dialog.half + BORDERWIDTH,
	  item->y + BORDERHEIGHT, data->texts[data->selected],
	  SELECTED (item) ? SELCOLOR (uih) : FGCOLOR (uih), BGCOLOR (uih), 0);
  ui_drawquestion (item);
}
static int
ui_keychoice (struct dialogitem *item, int key)
{
  struct ui_choicedata *data = (struct ui_choicedata *) item->data;
  if (!data->menu)
    {
      switch (key)
	{
	case ' ':
	case '\n':
	case 13:
	case UIKEY_UP:
	case UIKEY_DOWN:
	case UIKEY_RIGHT:
	  ui_buildchoicemenu (uih, data, dialog.half, item->y, item->width1);
	  return (1);
	}
    }
  else
    {
      switch (key)
	{
	case ' ':
	case 13:
	case '\n':
	  data->selected = data->active;
	  ui_closechoicemenu (uih, data);
	  return (1);
	case UIKEY_DOWN:
	  data->active++;
	  data->active %= data->n;
	  uih->display = 1;
	  return (1);
	case UIKEY_UP:
	  data->active--;
	  if (data->active < 0)
	    data->active = data->n - 1;
	  uih->display = 1;
	  return (1);
	case UIKEY_ESC:
	  ui_closechoicemenu (uih, data);
	  return (1);
	}
    }
  return 0;
}
static void
ui_mousechoice (struct dialogitem *item, int x, int y, int buttons, int flags)
{
  struct ui_choicedata *data = (struct ui_choicedata *) item->data;
  int in;
  if (data->menu != NULL)
    {
      in = 0;
      if (x > data->x && y > data->y && x < data->x + data->width
	  && y < data->y + data->height)
	in = 1;
      if ((flags & MOUSE_PRESS) && !in)
	{
	  ui_closechoicemenu (uih, data);
	  return;
	}
      if ((flags & MOUSE_MOVE) && in)
	{
	  in = (y - data->y) / xtextheight (uih->font);
	  if (in < 0)
	    in = 0;
	  if (in >= data->n)
	    in = data->n - 1;
	  if (data->active != in)
	    data->active = in, uih->display = 1;
	}
      if (flags & MOUSE_RELEASE)
	{
	  data->selected = data->active;
	  ui_closechoicemenu (uih, data);
	  return;
	}
    }
  else
    {
      if (flags & MOUSE_PRESS && x > dialog.half)
	ui_buildchoicemenu (uih, data, dialog.half, item->y, item->width1);
    }
}
static void
ui_unselectchoice (struct dialogitem *item)
{
  struct ui_choicedata *data = (struct ui_choicedata *) item->data;
  ui_closechoicemenu (uih, data);
}
CONST static struct dialogtype choicedialog = {
  ui_buildchoice,
  ui_keychoice,
  ui_mousechoice,
  ui_destroychoice,
  ui_drawchoice,
  ui_unselectchoice
};

static void
ui_dialogpos (struct uih_context *c, int *x, int *y, int *width, int *height,
	      void *data)
{
  *x = dialog.x;
  *y = dialog.y;
  if (filevisible || helpvisible)
    {
      *x = *y = *width = *height = 0;
      return;
    }
  *width = dialog.width;
  *height = dialog.height;
}
static void
ui_dialogdraw (struct uih_context *c, void *data)
{
  int n;
  for (n = 0; n < dialog.nitems; n++)
    dialog.items[n].type->draw (dialog.items + n);
}

#define YSKIP 2
void
ui_builddialog (CONST menuitem * item)
{
  int n = 2;
  int ypos;
  int width1 = 0;
  if (ui_nogui)
    {
      printf ("dialog \"%s\"\n", item->shortname);
      return;
    }
  if (driver->gui_driver && driver->gui_driver->dialog)
    {
      driver->gui_driver->dialog (uih, item->shortname);
      return;
    }
  dialogvisible = 1;
  dialog.width = 0;
  dialog.height = 0;
  dialog.dialog = menu_getdialog (uih, item);
  dialog.item = item;
  for (n = 0; dialog.dialog[n].question != NULL; n++);
  n++;
  dialog.nitems = n;
  dialog.items =
    (struct dialogitem *) malloc (sizeof (struct dialogitem) * dialog.nitems);
  dialog.mousereleased = 0;
  dialog.items[dialog.nitems - 1].type = &okdialog;
  for (n = 0; n < dialog.nitems; n++)
    {
      if (n < dialog.nitems - 1)
	{
	  switch (dialog.dialog[n].type)
	    {
	    case DIALOG_STRING:
	    case DIALOG_KEYSTRING:
	      dialog.items[n].type = &stringdialog;
	      break;
	    case DIALOG_INT:
	      dialog.items[n].type = &intdialog;
	      break;
	    case DIALOG_IFILE:
	    case DIALOG_OFILE:
	      dialog.items[n].type = &filedialog;
	      break;
	    case DIALOG_FLOAT:
	      dialog.items[n].type = &floatdialog;
	      break;
	    case DIALOG_COORD:
	      dialog.items[n].type = &coorddialog;
	      break;
	    case DIALOG_CHOICE:
	      dialog.items[n].type = &choicedialog;
	      break;
	    default:
	      printf ("uidialog:unknown type!\n");
	      exit (1);
	    }
	}
      dialog.items[n].dialog = dialog.dialog + n;
      dialog.items[n].type->build (dialog.items + n, dialog.dialog + n);
      dialog.height += dialog.items[n].height;
      if (width1 < dialog.items[n].width1)
	width1 = dialog.items[n].width1;
      if (dialog.width < dialog.items[n].width)
	dialog.width = dialog.items[n].width;
    }
  dialog.height += YSKIP * (n - 1);
  n =
    xtextwidth (uih->font, gettext ("OK")) + xtextwidth (uih->font,
							 gettext ("Cancel")) +
    xtextwidth (uih->font, gettext ("Help")) + 10;
  if (dialog.width < n)
    dialog.width = n;
  dialog.half = dialog.width + 2 * BORDERWIDTH;
  dialog.width += 2 * BORDERWIDTH + width1;
  dialog.height += 2 * BORDERHEIGHT;
  dialog.current = 0;
  dialog.x = (uih->image->width - dialog.width) / 2;
  dialog.half += dialog.x;
  dialog.y = (uih->image->height - dialog.height) / 2;
  ypos = dialog.y + BORDERHEIGHT;
  for (n = 0; n < dialog.nitems; n++)
    {
      dialog.items[n].y = ypos;
      ypos += dialog.items[n].height + YSKIP;
    }
  dialog.window =
    uih_registerw (uih, ui_dialogpos, ui_dialogdraw, NULL, DRAWBORDER);
  uih->display = 1;
}
static void
ui_dialogquestion (int succesfull)
{
  if (succesfull)
    ui_menuactivate (qitem, qparam);
  else
    menu_destroydialog (qitem, qparam, uih);
}

void
ui_closedialog (int succesfull)
{
  int n = 2;
  if (dialogvisible)
    {
      dialogparam *param =
	(dialogparam *) malloc (sizeof (dialogparam) * (dialog.nitems));
      dialogvisible = 0;
      uih_removew (uih, dialog.window);
      uih->display = 1;
      for (n = 0; n < dialog.nitems; n++)
	{
	  dialog.items[n].type->destroy (dialog.items + n, param + n);
	}
      free (dialog.items);
      if (succesfull)
	{
	  for (n = 0; n < dialog.nitems - 1; n++)
	    if (dialog.dialog[n].type == DIALOG_OFILE
		&& xio_exist (param[n].dpath))
	      {
		qparam = param;
		qitem = dialog.item;
		ui_buildyesno ("File exist. Overwrite?", ui_dialogquestion);
		return;
	      }
	  ui_menuactivate (dialog.item, param);
	}
      else
	menu_destroydialog (dialog.item, param, uih);
    }
}
int
ui_dialogmouse (int x, int y, int mousebuttons, int flags)
{
  int i;
  if (ui_mouseyesno (x, y, mousebuttons, flags))
    return 1;
  if (!dialogvisible)
    return 0;
  if (!dialog.mousereleased && (flags & MOUSE_RELEASE))
    {
      dialog.mousereleased = 1;
      return 1;
    }
  if (!dialog.mousereleased && (flags & MOUSE_DRAG))
    {
      return 1;
    }
  dialog.mousereleased = 1;
  if (dialog.mousegrab)
    {
      dialog.items[dialog.current].type->mouse (dialog.items + dialog.current,
						x, y, mousebuttons, flags);
      return 1;
    }
  if (dialog.x > x || dialog.y > y || dialog.x + dialog.width < x
      || dialog.y + dialog.height < y)
    {
      if (flags & MOUSE_PRESS)
	{
	  ui_closedialog (0);
	  return 1;
	}
      dialog.items[dialog.current].type->mouse (dialog.items + dialog.current,
						x, y, 0, 0);
      return 1;
    }
  for (i = dialog.nitems - 1; i >= 0; i--)
    if (dialog.items[i].y < y)
      break;
  if (i == -1)
    {
      dialog.items[dialog.current].type->mouse (dialog.items + dialog.current,
						x, y, 0, 0);
      return 1;
    }
  if (((flags & MOUSE_PRESS) || (flags & MOUSE_MOVE)) && dialog.current != i)
    {
      dialog.items[dialog.current].type->unselect (dialog.items +
						   dialog.current);
      dialog.current = i;
      uih->display = 1;
    }
  dialog.items[i].type->mouse (dialog.items + i, x, y, mousebuttons, flags);
  return 1;
}

int
ui_dialogkeys (int key)
{
  if (ui_keyyesno (key))
    return 1;
  if (!dialogvisible)
    return 0;
  if (key == UIKEY_ESC)
    {
      ui_closedialog (0);
      return 1;
    }
  if (!dialog.items[dialog.current].
      type->key (dialog.items + dialog.current, key))
    {
      switch (key)
	{
	case 'h':
	  ui_help (dialog.item->shortname);
	  return 1;
	case UIKEY_TAB:
	case UIKEY_DOWN:
	case UIKEY_RIGHT:
	  NEXT ();
	  break;
	case UIKEY_UP:
	case UIKEY_LEFT:
	  PREV ();
	  break;
	case 13:
	case '\n':
	  ui_closedialog (1);
	}
    }
  return 1;
}
