/* "Face" primitives.
   Copyright (C) 1993, 1994 Free Software Foundation.

This file is part of GNU Emacs.

GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* This is derived from work by Lucid (some parts very loosely so).  */

/* NOTE: face->font==0 does not mean unused face in this file ! */

#import <appkit/appkit.h>

#include <sys/types.h>
#include <sys/stat.h>

#include "config.h"
#include "lisp.h"

#ifdef HAVE_NS

#include "nsterm.h"
#include "buffer.h"
#include "dispextern.h"
#include "frame.h"
#include "blockinput.h"
#include "window.h"
#include "multi-frame.h"

/* The number of face-id's in use (same for all frames).  */
int ns_next_face_id;
static NXColor default_color;

int ns_face_name_id_number ( /* FRAME_PTR, Lisp_Object name */ );
void ns_recompute_basic_faces ( /* FRAME_PTR f */ );
static int new_computed_face (struct frame *f, struct ns_face *new_face);
static void ensure_face_ready (struct frame *f, int fid);
static int face_eql(const struct ns_face *f1,const struct ns_face *f2);

int ns_load_font(struct ns_face *f)
   {
   double shrink;
   float bbox1,bbox3,under;
   id font,screenfont;
   NXFontMetrics *m;

   if (!NUMBERP(ns_shrink_space)) return 4;
   shrink=XFLOATINT(ns_shrink_space);

   if (f->name==0 || f->size==-1) return 1;
   font=[Font newFont:f->name size:f->size];
   if (font==nil) return 2;

   if (screenfont=[font screenFont]) font=screenfont;

   m=[font metrics];
   if (!m->isFixedPitch) return 3;

   f->font=font;
   f->width=[font getWidthOf:"M"];

   bbox1=m->fontBBox[1];
   bbox3=m->fontBBox[3];
   under=m->underlinePosition;
   if (!m->isScreenFont)
      {
      bbox1=floor(f->size*bbox1);
      bbox3=ceil (f->size*bbox3);
      under=floor(f->size*under);
      }
   f->height=rint(shrink*bbox3) - bbox1;
   f->descender=-bbox1;
   f->underpos=under-bbox1;
   f->underwidth=rint(m->underlineThickness);
   return 0;
   }

/* Clear out face_vector and start anew.
   This should be done from time to time just to avoid
   keeping too many graphics contexts in face_vector
   that are no longer needed.  */

void ns_clear_face_cache (void)
   {
   /* Empty for NS */
   return;
   }

static int face_eql(const struct ns_face *f1,const struct ns_face *f2)
   {
   return(f1->size==f2->size &&
          f1->underline==f2->underline &&
          f1->stipple == f2->stipple &&
          NXEqualColor(f1->foreground_color,f2->foreground_color) &&
          NXEqualColor(f1->background_color,f2->background_color) &&
          (f1->name==f2->name));
   }

/* Managing parameter face arrays for frames. */

void ns_init_frame_faces (struct frame *f)
{
   ensure_face_ready (f, 0);
   ensure_face_ready (f, 1);

   f->output_data.ns->n_computed_faces=0;
   f->output_data.ns->computed_faces=0;

   new_computed_face (f, f->output_data.ns->param_faces[0]);
   new_computed_face (f, f->output_data.ns->param_faces[1]);
   ns_recompute_basic_faces(f);

#if 0
      {
      int i;
      Lisp_Object tail, frame;
      //FIXME/cl check
      FOR_EACH_FRAME (tail, frame)
         if (FRAME_NS_P (XFRAME (frame)) && XFRAME (frame) != f)
            for (i=2; i< XFRAME(frame)->output_data.ns->n_param_faces; i++)
               if (XFRAME(frame)->output_data.ns->param_faces[i])
                  ensure_face_ready (f,i);
      }
#else
   {
     Lisp_Object tail, frame, result;

     result = Qnil;
     FOR_EACH_FRAME (tail, frame)
       if (FRAME_NS_P (XFRAME (frame))
           && XFRAME (frame) != f)
         {
           result = frame;
           break;
         }

     /* If we didn't find any NS frames other than f, then we don't need
        any faces other than 0 and 1, so we're okay.  Otherwise, make
        sure that all faces valid on the selected frame are also valid
        on this new frame.  */
     if (FRAMEP (result))
       {
         int i;
         int n_faces = FRAME_N_PARAM_FACES (XFRAME (result));
         struct ns_face **faces = FRAME_PARAM_FACES (XFRAME (result));

         for (i = 2; i < n_faces; i++)
           if (faces[i])
             ensure_face_ready (f, i);
       }
   }
#endif
}


