
#include <Xfwf/Pen.h>
#include <Xfwf/StrToPmap.h>

#include <xite/includes.h>
#include <X11/Xmu/Drawing.h>
#include <X11/Xmu/CharSet.h>

#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/CoreP.h>
#include <X11/Shell.h>

#include XITE_MALLOC_H

/* I have observed that resource converters can be called before the
   widgets initialize methods have been called.

   One of the results of this is that the window field of the core
   part is garbage.  We must therefore save the window field in a temp
   variable (in case it's NOT garbage), set it to zero, call XtGetGC
   (which will do some magic), and then restore the window field.

   It bites.

   */

#ifndef FUNCPROTO
int SameXfwfPen(a,b)
     XfwfPen	*a,*b;
#else /* FUNCPROTO */
int SameXfwfPen(XfwfPen *a, XfwfPen *b)
#endif /* FUNCPROTO */
{
  return 0 == memcmp((char*)a,(char*)b,sizeof(*a));
}

#ifndef FUNCPROTO
int ValidateXfwfPen(w, pen)
     Widget	w;
     XfwfPen	*pen;
#else /* FUNCPROTO */
int ValidateXfwfPen(Widget w, XfwfPen *pen)
#endif /* FUNCPROTO */
{
  Drawable	win = w->core.window;

  if (pen->gc!=0)
    return 1;

  w->core.window = 0;		/* see note above */
  pen->gc = XtGetGC(w, pen->mask, &pen->xgcv);
  w->core.window = win;

  pen->user_gc = 0;

  return pen->gc != 0;
}

#ifndef FUNCPROTO
int FreeXfwfPen(w, pen)
     Widget	w;
     XfwfPen	*pen;
#else /* FUNCPROTO */
int FreeXfwfPen(Widget w, XfwfPen *pen)
#endif /* FUNCPROTO */
{
  Screen	*screen;
  Display	*dpy;
  if (!pen->user_gc) {
    XtReleaseGC(w, pen->gc);
    pen->gc = 0;
  }
  if (!XtIsWidget(w))
    w = XtParent(w);
  screen = XtScreen(w);
  dpy = XtDisplay(w);
  if (!pen->user_xgcv) {
    if (pen->mask&GCForeground &&
	/* these checks are a bad hack */
	pen->xgcv.foreground != WhitePixelOfScreen(screen) &&
	pen->xgcv.foreground != BlackPixelOfScreen(screen))
      XFreeColors(dpy, w->core.colormap,
		  &pen->xgcv.foreground, 1, 0L);
    if (pen->mask&GCBackground &&
	/* these checks are a bad hack */
	pen->xgcv.background != WhitePixelOfScreen(screen) &&
	pen->xgcv.background != BlackPixelOfScreen(screen))
      XFreeColors(dpy, w->core.colormap,
		  &pen->xgcv.background, 1, 0L);
    if (pen->mask&GCTile)
      XFreePixmap(dpy, pen->xgcv.tile);
    if (pen->mask&GCStipple)
      XFreePixmap(dpy, pen->xgcv.stipple);
    pen->mask &= ~XfwfPenGCBits;
  }
  if (pen->colors) {
    XFreeColors(dpy, w->core.colormap, pen->colors, pen->ncolors, 0L);
    free((char*)pen->colors);
    pen->colors = 0;
  }

  return 1;			/* no failure */
}

#ifndef FUNCPROTO
static int convert_pixel(dpy, screen, cmap, name, pixel)
     Display	*dpy;
     Screen	*screen;
     Colormap	cmap;
     char	*name;
     unsigned long *pixel;
#else /* FUNCPROTO */
static int convert_pixel(Display *dpy, Screen *screen, Colormap cmap, char *name, long unsigned int *pixel)
#endif /* FUNCPROTO */
{
  if (0 == XmuCompareISOLatin1(name, XtDefaultBackground)) {
    *pixel = BlackPixelOfScreen(screen);
  } else if (0 == XmuCompareISOLatin1(name, XtDefaultForeground)) {
    *pixel = WhitePixelOfScreen(screen);
  } else {
    XColor	rval, blah;
    if (!XAllocNamedColor(dpy, cmap, name, &rval, &blah)) {
      return 0;
    }
    *pixel = rval.pixel;
  }
  return 1;
}


XtConvertArgRec  Xfwf_StringToPen_Args[] = {
  { XtWidgetBaseOffset, (XtPointer) XtOffset(Widget, core.screen),
      sizeof(Screen*) },
  { XtWidgetBaseOffset, (XtPointer) XtOffset(Widget, core.colormap),
      sizeof(Colormap) },
  { XtBaseOffset, (XtPointer) XtOffset(Object, object.self),
      sizeof(Object) },
};

