From: "Aurélien Aptel" <aurelien.aptel+emacs@gmail.com>
To: Alp Aker <alptekin.aker@gmail.com>
Cc: "Juanma Barranquero" <lekktu@gmail.com>,
emacs-devel@gnu.org, "Juri Linkov" <juri@jurta.org>,
"Stefan Monnier" <monnier@iro.umontreal.ca>,
"Jan Djärv" <jan.h.d@swipnet.se>,
"Drew Adams" <drew.adams@oracle.com>
Subject: Re: [patch] add "underwave" face attribute
Date: Mon, 6 Feb 2012 12:27:36 +0100 [thread overview]
Message-ID: <CA+5B0FO7z-CWX2YTuc94SLv43B3i83QVnbABvptJAa+pQSBiXQ@mail.gmail.com> (raw)
In-Reply-To: <CACxch4rA0n_wcSJa8z7ZHZ3aCwPRTWada-By4KNCf4y-tmAYSQ@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 60 bytes --]
Ok, I will send the attached patch to the bug mailing list.
[-- Attachment #2: underwave-clip-3.patch --]
[-- Type: text/x-patch, Size: 32623 bytes --]
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 381eaf6..464dfaa 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2080,10 +2080,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.
diff --git a/lisp/cus-face.el b/lisp/cus-face.el
index d725111..3680a26 100644
--- a/lisp/cus-face.el
+++ b/lisp/cus-face.el
@@ -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"
diff --git a/lisp/faces.el b/lisp/faces.el
index 5d406ad..e68abb1 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -616,10 +616,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'
diff --git a/src/dispextern.h b/src/dispextern.h
index 2c59f4f..cdbbc16 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -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;
diff --git a/src/nsterm.m b/src/nsterm.m
index 70d3cc0..0378330 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -2596,6 +2596,68 @@ 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 = 2
+ --
+ 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, stringClip, finalClip;
+
+ 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);
+ ns_get_glyph_string_clip_rect (s, &stringClip);
+ finalClip = NSIntersectionRect (waveClip, stringClip);
+
+ if (NSIsEmptyRect (finalClip))
+ return;
+
+ [[NSGraphicsContext currentContext] saveGraphicsState];
+ NSRectClip (finalClip);
+
+ /* 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)
@@ -2609,63 +2671,75 @@ ns_draw_text_decoration (struct glyph_string *s, struct face *face,
/* 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;
+
+ position = max (position, underline_minimum_offset);
- s->underline_thickness = thickness;
- s->underline_position = position;
+ /* Ensure underlining is not cropped. */
+ if (descent <= position)
+ {
+ position = descent - 1;
+ thickness = 1;
+ }
+ else if (descent < position + thickness)
+ thickness = 1;
+ }
- r = NSMakeRect (x, s->ybase + position, width, thickness);
+ s->underline_thickness = thickness;
+ s->underline_position = position;
- if (face->underline_defaulted_p)
- [defaultCol set];
- else
- [ns_lookup_indexed_color (face->underline_color, s->f) set];
- NSRectFill (r);
- }
+ 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)
diff --git a/src/w32term.c b/src/w32term.c
index f764ad9..d53f0ab 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -309,6 +309,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
@@ -2343,60 +2431,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. */
diff --git a/src/xfaces.c b/src/xfaces.c
index 617097d..8bee2d6 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -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;
@@ -1889,7 +1890,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])
@@ -2520,7 +2522,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;
@@ -2944,15 +2947,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;
}
@@ -3762,6 +3804,7 @@ Value is nil if ATTR doesn't have a discrete set of valid values. */)
CHECK_SYMBOL (attr);
+ /* XXX: no check for QCbox? */
if (EQ (attr, QCunderline))
result = Fcons (Qt, Fcons (Qnil, Qnil));
else if (EQ (attr, QCoverline))
@@ -5563,7 +5606,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));
@@ -5696,29 +5739,76 @@ 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))
{
@@ -6465,6 +6555,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");
diff --git a/src/xterm.c b/src/xterm.c
index 4b34d63..a58e366 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -2653,6 +2653,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. */
@@ -2755,68 +2814,83 @@ 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)
- {
- /* 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
- 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;
- 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 (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;
+ 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
+ {
+ /* 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;
+ 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. */
if (s->face->overline_p)
{
next prev parent reply other threads:[~2012-02-06 11:27 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-01-26 20:16 [patch] add "underwave" face attribute Aurélien Aptel
2012-01-27 15:50 ` Stefan Monnier
2012-01-27 17:08 ` Drew Adams
2012-01-31 23:38 ` Aurélien Aptel
2012-02-01 16:21 ` Leo
2012-02-01 23:11 ` Juri Linkov
2012-02-02 0:18 ` Aurélien Aptel
2012-02-02 0:52 ` Juanma Barranquero
2012-02-03 19:37 ` Aurélien Aptel
2012-02-04 8:14 ` Jan Djärv
2012-02-04 18:22 ` Aurélien Aptel
2012-02-06 0:18 ` Alp Aker
2012-02-06 0:24 ` Aurélien Aptel
2012-02-06 0:51 ` Alp Aker
2012-02-06 0:55 ` Aurélien Aptel
2012-02-06 1:14 ` Alp Aker
2012-02-06 11:27 ` Aurélien Aptel [this message]
2012-02-06 17:20 ` Aurélien Aptel
2012-02-12 11:19 ` Aurélien Aptel
2012-02-12 16:59 ` Stefan Monnier
2012-02-12 17:08 ` Leo
2012-02-12 17:29 ` Aurélien Aptel
-- strict thread matches above, loose matches on Subject: below --
2012-01-26 17:55 Aurélien Aptel
2012-01-27 4:01 ` Leo
2012-01-27 10:25 ` Andreas Schwab
2012-01-27 13:02 ` Aurélien Aptel
2012-01-27 14:06 ` Andreas Schwab
2012-01-27 14:55 ` Aurélien Aptel
2012-01-27 15:53 ` Andreas Schwab
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CA+5B0FO7z-CWX2YTuc94SLv43B3i83QVnbABvptJAa+pQSBiXQ@mail.gmail.com \
--to=aurelien.aptel+emacs@gmail.com \
--cc=alptekin.aker@gmail.com \
--cc=drew.adams@oracle.com \
--cc=emacs-devel@gnu.org \
--cc=jan.h.d@swipnet.se \
--cc=juri@jurta.org \
--cc=lekktu@gmail.com \
--cc=monnier@iro.umontreal.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.