void ns_free_frame_faces (struct frame *f)
   {
   int i;

   for(i=0; i< f->output_data.ns->n_param_faces; i++)
      if (f->output_data.ns->param_faces[i])
         xfree (f->output_data.ns->param_faces[i]);

   for(i=0; i< f->output_data.ns->n_computed_faces; i++)
      if (f->output_data.ns->computed_faces[i])
         xfree (f->output_data.ns->computed_faces[i]);

   xfree (f->output_data.ns->param_faces);
   f->output_data.ns->param_faces =0;
   f->output_data.ns->n_param_faces =0;

   xfree (f->output_data.ns->computed_faces);
   f->output_data.ns->computed_faces =0;
   f->output_data.ns->n_computed_faces =0;
   }

/* Interning faces in a frame's face array.  */
static int new_computed_face (struct frame *f, struct ns_face *new_face)
   {
   int i;

   if (f->output_data.ns->n_computed_faces >= f->output_data.ns->size_computed_faces)
      {
      int new_size = f->output_data.ns->n_computed_faces + 32;

      f->output_data.ns->computed_faces = (struct ns_face **)
         xrealloc (f->output_data.ns->computed_faces,
                   new_size * sizeof (struct ns_face *));
      f->output_data.ns->size_computed_faces = new_size;
      }

   i = f->output_data.ns->n_computed_faces++;
   f->output_data.ns->computed_faces[i] =
      (struct ns_face *) xmalloc (sizeof(struct ns_face));

   *(f->output_data.ns->computed_faces[i]) = *new_face;
   return i;
   }


/* Find a match for NEW_FACE in a FRAME's computed face array, and add
   it if we don't find one.  */
static int intern_computed_face (struct frame *f, struct ns_face *new_face)
   {
   int i;

   /* Search for a computed face already on F equivalent to FACE.  */
   for (i = 0; i < f->output_data.ns->n_computed_faces; i++)
      {
      if (! f->output_data.ns->computed_faces[i])
         abort ();
      if (face_eql(new_face,f->output_data.ns->computed_faces[i]))
         return i;
      }

   /* We didn't find one; add a new one.  */
   return new_computed_face (f, new_face);
   }

/* Make parameter face id ID valid on frame F.  */
static void ensure_face_ready (struct frame *f, int fid)
   {
   if (f->output_data.ns->n_param_faces <= fid)
      {
      int n = fid + 10;
      f->output_data.ns->param_faces = (struct ns_face **)
         xrealloc (f->output_data.ns->param_faces,sizeof (struct ns_face *) * n);
      bzero (f->output_data.ns->param_faces + f->output_data.ns->n_param_faces,
             (n - f->output_data.ns->n_param_faces) * sizeof (struct ns_face *));

      f->output_data.ns->n_param_faces = n;
      }

   if (f->output_data.ns->param_faces [fid] == 0)
      {
      struct ns_face *nface=(struct ns_face *) xmalloc(sizeof (*nface));
      nface->underline=-1;
      nface->font=0;
      nface->name=0;
      nface->size=-1;
      nface->foreground_color=default_color;
      nface->background_color=default_color;
      nface->stipple = nil;
      f->output_data.ns->param_faces [fid] = nface;
      }
   }


