/*
 * Field.c -- Implementation of fields.
 *
 * Authors		: Patrick Lecoanet.
 * Creation date	: 
 *
 * $Id: Field.c,v 1.20 2004/03/23 14:53:45 lecoanet Exp $
 */

/*
 *  Copyright (c) 1993 - 2002 CENA, Patrick Lecoanet --
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This code 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this code; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include "Item.h"
#include "Types.h"
#include "WidgetInfo.h"
#include "Draw.h"
#include "Geo.h"
#include "tkZinc.h"

#include <string.h>
#include <stdlib.h>


static const char rcsid[] = "$Id: Field.c,v 1.20 2004/03/23 14:53:45 lecoanet Exp $";
static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $";


#define FIELD_SENSITIVE_BIT	1
#define FIELD_VISIBLE_BIT	2
#define FILLED_BIT		4
#define TEXT_ON_TOP_BIT		8
#define CACHE_OK		16


/*
 * Field record.
 */
typedef struct _FieldStruct {
  /* Public data */
  ZnGradient	*color;
  ZnGradient	*fill_color;
  ZnGradient	*border_color;
  char		*text;
  ZnImage	image;
  ZnImage	tile;
  Tk_Font	font;
  unsigned short flags;
  ZnBorder	border_edges;
  Tk_Justify	alignment;
  ZnReliefStyle	relief;
  ZnDim		relief_thickness;
  ZnAutoAlign	auto_alignment;
  
  /* Private data */
  ZnGradient	*gradient;
  ZnPoint	*grad_geo;
  short		orig_x;
  short		orig_y;
  short		corner_x;
  short		corner_y;
  int		insert_index;
#ifdef GL
  ZnTexFontInfo	*tfi;
#endif
} FieldStruct, *Field;


/*
 * The -text, -image, -border, -relief, -visible and
 * -filled attributes set the ZN_COORDS_FLAG to update
 * the leader that might protude if not clipped by the text.
 */
ZnAttrConfig	field_attrs[] = {
  { ZN_CONFIG_ALIGNMENT, "-alignment", NULL,
    Tk_Offset(FieldStruct, alignment), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_AUTO_ALIGNMENT, "-autoalignment", NULL,
    Tk_Offset(FieldStruct, auto_alignment), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_GRADIENT, "-backcolor", NULL,
    Tk_Offset(FieldStruct, fill_color), 0,
    ZN_DRAW_FLAG|ZN_BORDER_FLAG, False },
  { ZN_CONFIG_EDGE_LIST, "-border", NULL,
    Tk_Offset(FieldStruct, border_edges), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_GRADIENT, "-bordercolor", NULL,
    Tk_Offset(FieldStruct, border_color), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_GRADIENT, "-color", NULL,
    Tk_Offset(FieldStruct, color), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_BOOL, "-filled", NULL,
    Tk_Offset(FieldStruct, flags), FILLED_BIT, ZN_COORDS_FLAG, False },
  { ZN_CONFIG_BITMAP, "-fillpattern", NULL,
    Tk_Offset(FieldStruct, tile), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_FONT, "-font", NULL,
    Tk_Offset(FieldStruct, font), 0, ZN_COORDS_FLAG|ZN_CLFC_FLAG, False },
  { ZN_CONFIG_IMAGE, "-image", NULL,
    Tk_Offset(FieldStruct, image), 0,
    ZN_COORDS_FLAG|ZN_CLFC_FLAG, False },
  { ZN_CONFIG_RELIEF, "-relief", NULL,
    Tk_Offset(FieldStruct, relief), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_DIM, "-reliefthickness", NULL,
    Tk_Offset(FieldStruct, relief_thickness), 0, ZN_DRAW_FLAG, False },
  { ZN_CONFIG_BOOL, "-sensitive", NULL,
    Tk_Offset(FieldStruct, flags),
    FIELD_SENSITIVE_BIT, ZN_REPICK_FLAG, False },
  { ZN_CONFIG_STRING, "-text", NULL,
    Tk_Offset(FieldStruct, text), 0, ZN_COORDS_FLAG|ZN_CLFC_FLAG, False },
  { ZN_CONFIG_IMAGE, "-tile", NULL,
    Tk_Offset(FieldStruct, tile), 0, ZN_COORDS_FLAG, False },
  { ZN_CONFIG_BOOL, "-visible", NULL,
    Tk_Offset(FieldStruct, flags), FIELD_VISIBLE_BIT, 
    ZN_COORDS_FLAG|ZN_CLFC_FLAG, False }, /* Keep ZN_COORDS_FLAG here */

  { ZN_CONFIG_END, NULL, NULL, 0, 0, 0, False }
};

static	void GetLabelBBox(ZnFieldSet field_set, ZnDim *w, ZnDim *h);



/*
 **********************************************************************************
 *
 * ComputeFieldAttachment --
 *	Compute the location/size of the field, computing attachments if any.
 * 
 **********************************************************************************
 */
static void
ComputeFieldAttachment(ZnFieldSet	field_set,
		       unsigned int	field,
		       ZnBBox		*field_bbox)
{
  
  ZnBBox	ref_bbox;
  ZnDim		real_width, real_height;
  unsigned int	ref_field, num_fields;
  char		x_attach, y_attach, x_dim, y_dim;
  short		width_spec, height_spec;
  int		x_spec, y_spec, icon_width=0, icon_height=0;
  Field		fptr;
  Tk_FontMetrics fm;

  /*printf("ComputeFieldAttachment in\n");*/
  fptr = &field_set->fields[field];
  if (ISSET(fptr->flags, CACHE_OK)) {
    field_bbox->orig.x = (ZnPos) fptr->orig_x;
    field_bbox->orig.y = (ZnPos) fptr->orig_y;
    field_bbox->corner.x = fptr->corner_x;
    field_bbox->corner.y = fptr->corner_y;
    /*printf("ComputeFieldAttachment in cache\n");*/
    return;
  }

  /*
   * Preset this field to a default position/size and pretend
   * its cache is ok to break any deadlocks.
   */
  fptr->orig_x = fptr->orig_y = 0;
  fptr->corner_x = fptr->corner_y = 0;
  field_bbox->orig.x = field_bbox->orig.y = 0;
  field_bbox->corner.x = field_bbox->corner.y = 0;
  SET(fptr->flags, CACHE_OK);

  num_fields = ZnLFNumFields(field_set->label_format);
  ZnLFGetField(field_set->label_format, field,
	       &x_attach, &y_attach, &x_dim, &y_dim,
	       &x_spec, &y_spec, &width_spec, &height_spec);
  
  /*
   * First try to compute the field size which may be a factor
   * of the field content (but not a factor of other fields).
   */
  if ((fptr->image != ZnUnspecifiedImage) &&
      ((x_dim == ZN_LF_DIM_ICON) || (y_dim == ZN_LF_DIM_ICON) ||
       (x_dim == ZN_LF_DIM_AUTO) || (y_dim == ZN_LF_DIM_AUTO))) {
    ZnSizeOfImage(fptr->image, &icon_width, &icon_height);
  }

  switch (x_dim) {
  case ZN_LF_DIM_FONT:
    real_width = (ZnDim) (width_spec*Tk_TextWidth(fptr->font, "N", 1)/100);
    break;
  case ZN_LF_DIM_ICON:
    real_width = (ZnDim) (width_spec*icon_width/100);
    break;
  case ZN_LF_DIM_AUTO:
    {
      int	len = 0;
      ZnDim	text_width;

      if (fptr->text) {
	len = strlen(fptr->text);
      }
      real_width = 0.0;
      if (fptr->image != ZnUnspecifiedImage) {
	real_width = (ZnDim) icon_width;
      }
      if (len) {
	/*
	 * The 4 extra pixels are needed for border and padding.
	 */
	text_width = (ZnDim) Tk_TextWidth(fptr->font, fptr->text, len) + 4;
	real_width = text_width < real_width ? real_width : text_width;
      }
      real_width += (ZnDim) width_spec;
      break;
    }
  case ZN_LF_DIM_LABEL:
    {
      ZnDim	lh;

      GetLabelBBox(field_set, &real_width, &lh);
      break;
    }
  case ZN_LF_DIM_PIXEL:
  default:
    real_width = (ZnDim) width_spec;
    break;
  }
  /*printf("field %d, width = %g\n", field, real_width);*/

  switch (y_dim) {
  case ZN_LF_DIM_FONT:
    {
      Tk_GetFontMetrics(fptr->font, &fm);
      real_height = (ZnDim) (height_spec*(fm.ascent + fm.descent)/100);
      break;
    }
  case ZN_LF_DIM_ICON:
    real_height = (ZnDim) (height_spec*icon_height/100);
    break;
  case ZN_LF_DIM_AUTO:
    {
      ZnDim	text_height;
      
      real_height = 0.0;
      if (fptr->image != ZnUnspecifiedImage) {
	real_height = (ZnDim) icon_height;
      }
      if (fptr->text && strlen(fptr->text)) {
	Tk_GetFontMetrics(fptr->font, &fm);
	text_height = (ZnDim) (fm.ascent + fm.descent);
	real_height = text_height < real_height ? real_height : text_height;
      }
      real_height += (ZnDim) height_spec;
      break;
    }
  case ZN_LF_DIM_LABEL:
    {
      ZnDim	lw;
      
      GetLabelBBox(field_set, &lw, &real_height);
      break;
    }
  case ZN_LF_DIM_PIXEL:
  default:
    real_height = (ZnDim) height_spec;
    break;
  }
  /*printf("field %d, height = %g\n", field, real_height);*/

  /*
   * Update the cache with the newly computed infos
   * (breaking of deadlocks).
   */
  field_bbox->corner.x = real_width;
  field_bbox->corner.y = real_height;
  fptr->corner_x = (short) real_width;
  fptr->corner_y = (short) real_height;

  /*
   * Then try to deduce the position, resolving any attachments
   * if needed.
   */
  
  /*
   * Do the x axis.
   */
  if (x_dim != ZN_LF_DIM_LABEL) {
    if (x_attach == ZN_LF_ATTACH_PIXEL) {
      field_bbox->orig.x = (ZnPos) x_spec;
      field_bbox->corner.x = field_bbox->orig.x + real_width;
    }
    else {
      ref_field = x_spec;
      field_bbox->orig.x = field_bbox->corner.x = 0;
      if (ref_field >= num_fields) {
	ZnWarning ("Attached (x) to an inexistant field geometry\n");
      }
      else {
	ComputeFieldAttachment(field_set, ref_field, &ref_bbox);
	switch (x_attach) {
	case ZN_LF_ATTACH_FWD:
	  if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) {
	    field_bbox->orig.x = ref_bbox.corner.x;
	  }
	  else {
	    field_bbox->orig.x = ref_bbox.orig.x;
	  }
	  field_bbox->corner.x = field_bbox->orig.x + real_width;
	  break;
	case ZN_LF_ATTACH_BWD:
	  if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) {
	    field_bbox->corner.x = ref_bbox.orig.x;
	  }
	  else {
	    field_bbox->corner.x = ref_bbox.corner.x;
	  }
	  field_bbox->orig.x = field_bbox->corner.x - real_width;
	  break;
	case ZN_LF_ATTACH_LEFT:
	  field_bbox->orig.x = ref_bbox.orig.x;
	  field_bbox->corner.x = field_bbox->orig.x + real_width;
	  break;
	case ZN_LF_ATTACH_RIGHT:
	  if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) {
	    field_bbox->corner.x = ref_bbox.corner.x;
	  }
	  else {
	    field_bbox->corner.x = ref_bbox.orig.x;
	  }
	  field_bbox->orig.x = field_bbox->corner.x - real_width;	
	  break;
	}
      }
    }
    /*printf("field %d, x = %g\n", field, field_bbox->orig.x);*/
  }
  
  /*
   * Then the y axis.
   */
  if (y_dim != ZN_LF_DIM_LABEL) {
    if (y_attach == ZN_LF_ATTACH_PIXEL) {
      field_bbox->orig.y = (ZnPos) y_spec;
      field_bbox->corner.y = field_bbox->orig.y + real_height;
    }
    else {
      ref_field = y_spec;
      field_bbox->orig.y = field_bbox->corner.y = 0;
      if (ref_field >= num_fields) {
	ZnWarning ("Attached (y) to an inexistant field geometry\n");
      }
      else {
	ComputeFieldAttachment(field_set, ref_field, &ref_bbox);
	switch (y_attach) {
	case ZN_LF_ATTACH_FWD:
	  if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) {
	    field_bbox->orig.y = ref_bbox.corner.y;
	  }
	  else {
	    field_bbox->orig.y = ref_bbox.orig.y;
	  }
	  field_bbox->corner.y = field_bbox->orig.y + real_height;
	  break;
	case ZN_LF_ATTACH_BWD:
	  if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) {
	    field_bbox->corner.y = ref_bbox.orig.y;
	  }
	  else {
	    field_bbox->corner.y = ref_bbox.corner.y;
	  }
	  field_bbox->orig.y = field_bbox->corner.y - real_height;
	  break;
	case ZN_LF_ATTACH_LEFT:
	  field_bbox->orig.y = ref_bbox.orig.y;
	  field_bbox->corner.y = field_bbox->orig.y + real_height;
	  break;
	case ZN_LF_ATTACH_RIGHT:
	  if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) {
	    field_bbox->corner.y = ref_bbox.corner.y;
	  }
	  else {
	    field_bbox->corner.y = ref_bbox.orig.y;
	  }
	  field_bbox->orig.y = field_bbox->corner.y - real_height;	
	  break;
	}
      }
    }
    /*printf("field %d, y = %g\n", field, field_bbox->orig.y);*/
  }
  
  fptr->orig_x = (short) field_bbox->orig.x;
  fptr->orig_y = (short) field_bbox->orig.y;
  fptr->corner_x = (short) field_bbox->corner.x;
  fptr->corner_y = (short) field_bbox->corner.y;
  SET(fptr->flags, CACHE_OK);

  /*printf("ComputeFieldAttachment out\n");*/
}


