/* X11text - text items for X11 dialogs                                */
/* XLISP-STAT 2.1 Copyright (c) 1990, 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.                       */
 
/***********************************************************************/
/**                                                                   **/
/**                    General Includes and Definitions               **/
/**                                                                   **/
/***********************************************************************/

#include "dialogs.h"

extern Display *StX11Display();
extern Point DialogStringSize();
extern LVAL StX11ItemObject();
extern char *checkstring();
extern LVAL s_window_id, s_text_length;

typedef struct {
  unsigned long fore, back;
} ColorPair;

# define STATIC_TEXT_LEAD 5
# define STATIC_TEXT_PAD 10
# define EDIT_TEXT_LEAD 10
# define EDIT_TEXT_PAD 20

/***********************************************************************/
/**                                                                   **/
/**                        Global Variables                           **/
/**                                                                   **/
/***********************************************************************/

/* configuration parameters - should be set using the defaults database */
extern XFontStruct *DialogFont;
extern unsigned long DialogBorderColor;
extern ColorPair DialogC;
extern unsigned int text_border_width;

extern GC DialogGC, DialogRGC;

extern XContext ObjectContext, TextCursorContext;

extern Cursor IBeamCursor;

/***********************************************************************/
/**                                                                   **/
/**                            Text Items                             **/
/**                                                                   **/
/***********************************************************************/

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

static VOID draw_text(dpy, win, item, erase, move_cursor)
     Display *dpy;
     Window win;
     LVAL item;
     int erase, move_cursor;
{
  char *text, *bp;
  int x, y, len, ascent, font_height, editable, cx, cy;
  Window cursor;

  ascent = DialogFont->max_bounds.ascent;
  font_height = ascent + DialogFont->max_bounds.descent;

  if (erase) XClearWindow(dpy, win);

  text = checkstring(slot_value(item, s_text));
  editable = (slot_value(item, s_editable) != NIL) ? TRUE : FALSE;

  x = (editable) ? EDIT_TEXT_LEAD : 0;
  y = (editable) ? EDIT_TEXT_LEAD + ascent : STATIC_TEXT_LEAD + ascent;
  if (editable) {
    len = strlen(text);
    if (move_cursor) {
      cy = EDIT_TEXT_LEAD;
      cx = EDIT_TEXT_LEAD + XTextWidth(DialogFont, text, len);
      if (XFindContext(dpy, win, TextCursorContext, (caddr_t *) &cursor) != 0)
	xlfail("can't find text cursor context");
      XMoveWindow(dpy, cursor, cx, cy);
    }
    XDrawString(dpy, win, DialogGC, x, y, text, len);
  }
  else {
    for(; *text != '\0'; text++, y += font_height + STATIC_TEXT_LEAD) {
      for (bp = buf;
	   *text != '\0' && *text != '\r' && *text != '\n';
	   text++, bp++)
	*bp = *text;
      *bp = '\0';
      len = strlen(buf);
      XDrawString(dpy, win, DialogGC, x, y, buf, len);
    }
  }
}

#define bufsize 20

static int do_key(report, dpy, win, item)
     XEvent report;
     Display *dpy;
     Window win;
     LVAL item;
{
  char buffer[bufsize], *text;
  int count, len, erase = FALSE, result = FALSE;
  KeySym keysym;
  /*XComposeStatus compose;*/
  
  text = checkstring(slot_value(item, s_text));
  strcpy(buf, text);

  count = XLookupString((XKeyEvent *) &report, buffer, bufsize, &keysym,
			NULL /*&compose*/);
  if (keysym == XK_Return || keysym == XK_KP_Enter || keysym == XK_Linefeed) {
    result = TRUE;
/*    XBell(dpy, 100);*/
  }
  else if ((keysym >= XK_KP_Space && keysym <= XK_KP_9) 
	   || (keysym >= XK_space && keysym <= XK_asciitilde)) {
    if ((int) strlen(buf) + count >= STRMAX) XBell(dpy, 100);
    else strncat(buf, buffer, count);
  }
  else if (keysym >= XK_Shift_L && keysym <= XK_Hyper_R)
    ; /* do nothing because it is a modifier key */
  else if (keysym >= XK_F1 && keysym <= XK_F35) {
    if (count == 0) XBell(dpy, 100);
    else if ((int) strlen(buf) + count >= STRMAX) XBell(dpy, 100);
    else strncat(buf, buffer, count);
  }
  else if (keysym == XK_BackSpace || keysym == XK_Delete) {
    if ((len = strlen(buf)) > 0) {
      buf[len - 1] = '\0';
      erase = TRUE;
    }
    else XBell(dpy, 100);
  }
  else XBell(dpy, 100);

  set_slot_value(item, s_text, cvstring(buf));
  draw_text(dpy, win, item, erase, TRUE);
  return(result);
}

