1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-03 02:31:03 -08:00

Implement wave-style variant of underlining.

* doc/lispref/display.texi: Document new face attribute.
  * lisp/cus-face.el (custom-face-attributes): Add wave-style underline
  attribute.
  * lisp/faces.el (set-face-attribute): Update docstring.
  * src/dispextern.h (face_underline_type): New enum.
  (face): Add field for underline type.
  * src/nsterm.m (ns_draw_underwave): New function.
  (ns_draw_text_decoration): Use it.
  * src/w32term.c (w32_restore_glyph_string_clip, w32_draw_underwave):
  New functions.
  (x_draw_glyph_string): Use them.
  * src/xfaces.c (Qline, Qwave): New Lisp objects.
  (check_lface_attrs, merge_face_ref)
  (Finternal_set_lisp_face_attribute, realize_x_face): Handle
  wave-style underline face attributes.
  * src/xterm.c (x_draw_underwave): New function.
  (x_draw_glyph_string): Use it.
This commit is contained in:
Alp Aker 2012-06-16 20:32:36 -04:00
parent 40d8bcb854
commit 9b0e3ebaef
11 changed files with 590 additions and 180 deletions

View file

@ -1,3 +1,8 @@
2012-06-16 Aurelien Aptel <aurelien.aptel@gmail.com>
* doc/lispref/display.texi: Document wave-style underline
face attribute.
2012-06-13 Andreas Schwab <schwab@linux-m68k.org> 2012-06-13 Andreas Schwab <schwab@linux-m68k.org>
* configure.in: Rename --enable-use-lisp-union-type to * configure.in: Rename --enable-use-lisp-union-type to

View file

@ -2130,10 +2130,32 @@ Background color, a string. The value can be a system-defined color
name, or a hexadecimal color specification. @xref{Color Names}. name, or a hexadecimal color specification. @xref{Color Names}.
@item :underline @item :underline
Whether or not characters should be underlined, and in what color. If Whether or not characters should be underlined, and in what
the value is @code{t}, underlining uses the foreground color of the color. Here are the possible values of the @code{:underline}
face. If the value is a string, underlining uses that color. The attribute, and what they mean:
value @code{nil} means do not underline.
@table @asis
@item @code{nil}
Don't underline.
@item @code{t}
Underline with the foreground color of the face.
@item @var{color}
Underline in color @var{color}.
@item @code{(:color @var{color} :style @var{style})}
If @var{color} is a string, underline in it.
If @var{color} is @code{foreground-color}, underline with the
foreground color of the face.
If @var{style} is @code{wave} underline with a wave.
If @var{style} is @code{line} underline with a line.
If the attribute @code{:color} is omited, underline with the
foreground color of the face.
If the attribute @code{:style} is omited, underline with a line.
@end table
@item :overline @item :overline
Whether or not characters should be overlined, and in what color. Whether or not characters should be overlined, and in what color.

View file

@ -1,3 +1,10 @@
2012-06-16 Aurelien Aptel <aurelien.aptel@gmail.com>
* cus-face.el (custom-face-attributes): Add wave-style underline
attribute.
* faces.el (set-face-attribute): Update docstring to describe
wave-style underline attribute.
2012-06-16 Chong Yidong <cyd@gnu.org> 2012-06-16 Chong Yidong <cyd@gnu.org>
* term/xterm.el (terminal-init-xterm): Discard input before * term/xterm.el (terminal-init-xterm): Discard input before

View file