/*
 **********************************************************************************
 *
 * ClearFieldCache --
 *	Reset the geometric cache of all fields depending on a given field (or
 *	of all fields if the field is < 0). Clear also the label bounding box
 *	cache if some action has been taken on a field.
 * 
 **********************************************************************************
 */
static void
ClearFieldCache(ZnFieldSet	field_set,
		int		field)
{
  unsigned int	i, num_fields; 
  ZnBool	clear_bbox;
  int		x_spec, y_spec;
  char		x_attach, y_attach, x_dim, y_dim;
  short		width_spec, height_spec;

  if (!field_set->num_fields) {
    return;
  }
  if (field < 0) {
    for (i = 0; i < field_set->num_fields; i++) {
      CLEAR(field_set->fields[i].flags, CACHE_OK);
    }
    field_set->label_width = field_set->label_height = -1.0;
    return;
  }

  clear_bbox = False;
  if (!field_set->label_format) {
    return;
  }
  num_fields = ZnLFNumFields(field_set->label_format);
  if ((unsigned int) field >= num_fields) {
    return;
  }
  ZnLFGetField(field_set->label_format, (unsigned int) field,
	       &x_attach, &y_attach, &x_dim, &y_dim,		      
	       &x_spec, &y_spec, &width_spec, &height_spec);
  if ((x_dim != ZN_LF_DIM_PIXEL) || (y_dim != ZN_LF_DIM_PIXEL)) {
    CLEAR(field_set->fields[field].flags, CACHE_OK);
    clear_bbox = True;
  }
  for (i = 0; i < num_fields; i++) {
    ZnLFGetField(field_set->label_format, i,
		 &x_attach, &y_attach, &x_dim, &y_dim,
		 &x_spec, &y_spec, &width_spec, &height_spec);
    if ((x_attach == ZN_LF_ATTACH_PIXEL) && (y_attach == ZN_LF_ATTACH_PIXEL)) {
      continue;
    }
    if (x_attach != ZN_LF_ATTACH_PIXEL) {
      if ((x_spec == field) && ISSET(field_set->fields[i].flags, CACHE_OK)) {
	CLEAR(field_set->fields[i].flags, CACHE_OK);
	ClearFieldCache(field_set, (int) i);
	clear_bbox = True;
      }
    }
    if (y_attach != ZN_LF_ATTACH_PIXEL) {
      if ((y_spec == field) && ISSET(field_set->fields[i].flags, CACHE_OK)) {
	CLEAR(field_set->fields[i].flags, CACHE_OK);
	ClearFieldCache(field_set, (int) i);
	clear_bbox = True;
      }
    }
  }

  if (clear_bbox) {
    field_set->label_width = field_set->label_height = -1.0;
  }
}


/*
 **********************************************************************************
 *
 * GetLabelBBox --
 * 
 **********************************************************************************
 */
static void
GetLabelBBox(ZnFieldSet	field_set,
	     ZnDim	*w,
	     ZnDim	*h)
{
  ZnBBox	bbox, tmp_bbox;
  ZnLabelFormat	lf;
  unsigned int	i, num_fields;
  ZnDim		clip_w, clip_h;
  
  /*printf("GetLabelBBox in\n");*/
  if ((field_set->label_width >= 0.0) && (field_set->label_height >= 0.0)) {
    *w = field_set->label_width;
    *h = field_set->label_height;
    /*printf("GetLabelBBox in cache\n");*/
    return;
  }

  lf = field_set->label_format;
  if (lf == NULL) {
    *w = *h = field_set->label_width = field_set->label_height = 0.0;
    /*printf("GetLabelBBox no labelformat\n");*/
    return;
  }

  ZnResetBBox(&bbox);
  num_fields = ZnLFNumFields(lf);
  for (i = 0; i < num_fields; i++) {
    ComputeFieldAttachment(field_set, i, &tmp_bbox);
    /*printf("field %d bbox %g %g %g %g\n", i, tmp_bbox.orig.x, tmp_bbox.orig.y,
      tmp_bbox.corner.x, tmp_bbox.corner.y);*/
    ZnAddBBoxToBBox(&bbox, &tmp_bbox);
  }
  field_set->label_width = bbox.corner.x;
  field_set->label_height = bbox.corner.y;

  /*printf("GetLabelBBox size before clipping; w = %g, h = %g\n",
	 field_set->label_width, field_set->label_height);*/
  if (ZnLFGetClipBox(lf, &clip_w, &clip_h)) {
    if (clip_w < field_set->label_width) {
      field_set->label_width = clip_w;
    }
    if (clip_h < field_set->label_height) {
      field_set->label_height = clip_h;
    }
  }
  
  *w = field_set->label_width;
  *h = field_set->label_height;
  /*printf("GetLabelBBox returns computed size; w = %g, h = %g\n", *w, *h);*/
}


/*
 **********************************************************************************
 *
 * GetFieldBBox --
 *	Compute the location of the field described
 *	by the field entry index in the item current LabelFormat.
 * 
 **********************************************************************************
 */
static void
GetFieldBBox(ZnFieldSet		field_set,
	     unsigned int	index,
	     ZnBBox		*field_bbox)
{
  ZnReal	ox, oy;
  
  /*printf("GetFieldBBox in\n");*/
  if (field_set->label_format) {
    ox = ZnNearestInt(field_set->label_pos.x);
    oy = ZnNearestInt(field_set->label_pos.y);
    ComputeFieldAttachment(field_set, index, field_bbox);
    field_bbox->orig.x += ox;
    field_bbox->orig.y += oy;
    field_bbox->corner.x += ox;
    field_bbox->corner.y += oy;
  }
  else {
    ZnResetBBox(field_bbox);
  }
  /*printf("GetFieldBBox out\n");*/
}