int Xfwf_Num_StringToPen_Args = XtNumber(Xfwf_StringToPen_Args);

#ifndef FUNCPROTO
static Boolean ConvertStringToPen(dpy, screen, colormap, w, src, dest)
     Display	*dpy;
     Screen	*screen;
     Colormap	colormap;
     Widget	w;		/* could be an Object */
     char	*src;
     XfwfPen	*dest;
#else /* FUNCPROTO */
static Boolean ConvertStringToPen(Display *dpy, Screen *screen, Colormap colormap, Widget w, char *src, XfwfPen *dest)
           	  		/* 'w' could be an Object */
#endif /* FUNCPROTO */
{
  char		*scan, *s;

  /* zero all values */
  dest->gc = 0;
  dest->mask = 0;
  dest->colors = 0;
  dest->user_xgcv = 0;
  dest->user_gc = 0;

  src = XtNewString(src);

  scan = src;
  while (1) {
    s = scan;
    for (; *scan && *scan != ':'; scan++)
      ;
    if (scan==s) {
      if (scan!=src) {
	Drawable	win = w->core.window;

	w->core.window = 0;
	dest->gc = XtGetGC(w, dest->mask, &dest->xgcv);
	w->core.window = win;
      }
      break;
    }
    *scan = 0;
    scan++;

    /* s points to the first token, a code word */

    /* Draw using a solid color */
    if (0==XmuCompareISOLatin1(s, "solid")) {
      /* solid:pixel */
      dest->xgcv.fill_style = FillSolid;
      dest->mask |= GCFillStyle;
      if (!convert_pixel(dpy, screen, colormap, scan, &dest->xgcv.foreground))
	break;
      dest->mask |= GCForeground;
      scan += strlen(scan);

      /* draw using a foreground and a stipple pattern */
    } else if (0==XmuCompareISOLatin1(s, "stipple")) {
      /* stipple:pixel,bitmap */
      int	dummy;
      s = scan;
      for(; *scan && *scan != ','; scan++)
	;
      *scan = 0;
      scan++;

      dest->xgcv.fill_style = FillStippled;
      dest->mask |= GCFillStyle;

      if (!convert_pixel(dpy, screen, colormap, s, &dest->xgcv.foreground))
	break;
      dest->mask |= GCForeground;

      dest->xgcv.stipple = XmuLocateBitmapFile(screen, scan, (char*)0, 0,
					  &dummy, &dummy, &dummy, &dummy);
      if (dest->xgcv.stipple == None)
	break;
      dest->mask |= GCStipple;

      scan += strlen(scan);

      /* draw using a foreground and background and an opaque stipple pattern*/
    } else if (0==XmuCompareISOLatin1(s, "opaque-stipple")) {
      /* opaque-stipple:pixel,pixel,bitmap */
      int	dummy;
      s = scan;
      for(; *scan && *scan != ','; scan++)
	;
      *scan = 0;
      scan++;

      dest->xgcv.fill_style = FillOpaqueStippled;
      dest->mask |= GCFillStyle;

      if (!convert_pixel(dpy, screen, colormap, s, &dest->xgcv.foreground))
	break;
      dest->mask |= GCForeground;

      s = scan;
      for(; *scan && *scan != ','; scan++)
	;
      *scan = 0;
      scan++;

      if (!convert_pixel(dpy, screen, colormap, s, &dest->xgcv.background))
	break;
      dest->mask |= GCBackground;

      if (0==XmuCompareISOLatin1(scan, "XtCheckerBitmap")) {
	static char pixmap_bits[] = {
	  0x02, 0x01,
	};
	dest->xgcv.stipple = XCreateBitmapFromData
	  (dpy, RootWindowOfScreen(screen), pixmap_bits, 2, 2);
      } else {
	dest->xgcv.stipple = XmuLocateBitmapFile(screen, scan, (char*)0, 0,
						 &dummy, &dummy, &dummy,
						 &dummy);
      }

      if (dest->xgcv.stipple == None)
	break;
      dest->mask |= GCStipple;

      scan += strlen(scan);

#if 0
      /* Draw using a pixmap tile */
    } else if (0==XmuCompareISOLatin1(s, "tile")) {
      /* tile:pixmap */
      Visual	*visual;

      if (Xfwf_StringToPixmap_AssumeDefaultVisual) {
	visual = screen->root_visual;
      } else {
	Arg	arg;
	Widget	temp = w;
	visual = CopyFromParent;
	XtSetArg(arg, XtNvisual, &visual);
	while (temp && visual == CopyFromParent) {
	  XtGetValues(temp, &arg, 1);
	  temp = XtParent(temp);
	}
	if (!temp)
	  visual = screen->root_visual;
      }

      dest->xgcv.fill_style = FillTiled;
      dest->mask |= GCFillStyle;

#if 0
      {
	XrmValue	to, from;
	from.addr = scan;
	from.size = ?
	to.addr = &dest->xgcv.tile;
	to.size = sizeof(dest->xgcv.tile);
	XtConvertAndStore(w, XtRString, &from, XtRPixmap, &to);
      }
#else
      if (!XfwfLocatePixmapFile(screen, colormap, visual, scan,
				, (Pixmap*)0, (char**)0,
				(int*)0, (int*)0,
				&dest->colors, &dest->ncolors))
	break;
#endif
      dest->mask |= GCTile;

      scan += strlen(scan);
#endif
      /* if the depth is one, use one pen style, otherwise use the other */
    } else if (0==XmuCompareISOLatin1(s, "mono")) {
      /* mono:spec|spec */
      int	depth;

      s = scan;
      for(; *scan && *scan != '|'; scan++)
	;
      *scan = 0;
      scan++;

      depth = ((XtIsWidget(w)) ? w : XtParent(w) )
	->core.depth;

      if (depth<2) {
      /* if it's mono, parse the first spec */
	scan = s;
      } /* else, parse the second spec */
    } else {
      break;
    }
  }

  XtFree(src);

  if (dest->gc) {
    return 1;
  } else {
    /* clean up resources we might have allocated */
    FreeXfwfPen(w, dest);
    return 0;
  }
}
     

