/*
** XmgCvt.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 12:08:39 1999 
** Last update Thu Oct 28 20:22:36 1999 
*/
#include <stdio.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include "XmgI.h"
#include "Xmg.h"
#include "XmgCvt.h"
#include "XmgDict.h"
/*#define HAVE_XPM	1*/
#ifdef HAVE_XPM
# include "xpm.h"
#endif

#define	done(type,value) \
	{							\
	    if (toval->addr != NULL) {				\
		if (toval->size < sizeof(type)) {		\
		    toval->size = sizeof(type);			\
		    return False;				\
		}						\
		*(type*)(toval->addr) = (value);		\
	    }							\
	    else {						\
		static type static_val;				\
		static_val = (value);				\
		toval->addr = (XtPointer)&static_val;		\
	    }							\
	    toval->size = sizeof(type);				\
	    return True;					\
	}

t_assoc				XmgGCMembersAssoc[] =
{
  {"function=",			(VOID_PTR)GCFunction},
  {"plane_mask=",		(VOID_PTR)GCPlaneMask},
  {"foreground=",		(VOID_PTR)GCForeground},
  {"background=",		(VOID_PTR)GCBackground},
  {"line_width=",		(VOID_PTR)GCLineWidth},
  {"line_style=",		(VOID_PTR)GCLineStyle},
  {"cap_style=",		(VOID_PTR)GCCapStyle},
  {"join_style=",		(VOID_PTR)GCJoinStyle},
  {"fill_style=",		(VOID_PTR)GCFillStyle},
  {"fill_rule=",		(VOID_PTR)GCFillRule},
  {"arc_mode=",			(VOID_PTR)GCArcMode},
  {"tile=",			(VOID_PTR)GCTile},
  {"stipple=",			(VOID_PTR)GCStipple},
  {"ts_x_origin=",		(VOID_PTR)GCTileStipXOrigin},
  {"ts_y_origin=",		(VOID_PTR)GCTileStipYOrigin},
  {"font=",			(VOID_PTR)GCFont},
  {"subwindow_mode=",		(VOID_PTR)GCSubwindowMode},
  {"graphics_exposures=",	(VOID_PTR)GCGraphicsExposures},
  {"clip_x_origin=",		(VOID_PTR)GCClipXOrigin},
  {"clip_y_origin=",		(VOID_PTR)GCClipYOrigin},
  {"clip_mask=",		(VOID_PTR)GCClipMask},
  {"dash_offset=",		(VOID_PTR)GCDashOffset},
  {"dashes=",			(VOID_PTR)GCDashList},
  {NULL,			NULL},
};

t_assoc				XmgGCFunctionAssoc[] = 
{ 
  {"GXclear",			(VOID_PTR)GXclear},
  {"GXand",			(VOID_PTR)GXand},
  {"GXandReverse",		(VOID_PTR)GXandReverse},
  {"GXcopy",			(VOID_PTR)GXcopy},
  {"GXandInverted",		(VOID_PTR)GXandInverted},
  {"GXnoop",			(VOID_PTR)GXnoop},
  {"GXxor",			(VOID_PTR)GXxor},
  {"GXor",			(VOID_PTR)GXor},
  {"GXnor",			(VOID_PTR)GXnor},
  {"GXequiv",			(VOID_PTR)GXequiv},
  {"GXinvert",			(VOID_PTR)GXinvert},
  {"GXorReverse",		(VOID_PTR)GXorReverse},
  {"GXcopyInverted",		(VOID_PTR)GXcopyInverted},
  {"GXorInverted",		(VOID_PTR)GXorInverted},
  {"GXnand",			(VOID_PTR)GXnand},
  {"GXset",			(VOID_PTR)GXset},
  {NULL,			NULL},
};

t_assoc				XmgGCLineStyleAssoc[] =
{
  {"LineSolid",			(VOID_PTR)LineSolid},
  {"LineOnOffDash",		(VOID_PTR)LineOnOffDash},
  {"LineDoubleDash",		(VOID_PTR)LineDoubleDash},
  {NULL,			NULL},
};