/*
 **********************************************************************************
 *
 * ComputeFieldTextLocation -- 
 *	Compute the position of the text in a field. This is a position
 *	that we can give to XDrawText. The position is deduced from the
 *	field bounding box passed in bbox.
 *	Return also the text bounding box.
 *
 **********************************************************************************
 */
static void
ComputeFieldTextLocation(Field		fptr,
			 ZnBBox		*bbox,
			 ZnPoint	*pos,
			 ZnBBox		*text_bbox)
{
  ZnDim		w, h;
  Tk_FontMetrics fm;

  Tk_GetFontMetrics(fptr->font, &fm);
  w = 0;
  if (fptr->text) {
    w = Tk_TextWidth(fptr->font, fptr->text, (int) strlen(fptr->text));
  }
  h = fm.ascent + fm.descent;
  text_bbox->orig.y = (bbox->orig.y + bbox->corner.y - h) / 2.0;
  text_bbox->corner.y = text_bbox->orig.y + h;
  pos->y = text_bbox->orig.y + fm.ascent;
  
  switch (fptr->alignment) {
  case TK_JUSTIFY_LEFT:
    text_bbox->orig.x = bbox->orig.x + 2;
    break;
  case TK_JUSTIFY_RIGHT:
    text_bbox->orig.x = bbox->corner.x - w - 2;
    break;
  default:
    text_bbox->orig.x = (bbox->orig.x + bbox->corner.x - w) / 2.0;
    break;
  }
  text_bbox->corner.x = text_bbox->orig.x + w;
  pos->x = text_bbox->orig.x;
}


/*
 **********************************************************************************
 *
 * LeaderToLabel --
 *	Compute the segment part of segment <start, end> that lies
 *	outside the fields of item.
 *
 **********************************************************************************
 */
static void
LeaderToLabel(ZnFieldSet	field_set,
	      ZnPoint	*start,
	      ZnPoint	*end)
{
  int		b_num;
  ZnPoint	delta, inf, sup;
  ZnPos		xt=0, yu=0, yw=0, xv=0;
  Field		fptr;
  unsigned int	i;
  ZnBBox	field_bbox;

  /* Intersection points :						*/
  /*   T |xt / delta_y	U |x1		V |y1		W |yw / delta_x	*/
  /*     |y2	  	  |yu / delta_x	  |xv / delta_y	  |x2		*/
  /*						*/
  /* y = ax + b;				*/
  /* a = delta_y / delta_x			*/
  /* b = (y * delta_x - x * delta_y) / delta_x	*/

  delta.x = start->x - end->x;
  delta.y = start->y - end->y;
  b_num   = (int) (start->y*delta.x - start->x*delta.y);
  
  for (i = 0; i < ZnLFNumFields(field_set->label_format); i++) {
    fptr = &field_set->fields[i];
    /*
     * If the field is made invisible or has no graphics of
     * its own, don't clip.
     */
    if (ISCLEAR(fptr->flags, FIELD_VISIBLE_BIT) ||
	(!fptr->text &&
	 ISCLEAR(fptr->flags, FILLED_BIT) &&
	 (fptr->border_edges == ZN_NO_BORDER) &&
	 (fptr->relief == ZN_RELIEF_FLAT) &&
	 (fptr->image == ZnUnspecifiedImage))) {
      continue;
    }

    /*
     * field_bbox is in absolute device coordinates.
     */
    GetFieldBBox(field_set, i, &field_bbox);

    /*
     * Adjust leader on real text, not on field boundaries. This is
     * important when there are leading and trailing spaces.
     */
    if (fptr->text &&
	ISCLEAR(fptr->flags, FILLED_BIT) &&
 	(fptr->border_edges == ZN_NO_BORDER) &&
	(fptr->relief == ZN_RELIEF_FLAT) &&
 	(fptr->image == ZnUnspecifiedImage)) {
      ZnBBox   text_bbox;
      ZnPoint  text_pos;	/* dummy */
      int      space_width;
      int      scan_forw, scan_back;
      space_width = Tk_TextWidth(fptr->font, " ", 1);

      ComputeFieldTextLocation(fptr, &field_bbox, &text_pos, &text_bbox);
      /*
       * Correct adjusments made by ComputeFieldTextLocation (Vincent Pomey).
       *
       * PLC: IMHO, this is to compensate for exotic fonts like 'symbolesATC'.
       *      I am not planning to port this to Tk for two reasons:
       *	  1/ Current positions are no longer implemented as characters
       *      and 2/ Tk does not give access (easily) to lbearings and rbearings.
       * This patch has been contributed by Phidias team. I don't know the
       * problem it was meant to solve.
       * text_bbox.x -= fptr->font->per_char[fptr->text[0]].lbearing + 3;
       * text_bbox.width += fptr->font->per_char[fptr->text[0]].lbearing + 3;
       */
      /*
       * Change bbox according to leading and trailing spaces.
       */
      scan_forw = 0;
      while (fptr->text[scan_forw] == ' ') {
	/* leading spaces */
	text_bbox.orig.x += space_width;
	scan_forw++;
      }

      /*
       * Empty text.
       */
      if (!fptr->text || (fptr->text[scan_forw] == 0)) {
	continue;
      }
      
      scan_back = strlen(fptr->text)-1;
      while ((fptr->text[scan_back] == ' ') && (scan_back > scan_forw)) {
	/* trailing spaces */
	text_bbox.corner.x -= space_width;
	scan_back--;
      }

      field_bbox = text_bbox;
    }

    if (field_bbox.corner.x <= field_bbox.orig.x) {
      continue;
    }
    
    if ((start->x >= field_bbox.orig.x) && (start->x < field_bbox.corner.x) &&
	(start->y >= field_bbox.orig.y) && (start->y < field_bbox.corner.y)) {
      end->x = start->x;
      end->y = start->y;
    }
    if (delta.x) {
      yu = (field_bbox.orig.x*delta.y + b_num) / delta.x;
      yw = (field_bbox.corner.x*delta.y + b_num) / delta.x;
    }
    if (delta.y) {
      xt = (field_bbox.corner.y*delta.x - b_num) / delta.y;
      xv = (field_bbox.orig.y*delta.x - b_num) / delta.y;
    }
    
    inf.x = MIN(start->x, end->x);
    sup.x = MAX(start->x, end->x);
    inf.y = MIN(start->y, end->y);
    sup.y = MAX(start->y, end->y);
    
    if (delta.x) {
      if ((yu >= field_bbox.orig.y) && (yu <= field_bbox.corner.y) &&
	  (field_bbox.orig.x >= inf.x) && (field_bbox.orig.x <= sup.x) &&
	  (yu >= inf.y) && (yu <= sup.y)) {
	end->x = field_bbox.orig.x;
	end->y = yu;
	inf.x = MIN(start->x, end->x);
	sup.x = MAX(start->x, end->x);
	inf.y = MIN(start->y, end->y);
	sup.y = MAX(start->y, end->y);
      }
      if ((yw >= field_bbox.orig.y) && (yw <= field_bbox.corner.y) &&
	  (field_bbox.corner.x >= inf.x) && (field_bbox.corner.x <= sup.x) &&
	  (yw >= inf.y) && (yw <= sup.y)) {
	end->x = field_bbox.corner.x;
	end->y = yw;
	inf.x = MIN(start->x, end->x);
	sup.x = MAX(start->x, end->x);
	inf.y = MIN(start->y, end->y);
	sup.y = MAX(start->y, end->y);
      }
    }
    if (delta.y) {
      if ((xt >= field_bbox.orig.x) && (xt <= field_bbox.corner.x) &&
	  (xt >= inf.x) && (xt <= sup.x) &&
	  (field_bbox.corner.y >= inf.y) && (field_bbox.corner.y <= sup.y)) {
	end->x = xt;
	end->y = field_bbox.corner.y;
	inf.x = MIN(start->x, end->x);
	sup.x = MAX(start->x, end->x);
	inf.y = MIN(start->y, end->y);
	sup.y = MAX(start->y, end->y);
      }
      if ((xv >= field_bbox.orig.x) && (xv <= field_bbox.corner.x) &&
	  (xv >= inf.x) && (xv <= sup.x) &&
	  (field_bbox.orig.y >= inf.y) && (field_bbox.orig.y <= sup.y)) {
	end->x = xv;
	end->y = field_bbox.orig.y;
	inf.x = MIN(start->x, end->x);
	sup.x = MAX(start->x, end->x);
	inf.y = MIN(start->y, end->y);
	sup.y = MAX(start->y, end->y);
      }
    }
  }
}


/*
 **********************************************************************************
 *
 * InitFields --
 *
 *	Perform the init of each field in a ZnFieldSet. The number of such
 *	fields must have been inited before calling this fun.
 *
 **********************************************************************************
 */
static void
InitFields(ZnFieldSet	field_set)
{
  ZnWInfo		*wi = field_set->item->wi;
  Field			field;
  unsigned int		i, num_fields;

  /*printf("size of a field = %d\n", sizeof(FieldStruct));*/
  
  if (!field_set->num_fields) {
    return;
  }
  num_fields = field_set->num_fields;
  field_set->fields = (Field) ZnMalloc(num_fields*sizeof(FieldStruct));

  for (i = 0; i < num_fields; i++){
    field = &field_set->fields[i];

    field->color = ZnGetGradientByValue(wi->fore_color);
    field->fill_color = ZnGetGradientByValue(wi->back_color);
    field->border_color = ZnGetGradientByValue(wi->fore_color);
    SET(field->flags, FIELD_VISIBLE_BIT);
    SET(field->flags, FIELD_SENSITIVE_BIT);
    CLEAR(field->flags, FILLED_BIT);
    CLEAR(field->flags, CACHE_OK);
    field->text = NULL;
    field->image = ZnUnspecifiedImage;
    field->tile = ZnUnspecifiedImage;
    field->font = Tk_GetFont(wi->interp, wi->win, Tk_NameOfFont(wi->font));
#ifdef GL
    field->tfi = NULL;
#endif
    field->border_edges = ZN_NO_BORDER;
    field->alignment = TK_JUSTIFY_LEFT;
    field->auto_alignment.automatic = False;

    field->relief = ZN_RELIEF_FLAT;
    field->relief_thickness = 2;
    SET(field->flags, TEXT_ON_TOP_BIT);

    field->gradient = NULL;
    field->grad_geo = NULL;
  }
  field_set->label_pos.x = field_set->label_pos.y = 0.0;
  field_set->label_width = field_set->label_height = -1.0;
}


