1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -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>
* 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}.
@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.

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>
* term/xterm.el (terminal-init-xterm): Discard input before

View file

@ -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"

View file

@ -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'

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>
* makefile.w32-in ($(BLD)/emacs.$(O), $(BLD)/fringe.$(O))

View file

@ -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;

View file

@ -2595,6 +2595,60 @@ ns_get_glyph_string_clip_rect (struct glyph_string *s, NativeRectangle *nr)
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,6 +2662,18 @@ ns_draw_text_decoration (struct glyph_string *s, struct face *face,
/* Do underline. */
if (face->underline_p)
{
if (s->face->underline_type == FACE_UNDER_WAVE)
{
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 if (s->face->underline_type == FACE_UNDER_LINE)
{
NSRect r;
unsigned long thickness, position;
@ -2664,7 +2730,7 @@ ns_draw_text_decoration (struct glyph_string *s, struct face *face,
[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)

View file

@ -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
@ -2346,6 +2434,19 @@ x_draw_glyph_string (struct glyph_string *s)
{
/* Draw underline. */
if (s->face->underline_p)
{
if (s->face->underline_type == FACE_UNDER_WAVE)
{
COLORREF color;
if (s->face->underline_defaulted_p)
color = s->gc->foreground;
else
color = s->face->underline_color;
w32_draw_underwave (s, color);
}
else if (s->face->underline_type == FACE_UNDER_LINE)
{
unsigned long thickness, position;
int y;
@ -2403,6 +2504,7 @@ x_draw_glyph_string (struct glyph_string *s)
y, s->width, 1);
}
}
}
/* Draw overline. */
if (s->face->overline_p)
{

View file

@ -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,13 +2951,52 @@ 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))
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);
@ -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));
@ -5709,28 +5751,75 @@ realize_x_face (struct face_cache *cache, Lisp_Object *attrs)
/* 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). */
face->underline_p = 1;
face->underline_type = FACE_UNDER_LINE;
face->underline_defaulted_p = 1;
face->underline_color = 0;
}
else if (STRINGP (attrs[LFACE_UNDERLINE_INDEX]))
else if (STRINGP (underline))
{
/* Use specified color. */
face->underline_p = 1;
face->underline_type = FACE_UNDER_LINE;
face->underline_defaulted_p = 0;
face->underline_color
= load_color (f, face, attrs[LFACE_UNDERLINE_INDEX],
= load_color (f, face, underline,
LFACE_UNDERLINE_INDEX);
}
else if (NILP (attrs[LFACE_UNDERLINE_INDEX]))
else if (NILP (underline))
{
face->underline_p = 0;
face->underline_defaulted_p = 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];
if (STRINGP (overline))
@ -6476,6 +6565,8 @@ syms_of_xfaces (void)
DEFSYM (QCcolor, ":color");
DEFSYM (QCline_width, ":line-width");
DEFSYM (QCstyle, ":style");
DEFSYM (Qline, "line");
DEFSYM (Qwave, "wave");
DEFSYM (Qreleased_button, "released-button");
DEFSYM (Qpressed_button, "pressed-button");
DEFSYM (Qnormal, "normal");

View file

@ -2663,6 +2663,65 @@ x_draw_stretch_glyph_string (struct glyph_string *s)
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. */
@ -2765,6 +2824,21 @@ x_draw_glyph_string (struct glyph_string *s)
{
/* Draw underline. */
if (s->face->underline_p)
{
if (s->face->underline_type == FACE_UNDER_WAVE)
{
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;
@ -2826,7 +2900,7 @@ x_draw_glyph_string (struct glyph_string *s)
XSetForeground (s->display, s->gc, xgcv.foreground);
}
}
}
/* Draw overline. */
if (s->face->overline_p)
{