#ifndef FUNCPROTO
static Boolean XfwfCvtStringToPen(dpy, args, num_args, fromVal, toVal,
			   converter_data)
     Display   *dpy;
     XrmValue  *args;
     Cardinal  *num_args;
     XrmValue  *fromVal;
     XrmValue  *toVal;
     XtPointer *converter_data;
#else /* FUNCPROTO */
static Boolean XfwfCvtStringToPen(Display *dpy, XrmValue *args, Cardinal *num_args, XrmValue *fromVal, XrmValue *toVal, XtPointer *converter_data)
#endif /* FUNCPROTO */
{
  Screen	*screen;
  Colormap	colormap;
  Object	o;
  Widget	w;
  XfwfPen	*dest;
/*  char		*src;*/

  screen = *((Screen* *)args[0].addr);

  colormap = *((Colormap *)args[1].addr);

  o = *((Object *)args[2].addr);
  w = (Widget) o;

  {
    if (toVal->addr != NULL) {
      if (toVal->size < sizeof(XfwfPen*)) {
	toVal->size = sizeof(XfwfPen*);
	return False;
      }
    }
    else {
      static XfwfPen* static_val;
      toVal->addr = (XPointer)&static_val;
    }
    toVal->size = sizeof(XfwfPen*);
  }
  dest = *(XfwfPen**)toVal->addr = (XfwfPen*)XtMalloc(sizeof(XfwfPen));

  if (ConvertStringToPen(dpy, screen, colormap, w,
			 (char*)fromVal->addr, dest)) {
    return 1;
  } else {
    XtFree((XtPointer)dest);
    /* issue a warning */
    XtStringConversionWarning((char*)fromVal->addr, XtRXfwfPenPtr);
    return 0;
  }
}

#ifndef FUNCPROTO
static void XfwfStringToPenDestructor(app, to, converter_data, args, num_args)
      XtAppContext app;
      XrmValue *to;
      XtPointer converter_data;
      XrmValue *args;
      Cardinal *num_args;
#else /* FUNCPROTO */
static void XfwfStringToPenDestructor(XtAppContext app, XrmValue *to, XtPointer converter_data, XrmValue *args, Cardinal *num_args)
#endif /* FUNCPROTO */
{
  XfwfPen	*pen = *(XfwfPen**)to->addr;
  FreeXfwfPen((Widget)*(Object*)args[2].addr, pen);
  XtFree((char*)pen);
}

#ifndef FUNCPROTO
void XfwfInstallStringToPenConverter()
#else /* FUNCPROTO */
void XfwfInstallStringToPenConverter(void)
#endif /* FUNCPROTO */
{
  XtSetTypeConverter
    (XtRString, XtRXfwfPenPtr, XfwfCvtStringToPen,
     Xfwf_StringToPen_Args, Xfwf_Num_StringToPen_Args,
     XtCacheByDisplay|XtCacheRefCount, XfwfStringToPenDestructor);
}