/*
 **********************************************************************************
 *
 * CloneFields --
 *
 **********************************************************************************
 */
static void
CloneFields(ZnFieldSet	field_set)
{
  ZnWInfo	*wi = field_set->item->wi;
  Field		field, fields_ret;
  unsigned int	i, num_fields;
  char		*text;

  num_fields = field_set->num_fields;
  if (!num_fields) {
    return;
  }
  if (field_set->label_format) {
    field_set->label_format = ZnLFDuplicate(field_set->label_format);
  }
  fields_ret = (Field) ZnMalloc(num_fields*sizeof(FieldStruct));
  memcpy(fields_ret, field_set->fields, num_fields*sizeof(FieldStruct));
  field_set->fields = fields_ret;
  
  for (i = 0; i < num_fields; i++) {
    field = &fields_ret[i];
    if (field->gradient) {
      field->gradient = ZnGetGradientByValue(field->gradient);
    }
    if (field->grad_geo) {
      ZnPoint *grad_geo = ZnMalloc(4*sizeof(ZnPoint));
      memcpy(grad_geo, field->grad_geo, 4*sizeof(ZnPoint));
      field->grad_geo = grad_geo;    
    }    
    if (field->image != ZnUnspecifiedImage) {
      field->image = ZnGetImageByValue(field->image, ZnUpdateItemImage, field_set->item);
    }
    if (field->tile != ZnUnspecifiedImage) {
      field->tile = ZnGetImageByValue(field->tile, ZnUpdateItemImage, field_set->item);
    }
    field->font = Tk_GetFont(wi->interp, wi->win, Tk_NameOfFont(field->font));
#ifdef GL
    field->tfi = NULL;
#endif
    field->color = ZnGetGradientByValue(field->color);
    field->fill_color = ZnGetGradientByValue(field->fill_color);
    field->border_color = ZnGetGradientByValue(field->border_color);

    if (field->text) {
      text = (char *) ZnMalloc((strlen(field->text) + 1) * sizeof(char));
      strcpy(text, field->text);
      field->text = text;
    }
  }
}


/*
 **********************************************************************************
 *
 * ConfigureField -- 
 *
 **********************************************************************************
 */
static int
ConfigureField(ZnFieldSet	fs,
	       int		field,
	       int		argc,
	       Tcl_Obj *CONST	argv[],
	       int		*flags)
{
  unsigned int	i;
  Field		fptr;
  ZnBBox	bbox;
  ZnWInfo	*wi = fs->item->wi;
  XColor	*color;
  unsigned short alpha;
  int		old_num_chars, num_chars;
#ifdef GL
  Tk_Font	old_font;
#endif

  if ((field < 0) || ((unsigned int) field >= fs->num_fields)) {
    Tcl_AppendResult(wi->interp, "invalid field index", NULL);
    return TCL_ERROR;
  }
  
  fptr = &fs->fields[field];
#ifdef GL
  old_font = fptr->font;
#endif
  old_num_chars = 0;
  if (fptr->text) {
    old_num_chars = Tcl_NumUtfChars(fptr->text, (int) strlen(fptr->text));
  }

  if (ZnConfigureAttributes(wi, fs->item, fptr, field_attrs,
			    argc, argv, flags) == TCL_ERROR) {
    return TCL_ERROR;
  }

  num_chars = 0;
  if (fptr->text) {
    num_chars = Tcl_NumUtfChars(fptr->text, (int) strlen(fptr->text));
  }
  if (old_num_chars != num_chars) {
    ZnTextInfo *ti = &wi->text_info;
    /*
     * The text has changed, update the selection and
     * insertion pos to keep them valid.
     */
    if ((fs->item == ti->sel_item) && (field == ti->sel_field)) {
      if (ti->sel_last > num_chars) {
	ti->sel_last = num_chars;
      }
      if (ti->sel_first >= ti->sel_last) {
	ti->sel_item = ZN_NO_ITEM;
	ti->sel_field = ZN_NO_PART;
      }
      if ((ti->anchor_item == fs->item) && (ti->anchor_field == field) &&
	  (ti->sel_anchor > num_chars)) {
	ti->sel_anchor = num_chars;
      }
    }
    if (fptr->insert_index > num_chars) {
      fptr->insert_index = num_chars;
    }
  }

#ifdef GL
  if (old_font != fptr->font) {
    if (fptr->tfi) {
      ZnFreeTexFont(fptr->tfi);
      fptr->tfi = NULL;
    }
  }
#endif

  if (ISSET(*flags, ZN_REPICK_FLAG)) {
    SET(wi->flags, ZN_INTERNAL_NEED_REPICK);
  }
  if (ISSET(*flags, ZN_CLFC_FLAG)) {
    ClearFieldCache(fs, field);
  }
  
  if (fptr->gradient &&
      (ISSET(*flags, ZN_BORDER_FLAG) || (fptr->relief == ZN_RELIEF_FLAT))) {
    ZnFreeGradient(fptr->gradient);
    fptr->gradient = NULL;
  }
  if ((fptr->relief != ZN_RELIEF_FLAT) && !fptr->gradient) {
    color = ZnGetGradientColor(fptr->border_color, 51.0, &alpha);
    fptr->gradient = ZnGetReliefGradient(wi->interp, wi->win,
					      Tk_NameOfColor(color), alpha);
    if (fptr->gradient == NULL) {
      return TCL_ERROR;      
    }
  }

  /*
   * This is done here to limit the redraw to the area of the
   * modified fields.
   */
  if (ISCLEAR(*flags, ZN_COORDS_FLAG) &&
      fs->label_format && ISSET(*flags, ZN_DRAW_FLAG)) {
    for (i = 0; i < ZnLFNumFields(fs->label_format); i++) {
      if (i == (unsigned int) field) {
	GetFieldBBox(fs, i, &bbox);
	ZnDamage(wi, &bbox);
	break;
      }
    }
  }

  return TCL_OK;
}


/*
 **********************************************************************************
 *
 * QueryField --
 *
 **********************************************************************************
 */
static int
QueryField(ZnFieldSet		fs,
	   int			field,
	   int			argc __unused,
	   Tcl_Obj *CONST	argv[])
{
  if ((field < 0) || ((unsigned int) field >= fs->num_fields)) {
    Tcl_AppendResult(fs->item->wi->interp, "invalid field index \"", NULL);
    return TCL_ERROR;
  }
    
  if (ZnQueryAttribute(fs->item->wi, &fs->fields[field], field_attrs,
		       argv[0]) == TCL_ERROR) {
    return TCL_ERROR;
  }
  
  return TCL_OK;
}


/*
 **********************************************************************************
 *
 * FreeFields --
 *
 **********************************************************************************
 */
static void
FreeFields(ZnFieldSet	field_set)
{
  unsigned int	i, num_fields;
  Field		field;

  if (field_set->label_format) {
    ZnLFDelete(field_set->label_format);
  }
  
  num_fields = field_set->num_fields;
  for (i = 0; i < num_fields; i++) {
    field = &field_set->fields[i];
    
    if (field->text) {
      ZnFree(field->text);
    }
    if (field->gradient) {
      ZnFreeGradient(field->gradient);
    }
    if (field->grad_geo) {
      ZnFree(field->grad_geo);
    }
    if (field->image != ZnUnspecifiedImage) {
      ZnFreeImage(field->image, ZnUpdateItemImage, &field->image);
      field->image = ZnUnspecifiedImage;
    }
    if (field->tile != ZnUnspecifiedImage) {
      ZnFreeImage(field->tile, ZnUpdateItemImage, &field->tile);
      field->tile = ZnUnspecifiedImage;
    }
    Tk_FreeFont(field->font);
#ifdef GL
    if (field->tfi) {
      ZnFreeTexFont(field->tfi);
    }
#endif
    ZnFreeGradient(field->color);
    ZnFreeGradient(field->fill_color);
    ZnFreeGradient(field->border_color);
  }
  if (num_fields) {
    ZnFree(field_set->fields);
  }
}


/*
 **********************************************************************************
 *
 * FieldIndex,
 * FieldInsertChars,
 * FieldDeleteChars,
 * FieldCursor,
 * FieldSelection --
 *	These functions implement text edition in fields. The behavior
 * is the same as for Text items.
 *
 **********************************************************************************
 */
static int
FieldPointToChar(ZnFieldSet	fs,
		 unsigned int	field,
		 int		x,
		 int		y)
{
  Field		fptr;
  int		byte_index;
  ZnBBox	f_bbox, t_bbox;
  ZnPoint	t_orig;
  unsigned int	num_bytes, n, dummy;

  fptr = &fs->fields[field];
  num_bytes = 0;
  byte_index = 0;
  if (fptr->text) {
    num_bytes = strlen(fptr->text);
  }

  if (num_bytes == 0) {
    return 0;
  }
  
  GetFieldBBox(fs, field, &f_bbox);
  ComputeFieldTextLocation(fptr, &f_bbox, &t_orig, &t_bbox);

  /*
   * Point above text, returns index 0.
   */
  if (y < t_bbox.orig.y) {
    return 0;
  }
  
  if (y < t_bbox.corner.y) {
    if (x < t_bbox.orig.x) {
      /*
       * Point to the left of the current line, returns
       * index of first char.
       */
      return 0;
    }
    if (x >= t_bbox.corner.x) {
      /*
       * Point to the right of the current line, returns
       * index past the last char.
       */
      byte_index = num_bytes;
      goto convrt;
    }
    n = Tk_MeasureChars(fptr->font, fptr->text, num_bytes,
			x + 2 - (int) t_bbox.orig.x, TK_PARTIAL_OK, &dummy);
    byte_index = n - 1;
    goto convrt;
  }
  /*
   * Point below all lines, return the index after
   * the last char.
   */ 
  byte_index = num_bytes;
 convrt:
  return Tcl_NumUtfChars(fptr->text, byte_index);
}

