Commit 9b0e3eba authored by Alp Aker's avatar Alp Aker

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.
parent 40d8bcb8
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>
* configure.in: Rename --enable-use-lisp-union-type to
......
......@@ -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}.
@item :underline
Whether or not characters should be underlined, and in what color. If
the value is @code{t}, underlining uses the foreground color of the
face. If the value is a string, underlining uses that color. The
value @code{nil} means do not underline.
Whether or not characters should be underlined, and in what
color. Here are the possible values of the @code{:underline}
attribute, and what they mean:
@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
Whether or not characters should be overlined, and in what color.
......
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>
* term/xterm.el (terminal-init-xterm): Discard input before
......
......@@ -135,8 +135,13 @@
(choice :tag "Underline"
:help-echo "Control text underlining."
(const :tag "Off" nil)
(const :tag "On" t)
(color :tag "Colored")))
(list :tag "On"
(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
(choice :tag "Overline"
......
......@@ -623,10 +623,21 @@ VALUE must be a color name, a string.
`:underline'
VALUE specifies whether characters in FACE should be underlined. If
VALUE is t, underline with foreground color of the face. If VALUE is
a string, underline with that color. If VALUE is nil, explicitly
don't underline.
VALUE specifies whether characters in FACE should be underlined.
If VALUE is t, underline with foreground color of the face.
If VALUE is a string, underline with that color.
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'
......
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>
* makefile.w32-in ($(BLD)/emacs.$(O), $(BLD)/fringe.$(O))
......
......@@ -1510,6 +1510,13 @@ enum face_box_type
FACE_SUNKEN_BOX
};
/* Underline type. */
enum face_underline_type
{
FACE_UNDER_LINE,
FACE_UNDER_WAVE
};
/* Structure describing a realized face.
......@@ -1585,6 +1592,9 @@ struct face
drawing shadows. */
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,
strike-through or have a box drawn around it. */
unsigned underline_p : 1;
......
......@@ -2595,6 +2595,60 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
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
ns_draw_text_decoration (struct glyph_string *s, struct face *face,
NSColor *defaultCol, CGFloat width, CGFloat x)
......@@ -2608,63 +2662,75 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
/* Do underline. */
if (face->underline_p)
{
NSRect r;
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)
if (s->face->underline_type == FACE_UNDER_WAVE)
{
thickness = s->prev->underline_thickness;
position = s->prev->underline_position;
if (face->underline_defaulted_p)
[defaultCol set];
else
[ns_lookup_indexed_color (face->underline_color, s->f) set];
ns_draw_underwave (s, width, x);
}
else
else if (s->face->underline_type == FACE_UNDER_LINE)
{
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
position = underline_minimum_offset;
position = max (position, underline_minimum_offset);
NSRect r;
unsigned long thickness, position;
/* Ensure underlining is not cropped. */
if (descent <= position)
/* If the prev was underlined, match its appearance. */
if (s->prev && s->prev->face->underline_p
&& s->prev->underline_thickness > 0)
{
position = descent - 1;
thickness = 1;
thickness = s->prev->underline_thickness;
position = s->prev->underline_position;
}
else if (descent < position + thickness)
thickness = 1;
}
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
position = underline_minimum_offset;
s->underline_thickness = thickness;
s->underline_position = position;
position = max (position, underline_minimum_offset);
r = NSMakeRect (x, s->ybase + position, width, thickness);
/* Ensure underlining is not cropped. */
if (descent <= position)
{
position = descent - 1;
thickness = 1;
}
else if (descent < position + thickness)
thickness = 1;
}
if (face->underline_defaulted_p)
[defaultCol set];
else
[ns_lookup_indexed_color (face->underline_color, s->f) set];
NSRectFill (r);
}
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
and ignoring overline_margin. */
if (face->overline_p)
......
......@@ -313,6 +313,94 @@ w32_set_clip_rectangle (HDC hdc, RECT *rect)
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. */
void
......@@ -2347,60 +2435,74 @@ x_draw_glyph_string (struct glyph_string *s)
/* Draw underline. */
if (s->face->underline_p)
{
unsigned long thickness, position;
int y;
if (s->prev && s->prev->face->underline_p)
if (s->face->underline_type == FACE_UNDER_WAVE)
{
/* We use the same underline style as the previous one. */
thickness = s->prev->underline_thickness;
position = s->prev->underline_position;
COLORREF color;
if (s->face->underline_defaulted_p)
color = s->gc->foreground;
else
color = s->face->underline_color;
w32_draw_underwave (s, color);
}
else
else if (s->face->underline_type == FACE_UNDER_LINE)
{
/* Get the underline thickness. Default is 1 pixel. */
if (s->font && s->font->underline_thickness > 0)
thickness = s->font->underline_thickness;
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
thickness = 1;
if (x_underline_at_descent_line)
position = (s->height - thickness) - (s->ybase - s->y);
{
/* Get the underline thickness. Default is 1 pixel. */
if (s->font && s->font->underline_thickness > 0)
thickness = s->font->underline_thickness;
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 (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;
}
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
{
/* 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 (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;
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. */
......
......@@ -320,6 +320,7 @@ static Lisp_Object QCfontset;
Lisp_Object Qnormal;
Lisp_Object Qbold;
static Lisp_Object Qline, Qwave;
static Lisp_Object Qultra_light, Qextra_light, Qlight;
static Lisp_Object Qsemi_light, Qsemi_bold, Qextra_bold, Qultra_bold;
static Lisp_Object Qoblique, Qreverse_oblique, Qreverse_italic;
......@@ -1894,7 +1895,8 @@ check_lface_attrs (Lisp_Object *attrs)
xassert (UNSPECIFIEDP (attrs[LFACE_UNDERLINE_INDEX])
|| IGNORE_DEFFACE_P (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])
|| IGNORE_DEFFACE_P (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)
|| NILP (value)
|| STRINGP (value))
|| STRINGP (value)
|| CONSP (value))
to[LFACE_UNDERLINE_INDEX] = value;
else
err = 1;
......@@ -2948,15 +2951,54 @@ FRAME 0 means change the face on all frames, and change the default
}
else if (EQ (attr, QCunderline))
{
if (!UNSPECIFIEDP (value) && !IGNORE_DEFFACE_P (value))
if ((SYMBOLP (value)
&& !EQ (value, Qt)
&& !EQ (value, Qnil))
/* Underline color. */
|| (STRINGP (value)
&& SCHARS (value) == 0))
signal_error ("Invalid face underline", value);
int valid_p = 0;
if (UNSPECIFIEDP (value) || IGNORE_DEFFACE_P (value))
valid_p = 1;
else if (NILP (value) || EQ (value, Qt))
valid_p = 1;
else if (STRINGP (value) && SCHARS (value) > 0)
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);
LFACE_UNDERLINE (lface) = value;
}
......@@ -5576,7 +5618,7 @@ realize_x_face (struct face_cache *cache, Lisp_Object *attrs)
#ifdef HAVE_WINDOW_SYSTEM
struct face *default_face;
struct frame *f;
Lisp_Object stipple, overline, strike_through, box;
Lisp_Object stipple, underline, overline, strike_through, box;
xassert (FRAME_WINDOW_P (cache->f));