DEFUN ("ns-pixmap-spec-p", Fns_pixmap_spec_p, Sns_pixmap_spec_p, 1, 1, 0,
  "Return t if OBJECT is a valid pixmap specification.")
  (object)
     Lisp_Object object;
{
  Lisp_Object height, width;

  return ((STRINGP (object)
	   || (CONSP (object)
	       && CONSP (XCONS (object)->cdr)
	       && CONSP (XCONS (XCONS (object)->cdr)->cdr)
	       && NILP (XCONS (XCONS (XCONS (object)->cdr)->cdr)->cdr)
	       && (width = XCONS (object)->car, INTEGERP (width))
	       && (height = XCONS (XCONS (object)->cdr)->car, INTEGERP (height))
	       && STRINGP (XCONS (XCONS (XCONS (object)->cdr)->cdr)->car)
	       && XINT (width) > 0
	       && XINT (height) > 0
	       /* The string must have enough bits for width * height.  */
	       && ((XSTRING (XCONS (XCONS (XCONS (object)->cdr)->cdr)->car)->size
		    * (BITS_PER_INT / sizeof (int)))
		   >= XFASTINT (width) * XFASTINT (height)))
           || (CONSP (object)
               && CONSP (XCONS (object)->cdr)
	       && CONSP (XCONS (XCONS (object)->cdr)->cdr)
	       && NILP (XCONS (XCONS (XCONS (object)->cdr)->cdr)->cdr)
	       && (width = XCONS (object)->car, INTEGERP (width))
	       && (height = XCONS (XCONS (object)->cdr)->car, INTEGERP (height))
	       && CONSP (XCONS (XCONS (XCONS (object)->cdr)->cdr)->car)
               && XINT (width) > 0
	       && XINT (height) > 0))
	  ? Qt : Qnil);
}

/* Load a bitmap according to NAME (which is either a file name
   or a pixmap spec).  Return the EmacsImage object
   or get an error if NAME is invalid.

   Store the bitmap width in *W_PTR and height in *H_PTR.  */

static id
load_pixmap (f, name, w_ptr, h_ptr)
     FRAME_PTR f;
     Lisp_Object name;
     unsigned int *w_ptr, *h_ptr;
{
  id bitmap_id = nil;
  Lisp_Object tem;
  NXSize s;

  if (NILP (name))
    return nil;

  tem = Fns_pixmap_spec_p (name);
  if (NILP (tem))
    wrong_type_argument (Qpixmap_spec_p, name);

  if (CONSP (name))
    {
      /* Decode a bitmap spec into a bitmap.  */
      int h, w;
      Lisp_Object bits;

      w = XINT (Fcar (name));
      h = XINT (Fcar (Fcdr (name)));
      bits = Fcar (Fcdr (Fcdr (name)));
      if (STRINGP (bits))
        bitmap_id = [[EmacsImage alloc] initFromXBM:XSTRING (bits)->data
                                        width:w height:h];
      else if (CONSP (bits) &&
               !strcmp ((char *) XSYMBOL (XCONS (bits)->car)->name->data,
                        "xbm"))
        {
          bits = Fcdr (bits);
          bitmap_id = [[EmacsImage alloc]
                          initFromSkipXBM:XSTRING (bits)->data
                          width:w height:h length:XSTRING (bits)->size];
        }
    }
  else
    /* It must be a string -- a file name.  */
    bitmap_id = [EmacsImage allocInitFromFile:name];

  if (! bitmap_id)
    Fsignal (Qerror, Fcons (build_string ("invalid or undefined bitmap"),
			    Fcons (name, Qnil)));

  [[bitmap_id stippleRep] getSize:&s];
  *w_ptr = s.width;
  *h_ptr = s.height;

  return bitmap_id;
}


/* Return non-zero if FONT1 and FONT2 have the same width.
   We do not check the height, because we can now deal with
   different heights.
   We assume that they're both character-cell fonts.  */

int ns_same_size_fonts (struct ns_face *font1, struct ns_face *font2)
   {
   return (font1->width == font2->width);
   }

/* Update the line_height of frame F according to the biggest font in
   any face.  Return nonzero if if line_height changes.  */

int ns_frame_update_line_height (struct frame *f)
   {
   int i,shift;
   int biggest = (int)rint(f->output_data.ns->face->height);

   for (i = 0; i < f->output_data.ns->n_computed_faces; i++)
      if (f->output_data.ns->computed_faces[i]!=0)
         {
         if (f->output_data.ns->computed_faces[i]->font==0)
            ns_load_font(f->output_data.ns->computed_faces[i]);
         if ((f->output_data.ns->computed_faces[i]->height) > biggest)
            biggest = (int)rint(f->output_data.ns->computed_faces[i]->height);
         }

   if (biggest == f->output_data.ns->line_height)
      return 0;

   f->output_data.ns->line_height = biggest;
   return 1;
   }