static int
WordMoveFromIndex(char	*text,
		  int	index,
		  int	fwd)
{
  char const *strp;

  if (!text) {
    return index;
  }

  strp = Tcl_UtfAtIndex(text, index);
  if (fwd) {
    while ((strp[1] == ' ') || (strp[1] == '\n')) {
      strp++;
    }
    while ((strp[1] != ' ') && (strp[1] != '\n') && strp[1]) {
      strp++;
    }
    return Tcl_NumUtfChars(text, strp + 1 - text);
  }
  else {
    while ((strp != text) && ((strp[-1] == ' ') || (strp[-1] == '\n'))) {
      strp--;
    }
    while ((strp != text) && (strp[-1] != ' ') && (strp[-1] != '\n')) {
      strp--;
    }
    return Tcl_NumUtfChars(text, strp - text);
  }
}

static int
FieldIndex(ZnFieldSet	fs,
	   int		field,
	   Tcl_Obj	*index_spec,
	   int		*index)
{
  Field		fptr;
  ZnWInfo	*wi = fs->item->wi;
  ZnTextInfo	*ti = &wi->text_info;
  unsigned int	length;
  int		c, x, y;
  double	tmp;
  char		*end, *p;

  if ((field < 0) || ((unsigned int) field >= fs->num_fields)) {
    *index = 0;
    return TCL_OK;
  }

  fptr = &fs->fields[field];

  p = Tcl_GetString(index_spec);
  c = p[0];
  length = strlen(p);
  
  if ((c == 'e') && (strncmp(p, "end", length) == 0)) {
    *index = fptr->text ? Tcl_NumUtfChars(fptr->text, (int) strlen(fptr->text)) : 0;
  }
  else if ((c == 'e') && (length > 1) && (strncmp(p, "eol", length) == 0)) {
    *index = fptr->text ? Tcl_NumUtfChars(fptr->text, (int) strlen(fptr->text)) : 0;
  }
  else if ((c == 'b') && (length > 1) && (strncmp(p, "bol", length) == 0)) {
    *index = 0;
  }
  else if ((c == 'e') && (length > 1) && (strncmp(p, "eow", length) == 0)) {
    *index = WordMoveFromIndex(fptr->text, fptr->insert_index, 1);
  }
  else if ((c == 'b') && (length > 1) && (strncmp(p, "bow", length) == 0)) {
    *index = WordMoveFromIndex(fptr->text, fptr->insert_index, 0);
  }
  else if ((c == 'u') && (strncmp(p, "up", length) == 0)) {
    *index = fptr->insert_index;
  }
  else if ((c == 'd') && (strncmp(p, "down", length) == 0)) {
    *index = fptr->insert_index;
  }
  else if ((c == 'i') && (strncmp(p, "insert", length) == 0)) {
    *index = fptr->insert_index;
  }
  else if ((c == 's') && (strncmp(p, "sel.first", length) == 0) &&
	   (length >= 5)) {
    if ((ti->sel_item != fs->item) || (ti->sel_field != field)) {
    sel_err:
      Tcl_AppendResult(wi->interp, "selection isn't in field", (char *) NULL);
      return TCL_ERROR;
    }
    *index = ti->sel_first;
  }
  else if ((c == 's') && (strncmp(p, "sel.last", length) == 0) &&
	   (length >= 5)) {
    if ((ti->sel_item != fs->item) || (ti->sel_field != field)) {
      goto sel_err;
    }
    /*
     * We return a modified selection end so that it reflect
     * the text index of the last character _not_ the insertion
     * point between the last and the next.
     */
    *index = ti->sel_last-1;
  }
  else if (c == '@') {
    p++;
    tmp = strtod(p, &end);
    if ((end == p) || (*end != ',')) {
      goto badIndex;
    }
    x = (int) tmp;
    p = end+1;
    tmp = strtod(p, &end);
    if ((end == p) || (*end != 0)) {
      goto badIndex;
    }
    y = (int) tmp;
    
    *index = FieldPointToChar(fs, (unsigned int) field, x, y);
  }
  else if (Tcl_GetIntFromObj(wi->interp, index_spec, index) == TCL_OK) {
    int	num_chars = fptr->text ? Tcl_NumUtfChars(fptr->text, (int) strlen(fptr->text)) : 0;
    if (*index < 0){
      *index = 0;
    }
    else if (*index > num_chars) {
      *index = num_chars;
    }
  }
  else {
  badIndex:
    Tcl_AppendResult(wi->interp, "bad index \"", p, "\"", (char *) NULL);
    return TCL_ERROR;
  }
  
  return TCL_OK;  
}

static ZnBool
FieldInsertChars(ZnFieldSet	fs,
		 int		field,
		 int		*index,
		 char		*chars)
{
  Field		 fptr;
  ZnTextInfo	*ti = &fs->item->wi->text_info;
  int		num_chars, num_bytes, chars_added;
  unsigned int	byte_index, bytes_added = strlen(chars);
  char		*new;

  if ((field < 0) || ((unsigned int) field >= fs->num_fields)) {
    return False;
  }

  if (bytes_added == 0) {
    return False;
  }

  fptr = &fs->fields[field];
  num_chars = 0;
  num_bytes = 0;
  if (fptr->text) {
    num_bytes = strlen(fptr->text);
    num_chars = Tcl_NumUtfChars(fptr->text, num_bytes);
  }
  if (*index < 0) {
    *index = 0;
  }
  if (*index > num_chars) {
    *index = num_chars;
  }
  byte_index = Tcl_UtfAtIndex(fptr->text, *index) - fptr->text;
  chars_added = Tcl_NumUtfChars(chars, (int) bytes_added);

  new = ZnMalloc(num_bytes + bytes_added + 1);
  if (fptr->text) {
    /*
     * Copy the part before and the part after the new
     * text (if any).
     */
    memcpy(new, fptr->text, (size_t) byte_index);
    strcpy(new + byte_index + bytes_added, fptr->text + byte_index);
    ZnFree(fptr->text);
  }
  /*
   * Insert the new text.
   */
  memcpy(new + byte_index, chars, bytes_added);
  fptr->text = new;
  
  if (fptr->insert_index >= *index) {
    fptr->insert_index += chars_added;
  }
  if ((ti->sel_item == fs->item) && (ti->sel_field == field)) {
    if (ti->sel_first >= *index) {
      ti->sel_first += chars_added;
    }
    if (ti->sel_last >= *index) {
      ti->sel_last += chars_added;
    }
    if ((ti->anchor_item == fs->item) && (ti->anchor_field == field) &&
	(ti->sel_anchor >= *index)) {
      ti->sel_anchor += chars_added;
    }
  }

  /*
   * Need to redo the fields layout (maybe).
   */
  ClearFieldCache(fs, field);
  return True;
}

static ZnBool
FieldDeleteChars(ZnFieldSet	fs,
		 int		field,
		 int		*first,
		 int		*last)
{
  Field		 fptr;
  ZnTextInfo	*ti = &fs->item->wi->text_info;
  unsigned int	char_count, byte_count;
  unsigned int	num_bytes, num_chars, first_offset;
  char		*new;

  if ((field < 0) || ((unsigned int) field >= fs->num_fields)) {
    return False;
  }

  fptr = &fs->fields[field];
  num_chars = 0;
  num_bytes = 0;
  if (fptr->text) {
    num_bytes = strlen(fptr->text);
    num_chars = Tcl_NumUtfChars(fptr->text, (int) num_bytes);
  }
  if (num_chars == 0) {
    return False;
  }
  
  if (*first < 0) {
    *first = 0;
  }
  if (*last >= (int) num_chars) {
    *last = num_chars-1;
  }
  if (*first > *last) {
    return False;
  }
  char_count = *last + 1 - *first;
  first_offset = Tcl_UtfAtIndex(fptr->text, *first)-fptr->text;
  byte_count = Tcl_UtfAtIndex(fptr->text + first_offset, (int) char_count) -
    (fptr->text+first_offset);

  if (num_bytes - byte_count) {
    new = ZnMalloc(num_bytes + 1 - byte_count);
    memcpy(new, fptr->text, (size_t) first_offset);
    strcpy(new + first_offset, fptr->text + first_offset + byte_count);
    ZnFree(fptr->text);
    fptr->text = new;
  }
  else {
    ZnFree(fptr->text);
    fptr->text = NULL;
  }

  /*
   * Update the cursor to reflect the new string.
   */
  if (fptr->insert_index > *first) {
    fptr->insert_index -= char_count;
    if (fptr->insert_index < *first) {
      fptr->insert_index = *first;
    }
  }
  if ((ti->sel_item == fs->item) && (ti->sel_field == field)) {
    if (ti->sel_first > *first) {
      ti->sel_first -= char_count;
      if (ti->sel_first < *first) {
	ti->sel_first = *first;
      }
    }
    if (ti->sel_last >= *first) {
      ti->sel_last -= char_count;
      if (ti->sel_last < *first - 1) {
	ti->sel_last = *first - 1;
      }
    }
    if (ti->sel_first > ti->sel_last) {
      ti->sel_item = ZN_NO_ITEM;
    }
    if ((ti->anchor_item == fs->item) && (ti->anchor_field == field) &&
	(ti->sel_anchor > *first)) {
      ti->sel_anchor -= char_count;
      if (ti->sel_anchor < *first) {
	ti->sel_anchor = *first;
      }
    }
  }
  
  /*
   * Need to redo the fields layout (maybe).
   */
  ClearFieldCache(fs, field);
  return True;
}