static LVAL text_handler(report, modal)
     XEvent report;
     int modal;
{
  Display *dpy = StX11Display();
  Window win, cursor;
  LVAL item, result = NIL;
  int editable;

  win = report.xany.window;
  item = StX11ItemObject(dpy, win);
  editable = (slot_value(item, s_editable) != NIL) ? TRUE : FALSE;
  if (item != NIL) {
    switch (report.type) {
    case Expose:
      draw_text(dpy, win, item, FALSE, TRUE);
      break;
    case KeyPress:
      if (editable && do_key(report, dpy, win, item) && modal)
	result = slot_value(slot_value(item, s_dialog), s_default_button);
      break;
    case ButtonPress:
      break;
    case ButtonRelease:
      break;
    case EnterNotify:
      if (editable && report.xcrossing.subwindow == None
	  && report.xcrossing.focus) {
	if (XFindContext(dpy, win, TextCursorContext, (caddr_t *) &cursor)
	    != 0)
	  xlfail("can't find text cursor context");
	XSetWindowBackground(dpy, cursor, DialogC.fore);
	XClearWindow(dpy, cursor);
	XSetWindowBorderWidth(dpy, win, text_border_width + 1);
      }
      break;
    case LeaveNotify:
      if (editable && report.xcrossing.subwindow == None
	  && report.xcrossing.focus) {
	if (XFindContext(dpy, win, TextCursorContext, (caddr_t *) &cursor)
	    != 0)
	  xlfail("can't find text cursor context");
	XSetWindowBackground(dpy, cursor, DialogC.back);
	XClearWindow(dpy, cursor);
	XSetWindowBorderWidth(dpy, win, text_border_width);
      }
      break;
    default: 
      break;
    }
  }
  return(result);
}

VOID InstallTextItem(win, item)
     Window win;
     LVAL item;
{
  Display *dpy = StX11Display();
  Point loc, size;
  Window field, cursor;
  int editable, ascent;
    
  loc = ListToPoint(slot_value(item, s_location));
  size = ListToPoint(slot_value(item, s_size));
  editable = (slot_value(item, s_editable) != NIL) ? TRUE : FALSE;
  field = XCreateSimpleWindow(dpy, win, loc.h, loc.v, size.h, size.v,
			      (editable) ? text_border_width : 0,
                               DialogBorderColor, DialogC.back);
  if (editable)
    XSelectInput(dpy, field,
		 ExposureMask | EnterWindowMask | LeaveWindowMask | 
		 KeyPressMask | ButtonPressMask | ButtonReleaseMask);
  else XSelectInput(dpy, field, ExposureMask);
  if (editable) {
    XDefineCursor(dpy, field, IBeamCursor);
    ascent = DialogFont->max_bounds.ascent;
    cursor = XCreateSimpleWindow(dpy, field, 
				 EDIT_TEXT_LEAD, EDIT_TEXT_LEAD,
				 ascent / 2, ascent,
				 text_border_width, 
				 DialogBorderColor, DialogC.back);
    if (XSaveContext(dpy, field, TextCursorContext, (caddr_t) cursor) != 0)
      xlfail("can't install cursor for text field");
    XMapWindow(dpy, cursor);
  }

  set_slot_value(item, s_window_id, cvfixnum((FIXTYPE) field));

  install_dialog_item_handler(dpy, field, text_handler, item);
  if (XSaveContext(dpy, field, ObjectContext, (caddr_t) item) != 0)
    xlfail("could not install object in window");
}

VOID DeleteTextItem(win, item)
     Window win;
     LVAL item;
{
  Display *dpy = StX11Display();
  Window field;

  field = (Window) getfixnum(slot_value(item, s_window_id));

  delete_dialog_item_handler(dpy, field);
  if (XDeleteContext(dpy, field, ObjectContext) != 0)
    xlfail("could not delete object context");
  if (slot_value(item, s_editable) != NIL 
      && XDeleteContext(dpy, field, TextCursorContext) != 0)
    xlfail("could not delete cursor context");
  set_slot_value(item, s_window_id, NIL);
}

VOID DialogTextGetDefaultSize(item, width, height)
     LVAL item;
     int *width, *height;
{
  Point sz, em_sz;
  LVAL text = slot_value(item, s_text);
  LVAL text_length = slot_value(item, s_text_length);
  int w = 0;
  char *s;
  
  em_sz = DialogStringSize("M");
  sz.v = em_sz.v + 2 * STATIC_TEXT_LEAD;
  if (stringp(text)) {
    w = max_line_size(getstring(text));
    s = (char *) getstring(text);
    for (sz.v = em_sz.v + 2 * STATIC_TEXT_LEAD; *s != '\0'; s++)
      if (*s == '\n' || *s == '\r') sz.v += em_sz.v + STATIC_TEXT_LEAD;
  }
  if (fixp(text_length)) {
    w = max((int) (getfixnum(text_length) * em_sz.h), w);
  }
  if (slot_value(item, s_editable) != NIL) {
    if (width != NULL) *width = w + EDIT_TEXT_PAD;
    if (height != NULL) *height = em_sz.v + 2 * EDIT_TEXT_LEAD;
  }
  else {
    if (width != NULL) *width = w + STATIC_TEXT_PAD;
    if (height != NULL) *height = sz.v;
  }
}

LVAL DialogTextItemText(item, set, text)
     LVAL item;
     int set;
     char *text;
{
  Display *dpy = StX11Display();
  LVAL win_id;

  if (set) {
    set_slot_value(item, s_text, cvstring(text));
    win_id = slot_value(item, s_window_id);
    if (fixp(win_id)) 
      draw_text(dpy, (Window) getfixnum(win_id), item, TRUE, TRUE);
  }
  return(slot_value(item, s_text));
}

#ifdef TODO
mouse editing support
#endif /* TODO */


syntax highlighted by Code2HTML, v. 0.9.1