/* Modify face TO by copying from FROM all properties which have
   nondefault settings.  */

static void merge_faces (struct ns_face *from, struct ns_face *to)
   {
   if (from->name && to->name!=from->name)
      {
      to->name=from->name;
      to->font=0;
      }
   if (from->size!=-1 && to->size!=from->size)
      {
      to->size=from->size;
      to->font=0;
      }
   if (from->underline != -1)
      to->underline = from->underline;
   if (!NXEqualColor(from->foreground_color,default_color))
      to->foreground_color = from->foreground_color;
   if (!NXEqualColor(from->background_color,default_color))
      to->background_color = from->background_color;
   if (from->stipple && to->stipple != from->stipple)
     {
       to->stipple = from->stipple;
       to->pixmap_w = from->pixmap_w;
       to->pixmap_h = from->pixmap_h;
     }
   }

/* Set up the basic set of facial parameters, based on the frame's
   data; all faces are deltas applied to this.  */

static void compute_base_face (struct frame *f, struct ns_face *face)
   {
   *face = *f->output_data.ns->face;
   }

/* Return the face ID to use to display a special glyph which selects
   FACE_CODE as the face ID, assuming that ordinarily the face would
   be CURRENT_FACE.  F is the frame.  */

int ns_compute_glyph_face (struct frame *f, int face_code, int current_face)
   {
   struct ns_face face;

   face = *f->output_data.ns->param_faces[current_face];

   if (face_code >= 0 && face_code < f->output_data.ns->n_param_faces
       && f->output_data.ns->param_faces[face_code] != 0)
      merge_faces (f->output_data.ns->param_faces[face_code], &face);

   return intern_computed_face (f, &face);
   }

/* Return the face ID to use to display a special glyph which selects
   FACE_CODE as the face ID, assuming that ordinarily the face would
   be CURRENT_FACE.  F is the frame.  */

int ns_compute_glyph_face_1 (struct frame *f, Lisp_Object face_name,
                             int current_face)
   {
   struct ns_face face;
   
   face = *f->output_data.ns->param_faces[current_face];
   
   if (!NILP (face_name))
      {
      int face_code = ns_face_name_id_number (f, face_name);
      
      if (face_code >= 0 && face_code < f->output_data.ns->n_param_faces
          && f->output_data.ns->param_faces[face_code] != 0)
         merge_faces (f->output_data.ns->param_faces[face_code], &face);
      }
   
   return intern_computed_face (f, &face);
   }

/* Return the face ID associated with a buffer position POS.
   Store into *ENDPTR the position at which a different face is needed.
   This does not take account of glyphs that specify their own face codes.
   F is the frame in use for display, and W is a window displaying
   the current buffer.

   REGION_BEG, REGION_END delimit the region, so it can be highlighted.

   LIMIT is a position not to scan beyond.  That is to limit
   the time this function can take.

   If MOUSE is nonzero, use the character's mouse-face, not its face.  */