static void
FieldCursor(ZnFieldSet	fs,
	    int		field,
	    int		index)
{
  Field	fptr;
  int	num_chars;

  if ((field < 0) || ((unsigned int) field >= fs->num_fields)) {
    return;
  }

  fptr = &fs->fields[field];
  num_chars = 0;
  if (fptr->text) {
    num_chars = Tcl_NumUtfChars(fptr->text, (int) strlen(fptr->text));
  }

  if (index < 0) {
    fptr->insert_index = 0;
  }
  else if (index > num_chars) {
    fptr->insert_index = num_chars;
  }
  else {
    fptr->insert_index = index;
  }
}

static int
FieldSelection(ZnFieldSet	fs,
	       int	field,
	       int	offset,
	       char	*chars,
	       int	max_bytes)
{
  Field	   fptr;
  int	   count;
  char	const *sel_first, *sel_last;
  ZnTextInfo *ti;

  if ((field < 0) || ((unsigned int) field >= fs->num_fields)) {
    return 0;
  }
  ti = &fs->item->wi->text_info;
  if ((ti->sel_first < 0) ||
      (ti->sel_first > ti->sel_last)) {
    return 0;
  }
  
  fptr = &fs->fields[field];
  if (!fptr->text) {
    return 0;
  }

  sel_first = Tcl_UtfAtIndex(fptr->text, ti->sel_first);
  sel_last = Tcl_UtfAtIndex(sel_first, ti->sel_last + 1 - ti->sel_first);
  count = sel_last - sel_first - offset;
  if (count <= 0) {
    return 0;
  }
  if (count > max_bytes) {
    count = max_bytes;
  }

  memcpy(chars, sel_first + offset, (size_t) count);
  chars[count] = 0;
  
  return count;
}


/*
 **********************************************************************************
 *
 * ComputeFieldImageLocation -- 
 *	Compute the bounding box of the pixmap in a field. The position is
 *	deduced from the field bounding box passed in bbox.
 *
 **********************************************************************************
 */
static void
ComputeFieldImageLocation(Field		fptr,
			  ZnBBox	*bbox,
			  ZnBBox	*pm_bbox)
{
  int	width, height;

  ZnSizeOfImage(fptr->image, &width, &height);
  pm_bbox->orig.y = (bbox->orig.y + bbox->corner.y - height) / 2;
  pm_bbox->corner.y = pm_bbox->orig.y + height;
  
  switch (fptr->alignment) {
  case TK_JUSTIFY_LEFT:
    pm_bbox->orig.x = bbox->orig.x;
    break;
  case TK_JUSTIFY_RIGHT:
    pm_bbox->orig.x = bbox->corner.x - width - 1;
    break;
  default:
    pm_bbox->orig.x = (bbox->orig.x + bbox->corner.x - width) / 2;
    break;
  }
  pm_bbox->corner.x = pm_bbox->orig.x + width;
}


/*
 **********************************************************************************
 *
 * FieldsEngine -- 
 *
 **********************************************************************************
 */
static void
FieldsEngine(ZnFieldSet	field_set,
	     void	(*cb)())
{
  ZnWInfo	*wi = field_set->item->wi;
  /*int     	i;	This one *NEED* to be an int */
  unsigned int	i, num_fields, num_chars;
  Field		fptr;
  ZnTextInfo	*ti = &wi->text_info;
  ZnBBox 	lclip_bbox, fclip_bbox, bbox, *global_clip_box;
  ZnBBox	tmp_bbox, text_bbox, pm_bbox;
  ZnPoint	pts[2];
  ZnTriStrip	tristrip;
  ZnPoint	text_pos;
  ZnBool    	restore = False;
  ZnDim		lwidth, lheight;
  ZnReal	val;
  int		cursor;
  int		sel_start, sel_stop;

  if (!field_set->num_fields) {
    return;
  }

  if (field_set->label_format && ZnLFNumFields(field_set->label_format)) {
    bbox.orig.x = ZnNearestInt(field_set->label_pos.x);
    bbox.orig.y = ZnNearestInt(field_set->label_pos.y);
    GetLabelBBox(field_set, &lwidth, &lheight);
    bbox.corner.x = bbox.orig.x + lwidth;
    bbox.corner.y = bbox.orig.y + lheight;
    ZnCurrentClip(wi, NULL, &global_clip_box, NULL);

    if (!wi->render) {
      ZnIntersectBBox(global_clip_box, &bbox, &lclip_bbox);
      if (ZnIsEmptyBBox(&lclip_bbox)) {
	return;
      }
    }
    else {
      lclip_bbox = bbox;
    }

    num_fields = ZnLFNumFields(field_set->label_format);
    for (i = 0; i < num_fields; i++) {
      fptr = &field_set->fields[i];

      if (ISCLEAR(fptr->flags, FIELD_VISIBLE_BIT)) {
	continue;
      }
      
      GetFieldBBox(field_set, i, &bbox);
      ZnIntersectBBox(&lclip_bbox, &bbox, &fclip_bbox);
      if (ZnIsEmptyBBox(&fclip_bbox)) {
	continue;
      }
      
      /* we must call XSetClipRectangles only if it's required	*/
      val = fclip_bbox.orig.x - bbox.orig.x;
      restore = val > 0;
      val = fclip_bbox.orig.y - bbox.orig.y;
      restore |= val > 0;
      val = fclip_bbox.corner.x - bbox.corner.x;
      restore |= val < 0;
      val = fclip_bbox.corner.y - bbox.corner.y;
      restore |= val < 0;

      cursor = ((field_set->item == wi->focus_item) &&
		((unsigned int) wi->focus_field == i) &&
		ISSET(wi->flags, ZN_GOT_FOCUS) && ti->cursor_on) ? 0 : -1;
      sel_start = -1, sel_stop = -1;
      ComputeFieldTextLocation(fptr, &bbox, &text_pos, &text_bbox);

      if (fptr->text) {
	if (cursor != -1) {
	  cursor = Tk_TextWidth(fptr->font, fptr->text,
				Tcl_UtfAtIndex(fptr->text,
					       fptr->insert_index)-fptr->text);
	}
	num_chars  = Tcl_NumUtfChars(fptr->text, (int) strlen(fptr->text));
	if (num_chars) {
	  if ((field_set->item == ti->sel_item) && ((unsigned int) ti->sel_field == i) &&
	      (ti->sel_last >= 0) && (ti->sel_first <= (int) num_chars)) {
	    sel_start = Tk_TextWidth(fptr->font, fptr->text,
				     Tcl_UtfAtIndex(fptr->text,
						    ti->sel_first)-fptr->text);
	    sel_stop = Tk_TextWidth(fptr->font, fptr->text,
				    Tcl_UtfAtIndex(fptr->text,
						   ti->sel_last)-fptr->text);
	  }
	  
	  ZnIntersectBBox(&fclip_bbox, &text_bbox, &tmp_bbox);
	  
	  val = tmp_bbox.orig.x - text_bbox.orig.x;
	  restore |= val > 0;
	  val = tmp_bbox.orig.y - text_bbox.orig.y;
	  restore |= val > 0;
	  val = tmp_bbox.corner.x - text_bbox.corner.x;
	  restore |= val < 0;
	  val = tmp_bbox.corner.y - text_bbox.corner.y;
	  restore |= val < 0;
	}
      }

      if (fptr->image != ZnUnspecifiedImage) {
	ComputeFieldImageLocation(fptr, &bbox, &pm_bbox);

	ZnIntersectBBox(&fclip_bbox, &pm_bbox, &tmp_bbox);
	
	val = tmp_bbox.orig.x - pm_bbox.orig.x;
	restore |= val > 0;
	val = tmp_bbox.orig.y - pm_bbox.orig.y;
	restore |= val > 0;
	val = tmp_bbox.corner.x - pm_bbox.corner.x;
	restore |= val < 0;
	val = tmp_bbox.corner.y - pm_bbox.corner.y;
	restore |= val < 0;
      }
      
      /*restore = True;*/
      if (restore) {
	/* we must clip. */
	/*printf("clip: %d\n", i);*/
	pts[0] = fclip_bbox.orig;
	pts[1] = fclip_bbox.corner;
	ZnTriStrip1(&tristrip, pts, 2, False);
	ZnPushClip(wi, &tristrip, True, True);
      }

      (*cb)(wi, fptr, &bbox, &pm_bbox,
	    &text_pos, &text_bbox, cursor, sel_start, sel_stop);

      if (restore) {
	/* Restore the previous clip. */
	ZnPopClip(wi, True);
	restore = False;
      }
    }
  }
}


/*
 **********************************************************************************
 *
 * DrawFields -- 
 *
 **********************************************************************************
 */
