]> git.donarmstrong.com Git - lilypond.git/blob - scm/define-markup-commands.scm
input/test fixes
[lilypond.git] / scm / define-markup-commands.scm
1 ;;;; define-markup-commands.scm -- markup commands
2 ;;;;
3 ;;;;  source file of the GNU LilyPond music typesetter
4 ;;;; 
5 ;;;; (c)  2000--2004  Han-Wen Nienhuys <hanwen@cs.uu.nl>
6 ;;;;                  Jan Nieuwenhuizen <janneke@gnu.org>
7
8 ;;; markup commands
9 ;;; TODO:
10 ;;;  * each markup function should have a doc string with
11 ;;     syntax, description and example. 
12
13
14 (def-markup-command (stencil paper props stil) (ly:stencil?)
15   "Stencil as markup"
16   stil)
17
18
19 (def-markup-command (score paper props score) (ly:score?)
20   (let*
21       ((systems (ly:score-embedded-format score paper)))
22
23     (if (= 0 (vector-length systems))
24         (begin
25           (ly:warn "No systems found in \\score markup. Did you forget \\paper?")
26           empty-markup)
27         (begin
28           (let*
29               ((stencil (ly:paper-system-stencil  (vector-ref systems 0)))) 
30
31             (ly:stencil-align-to! stencil Y CENTER)
32             stencil)))))
33
34 (def-markup-command (simple paper props str) (string?)
35   "A simple text string; @code{\\markup @{ foo @}} is equivalent with
36 @code{\\markup @{ \\simple #\"foo\" @}}."
37     (interpret-markup paper props str))
38
39 (def-markup-command (encoded-simple paper props sym str) (symbol? string?)
40   "A text string, encoded with encoding @var{sym}. "
41   (Text_item::interpret_string paper
42                                props sym str))
43
44
45 ;; TODO: use font recoding.
46 ;;                    (make-line-markup
47 ;;                     (map make-word-markup (string-tokenize str)))))
48
49 (define-public empty-markup
50   (make-simple-markup ""))
51
52
53 (def-markup-command (postscript paper props str) (string?)
54
55   "This inserts @var{str} directly into the output as a PostScript command string."
56   (ly:make-stencil
57    (list 'embedded-ps str)
58    '(0 . 0) '(0 . 0)  ))
59
60 ;;(def-markup-command (fill-line paper props line-width markups)
61 ;;  (number? markup-list?)
62 ;; no parser tag -- should make number? markuk-list? thingy
63 (def-markup-command (fill-line paper props markups)
64   (markup-list?)
65   "Put @var{markups} in a horizontal line of width @var{line-width}.
66    The markups are spaced/flushed to fill the entire line."
67
68   (let* ((stencils (map (lambda (x) (interpret-markup paper props x))
69                         markups))
70          (text-width (apply + (map interval-length
71                                    (map (lambda (x)
72                                           (ly:stencil-extent x X))
73                                         stencils))))
74         (word-count (length markups))
75         (word-space (chain-assoc-get 'word-space props))
76         (line-width (chain-assoc-get 'linewidth props))
77         (fill-space (if (< line-width text-width)
78                         word-space
79                         (/ (- line-width text-width)
80                            (if (= word-count 1) 2 (- word-count 1)))))
81         (line-stencils (if (= word-count 1)
82                            (map (lambda (x) (interpret-markup paper props x))
83                                 (list (make-simple-markup "")
84                                       (make-stencil-markup (car stencils))
85                                       (make-simple-markup "")))
86                                 stencils)))
87     (stack-stencil-line fill-space line-stencils)))
88   
89 (define (font-markup qualifier value)
90   (lambda (paper props arg)
91     (interpret-markup paper
92                       (prepend-alist-chain qualifier value props)
93                       arg)))
94
95 (def-markup-command (line paper props args) (markup-list?)
96   "Put @var{args} in a horizontal line.  The property @code{word-space}
97 determines the space between each markup in @var{args}."
98   (stack-stencil-line
99    (chain-assoc-get 'word-space props)
100    (map (lambda (m) (interpret-markup paper props m)) args)))
101
102 (def-markup-command (combine paper props m1 m2) (markup? markup?)
103   "Print two markups on top of each other."
104   (let*
105       ((s1 (interpret-markup paper props m1))
106        (s2 (interpret-markup paper props m2)))
107              
108     (ly:stencil-add s1 s2)))
109
110 (def-markup-command (finger paper props arg) (markup?)
111   "Set the argument as small numbers."
112   (interpret-markup paper
113                     (cons '((font-size . -5) (font-family . number)) props)
114                     arg))
115
116 (def-markup-command (fontsize paper props mag arg) (number? markup?)
117   "This sets the relative font size, e.g.
118 @example
119 A \\fontsize #2 @{ B C @} D
120 @end example
121
122
123 This will enlarge the B and the C by two steps.
124 "
125   (interpret-markup
126    paper 
127    (prepend-alist-chain 'font-size mag props)
128    arg))
129
130 (def-markup-command (magnify paper props sz arg) (number? markup?)
131   "This sets the font magnification for the its argument. In the following
132 example, the middle A will be 10% larger:
133 @example
134 A \\magnify #1.1 @{ A @} A
135 @end example
136
137 Note: magnification only works if a font-name is explicitly selected.
138 Use @code{\\fontsize} otherwise."
139
140   (interpret-markup
141    paper 
142    (prepend-alist-chain 'font-magnification sz props)
143    arg))
144
145 (def-markup-command (bold paper props arg) (markup?)
146   "Switch to bold font-series"
147   (interpret-markup paper (prepend-alist-chain 'font-series 'bold props) arg))
148
149 (def-markup-command (sans paper props arg) (markup?)
150   "Switch to the sans-serif family"
151   (interpret-markup paper (prepend-alist-chain 'font-family 'sans props) arg))
152
153 (def-markup-command (number paper props arg) (markup?)
154   "Set font family to @code{number}, which yields the font used for
155 time signatures and fingerings.  This font only contains numbers and
156 some punctuation. It doesn't have any letters.  "
157   (interpret-markup paper (prepend-alist-chain 'font-encoding 'fetaNumber props) arg))
158
159 (def-markup-command (roman paper props arg) (markup?)
160   "Set font family to @code{roman}."
161   (interpret-markup paper (prepend-alist-chain 'font-family 'roman props) arg))
162
163 (def-markup-command (huge paper props arg) (markup?)
164   "Set font size to +2."
165   (interpret-markup paper (prepend-alist-chain 'font-size 2 props) arg))
166
167 (def-markup-command (large paper props arg) (markup?)
168   "Set font size to +1."
169   (interpret-markup paper (prepend-alist-chain 'font-size 1 props) arg))
170
171 (def-markup-command (normalsize paper props arg) (markup?)
172   "Set font size to default."
173   (interpret-markup paper (prepend-alist-chain 'font-size 0 props) arg))
174
175 (def-markup-command (small paper props arg) (markup?)
176   "Set font size to -1."
177   (interpret-markup paper (prepend-alist-chain 'font-size -1 props) arg))
178
179 (def-markup-command (tiny paper props arg) (markup?)
180   "Set font size to -2."
181   (interpret-markup paper (prepend-alist-chain 'font-size -2 props) arg))
182
183 (def-markup-command (teeny paper props arg) (markup?)
184   "Set font size to -3."
185   (interpret-markup paper (prepend-alist-chain 'font-size -3 props) arg))
186
187 (def-markup-command (caps paper props arg) (markup?)
188   "Set font shape to @code{caps}."
189   (interpret-markup paper (prepend-alist-chain 'font-shape 'caps props) arg))
190
191 (def-markup-command (latin-i paper props arg) (markup?)
192   "TEST latin1 encoding."
193   (interpret-markup paper (prepend-alist-chain 'font-shape 'latin1 props) arg))
194
195 (def-markup-command (dynamic paper props arg) (markup?)
196   "Use the dynamic font.  This font only contains @b{s}, @b{f}, @b{m},
197 @b{z}, @b{p}, and @b{r}.  When producing phrases, like ``pi@`{u} @b{f}'', the
198 normal words (like ``pi@`{u}'') should be done in a different font.  The
199 recommend font for this is bold and italic"
200   (interpret-markup
201    paper (prepend-alist-chain 'font-encoding 'fetaDynamic props) arg))
202
203 (def-markup-command (italic paper props arg) (markup?)
204   "Use italic @code{font-shape} for @var{arg}. "
205   (interpret-markup paper (prepend-alist-chain 'font-shape 'italic props) arg))
206
207 (def-markup-command (typewriter paper props arg) (markup?)
208   "Use @code{font-family} typewriter for @var{arg}."
209   (interpret-markup
210    paper (prepend-alist-chain 'font-family 'typewriter props) arg))
211
212 (def-markup-command (upright paper props arg) (markup?)
213   "Set font shape to @code{upright}."
214   (interpret-markup
215    paper (prepend-alist-chain 'font-shape 'upright props) arg))
216
217 (def-markup-command (doublesharp paper props) ()
218   "Draw a double sharp symbol."
219
220   (interpret-markup paper props (markup #:musicglyph "accidentals-4")))
221 (def-markup-command (sesquisharp paper props) ()
222   "Draw a 3/2 sharp symbol."
223   (interpret-markup paper props (markup #:musicglyph "accidentals-3")))
224
225 (def-markup-command (sharp paper props) ()
226   "Draw a sharp symbol."
227   (interpret-markup paper props (markup #:musicglyph "accidentals-2")))
228 (def-markup-command (semisharp paper props) ()
229   "Draw a semi sharp symbol."
230   (interpret-markup paper props (markup #:musicglyph "accidentals-1")))
231 (def-markup-command (natural paper props) ()
232   "Draw a natural symbol."
233
234   (interpret-markup paper props (markup #:musicglyph "accidentals-0")))
235 (def-markup-command (semiflat paper props) ()
236   "Draw a semiflat."
237   (interpret-markup paper props (markup #:musicglyph "accidentals--1")))
238 (def-markup-command (flat paper props) ()
239   "Draw a flat symbol."
240   
241   (interpret-markup paper props (markup #:musicglyph "accidentals--2")))
242 (def-markup-command (sesquiflat paper props) ()
243   "Draw a 3/2 flat symbol."
244   
245   (interpret-markup paper props (markup #:musicglyph "accidentals--3")))
246 (def-markup-command (doubleflat paper props) ()
247   "Draw a double flat symbol."
248
249   (interpret-markup paper props (markup #:musicglyph "accidentals--4")))
250
251
252 (def-markup-command (column paper props args) (markup-list?)
253   "Stack the markups in @var{args} vertically."
254   (stack-lines
255    -1 0.0 (chain-assoc-get 'baseline-skip props)
256    (map (lambda (m) (interpret-markup paper props m)) args)))
257
258 (def-markup-command (dir-column paper props args) (markup-list?)
259   "Make a column of args, going up or down, depending on the setting
260 of the @code{#'direction} layout property."
261   (let* ((dir (chain-assoc-get 'direction props)))
262     (stack-lines
263      (if (number? dir) dir -1)
264      0.0
265       (chain-assoc-get 'baseline-skip props)
266      (map (lambda (x) (interpret-markup paper props x)) args))))
267
268 (def-markup-command (center-align paper props args) (markup-list?)
269   "Put @code{args} in a centered column. "
270   (let* ((mols (map (lambda (x) (interpret-markup paper props x)) args))
271          (cmols (map (lambda (x) (ly:stencil-align-to! x X CENTER)) mols)))
272     (stack-lines -1 0.0 (chain-assoc-get 'baseline-skip props) mols)))
273
274 (def-markup-command (vcenter paper props arg) (markup?)
275   "Align @code{arg} to its center. "
276   (let* ((mol (interpret-markup paper props arg)))
277     (ly:stencil-align-to! mol Y CENTER)
278     mol))
279
280 (def-markup-command (right-align paper props arg) (markup?)
281   (let* ((m (interpret-markup paper props arg)))
282     (ly:stencil-align-to! m X RIGHT)
283     m))
284
285 (def-markup-command (left-align paper props arg) (markup?)
286   "Align @var{arg} on its left edge. "
287   
288   (let* ((m (interpret-markup paper props arg)))
289     (ly:stencil-align-to! m X LEFT)
290     m))
291
292 (def-markup-command (general-align paper props axis dir arg)  (integer? number? markup?)
293   "Align @var{arg} in @var{axis} direction to the @var{dir} side."
294   (let* ((m (interpret-markup paper props arg)))
295
296     (ly:stencil-align-to! m axis dir)
297     m
298   ))
299
300 (def-markup-command (halign paper props dir arg) (number? markup?)
301   "Set horizontal alignment. If @var{dir} is -1, then it is
302 left-aligned, while+1 is right. Values in between interpolate alignment
303 accordingly."
304
305   
306   (let* ((m (interpret-markup paper props arg)))
307     (ly:stencil-align-to! m X dir)
308     m))
309
310 (def-markup-command (musicglyph paper props glyph-name) (string?)
311   "This is converted to a musical symbol, e.g. @code{\\musicglyph
312 #\"accidentals-0\"} will select the natural sign from the music font.
313 See @usermanref{The Feta font} for  a complete listing of the possible glyphs.
314 "
315   (ly:find-glyph-by-name
316    (ly:paper-get-font paper (cons '((font-encoding . fetaMusic))
317                                   props))
318    glyph-name))
319
320
321 (def-markup-command (lookup paper props glyph-name) (string?)
322   "Lookup a glyph by name."
323   (ly:find-glyph-by-name (ly:paper-get-font paper props)
324                          glyph-name))
325
326 (def-markup-command (char paper props num) (integer?)
327   "This produces a single character, e.g. @code{\\char #65} produces the 
328 letter 'A'."
329   (ly:get-glyph (ly:paper-get-font paper props) num))
330
331 (def-markup-command (raise paper props amount arg) (number? markup?)
332   "
333 This  raises  @var{arg}, by the distance @var{amount}.
334 A negative @var{amount} indicates lowering:
335 @c
336 @lilypond[verbatim,fragment,relative=1]
337  c1^\\markup { C \\small \\raise #1.0 \\bold { \"9/7+\" }}
338 @end lilypond
339 The argument to @code{\\raise} is the vertical displacement amount,
340 measured in (global) staff spaces.  @code{\\raise} and @code{\\super}
341 raise objects in relation to their surrounding markups.
342
343 If the text object itself is positioned above or below the staff, then
344 @code{\\raise} cannot be used to move it, since the mechanism that
345 positions it next to the staff cancels any shift made with
346 @code{\\raise}. For vertical positioning, use the @code{padding}
347 and/or @code{extra-offset} properties. "
348
349   
350   (ly:stencil-translate-axis (interpret-markup paper props arg)
351                               amount Y))
352
353 (def-markup-command (fraction paper props arg1 arg2) (markup? markup?)
354   "Make a fraction of two markups."
355   
356   (let* ((m1 (interpret-markup paper props arg1))
357          (m2 (interpret-markup paper props arg2)))
358     (ly:stencil-align-to! m1 X CENTER)
359     (ly:stencil-align-to! m2 X CENTER)    
360     (let* ((x1 (ly:stencil-extent m1 X))
361            (x2 (ly:stencil-extent m2 X))
362            (line (ly:round-filled-box (interval-union x1 x2) '(-0.05 . 0.05) 0.0))
363            ;; should stack mols separately, to maintain LINE on baseline
364            (stack (stack-lines -1 0.2 0.6 (list m1 line m2))))
365       (ly:stencil-align-to! stack Y CENTER)
366       (ly:stencil-align-to! stack X LEFT)
367       ;; should have EX dimension
368       ;; empirical anyway
369       (ly:stencil-translate-axis stack 0.75 Y))))
370
371
372 ;; TODO: better syntax.
373
374 (def-markup-command (note-by-number paper props log dot-count dir) (number? number? number?)
375   "Construct a note symbol, with stem.  By using fractional values for
376 @var{dir}, you can obtain longer or shorter stems."
377   
378   (let* ((font (ly:paper-get-font paper (cons '((font-encoding . fetaMusic)) props)))
379          (stemlen (max 3 (- log 1)))
380          (headgl (ly:find-glyph-by-name
381                   font
382                   (string-append "noteheads-" (number->string (min log 2)))))
383          (stemth 0.13)
384          (stemy (* dir stemlen))
385          (attachx (if (> dir 0)
386                       (- (cdr (ly:stencil-extent headgl X)) stemth)
387                       0))
388          (attachy (* dir 0.28))
389          (stemgl (and (> log 0)
390                       (ly:round-filled-box
391                        (cons attachx (+ attachx  stemth))
392                        (cons (min stemy attachy)
393                              (max stemy attachy))
394                        (/ stemth 3))))
395          (dot (ly:find-glyph-by-name font "dots-dot"))
396          (dotwid (interval-length (ly:stencil-extent dot X)))
397          (dots (and (> dot-count 0)
398                     (apply ly:stencil-add
399                            (map (lambda (x)
400                                   (ly:stencil-translate-axis
401                                    dot  (* (+ 1 (* 2 x)) dotwid) X) )
402                                 (iota dot-count 1)))))
403          (flaggl (and (> log 2)
404                       (ly:stencil-translate
405                        (ly:find-glyph-by-name font
406                                               (string-append "flags-"
407                                                              (if (> dir 0) "u" "d")
408                                                              (number->string log)))
409                        (cons (+ attachx (/ stemth 2)) stemy)))))
410     (if flaggl
411         (set! stemgl (ly:stencil-add flaggl stemgl)))
412     (if (ly:stencil? stemgl)
413         (set! stemgl (ly:stencil-add stemgl headgl))
414         (set! stemgl headgl))
415     (if (ly:stencil? dots)
416         (set! stemgl
417               (ly:stencil-add
418                (ly:stencil-translate-axis dots
419                                            (+ (if (and (> dir 0) (> log 2))
420                                                   (* 1.5 dotwid)
421                                                   0)
422                                               ;; huh ? why not necessary?
423                                               ;;(cdr (ly:stencil-extent headgl X))
424                                               dotwid)
425                                            X)
426                stemgl)))
427     stemgl))
428
429 (use-modules (ice-9 regex))
430
431 (define-public log2 
432   (let ((divisor (log 2)))
433     (lambda (z) (inexact->exact (/ (log z) divisor)))))
434
435 (define (parse-simple-duration duration-string)
436   "Parse the `duration-string', e.g. ''4..'' or ''breve.'', and return a (log dots) list."
437   (let ((match (regexp-exec (make-regexp "(breve|longa|maxima|[0-9]+)(\\.*)") duration-string)))
438     (if (and match (string=? duration-string (match:substring match 0)))
439         (let ((len  (match:substring match 1))
440               (dots (match:substring match 2)))
441           (list (cond ((string=? len "breve")  -1)
442                       ((string=? len "longa")  -2)
443                       ((string=? len "maxima") -3)
444                       (else (log2 (string->number len))))
445                 (if dots (string-length dots) 0)))
446         (error "This is not a valid duration string:" duration-string))))
447
448 (def-markup-command (note paper props duration dir) (string? number?)
449   "This produces a note with a stem pointing in @var{dir} direction, with
450 the @var{duration} for the note head type and augmentation dots. For
451 example, @code{\\note #\"4.\" #-0.75} creates a dotted quarter note, with
452 a shortened down stem."
453   
454   (let ((parsed (parse-simple-duration duration)))
455     (note-by-number-markup paper props (car parsed) (cadr parsed) dir)))
456
457 (def-markup-command (normal-size-super paper props arg) (markup?)
458   "A superscript which does not use a smaller font."
459   
460   (ly:stencil-translate-axis (interpret-markup
461                                paper
462                                props arg)
463                               (* 0.5  (chain-assoc-get 'baseline-skip props))
464                               Y))
465
466 (def-markup-command (super paper props arg) (markup?)
467   "
468 @cindex raising text
469 @cindex lowering text
470 @cindex moving text
471 @cindex translating text
472
473 @cindex @code{\\super}
474
475
476 Raising and lowering texts can be done with @code{\\super} and
477 @code{\\sub}:
478
479 @lilypond[verbatim,fragment,relative=1]
480  c1^\\markup { E \"=\" mc \\super \"2\" }
481 @end lilypond
482
483 "
484   
485   (ly:stencil-translate-axis
486    (interpret-markup
487     paper
488     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
489     arg)
490    (* 0.5 (chain-assoc-get 'baseline-skip props))
491    Y))
492
493 (def-markup-command (translate paper props offset arg) (number-pair? markup?)
494   "This translates an object. Its first argument is a cons of numbers
495 @example
496 A \\translate #(cons 2 -3) @{ B C @} D
497 @end example
498 This moves `B C' 2 spaces to the right, and 3 down, relative to its
499 surroundings. This command cannot be used to move isolated scripts
500 vertically, for the same reason that @code{\\raise} cannot be used for
501 that.
502
503 . "
504   (ly:stencil-translate (interpret-markup  paper props arg)
505                          offset))
506
507 (def-markup-command (sub paper props arg) (markup?)
508   "Set @var{arg} in subscript."
509   
510   (ly:stencil-translate-axis
511    (interpret-markup
512     paper
513     (cons `((font-size . ,(- (chain-assoc-get 'font-size props 0) 3))) props)
514     arg)
515    (* -0.5 (chain-assoc-get 'baseline-skip props))
516    Y))
517
518 (def-markup-command (normal-size-sub paper props arg) (markup?)
519   "Set @var{arg} in subscript, in a normal font size."
520
521   (ly:stencil-translate-axis
522    (interpret-markup paper props arg)
523    (* -0.5 (chain-assoc-get 'baseline-skip props))
524    Y))
525
526 (def-markup-command (hbracket paper props arg) (markup?)
527   "Draw horizontal brackets around @var{arg}."  
528   (let ((th 0.1) ;; todo: take from GROB.
529         (m (interpret-markup paper props arg)))
530     (bracketify-stencil m X th (* 2.5 th) th)))
531
532 (def-markup-command (bracket paper props arg) (markup?)
533   "Draw vertical brackets around @var{arg}."  
534   (let ((th 0.1) ;; todo: take from GROB.
535         (m (interpret-markup paper props arg)))
536     (bracketify-stencil m Y th (* 2.5 th) th)))
537
538 ;; todo: fix negative space
539 (def-markup-command (hspace paper props amount) (number?)
540   "This produces a invisible object taking horizontal space.
541 @example 
542 \\markup @{ A \\hspace #2.0 B @} 
543 @end example
544 will put extra space between A and B, on top of the space that is
545 normally inserted before elements on a line.
546 "
547   (if (> amount 0)
548       (ly:make-stencil "" (cons 0 amount) '(-1 . 1) )
549       (ly:make-stencil "" (cons amount amount) '(-1 . 1))))
550
551 (def-markup-command (override paper props new-prop arg) (pair? markup?)
552   "Add the first argument in to the property list.  Properties may be
553 any sort of property supported by @internalsref{font-interface} and
554 @internalsref{text-interface}, for example
555
556 @verbatim
557 \\override #'(font-family . married) \"bla\"
558 @end verbatim
559
560 "
561   (interpret-markup paper (cons (list new-prop) props) arg))
562
563 (def-markup-command (smaller paper props arg) (markup?)
564   "Decrease the font size relative to current setting"
565   (let* ((fs (chain-assoc-get 'font-size props 0))
566          (entry (cons 'font-size (- fs 1))))
567     (interpret-markup paper (cons (list entry) props) arg)))
568
569
570 (def-markup-command (bigger paper props arg) (markup?)
571   "Increase the font size relative to current setting"
572   (let* ((fs (chain-assoc-get 'font-size props 0))
573          (entry (cons 'font-size (+ fs 1))))
574     (interpret-markup paper (cons (list entry) props) arg)))
575
576 (def-markup-command larger (markup?)
577   bigger-markup)
578
579 (def-markup-command (box paper props arg) (markup?)
580   "Draw a box round @var{arg}"
581   
582   (let ((th 0.1)
583         (pad 0.2)
584         (m (interpret-markup paper props arg)))
585     (box-stencil m th pad)))
586
587 (def-markup-command (strut paper props) ()
588   
589   "Create a box of the same height as the space in the current font.
590
591 FIXME: is this working? 
592 "
593   
594   (let ((m (Text_item::interpret_markup paper props " ")))
595     (ly:stencil-set-extent! m X '(1000 . -1000))
596     m))
597
598 (define number->mark-letter-vector (make-vector 25 #\A))
599
600 (do ((i 0 (1+ i))
601      (j 0 (1+ j)))
602     ((>= i 26))
603   (if (= i (- (char->integer #\I) (char->integer #\A)))
604       (set! i (1+ i)))
605   (vector-set! number->mark-letter-vector j
606                (integer->char (+ i (char->integer #\A)))))
607
608 (define (number->markletter-string n)
609   "Double letters for big marks."
610   (let*
611       ((l (vector-length number->mark-letter-vector)))
612     
613   (if (>= n l)
614       (string-append (number->markletter-string (1- (quotient n l)))
615                      (number->markletter-string (remainder n l)))
616       (make-string 1 (vector-ref number->mark-letter-vector n)))))
617
618
619 (def-markup-command (markletter paper props num) (integer?)
620    "Make a markup letter for @var{num}.  The letters start with A to Z
621  (skipping I), and continues with double letters."
622  
623    (Text_item::interpret_markup paper props (number->markletter-string num)))
624
625
626
627
628 (def-markup-command (bracketed-y-column paper props indices args)
629   (list? markup-list?)
630   "Make a column of the markups in @var{args}, putting brackets around
631 the elements marked in @var{indices}, which is a list of numbers."
632
633     (define (sublist l start stop)
634     (take (drop l start)  (- (1+ stop) start)) )
635
636   (define (stencil-list-extent ss axis)
637     (cons
638      (apply min (map (lambda (x) (car (ly:stencil-extent x axis))) ss))
639      (apply max (map (lambda (x) (cdr (ly:stencil-extent x axis))) ss))))
640             
641   (define (stack-stencils stencils bskip last-stencil)
642     (cond
643      ((null? stencils) '())
644      ((not last-stencil)
645       (cons (car stencils)
646             (stack-stencils (cdr stencils) bskip (car stencils))))
647      (else
648       (let*
649           ((orig (car stencils))
650            (dir (chain-assoc-get 'direction  props DOWN))
651            (new (ly:stencil-moved-to-edge last-stencil Y dir
652                                           orig
653                                           0.1 bskip))
654            )
655
656         (cons new (stack-stencils (cdr stencils) bskip new))))
657     ))
658
659   (define (make-brackets stencils indices acc)
660     (if (and stencils
661              (pair? indices)
662              (pair? (cdr indices)))
663         (let*
664             ((encl (sublist stencils (car indices) (cadr indices)))
665              (x-ext (stencil-list-extent encl X))
666              (y-ext (stencil-list-extent encl Y))
667              (thick 0.10)
668              (pad 0.35)
669              (protusion (* 2.5 thick))
670              (lb
671               (ly:stencil-translate-axis 
672                (ly:bracket Y y-ext thick protusion)
673                (- (car x-ext) pad) X))
674              (rb (ly:stencil-translate-axis
675                   (ly:bracket Y y-ext thick (- protusion))
676                   (+ (cdr x-ext) pad) X))
677              )
678
679           (make-brackets
680            stencils (cddr indices)
681            (append
682             (list lb rb)
683              acc)))
684         acc))
685
686   (let*
687       ((stencils
688         (map (lambda (x)
689                (interpret-markup
690                 paper
691                 props
692                 x)) args))
693        (leading
694          (chain-assoc-get 'baseline-skip props))
695        (stacked (stack-stencils stencils 1.25 #f))
696        (brackets (make-brackets stacked indices '()))
697        )
698
699     (apply ly:stencil-add
700            (append stacked brackets)
701            )))
702
703
704              
705
706   
707   
708
709