int ns_compute_char_face (struct frame *f, struct window *w, int pos,
                          int region_beg, int region_end, int *endptr, int limit,
                          int mouse)
   {
   struct ns_face face;
   Lisp_Object prop, position;
   int i, j, noverlays;
   int facecode;
   Lisp_Object *overlay_vec;
   Lisp_Object frame;
   int endpos;
   Lisp_Object propname;

   /* W must display the current buffer.  We could write this function
      to use the frame and buffer of W, but right now it doesn't.  */
   if (XBUFFER (w->buffer) != current_buffer)
      abort ();

   XSETFRAME (frame, f);

   endpos = ZV;
   if (pos < region_beg && region_beg < endpos)
      endpos = region_beg;

   XSETFASTINT (position, pos);

   if (mouse)
      propname = Qmouse_face;
   else
      propname = Qface;

   prop = Fget_text_property (position, propname, w->buffer);
      {
      Lisp_Object limit1, end;

      XSETFASTINT (limit1, (limit < endpos ? limit : endpos));
    end = Fnext_single_property_change (position, propname, w->buffer, limit1);
      if (INTEGERP (end))
         endpos = XINT (end);
      }

      {
      int next_overlay;
      int len;

      /* First try with room for 40 overlays.  */
      len = 40;
      overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));

      noverlays = overlays_at (pos, 0, &overlay_vec, &len,
                               &next_overlay, (int *) 0);

      /* If there are more than 40,
         make enough space for all, and try again.  */
      if (noverlays > len)
         {
         len = noverlays;
         overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
         noverlays = overlays_at (pos, 0, &overlay_vec, &len,
                                  &next_overlay, (int *) 0);
         }

      if (next_overlay < endpos)
         endpos = next_overlay;
      }

   *endptr = endpos;

   /* Optimize the default case.  */
   if (noverlays == 0 && NILP (prop)
       && !(pos >= region_beg && pos < region_end))
      return 0;

   compute_base_face (f, &face);

  if (CONSP (prop))
    {
      /* We have a list of faces, merge them in reverse order */
      Lisp_Object length;
      int len;
      Lisp_Object *faces;

      length = Fsafe_length (prop);
      len = XFASTINT (length);

      /* Put them into an array */
      faces = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
      for (j = 0; j < len; j++)
	{
	  faces[j] = Fcar (prop);
	  prop = Fcdr (prop);
	}
      /* So that we can merge them in the reverse order */
      for (j = len - 1; j >= 0; j--)
	{
	  facecode = ns_face_name_id_number (f, faces[j]);
	  if (facecode >= 0 && facecode < f->output_data.ns->n_param_faces
	      && f->output_data.ns->param_faces [facecode] != 0)
	    merge_faces (f->output_data.ns->param_faces [facecode], &face);
	}
    }
  else if (!NILP (prop))
      {
      facecode = ns_face_name_id_number (f, prop);
      if (facecode >= 0 && facecode < f->output_data.ns->n_param_faces
	  && f->output_data.ns->param_faces [facecode] != 0)
         merge_faces (f->output_data.ns->param_faces [facecode], &face);
      }

   noverlays = sort_overlays (overlay_vec, noverlays, w);

   /* Now merge the overlay data in that order.  */
   for (i = 0; i < noverlays; i++)
      {
      prop = Foverlay_get (overlay_vec[i], propname);
      if (CONSP (prop))
	{
	  /* We have a list of faces, merge them in reverse order */
	  Lisp_Object length;
	  int len;
	  Lisp_Object *faces;
	  int i;

	  length = Fsafe_length (prop);
	  len = XFASTINT (length);

	  /* Put them into an array */
	  faces = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
	  for (j = 0; j < len; j++)
	    {
	      faces[j] = Fcar (prop);
	      prop = Fcdr (prop);
	    }
	  /* So that we can merge them in the reverse order */
	  for (j = len - 1; j >= 0; j--)
	    {
	      facecode = ns_face_name_id_number (f, faces[j]);
	      if (facecode >= 0 && facecode < f->output_data.ns->n_param_faces
		  && f->output_data.ns->param_faces [facecode] != 0)
		merge_faces (f->output_data.ns->param_faces [facecode], &face);
	    }
          /* FIXME/cl maybe should calc endpos here as well !? */
	}
      else if (!NILP (prop))
         {
         Lisp_Object oend;
         int oendpos;

         facecode = ns_face_name_id_number (f, prop);
         if (facecode >= 0 && facecode < f->output_data.ns->n_param_faces
             && f->output_data.ns->param_faces[facecode] != 0)
            merge_faces (f->output_data.ns->param_faces[facecode], &face);

         oend = OVERLAY_END (overlay_vec[i]);
         oendpos = OVERLAY_POSITION (oend);
         if (oendpos < endpos)
            endpos = oendpos;
         }
      }

   if (pos >= region_beg && pos < region_end)
      {
      if (region_end < endpos)
         endpos = region_end;
      if (region_face >= 0 && region_face < ns_next_face_id)
         merge_faces (f->output_data.ns->param_faces[region_face], &face);
      }

   *endptr = endpos;

   return intern_computed_face (f, &face);
   }

/* Recompute the GC's for the default and modeline faces.
   We call this after changing frame parameters on which those GC's
   depend.  */