@ -135,8 +135,13 @@
(choice :tag "Underline" (choice :tag "Underline"
:help-echo "Control text underlining." :help-echo "Control text underlining."
(const :tag "Off" nil) (const :tag "Off" nil)
(const :tag "On" t) (list :tag "On"
(color :tag "Colored"))) (const :format "" :value :color)
(choice :tag "Color" (const :tag "Foreground Color" foreground-color) color)
(const :format "" :value :style)
(choice :tag "Style"
(const :tag "Line" line)
(const :tag "Wave" wave)))))
(:overline (:overline
(choice :tag "Overline" (choice :tag "Overline"

View file

@ -623,10 +623,21 @@ VALUE must be a color name, a string.
`:underline' `:underline'
VALUE specifies whether characters in FACE should be underlined. If VALUE specifies whether characters in FACE should be underlined.
VALUE is t, underline with foreground color of the face. If VALUE is If VALUE is t, underline with foreground color of the face.
a string, underline with that color. If VALUE is nil, explicitly If VALUE is a string, underline with that color.
don't underline. If VALUE is nil, explicitly don't underline.
Otherwise, VALUE must be a property list of the form:
`(:color COLOR :style STYLE)'.
COLOR can be a either a color name string or `foreground-color'.
STYLE can be either `line' or `wave'.
If a keyword/value pair is missing from the property list, a
default value will be used for the value.
The default value of COLOR is the foreground color of the face.
The default value of STYLE is `line'.
`:overline' `:overline'

View file

@ -1,3 +1,20 @@
2012-06-16 Aurelien Aptel <aurelien.aptel@gmail.com>
Implement wave-style variant of underlining.
* dispextern.h (face_underline_type): New enum.
(face): Add field for underline type.
* nsterm.m (ns_draw_underwave): New function.
(ns_draw_text_decoration): Use it.
* w32term.c (w32_restore_glyph_string_clip, w32_draw_underwave):
New functions.
(x_draw_glyph_string): Use them.
* xfaces.c (Qline, Qwave): New Lisp objects.
(check_lface_attrs, merge_face_ref)
(Finternal_set_lisp_face_attribute, realize_x_face): Handle
wave-style underline face attributes.
* xterm.c (x_draw_underwave): New function.
(x_draw_glyph_string): Use it.
2012-06-16 Juanma Barranquero <lekktu@gmail.com> 2012-06-16 Juanma Barranquero <lekktu@gmail.com>
* makefile.w32-in ($(BLD)/emacs.$(O), $(BLD)/fringe.$(O)) * makefile.w32-in ($(BLD)/emacs.$(O), $(BLD)/fringe.$(O))

View file

@ -1510,6 +1510,13 @@ enum face_box_type
FACE_SUNKEN_BOX FACE_SUNKEN_BOX
}; };
/* Underline type. */
enum face_underline_type
{
FACE_UNDER_LINE,
FACE_UNDER_WAVE
};
/* Structure describing a realized face. /* Structure describing a realized face.
@ -1585,6 +1592,9 @@ struct face
drawing shadows. */ drawing shadows. */
unsigned use_box_color_for_shadows_p : 1; unsigned use_box_color_for_shadows_p : 1;
/* Style of underlining. */
enum face_underline_type underline_type;
/* Non-zero if text in this face should be underlined, overlined, /* Non-zero if text in this face should be underlined, overlined,
strike-through or have a box drawn around it. */ strike-through or have a box drawn around it. */
unsigned underline_p : 1; unsigned underline_p : 1;

View file

@ -2595,6 +2595,60 @@ ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
return n; return n;
} }
/* --------------------------------------------------------------------
Draw a wavy line under glyph string s. The wave fills wave_height
pixels from y.
x wave_length = 3
--
y * * * * *
|* * * * * * * * *
wave_height = 3 | * * * *
--------------------------------------------------------------------- */
static void
ns_draw_underwave (struct glyph_string *s, CGFloat width, CGFloat x)
{
int wave_height = 3, wave_length = 3;
int y, dx, dy, odd, xmax;
NSPoint a, b;
NSRect waveClip;
dx = wave_length;
dy = wave_height - 1;
y = s->ybase + 1;
xmax = x + width;
/* Find and set clipping rectangle */
waveClip = NSMakeRect (x, y, width, wave_height);
[[NSGraphicsContext currentContext] saveGraphicsState];
NSRectClip (waveClip);
/* Draw the waves */
a.x = x - ((int)(x) % dx);
b.x = a.x + dx;
odd = (int)(a.x/dx) % 2;
a.y = b.y = y;
if (odd)
a.y += dy;
else
b.y += dy;
while (a.x <= xmax)
{
[NSBezierPath strokeLineFromPoint:a toPoint:b];
a.x = b.x, a.y = b.y;
b.x += dx, b.y = y + odd*dy;
odd = !odd;
}
/* Restore previous clipping rectangle(s) */
[[NSGraphicsContext currentContext] restoreGraphicsState];
}
void void
ns_draw_text_decoration (struct glyph_string *s, struct face *face, ns_draw_text_decoration (struct glyph_string *s, struct face *face,
NSColor *defaultCol, CGFloat width, CGFloat x) NSColor *defaultCol, CGFloat width, CGFloat x)
@ -2608,63 +2662,75 @@ ns_draw_text_decoration (struct glyph_string *s, struct face *face,
/* Do underline. */ /* Do underline. */
if (face->underline_p) if (face->underline_p)
{ {
NSRect r; if (s->face->underline_type == FACE_UNDER_WAVE)
unsigned long thickness, position;
/* If the prev was underlined, match its appearance. */
if (s->prev && s->prev->face->underline_p
&& s->prev->underline_thickness > 0)
{ {
thickness = s->prev->underline_thickness; if (face->underline_defaulted_p)
position = s->prev->underline_position; [defaultCol set];
}
else
{
struct font *font;
unsigned long descent;
font=s->font;
descent = s->y + s->height - s->ybase;
/* Use underline thickness of font, defaulting to 1. */
thickness = (font && font->underline_thickness > 0)
? font->underline_thickness : 1;
/* Determine the offset of underlining from the baseline. */
if (x_underline_at_descent_line)
position = descent - thickness;
else if (x_use_underline_position_properties
&& font && font->underline_position >= 0)
position = font->underline_position;
else if (font)
position = lround (font->descent / 2);
else else
position = underline_minimum_offset; [ns_lookup_indexed_color (face->underline_color, s->f) set];
position = max (position, underline_minimum_offset); ns_draw_underwave (s, width, x);
/* Ensure underlining is not cropped. */
if (descent <= position)
{
position = descent - 1;
thickness = 1;
}
else if (descent < position + thickness)
thickness = 1;
} }
else if (s->face->underline_type == FACE_UNDER_LINE)
{
s->underline_thickness = thickness; NSRect r;
s->underline_position = position; unsigned long thickness, position;
r = NSMakeRect (x, s->ybase + position, width, thickness); /* If the prev was underlined, match its appearance. */
if (s->prev && s->prev->face->underline_p
&& s->prev->underline_thickness > 0)
{
thickness = s->prev->underline_thickness;
position = s->prev->underline_position;
}
else
{
struct font *font;
unsigned long descent;
if (face->underline_defaulted_p) font=s->font;
[defaultCol set]; descent = s->y + s->height - s->ybase;
else
[ns_lookup_indexed_color (face->underline_color, s->f) set]; /* Use underline thickness of font, defaulting to 1. */
NSRectFill (r); thickness = (font && font->underline_thickness > 0)
? font->underline_thickness : 1;
/* Determine the offset of underlining from the baseline. */
if (x_underline_at_descent_line)
position = descent - thickness;
else if (x_use_underline_position_properties
&& font && font->underline_position >= 0)
position = font->underline_position;
else if (font)
position = lround (font->descent / 2);
else
position = underline_minimum_offset;
position = max (position, underline_minimum_offset);
/* Ensure underlining is not cropped. */
if (descent <= position)
{
position = descent - 1;
thickness = 1;
}
else if (descent < position + thickness)
thickness = 1;
}
s->underline_thickness = thickness;
s->underline_position = position;
r = NSMakeRect (x, s->ybase + position, width, thickness);
if (face->underline_defaulted_p)
[defaultCol set];
else
[ns_lookup_indexed_color (face->underline_color, s->f) set];
NSRectFill (r);
}
} }
/* Do overline. We follow other terms in using a thickness of 1 /* Do overline. We follow other terms in using a thickness of 1
and ignoring overline_margin. */ and ignoring overline_margin. */
if (face->overline_p) if (face->overline_p)

View file

@ -313,6 +313,94 @@ w32_set_clip_rectangle (HDC hdc, RECT *rect)
SelectClipRgn (hdc, NULL); SelectClipRgn (hdc, NULL);
} }
/* Restore clipping rectangle in S */
static void
w32_restore_glyph_string_clip (struct glyph_string *s)
{
RECT *r = s->clip;
int n = s->num_clips;
if (n == 1)
w32_set_clip_rectangle (s->hdc, r);
else if (n > 1)
{
HRGN clip1 = CreateRectRgnIndirect (r);
HRGN clip2 = CreateRectRgnIndirect (r + 1);
if (CombineRgn (clip1, clip1, clip2, RGN_OR) != ERROR)
SelectClipRgn (s->hdc, clip1);
DeleteObject (clip1);
DeleteObject (clip2);
}
}
/*
Draw a wavy line under S. The wave fills wave_height pixels from y0.
x0 wave_length = 2
--
y0 * * * * *
|* * * * * * * * *
wave_height = 3 | * * * *
*/
void
w32_draw_underwave (struct glyph_string *s, COLORREF color)
{
int wave_height = 2, wave_length = 3;
int dx, dy, x0, y0, width, x1, y1, x2, y2, odd, xmax;
XRectangle wave_clip, string_clip, final_clip;
RECT w32_final_clip, w32_string_clip;
HPEN hp, oldhp;
dx = wave_length;
dy = wave_height - 1;
x0 = s->x;
y0 = s->ybase + 1;
width = s->width;
xmax = x0 + width;
/* Find and set clipping rectangle */
wave_clip = (XRectangle){ x0, y0, width, wave_height };
get_glyph_string_clip_rect (s, &w32_string_clip);
CONVERT_TO_XRECT (string_clip, w32_string_clip);
if (!x_intersect_rectangles (&wave_clip, &string_clip, &final_clip))
return;
hp = CreatePen (PS_SOLID, 0, color);
oldhp = SelectObject (s->hdc, hp);
CONVERT_FROM_XRECT (final_clip, w32_final_clip);
w32_set_clip_rectangle (s->hdc, &w32_final_clip);
/* Draw the waves */
x1 = x0 - (x0 % dx);
x2 = x1 + dx;
odd = (x1/dx) % 2;
y1 = y2 = y0;
if (odd)
y1 += dy;
else
y2 += dy;
MoveToEx (s->hdc, x1, y1, NULL);
while (x1 <= xmax)
{
LineTo (s->hdc, x2, y2);
x1 = x2, y1 = y2;
x2 += dx, y2 = y0 + odd*dy;
odd = !odd;
}
/* Restore previous pen and clipping rectangle(s) */
w32_restore_glyph_string_clip (s);
SelectObject (s->hdc, oldhp);
DeleteObject (hp);
}
/* Draw a hollow rectangle at the specified position. */ /* Draw a hollow rectangle at the specified position. */
void void
@ -2347,60 +2435,74 @@ x_draw_glyph_string (struct glyph_string *s)
/* Draw underline. */ /* Draw underline. */
if (s->face->underline_p) if (s->face->underline_p)
{ {
unsigned long thickness, position; if (s->face->underline_type == FACE_UNDER_WAVE)
int y; {
COLORREF color;
if (s->prev && s->prev->face->underline_p) if (s->face->underline_defaulted_p)
{ color = s->gc->foreground;
/* We use the same underline style as the previous one. */
thickness = s->prev->underline_thickness;
position = s->prev->underline_position;
}
else
{
/* Get the underline thickness. Default is 1 pixel. */
if (s->font && s->font->underline_thickness > 0)
thickness = s->font->underline_thickness;
else else
thickness = 1; color = s->face->underline_color;
if (x_underline_at_descent_line)
position = (s->height - thickness) - (s->ybase - s->y); w32_draw_underwave (s, color);
}
else if (s->face->underline_type == FACE_UNDER_LINE)
{
unsigned long thickness, position;
int y;
if (s->prev && s->prev->face->underline_p)
{
/* We use the same underline style as the previous one. */
thickness = s->prev->underline_thickness;
position = s->prev->underline_position;
}
else else
{ {
/* Get the underline position. This is the recommended /* Get the underline thickness. Default is 1 pixel. */
vertical offset in pixels from the baseline to the top of if (s->font && s->font->underline_thickness > 0)
the underline. This is a signed value according to the thickness = s->font->underline_thickness;
specs, and its default is else
thickness = 1;
if (x_underline_at_descent_line)
position = (s->height - thickness) - (s->ybase - s->y);
else
{
/* Get the underline position. This is the recommended
vertical offset in pixels from the baseline to the top of
the underline. This is a signed value according to the
specs, and its default is
ROUND ((maximum_descent) / 2), with ROUND ((maximum_descent) / 2), with
ROUND (x) = floor (x + 0.5) */ ROUND (x) = floor (x + 0.5) */
if (x_use_underline_position_properties if (x_use_underline_position_properties
&& s->font && s->font->underline_position >= 0) && s->font && s->font->underline_position >= 0)
position = s->font->underline_position; position = s->font->underline_position;
else if (s->font) else if (s->font)
position = (s->font->descent + 1) / 2; position = (s->font->descent + 1) / 2;
}
position = max (position, underline_minimum_offset);
}
/* Check the sanity of thickness and position. We should
avoid drawing underline out of the current line area. */
if (s->y + s->height <= s->ybase + position)
position = (s->height - 1) - (s->ybase - s->y);
if (s->y + s->height < s->ybase + position + thickness)
thickness = (s->y + s->height) - (s->ybase + position);
s->underline_thickness = thickness;
s->underline_position =position;
y = s->ybase + position;
if (s->face->underline_defaulted_p)
{
w32_fill_area (s->f, s->hdc, s->gc->foreground, s->x,
y, s->width, 1);
}
else
{
w32_fill_area (s->f, s->hdc, s->face->underline_color, s->x,
y, s->width, 1);
} }
position = max (position, underline_minimum_offset);
}
/* Check the sanity of thickness and position. We should
avoid drawing underline out of the current line area. */
if (s->y + s->height <= s->ybase + position)
position = (s->height - 1) - (s->ybase - s->y);
if (s->y + s->height < s->ybase + position + thickness)
thickness = (s->y + s->height) - (s->ybase + position);
s->underline_thickness = thickness;
s->underline_position =position;
y = s->ybase + position;
if (s->face->underline_defaulted_p)
{
w32_fill_area (s->f, s->hdc, s->gc->foreground, s->x,
y, s->width, 1);
}
else
{
w32_fill_area (s->f, s->hdc, s->face->underline_color, s->x,
y, s->width, 1);
} }
} }
/* Draw overline. */ /* Draw overline. */