static void
DrawField(ZnWInfo	*wi,
	  Field		fptr,
	  ZnBBox	*bbox,
	  ZnBBox	*pm_bbox,
	  ZnPoint	*text_pos,
	  ZnBBox	*text_bbox,
	  int		cursor,
	  int		sel_start,
	  int		sel_stop)
{
  ZnTextInfo	*ti = &wi->text_info;
  XGCValues  	values;
  XRectangle	r;
  int		j, xs, num_bytes;
  int		pw, ph, fw, fh;
  TkRegion	clip_region;
  ZnBool	simple;
  Pixmap	pixmap;
  TkRegion	photo_region, clip;

  ZnBBox2XRect(bbox, &r);

  /*
   * Draw the background.
   */
  if (ISSET(fptr->flags, FILLED_BIT)) {
    values.foreground = ZnGetGradientPixel(fptr->fill_color, 0.0);
    
    if (fptr->tile != ZnUnspecifiedImage) {
      if (!ZnImageIsBitmap(fptr->tile)) { /* Fill tiled */
	values.fill_style = FillTiled;
	values.tile = ZnImagePixmap(fptr->tile, wi->win);
	values.ts_x_origin = (int) bbox->orig.x;
	values.ts_y_origin = (int) bbox->orig.y;
	XChangeGC(wi->dpy, wi->gc,
		  GCTileStipXOrigin|GCTileStipYOrigin|GCFillStyle|GCTile,
		  &values);
      }
      else { /* Fill stippled */
	values.fill_style = FillStippled;
	values.stipple = ZnImagePixmap(fptr->tile, wi->win);
	values.ts_x_origin = (int) bbox->orig.x;
	values.ts_y_origin = (int) bbox->orig.y;
	XChangeGC(wi->dpy, wi->gc,
		  GCTileStipXOrigin|GCTileStipYOrigin|GCFillStyle|GCStipple|GCForeground,
		  &values);
      }
    }
    else { /* Fill solid */
      values.fill_style = FillSolid;
      XChangeGC(wi->dpy, wi->gc, GCForeground|GCFillStyle, &values);
    }
    XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y, r.width, r.height);
  }
  
  /*
   * Draw the image and the text, which is in back depends on
   * the value of text_on_top.
   */
  for (j = 0; j < 2; j++) {
    if ((j == 0 && ISSET(fptr->flags, TEXT_ON_TOP_BIT)) ||
	(j == 1 && ISCLEAR(fptr->flags, TEXT_ON_TOP_BIT))) {
      /*
       * Draw the image.
       */
      if (fptr->image != ZnUnspecifiedImage) {
	pw = ZnNearestInt(pm_bbox->corner.x - pm_bbox->orig.x);
	ph = ZnNearestInt(pm_bbox->corner.y - pm_bbox->orig.y);
	fw = ZnNearestInt(bbox->corner.x - bbox->orig.x);
	fh = ZnNearestInt(bbox->corner.y - bbox->orig.y);

	pixmap = ZnImagePixmap(fptr->image, wi->win);
	photo_region = ZnImageRegion(fptr->image);
	ZnCurrentClip(wi, &clip_region, NULL, &simple);
	clip = TkCreateRegion();
	/*
	 * ZnImageRegion may fail: perl/Tk 800.24 doesn't support
	 * some internal TkPhoto functions.
	 * This is a workaround using a rectangular region based
	 * on the image size.
	 */
	if (photo_region == NULL) {
	  XRectangle rect;
	  rect.x = rect.y = 0;
	  rect.width = pw;
	  rect.height = ph;
	  TkUnionRectWithRegion(&rect, clip, clip);
	}
	else {
	  ZnUnionRegion(clip, photo_region, clip);
	}
	ZnOffsetRegion(clip, (int) pm_bbox->orig.x, (int) pm_bbox->orig.y);
	TkIntersectRegion(clip_region, clip, clip);
	TkSetRegion(wi->dpy, wi->gc, clip);
	XCopyArea(wi->dpy, pixmap, wi->draw_buffer, wi->gc,
		  (int) ZnNearestInt(bbox->orig.x-pm_bbox->orig.x),
		  (int) ZnNearestInt(bbox->orig.y-pm_bbox->orig.y),
		  (unsigned int) MIN(pw, fw),
		  (unsigned int) MIN(ph, fh),
		  (int) MAX(bbox->orig.x, pm_bbox->orig.x),
		  (int) MAX(bbox->orig.y, pm_bbox->orig.y));

	TkSetRegion(wi->dpy, wi->gc, clip_region);
	TkDestroyRegion(clip);
      }
    }
    else if (fptr->text) {
      /*
       * Draw the text.
       */
      num_bytes = strlen(fptr->text);
      if (num_bytes) {
	if (sel_start >= 0) {
	  values.foreground = ZnGetGradientPixel(ti->sel_color, 0.0);
	  values.fill_style = FillSolid;
	  XChangeGC(wi->dpy, wi->gc, GCForeground|GCFillStyle, &values);
	  XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc,
			 (int) (text_bbox->orig.x+sel_start),
			 (int) text_bbox->orig.y,
			 (unsigned int) (sel_stop-sel_start),
			 (unsigned int) (text_bbox->corner.y-text_bbox->orig.y));
	}
	values.foreground = ZnGetGradientPixel(fptr->color, 0.0);
	values.fill_style = FillSolid;
	values.font = Tk_FontId(fptr->font);
	XChangeGC(wi->dpy, wi->gc, GCForeground | GCFillStyle | GCFont, &values);
	Tk_DrawChars(wi->dpy, wi->draw_buffer, wi->gc, fptr->font,
		     fptr->text, num_bytes, (int) text_pos->x, (int) text_pos->y);
      }
    }
  }
  if (cursor >= 0) {
    values.line_width = ti->insert_width;
    values.foreground = ZnGetGradientPixel(ti->insert_color, 0.0);
    values.fill_style = FillSolid;
    XChangeGC(wi->dpy, wi->gc, GCForeground|GCLineWidth|GCFillStyle, &values);
    xs = (int) text_bbox->orig.x + cursor;
    XDrawLine(wi->dpy, wi->draw_buffer, wi->gc,
	      xs, (int) text_bbox->orig.y,
	      xs, (int) text_bbox->corner.y);
  }
  
  /*
   * Draw the border relief.
   */
  if ((fptr->relief != ZN_RELIEF_FLAT) && (fptr->relief_thickness > 1)) {
    ZnDrawRectangleRelief(wi, fptr->relief, fptr->gradient,
			  &r, fptr->relief_thickness);
  }
  
  /*
   * Draw the border line.
   */
  if (fptr->border_edges != ZN_NO_BORDER) {
    values.foreground = ZnGetGradientPixel(fptr->border_color, 0.0);
    values.line_width = 0;
    values.line_style = LineSolid;
    values.fill_style = FillSolid;
    XChangeGC(wi->dpy, wi->gc,
	      GCForeground | GCLineWidth | GCLineStyle | GCFillStyle, &values);
    if (fptr->border_edges & ZN_LEFT_BORDER) {
      XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y,
		r.x, r.y + r.height - 1);
    }
    if (fptr->border_edges & ZN_RIGHT_BORDER) {
      XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, 
		r.x + r.width - 1, r.y,
		r.x + r.width - 1, r.y + r.height - 1);
    }
    if (fptr->border_edges & ZN_TOP_BORDER) {
      XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, 
		r.x, r.y, r.x + r.width - 1, r.y);
    }
    if (fptr->border_edges & ZN_BOTTOM_BORDER) {
      XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, 
		r.x, r.y + r.height - 1,
		r.x + r.width - 1, r.y + r.height - 1);
    }
    if (fptr->border_edges & ZN_OBLIQUE) {
      XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, 
		r.x, r.y, r.x + r.width - 1, r.y + r.height - 1);
    }
    if (fptr->border_edges & ZN_COUNTER_OBLIQUE) {
      XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, 
		r.x, r.y + r.height - 1,
		r.x + r.width - 1, r.y);
    }
  }
}

static void
DrawFields(ZnFieldSet	field_set)
{
  FieldsEngine(field_set, DrawField);
}


/*
 **********************************************************************************
 *
 * RenderFields -- 
 *
 **********************************************************************************
 */
#ifdef GL
static void
FieldRenderCB(void *closure)
{
  ZnBBox *bbox = (ZnBBox *) closure;
  
  glBegin(GL_QUADS);
  glVertex2d(bbox->orig.x, bbox->orig.y);
  glVertex2d(bbox->orig.x, bbox->corner.y);
  glVertex2d(bbox->corner.x, bbox->corner.y);
  glVertex2d(bbox->corner.x, bbox->orig.y);
  glEnd();
}
#endif