t_assoc				XmgGCCapStyleAssoc[] = 
{
  {"CapNotLast",		(VOID_PTR)CapNotLast},
  {"CapButt",			(VOID_PTR)CapButt},
  {"CapRound",			(VOID_PTR)CapRound},
  {"CapProjecting",		(VOID_PTR)CapProjecting},
  {NULL,			NULL},
};

t_assoc				XmgGCJoinStyleAssoc[] = 
{
  {"JoinMiter",			(VOID_PTR)JoinMiter},
  {"JoinRound",			(VOID_PTR)JoinRound},
  {"JoinBevel",			(VOID_PTR)JoinBevel},
  {NULL,			NULL},
};

t_assoc				XmgGCFillStyleAssoc[] = 
{
  {"FillSolid",			(VOID_PTR)FillSolid},
  {"FillTiled",			(VOID_PTR)FillTiled},
  {"FillStippled",		(VOID_PTR)FillStippled},
  {"FillOpaqueStippled",	(VOID_PTR)FillOpaqueStippled},
  {NULL,			NULL},
};

t_assoc				XmgGCFillRuleAssoc[] = 
{
  {"EvenOddRule",		(VOID_PTR)EvenOddRule},
  {"WindingRule",		(VOID_PTR)WindingRule},
  {NULL,			NULL},
};

t_assoc				XmgGCSubwindowModeAssoc[] = 
{
  {"ClipByChildren",		(VOID_PTR)ClipByChildren},
  {"IncludeInferiors",		(VOID_PTR)IncludeInferiors},
  {NULL,			NULL},
};

t_assoc				XmgGCArcModeAssoc[] = 
{
  {"ArcChord",			(VOID_PTR)ArcChord},
  {"ArcPieSlice",		(VOID_PTR)ArcPieSlice},
  {NULL,			NULL},
};

/* converts a string to a GC.
   A string must be in the form:
   "member1=value1 <newline>
   member2=value2 <newline>
   ...
   membern=valuen [<newline>]". 
   Knowing that members are the one of the XGCValues structure and values
   are the one defined in X.h. E.g
   "function=GXxor <newline>
   line_width=2 <newline>
   clip_mode=IncludeInferiors <newline>
   foreground=blue"
   It might cause some X errors if the generated XGCValues is bad. Use
   with care. */
Boolean				XmgCvtStringToGC(display,
						 arg,
						 num_args,
						 fromval,
						 toval,
						 converter_data)