View file

@ -320,6 +320,7 @@ static Lisp_Object QCfontset;
Lisp_Object Qnormal; Lisp_Object Qnormal;
Lisp_Object Qbold; Lisp_Object Qbold;
static Lisp_Object Qline, Qwave;
static Lisp_Object Qultra_light, Qextra_light, Qlight; static Lisp_Object Qultra_light, Qextra_light, Qlight;
static Lisp_Object Qsemi_light, Qsemi_bold, Qextra_bold, Qultra_bold; static Lisp_Object Qsemi_light, Qsemi_bold, Qextra_bold, Qultra_bold;
static Lisp_Object Qoblique, Qreverse_oblique, Qreverse_italic; static Lisp_Object Qoblique, Qreverse_oblique, Qreverse_italic;
@ -1894,7 +1895,8 @@ check_lface_attrs (Lisp_Object *attrs)
xassert (UNSPECIFIEDP (attrs[LFACE_UNDERLINE_INDEX]) xassert (UNSPECIFIEDP (attrs[LFACE_UNDERLINE_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_UNDERLINE_INDEX]) || IGNORE_DEFFACE_P (attrs[LFACE_UNDERLINE_INDEX])
|| SYMBOLP (attrs[LFACE_UNDERLINE_INDEX]) || SYMBOLP (attrs[LFACE_UNDERLINE_INDEX])
|| STRINGP (attrs[LFACE_UNDERLINE_INDEX])); || STRINGP (attrs[LFACE_UNDERLINE_INDEX])
|| CONSP (attrs[LFACE_UNDERLINE_INDEX]));
xassert (UNSPECIFIEDP (attrs[LFACE_OVERLINE_INDEX]) xassert (UNSPECIFIEDP (attrs[LFACE_OVERLINE_INDEX])
|| IGNORE_DEFFACE_P (attrs[LFACE_OVERLINE_INDEX]) || IGNORE_DEFFACE_P (attrs[LFACE_OVERLINE_INDEX])
|| SYMBOLP (attrs[LFACE_OVERLINE_INDEX]) || SYMBOLP (attrs[LFACE_OVERLINE_INDEX])
@ -2525,7 +2527,8 @@ merge_face_ref (struct frame *f, Lisp_Object face_ref, Lisp_Object *to,
{ {
if (EQ (value, Qt) if (EQ (value, Qt)
|| NILP (value) || NILP (value)
|| STRINGP (value)) || STRINGP (value)
|| CONSP (value))
to[LFACE_UNDERLINE_INDEX] = value; to[LFACE_UNDERLINE_INDEX] = value;
else else
err = 1; err = 1;
@ -2948,15 +2951,54 @@ FRAME 0 means change the face on all frames, and change the default
} }
else if (EQ (attr, QCunderline)) else if (EQ (attr, QCunderline))
{ {
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value)) int valid_p = 0;
if ((SYMBOLP (value)
&& !EQ (value, Qt) if (UNSPECIFIEDP (value) || IGNORE_DEFFACE_P (value))
&& !EQ (value, Qnil)) valid_p = 1;
/* Underline color. */ else if (NILP (value) || EQ (value, Qt))
|| (STRINGP (value) valid_p = 1;
&& SCHARS (value) == 0)) else if (STRINGP (value) && SCHARS (value) > 0)
signal_error ("Invalid face underline", value); valid_p = 1;
else if (CONSP (value))
{
Lisp_Object key, val, list;
list = value;
valid_p = 1;
while (!NILP (CAR_SAFE(list)))
{
key = CAR_SAFE (list);
list = CDR_SAFE (list);
val = CAR_SAFE (list);
list = CDR_SAFE (list);
if(NILP (key) || NILP (val))
{
valid_p = 0;
break;
}
else if (EQ (key, QCcolor)
&& !(EQ (val, Qforeground_color)
|| (STRINGP (val) && SCHARS (val) > 0)))
{
valid_p = 0;
break;
}
else if (EQ (key, QCstyle)
&& !(EQ (val, Qline) || EQ (val, Qwave)))
{
valid_p = 0;
break;
}
}
}
if (!valid_p)
signal_error ("Invalid face underline", value);
old_value = LFACE_UNDERLINE (lface); old_value = LFACE_UNDERLINE (lface);
LFACE_UNDERLINE (lface) = value; LFACE_UNDERLINE (lface) = value;
} }
@ -5576,7 +5618,7 @@ realize_x_face (struct face_cache *cache, Lisp_Object *attrs)
#ifdef HAVE_WINDOW_SYSTEM #ifdef HAVE_WINDOW_SYSTEM
struct face *default_face; struct face *default_face;
struct frame *f; struct frame *f;
Lisp_Object stipple, overline, strike_through, box; Lisp_Object stipple, underline, overline, strike_through, box;
xassert (FRAME_WINDOW_P (cache->f)); xassert (FRAME_WINDOW_P (cache->f));
@ -5709,29 +5751,76 @@ realize_x_face (struct face_cache *cache, Lisp_Object *attrs)
/* Text underline, overline, strike-through. */ /* Text underline, overline, strike-through. */
if (EQ (attrs[LFACE_UNDERLINE_INDEX], Qt)) underline = attrs[LFACE_UNDERLINE_INDEX];
if (EQ (underline, Qt))
{ {
/* Use default color (same as foreground color). */ /* Use default color (same as foreground color). */
face->underline_p = 1; face->underline_p = 1;
face->underline_type = FACE_UNDER_LINE;
face->underline_defaulted_p = 1; face->underline_defaulted_p = 1;
face->underline_color = 0; face->underline_color = 0;
} }
else if (STRINGP (attrs[LFACE_UNDERLINE_INDEX])) else if (STRINGP (underline))
{ {
/* Use specified color. */ /* Use specified color. */
face->underline_p = 1; face->underline_p = 1;
face->underline_type = FACE_UNDER_LINE;
face->underline_defaulted_p = 0; face->underline_defaulted_p = 0;
face->underline_color face->underline_color
= load_color (f, face, attrs[LFACE_UNDERLINE_INDEX], = load_color (f, face, underline,
LFACE_UNDERLINE_INDEX); LFACE_UNDERLINE_INDEX);
} }
else if (NILP (attrs[LFACE_UNDERLINE_INDEX])) else if (NILP (underline))
{ {
face->underline_p = 0; face->underline_p = 0;
face->underline_defaulted_p = 0; face->underline_defaulted_p = 0;
face->underline_color = 0; face->underline_color = 0;
} }
else if (CONSP (underline))
{
/* `(:color COLOR :style STYLE)'.
STYLE being one of `line' or `wave'. */
face->underline_p = 1;
face->underline_color = 0;
face->underline_defaulted_p = 1;
face->underline_type = FACE_UNDER_LINE;
while (CONSP (underline))
{
Lisp_Object keyword, value;
keyword = XCAR (underline);
underline = XCDR (underline);
if (!CONSP (underline))
break;
value = XCAR (underline);
underline = XCDR (underline);
if (EQ (keyword, QCcolor))
{
if (EQ (value, Qforeground_color))
{
face->underline_defaulted_p = 1;
face->underline_color = 0;
}
else if (STRINGP (value))
{
face->underline_defaulted_p = 0;
face->underline_color = load_color (f, face, value,
LFACE_UNDERLINE_INDEX);
}
}
else if (EQ (keyword, QCstyle))
{
if (EQ (value, Qline))
face->underline_type = FACE_UNDER_LINE;
else if (EQ (value, Qwave))
face->underline_type = FACE_UNDER_WAVE;
}
}
}
overline = attrs[LFACE_OVERLINE_INDEX]; overline = attrs[LFACE_OVERLINE_INDEX];
if (STRINGP (overline)) if (STRINGP (overline))
{ {
@ -6476,6 +6565,8 @@ syms_of_xfaces (void)
DEFSYM (QCcolor, ":color"); DEFSYM (QCcolor, ":color");
DEFSYM (QCline_width, ":line-width"); DEFSYM (QCline_width, ":line-width");
DEFSYM (QCstyle, ":style"); DEFSYM (QCstyle, ":style");
DEFSYM (Qline, "line");
DEFSYM (Qwave, "wave");
DEFSYM (Qreleased_button, "released-button"); DEFSYM (Qreleased_button, "released-button");
DEFSYM (Qpressed_button, "pressed-button"); DEFSYM (Qpressed_button, "pressed-button");
DEFSYM (Qnormal, "normal"); DEFSYM (Qnormal, "normal");

View file

@ -2663,6 +2663,65 @@ x_draw_stretch_glyph_string (struct glyph_string *s)
s->background_filled_p = 1; s->background_filled_p = 1;
} }
/*
Draw a wavy line under S. The wave fills wave_height pixels from y0.
x0 wave_length = 2
--
y0 * * * * *
|* * * * * * * * *
wave_height = 3 | * * * *
*/
static void
x_draw_underwave (struct glyph_string *s)
{
int wave_height = 2, wave_length = 3;
int dx, dy, x0, y0, width, x1, y1, x2, y2, odd, xmax;
XRectangle wave_clip, string_clip, final_clip;
dx = wave_length;
dy = wave_height - 1;
x0 = s->x;
y0 = s->ybase + 1;
width = s->width;
xmax = x0 + width;
/* Find and set clipping rectangle */
wave_clip = (XRectangle){ x0, y0, width, wave_height };
get_glyph_string_clip_rect (s, &string_clip);
if (!x_intersect_rectangles (&wave_clip, &string_clip, &final_clip))
return;
XSetClipRectangles (s->display, s->gc, 0, 0, &final_clip, 1, Unsorted);
/* Draw the waves */
x1 = x0 - (x0 % dx);
x2 = x1 + dx;
odd = (x1/dx) % 2;
y1 = y2 = y0;
if (odd)
y1 += dy;
else
y2 += dy;
while (x1 <= xmax)
{
XDrawLine (s->display, s->window, s->gc, x1, y1, x2, y2);
x1 = x2, y1 = y2;
x2 += dx, y2 = y0 + odd*dy;
odd = !odd;
}
/* Restore previous clipping rectangle(s) */
XSetClipRectangles (s->display, s->gc, 0, 0, s->clip, s->num_clips, Unsorted);
}
/* Draw glyph string S. */ /* Draw glyph string S. */
@ -2765,68 +2824,83 @@ x_draw_glyph_string (struct glyph_string *s)
{ {
/* Draw underline. */ /* Draw underline. */
if (s->face->underline_p) if (s->face->underline_p)
{ {
unsigned long thickness, position; if (s->face->underline_type == FACE_UNDER_WAVE)
int y; {
if (s->face->underline_defaulted_p)
x_draw_underwave (s);
else
{
XGCValues xgcv;
XGetGCValues (s->display, s->gc, GCForeground, &xgcv);
XSetForeground (s->display, s->gc, s->face->underline_color);
x_draw_underwave (s);
XSetForeground (s->display, s->gc, xgcv.foreground);
}
}
else if (s->face->underline_type == FACE_UNDER_LINE)
{
unsigned long thickness, position;
int y;
if (s->prev && s->prev->face->underline_p) if (s->prev && s->prev->face->underline_p)
{ {
/* We use the same underline style as the previous one. */ /* We use the same underline style as the previous one. */
thickness = s->prev->underline_thickness; thickness = s->prev->underline_thickness;
position = s->prev->underline_position; position = s->prev->underline_position;
} }
else else
{ {
/* Get the underline thickness. Default is 1 pixel. */ /* Get the underline thickness. Default is 1 pixel. */
if (s->font && s->font->underline_thickness > 0) if (s->font && s->font->underline_thickness > 0)
thickness = s->font->underline_thickness; thickness = s->font->underline_thickness;
else else
thickness = 1; thickness = 1;
if (x_underline_at_descent_line) if (x_underline_at_descent_line)
position = (s->height - thickness) - (s->ybase - s->y); position = (s->height - thickness) - (s->ybase - s->y);
else else
{ {
/* Get the underline position. This is the recommended /* Get the underline position. This is the recommended
vertical offset in pixels from the baseline to the top of vertical offset in pixels from the baseline to the top of
the underline. This is a signed value according to the the underline. This is a signed value according to the
specs, and its default is specs, and its default is
ROUND ((maximum descent) / 2), with ROUND ((maximum descent) / 2), with
ROUND(x) = floor (x + 0.5) */ ROUND(x) = floor (x + 0.5) */
if (x_use_underline_position_properties
&& s->font && s->font->underline_position >= 0)
position = s->font->underline_position;
else if (s->font)
position = (s->font->descent + 1) / 2;
else
position = underline_minimum_offset;
}
position = max (position, underline_minimum_offset);
}
/* Check the sanity of thickness and position. We should
avoid drawing underline out of the current line area. */
if (s->y + s->height <= s->ybase + position)
position = (s->height - 1) - (s->ybase - s->y);
if (s->y + s->height < s->ybase + position + thickness)
thickness = (s->y + s->height) - (s->ybase + position);
s->underline_thickness = thickness;
s->underline_position = position;
y = s->ybase + position;
if (s->face->underline_defaulted_p)
XFillRectangle (s->display, s->window, s->gc,
s->x, y, s->width, thickness);
else
{
XGCValues xgcv;
XGetGCValues (s->display, s->gc, GCForeground, &xgcv);
XSetForeground (s->display, s->gc, s->face->underline_color);
XFillRectangle (s->display, s->window, s->gc,
s->x, y, s->width, thickness);
XSetForeground (s->display, s->gc, xgcv.foreground);
}
}
if (x_use_underline_position_properties
&& s->font && s->font->underline_position >= 0)
position = s->font->underline_position;
else if (s->font)
position = (s->font->descent + 1) / 2;
else
position = underline_minimum_offset;
}
position = max (position, underline_minimum_offset);
}
/* Check the sanity of thickness and position. We should
avoid drawing underline out of the current line area. */
if (s->y + s->height <= s->ybase + position)
position = (s->height - 1) - (s->ybase - s->y);
if (s->y + s->height < s->ybase + position + thickness)
thickness = (s->y + s->height) - (s->ybase + position);
s->underline_thickness = thickness;
s->underline_position = position;
y = s->ybase + position;
if (s->face->underline_defaulted_p)
XFillRectangle (s->display, s->window, s->gc,
s->x, y, s->width, thickness);
else
{
XGCValues xgcv;
XGetGCValues (s->display, s->gc, GCForeground, &xgcv);
XSetForeground (s->display, s->gc, s->face->underline_color);
XFillRectangle (s->display, s->window, s->gc,
s->x, y, s->width, thickness);
XSetForeground (s->display, s->gc, xgcv.foreground);
}
}
}
/* Draw overline. */ /* Draw overline. */
if (s->face->overline_p) if (s->face->overline_p)
{ {