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> 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
......
...@@ -2130,10 +2130,32 @@ Background color, a string. The value can be a system-defined color ...@@ -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.
......
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
......
...@@ -135,8 +135,13 @@ ...@@ -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"
......
...@@ -623,10 +623,21 @@ VALUE must be a color name, a string. ...@@ -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'
......
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))
......
...@@ -1510,6 +1510,13 @@ enum face_box_type ...@@ -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 ...@@ -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;
......
...@@ -2595,6 +2595,60 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. ...@@ -2595,6 +2595,60 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
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 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. ...@@ -2608,63 +2662,75 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors.
/* 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
[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 the prev was underlined, match its appearance. */
if (descent <= position) if (s->prev && s->prev->face->underline_p
&& s->prev->underline_thickness > 0)
{ {
position = descent - 1; thickness = s->prev->underline_thickness;
thickness = 1; position = s->prev->underline_position;
} }
else if (descent < position + thickness) else
thickness = 1; {
} 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; position = max (position, underline_minimum_offset);
s->underline_position = position;
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) s->underline_thickness = thickness;
[defaultCol set]; s->underline_position = position;
else
[ns_lookup_indexed_color (face->underline_color, s->f) set]; r = NSMakeRect (x, s->ybase + position, width, thickness);
NSRectFill (r);
}
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)
......
...@@ -313,6 +313,94 @@ w32_set_clip_rectangle (HDC hdc, RECT *rect) ...@@ -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) ...@@ -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;
if (s->prev && s->prev->face->underline_p)
{ {
/* We use the same underline style as the previous one. */ COLORREF color;
thickness = s->prev->underline_thickness;
position = s->prev->underline_position; 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. */ unsigned long thickness, position;
if (s->font && s->font->underline_thickness > 0) int y;
thickness = s->font->underline_thickness;
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
thickness = 1; {
if (x_underline_at_descent_line) /* Get the underline thickness. Default is 1 pixel. */
position = (s->height - thickness) - (s->ybase - s->y); 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 else
{ {
/* Get the underline position. This is the recommended w32_fill_area (s->f, s->hdc, s->face->underline_color, s->x,
vertical offset in pixels from the baseline to the top of y, s->width, 1);
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
{
w32_fill_area (s->f, s->hdc, s->face->underline_color, s->x,
y, s->width, 1);
} }
} }
/* Draw overline. */ /* Draw overline. */
......
...@@ -320,6 +320,7 @@ static Lisp_Object QCfontset; ...@@ -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) ...@@ -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, ...@@ -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 ...@@ -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;