Display				*display;
XrmValue			*arg;
Cardinal			*num_args;
XrmValue			*fromval;
XrmValue			*toval;
XtPointer			*converter_data;
{
  static GC			gc;
  unsigned long			mask;
  XGCValues			xgcv;
  t_status			status;
  char				buf[BUFSIZ];
  char				*str;
  char				*str2;
  char				*limit;

  buf[0] = 0;
  if ((status = str_cat_str(buf,
			    sizeof (buf),
			    (char *)(fromval->addr))) < 0)
    goto bad;
  str2 = buf;
  limit = str2 + strlen(str2);
  mask = 0;
  while (str2 < limit && 
	 ((str = index(str2,'\n')) ||
	 ((str = index(str2,0)))))
    {
      char			*member;
      unsigned long		member_mask;
      t_assoc			*assoc;
      char			*value;
            
      *str++ = 0;
      member = str2;
      str2 = str;
      if (assoc = assoc_str_ptr_from_nleft(XmgGCMembersAssoc,member))
	{
	  int			value_len;

	  member_mask = (unsigned long)(assoc->right);
	  value = member + strlen(assoc->left);
	}
      else
	{
	  XtDisplayStringConversionWarning(display,member,XtRGC);
	  continue ;
	}
#ifdef DEBUG
      if (XMG_VERB(VERB_XMG_CVT))
	{
	  assoc = assoc_str_int_from_right(XmgGCMembersAssoc,member_mask);
	  assert(assoc);
	  fprintf(stderr,"member=`%s' value=`%s'\n",
		  assoc->left,
		  value);
	}
#endif
      switch (member_mask)
	{
	case GCFunction:
	  {
	    if (assoc = assoc_str_ptr_from_left(XmgGCFunctionAssoc,value))
	      {
		xgcv.function = (int)(assoc->right);
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    break ;
	  }
	case GCForeground:
	case GCBackground:
	  {
	    XColor		screen_def_return;
	    XColor		exact_def_return;
	    t_hash_elt		*he;
	    t_boolean		from_cache;
	    Pixel		pixel;
	    
	    from_cache = FALSE;
	    if ((status = XmgPixelDictGet(value,
					  DefaultColormap(display,
					       DefaultScreen(display)),
					  &pixel)) == 0)
	      {
		from_cache = TRUE;
	      }
	    if (XAllocNamedColor(display,
				 DefaultColormap(display,
						 DefaultScreen(display)),
				 value,
				 &screen_def_return,
				 &exact_def_return) != BadColor)
	      {
		pixel = exact_def_return.pixel;
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    if (member_mask == GCForeground)
	      xgcv.foreground = pixel;
	    else
	      xgcv.background = pixel;
	    mask |= member_mask;
	    if (!from_cache)
	      {
		if ((status = XmgPixelDictAdd(value,
					DefaultColormap(display,
					       DefaultScreen(display)),      
					      pixel)) < 0)
		  {
		    XtDisplayStringConversionWarning(display,
						     "XmgPixelDict",
						     XtRGC);
		  }
	      }
	    break ;
	  }
	case GCLineWidth:
	  {
	    xgcv.line_width = atoi(value);
	    mask |= member_mask;
	    break ;
	  }
	case GCLineStyle:
	  {
	    if (assoc = assoc_str_ptr_from_left(XmgGCLineStyleAssoc,value))
	      {
		xgcv.line_style = (int)(assoc->right);
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    break ;
	  }
	case GCCapStyle:
	  {
	    if (assoc = assoc_str_ptr_from_left(XmgGCCapStyleAssoc,value))
	      {
		xgcv.cap_style = (int)(assoc->right);
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    break ;
	  }
	case GCJoinStyle:
	  {
	    if (assoc = assoc_str_ptr_from_left(XmgGCJoinStyleAssoc,value))
	      {
		xgcv.join_style = (int)(assoc->right);
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    break ;
	  }
	case GCFillStyle:
	  {
	    if (assoc = assoc_str_ptr_from_left(XmgGCFillStyleAssoc,value))
	      {
		xgcv.fill_style = (int)(assoc->right);
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    break ;
	  }
	case GCFillRule:
	  {
	    if (assoc = assoc_str_ptr_from_left(XmgGCFillRuleAssoc,value))
	      {
		xgcv.fill_rule = (int)(assoc->right);
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    break ;
	  }
	case GCArcMode:
	  {
	    if (assoc = assoc_str_ptr_from_left(XmgGCArcModeAssoc,value))
	      {
		xgcv.arc_mode = (int)(assoc->right);
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    break ;
	  }
	case GCTile:
	case GCStipple:
	case GCClipMask:
	  {
	    t_hash_elt			*he;
	    Pixmap			pixmap;
	    t_boolean			from_cache;

	    from_cache = FALSE;
	    if ((status = XmgPixmapDictGet(value,
					   AnyDepth,
					   &pixmap)) == 0)
	      {
		from_cache = TRUE;
	      }
	    else
	      {
		if (strstr(value,"xbm"))
		  {
		    unsigned int	w;
		    unsigned int	h;
		    int			x;
		    int			y;
		  
		    if (XReadBitmapFile(display,
					DefaultRootWindow(display),
					value,
					&w,
					&h,
					&pixmap,
					&x,
					&y) != BitmapSuccess)
		      {
			XtDisplayStringConversionWarning(display,value,XtRGC);
			continue ;
		      }
		  }
		else
		  {
#ifdef HAVE_XPM
		    if (strstr(value,"xpm"))
		      {
			XpmAttributes	attributes;
			Pixmap		pixmask;    
		      
			attributes.valuemask = XpmReturnPixels;
			pixmask = XtUnspecifiedPixmap;
			if (XpmReadFileToPixmap(display,
						DefaultRootWindow(display),
						value,
						&pixmap,
						&pixmask,
						&attributes) != 0)
			  {
			    XtDisplayStringConversionWarning(display,
							     value,XtRGC);
			    continue ;
			  }
		      }
		    else
#endif
		      {
			XtDisplayStringConversionWarning(display,
							 value,XtRGC);
			continue ;
		      }
		  }
	      }
	    if (member_mask == GCTile)
	      xgcv.tile = pixmap;
	    else
	      if (member_mask == GCStipple)
		xgcv.stipple = pixmap;
	      else
		xgcv.clip_mask = pixmap;
	    mask |= member_mask;
	    if (!from_cache)
	      {
		if ((status = XmgPixmapDictAdd(value,
					       AnyDepth,
					       pixmap)) < 0)
		  {
		    XtDisplayStringConversionWarning(display,
						     "XmgPixmapDict",
						     XtRGC);
		  }
	      }
	    break ;
	  }
	case GCTileStipXOrigin:
	  {
	    xgcv.ts_x_origin = atoi(value);
	    mask |= member_mask;
	    break ;
	  }
	case GCTileStipYOrigin:
	  {
	    xgcv.ts_y_origin = atoi(value);
	    mask |= member_mask;
	    break ;
	  }
	case GCFont:
	  {
	    t_hash_elt		*he;
	    XFontStruct		*xfs;
	    t_boolean		from_cache;
	    
	    from_cache = FALSE;
	    if ((status = XmgXFSDictGet(value,&xfs)) == 0)
	      {
		from_cache = TRUE;
	      }
	    else
	      if ((xfs = XLoadQueryFont(display,value)) == NULL)
		{
		  XtDisplayStringConversionWarning(display,value,XtRGC);
		  continue ;
		}
	    xgcv.font = xfs->fid;
	    mask |= member_mask;
	    if (!from_cache)
	      {
		if ((status = XmgXFSDictAdd(value,
					    xfs)) < 0)
		  {
		    XtDisplayStringConversionWarning(display,
						     "XmgXFSDict",
						     XtRGC);
		  }
	      }
	    break ;
	  }
	case GCSubwindowMode:
	  {
	    if (assoc = assoc_str_ptr_from_left(XmgGCSubwindowModeAssoc,value))
	      {
		xgcv.subwindow_mode = (int)(assoc->right);
		mask |= member_mask;
	      }
	    else
	      {
		XtDisplayStringConversionWarning(display,value,XtRGC);
		continue ;
	      }
	    break ;
	  }
	case GCGraphicsExposures:
	  {
	    xgcv.graphics_exposures = atobooleanfalse(value);
	    mask |= member_mask;
	    break ;
	  }
	case GCClipXOrigin:
	  {
	    xgcv.clip_x_origin = atoi(value);
	    mask |= member_mask;
	    break ;
	  }
	case GCClipYOrigin:
	  {
	    xgcv.clip_y_origin = atoi(value);
	    mask |= member_mask;
	    break ;
	  }
	case GCDashOffset:
	  {
	    xgcv.dash_offset = atoi(value);
	    mask |= member_mask;
	    break ;
	  }
	case GCDashList:
	  {
	    xgcv.dashes = atoi(value);
	    mask |= member_mask;
	    break ;
	  }
	default:
	  assert(0);
	}
    }
  gc = XCreateGC(display,
		 DefaultRootWindow(display),
		 mask,
		 &xgcv);
  done(GC,gc);
bad:
  XtDisplayStringConversionWarning(display,fromval->addr,XtRGC);
  return (False);
}

VOID_FUNC			XmgFreeCvtGC(app,
					     toval,
					     converter_data,
					     args,
					     num_args)
XtAppContext			app;
XrmValue			*toval;
XtPointer			converter_data;
XrmValue			*args;
Cardinal			*num_args;
{
  /* TODO */
}

/* registers Xmg converters */
VOID_FUNC			XmgRegisterConverters(VOID_DECL)
{
  static Boolean		first = True;
  static XtConvertArgRec	arg[]=
    {
      {
	XtWidgetBaseOffset,0,sizeof(CoreWidget)
      }
    };
  if (first == False)
    return;
  XtSetTypeConverter(XtRString,
		     XtRGC,
		     XmgCvtStringToGC,
		     arg,
		     XtNumber(arg),
		     XtCacheNone,
		     XmgFreeCvtGC);
  first = False;
}
