]> git.donarmstrong.com Git - lilypond.git/blob - scm/new-markup.scm
5cccdc2697756ef1d555d386a91e8cbb100d5440
[lilypond.git] / scm / new-markup.scm
1
2 "
3 Internally markup is stored as lists, whose head is a function.
4
5   (FUNCTION ARG1 ARG2 ... )
6
7 When the markup is formatted, then FUNCTION is called as follows
8
9   (FUNCTION GROB PROPS ARG1 ARG2 ... ) 
10
11 GROB is the current grob, PROPS is a list of alists, and ARG1.. are
12 the rest of the arguments.
13
14 The function should return a molecule (i.e. a formatted, ready to
15 print object).
16
17
18
19 To add a function,
20
21 1. It should be named  COMMAND-markup
22
23 2. It should have an object property set that describes it's
24 signature. This is to allow the parser to figure out how many
25 arguments to expect:
26
27   (set-object-property! COMMAND-markup  scm0-markup1)
28
29 (insert in the list below).
30
31 3. The command is now available in markup mode, e.g.
32
33
34   \markup { .... \COMMAND #1 argument ... }
35
36
37 BUGS:
38
39 At present, markup functions must be defined in this
40 file. Implementing user-access for markup functions is an excercise
41 for the reader.
42
43
44  
45
46 " ; " 
47
48 (define-public (simple-markup grob props . rest)
49   (Text_item::text_to_molecule grob props (car rest))
50   )
51
52
53 (define-public (stack-molecule-line space molecules)
54   (if (pair? molecules)
55       (if (pair? (cdr molecules))
56           (let* (
57                  (tail (stack-molecule-line  space (cdr molecules)))
58                  (head (car molecules))
59                  (xoff (+ space (cdr (ly:molecule-get-extent head X))))
60                  )
61             
62             (ly:molecule-add
63              head
64              (ly:molecule-translate-axis tail xoff X))
65           )
66           (car molecules))
67       '())
68   )
69
70 (define-public (line-markup grob props . rest)
71   (stack-molecule-line
72    (cdr (chain-assoc 'word-space props))
73    (map (lambda (x) (interpret-markup grob props x)) (car rest)))
74   )
75
76 (define (combine-molecule-list lst)
77   (if (null? (cdr lst)) (car lst)
78       (ly:molecule-add (car lst) (combine-molecule-list (cdr lst)))
79       ))
80
81 (define-public (combine-markup grob props . rest)
82   (ly:molecule-add
83    (interpret-markup grob props (car rest))
84    (interpret-markup grob props (cadr rest))))
85   
86 ;   (combine-molecule-list (map (lambda (x) (interpret-markup grob props x)) (car rest))))
87
88 (define (font-markup qualifier value)
89   (lambda (grob props . rest)
90     (interpret-markup grob (cons (cons `(,qualifier . ,value) (car props)) (cdr props)) (car rest))
91   
92   ))
93
94
95 (define-public (set-property-markup qualifier)
96   (lambda (grob props . rest  )
97     (interpret-markup grob
98                       (cons (cons `(,qualifier . ,(car rest))
99                                   (car props)) (cdr props))
100                       (cadr rest))
101     ))
102
103
104 (define-public (finger-markup grob props . rest)
105   (interpret-markup grob
106                     (cons (list '(font-relative-size . -3)
107                                 '(font-family . number))
108                                 props)
109                     (car rest)))
110
111
112 (define-public fontsize-markup (set-property-markup 'font-relative-size))
113 (define-public magnify-markup (set-property-markup 'font-magnification))
114
115 (define-public bold-markup
116   (font-markup 'font-series 'bold))
117 (define-public number-markup
118   (font-markup 'font-family 'number))
119 (define-public roman-markup
120   (font-markup 'font-family 'roman))
121
122
123 (define-public huge-markup
124   (font-markup 'font-relative-size 2))
125 (define-public large-markup
126   (font-markup 'font-relative-size 1))
127 (define-public small-markup
128   (font-markup 'font-relative-size -1))
129 (define-public tiny-markup
130   (font-markup 'font-relative-size -2))
131 (define-public teeny-markup
132   (font-markup 'font-relative-size -3))
133 (define-public dynamic-markup
134   (font-markup 'font-family 'dynamic))
135 (define-public italic-markup
136   (font-markup 'font-shape 'italic))
137
138
139 ;; TODO: baseline-skip should come from the font.
140 (define-public (column-markup grob props . rest)
141   (stack-lines
142    -1 0.0 (cdr (chain-assoc 'baseline-skip props))
143    (map (lambda (x) (interpret-markup grob props x)) (car rest)))
144   )
145
146 (define-public (dir-column-markup grob props . rest)
147   "Make a column of args, going up or down, depending on DIRECTION."
148   (let*
149       (
150        (dir (cdr (chain-assoc 'direction props)))
151        )
152     (stack-lines
153      (if (number? dir) dir -1)
154      0.0 (cdr (chain-assoc 'baseline-skip props))
155      (map (lambda (x) (interpret-markup grob props x)) (car rest)))
156     ))
157
158 (define-public (center-markup grob props . rest)
159   (let*
160     (
161      (mols (map (lambda (x) (interpret-markup grob props x)) (car rest)))
162      (cmols (map (lambda (x) (ly:molecule-align-to! x X CENTER)) mols))
163      )
164     
165     (stack-lines
166      -1 0.0 (cdr (chain-assoc 'baseline-skip props))
167      mols)
168     ))
169
170 (define-public (musicglyph-markup grob props . rest)
171   (ly:find-glyph-by-name
172    (ly:get-font grob (cons '((font-family . music)) props))
173    (car rest))
174   )
175
176
177 (define-public (lookup-markup grob props . rest)
178   "Lookup a glyph by name."
179   (ly:find-glyph-by-name
180    (ly:get-font grob props)
181    (car rest))
182   )
183
184 (define-public (char-markup grob props . rest)
185   "Syntax: \\char NUMBER. "
186   (ly:get-glyph  (ly:get-font grob props) (car rest))
187   )
188
189 (define-public (raise-markup grob props  . rest)
190   "Syntax: \\raise AMOUNT MARKUP. "
191   (ly:molecule-translate-axis (interpret-markup
192                                grob
193                                props
194                                (cadr rest))
195                               (car rest) Y)
196   )
197
198 (define-public (normal-size-super-markup grob props . rest)
199   (ly:molecule-translate-axis (interpret-markup
200                                grob
201                                props (car rest))
202                               (* 0.5 (cdr (chain-assoc 'baseline-skip props)))
203                               Y)
204   )
205
206 (define-public (super-markup grob props  . rest)
207   "Syntax: \\super MARKUP. "
208   (ly:molecule-translate-axis (interpret-markup
209                                grob
210                                (cons '((font-relative-size . -2)) props) (car rest))
211                               (* 0.5 (cdr (chain-assoc 'baseline-skip props)))
212                               Y)
213   )
214
215 (define-public (translate-markup grob props . rest)
216   "Syntax: \\translate OFFSET MARKUP. "
217   (ly:molecule-translate (interpret-markup  grob props (cadr rest))
218                          (car rest))
219
220   )
221
222 (define-public (sub-markup grob props  . rest)
223   "Syntax: \\sub MARKUP."
224   (ly:molecule-translate-axis (interpret-markup
225                                grob
226                                (cons '((font-relative-size . -2)) props)
227                                (car rest))
228                               (* -0.5 (cdr (chain-assoc 'baseline-skip props)))
229                               Y)
230   )
231
232 (define-public (normal-size-sub-markup grob props . rest)
233   (ly:molecule-translate-axis (interpret-markup
234                                grob
235                                props (car rest))
236                               (* -0.5 (cdr (chain-assoc 'baseline-skip props)))
237                               Y)
238   )
239
240 (define-public (hbracket-markup grob props . rest)
241   (let*
242       (
243        (th 0.1) ;; todo: take from GROB.
244        (m (interpret-markup grob props (car rest)))
245        )
246
247     (bracketify-molecule m X th (* 2.5 th) th)  
248 ))
249
250 (define-public (bracket-markup grob props . rest)
251   (let*
252       (
253        (th 0.1) ;; todo: take from GROB.
254        (m (interpret-markup grob props (car rest)))
255        )
256
257     (bracketify-molecule m Y th (* 2.5 th) th)  
258 ))
259
260
261 ;; todo: fix negative space
262 (define (hspace-markup grob props . rest)
263   "Syntax: \\hspace NUMBER."
264   (let*
265       ((amount (car rest)))
266     (if (> amount 0)
267         (ly:make-molecule "" (cons 0 amount) '(-1 . 1) )
268         (ly:make-molecule "" (cons amount amount) '(-1 . 1)))
269   ))
270
271 (define-public (override-markup grob props . rest)
272   "Tack the 1st arg in REST onto PROPS, e.g.
273
274 \override #'(font-family . married) \"bla\"
275
276 "
277   
278   (interpret-markup grob (cons (list (car rest)) props)
279                     (cadr rest)))
280
281 (define-public (smaller-markup  grob props . rest)
282   "Syntax: \\smaller MARKUP"
283   (let*
284       (
285        (fs (cdr (chain-assoc 'font-relative-size props)))
286        (entry (cons 'font-relative-size (- fs 1)))
287        )
288   (interpret-markup
289    grob (cons (list entry) props)
290    (car rest))
291
292   ))
293
294 (define-public (bigger-markup  grob props . rest)
295   "Syntax: \\bigger MARKUP"
296   (let*
297       (
298        (fs (cdr (chain-assoc 'font-relative-size props)))
299        (entry (cons 'font-relative-size (+ fs 1)))
300        )
301   (interpret-markup
302    grob (cons (list entry) props)
303    (car rest))
304   ))
305
306 (define (markup-signature-to-keyword sig)
307   " (A B C) -> a0-b1-c2 "
308   
309   (let* ((count  0))
310     (string->symbol (string-join
311      
312      (map
313      (lambda (func)
314        (set! count (+ count 1))
315        (string-append
316
317         ;; for reasons I don't get,
318         ;; (case func ((markup?) .. )
319         ;; doesn't work.
320         (cond 
321           ((eq? func markup?) "markup")
322           ((eq? func markup-list?) "markup-list")
323           (else "scheme")
324           )
325         (number->string (- count 1))
326         ))
327      
328      sig)
329      "-"))
330
331   ))
332
333
334 (define (markup-function? x)
335   (object-property x 'markup-signature)
336   )
337
338 (define (markup-list? arg)
339   (define (markup-list-inner? l)
340     (if (null? l)
341         #t
342         (and (markup? (car l)) (markup-list-inner? (cdr l)))
343     )
344   )
345   (and (list? arg) (markup-list-inner? arg)))
346
347 (define (markup-argument-list? signature arguments)
348   "Typecheck argument list."
349   (if (and (pair? signature) (pair? arguments))
350       (and ((car signature) (car arguments))
351            (markup-argument-list? (cdr signature) (cdr arguments)))
352       (and (null? signature) (null? arguments)))
353   )
354
355
356 (define (markup-argument-list-error signature arguments number)
357   "return (ARG-NR TYPE-EXPECTED ARG-FOUND) if an error is detected, or
358 #f is no error found.
359 "
360   (if (and (pair? signature) (pair? arguments))
361       (if (not ((car signature) (car arguments)))
362           (list number (type-name (car signature)) (car arguments))
363           (markup-argument-list-error (cdr signature) (cdr arguments) (+ 1 number)))
364       #f
365   ))
366
367 ;;
368 ;; full recursive typecheck.
369 ;;
370 (define (markup-typecheck? arg)
371   (or (string? arg)
372       (and (pair? arg)
373        (markup-function? (car arg))
374        (markup-argument-list?
375         (object-property (car arg) 'markup-signature)
376         (cdr arg))
377   ))
378 )
379
380 ;; 
381 ;; typecheck, and throw an error when something amiss.
382 ;; 
383 (define (markup-thrower-typecheck arg)
384   (cond
385    ((string? arg) #t)
386    ((not (pair? arg))
387     (throw 'markup-format "Not a pair" arg)
388     )
389    ((not (markup-function? (car arg)))
390     (throw 'markup-format "Not a markup function " (car arg)))
391    
392   
393    ((not (markup-argument-list? 
394           (object-property (car arg) 'markup-signature)
395           (cdr arg)))
396     (throw 'markup-format "Arguments failed  typecheck for " arg)))
397    #t
398   )
399
400 ;;
401 ;; good enough if you only  use make-XXX-markup functions.
402 ;; 
403 (define (cheap-markup? x)
404   (or (string? x)
405       (and (pair? x)
406            (markup-function? (car x))))
407 )
408
409 ;;
410 ;; replace by markup-thrower-typecheck for more detailed diagnostics.
411 ;; 
412 (define markup?  cheap-markup?)
413
414 (define markup-function-list
415   (list
416
417    ;; abs size
418    (cons teeny-markup (list markup?))
419    (cons tiny-markup (list markup?))
420    (cons small-markup (list markup?))
421    (cons dynamic-markup (list markup?))
422    (cons large-markup (list markup?)) 
423    
424    (cons huge-markup (list markup?))
425
426    ;; size
427    (cons smaller-markup (list markup?))
428    (cons bigger-markup (list markup?))
429
430    ;; 
431    (cons sub-markup (list markup?))
432    (cons normal-size-sub-markup (list markup?))
433    
434    (cons super-markup (list markup?))
435    (cons normal-size-super-markup (list markup?))
436
437    (cons finger-markup (list markup?))
438    (cons bold-markup (list markup?))
439    (cons italic-markup (list markup?))
440    (cons roman-markup (list markup?))
441    (cons number-markup (list markup?))
442    (cons hbracket-markup  (list markup?))
443    (cons bracket-markup  (list markup?))
444    
445    (cons column-markup (list markup-list?))
446    (cons dir-column-markup (list markup-list?))
447    (cons center-markup (list markup-list?))
448    (cons line-markup  (list markup-list?))
449
450    (cons combine-markup (list markup? markup?))
451    (cons simple-markup (list string?))
452    (cons musicglyph-markup (list scheme?))
453    (cons translate-markup (list number-pair? markup?))
454    (cons override-markup (list pair? markup?))
455    (cons char-markup (list integer?))
456    (cons lookup-markup (list string?))
457    
458    (cons hspace-markup (list number?))
459
460    (cons raise-markup (list number? markup?))
461    (cons magnify-markup (list number? markup?))
462    (cons fontsize-markup (list number? markup?))
463    )
464   )
465
466
467 (define markup-module (current-module))
468
469 (map (lambda (x)
470        (set-object-property! (car x) 'markup-signature (cdr x))
471        (set-object-property! (car x) 'markup-keyword (markup-signature-to-keyword (cdr x)))
472        )
473      markup-function-list)
474
475
476 ;; construct a
477 ;;
478 ;; make-FOO-markup function that typechecks its arguments.
479 ;;
480 ;; TODO: should construct a message says
481 ;; Invalid argument 4 : expecting a BLADIBLA, found: (list-ref 4 args)
482 ;;
483 ;; right now, you get the entire argument list.
484
485
486 (define (make-markup-maker  entry)
487   (let*
488         ((foo-markup (car entry))
489          (signature (cons 'list (cdr entry)))
490          (name (symbol->string (procedure-name foo-markup)))
491          (make-name  (string-append "make-" name))
492          )
493       
494       `(define (,(string->symbol make-name) . args)
495          (let*
496              (
497               (arglen (length  args))
498               (siglen (length ,signature))
499               (error-msg
500                (if (and (> 0 siglen) (> 0 arglen))
501                    (markup-argument-list-error ,signature args 1)))
502               
503               )
504          
505          (if (or (not (= arglen siglen)) (< siglen 0) (< 0 arglen))
506              (scm-error 'markup-format ,make-name "Expect ~A arguments for ~A. Found ~A: ~S"
507                         (list (length ,signature)
508                               ,make-name
509                               (length args)
510                               args) #f))
511          (if error-msg
512              (scm-error 'markup-format ,make-name "Invalid argument in position ~A\n Expect: ~A\nFound: ~S." error-msg #f)
513              
514              (cons ,foo-markup args)
515              )))
516     )
517 )
518
519
520
521 (define (make-markup markup-function make-name signature args)
522   
523   " Construct a markup object from MARKUP-FUNCTION and ARGS. Typecheck
524 against SIGNATURE, reporting MAKE-NAME as the user-invoked function.
525 "
526
527   (let*
528       (
529        (arglen (length args))
530        (siglen (length signature))
531        (error-msg
532         (if (and (> siglen 0) (> arglen 0))
533             (markup-argument-list-error signature args 1)))
534        )
535
536
537     (if (or (not (= arglen siglen)) (< siglen 0) (< arglen 0))
538         (scm-error 'markup-format make-name "Expect ~A arguments for ~A. Found ~A: ~S"
539                    (list siglen
540                          make-name
541                          arglen
542                          args) #f))
543
544     (if error-msg
545         (scm-error 'markup-format make-name "Invalid argument in position ~A\nExpect: ~A\nFound: ~S." error-msg #f)
546         
547         (cons markup-function  args)
548         )))
549
550 (define (make-markup-maker entry)
551   (let* (
552          (name (symbol->string (procedure-name (car entry))))
553          (make-name  (string-append "make-" name))
554          (signature (object-property (car entry) 'markup-signature))
555          )
556   
557     `(define-public (,(string->symbol make-name) . args)
558        (make-markup ,(car entry) ,make-name ,(cons 'list signature)  args)
559        ))
560   )
561
562 (eval
563  (cons 'begin (map make-markup-maker markup-function-list))
564  markup-module
565  )
566
567 (define-public (lookup-markup-command code)
568   (let*
569       ( (sym (string->symbol (string-append code "-markup")))
570         (var (module-local-variable markup-module sym))
571         )
572     (if (eq? var #f)
573         #f   
574         (cons (variable-ref var) (object-property  (variable-ref var) 'markup-keyword))
575     )
576   ))
577
578
579 (define-public (brew-new-markup-molecule grob)
580   (let*
581       ((t     (ly:get-grob-property grob 'text))
582        )
583     (if (null? t)
584         '()
585         (interpret-markup grob
586                           (Font_interface::get_property_alist_chain grob)
587                           t
588                           ))
589   ))
590
591 (define-public empty-markup `(,simple-markup ""))
592
593 (define (interpret-markup  grob props markup)
594   (if (string? markup)
595       (simple-markup grob props markup)
596       (let*
597           (
598            (func (car markup))
599            (args (cdr markup))
600            )
601         
602         (apply func (cons grob (cons props args)) )
603         )))
604
605
606 ;;;;;;;;;;;;;;;;
607 ;; utility
608
609 (define (markup-join markups sep)
610   "Return line-markup of MARKUPS, joining them with markup SEP"
611   (if (pair? markups)
612       (make-line-markup (list-insert-separator markups sep))
613       empty-markup))
614
615
616 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
617
618 (if #f
619    (define (typecheck-with-error x)
620      (catch
621       'markup-format
622       (lambda () (markup? x))
623       (lambda (key message arg)
624         (display "\nERROR: markup format error: \n")
625         (display message)
626         (newline)
627         (write arg (current-output-port))
628         )
629       )))
630
631 ;; test make-foo-markup functions
632 (if #f
633     (begin
634       (newline)
635       (newline)
636       (display (make-line-markup (list (make-simple-markup "FOO"))))
637       
638       (make-line-markup (make-simple-markup "FOO"))
639       (make-line-markup (make-simple-markup "FOO") (make-simple-markup "foo"))
640       (make-raise-markup "foo" (make-simple-markup "foo"))
641       )
642     )
643
644
645 ;;
646 ;; test typecheckers. Not wholly useful, because errors are detected
647 ;; in other places than they're made.
648 ;;
649 (if #f
650  (begin
651
652    ;; To get error messages, see above to install the alternate
653    ;; typecheck routine for markup?.
654    
655
656
657    (display (typecheck-with-error `(,simple-markup "foobar")))
658    (display (typecheck-with-error `(,simple-markup "foobar")))
659    (display (typecheck-with-error `(,simple-markup 1)))
660    (display
661     (typecheck-with-error  `(,line-markup ((,simple-markup "foobar"))
662                                           (,simple-markup 1))))
663    (display
664     (typecheck-with-error  `(,line-markup (,simple-markup "foobar")
665                                          (,simple-markup "bla"))))
666    
667    ))