void ns_recompute_basic_faces (struct frame *f)
   {
   /* If the frame's faces haven't been initialized yet, don't worry about
      this stuff.  */
   if (f->output_data.ns->n_param_faces < 2)
      return;

   compute_base_face (f, f->output_data.ns->computed_faces[0]);
   compute_base_face (f, f->output_data.ns->computed_faces[1]);

   merge_faces (f->output_data.ns->param_faces[0],
                f->output_data.ns->computed_faces[0]);
   merge_faces (f->output_data.ns->param_faces[1],
                f->output_data.ns->computed_faces[1]);
   }

DEFUN ("ns-make-face-internal", Fns_make_face_internal, Sns_make_face_internal, 1, 1, 0,
  "Create face number FACE-ID on all frames.")
  (face_id)
     Lisp_Object face_id;
   {
   Lisp_Object rest, frame;
   int id = XINT (face_id);

   CHECK_NUMBER (face_id, 0);
   if (id < 0 || id >= ns_next_face_id)
      error ("Face id out of range");

   FOR_EACH_FRAME (rest, frame)
     {
       if (FRAME_NS_P (XFRAME (frame)))
         ensure_face_ready (XFRAME (frame), id);
     }
   return Qnil;
   }

DEFUN ("ns-set-face-attribute-internal", Fns_set_face_attribute_internal,
       Sns_set_face_attribute_internal, 4, 4, 0, "")
     (face_id, attr_name, attr_value, frame)
     Lisp_Object face_id, attr_name, attr_value, frame;
   {
   struct ns_face *face;
   struct frame *f;
   int fid;
   int garbaged=0;

   CHECK_FRAME (frame, 0);
   CHECK_NUMBER (face_id, 0);
   CHECK_SYMBOL (attr_name, 0);

   f = XFRAME (frame);
   fid = XINT (face_id);
   if (fid < 0 || fid >= ns_next_face_id)
      error ("Face id out of range");

   if (! FRAME_NS_P (f))
      return;

   ensure_face_ready (f, fid);
   face = f->output_data.ns->param_faces[XINT (face_id)];

   if (EQ (attr_name, Qfont))
      {
      if (NILP(attr_value))
         {
         face->name=0;
         }
      else
         {
         CHECK_STRING(attr_value,0);
         face->name=NXUniqueString(XSTRING (attr_value)->data);
         }
      face->font=0;
      if (ns_frame_update_line_height (f))
         ns_set_window_size (f, 0, f->width, f->height);
      garbaged=1;
      }
   else if (EQ (attr_name, Qfontsize))
      {
      if (NILP(attr_value))
         face->size=-1;
      else
         {
         CHECK_NUMBER_OR_FLOAT (attr_value, 0);
         face->size=XFLOATINT(attr_value);
         if (face->size<=0)
            error("Facesize non-positive.");
         }
      face->font=0;
      if (ns_frame_update_line_height (f))
         ns_set_window_size (f, 0, f->width, f->height);
      garbaged=1;
      }
   else if (EQ (attr_name, intern ("foreground")))
      {
      if (NILP(attr_value))
         face->foreground_color=default_color;
      else if (ns_lisp_to_color(attr_value,&face->foreground_color))
         error("Unknown color.");
      garbaged=1;
      }
   else if (EQ (attr_name, intern ("background")))
      {
      if (NILP(attr_value))
         face->background_color=default_color;
      else if (ns_lisp_to_color(attr_value,&face->background_color))
         error("Unknown color.");
      garbaged=1;
      }
   else if (EQ (attr_name, intern ("background-pixmap")))
     {
       unsigned int w, h;
       id new_pixmap = load_pixmap (f, attr_value, &w, &h);

       [face->stipple free];
       face->stipple = new_pixmap;
       face->pixmap_w = w;
       face->pixmap_h = h;
       garbaged = 1;
     }
   else if (EQ (attr_name, Qunderline))
      {
      if (NILP(attr_value))
         face->underline=-1;
      else if (EQ(attr_value,Qt))
         face->underline=1;
      else
         face->underline=0;
      garbaged=1;
      }
   else
      error ("unknown face attribute");

   if (fid == 0 || fid == 1)
      ns_recompute_basic_faces (f);

   if (garbaged)
      SET_FRAME_GARBAGED (f);
   
   return Qnil;
   }

