This is nsfaces.m in view mode; [Download] [Up]
/* "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). */
#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_vector (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 &&
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->display.ns->n_computed_faces=0;
f->display.ns->computed_faces=0;
new_computed_face (f, f->display.ns->param_faces[0]);
new_computed_face (f, f->display.ns->param_faces[1]);
ns_recompute_basic_faces(f);
{
int i;
Lisp_Object tail, frame;
FOR_EACH_FRAME (tail, frame)
if (FRAME_NS_P (XFRAME (frame)) && XFRAME (frame) != f)
for (i=2; i< XFRAME(frame)->display.ns->n_param_faces; i++)
if (XFRAME(frame)->display.ns->param_faces[i])
ensure_face_ready (f,i);
}
}
void ns_free_frame_faces (struct frame *f)
{
int i;
for(i=0; i< f->display.ns->n_param_faces; i++)
if (f->display.ns->param_faces[i])
xfree (f->display.ns->param_faces[i]);
for(i=0; i< f->display.ns->n_computed_faces; i++)
if (f->display.ns->computed_faces[i])
xfree (f->display.ns->computed_faces[i]);
xfree (f->display.ns->param_faces);
f->display.ns->param_faces =0;
f->display.ns->n_param_faces =0;
xfree (f->display.ns->computed_faces);
f->display.ns->computed_faces =0;
f->display.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->display.ns->n_computed_faces >= f->display.ns->size_computed_faces)
{
int new_size = f->display.ns->n_computed_faces + 32;
f->display.ns->computed_faces = (struct ns_face **)
xrealloc (f->display.ns->computed_faces,
new_size * sizeof (struct ns_face *));
f->display.ns->size_computed_faces = new_size;
}
i = f->display.ns->n_computed_faces++;
f->display.ns->computed_faces[i] =
(struct ns_face *) xmalloc (sizeof(struct ns_face));
*(f->display.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->display.ns->n_computed_faces; i++)
{
if (! f->display.ns->computed_faces[i])
abort ();
if (face_eql(new_face,f->display.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->display.ns->n_param_faces <= fid)
{
int n = fid + 10;
f->display.ns->param_faces = (struct ns_face **)
xrealloc (f->display.ns->param_faces,sizeof (struct ns_face *) * n);
bzero (f->display.ns->param_faces + f->display.ns->n_param_faces,
(n - f->display.ns->n_param_faces) * sizeof (struct ns_face *));
f->display.ns->n_param_faces = n;
}
if (f->display.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;
f->display.ns->param_faces [fid] = nface;
}
}
/* 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->display.ns->face->height);
for (i = 0; i < f->display.ns->n_computed_faces; i++)
if (f->display.ns->computed_faces[i]!=0)
{
if (f->display.ns->computed_faces[i]->font==0)
ns_load_font(f->display.ns->computed_faces[i]);
if ((f->display.ns->computed_faces[i]->height) > biggest)
biggest = (int)rint(f->display.ns->computed_faces[i]->height);
}
if (biggest == f->display.ns->line_height)
return 0;
f->display.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;
}
/* 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->display.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->display.ns->param_faces[current_face];
if (face_code >= 0 && face_code < f->display.ns->n_param_faces
&& f->display.ns->param_faces[face_code] != 0)
merge_faces (f->display.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->display.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->display.ns->n_param_faces
&& f->display.ns->param_faces[face_code] != 0)
merge_faces (f->display.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 ();
XSET (frame, Lisp_Frame, f);
endpos = ZV;
if (pos < region_beg && region_beg < endpos)
endpos = region_beg;
XFASTINT (position) = pos;
if (mouse)
propname = Qmouse_face;
else
propname = Qface;
prop = Fget_text_property (position, propname, w->buffer);
{
Lisp_Object limit1, end;
XFASTINT (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);
/* 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);
}
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 (!NILP (prop))
{
facecode = ns_face_name_id_number (f, prop);
if (facecode >= 0 && facecode < f->display.ns->n_param_faces
&& f->display.ns->param_faces [facecode] != 0)
merge_faces (f->display.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 (!NILP (prop))
{
Lisp_Object oend;
int oendpos;
facecode = ns_face_name_id_number (f, prop);
if (facecode >= 0 && facecode < f->display.ns->n_param_faces
&& f->display.ns->param_faces[facecode] != 0)
merge_faces (f->display.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->display.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->display.ns->n_param_faces < 2)
return;
compute_base_face (f, f->display.ns->computed_faces[0]);
compute_base_face (f, f->display.ns->computed_faces[1]);
merge_faces (f->display.ns->param_faces[0],
f->display.ns->computed_faces[0]);
merge_faces (f->display.ns->param_faces[1],
f->display.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;
int fid = XINT (face_id);
check_ns();
CHECK_NUMBER (face_id, 0);
if (fid < 0 || fid >= ns_next_face_id)
error ("Face id out of range");
for (rest = Vframe_list; CONSP (rest); rest = XCONS (rest)->cdr)
{
struct frame *f = XFRAME (XCONS (rest)->car);
if (FRAME_NS_P (f))
ensure_face_ready (f, fid);
}
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_ns();
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->display.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, 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, "")
()
{
check_ns();
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_make_face_internal);
defsubr (&Sns_set_face_attribute_internal);
defsubr (&Sns_internal_next_face_id);
defsubr (&Sns_convert_font_trait_internal);
}
#endif
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.