#ifdef GL
static void
RenderField(ZnWInfo	*wi,
	    Field	fptr,
	    ZnBBox	*bbox,
	    ZnBBox	*pm_bbox,
	    ZnPoint	*text_pos,
	    ZnBBox	*text_bbox,
	    int		cursor,
	    int		sel_start,
	    int		sel_stop)
{
  unsigned short alpha;
  unsigned int	j, num_bytes;
  XColor	*color;
  ZnReal	xs;
  ZnTextInfo	*ti = &wi->text_info;

  /*
   * Draw the background.
   */
  if (ISSET(fptr->flags, FILLED_BIT)) {
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    if (!ZnGradientFlat(fptr->fill_color)) {
#if 0 /* TODO_GL Faire le dégradé dans le fond des champs. */
      int	type = fptr->fill_color->type;
      ZnBool	fast = (type == ZN_AXIAL_GRADIENT) && !fptr->grad_geo;

      RenderGradient(wi, fptr->fill_color,
		     fast ? NULL : FieldRenderCB,
		     bbox, fast ? (ZnPoint *) bbox : fptr->grad_geo);
#endif
    }
    else {
      if (fptr->tile != ZnUnspecifiedImage) { /* Fill tiled/stippled */
	ZnRenderTile(wi, fptr->tile, fptr->fill_color, FieldRenderCB, bbox,
		     (ZnPoint *) bbox);
      }
      else { /* Fill solid */
	color = ZnGetGradientColor(fptr->fill_color, 0.0, &alpha);
	alpha = ZnComposeAlpha(alpha, wi->alpha);
	glColor4us(color->red, color->green, color->blue, alpha);
	FieldRenderCB(bbox);
      }
    }
  }

  /*
   * Draw the image and the text, which one is in back depends on
   * the value of text_on_top.
   */
  for (j = 0; j < 2; j++) {
    if ((j == 0 && ISSET(fptr->flags, TEXT_ON_TOP_BIT)) ||
	(j == 1 && ISCLEAR(fptr->flags, TEXT_ON_TOP_BIT))) {
      /*
       * Draw the image.
       */
      if (fptr->image != ZnUnspecifiedImage) {
	ZnRenderIcon(wi, fptr->image, fptr->fill_color,
		     &pm_bbox->orig, False);
      }
    }
    else if (fptr->text) {
      /*
       * Draw the text.
       */
      num_bytes = strlen(fptr->text);
      if (num_bytes) {
	if (!fptr->tfi) {
	  if (! (fptr->tfi = ZnGetTexFont(wi, fptr->font))) {
	    continue;
	  }
	}
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	if (sel_start >= 0) {
	  color = ZnGetGradientColor(ti->sel_color, 0.0, &alpha);
	  alpha = ZnComposeAlpha(alpha, wi->alpha);
	  glColor4us(color->red, color->green, color->blue, alpha);
	  glBegin(GL_QUADS);
	  glVertex2d(text_bbox->orig.x+sel_start, text_bbox->orig.y);
	  glVertex2d(text_bbox->orig.x+sel_stop, text_bbox->orig.y);
	  glVertex2d(text_bbox->orig.x+sel_stop, text_bbox->corner.y);
	  glVertex2d(text_bbox->orig.x+sel_start, text_bbox->corner.y);
	  glEnd();
	}
	glEnable(GL_TEXTURE_2D);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	color = ZnGetGradientColor(fptr->color, 0.0, &alpha);
	alpha = ZnComposeAlpha(alpha, wi->alpha);
	glColor4us(color->red, color->green, color->blue, alpha);
	glBindTexture(GL_TEXTURE_2D, ZnTexFontTex(fptr->tfi));
	glPushMatrix();
	glTranslated(text_pos->x, text_pos->y, 0.0);
	ZnRenderString(fptr->tfi, fptr->text, num_bytes);
	glPopMatrix();
	glDisable(GL_TEXTURE_2D);
      }
    }
    if (cursor >= 0) {
      glLineWidth((GLfloat) ti->insert_width);
      color = ZnGetGradientColor(ti->insert_color, 0.0, &alpha);
      alpha = ZnComposeAlpha(alpha, wi->alpha);
      glColor4us(color->red, color->green, color->blue, alpha);
      xs = text_bbox->orig.x + cursor;
      glBegin(GL_LINES);
      glVertex2d(xs, text_bbox->orig.y);
      glVertex2d(xs, text_bbox->corner.y);
      glEnd();
    }
  }
  
  /*
   * Draw the border relief.
   */  
  if ((fptr->relief != ZN_RELIEF_FLAT) && (fptr->relief_thickness > 1)) {
    ZnPoint p[5];

    p[0].x = bbox->orig.x;
    p[0].y = bbox->orig.y;
    p[2].x = bbox->corner.x;
    p[2].y = bbox->corner.y;
    p[1].x = p[0].x;
    p[1].y = p[2].y;
    p[3].x = p[2].x;
    p[3].y = p[0].y;
    p[4] = p[0];
    ZnRenderPolygonRelief(wi, fptr->relief, fptr->gradient,
			  False, p, 5, fptr->relief_thickness);
  }

  /*
   * Draw the border line.
   */
  if (fptr->border_edges != ZN_NO_BORDER) {
    color = ZnGetGradientColor(fptr->border_color, 0.0, &alpha);
    alpha = ZnComposeAlpha(alpha, wi->alpha);
    glColor4us(color->red, color->green, color->blue, alpha);
    glLineWidth(1.5);
    ZnSetLineStyle(wi, ZN_LINE_SIMPLE);
    glBegin(GL_LINES);
    if (fptr->border_edges & ZN_LEFT_BORDER) {
      glVertex2d(bbox->orig.x, bbox->orig.y);
      glVertex2d(bbox->orig.x, bbox->corner.y);
    }
    if (fptr->border_edges & ZN_RIGHT_BORDER) {
      glVertex2d(bbox->corner.x, bbox->orig.y);
      glVertex2d(bbox->corner.x, bbox->corner.y);
    }
    if (fptr->border_edges & ZN_TOP_BORDER) {
      glVertex2d(bbox->orig.x, bbox->orig.y);
      glVertex2d(bbox->corner.x, bbox->orig.y);
    }
    if (fptr->border_edges & ZN_BOTTOM_BORDER) {
      glVertex2d(bbox->orig.x, bbox->corner.y);
      glVertex2d(bbox->corner.x, bbox->corner.y);
    }
    if (fptr->border_edges & ZN_OBLIQUE) {
      glVertex2d(bbox->orig.x, bbox->orig.y);
      glVertex2d(bbox->corner.x, bbox->corner.y);
    }
    if (fptr->border_edges & ZN_COUNTER_OBLIQUE) {
      glVertex2d(bbox->orig.x, bbox->corner.y);
      glVertex2d(bbox->corner.x, bbox->orig.y);
    }
    glEnd();
  }
}
#else
static void
RenderField(ZnWInfo	*wi __unused,
	    Field	fptr __unused,
	    ZnBBox	*bbox __unused,
	    ZnBBox	*pm_bbox __unused,
	    ZnPoint	*text_pos __unused,
	    ZnBBox	*text_bbox __unused,
	    int		cursor __unused,
	    int		sel_start __unused,
	    int		sel_stop __unused)
{
}
#endif

static void
RenderFields(ZnFieldSet	field_set)
{
  FieldsEngine(field_set, RenderField);
}


/*
 **********************************************************************************
 *
 * IsFieldsSensitive --
 *
 **********************************************************************************
 */
static ZnBool
IsFieldSensitive(ZnFieldSet	field_set,
		 int		part)
{
  if ((part >= 0) && ((unsigned int) part < field_set->num_fields)) {
    return ISSET(field_set->fields[part].flags, FIELD_SENSITIVE_BIT);
  }
  else {
    return False;
  }
}


/*
 **********************************************************************************
 *
 * FieldsPick --
 *	Return the first field that contains <x, y>.
 *	A field is selected if <x, y> is over a non transparent area.
 *	Such areas are : a solid filled background, a text, an icon.
 *	This does *NOT* do with *GLOBAL* visible and sensitive.
 *	But we need to take into account local field visible and
 *	sensitive as they modifiy local transparency. Local means
 *	within a single item.
 *
 **********************************************************************************
 */
static double
FieldsPick(ZnFieldSet	field_set,
	   ZnPoint	*p,
	   int		*part)
{
  Field		fptr;
  ZnBBox	bbox;
  unsigned int	best_field = 0;
  int		i;
  ZnReal	new_dist, dist = 1e40;

  if (!field_set->num_fields) {
    return dist;
  }

  if (field_set->label_format) {
    for (i = ZnLFNumFields(field_set->label_format)-1; i >= 0; i--) {    
      fptr = &field_set->fields[i];
      
      if (ISCLEAR(fptr->flags, FIELD_VISIBLE_BIT) &&
	  ISCLEAR(fptr->flags, FIELD_SENSITIVE_BIT)) {
	continue;
      }
      
      GetFieldBBox(field_set, (unsigned int) i, &bbox);

      new_dist = ZnRectangleToPointDist(&bbox, p);
      if (new_dist < dist) {
	dist = new_dist;
	best_field = i;
      }
      if (dist <= 0.0) {
	dist = 0.0;
	break;
      }
    }
  }
  
  *part = best_field;
  return dist;
}


/*
 **********************************************************************************
 *
 * FieldsToArea --
 *	Return -1 if no field is in the given area, 1 if they are
 *	all in it or 0 if there is some overlap. The function consider
 *	only fields that are either sensible or visible.
 *
 **********************************************************************************
 */
static int
FieldsToArea(ZnFieldSet	field_set,
	     ZnBBox	*area)
{
  Field		fptr;
  ZnBBox	bbox;
  int		i, inside = -1;
  ZnBool	first_done = False;
  
  if (!field_set->num_fields) {
    return inside;
  }

  for (i = ZnLFNumFields(field_set->label_format)-1; i >= 0; i--) {
    fptr = &field_set->fields[i];

    if (ISCLEAR(fptr->flags, FIELD_VISIBLE_BIT) &&
	ISCLEAR(fptr->flags, FIELD_SENSITIVE_BIT)) {
      continue;
    }

    GetFieldBBox(field_set, (unsigned int) i, &bbox);
    if (!first_done) {
      first_done = True;
      inside = ZnBBoxInBBox(&bbox, area);
      if (inside == 0) {
	return 0;
      }
    }
    else {
      if (ZnBBoxInBBox(&bbox, area) != inside) {
	return 0;
      }
    }
  }

  return inside;
}


/*
 **********************************************************************************
 *
 * SetFieldsAutoAlign --
 *
 **********************************************************************************
 */
static void
SetFieldsAutoAlign(ZnFieldSet	fs,
		   int		alignment)
{
  unsigned int	i;
  Field		field;

  if (!fs->num_fields) {
    return;
  }
  if ((alignment >= ZN_AA_LEFT) && (alignment <= ZN_AA_RIGHT)) {
    for (i = 0; i < fs->num_fields; i++) {
      field = &fs->fields[i];
      if (field->auto_alignment.automatic) {
	field->alignment = field->auto_alignment.align[alignment];
      }
    }
  }
}

static char *
GetFieldStruct(ZnFieldSet	fs,
	       int		field)
{
  if ((unsigned int) field >= fs->num_fields) {
    return NULL;
  }
  return (char *) &fs->fields[field];
}


static unsigned int
NumFields(ZnFieldSet	fs)
{
  return fs->num_fields;
}


struct _ZnFIELD ZnFIELD = {
  field_attrs,

  InitFields,
  CloneFields,
  FreeFields,
  ConfigureField,
  QueryField,
  DrawFields,
  RenderFields,
  FieldsToArea,
  IsFieldSensitive,
  FieldsPick,
  FieldIndex,
  FieldInsertChars,
  FieldDeleteChars,
  FieldCursor,
  FieldSelection,
  LeaderToLabel,
  GetLabelBBox,
  GetFieldBBox,
  SetFieldsAutoAlign,
  ClearFieldCache,
  GetFieldStruct,
  NumFields
};