DEFUN ("ns-internal-next-face-id", Fns_internal_next_face_id,
  Sns_internal_next_face_id, 0, 0, 0, "")
  ()
   {
   return make_number (ns_next_face_id++);
   }

DEFUN ("ns-convert-font-trait-internal",
  Fns_convert_font_trait_internal, Sns_convert_font_trait_internal,
  2, 2, 0, "")
  (Lisp_Object name, Lisp_Object attribute)
   {
   id fm;
   id font;
   check_ns();
   CHECK_STRING(name,0);
   CHECK_SYMBOL(attribute,0);
   fm=[FontManager new];
   font=[Font newFont:XSTRING(name)->data size:10.0];
   if (!fm || !font)
      error ("Unknown font.");

   if (EQ(attribute, intern ("italic")))
      font=[fm convert:font toHaveTrait:NX_ITALIC];
   else if (EQ(attribute, intern ("unitalic")))
      font=[fm convert:font toNotHaveTrait:NX_ITALIC];
   else if (EQ(attribute, intern ("bold")))
      font=[fm convert:font toHaveTrait:NX_BOLD];
   else if (EQ(attribute, intern ("unbold")))
      font=[fm convert:font toNotHaveTrait:NX_BOLD];
   else if (EQ(attribute, intern ("nonstandardcharset")))
      font=[fm convert:font toHaveTrait:NX_NONSTANDARDCHARSET];
   else if (EQ(attribute, intern ("unnonstandardcharset")))
      font=[fm convert:font toNotHaveTrait:NX_NONSTANDARDCHARSET];
   else if (EQ(attribute, intern ("narrow")))
      font=[fm convert:font toHaveTrait:NX_NARROW];
   else if (EQ(attribute, intern ("unnarrow")))
      font=[fm convert:font toNotHaveTrait:NX_NARROW];
   else if (EQ(attribute, intern ("expanded")))
      font=[fm convert:font toHaveTrait:NX_EXPANDED];
   else if (EQ(attribute, intern ("unexpanded")))
      font=[fm convert:font toNotHaveTrait:NX_EXPANDED];
   else if (EQ(attribute, intern ("condensed")))
      font=[fm convert:font toHaveTrait:NX_CONDENSED];
   else if (EQ(attribute, intern ("uncondensed")))
      font=[fm convert:font toNotHaveTrait:NX_CONDENSED];
   else if (EQ(attribute, intern ("smallcaps")))
      font=[fm convert:font toHaveTrait:NX_SMALLCAPS];
   else if (EQ(attribute, intern ("unsmallcaps")))
      font=[fm convert:font toNotHaveTrait:NX_SMALLCAPS];
   else if (EQ(attribute, intern ("poster")))
      font=[fm convert:font toHaveTrait:NX_POSTER];
   else if (EQ(attribute, intern ("unposter")))
      font=[fm convert:font toNotHaveTrait:NX_POSTER];
   else if (EQ(attribute, intern ("compressed")))
      font=[fm convert:font toHaveTrait:NX_COMPRESSED];
   else if (EQ(attribute, intern ("uncompressed")))
      font=[fm convert:font toNotHaveTrait:NX_COMPRESSED];
   return build_string([font name]);
   }

int ns_face_name_id_number (struct frame *f, Lisp_Object name)
   {
   Lisp_Object tem;

   tem= Fcdr (assq_no_quit (name, f->face_alist));
   if (NILP (tem)) return 0;

   CHECK_VECTOR (tem, 0);
   tem = XVECTOR (tem)->contents[2];
   CHECK_NUMBER (tem, 0);
   return XINT (tem);
   }

void syms_of_nsfaces ()
   {
   default_color=NX_COLORCLEAR;

   defsubr (&Sns_pixmap_spec_p);
   defsubr (&Sns_make_face_internal);
   defsubr (&Sns_set_face_attribute_internal);
   defsubr (&Sns_internal_next_face_id);
   defsubr (&Sns_convert_font_trait_internal);
   }

#endif

